Skip to content

Commit 6306f9f

Browse files
DLPX-95965 _framestr() parsing is fragile - should use drgn StackFrame API properties
PR URL: https://www.github.com/delphix/sdb/pull/356
1 parent 3950641 commit 6306f9f

File tree

1 file changed

+55
-36
lines changed

1 file changed

+55
-36
lines changed

sdb/commands/linux/threads.py

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2020 Delphix
2+
# Copyright 2025 Delphix
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -110,40 +110,59 @@ def no_input(self) -> Iterable[drgn.Object]:
110110
yield from for_each_task(sdb.get_prog())
111111

112112

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)
137149
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 ""
146164

165+
return id_str + " " + addr + function_str + location_str + inline_str
147166

148167
def _funcstr(frame: drgn.Object) -> str:
149168
name = frame.name
@@ -245,12 +264,12 @@ def pretty_print(self, threads: Iterable[drgn.Object]) -> None:
245264
+ str(KernelStacks.task_struct_get_state(thread))
246265
+ f" PID: {int(thread.pid)}"
247266
)
248-
for frame in stack_trace:
267+
for frame_index, frame in enumerate(stack_trace):
249268
if frame.pc == 0:
250269
# Note: We filter out frames with zero program counter,
251270
# as we often see the stack trace padded with zeros
252271
continue
253-
print(_framestr(frame, False))
272+
print(_framestr(frame_index, frame, False))
254273

255274
def no_input(self) -> Iterable[drgn.Object]:
256275
if self.args.task:
@@ -344,7 +363,7 @@ def pretty_print(self, threads: Iterable[drgn.Object]) -> None:
344363
if self.args.verbose:
345364
print(frame)
346365
else:
347-
print(_framestr(frame, True))
366+
print(_framestr(self.frame_id, frame, True))
348367
except IndexError:
349368
print(f"Frame {self.frame_id} out of range for thread {hex(thread)}")
350369
continue

0 commit comments

Comments
 (0)