GDB 调试指南
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫。
00 介绍
GDB(GNU Debugger)是 UNIX 及 UNIX-like 下的强大调试工具,可以调试 ada, c, c++, asm, minimal, d, fortran, objective-c, go, java,pascal 等语言,这一份指南我们主要以 c 来作为例子。
01 基础
1.1 判断程序是否可调试
# gdb helloworld
Reading symbols from helloWorld...(no debugging symbols found)...done.
# gdb helloworld
Reading symbols from helloWorld...done.
上面一种有 no debugging symbols found 表示不可调试,下面是可调式的。
或者 readelf 查看段信息:
1# readelf -S helloworld|grep debug
2 [28] .debug_aranges PROGBITS 0000000000000000 0000106d
3 [29] .debug_info PROGBITS 0000000000000000 0000109d
4 [30] .debug_abbrev PROGBITS 0000000000000000 0000115b
5 [31] .debug_line PROGBITS 0000000000000000 000011b9
6 [32] .debug_str PROGBITS 0000000000000000 000011fc
如果没有输出任何 debug 信息,也不能调试。
1.2 开启 gdb 编译
加上 -g 选项:
gcc -g -o xxx xxx.c
1.3 gdb xxx 进入调试
b 行号或函数添加断点r跑到下一个断点s单步跟踪n单步执行p查看当前程序的运行数据 比如:p a输出a变量的值 输出格式可以设置: 比如p/d a十进制输出a变量的值p array@idx可以查看数组 array 中 idx 处的值设置display,比如
display a这样以后每次调试都会输出a变量的值x查看内存地址中的值 语法:x/l查看原程序代码,l 9列出第9行附件的源码(l 2,8列出2-8行之间的数据),l func列出指定函数附件的源码p x=8在调试过程中修改变量x的值,下面生效jump实现跳转,可以是文件的行号,也可以是file:line,也可以是+num这种格式 jump address是代码行的内存地址signal产生信号量return强制返回call强制调用until(简写u)当在一个循环体内时,运行退出循环until +行号运行至某行停住,不仅仅跳出循环finish当前函数执行完,并打印函数返回时的堆栈地址和返回值及参数值等信息skip在 step 时跳过一些不想关注的函数或者某个文件的代码,如skip function add表示跳过函数 add,skip file step.c跳过文件 step.c,info skip查看跳过的信息。c继续执行 跳到下一个断点bt查看堆栈where报错时查看哪里出错,与bt类似info b查看断点情况q退出ptype输出结构体类型info registers显示寄存器值,info all-registers显示所有寄存器info breakpoints可以查看所有已设置的端点
1.5 命令进阶
1.5.1 设断点
info breakpoints查看所有断点b 9或者b test.c:9根据行号设置断点b func根据函数名设置断点b test.c:9 if b==0根据程序某个条件会出现问题,设置该条件断点(这样当出现问题时,会卡主,用来判断是否是该问题)rbreak print*对所有 print 开头的函数都设断点,rbreak test.c:.对test.c 中所有函数设断点tbreak test.c:9设 临时断点 ,即这个断点只生效一次ignore 1 30忽略某个断点的前面 30 次执行,从第 31 次开始生效,节约时间watch a观察某个值或表达式,什么时候发生变化disable/enable num禁用/启用所有/某个断点clear清除所有断点,用于清除某个函数,某行的断点,如clear func、clear linenumdelete删除所有断点,包括watchpoints, catchpoints,用于删除断点号的断点,如delete bnum
1.5.2 查看变量
p 'test.c'::a打印某个文件的变量,p 'main'::b打印某个函数定义的变量p *p@10打印指针指向的内容,@后面为打印的长度p *$.next打印链表linkNode的下一个节点内容p/x c按十六进制打印内容(x:十六进制,d:十进制,o:八进制,t:二进制,c:字符格式,f:浮点格式)x addr查看内存地址值display e程序断住显示某个变量的值
1.5.3 编辑源码
启动调试后,不想退出程序而编辑源码,如何做呢?
gdb 模式下用的默认编辑器是 /bin/ex ,如果没有或者想换成其他编辑器,如VIM,可以这样:
export EDITOR=/usr/bin/vim
gdb 模式下编辑源码:
(gdb)edit 3 # 编辑第三行
(gdb)edit func # 编辑func函数
(gdb)edit test.c:5 #编辑test.c第五行
完了之后,重新编译程序( 注意一定要带上 shell 命令,表明是shell命令 ):
(gdb)shell gcc -g -o main main.c test.c
或者这样:
启动是带上 tui(Text User Interface),可以在多个窗口调试:
gdb main -tui
1.6 带参数调试
1. 启动的时候带上参数
gdb --args xxx 参数
2. 启动之后 run 带上参数
# gdb xxx
(gdb)run 参数
3. 启动之后 set args 设置参数
# gdb xxx
(gdb) set args 参数
02 调试多进程
2.1 attach 方法
- 首先找到需调试的子进程:
ps -ef | grep xxx或pidof进程名 - 进入
gdb模式,输入attach pid - 打断点,运行进入调试
或者直接这样:gdb <program> pid(或 gdb <program> --pid pid),gdb 会 自动 attach。
如果出现如下错误:
Could not attach to process. If your uid matches the uid of the target
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.
切换到 root 用户,将 /etc/sysctl.d/10-ptrace.conf 中的
kernel.yama.ptrace_scope = 1
改为:
kernel.yama.ptrace_scope = 0
2.2 follow-fork-mode mode 方法
- 进入
gdb模式,输入set follow-fork-mode mode(mode 可选 parent、child,表示调试父进程还是子进程) - 打断点
2.3 调试已运行程序
已运行程序通常没有调试信息,但如果不能停止当前程序重新启动调试,可以:
同样的代码,再编译出一个带调试信息的版本,然后:
# gdb
(gdb) file hello
Reading symbols from hello...done.
(gdb)attach 20829
03 调试多线程
gdb 有一组命令能够辅助多线程的调试:
info threads:显示当前可调式的所有线程,线程 ID 前有 “*” 表示当前被调试的线程。thread id:调试目标 id 指定的线程set scheduler-locking [on|off|step]:多线程环境下,会存在多个线程运行,这会影响调试某个线程的结果,这个命令可以设置调试的时候多个线程的运行情况,on表示只有当前调试的线程会继续执行,off表示不屏蔽任何线程,所有线程都可以执行,step表示在单步执行时,只有当前线程会执行。
04 coredump 调试
coredump 调试依赖于 core 文件,core 文件是程序非法执行后 core dump 后产生的文件。这是 Linux 系统的一种保护机制,当出现某些连开发和测试费了九牛二虎之力都没能发现的问题时,Linux 系统还提供了最后一道屏障,通过 core 文件就可以让这些问题原形毕露。
4.1 开启 core dump
要想让程序崩溃时产生 core 文件,需要开启,输入 ulimit -c,如果输出为 0,表示默认关闭 core dump。
有两种方式可以开启,一种就是通过 ulimit 命令,一种是在程序中写代码开启,这里只讲第一种,第二种参考文末的引用1。
ulimit -c unlimied # 表示不限制core文件大小
ulimit -c 10 # 设置最大大小,单位为块,一块默认为512字节
上面是临时开启,永久开启要修改 /etc/security/limits.conf 文件,增加一行:
# /etc/security/limits.conf
# <domain> <type> <item> <value>
* soft core unlimited
这样就可以生成 core 文件,文件名就是 core,并且默认在当前程序所在目录下生成,如果要指定目录,则可以 echo "/tmp/corefile-%e-%p-%t" > /proc/sys/kernel/core_pattern 设置 core 文件保存在目录 "/tmp/corefile" 下,文件名格式为 “core-命令名-pid-时间戳”
还可以通过 echo 1 > /proc/sys/kernel/core_uses_pid 使得生成的 core 文件变成 core.pid,pid 是该进程的 pid。
4.2 调试 core dump
使用
gdb <program> core文件名
或者 gdb 启动后,使用
-core <file>-c <file>
来调试 core 文件
下面是一个例子:
#include <stdio.h>
int func(int *p)
{
int y = *p;
return y;
}
int main()
{
int *p = NULL;
return func(p);
}
编译:gdb -g -o core_dump core_dump.c,用 gdb 查看 core 文件
root@root:~$ gcc core_demo.c -o core_demo -g
root@root:~$ ./core_demo
Segmentation fault (core dumped)
root@root:~$ gdb core_demo core_demo.core.24816
...
Core was generated by './core_demo'.
Program terminated with signal 11, Segmentation fault.
#0 0x080483cd in func (p=0x0) at core_demo.c:5
5 int y = *p;
(gdb) where
#0 0x080483cd in func (p=0x0) at core_demo.c:5
#1 0x080483ef in main () at core_demo.c:12
(gdb) info frame
Stack level 0, frame at 0xffd590a4:
eip = 0x80483cd in func (core_demo.c:5); saved eip 0x80483ef
called by frame at 0xffd590c0
source language c.
Arglist at 0xffd5909c, args: p=0x0
Locals at 0xffd5909c, Previous frame's sp is 0xffd590a4
Saved registers:
ebp at 0xffd5909c, eip at 0xffd590a0
(gdb)
可以看到,我们可以还原 core_demo 执行时的场景,并使用 where 查看当前程序调用函数栈帧,还可以使用 gdb 中的命令查看寄存器,变量等信息。
常见问题
问题 1
开启 GDB 调试时出现:
Missing separate debuginfos, use: debuginfo-install libgcc-4.8.5-28.el7_5.1.x86_64 numactl-libs-2.0.9-7.el7.x86_64
解决:
- 修改文件
/etc/yum.repos.d/CentOS-Debuginfo.repo中的enabled参数,将其值修改为 1 yum install nss-softokn-debuginfo --nogpgcheckdebuginfo-install glibc如果出现下面的问题:-bash: debuginfo-install: command not found,则先安装yum-utils,使用命令:yum install yum-utils- 分别安装问题提示的两个库:
use: debuginfo-install libgcc-4.8.5-28.el7_5.1.x86_64 numactl-libs-2.0.9-7.el7.x86_64
参考
3 GDB 调试利器
公众号后台回复“加群”,带你进入高手如云交流群
我的公众号 「Linux云计算网络」(id: cloud_dev) ,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。
GDB 调试指南的更多相关文章
- GDB调试指南-变量查看
前言 在启动调试以及设置断点之后,就到了我们非常关键的一步-查看变量.GDB调试最大的目的之一就是走查代码,查看运行结果是否符合预期.既然如此,我们就不得不了解一些查看各种类型变量的方法,以帮助我们进 ...
- [原创]GDB调试指南-断点设置
前言 上篇<GDB调试指南-启动调试>我们讲到了GDB启动调试的多种方式,分别应用于多种场景.今天我们来介绍一下断点设置的多种方式. 为何要设置断点 在介绍之前,我们首先需要了解,为什么需 ...
- GDB调试指南-单步调试
前言 前面通过<启动调试>,<断点设置>,<变量查看>,我们已经了解了GDB基本的启动,设置断点,查看变量等,如果这些内容你还不知道,建议先回顾一下前面的内容.在启 ...
- GDB调试指南-启动调试
前言 GDB(GNU Debugger)是UNIX及UNIX-like下的强大调试工具,可以调试ada, c, c++, asm, minimal, d, fortran, objective-c, ...
- roslaunch & gdb 调试指南(待补充)
1. 安装xterm sudo apt-get install xterm 2. 在launch文件中添加如下内容: <node name="navigation" pkg= ...
- gdb 调试入门,大牛写的高质量指南
引用自:http://blog.jobbole.com/107759/ gdb 调试 ncurses 全过程: 发现网上的“gdb 示例”只有命令而没有对应的输出,我有点不满意.gdb 是 GNU 调 ...
- GDB调试器
/*this project used for gdb debug c programs*//*At first,using compile command turn out the executab ...
- linux内核调试指南
linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 ...
- windows下配置 GNU的gdb调试功能
1.配置 修改环境变量(前提电脑中存在gdb.exe) 1. 我的电脑->属性->环境......在path那一项后面添加你DEV-C++ Bin目录的路径(gdb.exe所在目录),如: ...
随机推荐
- C++的精度控制
#include <iostream> #include <iomanip> using namespace std; int main( void ) { const dou ...
- FaceNet人脸识别研究
https://github.com/WindZu/facenet_facerecognition (代码) https://segmentfault.com/a/1190000015917420?u ...
- 实战jmeter入门压测接口性能
什么是Jmeter? 是Apache组织开发的基于Java的压力测试工具. 准备工作: 一.安装配置好环境及压测工具 Jmeter下载地址:http://mirrors.tuna.tsinghua.e ...
- python学习——字典和集合
一.字典 1)字典介绍 字典是一种通过名字或者关键字引用的得数据结构,其键可以是数字.字符串.元组,这种不可变的结构类型也称之为映射.字典类型是Python中唯一內建的映射类型. 1)字典操作 &qu ...
- JSON转换方法解析
JSON.parse() 与 JSON.stringify() 的区别 JSON.parse() :是从一个字符串中解析出 json 对象 JSON.stringify():是从一个对象中解析出字符串 ...
- 关于turtle画蟒蛇小实例
import turtle turtle.setup(800,600) turtle.pensize(25) turtle.pencolor('blue') turtle.penup() #抬笔 tu ...
- UVA - 10480 Sabotage 最小割,输出割法
UVA - 10480 Sabotage 题意:现在有n个城市,m条路,现在要把整个图分成2部分,编号1,2的城市分成在一部分中,拆开每条路都需要花费,现在问达成目标的花费最少要隔开那几条路. 题解: ...
- 爬虫反爬之代理IP
爬虫反爬之代理IP 代理IP其实本就是在requests模块中的参数 定义: 代替原来的IP地址去对接网络的IP地址. 作用: 隐藏自身真实IP,避免被封. 获取代理IP网站 西刺代理.快代理.全网代 ...
- github 授权登录教程与如何设计第三方授权登录的用户表
需求:在网站上想评论一篇文章,而评论文章是要用户注册与登录的,那么怎么免去这麻烦的步骤呢?答案是通过第三方授权登录.本文讲解的就是 github 授权登录的教程. 效果体验地址:http://biao ...
- 字符串的api (基础)
一.基础 1.字符串.charAt(index) 根据下标获取字符串的某一个字符 应用: 判断字符串的首字母是否大写 任意给定的一串字母,统计字符串里面的大写字母和小写字母的个数 2.字符串.inde ...
