zoukankan      html  css  js  c++  java
  • breakpoints && lldb  && chisel 的使用

    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 命令后文中会解说。这个命令的作用是在断点触发时。打印回调栈信息):

    类似以下这种数组越界的问题。我们能够非常easy就定位到问题所在。不用再毫无头绪找来找去了:


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

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



    还有类似例如以下的错误能够通过这样的断点非常easy定位到:
    ,只是这样的问题,能够通过使用setValue:forKey:取代来避免。


    OpenGL ES Error Breakpoint

    同上图中,在Xcode的breakpoint navigator的下部加入button。选择”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类型,这个很easy理解,在读,写或者读写变量或内存的时候,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”输入选项,这个的作用非常easy理解,仅仅有在设置的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> 来获得某个command的详细使用方法

    expression

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

    比如:改动变量的值:expression count = 20
    或者声明一个新变量a:


    expression命令能够带有參数。但也会带来一些问题。比如: e -h +17 命令,这个命令就easy产生混淆,究竟-h是參数flag,然后+17是输入变量。还是说要计算 -h+17 表达式的终于值。LLDB提供的解决方案很easy,使用“ -- ”来指定命令參数的终止,命令输入的開始。比如,e -h -- +17表示前文中命令的第一个解释,e -- -h+17 表示表示前文中命令的第二个解释。


    print

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

    实际上,假设在console中输入“help print”,就会得到’print’ is an abbreviation for ‘expression --‘ 这句话,也就是说print实际上就相当于 expression -- 。

    这里就非常easy理解以下命令是怎样工作的。p _lastPoiID=20。这行命令表达式会被运行。



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

    使用 print/<fmt> 或者 p/<fmt> 来格式化打印变量,能够參考相应的格式列表: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,里面包括了非常多非常有意思的命令工具。

    安装方式非常easy。使用brew工具。具体參考官方站点,不多说。


    安装之后,使用help命令。在以下能够 user-defined commands 能够找到这个框架提供的一些自己定义命令。Chisel提供了许多很实用的命令行工具,调试UI的时候很方便,详细能够參考官方站点。



    參考

  • 相关阅读:
    通过HttpListener实现简单的Http服务
    WCF心跳判断服务端及客户端是否掉线并实现重连接
    NHibernate初学六之关联多对多关系
    NHibernate初学五之关联一对多关系
    EXTJS 4.2 资料 跨域的问题
    EXTJS 4.2 资料 控件之Grid 那些事
    EXTJS 3.0 资料 控件之 GridPanel属性与方法大全
    EXTJS 3.0 资料 控件之 Toolbar 两行的用法
    EXTJS 3.0 资料 控件之 combo 用法
    EXTJS 4.2 资料 控件之 Store 用法
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/6784585.html
Copyright © 2011-2022 走看看