[lldb] Add exe_ctx to examples commands (#193347)

Updates example python commands to use `SBExecutionContext` where
beneficial. This was to remove uses of
`GetSelected{Target,Thread,Frame}`.

While making this changes, I also renamed `dict` parameters to
`internal_dict`. In examples, it's better not to shadow `dict`, which is
the builtin type and constructor for dictionaries.

Lastly, I removed a command (`section_ptr_refs`) whose function was
disabled, and non-functional (it calls a non-existent function
`load_dylib`).
This commit is contained in:
Dave Lee
2026-04-21 19:04:26 -07:00
committed by GitHub
parent a680361bcd
commit f4bf729796
9 changed files with 75 additions and 173 deletions

View File

@@ -526,12 +526,12 @@ def type_flags_to_description(
return type_str
def dump_stack_history_entry(options, result, stack_history_entry, idx):
def dump_stack_history_entry(target, options, result, stack_history_entry, idx):
address = int(stack_history_entry.address)
if address:
type_flags = int(stack_history_entry.type_flags)
symbolicator = lldb.utils.symbolication.Symbolicator()
symbolicator.target = lldb.debugger.GetSelectedTarget()
symbolicator.target = target
type_str = type_flags_to_string(type_flags)
result.AppendMessage(
"stack[%u]: addr = 0x%x, type=%s, frames:" % (idx, address, type_str)
@@ -562,8 +562,9 @@ def dump_stack_history_entry(options, result, stack_history_entry, idx):
result.AppendMessage("")
def dump_stack_history_entries(options, result, addr, history):
def dump_stack_history_entries(exe_ctx, options, result, addr, history):
# malloc_stack_entry *get_stack_history_for_address (const void * addr)
target = exe_ctx.target
expr_prefix = """
typedef int kern_return_t;
typedef struct $malloc_stack_entry {
@@ -654,12 +655,7 @@ lldb_info""" % (
addr,
)
frame = (
lldb.debugger.GetSelectedTarget()
.GetProcess()
.GetSelectedThread()
.GetSelectedFrame()
)
frame = exe_ctx.frame
if history:
expr = history_expr
else:
@@ -686,7 +682,9 @@ lldb_info""" % (
i_max = options.max_history
for i in range(i_max):
stack_history_entry = malloc_stack_history.entries[i]
dump_stack_history_entry(options, result, stack_history_entry, i)
dump_stack_history_entry(
target, options, result, stack_history_entry, i
)
if num_stacks > options.max_history:
result.AppendMessage(
'warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks'
@@ -694,7 +692,7 @@ lldb_info""" % (
)
else:
stack_history_entry = lldb.value(expr_sbvalue)
dump_stack_history_entry(options, result, stack_history_entry, 0)
dump_stack_history_entry(target, options, result, stack_history_entry, 0)
else:
result.AppendMessage(
@@ -703,7 +701,7 @@ lldb_info""" % (
def display_match_results(
process,
exe_ctx,
result,
options,
arg_str_description,
@@ -711,12 +709,8 @@ def display_match_results(
print_no_matches,
expr_prefix=None,
):
frame = (
lldb.debugger.GetSelectedTarget()
.GetProcess()
.GetSelectedThread()
.GetSelectedFrame()
)
process = exe_ctx.process
frame = exe_ctx.frame
if not frame:
result.AppendMessage("error: invalid frame")
return 0
@@ -770,7 +764,7 @@ def display_match_results(
options.search_heap = True
options.search_vm_regions = False
if malloc_info_impl(
lldb.debugger, result, options, [hex(malloc_addr + offset)]
exe_ctx, result, options, [hex(malloc_addr + offset)]
):
print_entry = False
options.search_stack = search_stack_old
@@ -897,9 +891,9 @@ def display_match_results(
)
result.AppendMessage(cmd_result.GetOutput())
if options.stack_history:
dump_stack_history_entries(options, result, malloc_addr, 1)
dump_stack_history_entries(exe_ctx, options, result, malloc_addr, 1)
elif options.stack:
dump_stack_history_entries(options, result, malloc_addr, 0)
dump_stack_history_entries(exe_ctx, options, result, malloc_addr, 0)
return i
else:
result.AppendMessage(str(expr_sbvalue.error))
@@ -920,7 +914,7 @@ program."""
return parser
def find_variable(debugger, command, result, dict):
def find_variable(debugger, command, exe_ctx, result, internal_dict):
usage = "usage: %prog [options] <ADDR> [ADDR ...]"
description = (
"""Searches for a local variable in all frames that contains a hex ADDR."""
@@ -942,7 +936,7 @@ def find_variable(debugger, command, result, dict):
except:
return
process = debugger.GetSelectedTarget().GetProcess()
process = exe_ctx.process
if not process:
result.AppendMessage("error: invalid process")
return
@@ -962,7 +956,7 @@ def find_variable(debugger, command, result, dict):
break
def ptr_refs(debugger, command, result, dict):
def ptr_refs(debugger, command, exe_ctx, result, internal_dict):
command_args = shlex.split(command)
parser = get_ptr_refs_options()
try:
@@ -970,11 +964,11 @@ def ptr_refs(debugger, command, result, dict):
except:
return
process = debugger.GetSelectedTarget().GetProcess()
process = exe_ctx.process
if not process:
result.AppendMessage("error: invalid process")
return
frame = process.GetSelectedThread().GetSelectedFrame()
frame = exe_ctx.frame
if not frame:
result.AppendMessage("error: invalid frame")
return
@@ -1044,7 +1038,7 @@ baton.matches"""
)
arg_str_description = "malloc block containing pointer %s" % ptr_expr
display_match_results(
process, result, options, arg_str_description, expr, True, expr_prefix
exe_ctx, result, options, arg_str_description, expr, True, expr_prefix
)
else:
result.AppendMessage("error: no pointer arguments were given")
@@ -1064,7 +1058,7 @@ program."""
return parser
def cstr_refs(debugger, command, result, dict):
def cstr_refs(debugger, command, exe_ctx, result, internal_dict):
command_args = shlex.split(command)
parser = get_cstr_refs_options()
try:
@@ -1072,11 +1066,11 @@ def cstr_refs(debugger, command, result, dict):
except:
return
process = debugger.GetSelectedTarget().GetProcess()
process = exe_ctx.process
if not process:
result.AppendMessage("error: invalid process")
return
frame = process.GetSelectedThread().GetSelectedFrame()
frame = exe_ctx.frame
if not frame:
result.AppendMessage("error: invalid frame")
return
@@ -1148,7 +1142,7 @@ baton.matches"""
)
arg_str_description = 'malloc block containing "%s"' % cstr
display_match_results(
process, result, options, arg_str_description, expr, True, expr_prefix
exe_ctx, result, options, arg_str_description, expr, True, expr_prefix
)
else:
result.AppendMessage("error: command takes one or more C string arguments")
@@ -1168,25 +1162,25 @@ information in the program."""
return parser
def malloc_info(debugger, command, result, dict):
def malloc_info(debugger, command, exe_ctx, result, internal_dict):
command_args = shlex.split(command)
parser = get_malloc_info_options()
try:
(options, args) = parser.parse_args(command_args)
except:
return
malloc_info_impl(debugger, result, options, args)
malloc_info_impl(exe_ctx, result, options, args)
def malloc_info_impl(debugger, result, options, args):
def malloc_info_impl(exe_ctx, result, options, args):
# We are specifically looking for something on the heap only
options.type = "malloc_info"
process = debugger.GetSelectedTarget().GetProcess()
process = exe_ctx.process
if not process:
result.AppendMessage("error: invalid process")
return
frame = process.GetSelectedThread().GetSelectedFrame()
frame = exe_ctx.frame
if not frame:
result.AppendMessage("error: invalid frame")
return
@@ -1233,7 +1227,7 @@ baton.matches[1].addr = 0;"""
)
arg_str_description = "malloc block that contains %s" % ptr_expr
total_matches += display_match_results(
process, result, options, arg_str_description, expr, True, expr_prefix
exe_ctx, result, options, arg_str_description, expr, True, expr_prefix
)
return total_matches
else:
@@ -1345,84 +1339,6 @@ segments[%(index)u].size = 0x%(size)x;"""
return ""
def section_ptr_refs(debugger, command, result, dict):
command_args = shlex.split(command)
usage = "usage: %prog [options] <EXPR> [EXPR ...]"
description = """Searches section contents for pointer values in darwin user space programs."""
parser = optparse.OptionParser(
description=description, prog="section_ptr_refs", usage=usage
)
add_common_options(parser)
parser.add_option(
"--section",
action="append",
type="string",
dest="section_names",
help="section name to search",
default=list(),
)
try:
(options, args) = parser.parse_args(command_args)
except:
return
options.type = "pointer"
sections = list()
section_modules = list()
if not options.section_names:
result.AppendMessage(
"error: at least one section must be specified with the --section option"
)
return
target = debugger.GetSelectedTarget()
for module in target.modules:
for section_name in options.section_names:
section = module.section[section_name]
if section:
sections.append(section)
section_modules.append(module)
if sections:
dylid_load_err = load_dylib()
if dylid_load_err:
result.AppendMessage(dylid_load_err)
return
frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
for expr_str in args:
for idx, section in enumerate(sections):
expr = "find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)" % (
section.addr.load_addr,
section.size,
expr_str,
)
arg_str_description = 'section %s.%s containing "%s"' % (
section_modules[idx].file.fullpath,
section.name,
expr_str,
)
num_matches = display_match_results(
target.GetProcess(),
result,
options,
arg_str_description,
expr,
False,
)
if num_matches:
if num_matches < options.max_matches:
options.max_matches = options.max_matches - num_matches
else:
options.max_matches = 0
if options.max_matches == 0:
return
else:
result.AppendMessage(
"error: no sections were found that match any of %s"
% (", ".join(options.section_names))
)
def get_objc_refs_options():
usage = "usage: %prog [options] <CLASS> [CLASS ...]"
description = """Searches all allocations on the heap for instances of
@@ -1438,7 +1354,7 @@ program."""
return parser
def objc_refs(debugger, command, result, dict):
def objc_refs(debugger, command, exe_ctx, result, internal_dict):
command_args = shlex.split(command)
parser = get_objc_refs_options()
try:
@@ -1446,11 +1362,11 @@ def objc_refs(debugger, command, result, dict):
except:
return
process = debugger.GetSelectedTarget().GetProcess()
process = exe_ctx.process
if not process:
result.AppendMessage("error: invalid process")
return
frame = process.GetSelectedThread().GetSelectedFrame()
frame = exe_ctx.frame
if not frame:
result.AppendMessage("error: invalid frame")
return
@@ -1592,7 +1508,7 @@ int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Clas
)
arg_str_description = "objective C classes with isa 0x%x" % isa
display_match_results(
process,
exe_ctx,
result,
options,
arg_str_description,
@@ -1635,7 +1551,6 @@ def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand(
"command script add -o -f %s.find_variable find_variable" % __name__
)
# debugger.HandleCommand('command script add -o -f %s.section_ptr_refs section_ptr_refs' % package_name)
debugger.HandleCommand("command script add -o -f %s.objc_refs objc_refs" % __name__)
print(
'"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.'

View File

@@ -36,7 +36,7 @@ def read_memory(process, location, size):
return data
def diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict):
def diagnose_nsstring_Command_Impl(debugger, command, exe_ctx, result, internal_dict):
"""
A command to diagnose the LLDB NSString data formatter
invoke as
@@ -44,10 +44,9 @@ def diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict):
e.g.
(lldb) diagnose-nsstring @"Hello world"
"""
target = debugger.GetSelectedTarget()
process = target.GetProcess()
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
target = exe_ctx.target
process = exe_ctx.process
frame = exe_ctx.thread
if not target.IsValid() or not process.IsValid():
return "unable to get target/process - cannot proceed"
options = lldb.SBExpressionOptions()
@@ -214,7 +213,3 @@ def __lldb_init_module(debugger, internal_dict):
print(
'The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.'
)
__lldb_init_module(lldb.debugger, None)
__lldb_init_module = None

View File

@@ -83,10 +83,10 @@ def backtrace_print_frame(target, frame_num, addr, fp):
# seen and the second element is a list of addresses seen during the backtrace.
def simple_backtrace(debugger):
target = debugger.GetSelectedTarget()
process = target.GetProcess()
cur_thread = process.GetSelectedThread()
def simple_backtrace(exe_ctx):
target = exe_ctx.target
process = exe_ctx.process
cur_thread = exe_ctx.thread
initial_fp = cur_thread.GetFrameAtIndex(0).GetFP()
@@ -178,7 +178,7 @@ def print_stack_frame(process, fp):
return
def diagnose_unwind(debugger, command, result, dict):
def diagnose_unwind(debugger, command, exe_ctx, result, internal_dict):
"""
Gather diagnostic information to help debug incorrect unwind (backtrace)
behavior in lldb. When there is a backtrace that doesn't look
@@ -193,11 +193,11 @@ def diagnose_unwind(debugger, command, result, dict):
(options, args) = parser.parse_args(command_args)
except:
return
target = debugger.GetSelectedTarget()
target = exe_ctx.target
if target:
process = target.GetProcess()
process = exe_ctx.process
if process:
thread = process.GetSelectedThread()
thread = exe_ctx.thread
if thread:
lldb_versions_match = re.search(
r"[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?",

View File

@@ -124,7 +124,7 @@ Command Options Usage:
<location-id> serves to disambiguate when multiple locations could be meant."""
def jump(debugger, command, result, internal_dict):
def jump(debugger, command, exe_ctx, result, internal_dict):
if command == "":
result.AppendMessage(usage_string())
@@ -134,22 +134,20 @@ def jump(debugger, command, result, internal_dict):
result.AppendMessage("Invalid debugger!")
return
target = debugger.GetSelectedTarget()
target = exe_ctx.target
if not target.IsValid():
result.AppendMessage("jump requires a valid target.")
return
process = target.GetProcess()
if not process.IsValid():
if not exe_ctx.process.IsValid():
result.AppendMessage("jump requires a valid process.")
return
thread = process.GetSelectedThread()
if not thread.IsValid():
if not exe_ctx.thread.IsValid():
result.AppendMessage("jump requires a valid thread.")
return
frame = thread.GetSelectedFrame()
frame = exe_ctx.frame
if not frame.IsValid():
result.AppendMessage("jump requires a valid frame.")
return

View File

@@ -544,22 +544,19 @@ class DelegateTree(ttk.Frame):
@lldb.command("tk-variables")
def tk_variable_display(debugger, command, result, dict):
def tk_variable_display(debugger, command, exe_ctx, result, internal_dict):
# needed for tree creation in TK library as it uses sys.argv...
sys.argv = ["tk-variables"]
target = debugger.GetSelectedTarget()
if not target:
if not exe_ctx.target:
print("invalid target", file=result)
return
process = target.GetProcess()
if not process:
if not exe_ctx.process:
print("invalid process", file=result)
return
thread = process.GetSelectedThread()
if not thread:
if not exe_ctx.thread:
print("invalid thread", file=result)
return
frame = thread.GetSelectedFrame()
frame = exe_ctx.frame
if not frame:
print("invalid frame", file=result)
return
@@ -578,14 +575,13 @@ def tk_variable_display(debugger, command, result, dict):
@lldb.command("tk-process")
def tk_process_display(debugger, command, result, dict):
def tk_process_display(debugger, command, exe_ctx, result, internal_dict):
# needed for tree creation in TK library as it uses sys.argv...
sys.argv = ["tk-process"]
target = debugger.GetSelectedTarget()
if not target:
if not exe_ctx.target:
print("invalid target", file=result)
return
process = target.GetProcess()
process = exe_ctx.process
if not process:
print("invalid process", file=result)
return
@@ -603,10 +599,10 @@ def tk_process_display(debugger, command, result, dict):
@lldb.command("tk-target")
def tk_target_display(debugger, command, result, dict):
def tk_target_display(debugger, command, exe_ctx, result, internal_dict):
# needed for tree creation in TK library as it uses sys.argv...
sys.argv = ["tk-target"]
target = debugger.GetSelectedTarget()
target = exe_ctx.target
if not target:
print("invalid target", file=result)
return

View File

@@ -189,7 +189,7 @@ can be specified multiple times to look for longer streams of data.
return parser
def memfind_command(debugger, command, result, dict):
def memfind_command(debugger, command, exe_ctx, result, internal_dict):
# Use the Shell Lexer to properly parse up command options just like a
# shell would
command_args = shlex.split(command)
@@ -203,7 +203,7 @@ def memfind_command(debugger, command, result, dict):
# result.SetStatus (lldb.eReturnStatusFailed)
# print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string
# return
memfind(debugger.GetSelectedTarget(), options, args, result)
memfind(exe_ctx.target, options, args, result)
def print_error(str, show_usage, result):

View File

@@ -12,10 +12,10 @@ def dump_module_sources(module, result):
print(" %s" % (compile_unit.file), file=result)
def info_sources(debugger, command, result, dict):
description = """This command will dump all compile units in any modules that are listed as arguments, or for all modules if no arguments are supplied."""
def info_sources(debugger, command, exe_ctx, result, internal_dict):
"""This command will dump all compile units in any modules that are listed as arguments, or for all modules if no arguments are supplied."""
module_names = shlex.split(command)
target = debugger.GetSelectedTarget()
target = exe_ctx.target
if module_names:
for module_name in module_names:
dump_module_sources(target.module[module_name], result)
@@ -24,7 +24,7 @@ def info_sources(debugger, command, result, dict):
dump_module_sources(module, result)
def __lldb_init_module(debugger, dict):
def __lldb_init_module(debugger, internal_dict):
# Add any commands contained in this module to LLDB
debugger.HandleCommand("command script add -o -f sources.info_sources info_sources")
print(

View File

@@ -4,7 +4,7 @@ import optparse
import shlex
def stack_frames(debugger, command, result, dict):
def stack_frames(debugger, command, exe_ctx, result, internal_dict):
command_args = shlex.split(command)
usage = "usage: %prog [options] <PATH> [PATH ...]"
description = """This command will enumerate all stack frames, print the stack size for each, and print an aggregation of which functions have the largest stack frame sizes at the end."""
@@ -22,11 +22,8 @@ def stack_frames(debugger, command, result, dict):
except:
return
target = debugger.GetSelectedTarget()
process = target.GetProcess()
frame_info = {}
for thread in process:
for thread in exe_ctx.process:
last_frame = None
print("thread %u" % (thread.id))
for frame in thread.frames:

View File

@@ -9,6 +9,7 @@
# (lldb) command script import /path/to/cmdtemplate.py
# ----------------------------------------------------------------------
import lldb
import platform
import os
import re
@@ -299,7 +300,7 @@ def verify_type_recursive(
return (prev_end_offset, padding)
def check_padding_command(debugger, command, result, dict):
def check_padding_command(debugger, command, exe_ctx, result, internal_dict):
# Use the Shell Lexer to properly parse up command options just like a
# shell would
command_args = shlex.split(command)
@@ -313,11 +314,11 @@ def check_padding_command(debugger, command, result, dict):
# returning a string is the same as returning an error whose
# description is the string
return "option parsing failed"
verify_types(debugger.GetSelectedTarget(), options)
verify_types(exe_ctx.target, options)
@lldb.command("parse_all_struct_class_types")
def parse_all_struct_class_types(debugger, command, result, dict):
def parse_all_struct_class_types(debugger, command, result, internal_dict):
command_args = shlex.split(command)
for f in command_args:
error = lldb.SBError()
@@ -343,7 +344,7 @@ def verify_types(target, options):
modules.append(module)
else:
for module_name in options.modules:
module = lldb.target.module[module_name]
module = target.module[module_name]
if module:
modules.append(module)