iOS lldb断点调试

  Xcode的使用中总是离不开调试这个环境,在一年多的iOS开发时间中,我更多地依赖于XCode本身提供的GUI工具来进行调试,而对LLDB敬而远之,这段时间好好学习了LLDB的使用,发觉我错过了太多东西了……因此做一个比较完备的总结,也希望在写这篇文章的过程中进一步学习LLDB调试的各种实践方法。

LLDB阐述

LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展。

lldb下,相信大家用的较多的是po,或者使用条件断点。但是面对复杂的问题时,比如.a静态库,就需要其他调试方法了。

breakpoint

给某个文件的某一行下断点。可以使用如下两种方法,比如我想给Person.m文件的10行下一个断点。可以使用如下的方法。

(lldb) breakpoint set --file Person.m --line 10

如果出现如下提示则说明设置断点成功

Breakpoint 2: where = BreakPointDemo`-[Person foo] + 23 at Foo.m:10, address = 0x000000010b22e687

也可以使用简写的形式如下。

(lldb) breakpoint set -f Foo.m -l 10

给一个函数下断点

(lldb) breakpoint set --name foo
(lldb) breakpoint set -n foo

一次性给多个函数下断点

(lldb) breakpoint set --name foo --name bar

OC的方法,可以使用以下两种方式打断点,第二种S需要大写

(lldb) breakpoint set --selector foo

(lldb) breakpoint set -S foo

使用 正则匹配 你要打断点的函数。这个不限语言

(lldb) breakpoint set -r cFoo
(lldb) breakpoint set -r foo

也可以指定加载的动态库

(lldb) breakpoint set --shlib foo.dylib --name foo 
(lldb) breakpoint set -s foo.dylib -n foo

我们同样可以对命令进行简写。下面两个命令的效果是一样的

(lldb) breakpoint set -n "-[Foo foo]"
(lldb) br s -n "-[Foo foo]"

查看有多少断点可以使用

(lldb) breakpoint list

打印结果如下:

  Current breakpoints:
  1: file =       '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewCo ntroller.m', line = 20, exact_match = 0, locations = 0 (pending)
  2: file = '/Users/jianquan/Xcode/BreakPointDemo/BreakPointDemo/ViewController.mm', line = 33, exact_match = 0, locations = 1, resolved = 1, hit count = 0
  2.1: where = BreakPointDemo`::-[ViewController viewDidLoad]() + 186 at ViewController.mm:34, address = 0x0000000105f8362a, resolved, hit count = 0 
  ......

我们可以对断点进行相关的操作,比如在执行到2.1断点的时候打印追踪轨迹。bt是

(lldb) breakpoint command add 2.1
Enter your debugger command(s).  Type 'DONE' to end.
> bt
> DONE

删除所有断点

(lldb) breakpoint delete

watchpoint

观察变量值的具体变化
比如我需要观察某个变量a的值变化,我可以使用如下命令

(lldb) watchpoint set variable a

bt命令用来追踪程序的运行过程

(lldb) bt
* thread #1: tid = 0x5c52c2, 0x000000010ff465fe     BreakPointDemo`::-[ViewController viewDidLoad](self=0x00007f932cc07c50, _cmd="viewDidLoad") + 158 at ViewController.mm:36, queue = 'com.apple.main-thread', stop reason = watchpoint 1
* frame #0: 0x000000010ff465fe BreakPointDemo`::-[ViewController viewDidLoad](self=0x00007f932cc07c50, _cmd="viewDidLoad") + 158 at ViewController.mm:36
frame #1: 0x000000011112ba3d UIKit`-[UIViewController loadViewIfRequired] + 1258
 ......

我们可以使用frame命令查看变量a的具体值。

(lldb) frame variable a
(int) a = 100

补充一点watchpoint list的东西。这个命令包括了三个可选参数,我们可以使用help命令查看具体的值

(lldb) help watchpoint list

   -b ( --brief )
        Give a brief description of the watchpoint (no location info).

   -f ( --full )
        Give a full description of the watchpoint and its locations.

   -v ( --verbose )
        Explain everything we know about the watchpoint (for debugging
        debugger bugs).

-b是比较简略的信息,-f是比较全面的信息,-v是完整的信息。经过我的实验,如果使用watchpoint list,默认的是 watchpoint list -f。

process

使用process命令也可以做很多有趣的操作。具体能做什么,我们也可使用help命令查看

(lldb) process help
  attach    -- Attach to a process.
  connect   -- Connect to a remote debug service.
  continue  -- Continue execution of all threads in the current process.
  detach    -- Detach from the current target process.
  handle    -- Manage LLDB handling of OS signals for the current target
......

查看更详细的命令使用help <command> <subcommand>。比如

 (lldb) help process attach

thread

其实这个功能主要就是断点调试里面的如下这个功能。

1654054-8d533324eaa728a8.png

我们可以使用thread命令来做一些断点的操作,具体有那些命令我们可以使用thread help进行查看

(lldb) thread help

  ......

  select         -- Change the currently selected thread.
  step-in        -- Source level single step, stepping into calls. 
                    Defaults to current thread unless specified.
  step-inst      -- Instruction level single step, stepping into calls. 
                    Defaults to current thread unless specified.
  step-inst-over -- Instruction level single step, stepping over calls. 
                    Defaults to current thread unless specified.
  step-out       -- Finish executing the current stack frame and stop after
                    returning.  Defaults to current thread unless
                    specified.
  step-over      -- Source level single step, stepping over calls. 
                    Defaults to current thread unless specified.
  step-scripted  -- Step as instructed by the script class passed in the -C
                    option.
  until          -- Continue until a line number or address is reached by
                    the current or specified thread.  Stops when returning
                    from the current function as a safety measure.

用得比较多的应该是 step-开头的这几个命令,使用起来很容易。我个人感觉比用鼠标点击断点好用多了~
检查当前进程的状态,可以使用如下命令

 lldb)  thread list
Process 22323 stopped
* thread #1: tid = 0x62d0d7, 0x00000001082185fe BreakPointDemo`::-[ViewController viewDidLoad](self=0x00007ff81b60ab20, _cmd="viewDidLoad") + 158 at ViewController.mm:36, queue = 'com.apple.main-thread', stop reason = step until
......

*表明的就是当前的线程,可以使用如下的命令得到线程的回溯,这个词我也不确定怎么表达好,backtrace,也可以说是追踪。

(lldb) thread backtrace
* thread #1: tid = 0x354e72, 0x0000000103ce94bc RunTimeUse`-[ViewController viewDidLoad](self=0x00007ffea04050e0, _cmd="viewDidLoad") + 140 at ViewController.m:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000103ce94bc RunTimeUse`-[ViewController viewDidLoad](self=0x00007ffea04050e0, _cmd="viewDidLoad") + 140 at ViewController.m:24
frame #1: 0x0000000104e21c99 UIKit`-[UIViewController loadViewIfRequired] + 1258
frame #2: 0x0000000104e220cc UIKit`-[UIViewController view] + 27
frame #3: 0x0000000104cebc51 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 71
frame #4: 0x0000000104cec3a2 UIKit`-[UIWindow _setHidden:forced:] + 293
frame #5: 0x0000000104cffcb5 UIKit`-[UIWindow makeKeyAndVisible] + 42
frame #6: 0x0000000104c78c89 UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4818
frame #7: 0x0000000104c7ede9 UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1731
frame #8: 0x0000000104c7bf69 UIKit`-[UIApplication workspaceDidEndTransaction:] + 188
frame #9: 0x0000000107dd4723 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
frame #10: 0x0000000107dd459c FrontBoardServices`-[FBSSerialQueue _performNext] + 189
frame #11: 0x0000000107dd4925 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
frame #12: 0x0000000104802311 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #13: 0x00000001047e759c CoreFoundation`__CFRunLoopDoSources0 + 556
frame #14: 0x00000001047e6a86 CoreFoundation`__CFRunLoopRun + 918
frame #15: 0x00000001047e6494 CoreFoundation`CFRunLoopRunSpecific + 420
frame #16: 0x0000000104c7a7e6 UIKit`-[UIApplication _run] + 434
frame #17: 0x0000000104c80964 UIKit`UIApplicationMain + 159
frame #18: 0x0000000103ce9b1f RunTimeUse`main(argc=1, argv=0x00007fff5bf165e8) + 111 at main.m:14
frame #19: 0x000000010763d68d libdyld.dylib`start + 1
frame #20: 0x000000010763d68d libdyld.dylib`start + 1

当然我们如果想看所有线程的backtrace,可以使用thread backtrace all命令。内容太多,我这里就不演示log输出了。

如果我们想单独查看某个线程,我们可以先使用thread select 2跳到某个具体的线程,然后再进行其他操作,比如thread backtrace

为了方便的观测架构参数和本地变量,我们可以使用 frame variable 命令

如果我什么参数也不加,将会把所有的参数和本地变量到打印出来。

(lldb) frame variable
(ViewController *) self = 0x00007ffea04050e0
(SEL) _cmd = "viewDidLoad"
(Person *) p = 0x0000610000001940

要打印某个变量需要在参数里面指定,这个命令我们在前面也使用过,比如要查看self

(lldb) frame variable self
(ViewController *) self = 0x00007ff81b60ab20

更进一步,我们可以查看一些子元素

(lldb) frame variable self->isa
(Class) self->isa = ViewController

命令虽然不是完整的表达式解释器,当时可以识别一些基本的操作 比如 &, *, ->, [],不是重载运算符,数组也可以使用,因为数组本身也是指针。

(lldb) frame variable *self 

(ViewController) *self = {
UIViewController = {
UIResponder = {
  NSObject = {
    isa = ViewController
  }
......
}

和之前thread命令很类似,我可以使用frame select去选择另外的一个frame

(lldb) frame select 9

如果想看更复杂的数据,我们可以使用expression命令

(lldb) expression self
(ViewController *) $0 = 0x00007fefa4705110

更复杂一些,我们可以用来输出一个表达式

(lldb) expr (int) printf ("I have a pointer 0x%llx.\n", self)
I have a pointer 0x7fefa4705110.
(int) $1 = 33
call

其实这个命令完全可以使用po进行替代,call一般可以用来调用不需要返回值的调试命令,比如更改View的背景颜色,以下两个命令都可以达到相似的作用,更改当前View的背景颜色值。

(lldb) po [self.view setBackgroundColor:[UIColor redColor]]
(lldb) call [self.view setBackgroundColor:[UIColor redColor]]
image

这个比较实用,可用于寻找栈地址对应的代码位置。

//测试image命令使用
NSArray *a = @[@"1"];
NSLog(@"%@",a[1]);
很显然,数组越界了,以下显示崩溃信息

RunTimeUse[46698:3510999] *** Terminating app due to     uncaught exception 'NSRangeException', reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(
0   CoreFoundation                      0x000000010934d34b __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x0000000108dae21e objc_exception_throw + 48
2   CoreFoundation                      0x00000001093a5bdf -[__NSSingleObjectArrayI objectAtIndex:] + 111
3   RunTimeUse                          0x00000001087d9480 -[ViewController viewDidLoad] + 320
4   UIKit                               0x0000000109911c99 -[UIViewController loadViewIfRequired] + 1258
5   UIKit                               0x00000001099120cc -[UIViewController view] + 27
6   UIKit                               0x00000001097dbc51 -[UIWindow addRootViewControllerViewIfPossible] + 71
7   UIKit                               0x00000001097dc3a2 -[UIWindow _setHidden:forced:] + 293
8   UIKit                               0x00000001097efcb5 -[UIWindow makeKeyAndVisible] + 42
9   UIKit                               0x0000000109768c89 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4818
10  UIKit                               0x000000010976ede9 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1731
11  UIKit                               0x000000010976bf69 -[UIApplication workspaceDidEndTransaction:] + 188
12  FrontBoardServices                  0x000000010c8c4723 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
13  FrontBoardServices                  0x000000010c8c459c -[FBSSerialQueue _performNext] + 189
14  FrontBoardServices                  0x000000010c8c4925 -[FBSSerialQueue _performNextFromRunLoopSource] + 45
15  CoreFoundation                      0x00000001092f2311 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
16  CoreFoundation                      0x00000001092d759c __CFRunLoopDoSources0 + 556
17  CoreFoundation                      0x00000001092d6a86 __CFRunLoopRun + 918
18  CoreFoundation                      0x00000001092d6494 CFRunLoopRunSpecific + 420
19  UIKit                               0x000000010976a7e6 -[UIApplication _run] + 434
20  UIKit                               0x0000000109770964 UIApplicationMain + 159
21  RunTimeUse                          0x00000001087d9acf main + 111
22  libdyld.dylib                       0x000000010c12d68d start + 1
)

libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
程序奔溃在3,地址为:0x00000001087d9480,

因为我的Demo名字叫RunTimeUse。其他的名字很明显是系统的库。虽然log的21行也有RunTimeUse,但是经过观察应该是main函数,不在考虑范围之内。
我们使用image的 lookup命令,可以很快的定位到具体的代码行。

  (lldb) image lookup --address 0x00000001087d9480
  Address: RunTimeUse[0x0000000100001480] (RunTimeUse.__TEXT.__text + 320)
  Summary: RunTimeUse`-[ViewController viewDidLoad] + 320 at ViewController.m:28
1654054-7dd117c55d5e5772.png

可以看出,奔溃在ViewController.m中的第28行。

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

推荐阅读更多精彩内容

  • LLDB的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xc...
    CoderSC阅读 1,346评论 0 2
  • [转]浅谈LLDB调试器文章来源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc阅读 2,481评论 2 6
  • 前言 今天花了一天的时间终于把iOS的几种常见的调试方法给学习了一下,在这里给大家分享一下LLDB的使用,同时也是...
    Peak_One阅读 10,991评论 5 33
  • 转载 与调试器共舞 - LLDB 的华尔兹: https://objccn.io/issue-19-2/ 推荐:i...
    F麦子阅读 3,325评论 0 10
  • 不同于大年初一的阳光普照、初二云卷云舒,今儿(初三)寒风透骨把我从户外压回了室内。这时刻多数人还在梦国,我随...
    变脸加菲喵阅读 404评论 0 3