1. 前言
GNU调试器(英语:GNU Debugger,缩写:GDB),是GNU软件系统中的标准调试器,此外GDB也是个具有移携性的调试器,经过移携需求的调修与重新编译,如今许多的类UNIX操作系统上都可以使用GDB,而现有GDB所能支持调试的编程语言有C、C++、Pascal以及FORTRAN。在Linux下开发C语言,经常用到gdb进行调试,下面总结一下gdb调试过程中常用的命令。
2. 应用场景
一般来说,GDB主要帮忙你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、动态的改变你程序的执行环境。
3. 调试程序
要使用gdb进行调试,必须在编译的时候加上-g参数,不然是无法使用gdb进行调试的。
$ cc -g lkm.c -o lkm
编译完成后,使用gdb 可执行文件,启动gdb调试。
szp@szp-pc:~/code/mynode$ gdb lkm GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from lkm... (gdb)
使用l 行数可以列出源代码,然后直接按回车会重复执行l命令,会继续列出后面的代码。
(gdb) l 1 1 // 2 // Created by szp on 2020/10/10. 3 // 4 #include <stdio.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 #include <string.h> 8 void show_opt () { 9 printf("1. process name \n" 10 "2. group id \n" (gdb) l 11 "3. parent process \n" 12 "4. process group leader \n" 13 "5. child processes created \n" 14 "6. process memory segments \n" 15 "7 . virtual memory mapping \n" 16 "8. process priority \n" 17 "9. process state \n" 18 "10. cpu used by process \n" 19 "11. total fault count of process \n" 20 "12. process start time \n" (gdb) 21 "13. process link count \n" 22 "0. quit \n"); 23 printf ("\nEnter Option : "); 24 } 25 26 27 int main () { 28 char buf [700],opt[50]; 29 int rt; 30 // open device node of LKM (gdb)
按照行数设置断点,通过break 行数
(gdb) break 9 Breakpoint 1 at 0x1271: file lkm.c, line 9.
按照函数名设置断点,break 函数名
(gdb) break show_opt Breakpoint 2 at 0x1269: file lkm.c, line 8.
查看断点信息info break
(gdb) info break Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000001271 in show_opt at lkm.c:9 2 breakpoint keep y 0x0000000000001269 in show_opt at lkm.c:8 (gdb)
运行程序 r,run命令简写。在设置的断点处停下。
(gdb) r Starting program: /home/szp/code/mynode/lkm Enter Process PID:1 Breakpoint 2, show_opt () at lkm.c:8 8 void show_opt () { (gdb)
单条语句执行,next命令简写n。
(gdb) n Breakpoint 1, show_opt () at lkm.c:9 9 printf("1. process name \n"
打印变量的值p 变量,print的缩写。
(gdb) p buf $1 = 0x0
GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
(gdb) p/c buf $3 = 0 '\000' (gdb)
产看函数堆栈 bt
(gdb) bt #0 show_opt () at lkm.c:23 #1 0x0000555555555301 in main () at lkm.c:37
退出当前函数 finish
(gdb) finish Run till exit from #0 show_opt () at lkm.c:23 main () at lkm.c:38 38 scanf("%s",opt); (gdb)
继续执行c,continue缩写。
(gdb) c Continuing.
要退出gdb时,只用发quit或命令简称q就行了。
gdb的命令很多,gdb把之分成许多个种类。help命令只是例出gdb的命令种类,如果要看种类中的命令,可以使用help
(gdb) help
List of classes of commands:
aliases – Aliases of other commands.
breakpoints – Making program stop at certain points.
data – Examining data.
files – Specifying and examining files.
internals – Maintenance commands.
obscure – Obscure features.
running – Running the program.
stack – Examining the stack.
status – Status inquiries.
support – Support facilities.
tracepoints – Tracing of program execution without stopping the program.
user-defined – User-defined commands.
Type “help” followed by a class name for a list of commands in that class.
Type “help all” for the list of all commands.
Type “help” followed by command name for full documentation.
Type “apropos word” to search for commands related to “word”.
Type “apropos -v word” for full documentation of commands related to “word”.
Command name abbreviations are allowed if unambiguous.
(gdb)
gdb中,输入命令时,可以不用将命令敲完整,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令,在Linux下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其例出来。
在gdb中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕捉点(CatchPoint)、信号(Signals)、线程停止(Thread Stops)。如果要恢复程序运行,可以使用c或是continue命令。详细内容不介绍了。
info line命令查看源代码在内存中的地址。info line后面可以跟“行号”,“函数名”,“文件名:行号”,“文件名:函数名”,这个命令会打印出所指定的源码在运行时的内存地址,如:
(gdb) info line show_opt Line 8 of "lkm.c" starts at address 0x555555555269 <show_opt> and ends at 0x555555555271 <show_opt+8>.
disassemble可以查看源程序的当前执行时的汇编代码,这个命令会把目前内存中的指令dump出来。
(gdb) disassemble show_opt Dump of assembler code for function show_opt: => 0x0000555555555269 <+0>: endbr64 0x000055555555526d <+4>: push %rbp 0x000055555555526e <+5>: mov %rsp,%rbp 0x0000555555555271 <+8>: lea 0xd90(%rip),%rdi # 0x555555556008 0x0000555555555278 <+15>: callq 0x5555555550e0 <puts@plt> 0x000055555555527d <+20>: lea 0xebe(%rip),%rdi # 0x555555556142 0x0000555555555284 <+27>: mov $0x0,%eax 0x0000555555555289 <+32>: callq 0x555555555110 <printf@plt> 0x000055555555528e <+37>: nop 0x000055555555528f <+38>: pop %rbp 0x0000555555555290 <+39>: retq End of assembler dump.
查看寄存器的值info registers
(gdb) info registers rax 0x0 0 rbx 0x555555555430 93824992236592 rcx 0x0 0 rdx 0x0 0 rsi 0xa 10 rdi 0x7fffffffd750 140737488344912 rbp 0x7fffffffdc80 0x7fffffffdc80 rsp 0x7fffffffdc80 0x7fffffffdc80 r8 0xa 10 r9 0x12 18 r10 0x555555556172 93824992239986 r11 0x246 582 r12 0x555555555180 93824992235904 r13 0x7fffffffe0a0 140737488347296 r14 0x0 0 r15 0x0 0 rip 0x555555555271 0x555555555271 <show_opt+8> eflags 0x206 [ PF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 --Type <RET> for more, q to quit, c to continue without paging--
修改变量的值
修改被调试程序运行时的变量值,在GDB中很容易实现,使用GDB的print命令即可完成。如:
(gdb) print x=4
4. 总结
以上总结的都是gdb最基础的调试命令,满足简单使用,gdb还有很多高级命令,后续进行补充,此外,善用help命令可以发现更多gdb好用的命令。