本文旨在对程序debug利器gdb工具常用技巧进行汇总。从介绍、信息显示、函数、断电、观察点、打印、多进程、线程、core dump文件、汇编、图形化界面等方面展开。
信息显示 查看gdb版本信息
查看gdb版权信息
启动时不显示提示信息
退出时不显示提示信息 1 2 (gdb) set confirm off 可以把这个命令加到.gdbinit文件里
函数 列出函数名字 1 2 3 (gdb) info functions or (gdb) info functions regex
是否进入到调试信息的函数 1 2 step (s) 进入函数; next (n) 不进入函数
退出正在调试的函数
finish 函数会继续执行完,并且打印返回值,然后等待输入接下来的命令。
return 函数不会继续执行下面的语句,直接返回。也可以用 return expression 命令指定函数的返回值。
打印函数堆栈帧信息 使用gdb调试程序时,可以使用“i frame”命令显示函数堆栈帧信息。
1 2 3 i frame i registers disassemble func
选择函数堆栈帧 1 2 3 frame n (n是层数) or frame addr (addr是堆栈地址)
断点 命名空间设置断点
在程序地址上打断点
当调试汇编程序时,或者没有调试信息的程序时,可用此法。
在程序入口处打断点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 (base) huang@mlt:~/gdb$ strip hello (base) huang@mlt:~/gdb$ readelf -h hello ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400470 Start of program headers: 64 (bytes into file) Start of section headers: 4472 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 9 Size of section headers: 64 (bytes) Number of section headers: 29 Section header string table index: 28
当调试没有调试信息的程序时,直接运行start命令是没有效果的:
1 2 (gdb) start Function "main" not defined
如果不知道main在何处,那么可以在入口处打断点。先通过readelf或者进入gdb,执行info files获得入口地址,然后:
1 2 (gdb) b *0x400470 (gdb) r
在文件行号上打断点 1 2 3 (gdb) b n or (gdb) b file:linenum
设置临时断点
断点只生效一次,使用tbreak命令(tb)。
设置条件断点
观察点 设置观察点 1 2 3 4 5 (gdb) watch n or (gdb) p &a $1 = (int *) 0x6009c8 <a> (gdb) watch *(int*)0x6009c8
只有当一个变量值发生变化时,程序会停下来。
查看所有观察点。
打印 打印ASCII和宽字符字符串 1 2 3 4 5 6 7 8 9 #include <stdio.h> #include <wchar.h> int main() { char str1[] = "abcd"; wchar_t str2[] = L"abcd"; return 0; }
使用gdb调试时,使用“x/s”命令打印ASCII字符串。
1 2 3 4 5 6 7 8 9 (gdb) x/s str1 0x804779f: "abcd" (gdb) n 7 wchar_t str2[] = L"abcd"; (gdb) p sizeof(wchar_t) $1 = 4 (gdb) x/ws str2 0x8047788: U"abcd"
4个字节用“x/ws”,两个字节,则用”x/hs”命令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 (gdb) print a $11 = "\000\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017" (1)16进制格式打印数组a前16个byte的值 (gdb) x/16x a 0x7fffffffdf30: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x7fffffffdf38: 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f (2)以无符号10进制格式打印数组a前16个byte的值 (gdb) x/16u a 0x7fffffffdf30: 0 1 2 3 4 5 6 7 0x7fffffffdf38: 8 9 10 11 12 13 14 15 (3)2进制格式打印数组a前16个byte的值 (gdb) x/16t a 0x7fffffffdf30: 00000000 00000001 00000010 00000011 00000100 00000101 00000110 00000111 0x7fffffffdf38: 00001000 00001001 00001010 00001011 00001100 00001101 00001110 00001111 (4)以16进制格式打印数组第5到8 4个元素的值 (gdb) x/4x a[5]@4 0x7fffffffdf35: 0x05 0x06 0x07 0x08
打印数组中任意连续元素值 1 2 (gdb) p array[index]@num index是数组索引,从0开始计数,num是连续多少个元素
打印数组元素下标 1 2 (gdb) set print array-indexes on (gdb) p num
打印函数局部变量的值 1 2 3 (gdb) bt full or (gdb) info locals
打印进程内存信息 1 2 3 (gdb) i proc mappings or (gdb) i files
打印内存的值 1 2 3 4 5 6 7 8 9 #include <stdio.h> int main() { int i = 0; char a[100]; for (i=0; i<sizeof(a); i++) { a[i] = i; } return 0; }
gdb中使用“x”命令来打印内存的值,格式为“x/nfu addr”。含义为以f格式打印从addr开始的n个长度为u的内存值。具体参数含义如下:
n: 输出单元的个数。
f: 输出格式。比如f是16进制;o是8进制。
u:标明一个单位的长度。b是一个byte,h是两个byte(half word),w是四个byte (word),g是八个byte (giant word)。
汇编 设置汇编指令格式
将源程序和汇编指令映射起来 源代码:
1 2 3 4 5 6 7 8 9 #include <stdio.h> int main() { int i = 0; char a[100]; for (i=0; i<sizeof(a); i++) { a[i] = i; } return 0; }
使用“disas /m fun”命令将函数代码和汇编指令映射起来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 (gdb) disas /m main Dump of assembler code for function main: 3 int main(){ 0x0000000000400546 <+0>: push %rbp 0x0000000000400547 <+1>: mov %rsp,%rbp 0x000000000040054a <+4>: sub $0x30,%rsp 0x000000000040054e <+8>: mov %fs:0x28,%rax 0x0000000000400557 <+17>: mov %rax,-0x8(%rbp) 0x000000000040055b <+21>: xor %eax,%eax 4 int i = 0; 0x000000000040055d <+23>: movl $0x0,-0x24(%rbp) 5 char a[16]; 6 for(i=0; i<sizeof(a); ++i) 0x0000000000400564 <+30>: movl $0x0,-0x24(%rbp) 0x000000000040056b <+37>: jmp 0x40057f <main+57> 0x000000000040057b <+53>: addl $0x1,-0x24(%rbp) 0x000000000040057f <+57>: mov -0x24(%rbp),%eax 0x0000000000400582 <+60>: cmp $0xf,%eax 0x0000000000400585 <+63>: jbe 0x40056d <main+39> 7 a[i] = i; 0x000000000040056d <+39>: mov -0x24(%rbp),%eax 0x0000000000400570 <+42>: mov %eax,%edx 0x0000000000400572 <+44>: mov -0x24(%rbp),%eax 0x0000000000400575 <+47>: cltq 0x0000000000400577 <+49>: mov %dl,-0x20(%rbp,%rax,1) 8 return 0; => 0x0000000000400587 <+65>: mov $0x0,%eax 9 } 0x000000000040058c <+70>: mov -0x8(%rbp),%rcx 0x0000000000400590 <+74>: xor %fs:0x28,%rcx 0x0000000000400599 <+83>: je 0x4005a0 <main+90> 0x000000000040059b <+85>: callq 0x400420 <__stack_chk_fail@plt> 0x00000000004005a0 <+90>: leaveq 0x00000000004005a1 <+91>: retq End of assembler dump.
查看某行所对应的地址范围,使用“disassemble [start], [end]”命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Breakpoint 4, main () at mem.c:4 4 int i = 0; (gdb) l 1 #include <stdio.h> 2 3 int main(){ 4 int i = 0; 5 char a[16]; 6 for(i=0; i<sizeof(a); ++i) 7 a[i] = i; 8 return 0; 9 } (gdb) i line 4 Line 4 of "mem.c" starts at address 0x40055d <main+23> and ends at 0x400564 <main+30>. (gdb) disassemble 0x40055d, 0x400564 Dump of assembler code from 0x40055d to 0x400564: => 0x000000000040055d <main+23>: movl $0x0,-0x24(%rbp) End of assembler dump.
打印寄存器的值 1 2 3 (gdb) i registers (gdb) i all-registers (gdb) i registers eax
改变字符串的值 1 2 3 4 5 6 7 8 9 #incldue <stdio.h> int main() { char p1[] = "sam"; char *p2 = "Bob"; printf("p1 is %s\n", p1); printf("p2 is %s\n", p2); return 0; }
可以使用set命令改变字符串的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 (gdb) r Starting program: /home/huang/gdb/set p1 is sam Breakpoint 1, main () at set.c:7 7 printf("p2 is %s\n", p2); (gdb) l 2 int main() { 3 char p1[] = "sam"; 4 char *p2 = "Bob"; 5 6 printf("p1 is %s\n", p1); 7 printf("p2 is %s\n", p2); 8 return 0; 9 } (gdb) print p1 $1 = "sam" (gdb) print p2 $2 = 0x400694 "Bob" (gdb) set p1="abc" (gdb) set p2="zzz" (gdb) n p2 is zzz Breakpoint 2, main () at set.c:8 8 return 0;
设置变量的值 1 (gdb) set var variable=expr
跳转到指定位置
显示共享链接库信息 1 (gdb) info sharedlibrary regex
脚本 配置gdb init文件 配置文件:.gdbinit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 保存历史命令 set history filename ~/.gdb_history set history save on # 退出时不显示提示信息 set confirm off # 按照派生类型打印对象 set print object on # 打印数组的索引下标 set print array-indexes on #每行打印一个结构体成员 set print pretty on
图形化界面 进入和退出图形化调试界面 1 2 3 4 5 6 7 $ gdb -tui program # 显示汇编窗口 (gdb) layout asm or (gdb)layout split # 显示寄存器窗口 (gdb) layout regs
缩写技巧 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 b -> break c -> continue d -> delete f -> frame i -> info j -> jump l -> list n -> nxet p -> print r -> run s -> step u -> until ---- aw -> awatch bt -> backtrace dir -> directory disas -> disassemble fin -> finish ig -> ignore ni -> nexti rw -> rwatch si -> stepi tb -> tbreak wa -> watch win -> winheight
来源:https://github.com/hellogcc/100-gdb-tips