lldb-tutorial
Carpe Tu Black Whistle

intro

  • LLDB 是下一代高性能调试器(跟GDB比较
  • 目前可以用于调试 C/C++/Objective-C
  • doc: https://lldb.llvm.org lldb官方文档

简中教程推荐:bili-mo鱼老汉 感谢大大的教程视频
笔者,前些天,学习了公开课 MIT:the missing semester, 其中有一节lecture主题为debugger接触了ipdb,现学习完lldb之后,感觉有很多共通之处。

test code 1

main.c

1
2
3
4
5
6
#include<stdio.h>

int main() {
print("Hello, World!\n");
return 0;
}

base manipulate

clang

clang: C/C++/Objective-C 的轻量级编译器

平时编译运行c语言时命令为

clang main.c -> a.out

生成的a.out文件可以直接执行,lldb a.out也会正常启动,但是无法顺利断点

执行lldb a.out并顺利打断点需要使用clang-gflag

clang -g main.c (a.out)

此时会生成一个名为a.out.dSYM,此后就能顺利执行lldb a.out

image

run

command:r(run) 当启动lldb之后,输入命令

(lldb)r

或者是

(lldb)run

最后的结果都是,执行程序(到断点)

image

breakpoint

list

断点列表查询

command:(lldb)br list 能够列出目前存在的所有断点

image

下面会介绍打断点的方法

del

删除指定节点

dr del #

这里的#指代的不是断点所在行
而是指,br list打印出的断点表的编号

base break

breakpoint set -f src_file.c -l #
breakpoint set -f main.c -l 4

命令解析
breakpoint set: 设置断点
-f: flag后跟源文件名
-l #: stand for line,‘#’指示数字,-l 4 指在第四行打断点

命令简写

br(eakpoint) s(et) -f main.c -l 4

注意:breakpoint的缩写是br,不可以是b

打完断点后,再次执行(lldb)r程序会停止在程序的line:4

image

advance break

比起一长串施法更简洁的打断点法

b src_file.c :#

b 接源码名 :#等效-l #

也可以在特定函数处打上断点

b func_name  //没有文件名

更新 main.c 进行修改

test code 2

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

void test() { printf("This is a test func"); }

int main() {

printf("Hello world!\n");
test();

return 0;
}

此时在test()函数上打上断点

(lldb)b test

就会在test处,出现断点

image

新的断点会出现在test()函数体内的第一行。

control flow

next

n(ext): 下一步,不会进入函数内部

step

s(tep): 单步进入,进入函数

continue

c(ontinue): 直接运行到下一断点

test code 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

void test() {
int a = 10;
int b = 20;
int c = a + b;
a = a - 1;
printf("This is a test function");
}

int main() {
printf("Hello world!\n");
test();

return 0;
}

next/step/continue 三个命令可以用以上程序进行测试

输出各种变量/过程信息

varName

(lldb)p varName

会输出指定varName的变量内容

frame variable

(lldb)frame variable
(lldb)fr variable

会打印出当前栈帧中的所有变量

list frames

调用栈

(lldb)bt

会打印出所有的栈

select frame

(lldb)frame select #
(lldb)fr s #

br del后面跟的#,以上命令指代bt列出的frame序号

image

以上程序执行了以下步骤:

  1. 编译运行。
  2. 在第8行打上断点。
  3. (lldb)frame variable 打印出当前栈帧的所有变量。
  4. ‘(lldb)bt’展示所有栈帧。

对于c/c++ 程序,每次调用函数都会有新的帧入栈。栈中其他帧序号依此增大。

image

以上程序运行过程可以知道

  1. 切换帧后 * 会指示当前栈帧。
  2. 当我们继续调试,当调用栈函数运行结束后,函数帧会出栈

test code 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int myGlobalVar = 10;

void test() {
int a = 10;
int b = 20;
int c = a + b;
a = a - 1;
printf("This is a test function");
}

int main() {
printf("Hello world!\n");
test();
myGlobalVar = 30;
printf("%d\n", myGlobalVar);

return 0;
}

watchpoint

只有在程序运行过程(run)中,才能设置

(lldb)watchpoint set variable [some globalVar]
(lldb)w s v [some globalVar]

image

  1. 如果没有处于运行状态,无法设置watchpoint
  2. 设置完后执行,continue
  3. watchpoint的值改变后,断开。

进一步执行watchpoint list会打印出,监视断点列表(包含变化值)

(lldb)watchpoint list
(lldb)w list

image

kill

kill: 如果程序还在执行过程中,直接运行kill会直接结束这个进程。

若进程尚未结束 强行执行 r(un) 会提示是否kill掉当前进程