前言
LLDB是搭配LLVM的一個調(diào)試工具纬黎,就如同GDB之于GCC。我們?nèi)粘i_發(fā)中幾乎時時刻刻和它在打交道肄渗。下面簡單地從多個方面來了解以及使用LLDB:
- 1镇眷、BreakPoint和WatchPoint;
- 2啼肩、程序控制;
- 3程储、線程狀態(tài)和棧幀狀態(tài)萝挤;
其中BreakPoint也就是我們常說的斷點,它是我們?nèi)粘U{(diào)試中使用過最多的能力,其作用自不必多言;WatchPoint相對來說用的就很少了垦梆,我們可以稱之為“觀察點”他宛。我們可以通過添加觀察點來關注我們指定變量的變化船侧。
程序控制就是指的在我們調(diào)試過程中的單步執(zhí)行、繼續(xù)執(zhí)行厅各、進入子過程內(nèi)部以及從子過程內(nèi)部跳出镜撩。
線程狀態(tài)和棧幀狀態(tài)則是查看當前線程的執(zhí)行狀態(tài),包括過程調(diào)用队塘、變量信息以及寄存器狀態(tài)等等袁梗。
下面提及的所有命令,如果不清楚如何使用都可以使用 help xxx 形式來進行查詢:
help breakpoint
help watchpoint
help memory
...
程序啟動和控制相關
程序啟動大致可以分為兩種憔古,第一種是在啟動lldb的時候指定要debug的程序遮怜;第二種是在啟動lldb之后,再明確指定要啟動的程序:
/// 第一種啟動lldb的時候指定待調(diào)試的程序
? lldb lldb_demo
/// 第二種先啟動lldb投放,然后指定要啟動的調(diào)試程序
? lldb
(lldb) run /Users/.../db_demo 1 2 3
/// 第三種創(chuàng)建不同的target奈泪,在lldb里面隨意的啟動
? lldb
(lldb) target create /Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo
Current executable set to '/Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo' (x86_64).
(lldb) target list
Current targets:
* target #0: /Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo ( arch=x86_64-apple-macosx11.5.0, platform=host )
(lldb) r 0
Process 30082 launched: '/Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo' (x86_64)
一個小提示:我們在target create的時候需要知道某一個路徑的話,就需要lldb里面執(zhí)行shell相關的指令灸芳。可以使用如下方式:
platform shell xxxx拜姿。當然也可以使用command alias為platform shell取一個別名烙样。
下表中的內(nèi)容簡單的列舉了一下相關的指令:
類別 | 描述 | GDB | LLDB | 備注 |
---|---|---|---|---|
程序啟動 | 啟動指定進程 | run/r <args> xxx | process launch -- <args>、run/r <args> xxx | args啟動過程中需要的參數(shù),對應main函數(shù)中的參數(shù) |
展示傳入的參數(shù) | show args | settings show target.run-args | 展示我們設置的參數(shù) | |
設置環(huán)境變量 | set env DEBUG 1 | env DEBUG=1 | 設置環(huán)境變量 | |
程序控制 | 源碼級別進入執(zhí)行 | step/s | thread step-in | 單步執(zhí)行到指定過程的內(nèi)部(比如子函數(shù)調(diào)用時,進入子函數(shù)內(nèi)部) |
指令級別進入執(zhí)行 | stepi/si | thread step-inst | ||
源碼級別跳過執(zhí)行 | next/n | thread step-over | ||
指令級別跳過執(zhí)行 | nexti/ni | hread step-inst-over | ||
結束執(zhí)行 | finish | thread step-out |
///####################################### 展示傳入的參數(shù)
(lldb) settings show target.run-args
target.run-args (array of strings) =
[0]: "/Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo"
[1]: "1"
[2]: "2"
[3]: "3"
BreakPoint和WatchPoint
下面列出了我們調(diào)試過程中需要設置斷點和觀察點的方法恶复,以及編輯斷點和觀察點的命令盛末。
類別 | 描述 | GDB | LLDB | 備注 |
---|---|---|---|---|
斷點相關 | 基于函數(shù)名稱設置斷點 | break main | breakpoint set --name main | |
基于代碼文件以及某一行設置斷點 | break Controller.cpp:11 | breakpoint set --file Controller.cpp --line 11 | ||
基于方法名設置斷點 | breakpoint set --method pfb_lldb::Controller::increase | |||
基于Objective C相關斷點設置 | breakpoint set --name "-[Model setController:]"\breakpoint set --selector count | |||
獲取當前設置的斷點信息 | info break | breakpoint list | ||
刪除指定斷點 | delete 1 | breakpoint delete 1 | 這里命令后面帶的數(shù)字,即某一斷點在breakpoint list里面的序號 | |
使指定斷點失效 | disable 1 | breakpoint disable 1 | ||
使指定斷點生效 | enable 1 | breakpoint enable 1 | ||
觀察點相關 | 設置觀察點 | watch ctlptr->len | watchpoint set variable ctlptr->len | 這里命令后面帶的數(shù)字态坦,即某一觀察點在watchpoint list里面的序號 |
查看所有觀察點 | info break | watchpoint list | ||
刪除觀察點 | delete 1 | watchpoint delete 1 |
///####################################### 基于C++方法設置斷點
(lldb) breakpoint set --method pfb_lldb::Controller::increase
///####################################### 獲取當前所有的斷點
(lldb) breakpoint list
Current breakpoints:
1: name = '-[Model setController:]', locations = 1
1.1: where = lldb_demo`-[Model setController:] + 20 at Model.mm:14:5, address = lldb_demo[0x0000000100003de4], unresolved, hit count = 0
2: name = 'main', locations = 10
2.1: where = lldb_demo`main + 25 at main.mm:14:22, address = lldb_demo[0x0000000100003b49], unresolved, hit count = 0
2.2: where = Foundation`-[NSBlockOperation main], address = Foundation[0x00007fff21177335], unresolved, hit count = 0
2.3: where = Foundation`-[NSThread main], address = Foundation[0x00007fff2118e53d], unresolved, hit count = 0
2.4: where = Foundation`-[NSFilesystemItemRemoveOperation main], address = Foundation[0x00007fff2118f067], unresolved, hit count = 0
2.5: where = Foundation`-[NSInvocationOperation main], address = Foundation[0x00007fff211a34a8], unresolved, hit count = 0
2.6: where = Foundation`-[NSOperation main], address = Foundation[0x00007fff211a3c97], unresolved, hit count = 0
2.7: where = Foundation`-[NSFilesystemItemMoveOperation main], address = Foundation[0x00007fff211e2a9f], unresolved, hit count = 0
2.8: where = Foundation`-[NSDirectoryTraversalOperation main], address = Foundation[0x00007fff2122aa96], unresolved, hit count = 0
2.9: where = Foundation`-[_NSBarrierOperation main], address = Foundation[0x00007fff212ef78b], unresolved, hit count = 0
2.10: where = Security`Security::OSXCode::main(), address = Security[0x00007fff22450154], unresolved, hit count = 0
3: name = 'pfb_lldb::Controller::increase', locations = 1
3.1: where = lldb_demo`pfb_lldb::Controller::increase() + 12 at Controller.cpp:11:8, address = lldb_demo[0x0000000100003d9c], unresolved, hit count = 0
///####################################### 設置觀察點
(lldb) watchpoint set variable ctlptr->len
Watchpoint created: Watchpoint 1: addr = 0x100704080 size = 4 state = enabled type = w
declare @ '/Users/xxx/WorkStation/iOS/exec/lldb_demo/lldb_demo/main.mm:17'
watchpoint spec = 'ctlptr->len'
new value: 0
變量和表達式執(zhí)行
使用expr我們可以在debug的時候去執(zhí)行相關的表達式,以達到運行期間修改的目的。比如在運行時我們想要修改某一個View的背景色赔硫,那么我們就可以使用expr來達到這個目的,而不需要重新編譯執(zhí)行盐肃。
類別 | 描述 | GDB | LLDB | 備注 |
---|---|---|---|---|
變量 | 查看當前幀變量 | info args | frame variable | |
展示局部變量 | info locals | frame variable --no-args | 局部變量爪膊,即非外部傳入的變量 | |
打印指定變量 | p bar | p bar | ||
執(zhí)行表達式 | print (int) printf ("Print nine: %d.", 4 + 5) | expr (int) printf ("Print nine: %d.", 4 + 5) | ||
打印OC相關的對象 | po [SomeClass returnAnObject] | po [SomeClass returnAnObject]\expr -o -- [SomeClass returnAnObject] |
///####################################### 查看當前變量
(lldb) frame variable
(int) argc = 1
(const char **) argv = 0x00007ffeefbff4d0
(NSRunLoop *) loop = 0x0000000100405d80
(pfb_lldb::Controller *) ctlptr = 0x0000000100704080
(Model *) mdl = nil
///####################################### 查看當前局部變量
(lldb) frame variable --no-args
(NSRunLoop *) loop = 0x0000000100405d80
(pfb_lldb::Controller *) ctlptr = 0x0000000100704080
(Model *) mdl = nil
///####################################### 執(zhí)行表達式相關
(lldb) expr ctlptr->len = 10
(int) $0 = 10
(lldb) expr ctlptr->len++
(int) $1 = 10
(lldb) po ctlptr->len
11
(lldb) expr -o -- ctlptr->len
11
線程狀態(tài)
其中bt是我們使用的最多的一個命令,它可以打印出當前線程的調(diào)用堆棧砸王。
類別 | 描述 | GDB | LLDB | 備注 |
---|---|---|---|---|
Thread State | 列舉程序中的線程信息 | info threads | thread list | |
選中某個線程 | thread 1 | thread select 1 | 序號為thread list中的編號 | |
當前線程調(diào)用棧 | bt | thread backtrace | ||
所有線程調(diào)用棧 | thread apply all bt | bt all | ||
當前線程調(diào)用棧前2幀 | bt 2 | thread backtrace -c 2 | ||
選中某第1幀 | frame 1 | frame select 1 |
(lldb) thread list
Process 50356 stopped
* thread #1: tid = 0x50aac7, 0x0000000100003c1b lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:9, queue = 'com.apple.main-thread', stop reason = step over
thread #2: tid = 0x50afa5, 0x00007fff2050f420 libsystem_pthread.dylib`start_wqthread
thread #3: tid = 0x50e012, 0x00007fff22d7fcd2 libsystem_notify.dylib`___lldb_unnamed_symbol29$$libsystem_notify.dylib, queue = 'com.apple.root.user-initiated-qos'
///####################################### 調(diào)用堆棧
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
* frame #0: 0x0000000100003d9c lldb_demo`pfb_lldb::Controller::increase(this=0x0000000100704080) at Controller.cpp:11:8
frame #1: 0x0000000100003c24 lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:17
frame #2: 0x00007fff2052ef3d libdyld.dylib`start + 1
///####################################### 調(diào)用堆棧前2幀
(lldb) bt 2
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
* frame #0: 0x0000000100003d9c lldb_demo`pfb_lldb::Controller::increase(this=0x0000000100704080) at Controller.cpp:11:8
frame #1: 0x0000000100003c24 lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:17
/// 選中指定幀
(lldb) frame select 1
frame #1: 0x0000000100003c24 lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:17
15 NSRunLoop *loop = [NSRunLoop currentRunLoop];
16 [loop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
17 Controller *ctlptr = new Controller();
-> 18 ctlptr->increase();
19 Model *mdl = [Model new];
20 [mdl setController:ctlptr];
21 delete ctlptr;
內(nèi)存和寄存器
當我們發(fā)生了bad access/類型異常等等錯誤推盛,為了定位到底發(fā)生了什么我們就需要去查看對應地址上的內(nèi)存值。
類別 | 描述 | GDB | LLDB | 備注 |
---|---|---|---|---|
內(nèi)存相關 | 讀取指定地址的內(nèi)存值 | x/10xw 0x0000000100704080 | x/10xw 0x0000000100704080 |
上表中和內(nèi)存讀取相關的指令格式中存在一個緊跟著反斜杠的10bw內(nèi)容谦铃,這里我簡單解釋一下他們的作用:
- N(Number):需要讀取的個數(shù)耘成,比如這里是10;
- F(Format):我們讀取的格式,比如這里的b瘪菌。它和我們平時使用printf格式一樣:‘x’(十六進制), ‘d’, ‘u’, ‘o’, ‘t’, ‘a(chǎn)’, ‘c’, ‘f’, ‘s’, ‘m’件豌;
- U(Unit):上面提到的個數(shù),那么U就是決定每個的展示內(nèi)容的大锌厥取:
b Bytes:字節(jié)
h Halfwords (2 bytes):半字
w Words (4 bytes):字
g Giant words (8 bytes):巨字
(lldb) frame variable --no-args
(NSRunLoop *) loop = 0x0000000100405d80
(pfb_lldb::Controller *) ctlptr = 0x0000000100704080
(Model *) mdl = nil
(lldb) x/10xw 0x0000000100704080
0x100704080: 0x0000000b 0x00000000 0x00000000 0x00000000
0x100704090: 0x00000000 0x00000000 0x00000000 0x00000000
0x1007040a0: 0x00000000 0x00000000
在進行DEBUG的時候獲取寄存器的值茧彤,對我們定位問題有著至關重要的作用。對于通用目的寄存器來說疆栏,排查問題比較大的幫助的有bp/fp曾掂、sp用于確定當前棧的區(qū)域;ip/pc用于確定當前執(zhí)行指令壁顶;lr寄存器保存函數(shù)調(diào)用返回地址等等珠洗。
類別 | 描述 | GDB | LLDB | 備注 |
---|---|---|---|---|
寄存器相關 | 展示當前線程通用目的寄存器 | info registers | register read | |
展示所有寄存器的值 | info all-registers | register read --all | ||
修改指定寄存器的值 | register write rax 123 |
(lldb) register read
General Purpose Registers:
rbx = 0x0000000000000000
rbp = 0x00007ffeefbff4b0
rsp = 0x00007ffeefbff430
r12 = 0x0000000000000000
r13 = 0x0000000000000000
r14 = 0x0000000000000000
r15 = 0x0000000000000000
rip = 0x0000000100003c24 lldb_demo`main + 244 at main.mm:18:17
13 registers were unavailable.
(lldb) register read --all
General Purpose Registers:
rbx = 0x0000000000000000
rbp = 0x00007ffeefbff4b0
rsp = 0x00007ffeefbff430
r12 = 0x0000000000000000
r13 = 0x0000000000000000
r14 = 0x0000000000000000
r15 = 0x0000000000000000
rip = 0x0000000100003c24 lldb_demo`main + 244 at main.mm:18:17
ebx = 0x00000000
ebp = 0xefbff420
esp = 0xefbff420
sp = 0xf420
61 registers were unavailable.
Floating Point Registers:
50 registers were unavailable.
Exception State Registers:
3 registers were unavailable.
可執(zhí)行文件和共享庫相關查詢
我們在調(diào)試的時候用到image的場景不是特別常見,它主要用于調(diào)試的時候查看有哪些鏡像若专、以及這些鏡像的起始地址和結束地址许蓖。用于對比發(fā)生異常之后指令是位于哪個鏡像。
類別 | 描述 | GDB | LLDB |
---|---|---|---|
可執(zhí)行文件和共享庫 | 展示當前可執(zhí)行文件以及所依賴的所有庫 | info shared | image list |
查找指定地址相關信息 | info symbol 0x1ec4 | image lookup --address 0x1ec4 | |
dump所有的sections | maintenance info sections | image dump sections | |
dump指定image的sections | image dump sections a.out | ||
dump指定image的符號表 | image dump symtab lldb_demo |
(lldb) image lookup --address 0x0000000100003b30
Address: lldb_demo[0x0000000100003b30] (lldb_demo.__TEXT.__text + 0)
Summary: lldb_demo`main at main.mm:12
(lldb) image dump section lldb_demo
Sections for '/Users/wangwang/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo' (x86_64):
SectID Type Load Address Perm File Off. File Size Flags Section Name
---------- ---------------- --------------------------------------- ---- ---------- ---------- ---------- ----------------------------
0x00000100 container [0x0000000000000000-0x0000000100000000)* --- 0x00000000 0x00000000 0x00000000 lldb_demo.__PAGEZERO
0x00000200 container [0x0000000100000000-0x0000000100004000) r-x 0x00000000 0x00004000 0x00000000 lldb_demo.__TEXT
0x00000001 code [0x0000000100003b30-0x0000000100003e3e) r-x 0x00003b30 0x0000030e 0x80000400 lldb_demo.__TEXT.__text
0x00000002 code [0x0000000100003e3e-0x0000000100003e74) r-x 0x00003e3e 0x00000036 0x80000408 lldb_demo.__TEXT.__stubs
0x00000003 code [0x0000000100003e74-0x0000000100003eca) r-x 0x00003e74 0x00000056 0x80000400 lldb_demo.__TEXT.__stub_helper
0x00000004 regular [0x0000000100003ecc-0x0000000100003f08) r-x 0x00003ecc 0x0000003c 0x00000000 lldb_demo.__TEXT.__gcc_except_tab
0x00000005 data-cstr [0x0000000100003f08-0x0000000100003f5c) r-x 0x00003f08 0x00000054 0x00000002 lldb_demo.__TEXT.__objc_methname
0x00000006 data-cstr [0x0000000100003f5c-0x0000000100003f61) r-x 0x00003f5c 0x00000005 0x00000002 lldb_demo.__TEXT.__cstring
0x00000007 data-cstr [0x0000000100003f61-0x0000000100003f67) r-x 0x00003f61 0x00000006 0x00000002 lldb_demo.__TEXT.__objc_classname
0x00000008 data-cstr [0x0000000100003f67-0x0000000100003f95) r-x 0x00003f67 0x0000002e 0x00000002 lldb_demo.__TEXT.__objc_methtype
0x00000009 compact-unwind [0x0000000100003f98-0x0000000100003ff4) r-x 0x00003f98 0x0000005c 0x00000000 lldb_demo.__TEXT.__unwind_info
0x00000300 container [0x0000000100004000-0x0000000100008000) rw- 0x00004000 0x00004000 0x00000010 lldb_demo.__DATA_CONST
0x0000000a data-ptrs [0x0000000100004000-0x0000000100004028) rw- 0x00004000 0x00000028 0x00000006 lldb_demo.__DATA_CONST.__got
0x0000000b data-ptrs [0x0000000100004028-0x0000000100004030) rw- 0x00004028 0x00000008 0x10000000 lldb_demo.__DATA_CONST.__objc_classlist
0x0000000c regular [0x0000000100004030-0x0000000100004038) rw- 0x00004030 0x00000008 0x00000000 lldb_demo.__DATA_CONST.__objc_imageinfo
0x00000400 container [0x0000000100008000-0x000000010000c000) rw- 0x00008000 0x00004000 0x00000000 lldb_demo.__DATA
0x0000000d data-ptrs [0x0000000100008000-0x0000000100008048) rw- 0x00008000 0x00000048 0x00000007 lldb_demo.__DATA.__la_symbol_ptr
0x0000000e data-ptrs [0x0000000100008048-0x0000000100008168) rw- 0x00008048 0x00000120 0x00000000 lldb_demo.__DATA.__objc_const
0x0000000f data-cstr-ptr [0x0000000100008168-0x0000000100008190) rw- 0x00008168 0x00000028 0x10000005 lldb_demo.__DATA.__objc_selrefs
0x00000010 data-ptrs [0x0000000100008190-0x00000001000081a8) rw- 0x00008190 0x00000018 0x10000000 lldb_demo.__DATA.__objc_classrefs
0x00000011 regular [0x00000001000081a8-0x00000001000081b0) rw- 0x000081a8 0x00000008 0x00000000 lldb_demo.__DATA.__objc_ivar
0x00000012 data-ptrs [0x00000001000081b0-0x0000000100008200) rw- 0x000081b0 0x00000050 0x00000000 lldb_demo.__DATA.__objc_data
0x00000013 data [0x0000000100008200-0x0000000100008208) rw- 0x00008200 0x00000008 0x00000000 lldb_demo.__DATA.__data
0x00000500 container [0x000000010000c000-0x0000000100014000) r-- 0x0000c000 0x00005c10 0x00000000 lldb_demo.__LINKEDIT
使用Python腳本搭配LLDB
使用Python腳本的好處就是可以避免人工來做重復的事情调衰,比如命中某個斷點之后我們需要修改一些內(nèi)存膊爪、寄存器、或者代碼的執(zhí)行路徑等等嚎莉。都是可以基于Python腳本來進行的米酬。其大致步驟有:
- 編寫python腳本文件;
- 導入腳本文件趋箩;
- 導入腳本內(nèi)部的命令(這一步可以沒有)赃额;
其中最主要的是第一步,畢竟導入文件和導入命令就兩個lldb命令的事兒叫确。因此主要來看看腳本文件的編寫跳芳。
Python腳本編寫
由于是編寫基于LLDB的腳本,所以我們必須先導入lldb這個module(這是LLDB API 包含在一個名為 lldb 的 python 模塊中竹勉,完整的API見LLDB Python API ):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import lldb
其中我們可能會用到的一些變量包括有(下面表格里面所有提到的內(nèi)容都可以通過 script help(lldb.xxxx) 來獲取詳細地幫助信息):
變量 | 類型 | 類 | 描述 |
---|---|---|---|
lldb.debugger | lldb.SBDebugger | SBTarget.GetDebugger | 調(diào)試器 |
lldb.target | lldb.SBTarget | SBDebugger.GetSelectedTarget | 我們在啟動lldb之后飞盆,可以通過target create 來創(chuàng)建不同的target。然后對應run來指定要運行的target |
lldb.process | lldb.SBProcess | SBTarget.GetProcess | 進程 |
lldb.thread | lldb.SBThread | SBProcess.GetSelectedThread | 線程 |
lldb.frame | lldb.SBFrame | SBThread.GetSelectedFrame | 調(diào)用幀 |
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> print(lldb.debugger)
Debugger (instance: "debugger_1", id: 1)
>>> print(lldb.process)
SBProcess: pid = 30104, state = stopped, threads = 1, executable = lldb_demo
>>> print(lldb.thread)
thread #1: tid = 0x61552d, 0x0000000100003b49 lldb_demo`main(argc=2, argv=0x00007ffeefbff468) at main.mm:14:22, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
>>> print(lldb.frame)
frame #0: 0x0000000100003b49 lldb_demo`main(argc=2, argv=0x00007ffeefbff468) at main.mm:14:22
>>> print(lldb.target)
lldb_demo
這些是我們在lldb內(nèi)嵌的python解釋器中執(zhí)行相關的代碼得到的結論饶米。
現(xiàn)在我們來編寫Python腳本桨啃。首先是創(chuàng)建一個Target,我們可以根據(jù)debugger來創(chuàng)建:
def pfbCreateTarget():
# triple = "x86_64-apple-macosx"
# platform_name = None
# add_dependents = False
path = os.getcwd() + "/lldb_demo"
demo_target = lldb.debugger.CreateTargetWithFileAndArch (path, lldb.LLDB_ARCH_DEFAULT)
return demo_target
創(chuàng)建好了target檬输,那肯定是要執(zhí)行這個target的照瘾。這里會返回一個process:
def pfbRunTarget(target):
process = target.LaunchSimple(None, None, os.getcwd())
return process
程序默認執(zhí)行是不會暫停的,我們可以通過腳本來增加指定的斷點:
def pfbCreateMainBreakpoint(target):
breakpoint = target.BreakpointCreateByName("main", None)
breakpoint.enable = True
return breakpoint
到這里我們先讓這個腳本能RUN起來丧慈,那么第一步肯定是要讓我們這兩個函數(shù)有地方調(diào)用析命。在LLDB中給我們提供了一個函數(shù)入口__lldb_init_module:
def __lldb_init_module(debugger, internal_dict):
print("LLDB INIT MODULE")
pfbTarget = pfbCreateTarget()
print(pfbTarget)
pfbMainBreakPoint = pfbCreateMainBreakpoint(pfbTarget)
pfbProcess = pfbRunTarget(pfbTarget)
將腳本添加到lldb中:
(lldb) command script import ~/WorkStation/exec/llvm/lldb/script/python/search_fmp.py
可以得到如下結果主卫,程序在指定的位置(斷點處)暫停了:
如果是我們需要在腳本文件中處理斷點相關的debug操作的話,LLDB給我們提供斷點被執(zhí)行時的回調(diào)函數(shù)鹃愤。默認是breakpoint_function_wrapper簇搅。但是我們可以在執(zhí)行指令的時候通過--python-function指定我們需要處理的函數(shù)(指定的函數(shù)參數(shù)必須要包含frame、bp_loc):
def breakpoint_function_wrapper(frame, bp_loc, extra_args, internal_dict):
# Your code goes here
print("breakpoint_function_wrapper1 begin:")
print(frame)
print("breakpoint_function_wrapper1 end")
return True
'''
-----------------
私有函數(shù)
-----------------
'''
def pfbEnableBreakpointHook(target):
# breakpoint command add -s python xxx --python-function search_fmp.breakpoint_function_wrapper
bnums = target.GetNumBreakpoints()
print("Enable Breakpoint nums: " + str(bnums))
for i in range(0, bnums):
brkpt = target.GetBreakpointAtIndex(i)
cmd = "breakpoint command add -s python " + str(brkpt.id) + " --python-function search_fmp.breakpoint_function_wrapper"
print("Enable Breakpoint: " + cmd)
lldb.debugger.HandleCommand(cmd)
針對函數(shù)原型中提到的幾個參數(shù)软吐,下面做了一下簡單的解釋:
參數(shù)名稱 | 類型 | 備注 |
---|---|---|
frame | lldb.SBFrame | 斷點被命中的當前堆棧幀 |
bp_loc | lldb.SBBreakpointLocation | 命中的斷點位置 |
extra_args | lldb.SBStructuredData | 暫未使用 |
internal_dict | dict | python字典對象 |
得到的結論類似于這樣:
使用Python腳本給LLDB新增指令
就像我在本文開頭描述的那樣瘩将,我們需要在lldb里面使用shell的命令需要加一個platform shell的前綴。顯然這么麻煩的事情凹耙,并不是我們程序員要干的事兒姿现。需求有了,那就來實現(xiàn)一下肖抱!
首先是定義我們需要使用的指令备典,這里我就以最常用的ls和pwd來舉例:
def ls(debugger, command, exe_ctx, result, internal_dict):
ret = subprocess.check_output(["ls", "-l"])
print(ret)
def pwd(debugger, command, exe_ctx, result, internal_dict):
ret = subprocess.check_output(["pwd"])
print(ret)
def pfbShell(debugger, command, result, dict):
command_args = shlex.split(command)
ret = subprocess.check_output(command_args)
print(ret)
在定義了對應的函數(shù)之后,我們需要將這些函數(shù)添加到command中:
debugger.HandleCommand("command script add -f search_fmp.pfbShell pfb")
debugger.HandleCommand("command script add -f search_fmp.pwd pwd")
debugger.HandleCommand("command script add -f search_fmp.ls ls")
Python腳本搭配LLDB還有很多的玩法意述,我就列出了簡單的使用場景提佣。
完整的代碼如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# https://lldb.llvm.org/use/python-reference.html
import lldb
# python2 command, python3 subprocess
import subprocess
import optparse
import shlex
import os
'''
-----------------
LLDB 回調(diào)時機
-----------------
'''
# debugger: lldb.SBDebugger 調(diào)試器對象
def __lldb_init_module(debugger, internal_dict):
print("LLDB INIT MODULE")
pfbTarget = pfbCreateTarget()
pfbMainBreakPoint = pfbCreateMainBreakpoint(pfbTarget)
pfbEnableBreakpointHook(pfbTarget)
pfbProcess = pfbRunTarget(pfbTarget)
# lldb.target
# debugger.HandleCommand("")
debugger.HandleCommand("command script add -f search_fmp.pfbShell pfb")
debugger.HandleCommand("command script add -f search_fmp.pwd pwd")
debugger.HandleCommand("command script add -f search_fmp.ls ls")
# frame: lldb.SBFrame 斷點被命中的當前堆棧幀
# bp_loc: lldb.SBBreakpointLocation 命中的斷點位置
# extra_args: lldb.SBStructuredData
# internal_dict: dict
def breakpoint_function_wrapper(frame, bp_loc, extra_args, internal_dict):
# Your code goes here
print("breakpoint_function_wrapper1 begin:")
print(frame)
print("breakpoint_function_wrapper1 end")
return True
'''
-----------------
command
-----------------
'''
def ls(debugger, command, exe_ctx, result, internal_dict):
ret = subprocess.check_output(["ls", "-l"])
print(ret)
def pwd(debugger, command, exe_ctx, result, internal_dict):
ret = subprocess.check_output(["pwd"])
print(ret)
def pfbShell(debugger, command, result, dict):
command_args = shlex.split(command)
ret = subprocess.check_output(command_args)
print(ret)
'''
-----------------
私有函數(shù)
-----------------
'''
def pfbEnableBreakpointHook(target):
# breakpoint command add -s python xxx --python-function search_fmp.breakpoint_function_wrapper
bnums = target.GetNumBreakpoints()
print("Enable Breakpoint nums: " + str(bnums))
for i in range(0, bnums):
brkpt = target.GetBreakpointAtIndex(i)
cmd = "breakpoint command add -s python " + str(brkpt.id) + " --python-function search_fmp.breakpoint_function_wrapper"
print("Enable Breakpoint: " + cmd)
lldb.debugger.HandleCommand(cmd)
def pfbCreateMainBreakpoint(target):
breakpoint = target.BreakpointCreateByName("main", None)
breakpoint.enable = True
return breakpoint
def pfbCreateTarget():
triple = "x86_64-apple-macosx11.5.0"
platform_name = None
add_dependents = False
path = os.getcwd() + "/lldb_demo"
print("Target Create: " + path)
# https://lldb.llvm.org/use/symbolication.html#using-python-api-to-symbolicate
target = lldb.debugger.CreateTarget(path, triple, platform_name, add_dependents, lldb.SBError())
#target = lldb.debugger.CreateTargetWithFileAndArch (path, lldb.LLDB_ARCH_DEFAULT)
return target
def pfbRunTarget(target):
process = target.LaunchSimple(None, None, os.getcwd())
return process