先来看下man手册关于gdb的一些说明。
gdb的目的主要是让你知道程序内部正在运行什么代码,或者是当程序崩溃的时候正在执行什么代码。
GDB主要可以做四种事情来帮助你找到bug:
启动你的程序,指定任意可以影响程序行为的参数。
让你的程序在指定的条件停住.
测试你的程序停止的时候发生了什么。
改变程序内部的变量,来改正程序的错误继续执行。
你可以用gdb调试C/C++和modula-2程序。fortran的支持需要有Fortran编译器。
gdb使用shell命令gdb来调用,一旦开始,就从终端读取命令直接gdb退出,你可以通过help命令获取帮助信息。
可以不带任何参数或选项执行gdb命令,但是最常用的启动gdb的方式是带一个或者两个参数,指定一个可执行文件来作为参数:
gdb program(gdb+可执行文件名称)
也可以再gdb文件后面指定可执行文件 和 core文件的名称:
gdb program core(gdb + 可执行文件 +core文件)
You can, instead, specify a process ID as a second argument, if you want to debug a running process:
也可以指定一个进程id作为第二个参数,如果你想调试一个正在运行的程序:
gdb program 1234(gdb进程名+进程id)
gdb -p 1234
将GDB附加到进程1234(除非您还有一个名为1234的文件;GDB首先检查核心文件)。使用选项-p可以省略程序文件名。
gdb调试命令:
break [file:]function
设置一个断点在函数中(在文件中)
run [arglist]
启动程序带上指定的参数
bt Backtrace:
显示堆栈
print expr
显示表达式的值
c
继续执行你的程序(程序停住后,例如:在断点处停止)
next
执行程序的下一行代码(程序停止以后);跨国任何当前行的函数调用。
edit [file:]function
查看当前程序停在哪。
list [file:]function
显示程序当前停住的代码行附近的代码
step 单步调试
执行程序的下一行(程序停住后),进入当前行的函数调用的内部
help [name]
显示gdb命令的相关信息。
quit
退出gdb
需要详细的gdb说明,查看using gdb:一个gdb调试程序的指南,stallmain和roland写的,也有可用的在线文档作为gdb入口在gdb项目信息中。
选项:
除选项外的任何参数指定可执行文件和核心文件(或进程ID);也就是说,第一个没有关联选项标志的参数是等价的
到-se选项,第二个选项(如果有的话)相当于-c选项(如果它是文件的名称)。许多期权既有长期权也有短期权;两者都在这里显示。长
如果将表单截断,也可以识别它们,只要有足够多的选项是明确的。(如果你愿意,你可以用+而不是
-虽然我们演示了更常见的约定。
您提供的所有选项和命令行参数都是按顺序处理的。使用-x选项时,顺序会有所不同。
-help
-h 列出所有选项,并简要说明。
-symbols=file
-s file
从文件中读取符号表。
-write
允许写入可执行文件和核心文件。
-exec=file
-e file
在适当的时候使用file文件作为可执行文件执行,并与核心转储一起检查纯数据。
-se=file
从文件文件中读取符号表,并将其用作可执行文件。
-core=file
-c file
使用file文件作为核心转储进行检查。
-command=file
-x file
从文件中执行GDB命令。
-ex command
执行给定的GDB命令。
-directory=directory
-d directory
将目录添加到搜索源文件的路径。
-nh 不要从~/.gdbinit执行命令。
-nx
-n 不要从任何.gdbinit初始化文件执行命令。
-quiet
-q "Quiet". 不要打印介绍性和版权信息。这些消息也在批处理模式下被抑制。
-batch
以批处理模式运行。处理完用-x指定的所有命令文件后(如果不禁止,则使用.gdbinit)以0状态退出。如果出现错误,则以非零状态退出在命令文件中执行GDB命令时发生。
批处理模式对于作为过滤器运行GDB可能很有用,例如下载并在另一台计算机上运行一个程序;为了使这个消息更有用
程序正常退出。
(当在GDB控件下运行的程序终止时,通常会发出此命令)在批处理模式下运行时不会发出此命令。
-cd=directory
使用directory作为工作目录而不是当前目录运行GDB。
-fullname
-f Emacs在将GDB作为子进程运行时设置此选项。它告诉GDB在每次堆栈时以标准的、可识别的方式输出完整的文件名和行号显示框架(包括每次程序停止时)。这种可识别的格式看起来像两个\032字符,后跟文件名、行号和字符的位置由冒号和换行符分隔。的源代码。Emacs-to-GDB接口程序使用两个\032字符作为信号来显示框架。
-b bps
设置GDB用于远程调试的任何串行接口的行速度(波特率或比特/秒)。
-tty=device
使用设备运行程序的标准输入和输出。
====================================================================
看下常用的GDB总结
1.启动调试
前置条件:编译生成执行码时带上 -g,如果使用Makefile,通过给CFLAGS指定-g选项,否则调试时没有符号信息。
gdb program | 最常用的用gdb启动程序,开始调试的方式 |
---|---|
gdb program core | 用gdb查看core dump文件,跟踪程序core的原因 |
gdb program pid | 用gdb调试已经开始运行的程序,指定pid即可 |
gdb attach pid | 用gdb调试已经开始运行的程序,指定pid即可 |
2.调试命令
(1)执行命令模式
gdb -ex “set pagination 0” -ex “thread apply all bt” -batch -p $pid | 打印$pid进程所有线程的堆栈并退出。 |
---|---|
gdb -ex “handle SIGUSR1 noprint nostop” --args program abc | 先执行引号内的命令,并带上参数abc,运行gdb |
上面两个例子主要说明:
-ex 如何在启动gdb时,设置相应的命令(命令要用引号括住)。
–args 带参数启动
(2).交互模式
run(r) | 运行程序 |
---|---|
continue(c) | 中断后继续运行到下一个断点 |
step (s) | 单步执行,进入函数 |
next (n) | 单步执行 |
return | 函数未执行完,忽略未执行的语句,返回 |
finish | 函数执行完毕返回 |
bt | 查看调用栈信息 |
bt N | 显示开头N个栈桢 |
bt -N | 显示最后N个栈桢 |
(frame)f N | 显示第N层栈桢 |
list | 显示源码 |
pwd | 当前的工作目录 |
(3)反复执行
continue N | 连续执行cointiue N次,一般用于避免频繁断点 |
---|---|
step N | 连续执行step N次 |
next N | 连续执行next N次 |
3.断点
break 函数名 | 设置断在某个函数 |
---|---|
break 文件名:行号 | 设置断在某一行。例如 break hello.c:28 |
info break | 查看设置的断点信息 |
break if condition | 条件断点 |
break 函数名 thread 线程号 | 设置断点只断某个线程,通过info threads 查看线程号 |
delete 断点号 断点号… | 删除一个或多个断点 |
diable 断点号 断点号… | 禁止一个或多个断点 |
enable 断点号 断点号… | 打开一个或多个断点 |
command 断点号 | 断点触发时,执行命令,一般用于打印变量 |
举个例子
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just “end”.
print x
end
(gdb)
4.检测点
watch | 为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序 |
---|---|
rwatch | 当表达式(变量)expr被读时,停住程序 |
awatch | 当表达式(变量)的值被读或被写时,停住程序 |
info watchpoints | 列出当前所设置了的所有观察点 |
技巧经验:观察某个变量是否变化,被读或者被写,由于变量只在某一个作用域,可以获取变量的地址,然后观察。
比如:观察examined_rows变量什么时候被修改
(1).p &examined_rows,得到地址
(2).watch *(ha_rows *) 0x7ffec8005e28,则可以观察这个变量的变化情况。
5.查看变量
(1)设置
set print elements N | 指定打印的长度,对长字符串特别有用 |
---|---|
set print elements 0 | 输出完整的字符串 |
set print pretty [off] | 设置或关闭设置。设置GDB打印结构的时候,每行一个成员,并且有相应的缩进。缺省是关闭的 |
print (p)变量/地址 | 打印变量查看其值 |
info locals | 打印出当前函数中所有局部变量及其值 |
info args | 打印出当前函数的参数名及其值 |
display 变量 | 自动打印变量 |
undisplay | 取消自动打印 |
注意:默认编译的时候,调试过程是看不见宏的值的,编译时候需要给选项。-g3
6.内存查看
格式: x /nfu x 是 examine 的缩写
a.n表示要显示的内存单元的个数
b.f表示显示方式, 可取如下值
(1).x 按十六进制格式显示变量。
(2).d 按十进制格式显示变量。
(3).u 按十进制格式显示无符号整型。
(4).o 按八进制格式显示变量。
(5).t 按二进制格式显示变量。
(6).a 按十六进制格式显示变量。
(7).i 指令地址格式
(8).c 按字符格式显示变量。
(9).f 按浮点数格式显示变量。
c.u表示一个地址单元的长度
(1).b表示单字节,
(2).h表示双字节,
(3).w表示四字节,
(4).g表示八字节
比如:x/3xh buf
表示从内存地址buf读取内容,3表示三个单位,x表示按十六进制显示,h表示以双字节为一个单位。
7.多线程调试
info threads | 查看线程 |
---|---|
thread thread_no | 切换到线程号 |
thread apply all command | 所有线程都执行命令打印栈桢 |
比如:thread apply all bt //所有线程都打印栈桢
(1)线程锁
show scheduler-locking
set scheduler-locking on
set scheduler-locking off
默认是off,当程序继续运行的时候如果有断点,那么就把所有的线程都停下来,直到你指定某个线程继续执行(thread thread_no apply continue).
但是如果直接在当前线程执行continue的话,默认是会启动所有线程。这种模式有一种副作用,如果多个线程都断在同一个函数,这时候调试会出问题。
这个时候需要打开线程锁,但打开线程锁,意味着其它线程不能运行了。
(2)non-stop模式(7.0以后的版本支持)
set target-async 1
set pagination off
set non-stop on
gdb启动了不停模式,除了断点有关的线程会被停下来,其他线程会执行。
8.信号量
(1).singal 发送信号
假定你的程序已将一个专用的 SIGINT(键盘输入,或CTRL-C;信号2)信号处理程序设置成采取某个清理动作,
要想测试该信号处理程序,你可以设置一个断点并使用如下命令:
(gdb) signal 2
(2).handle 拦截信号
Handle命令可控制信号的处理,他有两个参数,一个是信号名,另一个是接受到信号时该作什么。几种可能的参数是:
- nostop 接收到信号时,不要将它发送给程序,也不要停止程序。
- stop 接受到信号时停止程序的执行,从而允许程序调试;显示一条表示已接受到信号的消息(禁止使用消息除外)
- print 接受到信号时显示一条消息
- noprint 接受到信号时不要显示消息(而且隐含着不停止程序运行)
- pass 将信号发送给程序,从而允许你的程序去处理它、停止运行或采取别的动作。
- nopass 停止程序运行,但不要将信号发送给程序。
比如:
handle SIGPIPE stop print //截获SIGPIPE信号,程序停止并打印信息
handle SIGUSR1 nostop noprint //忽略SIGUSR1信号
9.生产环境使用GDB场景
内核转储(coredump)
(1).配置产生core文件
前置条件:确保系统配置的core file size足够,一般设置成unlimited
ulimit -c unlimited
配置corefile的参数:
echo 2 > /proc/sys/fs/suid_dumpable [程序中切换用户,也要产生corefile]
mkdir /tmp/corefiles
chmod 777 /tmp/corefiles
echo “/tmp/corefiles/core”>/proc/sys/kernel/core_pattern //配置core文件产生的目录为/tmp/corefiles
echo “1” > /proc/sys/kernel/core_uses_pid
kill -sigsegv pid //模拟异常内存访问信号
注意:
a.确保配置的目录有足够的磁盘空间,否则产生core文件可能不完整。
b.对于mysqld而言,要保证正确产生core-file,需要加上–core-file,默认这个参数是不打开的。
c.kill -9 pid 是不能产生core文件的,因为SIGKILL信号不能被捕获。
(2).使用core文件
gdb /usr/mysql/bin/mysqld core.24556
(3).dump已经运行进程的状态信息
gdb attach pid
(gdb) generate-core-file
调试完毕后,通过detach命令退出。
另外,通过gcore pid 命令也可以dump core文件,生成在当前目录下。