mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
LLDB plugin for backtraces.
This commit is contained in:
parent
ed00211c05
commit
4aa402d6df
68
contrib/lldb_bt.py
Normal file
68
contrib/lldb_bt.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import lldb
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
debugger.HandleCommand('command script add -f lldb_bt.sbcl_backtrace sbt')
|
||||
print("Custom backtrace command 'sbt' loaded.")
|
||||
|
||||
def sbcl_backtrace(debugger, command, result, internal_dict):
|
||||
thread = debugger.GetSelectedTarget().GetProcess().GetSelectedThread()
|
||||
debugger.GetCommandInterpreter().HandleCommand("settings set show-progress false", lldb.SBCommandReturnObject())
|
||||
if thread:
|
||||
for frame in thread:
|
||||
result.AppendMessage(format_frame(frame, internal_dict))
|
||||
|
||||
def format_frame(frame, internal_dict):
|
||||
frame_number = frame.GetFrameID()
|
||||
pc_address = frame.GetPCAddress()
|
||||
target = lldb.debugger.GetSelectedTarget()
|
||||
pc_load_addr = pc_address.GetLoadAddress(target)
|
||||
|
||||
module_name = frame.module.GetFileSpec() if frame.module else "???"
|
||||
func = frame.GetFunction()
|
||||
|
||||
if func:
|
||||
func_name = func.GetName()
|
||||
output = f"frame #{frame_number}: {pc_load_addr:#016x} {func_name} in `{module_name}`"
|
||||
return output
|
||||
|
||||
symbol = frame.GetSymbol()
|
||||
if symbol.IsValid():
|
||||
func_name = symbol.GetName()
|
||||
return f"frame #{frame_number}: {pc_load_addr:#016x} {func_name} in `{module_name}`"
|
||||
|
||||
custom_name = None
|
||||
options = lldb.SBExpressionOptions()
|
||||
options.SetUnwindOnError(False)
|
||||
fp = frame.GetFP()
|
||||
|
||||
expression = f"""
|
||||
char buf[1024];
|
||||
FILE* stream = (FILE*)fmemopen(buf, sizeof(buf), "w");
|
||||
(void)print_backtrace_frame((char*){pc_load_addr}, (void*){fp}, -1, stream);
|
||||
(int)fclose(stream);
|
||||
(char*)strndup(buf, sizeof(buf));
|
||||
"""
|
||||
result = target.EvaluateExpression(expression, options)
|
||||
|
||||
if result.GetError().Fail():
|
||||
print(f"ERROR: Call to print_backtrace_frame failed: {result.GetError().GetCString()}")
|
||||
|
||||
if result.IsValid() and result.GetValueAsSigned() != 0:
|
||||
string_address = result.GetValueAsUnsigned()
|
||||
error = lldb.SBError()
|
||||
memory_bytes = target.ReadMemory(lldb.SBAddress(string_address, target), 1024, error)
|
||||
|
||||
if error.Success():
|
||||
null_pos = memory_bytes.find(b'\x00')
|
||||
if null_pos >= 0:
|
||||
raw_bytes = memory_bytes[:null_pos]
|
||||
else:
|
||||
raw_bytes = memory_bytes
|
||||
custom_name = raw_bytes.decode('utf-8', 'replace')
|
||||
free_expr = f"(void)free((void*){string_address})"
|
||||
target.EvaluateExpression(free_expr, options)
|
||||
|
||||
if custom_name:
|
||||
return f"frame #{frame_number}: {pc_load_addr:#016x} {custom_name}"
|
||||
else:
|
||||
return f"frame #{frame_number}: {pc_load_addr:#016x} in `{module_name}`"
|
||||
|
|
@ -347,6 +347,51 @@ lisp_backtrace(int nframes)
|
|||
print_lisp_backtrace(nframes, stdout);
|
||||
}
|
||||
|
||||
void print_backtrace_frame(char *pc, void *fp, int i, FILE *f) {
|
||||
if (i != -1) {
|
||||
#ifdef BACKTRACE_SHOW_FRAME_SIZE
|
||||
// This display is a little confusing. It's the size of the frame that this
|
||||
// frame will return to.
|
||||
fprintf(f, "%4d: fp=%p [%5x] pc=%p ", i, fp, (int)(*(char**)fp-(char*)fp), pc);
|
||||
#else
|
||||
fprintf(f, "%4d: fp=%p pc=%p ", i, fp, pc);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct code *code = (void*)component_ptr_from_pc(pc);
|
||||
if (code) {
|
||||
lispobj name = debug_function_name_from_pc(code, pc);
|
||||
if (name)
|
||||
print_entry_name(barrier_load(&name), f);
|
||||
else
|
||||
fprintf(f, "{code_serialno=%x}", code_serialno(code));
|
||||
} else if (gc_managed_heap_space_p((uword_t)pc)) {
|
||||
#ifdef LISP_FEATURE_X86
|
||||
// can't actually have a PC inside a random object, it's got to be a frame
|
||||
// that didn't set up the pointer chain, quite possibly a signal frame such as:
|
||||
// 7: fp=0xd78c8460 pc=0xf7fb51b0 Foreign function __kernel_rt_sigreturn
|
||||
// 8: fp=0xd78c8478 pc=0xd9c43159 (bad PC)
|
||||
// 9: fp=0xd78c84ec pc=0xd849a17e (FLET SB-C::DO-1-USE :IN SB-C::TENSION-IF-IF-1)
|
||||
// where, if you print the PC actually from the context, line 8 would be 0xd823ea78.
|
||||
fprintf(f, "(bad PC)");
|
||||
#else
|
||||
// It could be a generic-function with self-contained tramponline code,
|
||||
// or the executable JMP instruction in an fdefn.
|
||||
fprintf(f, "(unknown lisp object)");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef LISP_FEATURE_OS_PROVIDES_DLADDR
|
||||
Dl_info info;
|
||||
if (dladdr(pc, &info)) {
|
||||
fprintf(f, "Foreign function %s", info.dli_sname);
|
||||
} else
|
||||
#endif
|
||||
fprintf(f, "Foreign function");
|
||||
}
|
||||
|
||||
putc('\n', f);
|
||||
}
|
||||
|
||||
#if !(defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64))
|
||||
|
||||
/* KLUDGE: Sigh ... I know what the call frame looks like and it had
|
||||
|
|
@ -660,48 +705,6 @@ describe_thread_state(void)
|
|||
printf("Pending handler = %p\n", data->pending_handler);
|
||||
}
|
||||
|
||||
static void print_backtrace_frame(char *pc, void *fp, int i, FILE *f) {
|
||||
#ifdef BACKTRACE_SHOW_FRAME_SIZE
|
||||
// This display is a little confusing. It's the size of the frame that this
|
||||
// frame will return to.
|
||||
fprintf(f, "%4d: fp=%p [%5x] pc=%p ", i, fp, (int)(*(char**)fp-(char*)fp), pc);
|
||||
#else
|
||||
fprintf(f, "%4d: fp=%p pc=%p ", i, fp, pc);
|
||||
#endif
|
||||
struct code *code = (void*)component_ptr_from_pc(pc);
|
||||
if (code) {
|
||||
lispobj name = debug_function_name_from_pc(code, pc);
|
||||
if (name)
|
||||
print_entry_name(barrier_load(&name), f);
|
||||
else
|
||||
fprintf(f, "{code_serialno=%x}", code_serialno(code));
|
||||
} else if (gc_managed_heap_space_p((uword_t)pc)) {
|
||||
#ifdef LISP_FEATURE_X86
|
||||
// can't actually have a PC inside a random object, it's got to be a frame
|
||||
// that didn't set up the pointer chain, quite possibly a signal frame such as:
|
||||
// 7: fp=0xd78c8460 pc=0xf7fb51b0 Foreign function __kernel_rt_sigreturn
|
||||
// 8: fp=0xd78c8478 pc=0xd9c43159 (bad PC)
|
||||
// 9: fp=0xd78c84ec pc=0xd849a17e (FLET SB-C::DO-1-USE :IN SB-C::TENSION-IF-IF-1)
|
||||
// where, if you print the PC actually from the context, line 8 would be 0xd823ea78.
|
||||
fprintf(f, "(bad PC)");
|
||||
#else
|
||||
// It could be a generic-function with self-contained tramponline code,
|
||||
// or the executable JMP instruction in an fdefn.
|
||||
fprintf(f, "(unknown lisp object)");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef LISP_FEATURE_OS_PROVIDES_DLADDR
|
||||
Dl_info info;
|
||||
if (dladdr(pc, &info)) {
|
||||
fprintf(f, "Foreign function %s", info.dli_sname);
|
||||
} else
|
||||
#endif
|
||||
fprintf(f, "Foreign function");
|
||||
}
|
||||
|
||||
putc('\n', f);
|
||||
}
|
||||
|
||||
/* This function has been split from lisp_backtrace() to enable Lisp
|
||||
* backtraces from gdb with call backtrace_from_fp(...). Useful for
|
||||
* example when debugging threading deadlocks. (SBCL internals call
|
||||
|
|
|
|||
Loading…
Reference in a new issue