Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for function args + local variables #322

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions sdb/commands/linux/stacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,134 @@ def pretty_print(self, objs: Iterable[drgn.Object]) -> None:
def no_input(self) -> Iterable[drgn.Object]:
self.validate_context()
yield from [sdb.get_prog().crashed_thread().object]

class ArgStack(sdb.Locator, sdb.PrettyPrinter):
"""
Print a stack with arguments.

EXAMPLES
sdb> echo 0xffff96609b7d4680 | stack_args
(struct task_struct *)0xffff96609b7d4680
#0 struct rq *context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next, struct rq_flags *rf)
rf = (struct rq_flags *)<absent>
next = (struct task_struct *)<absent>
prev = (struct task_struct *)<absent>
rq = (struct rq *)<absent>
#1 void __schedule(bool preempt)
preempt = (bool)<absent>
prev = (struct task_struct *)0xffff96609b7d4680
next = (struct task_struct *)<absent>
switch_count = (unsigned long *)<absent>
rf = (struct rq_flags){ 0, {}, 4 }
rq = (struct rq *)<absent>
cpu = (int)<absent>
#2 void schedule()
tsk = (struct task_struct *)<absent>
...
"""

names = ["stack_args", "locals"]
input_type = "struct task_struct *"
output_type = "struct task_struct *"
load_on = [sdb.Kernel()]

def __init__(self,
args: Optional[List[str]] = None,
name: str = "_") -> None:
super().__init__(args, name)

@classmethod
def _init_parser(cls, name: str) -> argparse.ArgumentParser:
parser = super()._init_parser(name)
parser.add_argument(
"-r",
"--registers",
action="store_true",
help="print register values in output")
parser.add_argument(
"-f",
"--frame",
help="print detailed info for locals in a specific FRAME")
return parser

def validate_context(self) -> None:
if sdb.get_target_flags() & drgn.ProgramFlags.IS_LINUX_KERNEL == 0:
raise sdb.CommandError(self.name,
"command only works for linux kernel")

def pretty_print(self, objs: Iterable[drgn.Object]) -> None:
# self.validate_context()
for thread in objs:
if self.args.frame:
try:
frame_id = int(self.args.frame)
except ValueError:
raise sdb.CommandError(self.name,
f"frame id ({self.args.frame}) must be an integer value")

trace = sdb.get_prog().stack_trace(thread)
try:
frame = trace[frame_id]
except IndexError:
print(f"frame #{frame_id} is not present in stack {hex(thread.value_())}")
continue
print(frame)
print()
for local in frame.locals():
print(f"{local} = {(frame[local])}")
if self.args.registers:
print()
print(frame.registers())
continue

print(thread.format_(dereference=False))

frame_number = 0
for frame in sdb.get_prog().stack_trace(thread):
frame_info = f"#{frame_number} "
frame_number += 1

name = frame.name
if name is None:
try:
name = frame.symbol().name
except LookupError:
name = hex(frame.pc)
frame_info += f"{name}"
else:
func = sdb.get_prog().function(name)
func_type = func.type_
func_ret = func_type.type
if func_ret.kind == drgn.TypeKind.POINTER:
frame_info += f"{func_ret}{name}("
elif func_ret.kind == drgn.TypeKind.TYPEDEF:
frame_info += f"{func_ret.name} {name}("
else:
frame_info += f"{func_ret} {name}("
first = True
for parm in func_type.parameters:
if not first:
frame_info += f", "
parm_type = parm.type
if parm_type.kind == drgn.TypeKind.POINTER:
frame_info += f"{parm_type}{parm.name}"
elif parm_type.kind == drgn.TypeKind.TYPEDEF:
frame_info += f"{parm_type.name} {parm.name}"
elif parm_type.kind == drgn.TypeKind.ENUM:
frame_info += f"{parm_type.tag} {parm.name}"
else:
frame_info += f"{parm_type} {parm.name}"
first = False
frame_info += f")"
# The above is a lot of formatting work, we could just use:
# frame_info += f"{name} {sdb.get_prog().function(name)}"
print(frame_info)

local_symbols = frame.locals()
for local in local_symbols:
mmaybee marked this conversation as resolved.
Show resolved Hide resolved
print(f"{'':6s}{local} = {(frame[local].format_(dereference=False, member_type_names=False, member_names=False, members_same_line=True))}")
print("\n")

def no_input(self) -> Iterable[drgn.Object]:
# self.validate_context()
yield from for_each_task(sdb.get_prog())