gdb调试使用小结

24 篇文章 3 订阅

gdb安装

一般linux系统中都安装好了,如果没有可以使用下面命令

#:apt-get update

#:apt-get install gdb

gdb使用

一般要使用gdb调试某个程序,都需要程序中带了符号表,这样才能清晰的看到调试的每一行代码、调用的堆栈信息、变量名、函数名、寄存器、内存信息等,所以使用gcc编译程序时,需要加上 -g 选项:

gcc test.c -g -o test

gdb成功加载后会有下面的提示:

Reading symbols from /root/test/test...done.

如果没有符号表(没有加-g  或者  程序strip后)

Reading symbols from /root/test/test...(no debugging symbols found)...done.

gdb成功加载后就可以用各种调试命令了:

命令

描述

backtrace(bt)

查看各级函数调用及参数

finish

连续运行到当前函数返回为止,然后停下来等待命令

return结束当前调用函数并返回指定值,到上一层函数调用处,立即返回
until(u)运行到指定行停下来
jump(j)将当前程序执行流跳转到指定行或地址

frame(f)  帧编号

选择栈帧

info(i)  locals

查看当前栈帧局部变量的值

list(l)

列出源代码,接着上次的位置往下列,每次列十行 list+  list-   list file:linenumber某文件某行开始

list  行号

列出第几行开始的源代码

list  函数名

列出某个函数的源代码

next(n)

执行下一行语句

print(p)

打印表达式的值,通过表达式的值可以修改变量的值或者调用函数

quit(q)

退出gdb调试环境

set  var

修改变量的值

start

开始执行程序,停在main函数第一行语句前面等待命令

step(s)

执行下一行语句,如果有函数则进入到函数中

break(b)  

在某一行/函数开头设置断点

break(b)… if…

设置条件断点

tbreak(tb)添加临时断点
enable启用某个断点
disable禁用某个断点

continue(c)

从当前位置开始连续运行程序

delete breakpoints 断点号

删掉此号的断点

display  变量名

跟踪查看某个变量,每次停下来都显示它的值

disable  breakpoints 断点号

禁用此断点

info(i)  breakpoints

查看当前设置了哪些断点

run(r)

从头开始连续运行程序

undisplay  跟踪显示行号

取消跟踪显示

watch

设置观察点

info(i)   watchpoints

查看当前设置了哪些观察点

ptype查看变量类型

x

从某个位置开始打印存储单元的内容,全部当成字节来看,而不区分哪个字节属于哪个变量

disassemble(dis)

反汇编当前函数或者指定的函数,单独用disassemble命令是反汇编当前函数,如果disassemble命令后面跟函数名或地址则反汇编指定的函数。

info  registers

可以显示所有寄存器的当前值。在gdb中表示寄存器名时前面要加个$,例如p $esp可以打印esp寄存器的值。

set follow-fork-mode child/parent设置gdb在fork之后跟踪子进程/父进程
set  args  'command-line'给执行的程序传命令行参数
show args查看设置的命令行参数
thread切换到指定线程

 

使用 GDB 调试程序一般有三种方式:

  • gdb filename  直接调试可执行程序
  • gdb attach pid  附加进程
  • gdb filename corename  调试core文件

自定义core文件名字:

/proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 PID 作为扩展,如果添加则文件内容为 1,否则为 0;/proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名。修改方式如下:

echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

 

参数名称参数含义
%p添加 pid 到 core 文件名中
%u添加当前 uid 到 core 文件名中
%g添加当前 gid 到 core 文件名中
%s添加导致产生 core 的信号到 core 文件名中
%t添加 core 文件生成时间(UNIX)到 core 文件名中
%h添加主机名到 core 文件名中
%e添加程序名到 core 文件名中

 

1、直接gdb可执行程序

2、附加进程

程序启动后,可以使用 gdb attach 进程ID 来将gdb调试器附加到该进程ID的程序上,attach后程序会停下来,用continue命令继续执行,

调试完后可以在gdb中执行detach命令分离

3、调试core文件

有时候程序运行已经崩溃,此时如果产生了core文件,我们可以通过core来调试,当然产不产生core文件依赖是否配置

[root@izbp11ddoyj3i4745fb2nbz test]# ulimit -a
core file size          (blocks, -c) 0                   //为0表示不产生core文件,可以使用ulimit -c unlimited修改为不限制大小
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15088
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15088
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
 

一般core文件都是叫core.12958之类的

[root@izbp11ddoyj3i4745fb2nbz test]# gdb test core.12958

修改core文件名称

/proc/sys/kernel/core_uses_pid    //写0不添加pid为core文件扩展,写1为添加

/proc/sys/kernel/core_pattern   //设置格式化的core文件路径及文件名

echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

参数名称参数含义(英文)参数含义(中文)
%pinsert pid into filename添加 pid 到 core 文件名中
%uinsert current uid into filename添加当前 uid 到 core 文件名中
%ginsert current gid into filename添加当前 gid 到 core 文件名中
%sinsert signal that caused the coredump into the filename添加导致产生 core 的信号到 core 文件名中
%tinsert UNIX time that the coredump occurred into filename添加 core 文件生成时间(UNIX)到 core 文件名中
%hinsert hostname where the coredump happened into filename添加主机名到 core 文件名中
%einsert coredumping executable name into filename添加程序名到 core 文件名中

一些命令及用法的描述

jump:程序执行跳转

jump <location> 

可以是程序的行号或者函数的地址,jump 会让程序执行流跳转到指定位置执行,但是由于中间的代码没执行导致不可预知的错误

 

until:运行到什么条件停止

until + 行数

until + 条件    //例如until i == 200

 

disassemble:反汇编

dis 函数名

gdb默认反汇编为AT&T格式,可以通过show disassembly-flavor 查看,一般通过 set disassembly-flavor intel 来设置成intel汇编格式

 

x 打印内存地址的值

x /nfu <addr>

x 是 examine 的缩写

n表示要显示的内存单元的个数

f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。

u表示一个地址单元的长度,
b表示单字节,
h表示双字节,
w表示四字节,默认是这个
g表示八字节

例如:x/3xh buf  buf中读取 3个单位,双字节为一个单位,十六进制显示

 

set args和show args

set args 传递命令行参数给程序,如果想清除已经设置好的,那么执行set args不加任何参数即可

 

tbreak 临时断点

该断点出发一次后自动删除

注意:GDB中的断点调试分为软件断点和硬件断点两种类型: 
- 软件断点:由非法指令异常实现(软件实现) 
- 硬件断点:由硬件特性实现(数量有限) 需要cpu支持

通常情况下,我们使用的都是软件断点。那么,什么情况下我们需要用到硬件断点和数据断点呢? 
1. 当代码位于只读存储器(Flash)时,只能通过硬件断点调试。 
2. 硬件断点需要硬件支持,数量有限 
3. GDB中通过hbreak命令支持硬件断点 
4. hbreak与break使用方式完全一致
 

watch

监视一个变量或者一段内存,当这个变量或者该内存处的值发生变化时,GDB 就会中断下来

监视变量

int i;

watch i

监视指针 

char *p;

watch p 与 watch *p

监视数组或者内存空间

char buf[128];

watch buf

这里是对 buf 的 128 个数据进行了监视,此时不是采用硬件断点,而是用软中断实现的。用软中断方式去检查内存变量是比较耗费 CPU 资源的,精确地指明地址是硬件中断

 

display

监视的变量或者内存地址,每次程序中断下来都会自动输出这些变量或内存的值

 

将print打印结果显示完整

当使用 print 命令打印一个字符串或者字符数组时,如果该字符串太长,print 命令默认显示不全的,我们可以通过在 GDB 中输入 set print element 0 命令设置一下,这样再次使用 print 命令就能完整地显示该变量的所有字符串了

 

让被gdb调试的程序接收信号

请看如下代码

void prog_exit(int signo)
{
    std::cout << "program recv signal [" << signo << "] to exit." << std::endl;
}

int main(int argc, char* argv[])
{
    //设置信号处理
    signal(SIGCHLD, SIG_DFL);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, prog_exit);
    signal(SIGTERM, prog_exit);

    int ch;
    bool bdaemon = false;
    while ((ch = getopt(argc, argv, "d")) != -1)
    {
        switch (ch)
        {
        case 'd':
            bdaemon = true;
            break;
        }
    }

    if (bdaemon)
        daemon_run();

    //省略无关代码...
 }

在这个程序中,我们接收到 Ctrl + C 信号(对应信号 SIGINT)时会简单打印一行信息,而当用 GDB 调试这个程序时,由于 Ctrl + C 默认会被 GDB 接收到(让调试器中断下来),导致无法模拟程序接收这一信号。解决这个问题有两种方式:

  • 在 GDB 中使用 signal 函数手动给程序发送信号,这里就是 signal SIGINT;
  • 改变 GDB 信号处理的设置,通过 handle SIGINT nostop print 告诉 GDB 在接收到 SIGINT 时不要停止,并把该信号传递给调试目标程序 。

 

多线程下禁止线程切换

多线程程序执行时候使用n,可能跳到不同的执行语句,这是多线程cpu时间片切换到其他线程导致的,我们可以使用如下命令锁定当前线程:set scheduler-locking on(锁定)/off(解除)

 

gdb调试多进程

一个进程fork出来的子进程,有两种方式调试

1、gdb attach 到子进程

2、GDB 调试器提供了一个选项叫 follow-fork,可以使用 show follow-fork mode 查看当前值,也可以通过 set follow-fork mode 来设置是当一个进程 fork 出新的子进程时,GDB 是继续调试父进程还是子进程(取值是 child),默认是父进程( 取值是 parent)。

(gdb) show follow-fork mode

Debugger response to a program call of fork or vfork is "parent".

(gdb) set follow-fork child (gdb) show follow-fork mode

Debugger response to a program call of fork or vfork is "child".

 

自定义gdb调试命令

在某些场景下,我们需要根据自己的程序情况,制定一些可以在调试时输出程序特定信息的命令,这在 GDB 中很容易做到,只要在 Linux 当前用户家(home)目录下,如 root 用户是 “/root” 目录,非 root 用户则对应 “/home/ 用户名”目录。

在上述目录中自定义一个名叫 .gdbinit 文件,在 Linux 系统中以点号开头的文件名一般都是隐藏文件,因此 .gdbinit 也是一个隐藏文件,可以使用 ls -a 命令查看(a 的含义是 all 的意思,即显示所有文件,当然也就包括显示隐藏文件);如果不存在,使用 vim 或者 touch 命令创建一个就可以,然后在这个文件中写上你自定义命令的 shell 脚本即可。

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值