LLDB是个开源的内置于XCode的调试工具,这里来理一理常用用法。
lldb对于命令的简称,是头部匹配方式,只要不混淆,你可以随意简称某个命令。结果为在xcode下验证所得,可能与其它平台有所误差。
1 打印值、修改值、调用方法
1.1 p、po 打印值
打印相关的命令有:p、po。
p 和 po 的区别在于使用 po 只会输出对应的值,而 p 则会返回值的类型以及命令结果的引用名。
(lldb)p width(CGFloat)$10=70(lldb)po width70(lldb)p endTime(__NSCFString*)$14=0x0000608000437660@"08-11 11:43"(lldb)po endTime08-1111:43
对比结果:
po:输出值
p:输出值+值类型+引用名+内存地址(xcode中有内存地址,其它平台不确定)
除此之外,p还隐藏了一个有意思的功能,常量的进制转换:
//默认打印为10进制(lldb)p100(int)$8=100//转16进制(lldb)p/x100(int)$9=0x00000064//转8进制(lldb)p/o100(int)$10=0144//转二进制(lldb)p/t100(int)$2=0b00000000000000000000000001100100//字符转10进制数字(lldb)p/d'A'(char)$7=65//10进制数字转字符(lldb)p/c66(int)$10=B\0\0\0
1.2 expression 修改参数值
感觉exp命令是调试过程中最有价值有命令了,它可以打印值、修改值。
//expression打印值(lldb)expression width(CGFloat)$5=67//expression修改值(lldb)expression width=80(CGFloat)$6=80//打印修改后结果(lldb)p width(CGFloat)$7=80(lldb)
expression:同样可以输出值+值类型+引用名,但其一般用于修改。
1.3 call 方法调用
在断点调用某个方法,�并输出此方法的返回值。
(lldb)call width(CGFloat)$12=70(lldb)call endTime(__NSCFString*)$16=0x0000608000437660@"08-11 11:43"
call:同样为输出值+值类型+引用名
2 Thread
2.1 堆栈打印 thread backtrace
如果嫌堆栈打印太长,可以加一个值限制,如bt 10,只打印
(lldb)bt10*thread #1,queue='com.apple.main-thread',stop reason=breakpoint1.1*frame #0:0x00000001005e4906DiDi`-[FW_HomeCell_HotBillsetDataSource:](self=0x00007fd3938a7800,_cmd="setDataSource:",dataSource=0x00006080001c8bb0)atFW_HomeCell.m:357frame #1:0x00000001009a9fd7DiDi`-[FW_MyHomeTableViewtableView:cellForRowAtIndexPath:](self=0x00007fd3921fec00,_cmd="tableView:cellForRowAtIndexPath:",tableView=0x00007fd3921fec00,indexPath=0xc000000000000316)atFW_MyHomeTableView.m:247frame #2:0x00000001055a2ab2UIKit`-[UITableView_createPreparedCellForGlobalRow:withIndexPath:willDisplay:]+750frame #3:0x00000001055a2cf8UIKit`-[UITableView_createPreparedCellForGlobalRow:willDisplay:]+74frame #4:0x0000000105577639UIKit`-[UITableView_updateVisibleCellsNow:isRecursive:]+2845frame #5:0x00000001055abcccUIKit`-[UITableView_performWithCachedTraitCollection:]+111frame #6:0x0000000105592e7aUIKit`-[UITableViewlayoutSubviews]+233frame #7:0x00000001054f955bUIKit`-[UIView(CALayerDelegate)layoutSublayersOfLayer:]+1268frame #8:0x0000000105114904QuartzCore`-[CALayerlayoutSublayers]+146frame #9:0x0000000105108526QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*)+370
2.2 thread return 跳出当前方法的执行
Debug的时候,也许会因为各种原因,我们不想让代码执行某个方法,或者要直接返回一个想要的值。这时候就该thread return上场了。
有返回值的方法里,如:numberOfSectionsInTableView:,直接thread return 10,就可以直接跳过方法执行,返回10.
//跳出方法(lldb)threadreturn//让带有返回int值的方法直接跳出,并返回值10(lldb)threadreturn10
2.3 流程控制
实际上使用xcode自带的可视化工具来控制“继续”“暂停”“下一步”“进入”“跳出”更简单
继续:continue,c下一步:next,n进入:step,s跳出:finish,f
2.4 跳帧 frame select N
2.1中打印有10帧,如果我想跳转到第1帧:frame select 1
(lldb)frame select1frame #1:0x0000000105e91c3cDiDi`-[FW_HomeViewControllertableView:cellForRowAtIndexPath:](self=0x00007fbf9f73b410,_cmd="tableView:cellForRowAtIndexPath:",tableView=0x00007fbfa11dc400,indexPath=0xc000000000a00316)atFW_HomeViewController.m:597594break;595596case3:{->597cell.�[4md�[0mataSource=_hotBills[indexPath.row];598cell.textSelect=^(UITextField*text){599weakSelf.curruntText=text;600};
2.5 查看帧变量 frame variable
(lldb)frame variable(FW_HomeViewController*)self=0x00007faccbf587d0(SEL)_cmd="tableView:cellForRowAtIndexPath:"(UITableView*)tableView=0x00007faccd09b400(NSIndexPath*)indexPath=0xc000000000000316(FW_HomeViewController*)weakSelf=0x00007faccbf587d0(FW_HomeCell_HotBill*)cell=0x00007faccc101a00(UIView*)model=0x00007fff52c13d90(FW_HomeCell_HotBill*)billCell=0x00000001124e99f6
3 Image
3.1 image lookup -address 查找崩溃位置
当你遇见数组崩溃,你又没有找到崩溃的位置,只扔给你一堆报错信息,这时候image lookup来帮助你。如下
0CoreFoundation0x0000000103209b0b__exceptionPreprocess+1711libobjc.A.dylib0x00000001079db141objc_exception_throw+482CoreFoundation0x000000010313effb-[__NSArrayM objectAtIndex:]+2033DiDi0x00000001009a9f3a-[FW_MyHomeTableViewtableView:cellForRowAtIndexPath:]+13224UIKit0x00000001055a2ab2-[UITableView_createPreparedCellForGlobalRow:withIndexPath:willDisplay:]+7505UIKit0x00000001055a2cf8-[UITableView_createPreparedCellForGlobalRow:willDisplay:]+746UIKit0x0000000105577639-[UITableView_updateVisibleCellsNow:isRecursive:]+28457UIKit0x00000001055abccc-[UITableView_performWithCachedTraitCollection:]+1118UIKit0x0000000105592e7a-[UITableViewlayoutSubviews]+2339UIKit0x00000001054f955b-[UIView(CALayerDelegate)layoutSublayersOfLayer:]+126810QuartzCore0x0000000105114904-[CALayerlayoutSublayers]+146
寻找自己项目的标识,看到frame3位置,你只需这样查找位置:
image lookup-a0x00000001009a9f3aAddress:DiDi[0x0000000100609f3a](DiDi.__TEXT.__text+6323194)Summary:DiDi`-[FW_MyHomeTableViewtableView:cellForRowAtIndexPath:]+1322atFW_MyHomeTableView.m:243
项目中FW_MyHomeTableView.m:243,perfect!
3.2 image lookup -name 查找方法来源
此命令可以用来查找方法的来源。包括在第三方SDK中的方法,也能被查到。
例:查找transformOtherModelToSuit:
(lldb)image lookup-n transformOtherModelToSuit:1match foundin/Users/xxx/Library/Developer/Xcode/DerivedData/DiDi-cwpbvvyvqmeijmcjnneothzuthsy/Build/Products/Debug-iphonesimulator/DiDi.app/DiDi:Address:DiDi[0x0000000100293d60](DiDi.__TEXT.__text+2693664)Summary:DiDi`+[FW_BetFunctiontransformOtherModelToSuit:]atFW_BetFunction.m:107
3.3 image lookup –type 查看成员
查看某个class的所有属性和成员变量。不过貌似frameWork库中文件不能查看。
(lldb)image lookup-tMatchEvent1match foundin/Users/xxxx/Library/Developer/Xcode/DerivedData/DiDi-cwpbvvyvqmeijmcjnneothzuthsy/Build/Products/Debug-iphonesimulator/DiDi.app/DiDi:id={0x00433d32},name="MatchEvent",byte-size=48,decl=MatchEvent.h:11,compiler_type="@interfaceMatchEvent:NSObject{BOOL_isHome;NSString*_playerName;NSString*_timePoint;NSString*_eventType;NSString*_eventDesc;}@property(getter=isHome,setter=setIsHome:,assign,readwrite,nonatomic)BOOLisHome;@property(getter=playerName,setter=setPlayerName:,readwrite,copy,nonatomic)NSString*playerName;@property(getter=timePoint,setter=setTimePoint:,readwrite,copy,nonatomic)NSString*timePoint;@property(getter=eventType,setter=setEventType:,readwrite,copy,nonatomic)NSString*eventType;@property(getter=eventDesc,setter=setEventDesc:,readwrite,copy,nonatomic)NSString*eventDesc;@end"
4 breakpoint
4.1 文件名+行号 breakpoint set -f xxx -l xxx
我们平时操作xcode,在某一行点下断点,其实操作的就是这个命令。
(lldb)breakpointset-fFW_ProfilesDetailModel.m-l95Breakpoint3:where=DiDi`-[FW_ProfilesDetailModelincomeRate]+27atFW_ProfilesDetailModel.m:96,address=0x0000000105b404bb
4.2 函数名断点
4.2.1 方法名断点 breakpoint set -n 方法名
(lldb)breakpointset-n viewDidLoadBreakpoint4:414locations.
Tips:这里要说一下,xcode其实也有函数名断点,不过用breakpoint set -n实现,比xcode下断点快N倍,不过xcode下的断点还给提示所有断到的位置。
![](http://img.e-com-net.com/image/info10/265454eb06b1435b85a2d3e0d7be1f77.jpg)
viewDidLoad断点
4.2.2 类中方法断点 breakpoint set -n "-[类名 方法名]"
(lldb)breakpointset-n"-[FW_MyHomeViewController viewDidLoad]"Breakpoint8:where=DiDi`-[FW_MyHomeViewControllerviewDidLoad]+20atFW_MyHomeViewController.m:58,address=0x0000000105aec944
注意:-[FW_MyHomeViewController viewDidLoad],外面一定要加双引号,不然会误识别为-[FW_MyHomeViewController`
4.3 条件断点 breakpoint �set -c "xxxx"
和xcode中symbolic Breakpoint功能相同,我在FW_HomeCell.m 362行下断点,但又想过滤仅width>68的状态,操作如下:
breakpointset-fFW_HomeCell.m-l362-c"width > 68"Breakpoint5:where=DiDi`-[FW_HomeCell_HotBillsetDataSource:]+2006atFW_HomeCell.m:363,address=0x000000010d22e0a6
4.4 查看断点列表 breakpoint list
(lldb)breakpoint listCurrentbreakpoints:8:name='-[FW_MyHomeViewController viewDidLoad]',locations=1,resolved=1,hitcount=28.1:where=DiDi`-[FW_MyHomeViewControllerviewDidLoad]+20atFW_MyHomeViewController.m:58,address=0x0000000105aec944,resolved,hitcount=29:file='/Users/xxxx/didi-ios/DiDi/FollowWinner/Model/FW_HomeModel.m',line=24,exact_match=0,locations=1,resolved=1,hitcount=09.1:where=DiDi`+[FW_HomeModel_RankparasWithDict:limitNickLength:]+89atFW_HomeModel.m:24,address=0x00000001061bc169,resolved,hitcount=0
4.5 禁用/启用断点 breakpoint disable/enable
//禁用断点(lldb)breakpoint disable91breakpoints disabled.(lldb)breakpoint listCurrentbreakpoints:9:file='/Users/zmz/didi-ios/DiDi/FollowWinner/Model/FW_HomeModel.m',line=24,exact_match=0,locations=1Options:disabled9.1:where=DiDi`+[FW_HomeModel_RankparasWithDict:limitNickLength:]+89atFW_HomeModel.m:24,address=0x00000001061bc169,unresolved,hitcount=0//启用断点(lldb)breakpoint enable91breakpoints enabled.(lldb)breakpoint listCurrentbreakpoints:9:file='/Users/zmz/didi-ios/DiDi/FollowWinner/Model/FW_HomeModel.m',line=24,exact_match=0,locations=1,resolved=1,hitcount=09.1:where=DiDi`+[FW_HomeModel_RankparasWithDict:limitNickLength:]+89atFW_HomeModel.m:24,address=0x00000001061bc169,resolved,hitcount=0
4.6 移除断点 breakpoint delete
(lldb)breakpoint delete81breakpoints deleted;0breakpoint locations disabled.(lldb)breakpoint listCurrentbreakpoints:9:file='/Users/xxxx/didi-ios/DiDi/FollowWinner/Model/FW_HomeModel.m',line=24,exact_match=0,locations=1,resolved=1,hitcount=09.1:where=DiDi`+[FW_HomeModel_RankparasWithDict:limitNickLength:]+89atFW_HomeModel.m:24,address=0x00000001061bc169,resolved,hitcount=0
5 别名
其实很多命令是LLDB一些命令的别名,比如p是frame variable的别名,p view实际上是frame variable view。除了系统自建的LLDB别名,你也可以自定义别名。
5.1 自定义别名 command alias
很多时候,LLDB完整的命令是很长的。比如前面所说的image lookup --address这个组合命令。为了方便日常的使用,提高效率,LLDB命令也提供通过简称的方式调用命令。
为了说明问题,我来个奇葩的,中文定义!!!
(lldb)command alias 查看堆栈 thread backtrace(lldb)查看堆栈5thread #5frame #0:0x000000010bd3544elibsystem_kernel.dylib`__workq_kernreturn+10frame #1:0x000000010bd69621libsystem_pthread.dylib`_pthread_wqthread+1426frame #2:0x000000010bd6907dlibsystem_pthread.dylib`start_wqthread+13
5.2 带参数别名 command alias %1 %2等等
(lldb)command alias 删除断点 breakpoint delete%1(lldb)breakpoint listCurrentbreakpoints:3:file='JSONKit.m',line=725,exact_match=0,locations=1,resolved=1,hitcount=03.1:where=TEST`-[JKArraydealloc]+20atJSONKit.m:727,address=0x00000001073360c4,resolved,hitcount=0(lldb)删除断点31breakpoints deleted;0breakpoint locations disabled.
虽然我用了中文命名,但不要学习我哦,这里只是为了说明命名不只支持英文的问题。
而且,命名尽量符合简写规律,这样更有利于使用。如help command alias中给出的例子:
(lldb)command alias bpl breakpoint list(lldb)command alias bfl breakpointset-f%1-l%2
5.3 持久存储
我们在xcode下打command alias,在下次项目运行时直接就没了,有个办法可以做到持久存储。lldb初始化时会读取~/.lldbinit文件,我们可以把简写命令写进此文件中。
添加步骤:
打开终端,输入vi ~/.lldbinit进入文件编辑。
写入自定义内容,如:command alias 堆栈 thread backtrace。
然后ESC键,出现冒号后输入wq,enter键保存并退出编辑。
Xcode中断点使用
xcode中断点其实是对lldb的一个界面化,确实使用起来特别方便。
但有一点不同:使用lldb添加的断点,下次运行时就失效了,而xcode中设置的断点仍然正常使用。
示例
1. 条件断点
![](http://img.e-com-net.com/image/info10/837caf8a3c3e45a4a17ae483ff7600df.jpg)
条件断点
2. 添加断点事件
我们项目中就有一个使用场景:用户想要发贴,会先请求一个接口,返回用户是否有发帖权限。有权限跳转发帖页,无权限跳转无权限提示页。所以我们在debug的时候,就会在返回值isHasPermisson那里添加一个断点,action中把值改为1,勾选自动继续。如下图:
![](http://img.e-com-net.com/image/info10/914ae65bf22241a58d567fc6131bbfd7.jpg)
image.png
执行结果:
![](http://img.e-com-net.com/image/info10/3e5e7aa8e827434f91ac38e8d2a1247f.jpg)
image.png
通过添加断点,可以做到偷偷改动断点处的一个参数值,避免修改代码,或者频繁手动exp修改值,简直是方便。
结语:
有了这些命令,调试起来肯定就得心应手了。总结下常用的,供多看几遍加深回忆:
堆栈相关:bt查看堆栈、frame select跳帧、frame variable查看帧参数、thread return跳出当前执行、【step/finish/next/continue】进入/跳出/下一步/跳出本断点
断点相关:breakpoint set -f -l -c条件断点、breakpoint set -n方法断点、breakpoint delete断点移除、breakpoint list断点列表
image命令:image lookup -address崩溃定位、image lookup -name方法来源、 image lookup –type 查看成员
LLDB支持简写
作者:小小棒棒糖
链接:https://www.jianshu.com/p/7fb43e0b956a
来源:
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。