Skip to content

Commit eea7532

Browse files
committed
bt: use frame pointer when formatting aarch64 stack frames
aarch64 doesn't guarantee provide the value of SP when doing a frame pointer based unwind. Don't fail on this: instead fall back to FP. Fixes internal issue tracked as LSE-374. Signed-off-by: Stephen Brennan <[email protected]>
1 parent 8d82857 commit eea7532

File tree

2 files changed

+26
-7
lines changed

2 files changed

+26
-7
lines changed

drgn_tools/bt.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import typing as t
66

77
import drgn
8+
from drgn import Architecture
89
from drgn import FaultError
910
from drgn import Object
1011
from drgn import Program
@@ -159,6 +160,10 @@ def expand_traces(trace: drgn.StackTrace) -> t.List[drgn.StackTrace]:
159160
# We should continue appending traces so long as (a) we can find a pt_regs,
160161
# and (b) the stack pointer for that pt_regs is different than the stack
161162
# pointer for the current stack.
163+
#
164+
# NOTE: aarch64 does not guarantee having SP if we're unwinding with frame
165+
# pointers. However, trace[0] always has SP, because we generally have a
166+
# full register set to start the trace. Thus, this should be safe to test.
162167
while pt_regs is not None and pt_regs.sp.value_() != trace[0].sp:
163168
# Interrupted user address.
164169
if (
@@ -288,9 +293,28 @@ def print_frames(
288293
:param start_idx: Where to start counting the frame indices from
289294
:param indent: How many spaces to indent the output
290295
"""
296+
# On aarch64 without DWARF, it seems we're not guaranteed to have the stack
297+
# pointer, or the frame pointer. Fallback to FP, then NULL, here so we don't
298+
# crash during unwinds.
299+
if prog.platform.arch == Architecture.AARCH64:
300+
301+
def get_sp(frame: drgn.StackFrame) -> int:
302+
try:
303+
return frame.sp
304+
except LookupError:
305+
try:
306+
return frame.register("fp")
307+
except LookupError:
308+
return 0
309+
310+
else:
311+
312+
def get_sp(frame: drgn.StackFrame) -> int:
313+
return frame.sp
314+
291315
pfx = " " * indent
292316
for i, frame in enumerate(trace):
293-
sp = frame.sp # drgn 0.0.22
317+
sp = get_sp(frame)
294318
intr = "!" if frame.interrupted else " "
295319
try:
296320
pc = hex(frame.pc)
@@ -313,7 +337,7 @@ def print_frames(
313337
# with a different stack pointer than the previous. That is: only
314338
# when we reach the frame for a non-inline function. Also, only
315339
# output registers when we have show_vars=True.
316-
if i == len(trace) - 1 or trace[i].sp != trace[i + 1].sp:
340+
if i == len(trace) - 1 or sp != get_sp(trace[i + 1]):
317341
registers = frame.registers()
318342
regnames = list(registers.keys())
319343
# This formats the registers in three columns.

tests/test_bt.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@
88

99
@pytest.mark.skip_vmcore("*-uek4")
1010
def test_bt_smoke(prog, request, debuginfo_type):
11-
if (
12-
debuginfo_type == "ctf"
13-
and prog.platform.arch == drgn.Architecture.AARCH64
14-
):
15-
pytest.xfail("still unsupported for unwinding with aarch64 + CTF")
1611
if prog.flags & drgn.ProgramFlags.IS_LIVE:
1712
thread = prog.thread(1)
1813
else:

0 commit comments

Comments
 (0)