美文网首页
gdb的调试方法简介

gdb的调试方法简介

作者: Jamza | 来源:发表于2021-07-26 18:09 被阅读0次

使用 gdb 进行基本调试

编译

在编译时,使用 -g 选项进行编译

$ gcc -g program.c -o programname

启动调试

在 gdb 中启动程序

$ gdb programname 
(gdb) run arg1 "arg2" ...

在 gdb 中重启程序

(gdb) kill
Kill the program being debugged? (y or n) y
(gdb) run

退出 gdb

(gdb) quit
The program is running. Exit anyway? (y or n) 

调试已经正在运行的程序,需要使用到该程序的 PID,假设运行的程序 PID 为 20289,则使用 gdb 进行调试

$ gdb
(gdb) attach 20829

或者,简单直接调用

$ gdb att 20289

也可以直接使用 gdb program pid 的形式,比如

$ gdb hello 20829

单步调试

单步执行

(gdb) n

单步执行,进入函数

(gdb) s

跳出函数

(gdb) finish

执行跳至指定行号

(gdb) until [num]

查看代码

查看当前代码的运行位置

(gdb) where
#0  event_base_loop (base=0x602010, flags=0) at event.c:466
#1  0x00007ffff7bbb5c7 in event_base_dispatch (event_base=0x602010) at event.c:405
#2  0x0000000000400858 in main () at test.c:20

查看源代码

(gdb) l 

上述命令默认每次显示 10 行源代码,可以设置源码单次显示的行数

(gdb) set listsize 20
(gdb) show listsize
Number of source lines gdb will list by default is 20.

列出指定行附近的源代码

(gdb) l main.cpp:8

列出指定函数附近的源代码

(gdb) l printnum 

列出指定行之间的源代码

(gdb) l 3,15

执行 shell 命令

在 gdb 的命令行界面可以执行外部的 shell 命令

(gdb) shell ls
a.out test.c

断点

断点的设置原理:在程序中设置断点,就是先将该位置的原来的指令保存,然后向该位置写入 int 3 指令,当执行到 int 3 的时候,发生软中断,内核会给子进程发出 SIGTRAP 信号,当然这个信号会被转发给父进程。然后用保存的指令替换 int 3,等待恢复运行。

断点的实现原理:就是在指定的位置插入断点指令,当被调试的程序运行到断点的时候,产生 SIGTRAP 信号,该信号被 gdb 捕获并进行断点命中判定,当 gdb 判断出这次 SIGTRAP 是断点命中之后就会转入等待用户输入进行下一步处理,否则继续。

查看断点

(gdb) info b
Num     Type           Disp Enb Address            What
4       breakpoint     keep y   0x000000000040053f in main at test.c:6

设置行号断点

(gdb) break test.c:8 

设置 C 函数断点

(gdb) break func1

设置 C++ 函数断点

(gdb) break TestClass::testFunc(int) 

设置临时断点,临时断点只会断住一次,然后断点会自动被移除掉

(gdb) tbreak 8

设置条件断点

b 11 if i == 3

修改条件断点的条件,假设断点的号为 4

condition 4 i == 0  

去使能断点

(gdb) disable 2
(gdb) info breakpoints
Num Type           Disp Enb Address    What
2   breakpoint     keep n   0x080483c3 in func2 at test.c:5
3   breakpoint     keep y   0x080483da in func1 at test.c:10

跳过断点,假设在某个地方,我们知道可能出错,前面 30 次都没有问题,在这里我们设置了断点,但是我们想跳过前面 30 次,可以使用 ignore 命令,如下:第一个参数表示断点编号,第二个表示跳过次数。

(gdb) ignore 2 5
Will ignore next 5 crossings of breakpoint 2.

保存断点

(gdb) save breakpoints file-name-to-save

恢复保存的断点

(gdb) source file-name-to-save

堆栈

查看调用栈

(gdb) bt
#0  func2 (x=30) at test.c:5
#1  0x80483e6 in func1 (a=30) at test.c:10
#2  0x8048414 in main (argc=1, argv=0xbffffaf4) at test.c:19
#3  0x40037f5c in __libc_start_main () from /lib/libc.so.6
(gdb) 

选择栈帧,可以简写为 f 2

(gdb) frame 2
#2  0x8048414 in main (argc=1, argv=0xbffffaf4) at test.c:19
19        x = func1(x);
(gdb)

向上或者向下切换函数堆栈

(gdb) up 2
(gdb) down 2

查看栈帧

(gdb) info frame
Stack level 2, frame at 0xbffffa8c:
eip = 0x8048414 in main (test.c:19); saved eip 0x40037f5c
called by frame at 0xbffffac8, caller of frame at 0xbffffa5c
source language c.
Arglist at 0xbffffa8c, args: argc=1, argv=0xbffffaf4
Locals at 0xbffffa8c, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffffa8c, eip at 0xbffffa90

(gdb) info locals
x = 30
s = 0x8048484 "Hello World!\n"

(gdb) info args
argc = 1
argv = (char **) 0xbffffaf4

变量

最常见的就是使用 print 打印变量内容,其中 print 可以简写为 p

(gdb) p a

可以在变量名之前加上函数名来区分不同的同名变量

(gdb) p 'main'::b

可以按照特定的格式打印变量

  • x 按照十六进制格式打印变量
  • d 按照十进制格式打印变量
  • o 按照八进制格式打印变量
  • t 按照二进制格式打印变量
  • c 按照字符格式打印变量
  • f 按照浮点数格式打印变量
(gdb) p/x mask
$16 = 0x1

显示动态数组的内容,比如动态数组内存申请为 int *array = (int *) malloc (len * sizeof (int));

(gdb) p *array@len

自动显示变量,希望在程序被断住时,就显示某个变量的值,可以使用 display 命令

(gdb) display e

查看哪些变量设置了自动显示

(gdb) info display

清除自动显示变量

(gdb) del display [num]

去使能自动显示变量

(gdb) disable display [num]

使用 gdb 的高阶调试方法

查看内存

查看内存的命令格式为 x/[n][f][u] [address]

n 表示显示内存长度,默认值为 1

f 表示显示格式,如同上面打印变量定义

  • x 按十六进制格式显示变量
  • d 按十进制格式显示变量
  • u 按十六进制格式显示无符号整型
  • o 按八进制格式显示变量
  • t 按二进制格式显示变量
  • a 按十六进制格式显示变量
  • c 按字符格式显示变量
  • f 按浮点数格式显示变量

u 表示每次读取的字节数,默认是 4bytes

  • b 表示单字节
  • h 表示双字节
  • w 表示四字节
  • g 表示八字节

以字符串形式查看

char *s = "hello world";  // 源码

(gdb) x/s s
0x4005a0: "hello world"

以字符形式查看

(gdb) x/c s
0x4005a0: 104 'h'

以二进制形式查看

(gdb) x/t s
0x4005a0: 01101000

查看寄存器

(gdb) info registers
rax            0x4004f0 4195568
rbx            0x0  0
rcx            0x400510 4195600
rdx            0x7fffffffe598   140737488348568
rsi            0x7fffffffe588   140737488348552
rdi            0x1  1
rbp            0x7fffffffe4a0   0x7fffffffe4a0
rsp            0x7fffffffe4a0   0x7fffffffe4a0
r8             0x7ffff7dd6e80   140737351872128
r9             0x0  0
r10            0x7fffffffe2f0   140737488347888
r11            0x7ffff7a3ca40   140737348094528
r12            0x400400 4195328
r13            0x7fffffffe580   140737488348544
r14            0x0  0
r15            0x0  0
rip            0x400503 0x400503 <main+19>
eflags         0x246    [ PF ZF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0

查看汇编代码

(gdb) disassemble main
Dump of assembler code for function main:
   0x00000000004004f0 <+0>: push   %rbp
   0x00000000004004f1 <+1>: mov    %rsp,%rbp
   0x00000000004004f4 <+4>: mov    %edi,-0x14(%rbp)
   0x00000000004004f7 <+7>: mov    %rsi,-0x20(%rbp)
   0x00000000004004fb <+11>:    movq   $0x4005a0,-0x8(%rbp)
=> 0x0000000000400503 <+19>:    pop    %rbp
   0x0000000000400504 <+20>:    retq   
End of assembler dump.

信号

gdb 可以在调试程序的时候处理任何一种信号,即设置 gdb 需要处理哪种信号,并设置在 gdb 接收到信号后,立即停止程序,忽略信号等操作。

设置信号的格式为 handle <signal> <keyword>

keyword 可以为

  • nostop 接收到信号后,gdb 不停止程序,但是会打印消息,通知用户收到了某个信号
  • stop 接收到信号后,gdb 停止程序
  • noignore 接收到信号后,gdb 不处理信号,将其交给调试程序处理
  • ignore 接收到信号后,gdb 不让调试程序处理这个信号

多线程调试

查看当前进程的所有线程

(gdb) info threads

切换线程栈

(gdb) thread [线程号]

打印所有的线程堆栈

(gdb) thread apply all bt

打印指定线程堆栈

(gdb) thread apply 5 bt   # 5为线程id

用 gdb 调试多线程程序时,一旦程序断住,所有的线程都处于暂停状态。此时当你调试其中一个线程时(比如执行“step”,“next”命令),所有的线程都会同时执行,有时候我们希望执行流一直在某个线程执行,而不是切换到其他线程。

我们可以使用命令 set scheduler-locking on/off/step

set scheduler-locking on 可以用来锁定当前线程,只观察这个线程的运行情况, 当锁定这个线程时, 其他线程就处于了暂停状态。也就是说你在当前线程执行 next、step、until、finish、return 命令时,其他线程是不会运行的。

set scheduler-locking off 用于关闭锁定当前线程。

set scheduler-locking step 也是用来锁定当前线程,当且仅当使用 next 或 step 命令做单步调试时会锁定当前线程。如果你使用 until、finish、return 等线程内调试命令,但是它们不是单步命令,所以其他线程还是有机会运行的。相比较 on 选项值,step 选项值给为单步调试提供了更加精细化的控制。

相关文章

  • 2. gdb的使用

    参考链接1. GDB调试2. gdb调试方法3. gdb调试示例 1. 说说 gdb gdb 是一款 UNIX 系...

  • gdb调试基本方法

    gdb调试基本方法 gdb调试常用命令 在GDB中运行程序 断点(BreakPoint) 观察点(WatchPoi...

  • 【性能优化】如何使用 gdb dump 内存

    1.GDB 调试器简介 GDB 全称 “GNU symbolic debugger”,是 Linux 下常用的程序...

  • GDB学习笔记

    本文介绍使用gdb调试程序的常用命令。 简介 GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。如果...

  • gdb的使用简述

    gdb简介 gdb命令行调试工具非常强大,是linux下调试的神器。可以完成四个方面的功能 启动程序,按照自定义的...

  • gdb调试方法

    gdb是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 启动...

  • gdb调试方法

    gdb查看内存数据 格式: x /nfu说明:x 是 examine 的缩写,意思是检查。 n表示要显示的内存单元...

  • 开发工具·GDB使用笔记

    =============GDB调试程序===========1.调试代码初级方法加打印语句:在关键语句的前后,打...

  • gcc常用命令

    gdb相关 gcc加-g才能使用gdb调试gdb -tui a.out打开调试界面run/stop/continu...

  • GDB调试

    使用GDB调试配置环境参数: Attach进程: Debug GDB常用方法(1)常用的命令,以拍照为例拍照函数在...

网友评论

      本文标题:gdb的调试方法简介

      本文链接:https://www.haomeiwen.com/subject/sjlemltx.html