在LLDB中,如何實現函數步/追蹤步?也就是說,直到調用某個函數或從當前函數返回爲止。假設沒有源代碼可用於執行until
。函數步/追蹤步的lldb命令:繼續下一個函數調用或直到返回當前函數
這將相當於執行step-inst
,直到堆棧幀結構改變。
在LLDB中,如何實現函數步/追蹤步?也就是說,直到調用某個函數或從當前函數返回爲止。假設沒有源代碼可用於執行until
。函數步/追蹤步的lldb命令:繼續下一個函數調用或直到返回當前函數
這將相當於執行step-inst
,直到堆棧幀結構改變。
下面是一個LLDB針對性的Python腳本增加了一個「階梯函數」命令。只要調用棧結構發生變化,該命令就會停止。
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__)
用例:
$ 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 0x7fff5fc ; task_self_trap
0x7fff5fc22fc2: movl %eax, 69740(%rip) ; mach_task_self_
(lldb)
Call stack depth changed 4 -> 5
* thread #1: tid = 0x438b0, 0x00007fff5fcdyld`task_self_trap, stop reason = instruction step into
frame #0: 0x00007fff5fcdyld`task_self_trap
dyld`task_self_trap:
-> 0x7fff5fc: 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)
調試器在停止時打印的SB API等效函數是SBThread :: GetStatus(stream)函數。這將打印線程停止原因和帶有源的幀0輸出(使用線程格式和幀格式設置)。 –
Thanks Jim!將融入解決方案。 – Danra
不清楚是否要步驟或只是繼續下去,直到函數退出。 對於後一種情況,如果你能在堆棧上計算出返回地址的位置,你可以把一個讀表就可以了。該RET
指令,最終將離開目前的功能將需要閱讀該位置找到返回地址。
尋找寄信人地址位置可以,如果你有一個有效的幀指針實現自動化。下面是使用gdb
一個例子:
Breakpoint 1, 0x080483e6 in foo()
(gdb) disas foo
Dump of assembler code for function foo:
0x080483e3 <+0>: push %ebp
0x080483e4 <+1>: mov %esp,%ebp
=> 0x080483e6 <+3>: nop
0x080483e7 <+4>: xor %eax,%eax
0x080483e9 <+6>: mov %ebp,%esp
0x080483eb <+8>: pop %ebp
0x080483ec <+9>: ret
0x080483ed <+10>: nop
0x080483ee <+11>: nop
0x080483ef <+12>: nop
End of assembler dump.
(gdb) p/a $ebp+4
$1 = 0xffffd9f8
(gdb) rwatch *(int*)0xffffd9f8
Hardware read watchpoint 2: *(int*)0xffffd9f8
(gdb) c
Continuing.
Hardware read watchpoint 2: *(int*)0xffffd9f8
Value = 134513633
0x080483e1 in main()
(gdb) disas main
Dump of assembler code for function main:
0x080483dc <+0>: call 0x80483e3 <foo>
=> 0x080483e1 <+5>: nop
0x080483e2 <+6>: ret
End of assembler dump.
一旦你的返回地址,你也可以每天使用臨時斷點如果你的函數是不可重入:
(gdb) x/a $ebp+4
0xffffd9f8: 0x80483e1 <main+5>
(gdb) tbreak *0x80483e1
Temporary breakpoint 3 at 0x80483e1
(gdb) c
Continuing.
Temporary breakpoint 3, 0x080483e1 in main()
沒有幀指針,它只是簡單的在函數的開頭找到返回地址。否則,您需要進行一些逆向工程,以查看函數輸入後堆棧指針是如何改變的。
正如我寫道:「我正在尋找一種自動化的方式來執行step-inst,直到棧幀結構發生變化,這是一個函數被調用或當前返回。」所以調試器應該在任何一種情況下都會中斷。建議的解決方案僅適用於後一種情況。 – Danra
在LLDB中,如何才能繼續執行,直到剩下當前的彙編級功能? (沒有源代碼可用於執行)。 我正在尋找執行步驟-INST,直到堆棧幀結構的變化以自動方式,也就是一個函數被調用或當前一個是從返回。
當我檢查,目前當前版本的LLVM沒有這樣的步進模式,這將停止在函數返回或任何函數調用。
功能退出時有「完成」(「退出線程」)停止;還有「nexti」(「線程步驟結束」)單步執行而不訪問所謂的函數。
有LLDB的來源與所有支持模式的列表: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectThread.cpp?revision=194531&view=markup - 檢查文件的命令列表的最末端 - 在CommandObjectMultiwordThread::CommandObjectMultiwordThread
我認爲它可以很容易實現所需的步進模式在LLDB ,因爲存在執行兩個步驟直到返回(CommandObjectThreadStepWithTypeAndScope (... eStepTypeOut, eStepScopeSource)
=>QueueThreadPlanForStepOut
)和函數調用檢測器(對於CommandObjectThreadStepWithTypeAndScope (...eStepTypeTraceOver,eStepScopeInstruction)
=>QueueThreadPlanForStepSingleInstruction
)的組件。 Target/ThreadPlanStepInstruction.cpp中的代碼應該有所幫助。
Danra,是相同的功能存在於GDB?什麼是gdb命令? – osgx
命令'finish'(據說GDB和LLDB都擁有它)你需要什麼? –
@osgx - 我不知道這個gdb命令。 – Danra