基础用法
:::info 【 注意 】
- 所有指令支持 tab补全
- enter 默认重复执行上一次的指令 :::
#include <stdio.h>
void debug(char *str)
{
printf("debug info :%s\n", str);
}
void main(int argc, char *argv[])
{
int i, j;
j = 0;
for (i = 0; i < 10; i++)
{
j += 5;
printf("now a=%d\n", j);
}
}
命令 | 命令缩写 | 命令说明 |
---|---|---|
list | l | 显示多行源代码 |
break | b | 设置断点,程序运行到断点的位置会停下来 |
info | i | 描述程序的状态 |
run | r | 开始运行程序 |
display | disp | 跟踪查看某个变量,每次停下来都显示它的值 |
step | s | 执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句 |
next | n | 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句) |
p | 打印内部变量值 | |
continue | c | 继续程序的运行,直到遇到下一个断点 |
set var name=v | 设置变量的值 | |
start | st | 开始执行程序,在main函数的第一条语句前面停下来 |
file | 装入需要调试的程序 | |
kill | k | 终止正在调试的程序 |
watch | 监视变量值的变化 | |
backtrace | bt | 查看函数调用信息(堆栈) |
frame | f | 查看栈帧 f n 切换到编号为n的栈 |
quit | q | 退出GDB环境 |
list | l
潜水镜样式的源码查看:
- 默认焦点对准的是程序的入口
- l
— 指定聚焦位置到line_num行 - l
— 指定聚焦位置到某个函数 - l — 多次执行,潜水镜依次后移动
:::info
源码路径指定:
gdb —dir=<源码编译位置> 可执行文件 :::
break | b
断点的设置:
- b
- b
— tab 可以自动补全或者列出可以读到的函数名称
b funcA
b 1038
b file.c:funcA
b file.c:1038
b 1038 if i==99
断点信息查看:
info breakpoints | i b
断点删除:
**delete breakpoints | d breakpoints — 删除所有断点
delete
clear
display | disp
设置变量监控点,执行流触发时变量的值
disp
观测信息查看:
info display | i disp
set =var
设置变量
print | p
打印变量名
格式化输出
>> p/x x
$2 = 0x2a
执行流的跟踪
continue | c — 执行到下一个断点或者程序结尾
step | s — 逻辑流的单步调试
finish | fin — 跳出函数调用
next | n — 空间流的单步调试
watch 智能蹲守
wahtch i — 会将i的改动作为一个断点触发条件,这样方便跟踪变量的变化
backtrace | bt — 程序调用逻辑的追溯
backtrace | bt
bt f — 堆栈详细信息
frame | f <> — 堆栈聚焦
up
#include <stdio.h>
#include <stdlib.h>
void recurse(void)
{
static int i;
if (++i == 3)
abort();
else
recurse();
}
int main(int argc, char **argv)
{
recurse();
}
进阶用法
已有进程的操作
gstack
gdb -p pidof <Progress name>
gdb attach
汇编寄存器显微镜
检查内存
有些时候,仅仅输出一个数值还不能帮助我们查找出错误。我们需要一次性地打印出一整块内存来窥视全局。这时候我们就需要使用x命令。
x命令的格式是x/format address。其中address很简单,它通常是指向一块内存的表达式。但是format的语法就有点复杂了。它由三个部分组成:
第一个是要显示的块的数量;第二个是显示格式(如x代表16进制,d代表十进制,c代表字符);第三个是每个块的大小。值得注意的是第三部分,即块大小是用字符对应的。用b, h, w, g 分别表示1, 2, 4, 8 bytes。举例来说,用十六进制方式,打印从ptr开始的4个4-byte块应该这样写:
(gdb) x/4xw ptr
接下来举一个比较实际的例子。我们看一下NSObject类的内容:
(gdb) x/4xg (void *)[NSObject class] <br /> 0x7fff70adb468 : 0x00007fff70adb440 0x0000000000000000 <br /> 0x7fff70adb478 : 0x0000000100105ac0 0x0000000100104ac0
接下来再看看一个NSObject实例的内容:
(gdb) x/1xg (void *)[NSObject new] <br /> 0x100105ba0: 0x00007fff70adb468
现在我们看到,在实例开头引用了类的地址。
info registers rip
x /16i 0x5633b1ba311d — 汇编聚焦,以指定内存地址为起点,到后面的调用
info reg eax — 查看寄存器中的地址
thread apply all backtrace | t a a bt — 线程堆栈打印
设置触发coredump
查看当前core设置:
a) ulimit -a
b) cat /proc/sys/kernel/core_pattern
开启core并修改其位置:
a) ulimit -c unlimited
b) echo “/home/corefile/core-%e-%p-%t” > /proc/sys/kernel/core_pattern
c) echo “1” > /proc/sys/kernel/core_uses_pid
其中第3个带pid,第2个带程序名、pid、时间戳。
注意:该方法是临时修改,系统重启后失效。永久修改可以在/etc/sysctl.conf里面按语法规则追加。
编译带调试信息的二进制
生成符号表
objcopy —only-keep-debug app app.symbols
用gdb拉起进程
gdb —args app p1 p2 p3
gdb -s app.symbols -e app
多线程
info threads | info thr
多线程调试
info threads 显示当前可调试的所有线程
thread ID 切换当前调试的线程为指定ID的线程
attach process-id 在gdb状态下,开始调试一个正在运行的进程
thread apply all command 所有线程执行command
进程中追加参数
gdb —args 可执行文件 p1 p2