和 LLDB 调试器来一场说跳就跳的华尔兹(二)

序:原文 Dancing in the Debugger — A Waltz with LLDB
声明:译文有一部分参考自:与调试器共舞 - LLDB 的华尔兹


续...(接上一篇)

流程控制

打断点的方法(此处不翻译)
调试工具条:

调试按钮

左—>右,依次为: continue,step over,step into,step out
第一个, continue按钮,会取消程序的暂停,允许程序正常执行 (要么一直执行下去,要么到达下一个断点)。在 LLDB 中,你可以使用 process continue命令来达到同样的效果,它的别名为 continue,或者也可以缩写为 c
第二个, step over 按钮,会以黑盒的方式执行一行代码。如果所在这行代码是一个函数调用,那么就 不会跳进这个函数,而是会执行这个函数,然后继续。LLDB 则可以使用 thread step-overnext,或者 n命令。
如果你确实想跳进一个函数调用来调试或者检查程序的执行情况,那就用第三个按钮, step into,或者在LLDB中使用 thread step instep,或者 s命令。注意,当前行不是函数调用时, nextstep效果是一样的。大多数人知道 cns,但是其实还有第四个按钮, step out。如果你曾经不小心跳进一个函数,但实际上你想跳过它,常见的反应是重复的运行 n直到函数返回。其实这种情况, step out 按钮是你的救世主。它会继续执行到下一个返回语句 (直到一个堆栈帧结束) 然后再次停止。
看个例子:
示例

运行程序,让他停止在断点,然后执行下面的系列命令:

p i
n
s
p i
finish
p i
frame info

这里,frame info会告诉你当前的行数和源码文件,还有其他一些信息;查看 help framehelp threadhelp process 来获得更多信息。这一串命令的结果会是什么?看答案之前请先想一想。

(lldb) p i
(int) $0 = 99
(lldb) n
2014-11-22 10:49:26.445 DebuggerDance[60182:4832768] 101 is odd!
(lldb) s
(lldb) p i
(int) $2 = 110
(lldb) finish
2014-11-22 10:49:35.978 DebuggerDance[60182:4832768] 110 is even!
(lldb) p i
(int) $4 = 99
(lldb) frame info
frame #0: 0x000000010a53bcd4 DebuggerDance`main + 68 at main.m:17

它始终在 17 行的原因是 finish命令一直运行到 isEven()函数的return,然后立刻停止。注意即使它还在 17 行,其实这行已经被执行过了。

Thread Return

调试时,还有一个很棒的函数可以用来控制程序流程:thread return。它有一个可选参数,在执行时它会把可选参数加载进返回寄存器里,然后立刻执行返回命令,跳出当前栈帧。这意味这函数剩余的部分不会被执行。这会给 ARC 的引用计数造成一些问题,或者会使函数内的清理部分失效。但是在函数的开头执行这个命令,是个非常好的隔离这个函数,伪造返回值的方式 。

让我们稍微修改一下上面代码段并运行:

p i
s
thread return NO
n
p even0
frame info

看答案前思考一下。下面是答案:

(lldb) p i
(int) $0 = 99
(lldb) s
(lldb) thread return NO
(lldb) n
(lldb) p even0
(BOOL) $2 = NO
(lldb) frame info
frame #0: 0x00000001009a5cc4 DebuggerDance`main + 52 at main.m:17

断点

我们都把断点作为一个停止程序运行,检查当前状态,追踪 bug 的方式。但是如果我们改变和断点交互的方式,很多事情都变成可能。

断点允许控制程序什么时候停止,然后允许命令的运行。

想象把断点放在函数的开头,然后用 thread return 命令重写函数的行为,然后继续。想象一下让这个过程自动化,听起来不错,不是吗?

断点管理

此段无用不翻译

打开 Xcode 左侧面板,右边第二个是可以快速管理所有断点的面板。如图:


管理断点的面板

在这里你可以看到所有的断点 - 在 LLDB 中可以通过 breakpoint list (或者 br li) 命令也做同样的事儿。你也可以点击单个断点来开启或关闭 - 在 LLDB 中使用 breakpoint enable breakpoint disable

(lldb) br li
Current breakpoints:
1: file = '/Users/arig/Desktop/DebuggerDance/DebuggerDance/main.m', line = 16, locations = 1, resolved = 1, hit count = 1

  1.1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab, resolved, hit count = 1

(lldb) br dis 1
1 breakpoints disabled.
(lldb) br li
Current breakpoints:
1: file = '/Users/arig/Desktop/DebuggerDance/DebuggerDance/main.m', line = 16, locations = 1 Options: disabled

  1.1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab, unresolved, hit count = 1

(lldb) br del 1
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) br li
No breakpoints currently set.
创建断点

此段无用不翻译
要在调试器中创建断点,可以使用 breakpoint set命令。

(lldb) breakpoint set -f main.m -l 16
Breakpoint 1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab

也可以使用缩写形式 br。虽然 b 是一个完全不同的命令 (_regexp-break 的缩写),但恰好也可以实现和上面同样的效果。

(lldb) b main.m:17
Breakpoint 2: where = DebuggerDance`main + 52 at main.m:17, address = 0x000000010a3f6cc4

也可以在一个符号 (C 语言函数) 上创建断点,而完全不用指定哪一行 :

(lldb) b isEven
Breakpoint 3: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00
(lldb) br s -F isEven
Breakpoint 4: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00

这些断点会准确的停止在函数的开始。Objective-C 的方法也完全可以:

(lldb) breakpoint set -F "-[NSArray objectAtIndex:]"
Breakpoint 5: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x000000010ac7a950
(lldb) b -[NSArray objectAtIndex:]
Breakpoint 6: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x000000010ac7a950
(lldb) breakpoint set -F "+[NSSet setWithObject:]"
Breakpoint 7: where = CoreFoundation`+[NSSet setWithObject:], address = 0x000000010abd3820
(lldb) b +[NSSet setWithObject:]
Breakpoint 8: where = CoreFoundation`+[NSSet setWithObject:], address = 0x000000010abd3820

如果想在 Xcode 创建符号断点,方法如图:

和 LLDB 调试器来一场说跳就跳的华尔兹(二)_第1张图片
创建符号断点

编辑符号断点

这时会出现一个弹出框,你可以在里面添加例如 -[NSArray objectAtIndex:]
这样的符号断点。这样 每次调用这个函数的时候,程序都会停止,不管是你调用还是苹果调用。

如果你 Xcode 的 UI 上右击任意断点,然后选择 "Edit Breakpoint"的话,会有一些非常诱人的选择。


这里,断点已经被修改为 只有i是99的时候才会停止。你也可以使用 "ignore"选项来告诉断点最初的 n次调用 (并且条件为真的时候) 的时候不要停止。

接下来介绍 'Add Action' 按钮...

断点行为 (Action)

上面的例子中,你或许想知道每一次到达断点的时候 i 的值。我们可以使用 p i 作为断点行为。这样每次到达断点的时候,都会自动运行这个命令。


也可以添加多个行为,可以是调试器命令,shell 命令,也可以是更直接的打印:

可以看到它打印 i,然后大声念出那个句子,接着打印了自定义的表达式。

下面是在 LLDB 做这些的时候:

(lldb) breakpoint set -F isEven
Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00
(lldb) breakpoint modify -c 'i == 99' 1
(lldb) breakpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> p i
> DONE
(lldb) br li 1
1: name = 'isEven', locations = 1, resolved = 1, hit count = 0
    Breakpoint commands:
      p i

Condition: i == 99

  1.1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00, resolved, hit count = 0 

接下来说说自动化。

赋值后继续运行

看编辑断点弹出窗口的底部,你还会看到一个选项:*"Automatically continue after evaluation actions."*。它仅仅是一个选择框,但是却很强大。选中它,调试器会运行你所有的命令,然后继续运行。看起来就像没有执行任何断点一样 (除非断点太多,运行需要一段时间,拖慢了你的程序)。
这个选项框的效果和让最后断点的最后一个行为是continue一样。选框只是让这个操作变得更简单。调试器的输出是:

(lldb) breakpoint set -F isEven
Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00
(lldb) breakpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> continue
> DONE
(lldb) br li 1
1: name = 'isEven', locations = 1, resolved = 1, hit count = 0
    Breakpoint commands:
      continue

  1.1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00, resolved, hit count = 0

执行断点后自动继续运行,允许你完全通过断点来修改程序!你可以在某一行停止,运行一个 expression 命令来改变变量,然后继续运行。

#######示例
想想所谓的"打印调试"技术吧,不要这么做:

NSLog(@"%@", whatIsInsideThisThing);

而是用个打印变量的断点替换 log 语句,然后继续运行。

也不要:

int calculateTheTrickyValue {
  return 9;

  /*
   Figure this out later.
   ...
}

而是加一个使用 thread return 9 命令的断点,然后让它继续运行。

符号断点加上 action 真的很强大。你也可以在你朋友的 Xcode 工程上添加一些断点,并且加上大声朗读某些东西的 action。看看他们要花多久才能弄明白发生了什么。装逼必备神器

完全在调试器内运行

此部分感觉无用不翻译

未完待续...

移步 和 LLDB 调试器来一场说跳就跳的华尔兹(三)
接下来才是 LLDB 在项目中的实际使用场景,不容错过喔

你可能感兴趣的:(和 LLDB 调试器来一场说跳就跳的华尔兹(二))