美文网首页
好用的 LLDB 调试一

好用的 LLDB 调试一

作者: 厨子 | 来源:发表于2018-09-14 14:51 被阅读20次

    一句话介绍LLDB:内置在 Xcode 中的调试器。能在特定的位置暂停程序运行,并观察变量的值,也可以执行自定义指令。

    好处:通过 LLDB 对程序做了修改,而不需要重新运行项目,就可以看到效果

    目录

    - Basic
       - 帮助
       - 打印一
       - expression
       - 打印二
       - 打印三
       - 打印四
       - 声明变量
       - flow control
          - finish
          - frame info
          - thread return
    - Breakpoint
       - 管理断点
       - 创建断点
          - 为 OC API 添加断点
          - 符号断点
             - 通过LLDB
             - 通过UI
          - UI上的断点
             - Breakpoint Action
       - LLDB Debugger 缺点
    

    帮助

    借助 help <command> 查看某个命令,比如,help printhelp thread

    打印一

    如图所示,print count 会输出一个以 $ 开头的变量以及一个值,这个变量可以作为这个值的引用。所以当打印 print $0 + 7 输出的结果是106.

    LLDB 支持前缀字符匹配,prin, pri, or p 都代表一个意思。

    expression

    如果要修改这个值,可以使用 expression,它修改的不仅是LLDB中的值,连带程序里面的值也修改掉!它的简写是:e

    打印二

    我们打印 print count = 30 并再次打印 print count,会发现它的作用和 expression 一样。它们之间的区别是:expression 命令可以跟参数,print 命令不跟参数。

    来看一个命令:e -h +17。它会产生歧义:是把 -h 当做标记,只计算 +17呢?还是计算17与h的差值呢?连字符-会给人造成困扰。

    对于这种情况,可以使用 -- 应对。-- 表示标记位的结束,输入的开始。

    因此,就有下面两种输入格式:

    // 使用 `-h` 做标记
    1. e -h -- +17
    // 计算它们的差值
    2. e -- -h +17
    

    e -- 就是 print

    可以通过 help print 查看它的定义:'print' is an abbreviation for 'expression --'.

    打印三

    (lldb) p objects
    (lldb) (__NSCFConstantString *) $0 = 0x000000010ee9a158 @"abcd"
    (lldb) po objects
    (lldb) @"abcd"
    

    p objects 输出包含了很多内容,如果我们只想看值abcd,请使用:e -O -- objectse -O -- 的简写是: po

    打印四

    可以为 print 指定不同的打印format,语法:print <fmt> or p <fmt>. 查看完整的 fmts 列表

    • 打印16进制:
    (lldb) p/x 16
    (int) $0 = 0x00000010
    
    • 打印2进制:
    (lldb) p/t 16
    (int) $1 = 0b00000000000000000000000000010000
    (lldb) p/t (char)16
    (char) $2 = 0b00010000
    

    声明变量

    可以在LLDB中声明自定义变量,这些变量名前需要加上美元$符号。

    (lldb) e int $a = 2
    (lldb) p $a
    (int) $a = 2
    (lldb) p $a * 19
    (int) $3 = 38
    (lldb) e NSArray *$array = @[@"a",@"b",@"c"]
    (lldb) p [$array count]
    (NSUInteger) $4 = 3
    (lldb) po [[$array objectAtIndex:0] uppercaseString]
    A
    (lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
    error: no known method '-characterAtIndex:'; cast the message send to the method's return type
    

    最后的出错信息,时有发生,可以给 LLDB 些提示信息,使其正确输出结果:

    (lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
    (char) $6 = 'c'
    (lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
    (char) $7 = 99
    

    Flow control

    程序运行到断点处,在Debug 窗口,会有下图中的几个按钮,从左到右→_→依次是:继续(continue)单步执行(step over)跳入(step in)跳出(step out)

    • continue 后,程序会继续执行,直到下一个断点处。
      它的三种等价命令:process continue == continue == c
    • step over 执行后,跳到下一行可执行代码处,也就是一行一行执行。
      它的三种等价命令:thread step-over == next == n
    • step in 执行后,跳入函数调用内部。前提是处于调试的那行代码是对某个函数的调用。如果不是函数调用,它和 step over 没有区别。
      它的三种等价命令:thread step-in == step == s
    • step out 执行后,迅速执行完这个函数,从其内部跳出。
      命令:thread step-out.
      • 跳出后,程序继续执行,直到碰到 return ,然后会停在这个地方,等待下一步调试。
    finish

    finish 命令会从函数调用中跳出。如果通过 step in 进入某个函数调用,那么,finishstep out 效果相同

    frame info

    frame info 命令会打印出当前调试行的 行数 以及 文件信息

    (lldb) frame info
    frame #0: 0x00000001026525b1 TestEasy`main(argc=1, argv=0x00007ffeed5ad0c0) at main.m:25
    
    thread return

    thread return 命令后面可以跟一个可选参数,用来表示函数返回的结果。比如,在一个包含有 return 的函数中,执行thread return xxx 那么这个函数的返回结果就是 xxx

    另外注意一点,执行 thread return 后,其断点后的代码都不会执行,而是直接返回。从上图可以看出:

    • 执行 s 后进入函数调用的 13 行
    • 在这里执行 thread return YES ,13行之后的代码都不会执行,同事返回值被设置为了 YES

    管理断点

    • breakpoint list 列出所有断点信息。简写:br li
    • breakpoint disable <breakpointID> 置灰某个断点。简写:br dis <id>
    • breakpoint enable <breakpointID> 使某个断点可用。简写:br en
    • breakpoint delete <breakpointID> 删掉某个断点。简写:br del

    创建断点

    • breakpoint set 创建断点。-f 断点所在文件,-l 第几行
    (lldb) breakpoint set -f main.m -l 28
    Breakpoint 4: where = TestEasy`main + 70 at main.m:28, address = 0x000000010a71a5c6
    
    • b 文件名:第几行 创建断点
    (lldb) b main.m:24
    Breakpoint 5: where = TestEasy`main + 27 at main.m:24, address = 0x00000001053f458b
    
    • b 函数名 创建断点。 断点会定位在函数执行的第一行,函数名必须是项目中已知的。
    (lldb) b isEven
    Breakpoint 6: where = TestEasy`isEven + 16 at main.m:13, address = 0x00000001053f4640
    
    • br s -F 函数名 创建断点。
    (lldb) br s -F isEven
    Breakpoint 7: where = TestEasy`isEven + 16 at main.m:13, address = 0x00000001053f4640
    
    符号断点

    有两种添加 Symbol Breakpoint 的方式:LLDBUI

    通过LLDB
    • breakpoint set -F "OC API声明" 下面命令给 CoreFoundation 提供的数组 API 添加断点
    (lldb) breakpoint set -F "-[NSArray objectAtIndex:]"
    Breakpoint 8: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00000001066cb330
    
    • b API声明
    (lldb) b -[NSArray objectAtIndex:]
    Breakpoint 9: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00000001066cb330
    
    通过UI

    在图形界面上添加断点 如图一:

    图一
    点击 Symbolic Breakpoint 出现图二. 图二
    • Symbol 处可以输入要断点的 OC 方法,比如 "-[NSArray objectAtIndex:]",效果等同于 breakpoint set -F "OC API声明"
    • Module 填写方法所在的模块,比如 CoreFoundation
    UI上的断点

    选择 UI 上的断点,双击编辑,会出现下图的编辑弹窗:

    • Condition 断点执行条件
    • Ignore 满足断点执行条件的前提下, X 次之后再执行此断点
    • Action 下面单独介绍
    Breakpoint Action

    Action 可以根据类型,添加多个。

    • Debugger Command 类型,输入命令 po i ,满足断点条件时,就会执行该命令
    • Log Message 类型,满足条件时输出 message 或者 朗读 message
    • Automatically continue after evaluating actions 底部复选框, 意义是 所有的 Actions 执行完后,执行 continue。相当于在最后一个 Action 后面,又加了个 continue 命令的 Action。复选框把这一步简化了。

    可以在 LLDB 命令下添加 Debugger Command 类型的 Action:

    (lldb) br s -F isEven
    Breakpoint 3: where = TestEasy`isEven + 16 at main.m:13, address = 0x000000010c34c640
    (lldb) br modify -c 'i == 110' 3 /// -c 代表 condition
    (lldb) br command add 3
    Enter your debugger command(s).  Type 'DONE' to end.
    > p i
    > DONE
    (lldb) br li 3
    3: name = 'isEven', locations = 1, resolved = 1, hit count = 0
        Breakpoint commands:
          p i
    
    Condition: i == 110
    
      3.1: where = TestEasy`isEven + 16 at main.m:13, address = 0x000000010c34c640, resolved, hit count = 0
    

    LLDB Debugger 缺点

    LLDB 支持 C/Objective-C/C++/Swift 命令调试,但也有不足之处:

    • LLDB Debugger 不能创建新的 函数,也就意味着不能在 LLDB 中创建 ,block,函数,含有虚函数的C++类

    除此之外,它都可以做。

    我们可以申请分配一些字节:

    (lldb) e char *$str = (char *)malloc(8)
    (lldb) e (void)strcpy($str,"munkeys")
    (lldb) e $str[1]
    (char) $0 = 'u'
    (lldb) e $str[1] = 'o'
    (char) $1 = 'o'
    (lldb) p $str
    (char *) $str = 0x000060000001d4b0 "monkeys"
    

    x 命令读取内存,每次读取4个字节(x 是读取内存的缩写):

    // c 作为character输出
    (lldb) x/1c $str
    0x604000010a10: monk
    

    向后偏移3位读取:

    (lldb) x/1c '$str+3'
    0x604000010a13: keys
    

    用完要释放掉,不然会有内存泄露:

    (lldb) e (void)free($str)
    

    相关文章

      网友评论

          本文标题:好用的 LLDB 调试一

          本文链接:https://www.haomeiwen.com/subject/anlswftx.html