为什么80%的码农都做不了架构师?>>>   

显示gdb版本信息

技巧

使用gdb时,如果想查看gdb版本信息,可以使用“show version”命令:

显示gdb版权相关信息

技巧

使用gdb时,如果想查看gdb版权相关信息,可以使用“show copying”命令:

启动时不显示提示信息

例子

 
  1. $ gdb
  2. GNU gdb (GDB) 7.7.50.20140228-cvs
  3. Copyright (C) 2014 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "x86_64-unknown-linux-gnu".
  9. Type "show configuration" for configuration details.
  10. For bug reporting instructions, please see:
  11. <http://www.gnu.org/software/gdb/bugs/>.
  12. Find the GDB manual and other documentation resources online at:
  13. <http://www.gnu.org/software/gdb/documentation/>.
  14. For help, type "help".
  15. Type "apropos word" to search for commands related to "word".

技巧

gdb在启动时会显示如上类似的提示信息。

如果不想显示这个信息,则可以使用-q选项把提示信息关掉:

 
  1. $ gdb -q
  2. (gdb)

你可以在~/.bashrc中,为gdb设置一个别名:

 
  1. alias gdb="gdb -q"

gdb退出时不显示提示信息

技巧

gdb在退出时会提示:

 
  1. A debugging session is active.
  2. Inferior 1 [process 29686 ] will be killed.
  3. Quit anyway? (y or n) n

如果不想显示这个信息,则可以在gdb中使用如下命令把提示信息关掉:

 
  1. (gdb) set confirm off

也可以把这个命令加到.gdbinit文件里。

输出信息多时不会暂停输出

技巧

有时当gdb输出信息较多时,gdb会暂停输出,并会打印“---Type <return> to continue, or q <return> to quit---”这样的提示信息,如下面所示:

 
  1. 81 process 2639102 0xff04af84 in __lwp_park () from /usr/lib/libc.so.1
  2. 80 process 2573566 0xff04af84 in __lwp_park () from /usr/lib/libc.so.1
  3. ---Type <return> to continue, or q <return> to quit---Quit

解决办法是使用“set pagination off”或者“set height 0”命令。这样gdb就会全部输出,中间不会暂停。

列出函数的名字

使用gdb调试时,使用“info functions”命令可以列出可执行文件的所有函数名称。

是否进入带调试信息的函数

例子

 
  1. #include <stdio.h>
  2. int func(void)
  3. {
  4. return 3;
  5. }
  6. int main(void)
  7. {
  8. int a = 0;
  9. a = func();
  10. printf("%d\n", a);
  11. return 0;
  12. }

技巧

使用gdb调试遇到函数时,使用step命令(缩写为s)可以进入函数(函数必须有调试信息)。以上面代码为例:

 
  1. (gdb) n
  2. 12 a = func();
  3. (gdb) s
  4. func () at a.c:5
  5. 5 return 3;
  6. (gdb) n
  7. 6 }
  8. (gdb)
  9. main () at a.c:13
  10. 13 printf("%d\n", a);

可以看到gdb进入了func函数。

可以使用next命令(缩写为n)不进入函数,gdb会等函数执行完,再显示下一行要执行的程序代码:

 
  1. (gdb) n
  2. 12 a = func();
  3. (gdb) n
  4. 13 printf("%d\n", a);
  5. (gdb) n
  6. 3
  7. 14 return 0;

可以看到gdb没有进入func函数。

进入不带调试信息的函数

例子

 
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. typedef struct
  4. {
  5. int a;
  6. int b;
  7. int c;
  8. int d;
  9. pthread_mutex_t mutex;
  10. }ex_st;
  11. int main(void) {
  12. ex_st st = {1, 2, 3, 4, PTHREAD_MUTEX_INITIALIZER};
  13. printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d);
  14. return 0;
  15. }

技巧

默认情况下,gdb不会进入不带调试信息的函数。以上面代码为例:

 
  1. (gdb) n
  2. 15 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d);
  3. (gdb) s
  4. 1,2,3,4
  5. 16 return 0;

可以看到由于printf函数不带调试信息,所以“s”命令(s是“step”缩写)无法进入printf函数。

可以执行“set step-mode on”命令,这样gdb就不会跳过没有调试信息的函数:

 
  1. (gdb) set step-mode on
  2. (gdb) n
  3. 15 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d);
  4. (gdb) s
  5. 0x00007ffff7a993b0 in printf () from /lib64/libc.so.6
  6. (gdb) s
  7. 0x00007ffff7a993b7 in printf () from /lib64/libc.so.6

可以看到gdb进入了printf函数,接下来可以使用调试汇编程序的办法去调试函数。

退出正在调试的函数

例子

 
  1. #include <stdio.h>
  2. int func(void)
  3. {
  4. int i = 0;
  5. i += 2;
  6. i *= 10;
  7. return i;
  8. }
  9. int main(void)
  10. {
  11. int a = 0;
  12. a = func();
  13. printf("%d\n", a);
  14. return 0;
  15. }

技巧

当单步调试一个函数时,如果不想继续跟踪下去了,可以有两种方式退出。

第一种用“finish”命令,这样函数会继续执行完,并且打印返回值,然后等待输入接下来的命令。以上面代码为例:

 
  1. (gdb) n
  2. 17 a = func();
  3. (gdb) s
  4. func () at a.c:5
  5. 5 int i = 0;
  6. (gdb) n
  7. 7 i += 2;
  8. (gdb) fin
  9. find finish
  10. (gdb) finish
  11. Run till exit from #0 func () at a.c:7
  12. 0x08050978 in main () at a.c:17
  13. 17 a = func();
  14. Value returned is $1 = 20

可以看到当不想再继续跟踪func函数时,执行完“finish”命令,gdb会打印结果:“20”,然后停在那里。

详情参见gdb手册

第二种用“return”命令,这样函数不会继续执行下面的语句,而是直接返回。也可以用“return expression”命令指定函数的返回值。仍以上面代码为例:

 
  1. (gdb) n
  2. 17 a = func();
  3. (gdb) s
  4. func () at a.c:5
  5. 5 int i = 0;
  6. (gdb) n
  7. 7 i += 2;
  8. (gdb) n
  9. 8 i *= 10;
  10. (gdb) re
  11. record remove-inferiors return reverse-next reverse-step
  12. refresh remove-symbol-file reverse-continue reverse-nexti reverse-stepi
  13. remote restore reverse-finish reverse-search
  14. (gdb) return 40
  15. Make func return now? (y or n) y
  16. #0 0x08050978 in main () at a.c:17
  17. 17 a = func();
  18. (gdb) n
  19. 18 printf("%d\n", a);
  20. (gdb)
  21. 40
  22. 19 return 0;

可以看到“return”命令退出了函数并且修改了函数的返回值。

直接执行函数

例子

 
  1. #include <stdio.h>
  2. int global = 1;
  3. int func(void)
  4. {
  5. return (++global);
  6. }
  7. int main(void)
  8. {
  9. printf("%d\n", global);
  10. return 0;
  11. }

技巧

使用gdb调试程序时,可以使用“call”或“print”命令直接调用函数执行。以上面程序为例:

 
  1. (gdb) start
  2. Temporary breakpoint 1 at 0x4004e3: file a.c, line 12.
  3. Starting program: /data2/home/nanxiao/a
  4. Temporary breakpoint 1, main () at a.c:12
  5. 12 printf("%d\n", global);
  6. (gdb) call func()
  7. $1 = 2
  8. (gdb) print func()
  9. $2 = 3
  10. (gdb) n
  11. 3
  12. 13 return 0;

可以看到执行两次func函数后,global的值变成3

打印函数堆栈帧信息

例子

 
  1. #include <stdio.h>
  2. int func(int a, int b)
  3. {
  4. int c = a * b;
  5. printf("c is %d\n", c);
  6. }
  7. int main(void)
  8. {
  9. func(1, 2);
  10. return 0;
  11. }

技巧

使用gdb调试程序时,可以使用“i frame”命令(iinfo命令缩写)显示函数堆栈帧信息。以上面程序为例:

 
  1. Breakpoint 1, func (a=1, b=2) at a.c:5
  2. 5 printf("c is %d\n", c);
  3. (gdb) i frame
  4. Stack level 0, frame at 0x7fffffffe590:
  5. rip = 0x40054e in func (a.c:5); saved rip = 0x400577
  6. called by frame at 0x7fffffffe5a0
  7. source language c.
  8. Arglist at 0x7fffffffe580, args: a=1, b=2
  9. Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590
  10. Saved registers:
  11. rbp at 0x7fffffffe580, rip at 0x7fffffffe588
  12. (gdb) i registers
  13. rax 0x2 2
  14. rbx 0x0 0
  15. rcx 0x0 0
  16. rdx 0x7fffffffe688 140737488348808
  17. rsi 0x2 2
  18. rdi 0x1 1
  19. rbp 0x7fffffffe580 0x7fffffffe580
  20. rsp 0x7fffffffe560 0x7fffffffe560
  21. r8 0x7ffff7dd4e80 140737351863936
  22. r9 0x7ffff7dea560 140737351951712
  23. r10 0x7fffffffe420 140737488348192
  24. r11 0x7ffff7a35dd0 140737348066768
  25. r12 0x400440 4195392
  26. r13 0x7fffffffe670 140737488348784
  27. r14 0x0 0
  28. r15 0x0 0
  29. rip 0x40054e 0x40054e <func+24>
  30. eflags 0x202 [ IF ]
  31. cs 0x33 51
  32. ss 0x2b 43
  33. ds 0x0 0
  34. es 0x0 0
  35. fs 0x0 0
  36. gs 0x0 0
  37. (gdb) disassemble func
  38. Dump of assembler code for function func:
  39. 0x0000000000400536 <+0>: push %rbp
  40. 0x0000000000400537 <+1>: mov %rsp,%rbp
  41. 0x000000000040053a <+4>: sub $0x20,%rsp
  42. 0x000000000040053e <+8>: mov %edi,-0x14(%rbp)
  43. 0x0000000000400541 <+11>: mov %esi,-0x18(%rbp)
  44. 0x0000000000400544 <+14>: mov -0x14(%rbp),%eax
  45. 0x0000000000400547 <+17>: imul -0x18(%rbp),%eax
  46. 0x000000000040054b <+21>: mov %eax,-0x4(%rbp)
  47. => 0x000000000040054e <+24>: mov -0x4(%rbp),%eax
  48. 0x0000000000400551 <+27>: mov %eax,%esi
  49. 0x0000000000400553 <+29>: mov $0x400604,%edi
  50. 0x0000000000400558 <+34>: mov $0x0,%eax
  51. 0x000000000040055d <+39>: callq 0x400410 <printf@plt>
  52. 0x0000000000400562 <+44>: leaveq
  53. 0x0000000000400563 <+45>: retq
  54. End of assembler dump.

可以看到执行“i frame”命令后,输出了当前函数堆栈帧的地址,指令寄存器的值,局部变量地址及值等信息,可以对照当前寄存器的值和函数的汇编指令看一下。

打印尾调用堆栈帧信息

例子

 
  1. #include<stdio.h>
  2. void a(void)
  3. {
  4. printf("Tail call frame\n");
  5. }
  6. void b(void)
  7. {
  8. a();
  9. }
  10. void c(void)
  11. {
  12. b();
  13. }
  14. int main(void)
  15. {
  16. c();
  17. return 0;
  18. }

技巧

当一个函数最后一条指令是调用另外一个函数时,开启优化选项的编译器常常以最后被调用的函数返回值作为调用者的返回值,这称之为“尾调用(Tail call)”。以上面程序为例,编译程序(使用‘-O’):

 
  1. gcc -g -O -o test test.c

查看main函数汇编代码:

 
  1. (gdb) disassemble main
  2. Dump of assembler code for function main:
  3. 0x0000000000400565 <+0>: sub $0x8,%rsp
  4. 0x0000000000400569 <+4>: callq 0x400536 <a>
  5. 0x000000000040056e <+9>: mov $0x0,%eax
  6. 0x0000000000400573 <+14>: add $0x8,%rsp
  7. 0x0000000000400577 <+18>: retq

可以看到main函数直接调用了函数a,根本看不到函数b和函数c的影子。

在函数a入口处打上断点,程序停止后,打印堆栈帧信息:

 
  1. (gdb) i frame
  2. Stack level 0, frame at 0x7fffffffe590:
  3. rip = 0x400536 in a (test.c:4); saved rip = 0x40056e
  4. called by frame at 0x7fffffffe5a0
  5. source language c.
  6. Arglist at 0x7fffffffe580, args:
  7. Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590
  8. Saved registers:
  9. rip at 0x7fffffffe588

看不到尾调用的相关信息。

可以设置“debug entry-values”选项为非0的值,这样除了输出正常的函数堆栈帧信息以外,还可以输出尾调用的相关信息:

 
  1. (gdb) set debug entry-values 1
  2. (gdb) b test.c:4
  3. Breakpoint 1 at 0x400536: file test.c, line 4.
  4. (gdb) r
  5. Starting program: /home/nanxiao/test
  6. Breakpoint 1, a () at test.c:4
  7. 4 {
  8. (gdb) i frame
  9. tailcall: initial:
  10. Stack level 0, frame at 0x7fffffffe590:
  11. rip = 0x400536 in a (test.c:4); saved rip = 0x40056e
  12. called by frame at 0x7fffffffe5a0
  13. source language c.
  14. Arglist at 0x7fffffffe580, args:
  15. Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590
  16. Saved registers:
  17. rip at 0x7fffffffe588

可以看到输出了“tailcall: initial:”信息。

选择函数堆栈帧

例子

 
  1. #include <stdio.h>
  2. int func1(int a)
  3. {
  4. return 2 * a;
  5. }
  6. int func2(int a)
  7. {
  8. int c = 0;
  9. c = 2 * func1(a);
  10. return c;
  11. }
  12. int func3(int a)
  13. {
  14. int c = 0;
  15. c = 2 * func2(a);
  16. return c;
  17. }
  18. int main(void)
  19. {
  20. printf("%d\n", func3(10));
  21. return 0;
  22. }

技巧

用gdb调试程序时,当程序暂停后,可以用“frame n”命令选择函数堆栈帧,其中n是层数。以上面程序为例:

 
  1. (gdb) b test.c:5
  2. Breakpoint 1 at 0x40053d: file test.c, line 5.
  3. (gdb) r
  4. Starting program: /home/nanxiao/test
  5. Breakpoint 1, func1 (a=10) at test.c:5
  6. 5 return 2 * a;
  7. (gdb) bt
  8. #0 func1 (a=10) at test.c:5
  9. #1 0x0000000000400560 in func2 (a=10) at test.c:11
  10. #2 0x0000000000400586 in func3 (a=10) at test.c:18
  11. #3 0x000000000040059e in main () at test.c:24
  12. (gdb) frame 2
  13. #2 0x0000000000400586 in func3 (a=10) at test.c:18
  14. 18 c = 2 * func2(a);

可以看到程序断住后,最内层的函数帧为第0帧。执行frame 2命令后,当前的堆栈帧变成了fun3的函数帧。

也可以用“frame addr”命令选择函数堆栈帧,其中addr是堆栈地址。仍以上面程序为例:

 
  1. (gdb) frame 2
  2. #2 0x0000000000400586 in func3 (a=10) at test.c:18
  3. 18 c = 2 * func2(a);
  4. (gdb) i frame
  5. Stack level 2, frame at 0x7fffffffe590:
  6. rip = 0x400586 in func3 (test.c:18); saved rip = 0x40059e
  7. called by frame at 0x7fffffffe5a0, caller of frame at 0x7fffffffe568
  8. source language c.
  9. Arglist at 0x7fffffffe580, args: a=10
  10. Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590
  11. Saved registers:
  12. rbp at 0x7fffffffe580, rip at 0x7fffffffe588
  13. (gdb) frame 0x7fffffffe568
  14. #1 0x0000000000400560 in func2 (a=10) at test.c:11
  15. 11 c = 2 * func1(a);

使用“i frame”命令可以知道0x7fffffffe568func2的函数堆栈帧地址,使用“frame 0x7fffffffe568”可以切换到func2的函数堆栈帧。

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

例子

 
  1. #include <stdio.h>
  2. int func1(int a)
  3. {
  4. return 2 * a;
  5. }
  6. int func2(int a)
  7. {
  8. int c = 0;
  9. c = 2 * func1(a);
  10. return c;
  11. }
  12. int func3(int a)
  13. {
  14. int c = 0;
  15. c = 2 * func2(a);
  16. return c;
  17. }
  18. int main(void)
  19. {
  20. printf("%d\n", func3(10));
  21. return 0;
  22. }

技巧

用gdb调试程序时,当程序暂停后,可以用“up n”或“down n”命令向上或向下选择函数堆栈帧,其中n是层数。以上面程序为例:

 
  1. (gdb) b test.c:5
  2. Breakpoint 1 at 0x40053d: file test.c, line 5.
  3. (gdb) r
  4. Starting program: /home/nanxiao/test
  5. Breakpoint 1, func1 (a=10) at test.c:5
  6. 5 return 2 * a;
  7. (gdb) bt
  8. #0 func1 (a=10) at test.c:5
  9. #1 0x0000000000400560 in func2 (a=10) at test.c:11
  10. #2 0x0000000000400586 in func3 (a=10) at test.c:18
  11. #3 0x000000000040059e in main () at test.c:24
  12. (gdb) frame 2
  13. #2 0x0000000000400586 in func3 (a=10) at test.c:18
  14. 18 c = 2 * func2(a);
  15. (gdb) up 1
  16. #3 0x000000000040059e in main () at test.c:24
  17. 24 printf("%d\n", func3(10));
  18. (gdb) down 2
  19. #1 0x0000000000400560 in func2 (a=10) at test.c:11
  20. 11 c = 2 * func1(a);

可以看到程序断住后,先执行“frame 2”命令,切换到fun3函数。接着执行“up 1”命令,此时会切换到main函数,也就是会往外层的堆栈帧移动一层。反之,当执行“down 2”命令后,又会向内层堆栈帧移动二层。如果不指定n,则n默认为1.

还有“up-silently n”和“down-silently n”这两个命令,与“up n”和“down n”命令区别在于,切换堆栈帧后,不会打印信息,仍以上面程序为例:

 
  1. (gdb) up
  2. #2 0x0000000000400586 in func3 (a=10) at test.c:18
  3. 18 c = 2 * func2(a);
  4. (gdb) bt
  5. #0 func1 (a=10) at test.c:5
  6. #1 0x0000000000400560 in func2 (a=10) at test.c:11
  7. #2 0x0000000000400586 in func3 (a=10) at test.c:18
  8. #3 0x000000000040059e in main () at test.c:24
  9. (gdb) up-silently
  10. (gdb) i frame
  11. Stack level 3, frame at 0x7fffffffe5a0:
  12. rip = 0x40059e in main (test.c:24); saved rip = 0x7ffff7a35ec5
  13. caller of frame at 0x7fffffffe590
  14. source language c.
  15. Arglist at 0x7fffffffe590, args:
  16. Locals at 0x7fffffffe590, Previous frame's sp is 0x7fffffffe5a0
  17. Saved registers:
  18. rbp at 0x7fffffffe590, rip at 0x7fffffffe598

可以看到从func3切换到main函数堆栈帧时,并没有打印出相关信息。

在匿名空间设置断点

例子

 
  1. namespace Foo
  2. {
  3. void foo()
  4. {
  5. }
  6. }
  7. namespace
  8. {
  9. void bar()
  10. {
  11. }
  12. }

技巧

在gdb中,如果要对namespace Foo中的foo函数设置断点,可以使用如下命令:

 
  1. (gdb) b Foo::foo

如果要对匿名空间中的bar函数设置断点,可以使用如下命令:

 
  1. (gdb) b (anonymous namespace)::bar

在程序地址上打断点

例子

 
  1. 0000000000400522 <main>:
  2. 400522: 55 push %rbp
  3. 400523: 48 89 e5 mov %rsp,%rbp
  4. 400526: 8b 05 00 1b 00 00 mov 0x1b00(%rip),%eax # 40202c <he+0xc>
  5. 40052c: 85 c0 test %eax,%eax
  6. 40052e: 75 07 jne 400537 <main+0x15>
  7. 400530: b8 7c 06 40 00 mov $0x40067c,%eax
  8. 400535: eb 05 jmp 40053c <main+0x1a>

技巧

当调试汇编程序,或者没有调试信息的程序时,经常需要在程序地址上打断点,方法为b *address。例如:

 
  1. (gdb) b *0x400522

在程序入口处打断点

获取程序入口

方法一:

 
  1. $ strip a.out
  2. $ readelf -h a.out
  3. ELF Header:
  4. Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  5. Class: ELF64
  6. Data: 2's complement, little endian
  7. Version: 1 (current)
  8. OS/ABI: UNIX - System V
  9. ABI Version: 0
  10. Type: EXEC (Executable file)
  11. Machine: Advanced Micro Devices X86-64
  12. Version: 0x1
  13. Entry point address: 0x400440
  14. Start of program headers: 64 (bytes into file)
  15. Start of section headers: 4496 (bytes into file)
  16. Flags: 0x0
  17. Size of this header: 64 (bytes)
  18. Size of program headers: 56 (bytes)
  19. Number of program headers: 9
  20. Size of section headers: 64 (bytes)
  21. Number of section headers: 29
  22. Section header string table index: 28

方法二:

 
  1. $ gdb a.out
  2. >>> info files
  3. Symbols from "/home/me/a.out".
  4. Local exec file:
  5. `/home/me/a.out', file type elf64-x86-64.
  6. Entry point: 0x400440
  7. 0x0000000000400238 - 0x0000000000400254 is .interp
  8. 0x0000000000400254 - 0x0000000000400274 is .note.ABI-tag
  9. 0x0000000000400274 - 0x0000000000400298 is .note.gnu.build-id
  10. 0x0000000000400298 - 0x00000000004002b4 is .gnu.hash
  11. 0x00000000004002b8 - 0x0000000000400318 is .dynsym
  12. 0x0000000000400318 - 0x0000000000400355 is .dynstr
  13. 0x0000000000400356 - 0x000000000040035e is .gnu.version
  14. 0x0000000000400360 - 0x0000000000400380 is .gnu.version_r
  15. 0x0000000000400380 - 0x0000000000400398 is .rela.dyn
  16. 0x0000000000400398 - 0x00000000004003e0 is .rela.plt
  17. 0x00000000004003e0 - 0x00000000004003fa is .init
  18. 0x0000000000400400 - 0x0000000000400440 is .plt
  19. 0x0000000000400440 - 0x00000000004005c2 is .text
  20. 0x00000000004005c4 - 0x00000000004005cd is .fini
  21. 0x00000000004005d0 - 0x00000000004005e0 is .rodata
  22. 0x00000000004005e0 - 0x0000000000400614 is .eh_frame_hdr
  23. 0x0000000000400618 - 0x000000000040070c is .eh_frame
  24. 0x0000000000600e10 - 0x0000000000600e18 is .init_array
  25. 0x0000000000600e18 - 0x0000000000600e20 is .fini_array
  26. 0x0000000000600e20 - 0x0000000000600e28 is .jcr
  27. 0x0000000000600e28 - 0x0000000000600ff8 is .dynamic
  28. 0x0000000000600ff8 - 0x0000000000601000 is .got
  29. 0x0000000000601000 - 0x0000000000601030 is .got.plt
  30. 0x0000000000601030 - 0x0000000000601040 is .data
  31. 0x0000000000601040 - 0x0000000000601048 is .bss

技巧

当调试没有调试信息的程序时,直接运行start命令是没有效果的:

 
  1. (gdb) start
  2. Function "main" not defined.

如果不知道main在何处,那么可以在程序入口处打断点。先通过readelf或者进入gdb,执行info files获得入口地址,然后:

 
  1. (gdb) b *0x400440
  2. (gdb) r

在文件行号上打断点

例子

 
  1. /* a/file.c */
  2. #include <stdio.h>
  3. void print_a (void)
  4. {
  5. puts ("a");
  6. }
  7. /* b/file.c */
  8. #include <stdio.h>
  9. void print_b (void)
  10. {
  11. puts ("b");
  12. }
  13. /* main.c */
  14. extern void print_a(void);
  15. extern void print_b(void);
  16. int main(void)
  17. {
  18. print_a();
  19. print_b();
  20. return 0;
  21. }

技巧

这个比较简单,如果要在当前文件中的某一行打断点,直接b linenum即可,例如:

 
  1. (gdb) b 7

也可以显式指定文件,b file:linenum例如:

 
  1. (gdb) b file.c:6
  2. Breakpoint 1 at 0x40053b: file.c:6. (2 locations)
  3. (gdb) i breakpoints
  4. Num Type Disp Enb Address What
  5. 1 breakpoint keep y <MULTIPLE>
  6. 1.1 y 0x000000000040053b in print_a at a/file.c:6
  7. 1.2 y 0x000000000040054b in print_b at b/file.c:6

可以看出,gdb会对所有匹配的文件设置断点。你可以通过指定(部分)路径,来区分相同的文件名:

 
  1. (gdb) b a/file.c:6

注意:通过行号进行设置断点的一个弊端是,如果你更改了源程序,那么之前设置的断点就可能不是你想要的了。

保存已经设置的断点

例子

 
  1. $ gdb -q `which gdb`
  2. Reading symbols from /home/xmj/install/binutils-trunk/bin/gdb...done.
  3. (gdb) b gdb_main
  4. Breakpoint 1 at 0x5a7af0: file /home/xmj/project/binutils-trunk/gdb/main.c, line 1061.
  5. (gdb) b captured_main
  6. Breakpoint 2 at 0x5a6bd0: file /home/xmj/project/binutils-trunk/gdb/main.c, line 310.
  7. (gdb) b captured_command_loop
  8. Breakpoint 3 at 0x5a68b0: file /home/xmj/project/binutils-trunk/gdb/main.c, line 261.

技巧

在gdb中,可以使用如下命令将设置的断点保存下来:

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

下此调试时,可以使用如下命令批量设置保存的断点:

 
  1. (gdb) source file-name-to-save
 
  1. (gdb) info breakpoints
  2. Num Type Disp Enb Address What
  3. 1 breakpoint keep y 0x00000000005a7af0 in gdb_main at /home/xmj/project/binutils-trunk/gdb/main.c:1061
  4. 2 breakpoint keep y 0x00000000005a6bd0 in captured_main at /home/xmj/project/binutils-trunk/gdb/main.c:310
  5. 3 breakpoint keep y 0x00000000005a68b0 in captured_command_loop at /home/xmj/project/binutils-trunk/gdb/main.c:261

设置临时断点

例子

 
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. typedef struct
  4. {
  5. int a;
  6. int b;
  7. int c;
  8. int d;
  9. pthread_mutex_t mutex;
  10. }ex_st;
  11. int main(void) {
  12. ex_st st = {1, 2, 3, 4, PTHREAD_MUTEX_INITIALIZER};
  13. printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d);
  14. return 0;
  15. }

技巧

在使用gdb时,如果想让断点只生效一次,可以使用“tbreak”命令(缩写为:tb)。以上面程序为例:

 
  1. (gdb) tb a.c:15
  2. Temporary breakpoint 1 at 0x400500: file a.c, line 15.
  3. (gdb) i b
  4. Num Type Disp Enb Address What
  5. 1 breakpoint del y 0x0000000000400500 in main at a.c:15
  6. (gdb) r
  7. Starting program: /data2/home/nanxiao/a
  8. Temporary breakpoint 1, main () at a.c:15
  9. 15 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d);
  10. (gdb) i b
  11. No breakpoints or watchpoints.

首先在文件的第15行设置临时断点,当程序断住后,用“i b”(”info breakpoints”缩写)命令查看断点,发现断点没有了。也就是断点命中一次后,就被删掉了。

设置条件断点

例子

 
  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int i = 0;
  5. int sum = 0;
  6. for (i = 1; i <= 200; i++)
  7. {
  8. sum += i;
  9. }
  10. printf("%d\n", sum);
  11. return 0;
  12. }

技巧

gdb可以设置条件断点,也就是只有在条件满足时,断点才会被触发,命令是“break … if cond”。以上面程序为例:

 
  1. (gdb) start
  2. Temporary breakpoint 1 at 0x4004cc: file a.c, line 5.
  3. Starting program: /data2/home/nanxiao/a
  4. Temporary breakpoint 1, main () at a.c:5
  5. 5 int i = 0;
  6. (gdb) b 10 if i==101
  7. Breakpoint 2 at 0x4004e3: file a.c, line 10.
  8. (gdb) r
  9. Starting program: /data2/home/nanxiao/a
  10. Breakpoint 2, main () at a.c:10
  11. 10 sum += i;
  12. (gdb) p sum
  13. $1 = 5050

忽略断点

例子

 
  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int i = 0;
  5. int sum = 0;
  6. for (i = 1; i <= 200; i++)
  7. {
  8. sum += i;
  9. }
  10. printf("%d\n", sum);
  11. return 0;
  12. }

技巧

在设置断点以后,可以忽略断点,命令是“ignore bnum count”:意思是接下来count次编号为bnum的断点触发都不会让程序中断,只有第count + 1次断点触发才会让程序中断。以上面程序为例:

 
  1. (gdb) b 10
  2. Breakpoint 1 at 0x4004e3: file a.c, line 10.
  3. (gdb) ignore 1 5
  4. Will ignore next 5 crossings of breakpoint 1.
  5. (gdb) r
  6. Starting program: /data2/home/nanxiao/a
  7. Breakpoint 1, main () at a.c:10
  8. 10 sum += i;
  9. (gdb) p i
  10. $1 = 6

可以看到设定忽略断点前5次触发后,第一次断点断住时,打印i的值是6。如果想让断点下次就生效,可以将count置为0:“ignore 1 0”。

转载于:https://my.oschina.net/lamYu/blog/865870

linux下gdb所有实用方法相关推荐

  1. Linux下gdb相关使用方法

    目录 一.进入调试的方式 二.单线程下的GDB 三.多线程下的GDB 注1:gdb调试值core文件临时生成,首先使用ulimit -c查看core文件大小设置,并可用该命令更改大小设置,当为ulim ...

  2. linux下gdb调试方法和技巧详解

    linux下gdb调试方法和技巧整理 简介 UNIX或者UNIX-like下调试工具 启动gdb # 1. 在可执行程序不需要输入参数时,我们可以使用 gdb + 可执行程序 gdb ./typeid ...

  3. linux单步调试方法,linux下gdb单步调试(中).doc

    linux下gdb单步调试(中) linux下gdb单步调试(中) linux下gdb单步调试(中) 一.设置断点( BreakPoint ) 我们用 break 命令来设置断点.正面有几点设置断点的 ...

  4. linux常用调试,linux下gdb常用的调试命令

    用gdb调试程序时,常常很困惑一些命令的使用,要么是不知道这个命令,要么忘了命令的使用方法.接下来是小编为大家收集的linux下gdb常用的调试命令,希望能帮到大家. linux下gdb常用的调试命令 ...

  5. 网关屏蔽mac地址,linux下修改mac地址方法

    局域网里有一台机器,同一网段内的机器,只有部分能够ping通,也无法ping通网关,现象很奇怪,尝试过修改IP,重启网络服务,都无法解决问题. 最后怀疑是不是网关将该机器mac地址给屏蔽掉了,因此修改 ...

  6. gdb 调试_一文入门Linux下gdb调试(二)

    点击"蓝字"关注我吧 作者:良知犹存 转载授权以及围观:欢迎添加微信号:Conscience_Remains 总述     今天我们介绍一下core dump文件,Core dum ...

  7. linux gdb网络调试,一文入门Linux下gdb调试(二)

    本文转载自[微信公众号:羽林君,ID:Conscience_Remains] 总述 今天我们介绍一下core dump文件,Core dump叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快 ...

  8. Linux下环境变量配置方法梳理(.bash_profile和.bashrc的区别)

    博客园 首页 新随笔 联系 管理 订阅 <div class="blogStats"><!--done--> 随笔- 556  文章- 38  评论- 77 ...

  9. linux下添加路由的方法

    Linux中增加软路由的两种方法 第一种: route add -net 172.16.6.0 netmask 255.255.255.0 gw 172.16.2.254 dev eth0 /* 增加 ...

最新文章

  1. 从绩点 1.8 逆袭的复旦博士,华为天才少年!
  2. R语言Gamma分布函数Gamma Distribution(dgamma, pgamma, qgamma rgamma)实战
  3. css元素捕捉,css元素选择器
  4. Java基础点——面向对象(中)
  5. win8: 清除iframe的缓存
  6. friso mysql_非sqlite和nigix的开源c项目
  7. .net 集合分成几个等数量集合_[C#.NET 拾遗补漏]08:强大的LINQ
  8. 邻接矩阵中啥时候写0和无穷_(一)UDS诊断服务中的诊断会话控制(DiagnosticSessionControl,0x10)...
  9. 机器学习算法总结之支持向量机(三)
  10. ELK5.3日志分析平台部署
  11. errorC1083 无法打开源文件 c1xx
  12. 计算机爱情诗,优美诗句大全
  13. 用计算机作品装扮校园,童心向党艺绽放 幸福成长梦起航——惠安县八二三实验小学第七届校园文化艺术节之十五“手绘童心 泼洒阳光”计算机作品制作比赛...
  14. oracle发生20001,Oracle10g重建EM 报ORA-20001: SYSMAN already exists
  15. 题目1026 c语言,1026 程序运行时间——C/C++实现
  16. DAEMON tools lite “至少SPTD ..与 Windows 2000或更高版本” 报错的解决
  17. 【转】江枫:在ubuntu10.04上安装oracle 10g(学习一下)
  18. 我在windows10下,使用CMake gui 编译krita源码
  19. 0x00000001java_「十六进制表示」0x00000001是个啥?32位表示、十六进制表示 - seo实验室...
  20. 7.消费者的确认机制

热门文章

  1. Spring Boot 定义接口的方法是否可以声明为 private?
  2. Spring Boot 启动时,让方法自动执行的 4 种方法!
  3. 给 Spring Boot 项目减减肥!18.18M 到 0.18M 是如何做到的?
  4. 人工智能的产业落地经验!
  5. 中国移动这个编程大赛来了!
  6. 北大韦神透露现状:自己课讲得不太好,中期学生退课后就剩下5、6个人
  7. 北大校友“炼丹”分享:OpenAI如何训练千亿级模型?
  8. 北大毕业典礼上,一男博士求婚女硕士成功,网友直呼:科研人的爱情太甜了!...
  9. NLP界的「MVP」再次夺冠,刷新GLUE榜单
  10. 这10个让你笑的合不拢嘴的GitHub项目,居然拿了7万星