gdb调试
0: gdb的功能 :
gdb通过ptrace()这个系统调用实现对一个运行的程序进行调试
(0-1):启动你的程序,可以按照你自定义的要求随心所欲的运行程序。
(0-2):可以让调试程序在你所指定的位置的断点处停止。
(0-3):当程序停止时,可以检查此时你的程序中所发生的事情。
(0-4):动态的改变你程序的执行环境。
(0-5):调试运行程序。
1:编译生成执行文件:(linux下)
gcc -g test.c -o testcpp (C++下 g++ -g test.c -o testcpp)
2:gdb启动方式: 以下的testcpp为执行文件
(2-1):gdb testcpp_program
(2-2): gdb testcpp_program core, 使用bt查看调用栈,以及寄存器现场信息。
(2-3): gdb testcpp_program <PID> , 这个常见的有 gdb attach pid;gdb -p pid;
3:gdb常用参数
(gdb) l :(字母l)从第一行开始列出源码。
(gdb) break n :在第n行处设置断点
(gdb) break func:在函数func()的入口处设置断点
(gdb) info break: 查看断点信息
(gdb) r:运行程序,当遇到断点后,程序会在断点出停止运行,等待用户输入下一个gdb命令
(gdb) n:单步执行(不进入调用函数内部)
(gdb) c:继续运行
(gdb) p 变量名 :打印/查看变量。 p <para_name> = xx; 设置变量
(gdb) bt:查看函数堆栈
(gdb)f 栈行数: 查看栈帧
(gdb) s:单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数
(gdb) 回车: 重复上一条命令
(gdb)delete 断点号n:删除第n个断点 等价于clear n (这里的n是断点所在行数)
(gdb)set args:设置运行程序时的命令行参数,如:set args 33 55
(gdb)watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。如: watch a
(gdb)quit:简记为 q ,退出gdb
(gdb) gdb attch pid (此pid为正在运行的进程)
(gdb) detach (结束attch的pid, 之后再quit)
(gdb) b 文件名:行号 (断点打在某一行)
(gdb) info threads(当前进程所有的线程信息,*所指向的为当前线程)
(gdb) handle SIGUSR1 noprint nostop; handle SIGUSR2 noprint nodtop; (信号屏蔽)
(gdb) gdb -p pid -tui (一边看源码一边gdb, gdb -p pid等价于gdb attach)
(gdb) finish (跳出当前进入的函数)
(gdb) thread [thread number] (跳转到指定的线程)
(gdb) frame <n> (选择第n层调用栈帧)
(gdb) gdb跟踪一个正在运行的进程(注意这个是不设置端点的做法。设置断点的话,就需要 b 之后c)
(gdb) gdb XXX | tee -a file 将gdb记录的东西显示定向到file文件中
(gdb) infro threads 查看当前进程的所有线程信息
(gdb) t 线程号 切换线程
(gdb) CTRL+X+A 查看代码
(gdb) thread apply all bt 查看所有线程堆栈
(gdb) thread apply ID! ID2 command 在线程ID1 ID2执行GDB命令command
(gdb) b xxxx thread thread_ID 在线程上打断点
(gdb) set scheduler-locking off|on|step
off 不锁定任何线程。所有线程都执行,默认值
on 只有当前被调试程序线程会执行
step命令调试线程时,其他线程不会执行。用next命令调试线程,其他线程会执行
注意:使用完放开
(gdb) set print element 100 多打印
(gdb) b func_name if 变量==XXX (字符串类型需要使用[0]=='X' 来判断了)
(gdb) 条件变量 set A=XX
4: gdb 调试core文件
(4-1):输入ulimit -c 如果结果为0,说明当程序崩溃时,系统并不能生成core dump。
(4-2):使用ulimit -c unlimited命令,开启core dump功能,并且不限制生成core dump文件的大小。如果需要限制,加数字限制即可。ulimit - c 1024
(4-3):默认情况下,core dump生成的文件名为core,而且就在程序当前目录下。新的core会覆盖已存在的core。通过修改/proc/sys/kernel/core_uses_pid文件,可以将进程的pid作为作为扩展名,生成的core文件格式为core.xxx,其中xxx即为pid
(4-4):通过修改/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件格式。例如:将所有的core文件生成到/corefile目录下,文件名的格式为core-命令名-pid-时间戳. echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
5: 参考博客GDB调试工具总结 - 简书
6: gdb查看内存地址
(1) 查看内存地址 examine (缩写 x)
x/<n/f/u> <addr>
其中输出格式包括:
x: 16进制 d: 10进制 o: 8进制
eg:
x/10wx addr 查看10个字节按16进制输出
x/10i addr 查看该地址的10条指令
7: watch内存 以及 info register 查看寄存器
watch expr : GDB在expr被程序写及其值改变时停止
rwatch expr: GDB在expr被程序读时停止
watrch *(int*)addr : 查看一个地址内存是否有变化
i r: 查看当前栈的所有寄存器信息。
8: gdb常用的技巧
一般进程都有心跳机制,使用gdb需要先关闭心跳功能,避免进程长时间断住导致重启。
(1)define 循环打印线程栈
(gdb)define all_bt;
set $start=1
set $stop=2
while $start <= $stop
thread $start
bt
set $start=$start+1
end
end
(2)gdb脚本
gdb_all_bt.sh (在这个脚本里面写gdb命令)
eg: 实现打印进程pid的所有线程调用栈
#!/bin/bash
pid=$1
for i in $(ls /proc/${pid}/task)
do
echo "dump thread $i"
gdb -batch -ex "bt" -p $i
done
执行脚本sh gdb_all_bt.sh
9: gdb多线程调试
eg: 常用命令
bt
info thread
thread apply all bt
thread apply 1 2 bt (查看线程1 2 的调用栈)
t 1
t 2
b func1 thread 1 (断点只对线程1生效)
当不指定线程,那么每个线程运行时候只要执行到这个函数栈都会停下来。