http://www.cocoachina.com/ios/20150803/12805.html

Breakpoints

BreakPoint分类

breakpoint也是有分类的,我这里的文章内大致按使用的方式分为了 Normal Breakpoint,Exception Breakpoint,OpenGL ES Error breakpoint,Symbolic Breakpoint,Test Failure Breakpoint,WatchPoints。可以按具体的情景使用不同类型的breakpoint,解决问题为根本。

Normal Breakpoint

添加普通断点就不多说了,在源代码的右侧点击一下即可。或者,使用快捷键:command + \ 来添加和删除。这两种方式添加的breakpoints在Xcode上面是可以通过UI看到的。

还有可以通过下面两个LLDB命令直接在运行时添加断点,但是这种方式需要注意的是一方面无法通过UI直接看到断点,另外一方面只存在于本次运行,下一次启动模拟器重新运行的时候,这些断点就不生效了。

如上图,通过“br li”命令打印所有的breakpoint,可以看到一共有3个breakpoint,第一个是通过Xcode的UI添加的,后面两个分别是通过下面两个命令添加的:

“breakpoint set -f XXX.m -l XX” 和  “b XXX.m:XX”。

Exception Breakpoint

可以通过下图中Xcode的UI添加Exception Breakpoint。有时候,比如数组越界或者设置一个空对象等问题,都会抛出一个异常,但是这种类型的错误非常难以定位,这个时候就可以使用Exception Breakpoint来进行调试,在异常发生时可以捕捉到并停止程序的执行。OC中的异常是一个常被忽略的地方,但实际上系统框架内这个使用非常广泛,大部分这种错误信息,系统框架都会以异常的形式throw出来,所以善用这种breakpoint的话,我们能大大减少查找错误的时间。

例如,当我们添加如下Exception Breakpoint之后(bt 命令后文中会讲解,这个命令的作用是在断点触发时,打印回调栈信息):

类似下面这样的数组越界的问题,我们可以很容易就定位到问题所在,不用再毫无头绪找来找去了:

当断点暂停执行时,我们可以通过Xcode的UI中查看调用栈信息:

或者查看bt命令打印的调用栈信息:

还有类似如下的错误可以通过这种断点很容易定位到:

,不过这种问题,可以通过使用setValue:forKey:代替来避免。

OpenGL ES Error Breakpoint

同上图中,在Xcode的breakpoint navigator的下部添加按钮,选择”Add OpenGL ES Error Breakpoint”即可。这个breakpoint主要是用来在OpenGL ES发生错误时停止程序的运行。

Symbolic Breakpoint

通过Xcode的UI添加symbolic breakpoint的方式同exception breakpoint,弹出框如下:

Symbolic breakpoints 在某个特定的函数或者方法开始执行的时候,暂停程序的执行,通过这种方式添加断点,我们就不需要知道在源文件中添加,也不需要知道断点设置在文件的第几行。

上图中,最主要的设置是Symbol的内容,可以有如下几种:

1. A method name,方法名称,例如 pathsMatchingExtensions: 这样的方法名称,会对所有类的这个方法都起作用。

2. A method of a particular class. 特定类的某个方法。例如 ,[SKLine drawHandlesInView],或者 people::Person::name()

3. A function name。函数名称。例如 ,_objc_msgForward 这样C函数。

另外,也可以通过命令行的方式添加 Symbolic breakpoints。对C函数添加断点:

对OC的方法添加断点:

常用的这个类型的断点有,objc_exception_throw可以用来代替 Exception Breakpoint,还有一个-[NSObject doesNotRecognizeSelector:] 也比较常用,用于检测方法调用失败。

Test Failure Breakpoint

通过Xcode的UI添加方法同上。这个类型的break point 会在 test assertion 失败的时候暂停程序的执行。

Watchpoints

Watuchpoints是一个用来监听变量的值的变化或者内存地址的变化的工具,发生变化时会在debugger中触发一个暂停。对于那些不知道如何准确跟踪的状态问题,可以利用这个工具来解决。要设置watchpoint的话,在程序运行到stack frame包含有你想观察的变量时,让debugger暂停运行,这个时候变量在当前stack frame的scope内,这个时候才能对该变量设置watchpoint。

你可以在Xcode的GUI中设置watchpoint,在xcode的 Variables View中,把你想观察的变量保留出来,然后右键设置“Watch XXX”。例如下图,观察self的title变量,点击 Watch “_button1ClickCount” 即可。

命令行

或者也可以通过命令行来设置watchpoint:watch set variable _button1ClickCount,详细命令可以参考:http://lldb.llvm.org/lldb-gdb.html,有好几种命令可以达到同样的效果。

上面是对变量进行观察,实际上我们可以对任意内存地址进行观察,命令如下:watchpoint set expression — 0x123456,参考:http://stackoverflow.com/questions/21063995/watch-points-on-memory-address

需要注意的是,watchpoint是分类型的,包括read,write或者read_write类型,这个非常容易理解,在读,写或者读写变量或内存的时候,watchpoint是否被触发。read,write或read_write跟着-w参数后面表示类型。另外,命令行中,watchpoint还有一些简写,set简写为s,watch简写为wa,variable简写为v。

下面的示例是来自 http://www.dreamingwish.com/article/lldb-usage-a.html 网站的几个命令:

第一个命令是监听_abc4变量的内存地址write的变化,第二个是监听_abc4变量read的变化,第三个是监听_abc3变量read_write的变化。

需要注意的是,通过Xcode的GUI添加的watchpoint为默认类型,即write类型,如果想要添加读写都watch的watchpoint,则只能通过命令行工具进行添加了。

使用watchpoint modify -c ‘(XXX==XX)’,则修改watchpoint之后在某个值的时候才会监听。

编辑选项

BreakPoint Condition

当我们通过Xcode对breakpoint进行编辑时,可以发现normal breakpoint和symbolic breakpoint都有一个”Condition”输入选项,这个的作用很容易理解,只有在设置的condition表达式为YES的情况下这些断点才会起作用。

例如,下图中的breakpoint在判断字符串相等的时候才会停止运行:

可以注意到这里使用stringWithUTF8Stirng:方法,原因在于lldb的expression parser有一个bug,不兼容非ASCII字符,需要处理一下才行,否则会报错“An Objective-C constant string's string initializer is not an array”,参考:http://stackoverflow.com/questions/17192505/error-in-breakpoint-condition

更加简单一些的例子就不说了,比如 i == 99之类的简单比较,只要表达式的结果为BOOL类型即可。

Breakpoint Actions

可以看到上面的每种breakpoint编辑选项中基本上都有“Add Action”选项,当breakpoint被触发时,都首先会执行我们设置的这些action,然后我们才能得到控制权,即Xcode上面才会显示程序停止执行的UI。这个Action通过例子比较好理解,我们通过上面那个setObject:forKey:的异常来说明。代码如下:

设置Breakpoint:

可以看到上图中,我们一共设置了3个action。第一个action,用来打印exception的详细信息,用法参考:http://stackoverflow.com/questions/17238673/xcode-exception-breakpoint-doesnt-print-details-of-the-exception-being-thrown

第二个action,我们使用shell命令“say”,让电脑发声,把一段文字读出来。

第三个action,我们使用“bt”命令来打印调用栈信息

设置完成之后,当异常发生时,我们会听到电脑发声念上图中的英文,然后在log中可以看到如下信息,第一行是Exception的描述信息,下面是调用堆栈:

Continuing after Evaluation

看一下breakpoint的编辑弹窗,我们可以发现有一个 “Automatically continue after evaluation actions” checkbox选项。当我们勾选这个checkbox之后,debugger会执行breakpoint中添加的所有的actions,然后继续执行程序。对于我们来说,除了触发一大堆command并且执行时间很长的情况之外,程序会很快跳过这个breakpoint,所以我们可能根本不会注意到这个breakpoint的存在。所以,这个选项的功能相当于在执行的最后一个action之后,直接输入continue命令继续执行。

有了这个很强大的功能,我们可以直接通过breakpoints来单独对我们的程序进行修改。在某行代码时停止执行,使用”expression”命令来直接修改程序的某个变量设置直接修改UI,然后继续执行。expression / call 配合这个选项的时候,会非常强大,可以很方便实现很多很强大的功能。

例如,我们实现一个如下的功能,把tableview的第一个cell的selectBackgroundView的背景色改为红色:

action的内容为“expression [[cell selectedBackgroundView] setBackgroundColor:[UIColor redColor]]”,这里的表达式先不用关心,我们后面LLDB章节会讲到,修改之后,当我们点击cell的时候,cell的背景就会如下图一样变红:

使用这种方式,我们在不需要修改一行代码的情况下,只需要通过修改breakpoint,就可以实现对UI的各种调试效果。

参考:

LLDB

常用命令

help

直接输入help命令,列出所有可用的commands。

使用 help来获得某个command的具体用法

expression

简写形式:expr / e。在运行调试时执行表达式,用来直接修改程序的变量的值,或者使用这个命令声明一个变量对象。

例如:修改变量的值:expression count = 20

或者声明一个新变量a:

expression命令可以带有参数,但也会带来一些问题,例如: e -h +17 命令,这个命令就容易产生混淆,到底-h是参数flag,然后+17是输入变量,还是说要计算 -h+17 表达式的最终值。LLDB提供的解决方式非常简单,使用“ -- ”来指定命令参数的终止,命令输入的开始。例如,e -h -- +17表示前文中命令的第一个解释,e -- -h+17 表示表示前文中命令的第二个解释。

print

简写形式:prin / pri / p。但是不能用pr表示,因为会和process混淆。实际上,你会发现,lldb对于命令的简称,是头部匹配方式,只要不相互混淆,可以随意简称某个命令。

实际上,如果在console中输入“help print”,就会得到’print’ is an abbreviation for ‘expression --‘ 这句话,也就是说print实际上就相当于 expression -- 。这里就很容易理解下面命令是如何工作的,p _lastPoiID=20,这行命令表达式会被执行。

打印一个对象的话,使用 po 命令,使用expression表示的话就是 expression -O -- XXX

使用 print/或者 p/来格式化打印变量,可以参考对应的格式列表:https://sourceware.org/gdb/onlinedocs/gdb/Output-Formats.html。p/x使用十六进制打印一个变量,p/t 使用二进制打印整数 ,p/c 打印字符,p/s 打印c字符串。

既然我们可以print对象和简单类型,并且在debugger中通过expression命令直接修改他们,那么我们可以通过使用一些变量来减轻我们的工作量。前面讲过我们可以直接使用expression命令来声明变量,但需要注意的是,新声明的变量必须以 $ 符号开始。

最后一个命令报错了,是因为LLDB没法确定结果的类型,需要强制转换一下来告诉LLDB。

call

调用命令,类似expression,一般用户不需要打印结果或者无返回值的地方

bt

打印调用堆栈信息,使用 bt all命令可以打印出所有thread的调用栈信息

比如说:call [self.view setBackgroundColor:[UIColor redColor]],使用这个命令来设置view controller的背景色为红色

image

image命令可用于寻址,有多个组合命令。比较实用的用法是用于寻找栈地址对应的代码位置。详细用法参考:http://www.starfelix.com/blog/2014/03/17/lldbdiao-shi-ming-ling-chu-tan/http://blog.csdn.net/hursing/article/details/8745334

常见问题

对中文的兼容

前面文章中讲过,有中文时必须使用 [NSString stringWithUTF8String:] 方法,原因在于lldb的expression parser有一个bug,不兼容非ASCII字符,需要处理一下才行,否则会报错“An Objective-C constant string's string initializer is not an array”,参考:http://stackoverflow.com/questions/17192505/error-in-breakpoint-condition

类型的问题

出现类型不确定或类型不匹配的时候,就会报错,这个时候必须强制转换才可以。

比如前文中获取indexPath的row的方法的表达式:(int)[indexPath row],或者类似下面这种干脆无返回值 p (void)NSLog(@"%@",[self.view  viewWithTag:1001]),或者上文中的 p (char)[[$array objectAtIndex:$a] characterAtIndex:0],这些场景中都需要明确输出的类型。

找不到方法

注意,使用系统框架对象的属性时不能使用dot语法,比如下图中的问题。

改成如下的格式才行:

Chisel

基于LLDB对Python插件的支持,http://lldb.llvm.org/python-reference.html,facebook开发开源了一套LLDB命令库,https://github.com/facebook/chisel,里面包含了很多很有意思的命令工具。安装方式很简单,使用brew工具,详细参考官方网站,不多说。

安装之后,使用help命令,在下面可以 user-defined commands 可以找到这个框架提供的一些自定义命令。Chisel提供了非常多很有用的命令行工具,调试UI的时候非常方便,具体可以参考官方网站。

参考

版权声明:本文为博主原创文章,未经博主允许不得转载。

breakpoints、lldb 和 chisel 的使用的更多相关文章

  1. iOS开发——测试篇&breakpoints、lldb 和 chisel 的详解

    breakpoints.lldb 和 chisel 的详解 Breakpoints BreakPoint分类 breakpoint也是有分类的,我这里的文章内大致按使用的方式分为了 Normal Br ...

  2. LLDB 和Chisel 使用例子

    打印变量 打印数字 (lldb) p/d 16 16 16 进制格式 (lldb) p/x 16 0x10 2 进制格式 (lldb) p/t 16 0b00000000000000000000000 ...

  3. iOS LLDB调试器和断点调试

    技巧一:运行时修改变量的值 你以前怎么验证是不是某个变量的值导致整段程序不能正常工作?修改代码中的变量的值,然后cmd+r重新启动app?现在你不需要这么做了,只需要设置一个断点,当程序在这进入调试模 ...

  4. iOS开发——开发技巧&LLDB详解

    开胃小菜--简单的断点调试 在xcode中打开一个app,在想要break的行号上单击,即可生成一个深色的箭头标识--断点.如下图,在viewDidLoad:中设置了断点. 运行app,等待...就可 ...

  5. 调试工具Chisel-LLDB插件

    Chisel-LLDB命令插件 相信每个人或多或少都在用LLDB来调试,比如po一个对象.LLDB的是非常强大的,且有内建的,完整的 Python 支持.今天我们主要介绍一个 facebook 开源的 ...

  6. Chisel-LLDB命令插件,让调试更Easy

    http://blog.cnbluebox.com/blog/2015/03/05/chisel/ LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器.LLDB 绑定在 ...

  7. 编译调试 .NET Core 5.0 Preview 并分析 Span 的实现原理

    很久没有写过 .NET Core 相关的文章了,目前关店在家休息所以有些时间写一篇新的

  8. 说说XcodeLLDB调试的那些事儿

    使用场景之一,接收他人的项目,快速理清其层次结构,可以打标识符断点,如下图 每一个VC,都加了个在viewDidLoad方法处的断点,这样运行程序时,逐步断点,便可以理清层次, 但是,需要手动不断的继 ...

  9. breakpoints && lldb  && chisel 的使用

    Breakpoints BreakPoint分类 breakpoint也是有分类的.我这里的文章内大致按使用的方式分为了 Normal Breakpoint,Exception Breakpoint, ...

随机推荐

  1. Lua脚本之语法基础快速入门

    要 1.基本数据类型 2.Lua中的常用语句结构以及函数 3.Lua中的常用语句结构介绍 4.Lua中的库函数 目录[-] 一.基本数据类型 二.Lua中的常用语句结构以及函数 1.Lua中的常用语句 ...

  2. mysql---多表关联

    首先要介绍一下集合的概念:集合具有无序性.唯一性. 无序性:指集合内部元素没有相对顺序的概念,对于两个集合而言,只要元素值和元素个数相同则两个集合相等. 唯一性:指集合内部元素不存在值相等的元素. 上 ...

  3. #Leet Code# Unique Path(todo)

    描述: 使用了递归,有些计算是重复的,用了额外的空间,Version 1是m*n Bonus:一共走了m+n步,例如 m = 2, n = 3 [#, @, @, #, @],所以抽象成数学问题,解是 ...

  4. ImportError: No module named pysqlite2 --安装pysqlite

    yum install sqlite-devel -y pip install pysqlite 每次使用yum安装额外的包之后都需要重新安装python,否则可能会有各种奇奇怪怪的问题出现 cd P ...

  5. MSSql ID自动增长删除数据重1开始

    dbcc checkident('db_Tome1.dbo.员工信息表',reseed,0) 注:dbcc checkident('表名',reseed,0)

  6. Hadoop学习—最大的敌人是自己

    (大讲台:国内首个it在线教育混合式自适应学习) 如果没有那次学习机会,我依然深陷在封闭的泥塘里. 我是今年刚毕业的大学生,我学习成绩不错,所学也是国内很厉害的专业,全国范围内只有6所院校拥有学位授予 ...

  7. Java学习-数组

    1.数组的是Object的直接子类,它属于“第一类对象”,但是它又与普通的java对象存在很大的不同,类名为:[I 一维数组:[I 二维数组:[[I 三维数组:[[[I 2.[代表了数组的维度,一个[ ...

  8. 一步步学习ASP.NET MVC3 (10)——@Ajax,JavaScriptResult(1)

    请注明转载地址:http://www.cnblogs.com/arhat 首先老魏先说一下抱歉,昨天由于在安装CentOS,Mono,Jexus配置Linux环境下的ASP.NET运行环境,花费了不少 ...

  9. codevs 1743 反转卡片

    题目描述 Description [dzy493941464|yywyzdzr原创] 小A将N张卡片整齐地排成一排,其中每张卡片上写了1~N的一个整数,每张卡片上的数各不相同. 比如下图是N=5的一种 ...

  10. iOS 中SQLite数据库操作

    在iOS中实现SQLite数据库的操作:1.导入框架(libsqlite3.0.tbd) 2.导入头文件<sqlite3.h> 3.实现数据的增删改查 实现简单 SQLite数据库操作 的 ...