|
1 | 1 | # |
2 | | -# Copyright 2020 Delphix |
| 2 | +# Copyright 2025 Delphix |
3 | 3 | # |
4 | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
5 | 5 | # you may not use this file except in compliance with the License. |
@@ -110,40 +110,59 @@ def no_input(self) -> Iterable[drgn.Object]: |
110 | 110 | yield from for_each_task(sdb.get_prog()) |
111 | 111 |
|
112 | 112 |
|
113 | | -def _framestr(frame: drgn.Object, args: bool) -> str: |
114 | | - # This parsing is a bit fragile. It depends on the string |
115 | | - # for the frame to have the following format: |
116 | | - # <id> in <addr> <symbol> [in <function>] at <filepath> [(inlined)] |
117 | | - # It pulls off the last element of the file path and returns: |
118 | | - # <id> <addr> in <function>() at <filename> [(inlined)] |
119 | | - # or (if no function is available): |
120 | | - # <id> <addr> <symbol> at <filename> [(inlined)] |
121 | | - inlined = "" |
122 | | - parts = str(frame).split() |
123 | | - id = parts[0] |
124 | | - if len(id) < 3: |
125 | | - id = id + " " |
126 | | - addr = parts[2] |
127 | | - # Just have an address |
128 | | - if len(parts) < 4: |
129 | | - return id + " " + addr |
130 | | - # No function or file path available? Just return the symbol |
131 | | - if len(parts) < 5: |
132 | | - return id + " " + addr + " " + parts[3] |
133 | | - if parts[4] != "in": |
134 | | - addr = addr + " " + parts[3] |
135 | | - function = "" |
136 | | - filepath = parts[5] |
| 113 | +def _framestr(frame_index: int, frame: drgn.Object, args: bool) -> str: |
| 114 | + """ |
| 115 | + Format a stack frame using drgn StackFrame API properties. |
| 116 | +
|
| 117 | + Args: |
| 118 | + frame_index: Frame index number |
| 119 | + frame: drgn StackFrame object |
| 120 | + args: If True, show function arguments |
| 121 | +
|
| 122 | + Returns: |
| 123 | + Formatted frame string like: "#0 0xaddr in function() at file.c:line:col (inlined)" |
| 124 | + """ |
| 125 | + # Format frame index with padding for alignment |
| 126 | + id_str = f"#{frame_index}" |
| 127 | + if len(id_str) < 3: |
| 128 | + id_str = id_str + " " |
| 129 | + |
| 130 | + # Get program counter (address) |
| 131 | + try: |
| 132 | + addr = hex(frame.pc) |
| 133 | + except LookupError: |
| 134 | + # No PC available |
| 135 | + return id_str + " <no address found>" |
| 136 | + |
| 137 | + # Get function/symbol name |
| 138 | + function_name = frame.function_name |
| 139 | + if function_name is None: |
| 140 | + try: |
| 141 | + function_name = frame.symbol().name |
| 142 | + except LookupError: |
| 143 | + # No symbol available, just return address |
| 144 | + return f"{id_str} {addr}" |
| 145 | + |
| 146 | + # Build function string |
| 147 | + if args: |
| 148 | + function_str = " in " + _funcstr(frame) |
137 | 149 | else: |
138 | | - function = " in " + parts[5] + "()" |
139 | | - if args: |
140 | | - function = " in " + _funcstr(frame) |
141 | | - filepath = parts[7] |
142 | | - filename = " at " + filepath.split("/")[-1] |
143 | | - if frame.is_inline: |
144 | | - inlined = " (inlined)" |
145 | | - return id + " " + addr + function + filename + inlined |
| 150 | + function_str = f" in {function_name}()" |
| 151 | + |
| 152 | + # Get source location |
| 153 | + try: |
| 154 | + filename, line, column = frame.source() |
| 155 | + # Extract just the filename from the full path |
| 156 | + filename_only = filename.split("/")[-1] if filename else "??" |
| 157 | + location_str = f" at {filename_only}:{line}:{column}" |
| 158 | + except LookupError: |
| 159 | + # No source information available |
| 160 | + location_str = "" |
| 161 | + |
| 162 | + # Add inline marker if applicable |
| 163 | + inline_str = " (inlined)" if frame.is_inline else "" |
146 | 164 |
|
| 165 | + return id_str + " " + addr + function_str + location_str + inline_str |
147 | 166 |
|
148 | 167 | def _funcstr(frame: drgn.Object) -> str: |
149 | 168 | name = frame.name |
@@ -245,12 +264,12 @@ def pretty_print(self, threads: Iterable[drgn.Object]) -> None: |
245 | 264 | + str(KernelStacks.task_struct_get_state(thread)) |
246 | 265 | + f" PID: {int(thread.pid)}" |
247 | 266 | ) |
248 | | - for frame in stack_trace: |
| 267 | + for frame_index, frame in enumerate(stack_trace): |
249 | 268 | if frame.pc == 0: |
250 | 269 | # Note: We filter out frames with zero program counter, |
251 | 270 | # as we often see the stack trace padded with zeros |
252 | 271 | continue |
253 | | - print(_framestr(frame, False)) |
| 272 | + print(_framestr(frame_index, frame, False)) |
254 | 273 |
|
255 | 274 | def no_input(self) -> Iterable[drgn.Object]: |
256 | 275 | if self.args.task: |
@@ -344,7 +363,7 @@ def pretty_print(self, threads: Iterable[drgn.Object]) -> None: |
344 | 363 | if self.args.verbose: |
345 | 364 | print(frame) |
346 | 365 | else: |
347 | | - print(_framestr(frame, True)) |
| 366 | + print(_framestr(self.frame_id, frame, True)) |
348 | 367 | except IndexError: |
349 | 368 | print(f"Frame {self.frame_id} out of range for thread {hex(thread)}") |
350 | 369 | continue |
|
0 commit comments