xref: /llvm-project/openmp/libompd/gdb-plugin/ompd/ompd_address_space.py (revision f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c)
1from __future__ import print_function
2import ompdModule
3from ompd_handles import ompd_thread, ompd_task, ompd_parallel
4import gdb
5import sys
6import traceback
7from enum import Enum
8
9
10class ompd_scope(Enum):
11    ompd_scope_global = 1
12    ompd_scope_address_space = 2
13    ompd_scope_thread = 3
14    ompd_scope_parallel = 4
15    ompd_scope_implicit_task = 5
16    ompd_scope_task = 6
17
18
19class ompd_address_space(object):
20    def __init__(self):
21        """Initializes an ompd_address_space object by calling ompd_initialize
22        in ompdModule.c
23        """
24        self.addr_space = ompdModule.call_ompd_initialize()
25        # maps thread_num (thread id given by gdb) to ompd_thread object with thread handle
26        self.threads = {}
27        self.states = None
28        self.icv_map = None
29        self.ompd_tool_test_bp = None
30        self.scope_map = {
31            1: "global",
32            2: "address_space",
33            3: "thread",
34            4: "parallel",
35            5: "implicit_task",
36            6: "task",
37        }
38        self.sched_map = {1: "static", 2: "dynamic", 3: "guided", 4: "auto"}
39        gdb.events.stop.connect(self.handle_stop_event)
40        self.new_thread_breakpoint = gdb.Breakpoint(
41            "ompd_bp_thread_begin", internal=True
42        )
43        tool_break_symbol = gdb.lookup_global_symbol("ompd_tool_break")
44        if tool_break_symbol is not None:
45            self.ompd_tool_test_bp = gdb.Breakpoint("ompd_tool_break", internal=True)
46
47    def handle_stop_event(self, event):
48        """Sets a breakpoint at different events, e.g. when a new OpenMP
49        thread is created.
50        """
51        if isinstance(event, gdb.BreakpointEvent):
52            # check if breakpoint has already been hit
53            if self.new_thread_breakpoint in event.breakpoints:
54                self.add_thread()
55                gdb.execute("continue")
56                return
57            elif (
58                self.ompd_tool_test_bp is not None
59                and self.ompd_tool_test_bp in event.breakpoints
60            ):
61                try:
62                    self.compare_ompt_data()
63                    gdb.execute("continue")
64                except ():
65                    traceback.print_exc()
66        elif isinstance(event, gdb.SignalEvent):
67            # TODO: what do we need to do on SIGNALS?
68            pass
69        else:
70            # TODO: probably not possible?
71            pass
72
73    def get_icv_map(self):
74        """Fills ICV map."""
75        self.icv_map = {}
76        current = 0
77        more = 1
78        while more > 0:
79            tup = ompdModule.call_ompd_enumerate_icvs(self.addr_space, current)
80            (current, next_icv, next_scope, more) = tup
81            self.icv_map[next_icv] = (current, next_scope, self.scope_map[next_scope])
82        print("Initialized ICV map successfully for checking OMP API values.")
83
84    def compare_ompt_data(self):
85        """Compares OMPT tool data about parallel region to data returned by OMPD functions."""
86        # make sure all threads and states are set
87        self.list_threads(False)
88
89        thread_id = gdb.selected_thread().ptid[1]
90        curr_thread = self.get_curr_thread()
91
92        # check if current thread is LWP thread; return if "ompd_rc_unavailable"
93        thread_handle = ompdModule.get_thread_handle(thread_id, self.addr_space)
94        if thread_handle == -1:
95            print("Skipping OMPT-OMPD checks for non-LWP thread.")
96            return
97
98        print("Comparing OMPT data to OMPD data...")
99        field_names = [i.name for i in gdb.parse_and_eval("thread_data").type.fields()]
100        thread_data = gdb.parse_and_eval("thread_data")
101
102        if self.icv_map is None:
103            self.get_icv_map()
104
105        # compare state values
106        if "ompt_state" in field_names:
107            if self.states is None:
108                self.enumerate_states()
109            ompt_state = str(thread_data["ompt_state"])
110            ompd_state = str(self.states[curr_thread.get_state()[0]])
111            if ompt_state != ompd_state:
112                print(
113                    "OMPT-OMPD mismatch: ompt_state (%s) does not match OMPD state (%s)!"
114                    % (ompt_state, ompd_state)
115                )
116
117        # compare wait_id values
118        if "ompt_wait_id" in field_names:
119            ompt_wait_id = thread_data["ompt_wait_id"]
120            ompd_wait_id = curr_thread.get_state()[1]
121            if ompt_wait_id != ompd_wait_id:
122                print(
123                    "OMPT-OMPD mismatch: ompt_wait_id (%d) does not match OMPD wait id (%d)!"
124                    % (ompt_wait_id, ompd_wait_id)
125                )
126
127        # compare thread id
128        if "omp_thread_num" in field_names and "thread-num-var" in self.icv_map:
129            ompt_thread_num = thread_data["omp_thread_num"]
130            icv_value = ompdModule.call_ompd_get_icv_from_scope(
131                curr_thread.thread_handle,
132                self.icv_map["thread-num-var"][1],
133                self.icv_map["thread-num-var"][0],
134            )
135            if ompt_thread_num != icv_value:
136                print(
137                    "OMPT-OMPD mismatch: omp_thread_num (%d) does not match OMPD thread num according to ICVs (%d)!"
138                    % (ompt_thread_num, icv_value)
139                )
140
141        # compare thread data
142        if "ompt_thread_data" in field_names:
143            ompt_thread_data = thread_data["ompt_thread_data"].dereference()["value"]
144            ompd_value = ompdModule.call_ompd_get_tool_data(
145                3, curr_thread.thread_handle
146            )[0]
147            if ompt_thread_data != ompd_value:
148                print(
149                    "OMPT-OMPD mismatch: value of ompt_thread_data (%d) does not match that of OMPD data union (%d)!"
150                    % (ompt_thread_data, ompd_value)
151                )
152
153        # compare number of threads
154        if "omp_num_threads" in field_names and "team-size-var" in self.icv_map:
155            ompt_num_threads = thread_data["omp_num_threads"]
156            icv_value = ompdModule.call_ompd_get_icv_from_scope(
157                curr_thread.get_current_parallel_handle(),
158                self.icv_map["team-size-var"][1],
159                self.icv_map["team-size-var"][0],
160            )
161            if ompt_num_threads != icv_value:
162                print(
163                    "OMPT-OMPD mismatch: omp_num_threads (%d) does not match OMPD num threads according to ICVs (%d)!"
164                    % (ompt_num_threads, icv_value)
165                )
166
167        # compare omp level
168        if "omp_level" in field_names and "levels-var" in self.icv_map:
169            ompt_levels = thread_data["omp_level"]
170            icv_value = ompdModule.call_ompd_get_icv_from_scope(
171                curr_thread.get_current_parallel_handle(),
172                self.icv_map["levels-var"][1],
173                self.icv_map["levels-var"][0],
174            )
175            if ompt_levels != icv_value:
176                print(
177                    "OMPT-OMPD mismatch: omp_level (%d) does not match OMPD levels according to ICVs (%d)!"
178                    % (ompt_levels, icv_value)
179                )
180
181        # compare active level
182        if "omp_active_level" in field_names and "active-levels-var" in self.icv_map:
183            ompt_active_levels = thread_data["omp_active_level"]
184            icv_value = ompdModule.call_ompd_get_icv_from_scope(
185                curr_thread.get_current_parallel_handle(),
186                self.icv_map["active-levels-var"][1],
187                self.icv_map["active-levels-var"][0],
188            )
189            if ompt_active_levels != icv_value:
190                print(
191                    "OMPT-OMPD mismatch: active levels (%d) do not match active levels according to ICVs (%d)!"
192                    % (ompt_active_levels, icv_value)
193                )
194
195        # compare parallel data
196        if "ompt_parallel_data" in field_names:
197            ompt_parallel_data = thread_data["ompt_parallel_data"].dereference()[
198                "value"
199            ]
200            current_parallel_handle = curr_thread.get_current_parallel_handle()
201            ompd_value = ompdModule.call_ompd_get_tool_data(4, current_parallel_handle)[
202                0
203            ]
204            if ompt_parallel_data != ompd_value:
205                print(
206                    "OMPT-OMPD mismatch: value of ompt_parallel_data (%d) does not match that of OMPD data union (%d)!"
207                    % (ompt_parallel_data, ompd_value)
208                )
209
210        # compare max threads
211        if "omp_max_threads" in field_names and "nthreads-var" in self.icv_map:
212            ompt_max_threads = thread_data["omp_max_threads"]
213            icv_value = ompdModule.call_ompd_get_icv_from_scope(
214                curr_thread.thread_handle,
215                self.icv_map["nthreads-var"][1],
216                self.icv_map["nthreads-var"][0],
217            )
218            if icv_value is None:
219                icv_string = ompdModule.call_ompd_get_icv_string_from_scope(
220                    curr_thread.thread_handle,
221                    self.icv_map["nthreads-var"][1],
222                    self.icv_map["nthreads-var"][0],
223                )
224                if icv_string is None:
225                    print(
226                        "OMPT-OMPD mismatch: omp_max_threads (%d) does not match OMPD thread limit according to ICVs (None Object)"
227                        % (ompt_max_threads)
228                    )
229                else:
230                    if ompt_max_threads != int(icv_string.split(",")[0]):
231                        print(
232                            "OMPT-OMPD mismatch: omp_max_threads (%d) does not match OMPD thread limit according to ICVs (%d)!"
233                            % (ompt_max_threads, int(icv_string.split(",")[0]))
234                        )
235            else:
236                if ompt_max_threads != icv_value:
237                    print(
238                        "OMPT-OMPD mismatch: omp_max_threads (%d) does not match OMPD thread limit according to ICVs (%d)!"
239                        % (ompt_max_threads, icv_value)
240                    )
241
242        # compare omp_parallel
243        # NOTE: omp_parallel = true if active-levels-var > 0
244        if "omp_parallel" in field_names:
245            ompt_parallel = thread_data["omp_parallel"]
246            icv_value = ompdModule.call_ompd_get_icv_from_scope(
247                curr_thread.get_current_parallel_handle(),
248                self.icv_map["active-levels-var"][1],
249                self.icv_map["active-levels-var"][0],
250            )
251            if (
252                ompt_parallel == 1
253                and icv_value <= 0
254                or ompt_parallel == 0
255                and icv_value > 0
256            ):
257                print(
258                    "OMPT-OMPD mismatch: ompt_parallel (%d) does not match OMPD parallel according to ICVs (%d)!"
259                    % (ompt_parallel, icv_value)
260                )
261
262        # compare omp_final
263        if "omp_final" in field_names and "final-task-var" in self.icv_map:
264            ompt_final = thread_data["omp_final"]
265            current_task_handle = curr_thread.get_current_task_handle()
266            icv_value = ompdModule.call_ompd_get_icv_from_scope(
267                current_task_handle,
268                self.icv_map["final-task-var"][1],
269                self.icv_map["final-task-var"][0],
270            )
271            if icv_value != ompt_final:
272                print(
273                    "OMPT-OMPD mismatch: omp_final (%d) does not match OMPD final according to ICVs (%d)!"
274                    % (ompt_final, icv_value)
275                )
276
277        # compare omp_dynamic
278        if "omp_dynamic" in field_names and "dyn-var" in self.icv_map:
279            ompt_dynamic = thread_data["omp_dynamic"]
280            icv_value = ompdModule.call_ompd_get_icv_from_scope(
281                curr_thread.thread_handle,
282                self.icv_map["dyn-var"][1],
283                self.icv_map["dyn-var"][0],
284            )
285            if icv_value != ompt_dynamic:
286                print(
287                    "OMPT-OMPD mismatch: omp_dynamic (%d) does not match OMPD dynamic according to ICVs (%d)!"
288                    % (ompt_dynamic, icv_value)
289                )
290
291        # compare omp_max_active_levels
292        if (
293            "omp_max_active_levels" in field_names
294            and "max-active-levels-var" in self.icv_map
295        ):
296            ompt_max_active_levels = thread_data["omp_max_active_levels"]
297            icv_value = ompdModule.call_ompd_get_icv_from_scope(
298                curr_thread.get_current_task_handle(),
299                self.icv_map["max-active-levels-var"][1],
300                self.icv_map["max-active-levels-var"][0],
301            )
302            if ompt_max_active_levels != icv_value:
303                print(
304                    "OMPT-OMPD mismatch: omp_max_active_levels (%d) does not match OMPD max active levels (%d)!"
305                    % (ompt_max_active_levels, icv_value)
306                )
307
308                # compare omp_kind: TODO: Add the test for monotonic/nonmonotonic modifier
309        if "omp_kind" in field_names and "run-sched-var" in self.icv_map:
310            ompt_sched_kind = thread_data["omp_kind"]
311            icv_value = ompdModule.call_ompd_get_icv_string_from_scope(
312                curr_thread.get_current_task_handle(),
313                self.icv_map["run-sched-var"][1],
314                self.icv_map["run-sched-var"][0],
315            )
316            ompd_sched_kind = icv_value.split(",")[0]
317            if self.sched_map.get(int(ompt_sched_kind)) != ompd_sched_kind:
318                print(
319                    "OMPT-OMPD mismatch: omp_kind kind (%s) does not match OMPD schedule kind according to ICVs (%s)!"
320                    % (self.sched_map.get(int(ompt_sched_kind)), ompd_sched_kind)
321                )
322
323        # compare omp_modifier
324        if "omp_modifier" in field_names and "run-sched-var" in self.icv_map:
325            ompt_sched_mod = thread_data["omp_modifier"]
326            icv_value = ompdModule.call_ompd_get_icv_string_from_scope(
327                curr_thread.get_current_task_handle(),
328                self.icv_map["run-sched-var"][1],
329                self.icv_map["run-sched-var"][0],
330            )
331            token = icv_value.split(",")[1]
332            if token is not None:
333                ompd_sched_mod = int(token)
334            else:
335                ompd_sched_mod = 0
336            if ompt_sched_mod != ompd_sched_mod:
337                print(
338                    "OMPT-OMPD mismatch: omp_kind modifier does not match OMPD schedule modifier according to ICVs!"
339                )
340
341        # compare omp_proc_bind
342        if "omp_proc_bind" in field_names and "bind-var" in self.icv_map:
343            ompt_proc_bind = thread_data["omp_proc_bind"]
344            icv_value = ompdModule.call_ompd_get_icv_from_scope(
345                curr_thread.get_current_task_handle(),
346                self.icv_map["bind-var"][1],
347                self.icv_map["bind-var"][0],
348            )
349            if icv_value is None:
350                icv_string = ompdModule.call_ompd_get_icv_string_from_scope(
351                    curr_thread.get_current_task_handle(),
352                    self.icv_map["bind-var"][1],
353                    self.icv_map["bind-var"][0],
354                )
355                if icv_string is None:
356                    print(
357                        "OMPT-OMPD mismatch: omp_proc_bind (%d) does not match OMPD proc bind according to ICVs (None Object)"
358                        % (ompt_proc_bind)
359                    )
360                else:
361                    if ompt_proc_bind != int(icv_string.split(",")[0]):
362                        print(
363                            "OMPT-OMPD mismatch: omp_proc_bind (%d) does not match OMPD proc bind according to ICVs (%d)!"
364                            % (ompt_proc_bind, int(icv_string.split(",")[0]))
365                        )
366            else:
367                if ompt_proc_bind != icv_value:
368                    print(
369                        "OMPT-OMPD mismatch: omp_proc_bind (%d) does not match OMPD proc bind according to ICVs (%d)!"
370                        % (ompt_proc_bind, icv_value)
371                    )
372
373        # compare enter and exit frames
374        if "ompt_frame_list" in field_names:
375            ompt_task_frame_dict = thread_data["ompt_frame_list"].dereference()
376            ompt_task_frames = (
377                int(ompt_task_frame_dict["enter_frame"].cast(gdb.lookup_type("long"))),
378                int(ompt_task_frame_dict["exit_frame"].cast(gdb.lookup_type("long"))),
379            )
380            current_task = curr_thread.get_current_task()
381            ompd_task_frames = current_task.get_task_frame()
382            if ompt_task_frames != ompd_task_frames:
383                print(
384                    "OMPT-OMPD mismatch: ompt_task_frames (%s) do not match OMPD task frames (%s)!"
385                    % (ompt_task_frames, ompd_task_frames)
386                )
387
388        # compare task data
389        if "ompt_task_data" in field_names:
390            ompt_task_data = thread_data["ompt_task_data"].dereference()["value"]
391            current_task_handle = curr_thread.get_current_task_handle()
392            ompd_value = ompdModule.call_ompd_get_tool_data(6, current_task_handle)[0]
393            if ompt_task_data != ompd_value:
394                print(
395                    "OMPT-OMPD mismatch: value of ompt_task_data (%d) does not match that of OMPD data union (%d)!"
396                    % (ompt_task_data, ompd_value)
397                )
398
399    def save_thread_object(self, thread_num, thread_id, addr_space):
400        """Saves thread object for thread_num inside threads dictionary."""
401        thread_handle = ompdModule.get_thread_handle(thread_id, addr_space)
402        self.threads[int(thread_num)] = ompd_thread(thread_handle)
403
404    def get_thread(self, thread_num):
405        """Get thread object from map."""
406        return self.threads[int(thread_num)]
407
408    def get_curr_thread(self):
409        """Get current thread object from map or add new one to map, if missing."""
410        thread_num = int(gdb.selected_thread().num)
411        if thread_num not in self.threads:
412            self.add_thread()
413        return self.threads[thread_num]
414
415    def add_thread(self):
416        """Add currently selected (*) thread to dictionary threads."""
417        inf_thread = gdb.selected_thread()
418        try:
419            self.save_thread_object(inf_thread.num, inf_thread.ptid[1], self.addr_space)
420        except:
421            traceback.print_exc()
422
423    def list_threads(self, verbose):
424        """Prints OpenMP threads only that are being tracking inside the "threads" dictionary.
425        See handle_stop_event and add_thread.
426        """
427        list_tids = []
428        curr_inferior = gdb.selected_inferior()
429
430        for inf_thread in curr_inferior.threads():
431            list_tids.append((inf_thread.num, inf_thread.ptid))
432        if verbose:
433            if self.states is None:
434                self.enumerate_states()
435            for (thread_num, thread_ptid) in sorted(list_tids):
436                if thread_num in self.threads:
437                    try:
438                        print(
439                            "Thread %i (%i) is an OpenMP thread; state: %s"
440                            % (
441                                thread_num,
442                                thread_ptid[1],
443                                self.states[self.threads[thread_num].get_state()[0]],
444                            )
445                        )
446                    except:
447                        traceback.print_exc()
448                else:
449                    print(
450                        "Thread %i (%i) is no OpenMP thread"
451                        % (thread_num, thread_ptid[1])
452                    )
453
454    def enumerate_states(self):
455        """Helper function for list_threads: initializes map of OMPD states for output of
456        'ompd threads'.
457        """
458        if self.states is None:
459            self.states = {}
460            current = int("0x102", 0)
461            count = 0
462            more = 1
463
464            while more > 0:
465                tup = ompdModule.call_ompd_enumerate_states(self.addr_space, current)
466                (next_state, next_state_name, more) = tup
467
468                self.states[next_state] = next_state_name
469                current = next_state
470