概述
文章目录
- 一、介绍
- 二、简单操作
- 三、用gdb调试多进程程序
- 四、用gdb调试多线程程序
- 五、用gdb工具分析core文件
- 1、core文件开关
- 2、core文件命名和保存路径
- 3、分析Core 文件
- 4、运行这个有bug 的程序,可以在当前目录下生成core文件
- 六、调试一个正在运行的程序
- 七、设置断点的方式
- 方式1、设置程序运行到某个函数
- 方式2、根据代码行位置设置断点
- 方式3、根据运行时的地址设置断点
- 八、断点操作
- 九、在调试程序时,gdb常用命令总结
一、介绍
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具
注意:利用gdb调试,需要在gcc编译过程中加上-g选项,这样编译生成的可执行文件才可以利用gdb进行源码调试。-g 选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb能找到源文件。如果把当前的gdb.c改名为g.c或者将gdb.c移动到其他地方,则gdb无法进行调试。
二、简单操作
1)启动gdb
gdb hello
或者
gdb
file hello
2)显示程序
list 或者 l
3)打断点
break(b) main() //函数的入口处
break(b) 11 //行号
break(b) hello.c:14 //特定文件的行号
break(b) 12 if i=10 //if语句满足后在行号处打断点
4)获取断点信息
info break
5)删除断点
delete 13(断点编号)
6)运行程序
run(r)
7)单步(不进入子函数)
next(n)
8)单步(进入子函数)
step(s)
9)继续运行
continue(c)
10)查看变量
print(p) i(变量名)
11)运行程序到当前函数结束
finish
12)监控变量
watch i(变量名)
13)退出gdb
quit(q)
三、用gdb调试多进程程序
如果一个进程通过fork系统调用创建了子进程,gdb会继续调试原来的进程,子进程则正常运行。
那么如何调试子进程呢?
1、单独调试子进程
子进程本质也是一个进程,因此也可通过gdb来调试,首先找到目标子进程的PID,再将其附加(attach)到
gdb调试器上,具体操作如下:
$ps -ef|grep 进程名 //找到待调试进程的PID
$gdb
(gdb) attach "PID"
2、使用调试器选项follow-fork-mode
gdb调试器的选项follow-fork-mode允许我们
选择程序在执行fork系统调用后是继续调试父进程还是调试子进程。
用法:
(gdb)set follow-fork-mode m
m 可以选parent 或 child, 分别表示调试父进程和子进程
举例:
$gdb XX
(gdb) set follow-fork-mode child
(gdb) b process.h:264
//打断点
四、用gdb调试多线程程序
gdb有一组命令可辅助多线程程序的调试。
info threads
显示当前可调试的所有线程。gdb会为每个线程分配一个ID,我们可以使用这个ID来操作对应的线程。
ID前面有“*”的线程是当前被调试的线程。
thread ID
调试目标ID指定的线程。
查看所有线程的栈信息:thread apply all bt
调试多线程程序时,默认除了被调试的线程在执行外,其他线程也在继续执行,
但有的时候我们希望只让被调试的线程运行。这可以通过这个命令来实现。
set scheduler-locking [off|on|step]
该命令设置sceduler-locking的值:
- off表示不锁定任何线程,即所有线程都可以继续执行,这是默认值。
- on表示只有当前被调试的线程会继续执行。
- step表示在单步执行的时候,只有当前线程会执行。
五、用gdb工具分析core文件
在Unix系统下,应用程序崩溃,一般会产生core文件,如何根据core文件查找问题的所在,
并做相应的分析和调试,是非常重要的。
许多程序出错时都会产生一个Core 文件,通过工具分析这个文件,
我们可以定位到程序异常退出时对应的堆栈调用等信息,找出问题所在并进行及时解决。
段错误,就是大名鼎鼎的Segmentation Fault,这通常是由指针错误引起的。
简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,
或者根本就不存在对应的物理内存,尤其常见的是访问0 地址。
在编程中有以下几种做法容易导致段错误,基本都是错误地使用指针引起的:
- 访问系统数据区,尤其是往系统保护的内存地址写数据,最常见的就是给指针以0地址
- 内存越界(数据越界、变量类型不一致等)访问到不属于你的内存区域
程序在运行过程中如果出现段错误,那么就会收到SIGSEGV 信号,
SIGSEGV 默认handler 的动作是打印“段错误”的出错信息,并产生Core 文件。
1、core文件开关
ulimit -c 查看core开关,如果为0表示关闭,不会生成core文件;
使用 ulimit -c [filesize]
设置core文件大小,当最小设置为4之后才会生成core文件;
使用 ulimit -c unlimited 设置core文件大小为不限制,这是常用的做法;
如果需要开机就执行,则需要将这句命令写到 /etc/profile 等文件。
core文件路径一般在可执行文件同一目录
2、core文件命名和保存路径
core文件有默认的名称和路径,但为了方便,我们通常会自己命名和指定保存路径;
可以通过 /proc/sys/kernel/core_pattern 设置 core 文件名和保存路径,方法如下:
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
命名的参数列表:
- %p - insert pid into filename 添加pid
- %u - insert current uid into filename 添加当前uid
- %g - insert current gid into filename 添加当前gid
- %s - insert signal that caused the coredump into the filename 添加导致产生core的信号
- %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
- %h - insert hostname where the coredump happened into filename 添加主机名
- %e - insert coredumping executable name into filename 添加命令名。
3、分析Core 文件
设置Core文件大小,运行程序生成Core文件
执行ulimit -c unlimited表示不限制生成的Core 文件的大小,
4、运行这个有bug 的程序,可以在当前目录下生成core文件
执行 gdb [exec file] [core file]启动gdb后,
调用gdb的where或bt命令可以查看当时的调用栈信息,
进入某个栈:f N,f是frame的缩写,N是栈号,如0、1、2、3…
进入到某个栈后,才能通过p命令查看这个栈的临时变量,否则只能查看全局变量。
六、调试一个正在运行的程序
使用gdb -p PID命令,PID即程序的pid。
需要注意的是,gdb调试正在运行的程序会导致程序挂起,因此请记住不要gdb调试正在运行的在线服务。
七、设置断点的方式
有很多种,最常见的有两种:一是根据代码行位置设置断点,二是设置程序运行到某个函数。
设置程序运行到某一行,通过“文件名:行号”的形式:
方式1、设置程序运行到某个函数
设置程序运行到某个函数,通过“名字空间::函数名”的形式:
b namespace_a::func
但并不是所有函数,任何时候都能设置断点的。比如优化后的静态函数,inline函数。在特定的情况下,通过函数名进行断点设置便不可为,而又需要查看运行时该函数的运行情况,这时就需要使用第二种方式。
方式2、根据代码行位置设置断点
b test.cpp:100
方式3、根据运行时的地址设置断点
此时有两种方式,一是通过直接指定地址进行,进行断点设置。二是通过print命令获得相关信息
例子1:b 0x5859c0。""号是必须加在地址前的,0x5859c0为函数指针的地址
例子2:展示变量内容
(gdb) p *thread_scheduler
$4 = {max_threads = 151, init = 0, init_new_connection_thread = 0x6a3310
<init_new_connection_handler_thread()>,
add_connection = 0x5859c0 <create_thread_to_handle_connection(THD*)>,
thd_wait_begin = 0, thd_wait_end = 0, post_kill_notification = 0,
end_thread = 0x57ea50 <one_thread_per_connection_end(THD*, bool)>, end = 0}
在打印thread_scheduler变量的内容时,保存函数指针的变量add_connection的内容被打印出来,保活函数的指针和函数的名字,通过指针可使用b *0x5859c0进行断点设置;通过函数名可使用方式1进行断点设置
八、断点操作
查看断点:info b
删除断点:d N,d是delete的缩写,N是断点的编号,可以通过info b查看。
无论哪种方式设置断点,都要执行c命令(continue),让程序继续运行。
九、在调试程序时,gdb常用命令总结
常用:n、s、p
- n即next,单步执行,执行下一步的意思,遇到函数会调用函数。
- s即step,也是单步执行,但是会进入函数内部,然后结合n命令来调试函数。
- p即print,打印变量,最常用的命令。p可以打印普通变量、std::string字符串、指针、数组等。gdb打印字符串支持c_str()、length()等
std::string str;
p str,p str.c_str()查看字符串内容,p str.length(),查看字符串长度
有时会遇到字符串太长不能显示全,最后显示"…",可以通过命令取消长度限制:
set print elements 0
这样就能打印完整的字符串。
命令 | 描述 |
---|---|
backtrace(或bt) | 查看各级函数调用及参数 |
finish | 连续运行到当前函数返回为止,然后停下来等待命令 |
frame(或f) | 帧编号 选择栈帧 |
info(或i) | locals 查看当前栈帧局部变量的值 |
list(或l) | 列出源代码,接着上次的位置往下列,每次列10行 |
list 行号 | 列出从第几行开始的源代码 |
list 函数名 | 列出某个函数的源代码 |
next(或n) | 执行下一行语句 |
print(或p) | 打印表达式的值,通过表达式可以修改变量的值或者调用函数 |
quit(或q) | 退出gdb调试环境 |
set var | 修改变量的值 |
start | 开始执行程序,停在main函数第一行语句前面等待命令 |
step(或s) | 执行下一行语句,如果有函数调用则进入到函数中 |
最后说一下gdb中如何打印STL的vector和map,gdb默认不支持STL,
需要从网上下载一个txt文件,然后将其内容追加到.gdbinit文件中,就可以使用pvector命令查看vector容器数据。
最后
以上就是花痴皮皮虾为你收集整理的gdb调试总结笔记的全部内容,希望文章能够帮你解决gdb调试总结笔记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复