xref: /llvm-project/openmp/libompd/gdb-plugin/ompd/frame_filter.py (revision f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c)
1import gdb
2import ompdModule
3import itertools
4from gdb.FrameDecorator import FrameDecorator
5import ompd
6from ompd_handles import ompd_task, ompd_parallel, ompd_thread
7import traceback
8from tempfile import NamedTemporaryFile
9
10
11class OmpdFrameDecorator(FrameDecorator):
12    def __init__(self, fobj, curr_task_handle):
13        """Initializes a FrameDecorator with the given GDB Frame object. The global OMPD address space defined in
14        ompd.py is set as well.
15        """
16        super(OmpdFrameDecorator, self).__init__(fobj)
17        self.addr_space = ompd.addr_space
18        self.fobj = None
19        if isinstance(fobj, gdb.Frame):
20            self.fobj = fobj
21        elif isinstance(fobj, FrameDecorator):
22            self.fobj = fobj.inferior_frame()
23        self.curr_task_handle = curr_task_handle
24
25    def function(self):
26        """This appends the name of a frame that is printed with the information whether the task started in the frame
27        is implicit or explicit. The ICVs are evaluated to determine that.
28        """
29        name = str(self.fobj.name())
30
31        if self.curr_task_handle is None:
32            return name
33
34        icv_value = ompdModule.call_ompd_get_icv_from_scope(
35            self.curr_task_handle,
36            ompd.icv_map["implicit-task-var"][1],
37            ompd.icv_map["implicit-task-var"][0],
38        )
39        if icv_value == 0:
40            name = '@thread %i: %s "#pragma omp task"' % (
41                gdb.selected_thread().num,
42                name,
43            )
44        elif icv_value == 1:
45            name = '@thread %i: %s "#pragma omp parallel"' % (
46                gdb.selected_thread().num,
47                name,
48            )
49        else:
50            name = "@thread %i: %s" % (gdb.selected_thread().num, name)
51        return name
52
53
54class OmpdFrameDecoratorThread(FrameDecorator):
55    def __init__(self, fobj):
56        """Initializes a FrameDecorator with the given GDB Frame object."""
57        super(OmpdFrameDecoratorThread, self).__init__(fobj)
58        if isinstance(fobj, gdb.Frame):
59            self.fobj = fobj
60        elif isinstance(fobj, FrameDecorator):
61            self.fobj = fobj.inferior_frame()
62
63    def function(self):
64        name = str(self.fobj.name())
65        return "@thread %i: %s" % (gdb.selected_thread().num, name)
66
67
68class FrameFilter:
69    def __init__(self, addr_space):
70        """Initializes the FrameFilter, registers is in the GDB runtime and saves the given OMPD address space capsule."""
71        self.addr_space = addr_space
72        self.name = "Filter"
73        self.priority = 100
74        self.enabled = True
75        gdb.frame_filters[self.name] = self
76        self.switched_on = False
77        self.continue_to_master = False
78
79    def set_switch(self, on_off):
80        """Prints output when executing 'ompd bt on' or 'ompd bt off'."""
81        self.switched_on = on_off
82        if self.switched_on:
83            print('Enabled filter for "bt" output successfully.')
84        else:
85            print('Disabled filter for "bt" output successfully.')
86
87    def set_switch_continue(self, on_off):
88        """Prints output when executing 'ompd bt on continued'." """
89        self.continue_to_master = on_off
90        if self.continue_to_master:
91            print(
92                'Enabled "bt" mode that continues backtrace on to master thread for worker threads.'
93            )
94        else:
95            print('Disabled "bt" mode that continues onto master thread.')
96
97    def get_master_frames_for_worker(self, past_thread_num, latest_sp):
98        """Prints master frames for worker thread with id past_thread_num."""
99        gdb.execute("t 1")
100        gdb.execute("ompd bt on")
101        gdb.execute("bt")
102
103        frame = gdb.newest_frame()
104
105        while frame.older() is not None:
106            print("master frame sp:", str(frame.read_register("sp")))
107            yield OmpdFrameDecorator(frame)
108            frame = frame.older()
109        print("latest sp:", str(latest_sp))
110
111        gdb.execute("ompd bt on continued")
112        gdb.execute("t %d" % int(past_thread_num))
113
114    def filter_frames(self, frame_iter):
115        """Iterates through frames and only returns those that are relevant to the application
116        being debugged. The OmpdFrameDecorator is applied automatically.
117        """
118        curr_thread_num = gdb.selected_thread().num
119        is_no_omp_thread = False
120        if curr_thread_num in self.addr_space.threads:
121            curr_thread_obj = self.addr_space.threads[curr_thread_num]
122            self.curr_task = curr_thread_obj.get_current_task()
123            self.frames = self.curr_task.get_task_frame()
124        else:
125            is_no_omp_thread = True
126            print(
127                "Thread %d is no OpenMP thread, printing all frames:" % curr_thread_num
128            )
129
130        stop_iter = False
131        for x in frame_iter:
132            if is_no_omp_thread:
133                yield OmpdFrameDecoratorThread(x)
134                continue
135
136            if x.inferior_frame().older() is None:
137                continue
138            if self.curr_task.task_handle is None:
139                continue
140
141            gdb_sp = int(str(x.inferior_frame().read_register("sp")), 16)
142            gdb_sp_next_new = int(
143                str(x.inferior_frame()).split(",")[0].split("=")[1], 16
144            )
145            if x.inferior_frame().older():
146                gdb_sp_next = int(
147                    str(x.inferior_frame().older().read_register("sp")), 16
148                )
149            else:
150                gdb_sp_next = int(str(x.inferior_frame().read_register("sp")), 16)
151            while 1:
152                (ompd_enter_frame, ompd_exit_frame) = self.frames
153
154                if ompd_enter_frame != 0 and gdb_sp_next_new < ompd_enter_frame:
155                    break
156                if ompd_exit_frame != 0 and gdb_sp_next_new < ompd_exit_frame:
157                    if (
158                        x.inferior_frame().older().older()
159                        and int(
160                            str(x.inferior_frame().older().older().read_register("sp")),
161                            16,
162                        )
163                        < ompd_exit_frame
164                    ):
165                        if self.continue_to_master:
166                            yield OmpdFrameDecoratorThread(x)
167                        else:
168                            yield OmpdFrameDecorator(x, self.curr_task.task_handle)
169                    else:
170                        yield OmpdFrameDecorator(x, self.curr_task.task_handle)
171                    break
172                sched_task_handle = self.curr_task.get_scheduling_task_handle()
173
174                if sched_task_handle is None:
175                    stop_iter = True
176                    break
177
178                self.curr_task = self.curr_task.get_scheduling_task()
179                self.frames = self.curr_task.get_task_frame()
180            if stop_iter:
181                break
182
183        # implementation of "ompd bt continued"
184        if self.continue_to_master:
185
186            orig_thread = gdb.selected_thread().num
187            gdb_threads = dict([(t.num, t) for t in gdb.selected_inferior().threads()])
188
189            # iterate through generating tasks until outermost task is reached
190            while 1:
191                # get OMPD thread id for master thread (systag in GDB output)
192                try:
193                    master_num = (
194                        self.curr_task.get_task_parallel()
195                        .get_thread_in_parallel(0)
196                        .get_thread_id()
197                    )
198                except:
199                    break
200                # search for thread id without the "l" for long via "thread find" and get GDB thread num from output
201                hex_str = str(hex(master_num))
202                thread_output = gdb.execute(
203                    "thread find %s" % hex_str[0 : len(hex_str) - 1], to_string=True
204                ).split(" ")
205                if thread_output[0] == "No":
206                    raise ValueError("Master thread num could not be found!")
207                gdb_master_num = int(thread_output[1])
208                # get task that generated last task of worker thread
209                try:
210                    self.curr_task = (
211                        self.curr_task.get_task_parallel()
212                        .get_task_in_parallel(0)
213                        .get_generating_task()
214                    )
215                except:
216                    break
217                self.frames = self.curr_task.get_task_frame()
218                (enter_frame, exit_frame) = self.frames
219                if exit_frame == 0:
220                    print("outermost generating task was reached")
221                    break
222
223                # save GDB num for worker thread to change back to it later
224                worker_thread = gdb.selected_thread().num
225
226                # use InferiorThread.switch()
227                gdb_threads = dict(
228                    [(t.num, t) for t in gdb.selected_inferior().threads()]
229                )
230                gdb_threads[gdb_master_num].switch()
231                print("#### switching to thread %i ####" % gdb_master_num)
232
233                frame = gdb.newest_frame()
234                stop_iter = False
235
236                while not stop_iter:
237                    if self.curr_task.task_handle is None:
238                        break
239                    self.frames = self.curr_task.get_task_frame()
240
241                    while frame:
242                        if self.curr_task.task_handle is None:
243                            break
244
245                        gdb_sp_next_new = int(
246                            str(frame).split(",")[0].split("=")[1], 16
247                        )
248
249                        if frame.older():
250                            gdb_sp_next = int(
251                                str(frame.older().read_register("sp")), 16
252                            )
253                        else:
254                            gdb_sp_next = int(str(frame.read_register("sp")), 16)
255
256                        while 1:
257                            (ompd_enter_frame, ompd_exit_frame) = self.frames
258
259                            if (
260                                ompd_enter_frame != 0
261                                and gdb_sp_next_new < ompd_enter_frame
262                            ):
263                                break
264                            if (
265                                ompd_exit_frame == 0
266                                or gdb_sp_next_new < ompd_exit_frame
267                            ):
268                                if (
269                                    ompd_exit_frame == 0
270                                    or frame.older()
271                                    and frame.older().older()
272                                    and int(
273                                        str(frame.older().older().read_register("sp")),
274                                        16,
275                                    )
276                                    < ompd_exit_frame
277                                ):
278                                    yield OmpdFrameDecoratorThread(frame)
279                                else:
280                                    yield OmpdFrameDecorator(
281                                        frame, self.curr_task.task_handle
282                                    )
283                                break
284                            sched_task_handle = (
285                                ompdModule.call_ompd_get_scheduling_task_handle(
286                                    self.curr_task.task_handle
287                                )
288                            )
289
290                            if sched_task_handle is None:
291                                stop_iter = True
292                                break
293                            self.curr_task = self.curr_task.get_generating_task()
294                            self.frames = self.curr_task.get_task_frame()
295
296                        frame = frame.older()
297                    break
298
299                gdb_threads[worker_thread].switch()
300
301            gdb_threads[orig_thread].switch()
302
303    def filter(self, frame_iter):
304        """Function is called automatically with every 'bt' executed. If switched on, this will only let revelant frames be printed
305        or all frames otherwise. If switched on, a FrameDecorator will be applied to state whether '.ompd_task_entry.' refers to an
306        explicit or implicit task.
307        """
308        if self.switched_on:
309            return self.filter_frames(frame_iter)
310        else:
311            return frame_iter
312