LLDB (Low Level Debugger) 輕量級高性能調(diào)試器曲尸,掌握 LLDB 可以有效的提升 Debug 能力,提高工作效率瓣俯。
一肋乍、常用命令
對象操作 (p/po/e/call)
NSArray *array = @[@"a", @"b", @"b", @"c"];
//(lldb) p array
//(__NSArray0 *) $3 = 0x00007fff8db5c830 @"0 elements"
//(lldb) p array = @[@"a"]
//(__NSSingleObjectArrayI *) $4 = 0x00000001005013b0 @"1 element"
// (lldb) po array
//<__NSArrayI 0x1006022f0>(
//a,
//b,
//b,
//c
//)
ZSan *zsan = [[ZSan alloc] init];
zsan->_no = 1;
//(lldb) e zsan->_no = 2;
// (int) $2 = 2
lldb
中 p
打印對象的描述信息俊马,包括類型,指針地址等信息肩杈;po
打印的是對象的詳細信息柴我。
po
同時也可以修改對象信息,e
可以修改對象中屬性的值扩然,call
可以讓對象執(zhí)行方法艘儒。
p/po/e/call
都可以打印對象的信息。
函數(shù)操作 (bt/frame/image)
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 斷點處
NSArray *array = @[@"a", @"b", @"b", @"c"];
}
return 0;
}
// (lldb) bt
// * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
// * frame #0: 0x0000000100000d58 BLBaseNSObject`main(argc=1, argv=0x00007ffeefbff4f8) at main.m:122:22
// frame #1: 0x00007fff6c55e7fd libdyld.dylib`start + 1
// frame #2: 0x00007fff6c55e7fd libdyld.dylib`start + 1
//(lldb) frame select 0
//frame #0: 0x0000000100000d58 BLBaseNSObject`main(argc=1, argv=0x00007ffeefbff4f8) at main.m:122:22
// 119
// 120 NSArray *array = @[@"a", @"b", @"b", @"c"];
// 121
//-> 122 NSLog(@"%@", array);
// ^
// 123 }
// 124 return 0;
// 125 }
bt(backtrace)
打印當前調(diào)用堆棧(crash堆棧)与学;frame
打印函數(shù)調(diào)用詳情以及調(diào)用順序彤悔;image
打印棧地址對應的代碼。
frame selecte 0
可以查看當前函數(shù)調(diào)用的信息索守,通過 up
和 down
可以追蹤函數(shù)的調(diào)用和被調(diào)用關(guān)系晕窑。
frame variable
很方便的查方法的調(diào)用者及方法名稱。
image lookup -a 地址
這個方法主要用于尋址卵佛,常用與在崩潰的時候杨赤,查找崩潰地址對應的文件代碼的位置。
當項目遇到崩潰的時候同時利用 bt
跟 image loopup -a 地址
可以更好的找到問題截汪。
屬性&變量操作 (watchpoint)
有時候我們開發(fā)過程中會遇到類似的問題疾牲,有一些屬性或者變量值發(fā)生了變化,但是不知道具體是哪里開始發(fā)生的變化衙解,導致最后的結(jié)果與我們預期有了出入阳柔。
這里就可以使用 watchpoint
來監(jiān)聽我們的屬性或者變量。
ZSan *zsan = [[ZSan alloc] init];
zsan.ccc = 10;
zsan.ccc = 1000;
//(lldb) watchpoint set variable zsan->_ccc
//Watchpoint created: Watchpoint 1: addr = 0x100555c30 size = 4 state = enabled type = w
// declare @ '/Users/Charlyliu/Desktop/學習項目/OC/BLBaseNSObject/BLBaseNSObject/main.m:108'
// watchpoint spec = 'zsan->_ccc'
// new value: 0
//
//Watchpoint 1 hit:
//old value: 0
//new value: 10
//(lldb) pt
//error: 'pt' is not a valid command.
//(lldb) bt
//* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
// * frame #0: 0x0000000100001a70 BLBaseNSObject`-[ZSan setCcc:](self=0x0000000100555c10, _cmd="setCcc:", ccc=10) at main.m:60:35
// frame #1: 0x0000000100001c69 BLBaseNSObject`main(argc=1, argv=0x00007ffeefbff4f8) at main.m:114:14
// frame #2: 0x00007fff6c55e7fd libdyld.dylib`start + 1
// frame #3: 0x00007fff6c55e7fd libdyld.dylib`start + 1
//(lldb) n
//(lldb) bt
//* thread #1, queue = 'com.apple.main-thread', stop reason = step over
// * frame #0: 0x0000000100001c69 BLBaseNSObject`main(argc=1, argv=0x00007ffeefbff4f8) at main.m:120:111
// frame #1: 0x00007fff6c55e7fd libdyld.dylib`start + 1
// frame #2: 0x00007fff6c55e7fd libdyld.dylib`start + 1
//(lldb) c
//Process 36061 resuming
//2019-12-27 13:40:11.600731+0800 BLBaseNSObject[36061:1628481] 測試多層繼承關(guān)系成員變量大小的內(nèi)存對齊邏輯---40
//2019-12-27 13:40:11.601062+0800 BLBaseNSObject[36061:1628481] 測試多層繼承關(guān)系內(nèi)存分配---48
//
//Watchpoint 1 hit:
//old value: 10
//new value: 1000
//(lldb) bt
//* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
// * frame #0: 0x0000000100001a70 BLBaseNSObject`-[ZSan setCcc:](self=0x0000000100555c10, _cmd="setCcc:", ccc=1000) at main.m:60:35
// frame #1: 0x0000000100001cd5 BLBaseNSObject`main(argc=1, argv=0x00007ffeefbff4f8) at main.m:123:14
// frame #2: 0x00007fff6c55e7fd libdyld.dylib`start + 1
// frame #3: 0x00007fff6c55e7fd libdyld.dylib`start + 1
//(lldb) image lookup 0x0000000100001a70
//error: invalid combination of options for the given command
//(lldb) image lookup -a 0x0000000100001a70
// Address: BLBaseNSObject[0x0000000100001a70] (BLBaseNSObject.__TEXT.__text + 64)
// Summary: BLBaseNSObject`-[ZSan setCcc:] + 32 at main.m:60:35
//(lldb)
-
watchpoint set variable zsan->_ccc
注意蚓峦,這里的zsan->_ccc
不能使用點語法舌剂,這條命令主要是監(jiān)聽屬性地址,如果其地址內(nèi)保存的值發(fā)生變化就會走進斷點暑椰,而點語法實際上是調(diào)用的getter
方法霍转。 -
watchpoint set variable testInt
設(shè)置變量觀察,testInt
變量發(fā)生改變后觸發(fā)一汽。 -
watchpoint set expression -- 0x000000010f46c070
設(shè)置內(nèi)存觀察避消。 -
watchpoint list / watch l
當前所有觀察列表。 -
watchpoint modify -c 'testInt == 2'
設(shè)置條件觸發(fā)觀察召夹。 -
watchpoint delete 2
刪除對應的觀察岩喷。
這里我們可以配合 bt
堆棧信息使用,當發(fā)現(xiàn)值變化的時候可以查閱堆棧信息定位修改代碼的位置监憎。
內(nèi)存操作 (memory/x {read/write})
ZSan *zsan = [[ZSan alloc] init];
zsan->_no = 1;
zsan->_num = 1;
zsan->_age = 18;
//(lldb) memory read zsan
//0x1007009a0: 21 23 00 00 01 80 1d 00 01 00 00 00 01 00 00 00 !#..............
//0x1007009b0: 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 表示修改 0x1007009a8 后的值纱意,將其值修改為 4;根據(jù)剛才獲取內(nèi)存信息可知枫虏,從 0x1007009a8 地址之后是 _no 的值妇穴,值位 0x01爬虱,這里我們將其修改成為 0x04,也就是 zsan->_no = 4
//(lldb) memory write 0x1007009a8 4
//(lldb) po zsan->_no
//4
memory read
可以用 x
代替腾它,使用起來更方便跑筝,用法 memory read/數(shù)量格式字節(jié)數(shù) 讀取對象/或某個具體的內(nèi)存地址
或者 x/數(shù)量格式字節(jié)數(shù) 讀取對象/或某個具體的內(nèi)存地址
。
/
后各個字段描述:
- 數(shù)量瞒滴,表示讀取從該對象起始地址或該內(nèi)存地址起始地址后多長的地址
- 格式
-
x
是16
進制 -
f
是浮點 -
d
是10
進制
-
- 字節(jié)數(shù)
-
b
:byte 1
字節(jié) -
h
:half word 2
字節(jié) -
w
:word 4
字節(jié) -
g
:giant word 8
字節(jié)
-
舉例:讀取 szan
對象地址曲梗,要求以 8
個字節(jié),用 16
進制格式讀取 4
組數(shù)據(jù)妓忍。
x/4xg szan
或者 x/4xg 0x10055d610
虏两,其中 0x10055d610
是 szan
對象的地址。
memory write 地址 修改后的數(shù)據(jù)
世剖,通過 write
可以修改內(nèi)存中的數(shù)據(jù)定罢。
斷點操作 (breakpoind/b)
Xcode
工具為我們提供了斷點調(diào)試的功能,為什么還要使用這個命令呢旁瘫?LLDB
的斷點使用起來速度更快祖凫,功能更全面。
0. 流程控制
-
c/continue
繼續(xù)執(zhí)行酬凳,跳到下一個斷點或者沒有斷點程序執(zhí)行完畢 -
next/n(step-over)
單步運行惠况,將子函數(shù)當做整體一步運行 -
stepi/s(step-in)
單步運行,遇到子函數(shù)會進入 -
finish(step-out)
如果進入一個子函數(shù)宁仔,則可以通過這個方法退出子函數(shù)
1. 增刪查
-
set
添加斷點-
-a {內(nèi)存地址}
根據(jù)函數(shù)地址打斷點(常用與逆向) -
-n {xxx:}
設(shè)置方法名斷點breakpoint/b set -n login:
-
-n { [Class xxxx]}
根據(jù)對應的類設(shè)置該類的方法名斷點(可多個:一組)breakpoint/b set -n "-[Class1 xxx]" -n "-[Class2 xxx]"
-
-l {lineNumber}
根據(jù)行號設(shè)置斷點 -
-f {fileName}
根據(jù)文件名中的信息設(shè)置斷點 -
-F {函數(shù)全名}
根據(jù)函數(shù)全名打斷點 -
-r {部分函數(shù)名}
根據(jù)部分函數(shù)名打斷點 -
-N{別名}
給斷點添加別名
-
-
delete {breakpoint_num}
刪除一個斷點 -
list {breakpoint_num}
列舉指定斷點信息稠屠,breakpoint_num
可選 -
enable/disable {breakpoint_num}
啟用或禁用某個/某組斷點 -
commond
執(zhí)行一些特殊命令-
add {breakpoint_num} {特殊命令(如p/po/bt等)}
斷點執(zhí)行后執(zhí)行一些特殊LLDB
操作 -
delete{breakpoint_num}
刪除某個斷點添加的特殊操作
-
2. 條件、命令
-
-i {number}
設(shè)置斷點忽略次數(shù) -
-o
只斷住一次 -
-c {條件(類似數(shù)據(jù)庫where操作)}
設(shè)置斷點條件
其他操作
-
methods
命令可以打印當前對象的屬性和方法 -
previews
命令可以打印當前視圖的層級結(jié)構(gòu) -
help
獲取幫助信息翎苫,查看更多LLDB
命令