Here lldb is for a python script that adds the step-function command. The command stops whenever the structure of the call stack changes.
step_func.py
import lldb def step_func(debugger, command, result, internal_dict): thread = debugger.GetSelectedTarget().GetProcess().GetSelectedThread() start_num_frames = thread.GetNumFrames() if start_num_frames == 0: return while True: thread.StepInstruction(0) if thread.GetNumFrames() != start_num_frames: stream = lldb.SBStream() thread.GetStatus(stream) description = stream.GetData() print >>result, "Call stack depth changed %d -> %d" % (start_num_frames, thread.GetNumFrames()) print >>result, description, break def __lldb_init_module (debugger, dict): debugger.HandleCommand('command script add -f %s.step_func sf' % __name__)
Usage example:
$ lldb /bin/ls Current executable set to '/bin/ls' (x86_64). (lldb) command script import step_func (lldb) process launch --stop-at-entry Process 12944 launched: '/bin/ls' (x86_64) Process 12944 stopped * thread #1: tid = 0x438b0, 0x00007fff5fc01028 dyld`_dyld_start, stop reason = signal SIGSTOP frame #0: 0x00007fff5fc01028 dyld`_dyld_start dyld`_dyld_start: -> 0x7fff5fc01028: popq %rdi 0x7fff5fc01029: pushq $0 0x7fff5fc0102b: movq %rsp, %rbp 0x7fff5fc0102e: andq $-16, %rsp (lldb) sf Call stack depth changed 1 -> 2 * thread #1: tid = 0x438b0, 0x00007fff5fc0109e dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*), stop reason = instruction step into frame #0: 0x00007fff5fc0109e dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*): -> 0x7fff5fc0109e: pushq %rbp 0x7fff5fc0109f: movq %rsp, %rbp 0x7fff5fc010a2: pushq %r15 0x7fff5fc010a4: pushq %r14 (lldb) Call stack depth changed 2 -> 3 * thread #1: tid = 0x438b0, 0x00007fff5fc22f9b dyld`mach_init, stop reason = instruction step into frame #0: 0x00007fff5fc22f9b dyld`mach_init dyld`mach_init: -> 0x7fff5fc22f9b: pushq %rbp 0x7fff5fc22f9c: movq %rsp, %rbp 0x7fff5fc22f9f: movb 326075(%rip), %al ; mach_init.mach_init_inited 0x7fff5fc22fa5: testb %al, %al (lldb) Call stack depth changed 3 -> 4 * thread #1: tid = 0x438b0, 0x00007fff5fc22fb9 dyld`mach_init_doit, stop reason = instruction step into frame #0: 0x00007fff5fc22fb9 dyld`mach_init_doit dyld`mach_init_doit: -> 0x7fff5fc22fb9: pushq %rbp 0x7fff5fc22fba: movq %rsp, %rbp 0x7fff5fc22fbd: callq 0x7fff5fc23210 ; task_self_trap 0x7fff5fc22fc2: movl %eax, 69740(%rip) ; mach_task_self_ (lldb) Call stack depth changed 4 -> 5 * thread #1: tid = 0x438b0, 0x00007fff5fc23210 dyld`task_self_trap, stop reason = instruction step into frame #0: 0x00007fff5fc23210 dyld`task_self_trap dyld`task_self_trap: -> 0x7fff5fc23210: movq %rcx, %r10 0x7fff5fc23213: movl $16777244, %eax 0x7fff5fc23218: syscall 0x7fff5fc2321a: ret (lldb) Call stack depth changed 5 -> 4 * thread #1: tid = 0x438b0, 0x00007fff5fc22fc2 dyld`mach_init_doit + 9, stop reason = instruction step into frame #0: 0x00007fff5fc22fc2 dyld`mach_init_doit + 9 dyld`mach_init_doit + 9: -> 0x7fff5fc22fc2: movl %eax, 69740(%rip) ; mach_task_self_ 0x7fff5fc22fc8: callq 0x7fff5fc231f8 ; mach_reply_port 0x7fff5fc22fcd: leaq 69724(%rip), %rcx ; _task_reply_port 0x7fff5fc22fd4: movl %eax, (%rcx) (lldb)