2014-02-25 75 views
6

在LLDB中,如何實現函數步/追蹤步?也就是說,直到調用某個函數或從當前函數返回爲止。假設沒有源代碼可用於執行until函數步/追蹤步的lldb命令:繼續下一個函數調用或直到返回當前函數

這將相當於執行step-inst,直到堆棧幀結構改變。

+0

Danra,是相同的功能存在於GDB?什麼是gdb命令? – osgx

+0

命令'finish'(據說GDB和LLDB都擁有它)你需要什麼? –

+0

@osgx - 我不知道這個gdb命令。 – Danra

回答

6

下面是一個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) 
+0

調試器在停止時打印的SB API等效函數是SBThread :: GetStatus(stream)函數。這將打印線程停止原因和帶有源的幀0輸出(使用線程格式和幀格式設置)。 –

+0

Thanks Jim!將融入解決方案。 – Danra

1

不清楚是否要步驟或只是繼續下去,直到函數退出。 對於後一種情況,如果你能在堆棧上計算出返回地址的位置,你可以把一個讀表就可以了。該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() 

沒有幀指針,它只是簡單的在函數的開頭找到返回地址。否則,您需要進行一些逆向工程,以查看函數輸入後堆棧指針是如何改變的。

+0

正如我寫道:「我正在尋找一種自動化的方式來執行step-inst,直到棧幀結構發生變化,這是一個函數被調用或當前返回。」所以調試器應該在任何一種情況下都會中斷。建議的解決方案僅適用於後一種情況。 – Danra

3

在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中的代碼應該有所幫助。

+0

希望有一個解決方案,它不會要求我實現它:) – Danra

+0

在LLDB中不需要支持所需的ThreadPlan,所以要麼應該在LLDB中實現新的Plan,要麼嘗試在單步指令步驟之前執行一些腳本檢測「ret」和「call」指令。 – osgx