gdb的调试方法简介

日期: 栏目:常识 浏览:9

gdb的调试方法简介

使用 gdb 进行基本调试

编译

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

$ gcc -g program.c -o programname

启动调试

在 gdb 中启动程序

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

在 gdb 中重启程序

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

退出 gdb

(gdb) quitThe 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 listsizeNumber 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 lsa.out test.c

断点

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

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

查看断点

(gdb) info bNum Type Disp Enb Address What4 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 breakpointsNum Type Disp Enb Address What2 breakpoint keep n 0x080483c3 in func2 at test.c:53 breakpoint keep y 0x080483da in func1 at test.c:10

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

(gdb) ignore 2 5Will 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:1919 x = func1(x);(gdb)

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

(gdb) up 2(gdb) down 2

查看栈帧

(gdb) info frameStack level 2, frame at 0xbffffa8c:eip = 0x8048414 in main (test.c:19); saved eip 0x40037f5ccalled by frame at 0xbffffac8, caller of frame at 0xbffffa5csource language c.Arglist at 0xbffffa8c, args: argc=1, argv=0xbffffaf4Locals at 0xbffffa8c, Previous frame's sp is 0x0Saved registers:ebp at 0xbffffa8c, eip at 0xbffffa90(gdb) info localsx = 30s = 0x8048484 "Hello World!\n"(gdb) info argsargc = 1argv = (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 s0x4005a0: "hello world"

以字符形式查看

(gdb) x/c s0x4005a0: 104 'h'

以二进制形式查看

(gdb) x/t s0x4005a0: 01101000

查看寄存器

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

查看汇编代码

(gdb) disassemble mainDump 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>: retqEnd of assembler dump.

信号

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

设置信号的格式为 handle

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 选项值给为单步调试提供了更加精细化的控制。

您还可以搜索:gdb的调试过程,gdb调试是什么意思,gdb调试设置参数,gdb调试命令的使用及总结,gdb调试工具的使用,gdb调试常用命令,gdb调试器的使用方法和编译步骤,gdb调试器的功能,gdb调试详解,gdb调试技巧④

本文地址:https://caijingdemo.com/changshi/36269.html

标签: