iOS调试 - LLDB

开发iOS的时候常常会用到调试跟踪,如何正确的使用调试器来debug十分重要。
Xcode里有内置的Debugger,老版使用的是GDB,Xcode自4.3之后默认使用的就是LLDB了。

GDB: UNIX及UNIX-like下的调试工具。

LLDB: 开源的内置于XCode的具有REPL(read-eval-print-loop)特征的Debugger,其可以安装C++或者Python插件。

两个都是调试用的Debugger,只是LLDB是比较高级的版本,或者在调试开发iOS应用时比较好用,lldb与gdb命令名的对照表:http://lldb.llvm.org/lldb-gdb.html

开始使用LLDB

在什么地方可以输入这个命令?
首先, 在程序里你需要的地方设置断点。
当断点断住的时候你就能看到我们进入LLDB调试器了。

这时就可以使用一些LLDB命令来进行一些调试了。

一些Xcode调试快捷键:
command+shift+Y 打开调试窗口
command+Y 调试运行程序
command+option+P 继续
command+shift+O 跳过
command+shift+I 进入
command+shift+T 跳出

常用命令

<li>help</li>

最简单命令 help 会列举出所有的命令。如果你忘记了一个命令是做什么的,或者想知道更多的话,你可以通过 help <command> 来了解更多细节,例如 help print 或者 help thread。


<li>print</li>

试试 print 命令:



LLDB 实际上会作前缀匹配。所以你也可以使用 prin,pri,或者 p。但你不能使用 pr,因为 LLDB 不能消除和 process 的歧义。

结果中有个 $0。实际上你可以使用它来指向这个结果。试试 print $0 + 7,你会看到 106。任何以美元符开头的东西都是存在于 LLDB 的命名空间的,它们是为了帮助你进行调试而存在的。

输出view 下 subview 的数量

//由于 subview 的数量是一个 int 类型的值,所以我们使用命令p:
(lldb)p (int)[[[self view] subviews] count]

直接调用方法改变背景颜色之类

其实使用p,po,call都可以调用方法,只是p和po都是用于输出的有返回值的。call一般只在不需要显示输出,或是方法无返回值时使用。
(lldb)p [self.view setBackgroundColor:[UIColor redColor]]
(lldb)p (void)[CATransaction flush]
上述的p一般使用call比较好,因为方法是没有返回值的。

<li>p objects</li>

命令p objects跟p很像。p输出的是基本类型,po输出的Objective-C对象。调试器会输出这个 object 的 description。
po (print object 的缩写):


<li>expression</li>

如果想改变一个值怎么办?其实这时候我们要用到的是 expression 这个方便的命令。
expression的简写就是e。可以用expression来声明新的变量,也可以改变已有变量的值。我们看到e声明的都是$开头的变量。我们在使用时也需要加上$符号。

创建新的变量示例:



注意:如果上面这里输入以下命令,会发生错误。说明lldb无法判定某一步的计算结果是什么数据类型,这时需要强制类型转换来告诉lldb。

(lldb) p [[$array objectAtIndex:0] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
error: 1 errors parsing expression

(lldb) p (char)[[$array objectAtIndex:0] characterAtIndex:0]
'o'

修改已有变量示例:


<li>image</li>

image 命令可用于寻址,有多个组合命令。
比较实用的用法是用于寻找栈地址对应的代码位置。
如:

NSArray *arr=[[NSArray alloc] initWithObjects:@"1",@"2", nil];
NSLog(@"%@",arr[2]);

这段代码有明显的错误,程序运行这段代码后会抛出下面的异常:

 *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(
 0   CoreFoundation                      0x0000000101951495 __exceptionPreprocess + 165
 1   libobjc.A.dylib                     0x00000001016b099e objc_exception_throw + 43
2   CoreFoundation                      0x0000000101909e3f -[__NSArrayI objectAtIndex:] + 175
3   ControlStyleDemo                    0x0000000100004af8 -[RootViewController viewDidLoad] + 312
4   UIKit                               0x000000010035359e -[UIViewController loadViewIfRequired] + 562
5   UIKit                               0x0000000100353777 -[UIViewController view] + 29
6   UIKit                               0x000000010029396b -[UIWindow addRootViewControllerViewIfPossible] + 58
7   UIKit                               0x0000000100293c70 -[UIWindow _setHidden:forced:] + 282
8   UIKit                               0x000000010029cffa -[UIWindow makeKeyAndVisible] + 51
9   ControlStyleDemo                    0x00000001000045e0 -[AppDelegate application:didFinishLaunchingWithOptions:] + 672
10  UIKit                               0x00000001002583d9 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 264
11  UIKit                               0x0000000100258be1 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1605
12  UIKit                               0x000000010025ca0c -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 660
13  UIKit                               0x000000010026dd4c -[UIApplication handleEvent:withNewEvent:] + 3189
14  UIKit                               0x000000010026e216 -[UIApplication sendEvent:] + 79
15  UIKit                               0x000000010025e086 _UIApplicationHandleEvent + 578
16  GraphicsServices                    0x0000000103aca71a _PurpleEventCallback + 762
17  GraphicsServices                    0x0000000103aca1e1 PurpleEventCallback + 35
18  CoreFoundation                      0x00000001018d3679 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
19  CoreFoundation                      0x00000001018d344e __CFRunLoopDoSource1 + 478
20  CoreFoundation                      0x00000001018fc903 __CFRunLoopRun + 1939
21  CoreFoundation                      0x00000001018fbd83 CFRunLoopRunSpecific + 467
22  UIKit                               0x000000010025c2e1 -[UIApplication _run] + 609
23  UIKit                               0x000000010025de33 UIApplicationMain + 1010
24  ControlStyleDemo                    0x0000000100006b73 main + 115
25  libdyld.dylib                       0x0000000101fe95fd start + 1
26  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

现在,我们怀疑出错的地址是0x0000000100004af8(可以根据执行文件名判断,或者最小的栈地址)。为了进一步精确定位,我们可以输入以下的命令:

(lldb)image lookup --address 0x0000000100004af8
(lldb)im loo -a 0x0000000100004af8

命令执行后返回:

Address: ControlStyleDemo[0x0000000100004af8] (ControlStyleDemo.__TEXT.__text + 13288)
Summary: ControlStyleDemo`-[RootViewController viewDidLoad] + 312 at RootViewController.m:53

可以看到,出错的位置是<code>RootViewController.m</code>的第53行。

<li>call</li>

call即是调用的意思。其实上述的po和p也有调用的功能。
因此一般只在不需要显示输出,或是方法无返回值时使用call。
和上面的命令一样,我们在viewDidLoad:里面设置断点,然后在程序中断的时候输入下面的命令:

call [self.view setBackgroundColor:[UIColor redColor]]

继续运行程序,看看view的背景颜色是不是变成红色的了!
在调试的时候灵活运用call命令可以起到事半功倍的作用。

<li>bt</li>

打印调用堆栈,加all可打印所有thread的堆栈。

<li>流程控制命令</li>

实际上使用xcode自带的可视化工具来控制“继续”“暂停”“下一步”“进入”“跳出”更简单,但这里还是列出其所对应的命令名:

继续:process continue, continue, c

下一步:thread step-over, next, n

进入:thread step-in, step, s

跳出:thread step-out, finish, f

<li>thread return</li>

执行thread return命令可以使得当前函数立即返回,也就是说,后续代码都不会执行了。当然执行此命令可能会使得arc的计数追踪出现错乱。

thread return命令需要一个参数来指明函数强制返回时的返回值。

<li>断点命令</li>

断点有很多进阶使用方法:条件断点、条件执行、记录日志、自动继续、重复断点跳过。
使用xcode提供的可视化工具来操作是很容易的:


<li>调试中执行任意代码</li>

(lldb) e char *$str = (char *)malloc(128)
(lldb) e (void)strcpy($str, "wxrld of warcraft")
(lldb) e $str[1] = 'o'
(char) $0 = 'o'
(lldb) p $str
(char *) $str = 0x00007fd04a900040 "world of warcraft"
(lldb) e (void)free($str)

所以,在debugger中可以修改view的颜色、尺寸、甚至创建controller来push。

<li>watchpoint</li>

watchpoint可以在某个变量被写入/读取时暂停程序运行:

(lldb) watchpoint set expression -- (int*)&_abc4
Watchpoint created: Watchpoint 7: addr = 0x15e36d3c size = 4 state = enabled type = w
    new value: 0x00000000
(lldb) watchpoint set v -w read _abc4
Watchpoint created: Watchpoint 8: addr = 0x15e36d3c size = 4 state = enabled type = r
    watchpoint spec = '_abc4'
    new value: 0
(lldb) watchpoint set v -w read_write _abc3
Watchpoint created: Watchpoint 9: addr = 0x15e36d38 size = 4 state = enabled type = rw
    watchpoint spec = '_abc3'
    new value: 0

实际上可以使用watchpoint来监视任意一段内存的读写。
使用XCode也可以方便地创建watchpoint。

XCode的可视化debug工具中的watch是一个write类型watchpoint(也就是默认的)

另外,上述语句中 v是variable的简写,同样的,set可以简写为s,watch可以简写为wa,而-w后面的参数是不可以简写的必须为read、write或者read_write。

当前在arm和x86上,我们一次最多创建4个watchpoint,继续创建会提示错误。

<li>查看内存</li>

使用XCode的可视化工具来查看memory,要注意watch memory of "p" 和watch memory of "*p"的区别。

手动执行命令可以help x或者 help memory。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • [转]浅谈LLDB调试器文章来源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc阅读 2,480评论 2 6
  • Xcode的使用中总是离不开调试这个环境,在一年多的iOS开发时间中,我更多地依赖于XCode本身提供的GUI工具...
    王小明if阅读 17,535评论 8 94
  • 转载 与调试器共舞 - LLDB 的华尔兹: https://objccn.io/issue-19-2/ 推荐:i...
    F麦子阅读 3,325评论 0 10
  • 前言 今天花了一天的时间终于把iOS的几种常见的调试方法给学习了一下,在这里给大家分享一下LLDB的使用,同时也是...
    Peak_One阅读 10,991评论 5 33
  • 被阴冷启蒙 那一池的清澄 输给了不绝的寒风 瑟瑟的冷 是被严冬同化的余生声 风本无依 自无所系 不了解它的秘密 就...
    琴歌素简阅读 200评论 0 0