├── 100个gdb小技巧(v1.0).pdf ├── LICENSE ├── README.md ├── build.sh ├── refcard.pdf ├── src ├── add-copy-inferiors.md ├── attach-process.md ├── break-anonymous-namespace.md ├── break-on-address.md ├── break-on-entry.md ├── break-on-first-assembly-code.md ├── break-on-linenum.md ├── breakpoint-command.md ├── call-func.md ├── catch-exec.md ├── catch-fork.md ├── catch-ptrace.md ├── catch-syscall.md ├── catch-vfork.md ├── change-string.md ├── config-gdbinit.md ├── directory.md ├── disassemble-next-line.md ├── disassemble-raw-machine-code.md ├── display-instruction-pc.md ├── examine-memory.md ├── finish-and-return.md ├── generate-core-dump-file.md ├── help.md ├── ignore-break.md ├── index.md ├── info-frame.md ├── info-function.md ├── info-signals.md ├── info_sharedlibrary.md ├── jump.md ├── keep-unused-types.md ├── layout-asm.md ├── layout-regs.md ├── load-executable-and-coredump-file.md ├── load-library.md ├── maint-info-program-space.md ├── maint-info-sol-threads.md ├── map-source-code-and-assembly.md ├── modify-pc-register.md ├── option-format.md ├── pass-signal.md ├── patch-program.md ├── preprocessor-macro.md ├── print-STL-container.md ├── print-all-threads-bt.md ├── print-array-indexes.md ├── print-ascii-and-wide-string.md ├── print-consecutive-array-elements.md ├── print-derived-type.md ├── print-formatted-array.md ├── print-frame-variables.md ├── print-large-array.md ├── print-local-variables.md ├── print-malloc-memory.md ├── print-process-memory.md ├── print-registers.md ├── print-signal.md ├── print-source-lines.md ├── print-static-variables.md ├── print-struct-with-offset.md ├── print-threads.md ├── print-variable-info.md ├── quit-gdb-silently.md ├── run-cd-pwd.md ├── run-shell-command.md ├── save-breakpoints.md ├── save-history-commands.md ├── select-frame.md ├── send-signal.md ├── set-condition-break.md ├── set-debug-entry-values.md ├── set-detach-on-fork.md ├── set-disassembly-flavor.md ├── set-follow-fork-mode-child.md ├── set-io-tty.md ├── set-logging.md ├── set-pagination-off.md ├── set-print-pretty-on.md ├── set-program-args.md ├── set-program-env.md ├── set-prompt.md ├── set-read-watchpoint.md ├── set-read-write-watchpoint.md ├── set-scheduler-locking-on.md ├── set-script-extension.md ├── set-step-mode-on.md ├── set-tbreak.md ├── set-var.md ├── set-watchpoint-on-specified-thread.md ├── set-watchpoint.md ├── show-copying-warranty.md ├── show-print-thread-events.md ├── show-version.md ├── show-vtbl-content.md ├── start-gdb-silently.md ├── step-and-next-function.md ├── stop-signal.md ├── substitute-path.md ├── tcatch.md ├── trace-instructions.md ├── tui-mode.md ├── up-down-select-frame.md ├── use-$_-$__-variables.md ├── use-$_exitcode.md ├── use-$_siginfo-variable.md ├── use-$_thread-variable.md ├── use-short-command.md └── winheight.md └── utils └── build.go /100个gdb小技巧(v1.0).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellogcc/100-gdb-tips/b134d12400cdc4c56f492407611044834be4f7b4/100个gdb小技巧(v1.0).pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《100个gdb小技巧》 2 | 3 | 一个关于gdb使用小技巧的文档。100,在这里可能只是表明很多;具体的数目取决于您的参与和贡献。 4 | 5 | ## 在线阅读 6 | [开始阅读]() 7 | 8 | ## 如何参与 9 | 10 | 直接发PULL REQUEST,或与我们联系。 11 | 12 | 增加一个小技巧的步骤: 13 | 14 | 1. 在src目录下新增一个md文件,参照现有文件的格式风格,编写一个小技巧 15 | markdown语法参见 http://wowubuntu.com/markdown/ 16 | md文件编写可以使用在线所见即所得编辑器 https://www.zybuluo.com/mdeditor 17 | 2. 在index.md中为新md文件增加一个索引,可以放到已有分类中,或增加一个分类 18 | 3. 如果预览下没有问题,OK! 19 | 20 | 本地生成html的步骤: 21 | 22 | 1. 确保[go](http://code.google.com/p/go)和[md2min](https://github.com/fairlyblank/md2min)已经安装并可用 23 | 2. 直接运行build.sh 24 | 3. 如果顺利,会在html目录下生成所有的html文件 25 | 26 | ## 联系方式 27 | 28 | - [博客网站](http://www.hellogcc.org) 29 | - 在线讨论问题:IRC, freenode, #hellogcc房间 30 | - [邮件列表](http://www.freelists.org/list/hellogcc) (发信需要先订阅) 31 | 32 | ## 版权 33 | 34 | 本文档版权归贡献者所有。 35 | 36 | ## 授权许可 37 | 38 | 本文档使用的是[GNU Free Documentation License](http://www.gnu.org/licenses/fdl.html)。 39 | 40 | ## 致谢 41 | 42 | - 各位参与者 43 | 44 | ## 其它资源 45 | 46 | - [GDB在线手册](https://sourceware.org/gdb/onlinedocs/gdb) 47 | - [GDB命令卡片](https://github.com/hellogcc/100-gdb-tips/blob/master/refcard.pdf) 48 | - [GDB dashboard](https://github.com/cyrus-and/gdb-dashboard) 49 | - [Gdbinit for OS X, iOS and others - x86, x86_64 and ARM](https://github.com/gdbinit/Gdbinit) 50 | - [dotgdb:关于底层调试和反向工程的gdb脚本集](https://github.com/dholm/dotgdb) 51 | 52 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script invokes md2min to convert markdown files to minimal html files, 4 | # using github css. So go and md2min should be available before you run it. 5 | # See https://github.com/fairlyblank/md2min 6 | # See http://code.google.com/p/go 7 | 8 | TOPDIR=`dirname $0` 9 | 10 | if [ ! type -P go >/dev/null 2>&1 ]; then 11 | echo "error: can't find go, which is necessary for building html" 12 | exit 0 13 | fi 14 | 15 | if [ ! type -P md2min >/dev/null 2>&1 ]; then 16 | echo "error: can't find md2min, which is necessary for building html" 17 | exit 0 18 | fi 19 | 20 | mkdir -p "$TOPDIR/html" 21 | 22 | ( 23 | export SRC="$TOPDIR/src" 24 | export HTML="$TOPDIR/html" 25 | go run "$TOPDIR/utils/build.go" 26 | ) 27 | 28 | -------------------------------------------------------------------------------- /refcard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellogcc/100-gdb-tips/b134d12400cdc4c56f492407611044834be4f7b4/refcard.pdf -------------------------------------------------------------------------------- /src/add-copy-inferiors.md: -------------------------------------------------------------------------------- 1 | # 一个gdb会话中同时调试多个程序 2 | ## 例子 3 | a.c: 4 | #include 5 | int func(int a, int b) 6 | { 7 | int c = a * b; 8 | printf("c is %d\n", c); 9 | } 10 | 11 | int main(void) 12 | { 13 | func(1, 2); 14 | return 0; 15 | } 16 | 17 | 18 | b.c: 19 | #include 20 | 21 | int func1(int a) 22 | { 23 | return 2 * a; 24 | } 25 | 26 | int func2(int a) 27 | { 28 | int c = 0; 29 | c = 2 * func1(a); 30 | return c; 31 | } 32 | 33 | int func3(int a) 34 | { 35 | int c = 0; 36 | c = 2 * func2(a); 37 | return c; 38 | } 39 | 40 | int main(void) 41 | { 42 | printf("%d\n", func3(10)); 43 | return 0; 44 | } 45 | 46 | 47 | ## 技巧 48 | gdb支持在一个会话中同时调试多个程序。以上面程序为例,首先调试`a`程序: 49 | 50 | root@bash:~$ gdb a 51 | GNU gdb (Ubuntu 7.7-0ubuntu3) 7.7 52 | Copyright (C) 2014 Free Software Foundation, Inc. 53 | License GPLv3+: GNU GPL version 3 or later 54 | This is free software: you are free to change and redistribute it. 55 | There is NO WARRANTY, to the extent permitted by law. Type "show copying" 56 | and "show warranty" for details. 57 | This GDB was configured as "x86_64-linux-gnu". 58 | Type "show configuration" for configuration details. 59 | For bug reporting instructions, please see: 60 | . 61 | Find the GDB manual and other documentation resources online at: 62 | . 63 | For help, type "help". 64 | Type "apropos word" to search for commands related to "word"... 65 | Reading symbols from a...done. 66 | (gdb) start 67 | Temporary breakpoint 1 at 0x400568: file a.c, line 10. 68 | Starting program: /home/nanxiao/a 69 | 70 | 接着使用“`add-inferior [ -copies n ] [ -exec executable ]`”命令加载可执行文件`b`。其中`n`默认为1: 71 | 72 | (gdb) add-inferior -copies 2 -exec b 73 | Added inferior 2 74 | Reading symbols from b...done. 75 | Added inferior 3 76 | Reading symbols from b...done. 77 | (gdb) i inferiors 78 | Num Description Executable 79 | 3 /home/nanxiao/b 80 | 2 /home/nanxiao/b 81 | * 1 process 1586 /home/nanxiao/a 82 | (gdb) inferior 2 83 | [Switching to inferior 2 [] (/home/nanxiao/b)] 84 | (gdb) start 85 | Temporary breakpoint 2 at 0x400568: main. (3 locations) 86 | Starting program: /home/nanxiao/b 87 | 88 | Temporary breakpoint 2, main () at b.c:24 89 | 24 printf("%d\n", func3(10)); 90 | (gdb) i inferiors 91 | Num Description Executable 92 | 3 /home/nanxiao/b 93 | * 2 process 1590 /home/nanxiao/b 94 | 1 process 1586 /home/nanxiao/a 95 | 可以看到可以调试`b`程序了。 96 | 97 | 另外也可用“`clone-inferior [ -copies n ] [ infno ]`”克隆现有的`inferior`,其中`n`默认为1,`infno`默认为当前的`inferior`: 98 | 99 | (gdb) i inferiors 100 | Num Description Executable 101 | 3 /home/nanxiao/b 102 | * 2 process 1590 /home/nanxiao/b 103 | 1 process 1586 /home/nanxiao/a 104 | (gdb) clone-inferior -copies 1 105 | Added inferior 4. 106 | (gdb) i inferiors 107 | Num Description Executable 108 | 4 /home/nanxiao/b 109 | 3 /home/nanxiao/b 110 | * 2 process 1590 /home/nanxiao/b 111 | 1 process 1586 /home/nanxiao/a 112 | 可以看到又多了一个`b`程序。 113 | 114 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Inferiors-and-Programs.html). 115 | 116 | ## 贡献者 117 | 118 | nanxiao 119 | -------------------------------------------------------------------------------- /src/attach-process.md: -------------------------------------------------------------------------------- 1 | # 调试已经运行的进程 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | void *thread_func(void *p_arg) 8 | { 9 | while (1) 10 | { 11 | printf("%s\n", (char*)p_arg); 12 | sleep(10); 13 | } 14 | } 15 | int main(void) 16 | { 17 | pthread_t t1, t2; 18 | 19 | pthread_create(&t1, NULL, thread_func, "Thread 1"); 20 | pthread_create(&t2, NULL, thread_func, "Thread 2"); 21 | 22 | sleep(1000); 23 | return; 24 | } 25 | 26 | 27 | 28 | ## 技巧 29 | 30 | 调试已经运行的进程有两种方法:一种是gdb启动时,指定进程的ID:gdb program processID(也可以用-p或者--pid指定进程ID,例如:gdb program -p=10210)。以上面代码为例,用“ps”命令已经获得进程ID为10210: 31 | 32 | bash-3.2# gdb -q a 10210 33 | Reading symbols from /data/nan/a...done. 34 | Attaching to program `/data/nan/a', process 10210 35 | [New process 10210] 36 | Retry #1: 37 | Retry #2: 38 | Retry #3: 39 | Retry #4: 40 | Reading symbols from /usr/lib/libc.so.1...(no debugging symbols found)...done. 41 | [Thread debugging using libthread_db enabled] 42 | [New LWP 3 ] 43 | [New LWP 2 ] 44 | [New Thread 1 (LWP 1)] 45 | [New Thread 2 (LWP 2)] 46 | [New Thread 3 (LWP 3)] 47 | Loaded symbols for /usr/lib/libc.so.1 48 | Reading symbols from /lib/ld.so.1...(no debugging symbols found)...done. 49 | Loaded symbols for /lib/ld.so.1 50 | [Switching to Thread 1 (LWP 1)] 51 | 0xfeeeae55 in ___nanosleep () from /usr/lib/libc.so.1 52 | (gdb) bt 53 | #0 0xfeeeae55 in ___nanosleep () from /usr/lib/libc.so.1 54 | #1 0xfeedcae4 in sleep () from /usr/lib/libc.so.1 55 | #2 0x080509ef in main () at a.c:17 56 | 57 | 如果嫌每次ps查看进程号比较麻烦,请尝试如下脚本 58 | 59 | ```shell 60 | # 保存为xgdb.sh(添加可执行权限) 61 | # 用法 xgdb.sh a 62 | prog_bin=$1 63 | running_name=$(basename $prog_bin) 64 | pid=$(/sbin/pidof $running_name) 65 | gdb attach $pid 66 | ``` 67 | 68 | 69 | 另一种是先启动gdb,然后用“attach”命令“附着”在进程上: 70 | 71 | bash-3.2# gdb -q a 72 | Reading symbols from /data/nan/a...done. 73 | (gdb) attach 10210 74 | Attaching to program `/data/nan/a', process 10210 75 | [New process 10210] 76 | Retry #1: 77 | Retry #2: 78 | Retry #3: 79 | Retry #4: 80 | Reading symbols from /usr/lib/libc.so.1...(no debugging symbols found)...done. 81 | [Thread debugging using libthread_db enabled] 82 | [New LWP 3 ] 83 | [New LWP 2 ] 84 | [New Thread 1 (LWP 1)] 85 | [New Thread 2 (LWP 2)] 86 | [New Thread 3 (LWP 3)] 87 | Loaded symbols for /usr/lib/libc.so.1 88 | Reading symbols from /lib/ld.so.1...(no debugging symbols found)...done. 89 | Loaded symbols for /lib/ld.so.1 90 | [Switching to Thread 1 (LWP 1)] 91 | 0xfeeeae55 in ___nanosleep () from /usr/lib/libc.so.1 92 | (gdb) bt 93 | #0 0xfeeeae55 in ___nanosleep () from /usr/lib/libc.so.1 94 | #1 0xfeedcae4 in sleep () from /usr/lib/libc.so.1 95 | #2 0x080509ef in main () at a.c:17 96 | 97 | 98 | 99 | 如果不想继续调试了,可以用“detach”命令“脱离”进程: 100 | 101 | (gdb) detach 102 | Detaching from program: /data/nan/a, process 10210 103 | (gdb) bt 104 | No stack. 105 | 106 | 107 | 详情参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Attach.html#index-attach) 108 | 109 | ## 贡献者 110 | 111 | nanxiao 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/break-anonymous-namespace.md: -------------------------------------------------------------------------------- 1 | # 在匿名空间设置断点 2 | 3 | ## 例子 4 | namespace Foo 5 | { 6 | void foo() 7 | { 8 | } 9 | } 10 | 11 | namespace 12 | { 13 | void bar() 14 | { 15 | } 16 | } 17 | 18 | ## 技巧 19 | 20 | 可以先使用查看所有函数信息,这样便于理解,使用如下命令: 21 | 22 | (gdb) info functions 23 | All defined functions: 24 | 25 | File test.cpp: 26 | 3: void Foo::foo(); 27 | 10: static void (anonymous namespace)::bar(); 28 | 29 | 在gdb中,如果要对namespace Foo中的foo函数设置断点,可以使用如下命令: 30 | 31 | (gdb) b Foo::foo 32 | 33 | 如果要对匿名空间中的bar函数设置断点,可以使用如下命令: 34 | 35 | (gdb) b (anonymous namespace)::bar 36 | 37 | ## 贡献者 38 | 39 | xmj, Franklin-Qi 40 | 41 | -------------------------------------------------------------------------------- /src/break-on-address.md: -------------------------------------------------------------------------------- 1 | # 在程序地址上打断点 2 | 3 | ## 例子 4 | 5 | 0000000000400522
: 6 | 400522: 55 push %rbp 7 | 400523: 48 89 e5 mov %rsp,%rbp 8 | 400526: 8b 05 00 1b 00 00 mov 0x1b00(%rip),%eax # 40202c 9 | 40052c: 85 c0 test %eax,%eax 10 | 40052e: 75 07 jne 400537 11 | 400530: b8 7c 06 40 00 mov $0x40067c,%eax 12 | 400535: eb 05 jmp 40053c 13 | 14 | ## 技巧 15 | 16 | 当调试汇编程序,或者没有调试信息的程序时,经常需要在程序地址上打断点,方法为`b *address`。例如: 17 | 18 | (gdb) b *0x400522 19 | 20 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Specify-Location.html#Specify-Location) 21 | 22 | ## 贡献者 23 | 24 | xmj 25 | 26 | -------------------------------------------------------------------------------- /src/break-on-entry.md: -------------------------------------------------------------------------------- 1 | # 在程序入口处打断点 2 | 3 | ## 获取程序入口 4 | 5 | ### 方法一: 6 | 7 | $ strip a.out 8 | $ readelf -h a.out 9 | ELF Header: 10 | Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 11 | Class: ELF64 12 | Data: 2's complement, little endian 13 | Version: 1 (current) 14 | OS/ABI: UNIX - System V 15 | ABI Version: 0 16 | Type: EXEC (Executable file) 17 | Machine: Advanced Micro Devices X86-64 18 | Version: 0x1 19 | Entry point address: 0x400440 20 | Start of program headers: 64 (bytes into file) 21 | Start of section headers: 4496 (bytes into file) 22 | Flags: 0x0 23 | Size of this header: 64 (bytes) 24 | Size of program headers: 56 (bytes) 25 | Number of program headers: 9 26 | Size of section headers: 64 (bytes) 27 | Number of section headers: 29 28 | Section header string table index: 28 29 | 30 | ### 方法二: 31 | 32 | $ gdb a.out 33 | >>> info files 34 | Symbols from "/home/me/a.out". 35 | Local exec file: 36 | `/home/me/a.out', file type elf64-x86-64. 37 | Entry point: 0x400440 38 | 0x0000000000400238 - 0x0000000000400254 is .interp 39 | 0x0000000000400254 - 0x0000000000400274 is .note.ABI-tag 40 | 0x0000000000400274 - 0x0000000000400298 is .note.gnu.build-id 41 | 0x0000000000400298 - 0x00000000004002b4 is .gnu.hash 42 | 0x00000000004002b8 - 0x0000000000400318 is .dynsym 43 | 0x0000000000400318 - 0x0000000000400355 is .dynstr 44 | 0x0000000000400356 - 0x000000000040035e is .gnu.version 45 | 0x0000000000400360 - 0x0000000000400380 is .gnu.version_r 46 | 0x0000000000400380 - 0x0000000000400398 is .rela.dyn 47 | 0x0000000000400398 - 0x00000000004003e0 is .rela.plt 48 | 0x00000000004003e0 - 0x00000000004003fa is .init 49 | 0x0000000000400400 - 0x0000000000400440 is .plt 50 | 0x0000000000400440 - 0x00000000004005c2 is .text 51 | 0x00000000004005c4 - 0x00000000004005cd is .fini 52 | 0x00000000004005d0 - 0x00000000004005e0 is .rodata 53 | 0x00000000004005e0 - 0x0000000000400614 is .eh_frame_hdr 54 | 0x0000000000400618 - 0x000000000040070c is .eh_frame 55 | 0x0000000000600e10 - 0x0000000000600e18 is .init_array 56 | 0x0000000000600e18 - 0x0000000000600e20 is .fini_array 57 | 0x0000000000600e20 - 0x0000000000600e28 is .jcr 58 | 0x0000000000600e28 - 0x0000000000600ff8 is .dynamic 59 | 0x0000000000600ff8 - 0x0000000000601000 is .got 60 | 0x0000000000601000 - 0x0000000000601030 is .got.plt 61 | 0x0000000000601030 - 0x0000000000601040 is .data 62 | 0x0000000000601040 - 0x0000000000601048 is .bss 63 | 64 | ## 技巧 65 | 66 | 当调试没有调试信息的程序时,直接运行`start`命令是没有效果的: 67 | 68 | (gdb) start 69 | Function "main" not defined. 70 | 71 | 如果不知道main在何处,那么可以在程序入口处打断点。先通过`readelf`或者进入gdb,执行`info files`获得入口地址,然后: 72 | 73 | (gdb) b *0x400440 74 | (gdb) r 75 | 76 | ## 贡献者 77 | 78 | * xmj 79 | * [weekface](https://github.com/weekface) 80 | -------------------------------------------------------------------------------- /src/break-on-first-assembly-code.md: -------------------------------------------------------------------------------- 1 | # 在函数的第一条汇编指令打断点 2 | 3 | ## 例子 4 | 5 | #include 6 | int global_var; 7 | 8 | void change_var(){ 9 | global_var=100; 10 | } 11 | 12 | int main(void){ 13 | change_var(); 14 | return 0; 15 | } 16 | 17 | 18 | ## 技巧 19 | 20 | 通常给函数打断点的命令:“b func”(b是break命令的缩写),不会把断点设置在汇编指令层次函数的开头,例如: 21 | 22 | (gdb) b main 23 | Breakpoint 1 at 0x8050c12: file a.c, line 9. 24 | (gdb) r 25 | Starting program: /data1/nan/a 26 | [Thread debugging using libthread_db enabled] 27 | [New Thread 1 (LWP 1)] 28 | [Switching to Thread 1 (LWP 1)] 29 | 30 | Breakpoint 1, main () at a.c:9 31 | 9 change_var(); 32 | (gdb) disassemble 33 | Dump of assembler code for function main: 34 | 0x08050c0f <+0>: push %ebp 35 | 0x08050c10 <+1>: mov %esp,%ebp 36 | => 0x08050c12 <+3>: call 0x8050c00 37 | 0x08050c17 <+8>: mov $0x0,%eax 38 | 0x08050c1c <+13>: pop %ebp 39 | 0x08050c1d <+14>: ret 40 | End of assembler dump. 41 | 42 | 43 | 44 | 45 | 可以看到程序停在了第三条汇编指令(箭头所指位置)。如果要把断点设置在汇编指令层次函数的开头,要使用如下命令:“b *func”,例如: 46 | 47 | (gdb) b *main 48 | Breakpoint 1 at 0x8050c0f: file a.c, line 8. 49 | (gdb) r 50 | Starting program: /data1/nan/a 51 | [Thread debugging using libthread_db enabled] 52 | [New Thread 1 (LWP 1)] 53 | [Switching to Thread 1 (LWP 1)] 54 | 55 | Breakpoint 1, main () at a.c:8 56 | 8 int main(void){ 57 | (gdb) disassemble 58 | Dump of assembler code for function main: 59 | => 0x08050c0f <+0>: push %ebp 60 | 0x08050c10 <+1>: mov %esp,%ebp 61 | 0x08050c12 <+3>: call 0x8050c00 62 | 0x08050c17 <+8>: mov $0x0,%eax 63 | 0x08050c1c <+13>: pop %ebp 64 | 0x08050c1d <+14>: ret 65 | End of assembler dump. 66 | 67 | 可以看到程序停在了第一条汇编指令(箭头所指位置)。 68 | 69 | 70 | ## 贡献者 71 | 72 | nanxiao 73 | 74 | -------------------------------------------------------------------------------- /src/break-on-linenum.md: -------------------------------------------------------------------------------- 1 | # 在文件行号上打断点 2 | 3 | ## 例子 4 | 5 | /* a/file.c */ 6 | #include 7 | 8 | void print_a (void) 9 | { 10 | puts ("a"); 11 | } 12 | 13 | /* b/file.c */ 14 | #include 15 | 16 | void print_b (void) 17 | { 18 | puts ("b"); 19 | } 20 | 21 | /* main.c */ 22 | extern void print_a(void); 23 | extern void print_b(void); 24 | 25 | int main(void) 26 | { 27 | print_a(); 28 | print_b(); 29 | return 0; 30 | } 31 | 32 | ## 技巧 33 | 34 | 这个比较简单,如果要在当前文件中的某一行打断点,直接`b linenum`即可,例如: 35 | 36 | (gdb) b 7 37 | 38 | 也可以显式指定文件,`b file:linenum`例如: 39 | 40 | (gdb) b file.c:6 41 | Breakpoint 1 at 0x40053b: file.c:6. (2 locations) 42 | (gdb) i breakpoints 43 | Num Type Disp Enb Address What 44 | 1 breakpoint keep y 45 | 1.1 y 0x000000000040053b in print_a at a/file.c:6 46 | 1.2 y 0x000000000040054b in print_b at b/file.c:6 47 | 48 | 可以看出,gdb会对所有匹配的文件设置断点。你可以通过指定(部分)路径,来区分相同的文件名: 49 | 50 | (gdb) b a/file.c:6 51 | 52 | 注意:通过行号进行设置断点的一个弊端是,如果你更改了源程序,那么之前设置的断点就可能不是你想要的了。 53 | 54 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Specify-Location.html#Specify-Location) 55 | 56 | ## 贡献者 57 | 58 | xmj 59 | 60 | -------------------------------------------------------------------------------- /src/breakpoint-command.md: -------------------------------------------------------------------------------- 1 | # 使用断点命令改变程序的执行 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | 8 | void drawing (int n) 9 | { 10 | if (n != 0) 11 | puts ("Try again?\nAll you need is a dollar, and a dream."); 12 | else 13 | puts ("You win $3000!"); 14 | } 15 | 16 | int main (void) 17 | { 18 | int n; 19 | 20 | srand (time (0)); 21 | n = rand () % 10; 22 | printf ("Your number is %d\n", n); 23 | drawing (n); 24 | 25 | return 0; 26 | } 27 | 28 | ## 技巧 29 | 30 | 这个例子程序可能不太好,只是可以用来演示下断点命令的用法: 31 | 32 | (gdb) b drawing 33 | Breakpoint 1 at 0x40064d: file win.c, line 6. 34 | (gdb) command 1 35 | Type commands for breakpoint(s) 1, one per line. 36 | End with a line saying just "end". 37 | >silent 38 | >set variable n = 0 39 | >continue 40 | >end 41 | (gdb) r 42 | Starting program: /home/xmj/tmp/a.out 43 | [Thread debugging using libthread_db enabled] 44 | Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". 45 | Your number is 6 46 | You win $3000! 47 | [Inferior 1 (process 4134) exited normally] 48 | 49 | 可以看到,当程序运行到断点处,会自动把变量n的值修改为0,然后继续执行。 50 | 51 | 如果你在调试一个大程序,重新编译一次会花费很长时间,比如调试编译器的bug,那么你可以用这种方式在gdb中先实验性的修改下试试,而不需要修改源码,重新编译。 52 | 53 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Break-Commands.html#Break-Commands) 54 | 55 | ## 贡献者 56 | 57 | xmj 58 | 59 | -------------------------------------------------------------------------------- /src/call-func.md: -------------------------------------------------------------------------------- 1 | # 直接执行函数 2 | ## 例子 3 | #include 4 | 5 | int global = 1; 6 | 7 | int func(void) 8 | { 9 | return (++global); 10 | } 11 | 12 | int main(void) 13 | { 14 | printf("%d\n", global); 15 | return 0; 16 | } 17 | 18 | 19 | 20 | ## 技巧 21 | 使用gdb调试程序时,可以使用“`call`”或“`print`”命令直接调用函数执行。以上面程序为例: 22 | 23 | (gdb) start 24 | Temporary breakpoint 1 at 0x4004e3: file a.c, line 12. 25 | Starting program: /data2/home/nanxiao/a 26 | 27 | Temporary breakpoint 1, main () at a.c:12 28 | 12 printf("%d\n", global); 29 | (gdb) call func() 30 | $1 = 2 31 | (gdb) print func() 32 | $2 = 3 33 | (gdb) n 34 | 3 35 | 13 return 0; 36 | 37 | 可以看到执行两次`func`函数后,`global`的值变成`3`。 38 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Calling.html). 39 | 40 | ## 贡献者 41 | 42 | nanxiao 43 | -------------------------------------------------------------------------------- /src/catch-exec.md: -------------------------------------------------------------------------------- 1 | # 为exec调用设置catchpoint 2 | ## 例子 3 | #include 4 | 5 | int main(void) { 6 | execl("/bin/ls", "ls", NULL); 7 | return 0; 8 | } 9 | 10 | 11 | 12 | ## 技巧 13 | 使用gdb调试程序时,可以用“`catch exec`”命令为`exec`系列系统调用设置`catchpoint`,以上面程序为例: 14 | 15 | (gdb) catch exec 16 | Catchpoint 1 (exec) 17 | (gdb) r 18 | Starting program: /home/nan/a 19 | process 32927 is executing new program: /bin/ls 20 | 21 | Catchpoint 1 (exec'd /bin/ls), 0x00000034e3a00b00 in _start () from /lib64/ld-linux-x86-64.so.2 22 | (gdb) bt 23 | #0 0x00000034e3a00b00 in _start () from /lib64/ld-linux-x86-64.so.2 24 | #1 0x0000000000000001 in ?? () 25 | #2 0x00007fffffffe73d in ?? () 26 | #3 0x0000000000000000 in ?? () 27 | 28 | 29 | 可以看到当`execl`调用发生后,gdb会暂停程序的运行。 30 | 注意:目前只有HP-UX和GNU/Linux支持这个功能。 31 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.html). 32 | 33 | ## 贡献者 34 | 35 | nanxiao 36 | -------------------------------------------------------------------------------- /src/catch-fork.md: -------------------------------------------------------------------------------- 1 | # 为fork调用设置catchpoint 2 | ## 例子 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | pid_t pid; 10 | 11 | pid = fork(); 12 | if (pid < 0) 13 | { 14 | exit(1); 15 | } 16 | else if (pid > 0) 17 | { 18 | exit(0); 19 | } 20 | printf("hello world\n"); 21 | return 0; 22 | } 23 | 24 | 25 | 26 | ## 技巧 27 | 使用gdb调试程序时,可以用“`catch fork`”命令为`fork`调用设置`catchpoint`,以上面程序为例: 28 | 29 | (gdb) catch fork 30 | Catchpoint 1 (fork) 31 | (gdb) r 32 | Starting program: /home/nan/a 33 | 34 | Catchpoint 1 (forked process 33499), 0x00000034e42acdbd in fork () from /lib64/libc.so.6 35 | (gdb) bt 36 | #0 0x00000034e42acdbd in fork () from /lib64/libc.so.6 37 | #1 0x0000000000400561 in main () at a.c:9 38 | 可以看到当`fork`调用发生后,gdb会暂停程序的运行。 39 | 注意:目前只有HP-UX和GNU/Linux支持这个功能。 40 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.html). 41 | 42 | ## 贡献者 43 | 44 | nanxiao 45 | -------------------------------------------------------------------------------- /src/catch-ptrace.md: -------------------------------------------------------------------------------- 1 | # 通过为ptrace调用设置catchpoint破解anti-debugging的程序 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0 ) { 9 | printf("Gdb is debugging me, exit.\n"); 10 | return 1; 11 | } 12 | printf("No debugger, continuing\n"); 13 | return 0; 14 | } 15 | 16 | 17 | 18 | ## 技巧 19 | 有些程序不想被gdb调试,它们就会在程序中调用“`ptrace`”函数,一旦返回失败,就证明程序正在被gdb等类似的程序追踪,所以就直接退出。以上面程序为例: 20 | 21 | (gdb) start 22 | Temporary breakpoint 1 at 0x400508: file a.c, line 6. 23 | Starting program: /data2/home/nanxiao/a 24 | 25 | Temporary breakpoint 1, main () at a.c:6 26 | 6 if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0 ) { 27 | (gdb) n 28 | 7 printf("Gdb is debugging me, exit.\n"); 29 | (gdb) 30 | Gdb is debugging me, exit. 31 | 8 return 1; 32 | 33 | 34 | 35 | 破解这类程序的办法就是为`ptrace`调用设置`catchpoint`,通过修改`ptrace`的返回值,达到目的。仍以上面程序为例: 36 | 37 | (gdb) catch syscall ptrace 38 | Catchpoint 2 (syscall 'ptrace' [101]) 39 | (gdb) r 40 | Starting program: /data2/home/nanxiao/a 41 | 42 | Catchpoint 2 (call to syscall ptrace), 0x00007ffff7b2be9c in ptrace () from /lib64/libc.so.6 43 | (gdb) c 44 | Continuing. 45 | 46 | Catchpoint 2 (returned from syscall ptrace), 0x00007ffff7b2be9c in ptrace () from /lib64/libc.so.6 47 | (gdb) set $rax = 0 48 | (gdb) c 49 | Continuing. 50 | No debugger, continuing 51 | [Inferior 1 (process 11491) exited normally] 52 | 53 | 可以看到,通过修改`rax`寄存器的值,达到修改返回值的目的,从而让gdb可以继续调试程序(打印“`No debugger, continuing`”)。 54 | 详细过程,可以参见这篇文章[避開 PTRACE_TRACME 反追蹤技巧](http://blog.linux.org.tw/~jserv/archives/2011_08.html). 55 | 56 | ## 贡献者 57 | 58 | nanxiao 59 | -------------------------------------------------------------------------------- /src/catch-syscall.md: -------------------------------------------------------------------------------- 1 | # 为系统调用设置catchpoint 2 | ## 例子 3 | #include 4 | 5 | int main(void) 6 | { 7 | char p1[] = "Sam"; 8 | char *p2 = "Bob"; 9 | 10 | printf("p1 is %s, p2 is %s\n", p1, p2); 11 | return 0; 12 | } 13 | 14 | 15 | 16 | ## 技巧 17 | 使用gdb调试程序时,可以使用`catch syscall [name | number]`为关注的系统调用设置`catchpoint`,以上面程序为例: 18 | 19 | (gdb) catch syscall mmap 20 | Catchpoint 1 (syscall 'mmap' [9]) 21 | (gdb) r 22 | Starting program: /home/nan/a 23 | 24 | Catchpoint 1 (call to syscall mmap), 0x00000034e3a16f7a in mmap64 () 25 | from /lib64/ld-linux-x86-64.so.2 26 | (gdb) c 27 | Continuing. 28 | 29 | Catchpoint 1 (returned from syscall mmap), 0x00000034e3a16f7a in mmap64 () 30 | from /lib64/ld-linux-x86-64.so.2 31 | 32 | 33 | 可以看到当`mmap`调用发生后,gdb会暂停程序的运行。 34 | 也可以使用系统调用的编号设置`catchpoint`,仍以上面程序为例: 35 | 36 | (gdb) catch syscall 9 37 | Catchpoint 1 (syscall 'mmap' [9]) 38 | (gdb) r 39 | Starting program: /home/nan/a 40 | 41 | Catchpoint 1 (call to syscall mmap), 0x00000034e3a16f7a in mmap64 () 42 | from /lib64/ld-linux-x86-64.so.2 43 | (gdb) c 44 | Continuing. 45 | 46 | Catchpoint 1 (returned from syscall mmap), 0x00000034e3a16f7a in mmap64 () 47 | from /lib64/ld-linux-x86-64.so.2 48 | (gdb) c 49 | Continuing. 50 | 51 | Catchpoint 1 (call to syscall mmap), 0x00000034e3a16f7a in mmap64 () 52 | from /lib64/ld-linux-x86-64.so.2 53 | 可以看到和使用`catch syscall mmap`效果是一样的。(系统调用和编号的映射参考具体的`xml`文件,以我的系统为例,就是在`/usr/local/share/gdb/syscalls`文件夹下的`amd64-linux.xml`。) 54 | 55 | 如果不指定具体的系统调用,则会为所有的系统调用设置`catchpoint`,仍以上面程序为例: 56 | 57 | (gdb) catch syscall 58 | Catchpoint 1 (any syscall) 59 | (gdb) r 60 | Starting program: /home/nan/a 61 | 62 | Catchpoint 1 (call to syscall brk), 0x00000034e3a1618a in brk () 63 | from /lib64/ld-linux-x86-64.so.2 64 | (gdb) c 65 | Continuing. 66 | 67 | Catchpoint 1 (returned from syscall brk), 0x00000034e3a1618a in brk () 68 | from /lib64/ld-linux-x86-64.so.2 69 | (gdb) 70 | Continuing. 71 | 72 | Catchpoint 1 (call to syscall mmap), 0x00000034e3a16f7a in mmap64 () 73 | from /lib64/ld-linux-x86-64.so.2 74 | 75 | 76 | 77 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.html). 78 | 79 | ## 贡献者 80 | 81 | nanxiao 82 | -------------------------------------------------------------------------------- /src/catch-vfork.md: -------------------------------------------------------------------------------- 1 | # 为vfork调用设置catchpoint 2 | ## 例子 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | pid_t pid; 10 | 11 | pid = vfork(); 12 | if (pid < 0) 13 | { 14 | exit(1); 15 | } 16 | else if (pid > 0) 17 | { 18 | exit(0); 19 | } 20 | printf("hello world\n"); 21 | return 0; 22 | } 23 | 24 | 25 | 26 | ## 技巧 27 | 使用gdb调试程序时,可以用“`catch vfork`”命令为`vfork`调用设置`catchpoint`,以上面程序为例: 28 | 29 | (gdb) catch vfork 30 | Catchpoint 1 (vfork) 31 | (gdb) r 32 | Starting program: /home/nan/a 33 | 34 | Catchpoint 1 (vforked process 27312), 0x00000034e42acfc4 in vfork () 35 | from /lib64/libc.so.6 36 | (gdb) bt 37 | #0 0x00000034e42acfc4 in vfork () from /lib64/libc.so.6 38 | #1 0x0000000000400561 in main () at a.c:9 39 | 40 | 可以看到当`vfork`调用发生后,gdb会暂停程序的运行。 41 | 注意:目前只有HP-UX和GNU/Linux支持这个功能。 42 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.html). 43 | 44 | ## 贡献者 45 | 46 | nanxiao 47 | -------------------------------------------------------------------------------- /src/change-string.md: -------------------------------------------------------------------------------- 1 | # 改变字符串的值 2 | ## 例子 3 | #include 4 | 5 | int main(void) 6 | { 7 | char p1[] = "Sam"; 8 | char *p2 = "Bob"; 9 | 10 | printf("p1 is %s, p2 is %s\n", p1, p2); 11 | return 0; 12 | } 13 | 14 | 15 | 16 | ## 技巧 17 | 使用gdb调试程序时,可以用“`set`”命令改变字符串的值,以上面程序为例: 18 | 19 | (gdb) start 20 | Temporary breakpoint 1 at 0x8050af0: file a.c, line 5. 21 | Starting program: /data1/nan/a 22 | [Thread debugging using libthread_db enabled] 23 | [New Thread 1 (LWP 1)] 24 | [Switching to Thread 1 (LWP 1)] 25 | 26 | Temporary breakpoint 1, main () at a.c:5 27 | 5 char p1[] = "Sam"; 28 | (gdb) n 29 | 6 char *p2 = "Bob"; 30 | (gdb) 31 | 8 printf("p1 is %s, p2 is %s\n", p1, p2); 32 | (gdb) set main::p1="Jil" 33 | (gdb) set main::p2="Bill" 34 | (gdb) n 35 | p1 is Jil, p2 is Bill 36 | 9 return 0; 37 | 可以看到执行`p1`和`p2`的字符串都发生了变化。也可以通过访问内存地址的方法改变字符串的值: 38 | 39 | Starting program: /data1/nan/a 40 | [Thread debugging using libthread_db enabled] 41 | [New Thread 1 (LWP 1)] 42 | [Switching to Thread 1 (LWP 1)] 43 | 44 | Temporary breakpoint 2, main () at a.c:5 45 | 5 char p1[] = "Sam"; 46 | (gdb) n 47 | 6 char *p2 = "Bob"; 48 | (gdb) p p1 49 | $1 = "Sam" 50 | (gdb) p &p1 51 | $2 = (char (*)[4]) 0x80477a4 52 | (gdb) set {char [4]} 0x80477a4 = "Ace" 53 | (gdb) n 54 | 8 printf("p1 is %s, p2 is %s\n", p1, p2); 55 | (gdb) 56 | p1 is Ace, p2 is Bob 57 | 9 return 0; 58 | 59 | 在改变字符串的值时候,一定要注意内存越界的问题。 60 | 参见[stackoverflow](http://stackoverflow.com/questions/19503057/in-gdb-how-can-i-write-a-string-to-memory). 61 | 62 | ## 贡献者 63 | 64 | nanxiao 65 | -------------------------------------------------------------------------------- /src/config-gdbinit.md: -------------------------------------------------------------------------------- 1 | # 配置gdb init文件 2 | 3 | ## 技巧 4 | 5 | 当gdb启动时,会读取HOME目录和当前目录下的的配置文件,执行里面的命令。这个文件通常为“.gdbinit”。 6 | 7 | 这里给出了本文档中介绍过的,可以放在“.gdbinit”中的一些配置: 8 | 9 | # 打印STL容器中的内容 10 | python 11 | import sys 12 | sys.path.insert(0, "/home/xmj/project/gcc-trunk/libstdc++-v3/python") 13 | from libstdcxx.v6.printers import register_libstdcxx_printers 14 | register_libstdcxx_printers (None) 15 | end 16 | 17 | # 保存历史命令 18 | set history filename ~/.gdb_history 19 | set history save on 20 | 21 | # 退出时不显示提示信息 22 | set confirm off 23 | 24 | # 按照派生类型打印对象 25 | set print object on 26 | 27 | # 打印数组的索引下标 28 | set print array-indexes on 29 | 30 | # 每行打印一个结构体成员 31 | set print pretty on 32 | 33 | 欢迎补充。 34 | 35 | ## 贡献者 36 | 37 | xmj 38 | 39 | -------------------------------------------------------------------------------- /src/directory.md: -------------------------------------------------------------------------------- 1 | # 设置源文件查找路径 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | time_t now = time(NULL); 8 | struct tm local = {0}; 9 | struct tm gmt = {0}; 10 | 11 | localtime_r(&now, &local); 12 | gmtime_r(&now, &gmt); 13 | 14 | return 0; 15 | } 16 | 17 | 18 | 19 | 20 | ## 技巧 21 | 有时gdb不能准确地定位到源文件的位置(比如文件被移走了,等等),此时可以用`directory`命令设置查找源文件的路径。以上面程序为例: 22 | 23 | (gdb) start 24 | Temporary breakpoint 1 at 0x400560: file a.c, line 5. 25 | Starting program: /home/nan/a 26 | 27 | Temporary breakpoint 1, main () at a.c:5 28 | 5 a.c: No such file or directory. 29 | (gdb) directory ../ki/ 30 | Source directories searched: /home/nan/../ki:$cdir:$cwd 31 | (gdb) n 32 | 6 struct tm local = {0}; 33 | (gdb) 34 | 7 struct tm gmt = {0}; 35 | (gdb) 36 | 9 localtime_r(&now, &local); 37 | (gdb) 38 | 10 gmtime_r(&now, &gmt); 39 | (gdb) q 40 | 41 | 可以看到,使用`directory`(或`dir`)命令设置源文件的查找目录后,gdb就可以正常地解析源代码了。 42 | 43 | 如果希望在gdb启动时,加载code的位置,避免每次在gdb中再次输入命令,可以使用gdb的`-d` 参数 44 | ```shell 45 | gdb -q a.out -d /search/code/some 46 | ``` 47 | 48 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Source-Path.html). 49 | 50 | ## 贡献者 51 | 52 | nanxiao 53 | -------------------------------------------------------------------------------- /src/disassemble-next-line.md: -------------------------------------------------------------------------------- 1 | # 自动反汇编后面要执行的代码 2 | 3 | ## 例子 4 | (gdb) set disassemble-next-line on 5 | (gdb) start 6 | The program being debugged has been started already. 7 | Start it from the beginning? (y or n) y 8 | Temporary breakpoint 3 at 0x400543: file 1.c, line 14. 9 | Starting program: /home/teawater/tmp/a.out 10 | 11 | Temporary breakpoint 3, main (argc=1, argv=0x7fffffffdf38, envp=0x7fffffffdf48) at 1.c:14 12 | 14 printf("1\n"); 13 | => 0x0000000000400543 : bf f0 05 40 00 mov $0x4005f0,%edi 14 | 0x0000000000400548 : e8 c3 fe ff ff callq 0x400410 15 | (gdb) si 16 | 0x0000000000400548 14 printf("1\n"); 17 | 0x0000000000400543 : bf f0 05 40 00 mov $0x4005f0,%edi 18 | => 0x0000000000400548 : e8 c3 fe ff ff callq 0x400410 19 | (gdb) 20 | 0x0000000000400410 in puts@plt () 21 | => 0x0000000000400410 : ff 25 02 0c 20 00 jmpq *0x200c02(%rip) # 0x601018 22 | 23 | (gdb) set disassemble-next-line auto 24 | (gdb) start 25 | Temporary breakpoint 1 at 0x400543: file 1.c, line 14. 26 | Starting program: /home/teawater/tmp/a.out 27 | 28 | Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdf38, envp=0x7fffffffdf48) at 1.c:14 29 | 14 printf("1\n"); 30 | (gdb) si 31 | 0x0000000000400548 14 printf("1\n"); 32 | (gdb) 33 | 0x0000000000400410 in puts@plt () 34 | => 0x0000000000400410 : ff 25 02 0c 20 00 jmpq *0x200c02(%rip) # 0x601018 35 | (gdb) 36 | 0x0000000000400416 in puts@plt () 37 | => 0x0000000000400416 : 68 00 00 00 00 pushq $0x0 38 | 39 | ## 技巧 40 | 41 | 如果要在任意情况下反汇编后面要执行的代码: 42 | 43 | (gdb) set disassemble-next-line on 44 | 45 | 如果要在后面的代码没有源码的情况下才反汇编后面要执行的代码: 46 | 47 | (gdb) set disassemble-next-line auto 48 | 49 | 关闭这个功能: 50 | 51 | (gdb) set disassemble-next-line off 52 | 53 | ## 贡献者 54 | 55 | teawater -------------------------------------------------------------------------------- /src/disassemble-raw-machine-code.md: -------------------------------------------------------------------------------- 1 | # 显示程序原始机器码 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int main(void) 8 | { 9 | printf("Hello, world\n"); 10 | return 0; 11 | } 12 | 13 | 14 | 15 | ## 技巧 16 | 17 | 使用“disassemble /r”命令可以用16进制形式显示程序的原始机器码。以上面程序为例: 18 | 19 | (gdb) disassemble /r main 20 | Dump of assembler code for function main: 21 | 0x0000000000400530 <+0>: 55 push %rbp 22 | 0x0000000000400531 <+1>: 48 89 e5 mov %rsp,%rbp 23 | 0x0000000000400534 <+4>: bf e0 05 40 00 mov $0x4005e0,%edi 24 | 0x0000000000400539 <+9>: e8 d2 fe ff ff callq 0x400410 25 | 0x000000000040053e <+14>: b8 00 00 00 00 mov $0x0,%eax 26 | 0x0000000000400543 <+19>: 5d pop %rbp 27 | 0x0000000000400544 <+20>: c3 retq 28 | End of assembler dump. 29 | (gdb) disassemble /r 0x0000000000400534,+4 30 | Dump of assembler code from 0x400534 to 0x400538: 31 | 0x0000000000400534 : bf e0 05 40 00 mov $0x4005e0,%edi 32 | End of assembler dump. 33 | 34 | 35 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Machine-Code.html) 36 | 37 | ## 贡献者 38 | 39 | nanxiao 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/display-instruction-pc.md: -------------------------------------------------------------------------------- 1 | # 显示将要执行的汇编指令 2 | 3 | ## 例子 4 | 5 | #include 6 | int global_var; 7 | 8 | void change_var(){ 9 | global_var=100; 10 | } 11 | 12 | int main(void){ 13 | change_var(); 14 | return 0; 15 | } 16 | 17 | 18 | ## 技巧 19 | 20 | 使用gdb调试汇编程序时,可以用“`display /i $pc`”命令显示当程序停止时,将要执行的汇编指令。以上面程序为例: 21 | 22 | (gdb) start 23 | Temporary breakpoint 1 at 0x400488: file a.c, line 9. 24 | Starting program: /data2/home/nanxiao/a 25 | 26 | Temporary breakpoint 1, main () at a.c:9 27 | 9 change_var(); 28 | (gdb) display /i $pc 29 | 1: x/i $pc 30 | => 0x400488 : mov $0x0,%eax 31 | (gdb) si 32 | 0x000000000040048d 9 change_var(); 33 | 1: x/i $pc 34 | => 0x40048d : callq 0x400474 35 | (gdb) 36 | change_var () at a.c:4 37 | 4 void change_var(){ 38 | 1: x/i $pc 39 | => 0x400474 : push %rbp 40 | 41 | 可以看到打印出了将要执行的汇编指令。此外也可以一次显示多条指令: 42 | 43 | (gdb) display /3i $pc 44 | 2: x/3i $pc 45 | => 0x400474 : push %rbp 46 | 0x400475 : mov %rsp,%rbp 47 | 0x400478 : movl $0x64,0x2003de(%rip) # 0x600860 48 | 可以看到一次显示了`3`条指令。 49 | 50 | 取消显示可以用`undisplay`命令。 51 | 52 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Auto-Display.html) 53 | 54 | ## 贡献者 55 | 56 | nanxiao 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/examine-memory.md: -------------------------------------------------------------------------------- 1 | # 打印内存的值 2 | ## 例子 3 | #include 4 | 5 | int main(void) 6 | { 7 | int i = 0; 8 | char a[100]; 9 | 10 | for (i = 0; i < sizeof(a); i++) 11 | { 12 | a[i] = i; 13 | } 14 | 15 | return 0; 16 | } 17 | 18 | 19 | ## 技巧 20 | gdb中使用“`x`”命令来打印内存的值,格式为“`x/nfu addr`”。含义为以`f`格式打印从`addr`开始的`n`个长度单元为`u`的内存值。参数具体含义如下: 21 | a)n:输出单元的个数。 22 | b)f:是输出格式。比如`x`是以16进制形式输出,`o`是以8进制形式输出,等等。 23 | c)u:标明一个单元的长度。`b`是一个`byte`,`h`是两个`byte`(halfword),`w`是四个`byte`(word),`g`是八个`byte`(giant word)。 24 | 25 | 以上面程序为例: 26 | (1) 以16进制格式打印数组前`a`16个byte的值: 27 | 28 | (gdb) x/16xb a 29 | 0x7fffffffe4a0: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 30 | 0x7fffffffe4a8: 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 31 | (2) 以无符号10进制格式打印数组`a`前16个byte的值: 32 | 33 | (gdb) x/16ub a 34 | 0x7fffffffe4a0: 0 1 2 3 4 5 6 7 35 | 0x7fffffffe4a8: 8 9 10 11 12 13 14 15 36 | (3) 以2进制格式打印数组前16个`a`byte的值: 37 | 38 | (gdb) x/16tb a 39 | 0x7fffffffe4a0: 00000000 00000001 00000010 00000011 00000100 00000101 00000110 00000111 40 | 0x7fffffffe4a8: 00001000 00001001 00001010 00001011 00001100 00001101 00001110 00001111 41 | (4) 以16进制格式打印数组`a`前16个word(4个byte)的值: 42 | 43 | (gdb) x/16xw a 44 | 0x7fffffffe4a0: 0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c 45 | 0x7fffffffe4b0: 0x13121110 0x17161514 0x1b1a1918 0x1f1e1d1c 46 | 0x7fffffffe4c0: 0x23222120 0x27262524 0x2b2a2928 0x2f2e2d2c 47 | 0x7fffffffe4d0: 0x33323130 0x37363534 0x3b3a3938 0x3f3e3d3c 48 | 49 | 50 | 51 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Memory.html). 52 | 53 | ## 贡献者 54 | 55 | nanxiao 56 | -------------------------------------------------------------------------------- /src/finish-and-return.md: -------------------------------------------------------------------------------- 1 | # 退出正在调试的函数 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int func(void) 8 | { 9 | int i = 0; 10 | 11 | i += 2; 12 | i *= 10; 13 | 14 | return i; 15 | } 16 | 17 | int main(void) 18 | { 19 | int a = 0; 20 | 21 | a = func(); 22 | printf("%d\n", a); 23 | return 0; 24 | } 25 | 26 | 27 | 28 | ## 技巧 29 | 30 | 当单步调试一个函数时,如果不想继续跟踪下去了,可以有两种方式退出。 31 | 32 | 第一种用“`finish`”命令,这样函数会继续执行完,并且打印返回值,然后等待输入接下来的命令。以上面代码为例: 33 | 34 | (gdb) n 35 | 17 a = func(); 36 | (gdb) s 37 | func () at a.c:5 38 | 5 int i = 0; 39 | (gdb) n 40 | 7 i += 2; 41 | (gdb) fin 42 | find finish 43 | (gdb) finish 44 | Run till exit from #0 func () at a.c:7 45 | 0x08050978 in main () at a.c:17 46 | 17 a = func(); 47 | Value returned is $1 = 20 48 | 49 | 50 | 可以看到当不想再继续跟踪`func`函数时,执行完“`finish`”命令,gdb会打印结果:“`20`”,然后停在那里。 51 | 52 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Continuing-and-Stepping.html) 53 | 54 | 第二种用“`return`”命令,这样函数不会继续执行下面的语句,而是直接返回。也可以用“`return expression`”命令指定函数的返回值。仍以上面代码为例: 55 | 56 | (gdb) n 57 | 17 a = func(); 58 | (gdb) s 59 | func () at a.c:5 60 | 5 int i = 0; 61 | (gdb) n 62 | 7 i += 2; 63 | (gdb) n 64 | 8 i *= 10; 65 | (gdb) re 66 | record remove-inferiors return reverse-next reverse-step 67 | refresh remove-symbol-file reverse-continue reverse-nexti reverse-stepi 68 | remote restore reverse-finish reverse-search 69 | (gdb) return 40 70 | Make func return now? (y or n) y 71 | #0 0x08050978 in main () at a.c:17 72 | 17 a = func(); 73 | (gdb) n 74 | 18 printf("%d\n", a); 75 | (gdb) 76 | 40 77 | 19 return 0; 78 | 79 | 80 | 81 | 可以看到“`return`”命令退出了函数并且修改了函数的返回值。 82 | 83 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Returning.html#Returning) 84 | 85 | ## 贡献者 86 | 87 | nanxiao 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/generate-core-dump-file.md: -------------------------------------------------------------------------------- 1 | # 为调试进程产生core dump文件 2 | 3 | 4 | # 技巧 5 | 在用gdb调试程序时,我们有时想让被调试的进程产生core dump文件,记录现在进程的状态,以供以后分析。可以用“generate-core-file”命令来产生core dump文件: 6 | 7 | (gdb) help generate-core-file 8 | Save a core file with the current state of the debugged process. 9 | Argument is optional filename. Default filename is 'core.'. 10 | 11 | (gdb) start 12 | Temporary breakpoint 1 at 0x8050c12: file a.c, line 9. 13 | Starting program: /data1/nan/a 14 | [Thread debugging using libthread_db enabled] 15 | [New Thread 1 (LWP 1)] 16 | [Switching to Thread 1 (LWP 1)] 17 | 18 | Temporary breakpoint 1, main () at a.c:9 19 | 9 change_var(); 20 | (gdb) generate-core-file 21 | Saved corefile core.12955 22 | 23 | 也可使用“gcore”命令: 24 | 25 | (gdb) help gcore 26 | Save a core file with the current state of the debugged process. 27 | Argument is optional filename. Default filename is 'core.'. 28 | (gdb) gcore 29 | Saved corefile core.13256 30 | 31 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Core-File-Generation.html) 32 | 33 | ## 贡献者 34 | 35 | nanxiao 36 | 37 | -------------------------------------------------------------------------------- /src/help.md: -------------------------------------------------------------------------------- 1 | # 得到命令的帮助信息 2 | 3 | ## 技巧 4 | 5 | 使用`help`命令可以得到gdb的命令帮助信息: 6 | 7 | (1)`help`命令不加任何参数会得到命令的分类: 8 | 9 | (gdb) help 10 | List of classes of commands: 11 | 12 | aliases -- Aliases of other commands 13 | breakpoints -- Making program stop at certain points 14 | data -- Examining data 15 | files -- Specifying and examining files 16 | internals -- Maintenance commands 17 | obscure -- Obscure features 18 | running -- Running the program 19 | stack -- Examining the stack 20 | status -- Status inquiries 21 | support -- Support facilities 22 | tracepoints -- Tracing of program execution without stopping the program 23 | user-defined -- User-defined commands 24 | 25 | Type "help" followed by a class name for a list of commands in that class. 26 | Type "help all" for the list of all commands. 27 | Type "help" followed by command name for full documentation. 28 | Type "apropos word" to search for commands related to "word". 29 | Command name abbreviations are allowed if unambiguous. 30 | (2)当输入`help class`命令时,可以得到这个类别下所有命令的列表和命令功能: 31 | 32 | (gdb) help data 33 | Examining data. 34 | 35 | List of commands: 36 | 37 | append -- Append target code/data to a local file 38 | append binary -- Append target code/data to a raw binary file 39 | append binary memory -- Append contents of memory to a raw binary file 40 | append binary value -- Append the value of an expression to a raw binary file 41 | append memory -- Append contents of memory to a raw binary file 42 | append value -- Append the value of an expression to a raw binary file 43 | call -- Call a function in the program 44 | disassemble -- Disassemble a specified section of memory 45 | display -- Print value of expression EXP each time the program stops 46 | dump -- Dump target code/data to a local file 47 | dump binary -- Write target code/data to a raw binary file 48 | dump binary memory -- Write contents of memory to a raw binary file 49 | dump binary value -- Write the value of an expression to a raw binary file 50 | ...... 51 | (3)也可以用`help command`命令得到某一个具体命令的用法: 52 | 53 | (gdb) help mem 54 | Define attributes for memory region or reset memory region handling totarget-based. 55 | Usage: mem auto 56 | mem [ ], 57 | where may be rw (read/write), ro (read-only) or wo (write-only), 58 | may be 8, 16, 32, or 64, and 59 | may be cache or nocache 60 | 61 | (4)用`apropos regexp`命令查找所有符合`regexp`正则表达式的命令信息: 62 | 63 | (gdb) apropos set 64 | awatch -- Set a watchpoint for an expression 65 | b -- Set breakpoint at specified line or function 66 | br -- Set breakpoint at specified line or function 67 | bre -- Set breakpoint at specified line or function 68 | brea -- Set breakpoint at specified line or function 69 | ...... 70 | 71 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Help.html) 72 | 73 | ## 贡献者 74 | 75 | nanxiao 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/ignore-break.md: -------------------------------------------------------------------------------- 1 | # 忽略断点 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int main(void) 8 | { 9 | int i = 0; 10 | int sum = 0; 11 | 12 | for (i = 1; i <= 200; i++) 13 | { 14 | sum += i; 15 | } 16 | 17 | printf("%d\n", sum); 18 | return 0; 19 | } 20 | 21 | 22 | 23 | ## 技巧 24 | 25 | 在设置断点以后,可以忽略断点,命令是“`ignore bnum count`”:意思是接下来`count`次编号为`bnum`的断点触发都不会让程序中断,只有第`count + 1`次断点触发才会让程序中断。以上面程序为例: 26 | 27 | (gdb) b 10 28 | Breakpoint 1 at 0x4004e3: file a.c, line 10. 29 | (gdb) ignore 1 5 30 | Will ignore next 5 crossings of breakpoint 1. 31 | (gdb) r 32 | Starting program: /data2/home/nanxiao/a 33 | 34 | Breakpoint 1, main () at a.c:10 35 | 10 sum += i; 36 | (gdb) p i 37 | $1 = 6 38 | 39 | 40 | 可以看到设定忽略断点前`5`次触发后,第一次断点断住时,打印`i`的值是`6`。如果想让断点下次就生效,可以将`count`置为`0`:“`ignore 1 0`”。 41 | 42 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Conditions.html) 43 | 44 | ## 贡献者 45 | 46 | nanxiao 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/index.md: -------------------------------------------------------------------------------- 1 | # 信息显示 2 | * [显示gdb版本信息](show-version.md) 3 | * [显示gdb版权相关信息](show-copying-warranty.md) 4 | * [启动时不显示提示信息](start-gdb-silently.md) 5 | * [退出时不显示提示信息](quit-gdb-silently.md) 6 | * [输出信息多时不会暂停输出](set-pagination-off.md) 7 | 8 | # 函数 9 | * [列出函数的名字](info-function.md) 10 | * [是否进入带调试信息的函数](step-and-next-function.md) 11 | * [进入不带调试信息的函数](set-step-mode-on.md) 12 | * [退出正在调试的函数](finish-and-return.md) 13 | * [直接执行函数](call-func.md) 14 | * [打印函数堆栈帧信息](info-frame.md) 15 | * [打印尾调用堆栈帧信息](set-debug-entry-values.md) 16 | * [选择函数堆栈帧](select-frame.md) 17 | * [向上或向下切换函数堆栈帧](up-down-select-frame.md) 18 | 19 | # 断点 20 | * [在匿名空间设置断点](break-anonymous-namespace.md) 21 | * [在程序地址上打断点](break-on-address.md) 22 | * [在程序入口处打断点](break-on-entry.md) 23 | * [在文件行号上打断点](break-on-linenum.md) 24 | * [保存已经设置的断点](save-breakpoints.md) 25 | * [设置临时断点](set-tbreak.md) 26 | * [设置条件断点](set-condition-break.md) 27 | * [忽略断点](ignore-break.md) 28 | 29 | # 观察点 30 | * [设置观察点](set-watchpoint.md) 31 | * [设置观察点只针对特定线程生效](set-watchpoint-on-specified-thread.md) 32 | * [设置读观察点](set-read-watchpoint.md) 33 | * [设置读写观察点](set-read-write-watchpoint.md) 34 | 35 | # Catchpoint 36 | * [让catchpoint只触发一次](tcatch.md) 37 | * [为fork调用设置catchpoint](catch-fork.md) 38 | * [为vfork调用设置catchpoint](catch-vfork.md) 39 | * [为exec调用设置catchpoint](catch-exec.md) 40 | * [为系统调用设置catchpoint](catch-syscall.md) 41 | * [通过为ptrace调用设置catchpoint破解anti-debugging的程序](catch-ptrace.md) 42 | 43 | # 打印 44 | * [打印ASCII和宽字符字符串](print-ascii-and-wide-string.md) 45 | * [打印STL容器中的内容](print-STL-container.md) 46 | * [打印大数组中的内容](print-large-array.md) 47 | * [打印数组中任意连续元素值](print-consecutive-array-elements.md) 48 | * [打印数组的索引下标](print-array-indexes.md) 49 | * [格式化打印数组](print-formatted-array.md) 50 | * [打印函数局部变量的值](print-local-variables.md) 51 | * [打印进程内存信息](print-process-memory.md) 52 | * [打印静态变量的值](print-static-variables.md) 53 | * [打印变量的类型和所在文件](print-variable-info.md) 54 | * [打印内存的值](examine-memory.md) 55 | * [打印源代码行](print-source-lines.md) 56 | * [每行打印一个结构体成员](set-print-pretty-on.md) 57 | * [按照派生类型打印对象](print-derived-type.md) 58 | * [指定程序的输入输出设备](set-io-tty.md) 59 | * [使用“$\\_”和“$\\__”变量](use-$_-$__-variables.md) 60 | * [打印程序动态分配内存的信息](print-malloc-memory.md) 61 | * [打印调用栈帧中变量的值](print-frame-variables.md) 62 | 63 | # 多进程/线程 64 | * [调试已经运行的进程](attach-process.md) 65 | * [调试子进程](set-follow-fork-mode-child.md) 66 | * [同时调试父进程和子进程](set-detach-on-fork.md) 67 | * [查看线程信息](print-threads.md) 68 | * [打印所有线程的堆栈信息](print-all-threads-bt.md) 69 | * [在Solaris上使用maintenance命令查看线程信息](maint-info-sol-threads.md) 70 | * [不显示线程启动和退出信息](show-print-thread-events.md) 71 | * [只允许一个线程运行](set-scheduler-locking-on.md) 72 | * [使用“$_thread”变量](use-$_thread-variable.md) 73 | * [一个gdb会话中同时调试多个程序](add-copy-inferiors.md) 74 | * [打印程序进程空间信息](maint-info-program-space.md) 75 | * [使用“$_exitcode”变量](use-$_exitcode.md) 76 | 77 | # core dump文件 78 | * [为调试进程产生core dump文件](generate-core-dump-file.md) 79 | * [加载可执行程序和core dump文件](load-executable-and-coredump-file.md) 80 | 81 | # 汇编 82 | * [设置汇编指令格式](set-disassembly-flavor.md) 83 | * [在函数的第一条汇编指令打断点](break-on-first-assembly-code.md) 84 | * [自动反汇编后面要执行的代码](disassemble-next-line.md) 85 | * [将源程序和汇编指令映射起来](map-source-code-and-assembly.md) 86 | * [显示将要执行的汇编指令](display-instruction-pc.md) 87 | * [打印寄存器的值](print-registers.md) 88 | * [显示程序原始机器码](disassemble-raw-machine-code.md) 89 | 90 | # 改变程序的执行 91 | * [改变字符串的值](change-string.md) 92 | * [设置变量的值](set-var.md) 93 | * [修改PC寄存器的值](modify-pc-register.md) 94 | * [跳转到指定位置执行](jump.md) 95 | * [使用断点命令改变程序的执行](breakpoint-command.md) 96 | * [修改被调试程序的二进制文件](patch-program.md) 97 | 98 | # 信号 99 | * [查看信号处理信息](info-signals.md) 100 | * [信号发生时是否暂停程序](stop-signal.md) 101 | * [信号发生时是否打印信号信息](print-signal.md) 102 | * [信号发生时是否把信号丢给程序处理](pass-signal.md) 103 | * [给程序发送信号](send-signal.md) 104 | * [使用“$_siginfo”变量](use-$_siginfo-variable.md) 105 | 106 | # 共享库 107 | * [显示共享链接库信息](info_sharedlibrary.md) 108 | 109 | # 脚本 110 | * [配置gdb init文件](config-gdbinit.md) 111 | * [按何种方式解析脚本文件](set-script-extension.md) 112 | * [保存历史命令](save-history-commands.md) 113 | 114 | # 源文件 115 | * [设置源文件查找路径](directory.md) 116 | * [替换查找源文件的目录](substitute-path.md) 117 | 118 | # 图形化界面 119 | * [进入和退出图形化调试界面](tui-mode.md) 120 | * [显示汇编代码窗口](layout-asm.md) 121 | * [显示寄存器窗口](layout-regs.md) 122 | * [调整窗口大小](winheight.md) 123 | 124 | # 其它 125 | * [命令行选项的格式](option-format.md) 126 | * [支持预处理器宏信息](preprocessor-macro.md) 127 | * [保留未使用的类型](keep-unused-types.md) 128 | * [使用命令的缩写形式](use-short-command.md) 129 | * [在gdb中执行shell命令和make](run-shell-command.md) 130 | * [在gdb中执行cd和pwd命令](run-cd-pwd.md) 131 | * [设置命令提示符](set-prompt.md) 132 | * [设置被调试程序的参数](set-program-args.md) 133 | * [设置被调试程序的环境变量](set-program-env.md) 134 | * [得到命令的帮助信息](help.md) 135 | * [记录执行gdb的过程](set-logging.md) 136 | * [打印C++虚表及其内容](show-vtbl-content.md) 137 | 138 | -------------------------------------------------------------------------------- /src/info-frame.md: -------------------------------------------------------------------------------- 1 | # 打印函数堆栈帧信息 2 | ## 例子 3 | #include 4 | int func(int a, int b) 5 | { 6 | int c = a * b; 7 | printf("c is %d\n", c); 8 | } 9 | 10 | int main(void) 11 | { 12 | func(1, 2); 13 | return 0; 14 | } 15 | 16 | 17 | 18 | ## 技巧 19 | 使用gdb调试程序时,可以使用“`i frame`”命令(`i`是`info`命令缩写)显示函数堆栈帧信息。以上面程序为例: 20 | 21 | Breakpoint 1, func (a=1, b=2) at a.c:5 22 | 5 printf("c is %d\n", c); 23 | (gdb) i frame 24 | Stack level 0, frame at 0x7fffffffe590: 25 | rip = 0x40054e in func (a.c:5); saved rip = 0x400577 26 | called by frame at 0x7fffffffe5a0 27 | source language c. 28 | Arglist at 0x7fffffffe580, args: a=1, b=2 29 | Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590 30 | Saved registers: 31 | rbp at 0x7fffffffe580, rip at 0x7fffffffe588 32 | (gdb) i registers 33 | rax 0x2 2 34 | rbx 0x0 0 35 | rcx 0x0 0 36 | rdx 0x7fffffffe688 140737488348808 37 | rsi 0x2 2 38 | rdi 0x1 1 39 | rbp 0x7fffffffe580 0x7fffffffe580 40 | rsp 0x7fffffffe560 0x7fffffffe560 41 | r8 0x7ffff7dd4e80 140737351863936 42 | r9 0x7ffff7dea560 140737351951712 43 | r10 0x7fffffffe420 140737488348192 44 | r11 0x7ffff7a35dd0 140737348066768 45 | r12 0x400440 4195392 46 | r13 0x7fffffffe670 140737488348784 47 | r14 0x0 0 48 | r15 0x0 0 49 | rip 0x40054e 0x40054e 50 | eflags 0x202 [ IF ] 51 | cs 0x33 51 52 | ss 0x2b 43 53 | ds 0x0 0 54 | es 0x0 0 55 | fs 0x0 0 56 | gs 0x0 0 57 | (gdb) disassemble func 58 | Dump of assembler code for function func: 59 | 0x0000000000400536 <+0>: push %rbp 60 | 0x0000000000400537 <+1>: mov %rsp,%rbp 61 | 0x000000000040053a <+4>: sub $0x20,%rsp 62 | 0x000000000040053e <+8>: mov %edi,-0x14(%rbp) 63 | 0x0000000000400541 <+11>: mov %esi,-0x18(%rbp) 64 | 0x0000000000400544 <+14>: mov -0x14(%rbp),%eax 65 | 0x0000000000400547 <+17>: imul -0x18(%rbp),%eax 66 | 0x000000000040054b <+21>: mov %eax,-0x4(%rbp) 67 | => 0x000000000040054e <+24>: mov -0x4(%rbp),%eax 68 | 0x0000000000400551 <+27>: mov %eax,%esi 69 | 0x0000000000400553 <+29>: mov $0x400604,%edi 70 | 0x0000000000400558 <+34>: mov $0x0,%eax 71 | 0x000000000040055d <+39>: callq 0x400410 72 | 0x0000000000400562 <+44>: leaveq 73 | 0x0000000000400563 <+45>: retq 74 | End of assembler dump. 75 | 76 | 可以看到执行“`i frame`”命令后,输出了当前函数堆栈帧的地址,指令寄存器的值,局部变量地址及值等信息,可以对照当前寄存器的值和函数的汇编指令看一下。 77 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Frame-Info.html). 78 | 79 | ## 贡献者 80 | 81 | nanxiao 82 | -------------------------------------------------------------------------------- /src/info-function.md: -------------------------------------------------------------------------------- 1 | # 列出函数的名字 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | void *thread_func(void *p_arg) 8 | { 9 | while (1) 10 | { 11 | sleep(10); 12 | } 13 | } 14 | int main(void) 15 | { 16 | pthread_t t1, t2; 17 | 18 | pthread_create(&t1, NULL, thread_func, "Thread 1"); 19 | pthread_create(&t2, NULL, thread_func, "Thread 2"); 20 | 21 | sleep(1000); 22 | return; 23 | } 24 | 25 | 26 | 27 | 28 | ## 技巧 29 | 30 | 使用gdb调试时,使用“`info functions`”命令可以列出可执行文件的所有函数名称。以上面代码为例: 31 | 32 | (gdb) info functions 33 | All defined functions: 34 | 35 | File a.c: 36 | int main(void); 37 | void *thread_func(void *); 38 | 39 | Non-debugging symbols: 40 | 0x0805079c _PROCEDURE_LINKAGE_TABLE_ 41 | 0x080507ac _cleanup@plt 42 | 0x080507bc atexit 43 | 0x080507bc atexit@plt 44 | 0x080507cc __fpstart 45 | 0x080507cc __fpstart@plt 46 | 0x080507dc exit@plt 47 | 0x080507ec __deregister_frame_info_bases@plt 48 | 0x080507fc __register_frame_info_bases@plt 49 | 0x0805080c _Jv_RegisterClasses@plt 50 | 0x0805081c sleep 51 | 0x0805081c sleep@plt 52 | 0x0805082c pthread_create@plt 53 | 0x0805083c _start 54 | 0x080508b4 _mcount 55 | 0x080508b8 __do_global_dtors_aux 56 | 0x08050914 frame_dummy 57 | 0x080509f4 __do_global_ctors_aux 58 | 0x08050a24 _init 59 | 0x08050a31 _fini 60 | 61 | 62 | 可以看到会列出函数原型以及不带调试信息的函数。 63 | 64 | 另外这个命令也支持正则表达式:“`info functions regex`”,这样只会列出符合正则表达式的函数名称,例如: 65 | 66 | (gdb) info functions thre* 67 | All functions matching regular expression "thre*": 68 | 69 | File a.c: 70 | void *thread_func(void *); 71 | 72 | Non-debugging symbols: 73 | 0x0805082c pthread_create@plt 74 | 75 | 76 | 77 | 78 | 可以看到gdb只会列出名字里包含“`thre`”的函数。 79 | 80 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Symbols.html) 81 | 82 | ## 贡献者 83 | 84 | nanxiao 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/info-signals.md: -------------------------------------------------------------------------------- 1 | # 查看信号处理信息 2 | ## 例子 3 | #include 4 | #include 5 | 6 | void handler(int sig); 7 | 8 | void handler(int sig) 9 | { 10 | signal(sig, handler); 11 | printf("Receive signal: %d\n", sig); 12 | } 13 | 14 | int main(void) { 15 | signal(SIGINT, handler); 16 | signal(SIGALRM, handler); 17 | 18 | while (1) 19 | { 20 | sleep(1); 21 | } 22 | return 0; 23 | } 24 | 25 | ## 技巧 26 | 用gdb调试程序时,可以用“`i signals`”命令(或者“`i handle`”命令,`i`是`info`命令缩写)查看gdb如何处理进程收到的信号: 27 | 28 | (gdb) i signals 29 | Signal Stop Print Pass to program Description 30 | 31 | SIGHUP Yes Yes Yes Hangup 32 | SIGINT Yes Yes No Interrupt 33 | SIGQUIT Yes Yes Yes Quit 34 | ...... 35 | SIGALRM No No Yes Alarm clock 36 | ...... 37 | 38 | 第一项(`Signal`):标示每个信号。 39 | 第二项(`Stop`):表示被调试的程序有对应的信号发生时,gdb是否会暂停程序。 40 | 第三项(`Print`):表示被调试的程序有对应的信号发生时,gdb是否会打印相关信息。 41 | 第四项(`Pass to program`):gdb是否会把这个信号发给被调试的程序。 42 | 第五项(`Description`):信号的描述信息。 43 | 44 | 从上面的输出可以看到,当`SIGINT`信号发生时,gdb会暂停被调试的程序,并打印相关信息,但不会把这个信号发给被调试的程序。而当`SIGALRM`信号发生时,gdb不会暂停被调试的程序,也不打印相关信息,但会把这个信号发给被调试的程序。 45 | 46 | 启动gdb调试上面的程序,同时另起一个终端,先后发送`SIGINT`和`SIGALRM`信号给被调试的进程,输出如下: 47 | 48 | Program received signal SIGINT, Interrupt. 49 | 0xfeeeae55 in ___nanosleep () from /lib/libc.so.1 50 | (gdb) c 51 | Continuing. 52 | Receive signal: 14 53 | 54 | 可以看到收到`SIGINT`时,程序暂停了,也输出了信号信息,但并没有把`SIGINT`信号交由进程处理(程序没有输出)。而收到`SIGALRM`信号时,程序没有暂停,也没有输出信号信息,但把`SIGALRM`信号交由进程处理了(程序打印了输出)。 55 | 56 | 57 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Signals.html). 58 | 59 | ## 贡献者 60 | 61 | nanxiao 62 | -------------------------------------------------------------------------------- /src/info_sharedlibrary.md: -------------------------------------------------------------------------------- 1 | # 显示共享链接库信息 2 | ## 例子 3 | #include 4 | 5 | int main(void) 6 | { 7 | char a[1026] = {0}; 8 | redisContext *c = NULL; 9 | void *reply = NULL; 10 | 11 | memset(a, 'a', (sizeof(a) - 1)); 12 | c = redisConnect("127.0.0.1", 6379); 13 | if (NULL != c) 14 | { 15 | reply = redisCommand(c, "set 1 %s", a); 16 | freeReplyObject(reply); 17 | 18 | reply = redisCommand(c, "get 1"); 19 | freeReplyObject(reply); 20 | 21 | redisFree(c); 22 | } 23 | return 0; 24 | } 25 | 26 | 27 | ## 技巧 28 | 使用"`info sharedlibrary regex`"命令可以显示程序加载的共享链接库信息,其中`regex`可以是正则表达式,意为显示名字符合`regex`的共享链接库。如果没有`regex`,则列出所有的库。以上面程序为例: 29 | 30 | (gdb) start 31 | Temporary breakpoint 1 at 0x109f0: file a.c, line 5. 32 | Starting program: /export/home/nan/a 33 | [Thread debugging using libthread_db enabled] 34 | [New Thread 1 (LWP 1)] 35 | [Switching to Thread 1 (LWP 1)] 36 | 37 | Temporary breakpoint 1, main () at a.c:5 38 | 5 char a[1026] = {0}; 39 | (gdb) info sharedlibrary 40 | From To Syms Read Shared Object Library 41 | 0xff3b44a0 0xff3e3490 Yes (*) /usr/lib/ld.so.1 42 | 0xff3325f0 0xff33d4b4 Yes /usr/local/lib/libhiredis.so.0.11 43 | 0xff3137f0 0xff31a9f4 Yes (*) /lib/libsocket.so.1 44 | 0xff215fd4 0xff28545c Yes (*) /lib/libnsl.so.1 45 | 0xff0a3a20 0xff14fedc Yes (*) /lib/libc.so.1 46 | 0xff320400 0xff3234c8 Yes (*) /platform/SUNW,UltraAX-i2/lib/libc_psr.so.1 47 | (*): Shared library is missing debugging information. 48 | 49 | 可以看到列出所有加载的共享链接库信息,带“`*`”表示库缺少调试信息。 50 | 51 | 另外也可以使用正则表达式: 52 | 53 | (gdb) i sharedlibrary hiredi* 54 | From To Syms Read Shared Object Library 55 | 0xff3325f0 0xff33d4b4 Yes /usr/local/lib/libhiredis.so.0.11 56 | 57 | 可以看到只列出了一个库信息。 58 | 参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Files.html#index-shared-libraries). 59 | 60 | ## 贡献者 61 | 62 | nanxiao 63 | -------------------------------------------------------------------------------- /src/jump.md: -------------------------------------------------------------------------------- 1 | # 跳转到指定位置执行 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | void fun (int x) 8 | { 9 | if (x < 0) 10 | puts ("error"); 11 | } 12 | 13 | int main (void) 14 | { 15 | int i = 1; 16 | 17 | fun (i--); 18 | fun (i--); 19 | fun (i--); 20 | 21 | return 0; 22 | } 23 | 24 | ## 技巧 25 | 26 | 当调试程序时,你可能不小心走过了出错的地方: 27 | 28 | (gdb) n 29 | 13 fun (i--); 30 | (gdb) 31 | 14 fun (i--); 32 | (gdb) 33 | 15 fun (i--); 34 | (gdb) 35 | error 36 | 17 return 0; 37 | 38 | 看起来是在15行,调用fun的时候出错了。常见的办法是在15行设置个断点,然后从头`run`一次。 39 | 40 | 如果你的环境支持反向执行,那么更好了。 41 | 42 | 如果不支持,你也可以直接`jump`到15行,再执行一次: 43 | 44 | (gdb) b 15 45 | Breakpoint 2 at 0x40056a: file jump.c, line 15. 46 | (gdb) j 15 47 | Continuing at 0x40056a. 48 | 49 | Breakpoint 2, main () at jump.c:15 50 | 15 fun (i--); 51 | (gdb) s 52 | fun (x=-2) at jump.c:5 53 | 5 if (x < 0) 54 | (gdb) n 55 | 6 puts ("error"); 56 | 57 | 需要注意的是: 58 | 59 | 1. `jump`命令只改变pc的值,所以改变程序执行可能会出现不同的结果,比如变量i的值 60 | 2. 通过(临时)断点的配合,可以让你的程序跳到指定的位置,并停下来 61 | 62 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Jumping.html#Jumping) 63 | 64 | ## 贡献者 65 | 66 | xmj 67 | 68 | -------------------------------------------------------------------------------- /src/keep-unused-types.md: -------------------------------------------------------------------------------- 1 | # 保留未使用的类型 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | union Type { 8 | int a; 9 | int *b; 10 | }; 11 | 12 | int main() 13 | { 14 | printf("sizeof(union Type) is %lu\n", sizeof(union Type)); 15 | return 0; 16 | } 17 | 18 | ## 技巧 19 | 20 | 使用`gcc -g`编译生成的程序,是不包含union Type的符号信息: 21 | 22 | (gdb) p sizeof(union Type) 23 | No union type named Type. 24 | 25 | 如果想让gcc保留这些没有被使用的类型信息(猜测应该是sizeof在编译时即被替换成常数,所以gcc认为union Type是未使用的类型),则可以使用`gcc -g -fno-eliminate-unused-debug-types`进行编译: 26 | 27 | (gdb) p sizeof(union Type) 28 | $1 = 8 29 | 30 | 参见[gcc手册](https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options) 31 | 32 | ## 贡献者 33 | 34 | xmj 35 | 36 | -------------------------------------------------------------------------------- /src/layout-asm.md: -------------------------------------------------------------------------------- 1 | # 显示汇编代码窗口 2 | ## 例子 3 | #include 4 | 5 | void fun1(void) 6 | { 7 | int i = 0; 8 | 9 | i++; 10 | i = i * 2; 11 | printf("%d\n", i); 12 | } 13 | 14 | void fun2(void) 15 | { 16 | int j = 0; 17 | 18 | fun1(); 19 | j++; 20 | j = j * 2; 21 | printf("%d\n", j); 22 | } 23 | 24 | int main(void) 25 | { 26 | fun2(); 27 | return 0; 28 | } 29 | 30 | 31 | ## 技巧 32 | 使用gdb图形化调试界面时,可以使用“`layout asm`”命令显示汇编代码窗口。以调试上面程序为例: 33 | 34 | ┌───────────────────────────────────────────────────────────────────────────────────────────────┐ 35 | >│0x40052b callq 0x4004f3 │ 36 | │0x400530 mov $0x0,%eax │ 37 | │0x400535 leaveq │ 38 | │0x400536 retq │ 39 | │0x400537 nop │ 40 | │0x400538 nop │ 41 | │0x400539 nop │ 42 | │0x40053a nop │ 43 | │0x40053b nop │ 44 | │0x40053c nop │ 45 | │0x40053d nop │ 46 | │0x40053e nop │ 47 | │0x40053f nop │ 48 | │0x400540 <__libc_csu_fini> repz retq │ 49 | │0x400542 data16 data16 data16 data16 nopw %cs:0x0(%rax,%rax,1) │ 50 | │0x400550 <__libc_csu_init> mov %rbp,-0x28(%rsp) │ 51 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 52 | native process 44658 In: main Line: 24 PC: 0x40052b 53 | 54 | (gdb) start 55 | Temporary breakpoint 1 at 0x40052b: file a.c, line 24. 56 | Starting program: /home/nan/a 57 | 58 | Temporary breakpoint 1, main () at a.c:24 59 | (gdb) 60 | 61 | 可以看到,显示了当前的程序的汇编代码。 62 | 如果既想显示源代码,又想显示汇编代码,可以使用“`layout split`”命令: 63 | 64 | ┌──a.c──────────────────────────────────────────────────────────────────────────────────────────┐ 65 | >│24 fun2(); │ 66 | │25 return 0; │ 67 | │26 } │ 68 | │27 │ 69 | │28 │ 70 | │29 │ 71 | │30 │ 72 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 73 | >│0x40052b callq 0x4004f3 │ 74 | │0x400530 mov $0x0,%eax │ 75 | │0x400535 leaveq │ 76 | │0x400536 retq │ 77 | │0x400537 nop │ 78 | │0x400538 nop │ 79 | │0x400539 nop │ 80 | │0x40053a nop │ 81 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 82 | native process 44658 In: main Line: 24 PC: 0x40052b 83 | 84 | (gdb) start 85 | Temporary breakpoint 1 at 0x40052b: file a.c, line 24. 86 | Starting program: /home/nan/a 87 | 88 | Temporary breakpoint 1, main () at a.c:24 89 | (gdb) 90 | 91 | 可以看到上面显示的是源代码,下面显示的是汇编代码。 92 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/TUI-Commands.html). 93 | 94 | ## 贡献者 95 | 96 | nanxiao 97 | -------------------------------------------------------------------------------- /src/layout-regs.md: -------------------------------------------------------------------------------- 1 | # 显示寄存器窗口 2 | ## 例子 3 | #include 4 | 5 | void fun1(void) 6 | { 7 | int i = 0; 8 | 9 | i++; 10 | i = i * 2; 11 | printf("%d\n", i); 12 | } 13 | 14 | void fun2(void) 15 | { 16 | int j = 0; 17 | 18 | fun1(); 19 | j++; 20 | j = j * 2; 21 | printf("%d\n", j); 22 | } 23 | 24 | int main(void) 25 | { 26 | fun2(); 27 | return 0; 28 | } 29 | 30 | 31 | ## 技巧 32 | 使用gdb图形化调试界面时,可以使用“`layout regs`”命令显示寄存器窗口。以调试上面程序为例: 33 | 34 | ┌──Register group: general─────────────────────────────────────────────────────────────────────────┐ 35 | │rax 0x34e4590f60 227169341280 rbx 0x0 0 │ 36 | │rcx 0x0 0 rdx 0x7fffffffe4b8 140737488348344 │ 37 | │rsi 0x7fffffffe4a8 140737488348328 rdi 0x1 1 │ 38 | │rbp 0x7fffffffe3c0 0x7fffffffe3c0 rsp 0x7fffffffe3c0 0x7fffffffe3c0 │ 39 | │r8 0x34e458f300 227169334016 r9 0x34e3a0e9f0 227157273072 │ 40 | │r10 0x7fffffffe210 140737488347664 r11 0x34e421ec20 227165727776 │ 41 | │r12 0x4003e0 4195296 r13 0x7fffffffe4a0 140737488348320 │ 42 | └──────────────────────────────────────────────────────────────────────────────────────────────────┘ 43 | │17 j++; │ 44 | │18 j = j * 2; │ 45 | │19 printf("%d\n", j); │ 46 | │20 } │ 47 | │21 │ 48 | │22 int main(void) │ 49 | │23 { │ 50 | >│24 fun2(); │ 51 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 52 | native process 12552 In: main Line: 24 PC: 0x40052b 53 | Reading symbols from a...done. 54 | (gdb) start 55 | Temporary breakpoint 1 at 0x40052b: file a.c, line 24. 56 | Starting program: /home/nan/a 57 | 58 | Temporary breakpoint 1, main () at a.c:24 59 | (gdb) 60 | 61 | 可以看到,显示了通用寄存器的内容。 62 | 如果想查看浮点寄存器,可以使用“`tui reg float`”命令: 63 | 64 | ┌──Register group: float───────────────────────────────────────────────────────────────────────────┐ 65 | │st0 0 (raw 0x00000000000000000000) │ 66 | │st1 0 (raw 0x00000000000000000000) │ 67 | │st2 0 (raw 0x00000000000000000000) │ 68 | │st3 0 (raw 0x00000000000000000000) │ 69 | │st4 0 (raw 0x00000000000000000000) │ 70 | │st5 0 (raw 0x00000000000000000000) │ 71 | │st6 0 (raw 0x00000000000000000000) │ 72 | └──────────────────────────────────────────────────────────────────────────────────────────────────┘ 73 | │16 fun1(); │ 74 | │17 j++; │ 75 | │18 j = j * 2; │ 76 | │19 printf("%d\n", j); │ 77 | │20 } │ 78 | │21 │ 79 | │22 int main(void) │ 80 | │23 { │ 81 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 82 | native process 12552 In: main Line: 24 PC: 0x40052b 83 | Temporary breakpoint 1 at 0x40052b: file a.c, line 24. 84 | Starting program: /home/nan/a 85 | 86 | Temporary breakpoint 1, main () at a.c:24 87 | (gdb) tui reg float 88 | 89 | “`tui reg system`”命令显示系统寄存器: 90 | 91 | ┌──Register group: system──────────────────────────────────────────────────────────────────────────┐ 92 | │orig_rax 0xffffffffffffffff -1 │ 93 | │ │ 94 | │ │ 95 | │ │ 96 | │ │ 97 | │ │ 98 | │ │ 99 | └──────────────────────────────────────────────────────────────────────────────────────────────────┘ 100 | │16 fun1(); │ 101 | │17 j++; │ 102 | │18 j = j * 2; │ 103 | │19 printf("%d\n", j); │ 104 | │20 } │ 105 | │21 │ 106 | │22 int main(void) │ 107 | │23 { │ 108 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 109 | native process 12552 In: main Line: 24 PC: 0x40052b 110 | 111 | Temporary breakpoint 1, main () at a.c:24 112 | (gdb) tui reg system 113 | (gdb) 114 | 想切换回显示通用寄存器内容,可以使用“`tui reg general`”命令: 115 | 116 | ┌──Register group: general─────────────────────────────────────────────────────────────────────────┐ 117 | │rax 0x34e4590f60 227169341280 rbx 0x0 0 │ 118 | │rcx 0x0 0 rdx 0x7fffffffe4b8 140737488348344 │ 119 | │rsi 0x7fffffffe4a8 140737488348328 rdi 0x1 1 │ 120 | │rbp 0x7fffffffe3c0 0x7fffffffe3c0 rsp 0x7fffffffe3c0 0x7fffffffe3c0 │ 121 | │r8 0x34e458f300 227169334016 r9 0x34e3a0e9f0 227157273072 │ 122 | │r10 0x7fffffffe210 140737488347664 r11 0x34e421ec20 227165727776 │ 123 | │r12 0x4003e0 4195296 r13 0x7fffffffe4a0 140737488348320 │ 124 | └──────────────────────────────────────────────────────────────────────────────────────────────────┘ 125 | │16 fun1(); │ 126 | │17 j++; │ 127 | │18 j = j * 2; │ 128 | │19 printf("%d\n", j); │ 129 | │20 } │ 130 | │21 │ 131 | │22 int main(void) │ 132 | │23 { │ 133 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 134 | native process 12552 In: main Line: 24 PC: 0x40052b 135 | (gdb) tui reg general 136 | (gdb) 137 | 138 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/TUI-Commands.html). 139 | 140 | ## 贡献者 141 | 142 | nanxiao 143 | -------------------------------------------------------------------------------- /src/load-executable-and-coredump-file.md: -------------------------------------------------------------------------------- 1 | # 加载可执行程序和core dump文件 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int main(void) { 8 | int *p = NULL; 9 | printf("hello world\n"); 10 | *p = 0; 11 | return 0; 12 | } 13 | 14 | 15 | 16 | ## 技巧 17 | 18 | 例子程序访问了一个空指针,所以程序会crash并产生core dump文件。用gdb调试core dump文件,通常用这个命令形式:“gdb path/to/the/executable path/to/the/coredump”,然后gdb会显示程序crash的位置: 19 | 20 | bash-3.2# gdb -q /data/nan/a /var/core/core.a.22268.1402638140 21 | Reading symbols from /data/nan/a...done. 22 | [New LWP 1] 23 | [Thread debugging using libthread_db enabled] 24 | [New Thread 1 (LWP 1)] 25 | Core was generated by `./a'. 26 | Program terminated with signal 11, Segmentation fault. 27 | #0 0x0000000000400cdb in main () at a.c:6 28 | 6 *p = 0; 29 | 30 | 有时我们想在gdb启动后,动态加载可执行程序和core dump文件,这时可以用“file”和“core”(core-file命令缩写)命令。“file”命令用来读取可执行文件的符号表信息,而“core”命令则是指定core dump文件的位置: 31 | 32 | bash-3.2# gdb -q 33 | (gdb) file /data/nan/a 34 | Reading symbols from /data/nan/a...done. 35 | (gdb) core /var/core/core.a.22268.1402638140 36 | [New LWP 1] 37 | [Thread debugging using libthread_db enabled] 38 | [New Thread 1 (LWP 1)] 39 | Core was generated by `./a'. 40 | Program terminated with signal 11, Segmentation fault. 41 | #0 0x0000000000400cdb in main () at a.c:6 42 | 6 *p = 0; 43 | 44 | 45 | 46 | 可以看到gdb同样显示程序crash的位置。 47 | 48 | 这两个命令可参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Files.html#index-core-dump-file) 49 | 50 | ## 贡献者 51 | 52 | nanxiao 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/load-library.md: -------------------------------------------------------------------------------- 1 | # 将库加载到被调试进程的内存空间中 2 | 3 | ``` 4 | ################################################ 5 | # init 6 | target remote 127.0.0.1:12345 7 | set solib-search-path 8 | 9 | b main 10 | c 11 | 12 | ### task: load a library inside the debuggee process memory space 13 | # e.g. load 'libpino.so' and use its function 'ciao_pino' to retrieve the base address 14 | call (void *) dlopen("libpino.so", 0) 15 | set $handle=$1 16 | call (void *) dlsym($handle, "ciao_pino") 17 | 18 | # save base address where library was loaded by the loader 19 | set $offset_ciao_pino=0x111 20 | set $base_addr_libpino=$2 - $offset_ciao_pino 21 | 22 | # save ciao_pino's return value 23 | call (int) ciao_pino(1) 24 | set $return_value=$3 25 | ``` 26 | 27 | 详情参见[load_library_inject.gdb](https://github.com/tin-z/IoT_toolbox/blob/main/gdb/load_library_inject.gdb) 28 | 29 | ## 贡献者 30 | 31 | tin-z 32 | -------------------------------------------------------------------------------- /src/maint-info-program-space.md: -------------------------------------------------------------------------------- 1 | # 打印程序进程空间信息 2 | ## 例子 3 | a.c: 4 | #include 5 | int func(int a, int b) 6 | { 7 | int c = a * b; 8 | printf("c is %d\n", c); 9 | } 10 | 11 | int main(void) 12 | { 13 | func(1, 2); 14 | return 0; 15 | } 16 | 17 | 18 | b.c: 19 | #include 20 | 21 | int func1(int a) 22 | { 23 | return 2 * a; 24 | } 25 | 26 | int func2(int a) 27 | { 28 | int c = 0; 29 | c = 2 * func1(a); 30 | return c; 31 | } 32 | 33 | int func3(int a) 34 | { 35 | int c = 0; 36 | c = 2 * func2(a); 37 | return c; 38 | } 39 | 40 | int main(void) 41 | { 42 | printf("%d\n", func3(10)); 43 | return 0; 44 | } 45 | 46 | 47 | ## 技巧 48 | 使用gdb调试多个进程时,可以使用“`maint info program-spaces`”打印当前所有被调试的进程信息。以上面程序为例: 49 | 50 | [root@localhost nan]# gdb a 51 | GNU gdb (GDB) 7.8.1 52 | ...... 53 | Reading symbols from a...done. 54 | (gdb) start 55 | Temporary breakpoint 1 at 0x4004f9: file a.c, line 10. 56 | Starting program: /home/nan/a 57 | 58 | Temporary breakpoint 1, main () at a.c:10 59 | 10 func(1, 2); 60 | (gdb) add-inferior -exec b 61 | Added inferior 2 62 | Reading symbols from b...done. 63 | (gdb) i inferiors b 64 | Args must be numbers or '$' variables. 65 | (gdb) i inferiors 66 | Num Description Executable 67 | 2 /home/nan/b 68 | * 1 process 15753 /home/nan/a 69 | (gdb) inferior 2 70 | [Switching to inferior 2 [] (/home/nan/b)] 71 | (gdb) start 72 | Temporary breakpoint 2 at 0x4004f9: main. (2 locations) 73 | Starting program: /home/nan/b 74 | 75 | Temporary breakpoint 2, main () at b.c:24 76 | 24 printf("%d\n", func3(10)); 77 | (gdb) i inferiors 78 | Num Description Executable 79 | * 2 process 15902 /home/nan/b 80 | 1 process 15753 /home/nan/a 81 | (gdb) clone-inferior -copies 2 82 | Added inferior 3. 83 | Added inferior 4. 84 | (gdb) i inferiors 85 | Num Description Executable 86 | 4 /home/nan/b 87 | 3 /home/nan/b 88 | * 2 process 15902 /home/nan/b 89 | 1 process 15753 /home/nan/a 90 | (gdb) maint info program-spaces 91 | Id Executable 92 | 4 /home/nan/b 93 | Bound inferiors: ID 4 (process 0) 94 | 3 /home/nan/b 95 | Bound inferiors: ID 3 (process 0) 96 | * 2 /home/nan/b 97 | Bound inferiors: ID 2 (process 15902) 98 | 1 /home/nan/a 99 | Bound inferiors: ID 1 (process 15753) 100 | 可以看到执行“`maint info program-spaces`”命令后,打印出当前有4个`program-spaces`(编号从1到4)。另外还有每个`program-spaces`对应的程序,`inferior`编号及进程号。 101 | 102 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Inferiors-and-Programs.html). 103 | 104 | ## 贡献者 105 | 106 | nanxiao 107 | -------------------------------------------------------------------------------- /src/maint-info-sol-threads.md: -------------------------------------------------------------------------------- 1 | # 在Solaris上使用maintenance命令查看线程信息 2 | 3 | 4 | 5 | 6 | ## 技巧 7 | 用gdb调试多线程程序时,如果想查看线程信息,可以使用“i threads”命令(i是info命令缩写),例如: 8 | 9 | (gdb) i threads 10 | 106 process 2689429 0xff04af84 in __lwp_park () from /lib/libc.so.1 11 | 105 process 2623893 0xff04af84 in __lwp_park () from /lib/libc.so.1 12 | 104 process 2558357 0xff04af84 in __lwp_park () from /lib/libc.so.1 13 | 103 process 2492821 0xff04af84 in __lwp_park () from /lib/libc.so.1 14 | 15 | 16 | 17 | 在Solaris操作系统上,gdb为Solaris量身定做了一个查看线程信息的命令:“maint info sol-threads”(maint是maintenance命令缩写),例如: 18 | 19 | (gdb) maint info sol-threads 20 | user thread #1, lwp 1, (active) 21 | user thread #2, lwp 2, (active) startfunc: monitor_thread 22 | user thread #3, lwp 3, (asleep) startfunc: mem_db_thread 23 | - Sleep func: 0x000aa32c 24 | 25 | 26 | 可以看到相比于info命令,maintenance命令显示了更多信息。例如线程当前状态(active,asleep),入口函数(startfunc)等。 27 | 28 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Threads.html) 29 | 30 | ## 贡献者 31 | 32 | nanxiao 33 | 34 | -------------------------------------------------------------------------------- /src/map-source-code-and-assembly.md: -------------------------------------------------------------------------------- 1 | # 将源程序和汇编指令映射起来 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | typedef struct 8 | { 9 | int a; 10 | int b; 11 | int c; 12 | int d; 13 | }ex_st; 14 | 15 | int main(void) { 16 | ex_st st = {1, 2, 3, 4}; 17 | printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 18 | return 0; 19 | } 20 | 21 | ## 技巧一 22 | 23 | 可以用“disas /m fun”(disas是disassemble命令缩写)命令将函数代码和汇编指令映射起来,以上面代码为例: 24 | 25 | (gdb) disas /m main 26 | Dump of assembler code for function main: 27 | 11 int main(void) { 28 | 0x00000000004004c4 <+0>: push %rbp 29 | 0x00000000004004c5 <+1>: mov %rsp,%rbp 30 | 0x00000000004004c8 <+4>: push %rbx 31 | 0x00000000004004c9 <+5>: sub $0x18,%rsp 32 | 33 | 12 ex_st st = {1, 2, 3, 4}; 34 | 0x00000000004004cd <+9>: movl $0x1,-0x20(%rbp) 35 | 0x00000000004004d4 <+16>: movl $0x2,-0x1c(%rbp) 36 | 0x00000000004004db <+23>: movl $0x3,-0x18(%rbp) 37 | 0x00000000004004e2 <+30>: movl $0x4,-0x14(%rbp) 38 | 39 | 13 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 40 | 0x00000000004004e9 <+37>: mov -0x14(%rbp),%esi 41 | 0x00000000004004ec <+40>: mov -0x18(%rbp),%ecx 42 | 0x00000000004004ef <+43>: mov -0x1c(%rbp),%edx 43 | 0x00000000004004f2 <+46>: mov -0x20(%rbp),%ebx 44 | 0x00000000004004f5 <+49>: mov $0x400618,%eax 45 | 0x00000000004004fa <+54>: mov %esi,%r8d 46 | 0x00000000004004fd <+57>: mov %ebx,%esi 47 | 0x00000000004004ff <+59>: mov %rax,%rdi 48 | 0x0000000000400502 <+62>: mov $0x0,%eax 49 | 0x0000000000400507 <+67>: callq 0x4003b8 50 | 51 | 14 return 0; 52 | 0x000000000040050c <+72>: mov $0x0,%eax 53 | 54 | 15 } 55 | 0x0000000000400511 <+77>: add $0x18,%rsp 56 | 0x0000000000400515 <+81>: pop %rbx 57 | 0x0000000000400516 <+82>: leaveq 58 | 0x0000000000400517 <+83>: retq 59 | 60 | End of assembler dump. 61 | 62 | 可以看到每一条C语句下面是对应的汇编代码。 63 | 64 | ## 技巧二 65 | 66 | 如果只想查看某一行所对应的地址范围,可以: 67 | 68 | (gdb) i line 13 69 | Line 13 of "foo.c" starts at address 0x4004e9 and ends at 0x40050c . 70 | 71 | 72 | 如果只想查看这一条语句对应的汇编代码,可以使用“`disassemble [Start],[End]`”命令: 73 | 74 | (gdb) disassemble 0x4004e9, 0x40050c 75 | Dump of assembler code from 0x4004e9 to 0x40050c: 76 | 0x00000000004004e9 : mov -0x14(%rbp),%esi 77 | 0x00000000004004ec : mov -0x18(%rbp),%ecx 78 | 0x00000000004004ef : mov -0x1c(%rbp),%edx 79 | 0x00000000004004f2 : mov -0x20(%rbp),%ebx 80 | 0x00000000004004f5 : mov $0x400618,%eax 81 | 0x00000000004004fa : mov %esi,%r8d 82 | 0x00000000004004fd : mov %ebx,%esi 83 | 0x00000000004004ff : mov %rax,%rdi 84 | 0x0000000000400502 : mov $0x0,%eax 85 | 0x0000000000400507 : callq 0x4003b8 86 | End of assembler dump. 87 | 88 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Machine-Code.html) 89 | 90 | ## 贡献者 91 | 92 | nanxiao 93 | 94 | xmj 95 | 96 | -------------------------------------------------------------------------------- /src/modify-pc-register.md: -------------------------------------------------------------------------------- 1 | # 修改PC寄存器的值 2 | ## 例子 3 | #include 4 | int main(void) 5 | { 6 | int a =0; 7 | 8 | a++; 9 | a++; 10 | printf("%d\n", a); 11 | return 0; 12 | } 13 | 14 | 15 | ## 技巧 16 | PC寄存器会存储程序下一条要执行的指令,通过修改这个寄存器的值,可以达到改变程序执行流程的目的。 17 | 上面的程序会输出“`a=2`”,下面介绍一下如何通过修改PC寄存器的值,改变程序执行流程。 18 | 19 | 4 int a =0; 20 | (gdb) disassemble main 21 | Dump of assembler code for function main: 22 | 0x08050921 : push %ebp 23 | 0x08050922 : mov %esp,%ebp 24 | 0x08050924 : sub $0x8,%esp 25 | 0x08050927 : and $0xfffffff0,%esp 26 | 0x0805092a : mov $0x0,%eax 27 | 0x0805092f : add $0xf,%eax 28 | 0x08050932 : add $0xf,%eax 29 | 0x08050935 : shr $0x4,%eax 30 | 0x08050938 : shl $0x4,%eax 31 | 0x0805093b : sub %eax,%esp 32 | 0x0805093d : movl $0x0,-0x4(%ebp) 33 | 0x08050944 : lea -0x4(%ebp),%eax 34 | 0x08050947 : incl (%eax) 35 | 0x08050949 : lea -0x4(%ebp),%eax 36 | 0x0805094c : incl (%eax) 37 | 0x0805094e : sub $0x8,%esp 38 | 0x08050951 : pushl -0x4(%ebp) 39 | 0x08050954 : push $0x80509b4 40 | 0x08050959 : call 0x80507cc 41 | 0x0805095e : add $0x10,%esp 42 | 0x08050961 : mov $0x0,%eax 43 | 0x08050966 : leave 44 | 0x08050967 : ret 45 | End of assembler dump. 46 | (gdb) info line 6 47 | Line 6 of "a.c" starts at address 0x8050944 and ends at 0x8050949 . 48 | (gdb) info line 7 49 | Line 7 of "a.c" starts at address 0x8050949 and ends at 0x805094e . 50 | 51 | 通过“`info line 6`”和“`info line 7`”命令可以知道两条“`a++;`”语句的汇编指令起始地址分别是`0x8050944`和`0x8050949`。 52 | 53 | (gdb) n 54 | 6 a++; 55 | (gdb) p $pc 56 | $3 = (void (*)()) 0x8050944 57 | (gdb) set var $pc=0x08050949 58 | 当程序要执行第一条“`a++;`”语句时,打印`pc`寄存器的值,看到`pc`寄存器的值为`0x8050944`,与“`info line 6`”命令得到的一致。接下来,把`pc`寄存器的值改为`0x8050949`,也就是通过“`info line 7`”命令得到的第二条“`a++;`”语句的起始地址。 59 | 60 | (gdb) n 61 | 8 printf("a=%d\n", a); 62 | (gdb) 63 | a=1 64 | 9 return 0; 65 | 66 | 接下来执行,可以看到程序输出“`a=1`”,也就是跳过了第一条“`a++;`”语句。 67 | 68 | 69 | ## 贡献者 70 | 71 | nanxiao 72 | -------------------------------------------------------------------------------- /src/option-format.md: -------------------------------------------------------------------------------- 1 | # 命令行选项的格式 2 | 3 | ## 技巧 4 | 5 | gdb的帮助信息和在线文档对于长选项的形式使用了不同的风格。你可能有点迷惑,gdb的长选项究竟应该是“-”,还是“--”? 6 | 7 | 是的,这两种方式都可以。例如: 8 | 9 | $ gdb -help 10 | $ gdb --help 11 | 12 | $ gdb -args ./a.out a b c 13 | $ gdb --args ./a.out a b c 14 | 15 | 好吧,使用短的。 16 | 17 | ## 贡献者 18 | 19 | xmj 20 | 21 | -------------------------------------------------------------------------------- /src/pass-signal.md: -------------------------------------------------------------------------------- 1 | # 信号发生时是否把信号丢给程序处理 2 | ## 例子 3 | #include 4 | #include 5 | 6 | void handler(int sig); 7 | 8 | void handler(int sig) 9 | { 10 | signal(sig, handler); 11 | printf("Receive signal: %d\n", sig); 12 | } 13 | 14 | int main(void) { 15 | signal(SIGHUP, handler); 16 | 17 | while (1) 18 | { 19 | sleep(1); 20 | } 21 | return 0; 22 | } 23 | 24 | ## 技巧 25 | 用gdb调试程序时,可以用“`handle signal pass(noignore)/nopass(ignore)`”命令设置当信号发生时,是否把信号丢给程序处理.其中`pass`和`noignore`含义相同,`nopass`和`ignore`含义相同。以上面程序为例: 26 | 27 | (gdb) i signals 28 | Signal Stop Print Pass to program Description 29 | 30 | SIGHUP Yes Yes Yes Hangup 31 | ...... 32 | 33 | (gdb) r 34 | Starting program: /data1/nan/test 35 | [Thread debugging using libthread_db enabled] 36 | [New Thread 1 (LWP 1)] 37 | 38 | Program received signal SIGHUP, Hangup. 39 | [Switching to Thread 1 (LWP 1)] 40 | 0xfeeeae55 in ___nanosleep () from /lib/libc.so.1 41 | (gdb) c 42 | Continuing. 43 | Receive signal: 1 44 | 45 | 可以看到,默认情况下,发生`SIGHUP`信号时,gdb会把信号丢给程序处理。 46 | 47 | 接下来用“`handle SIGHUP nopass`”命令设置当`SIGHUP`信号发生时,gdb不把信号丢给程序处理,执行如下: 48 | 49 | (gdb) handle SIGHUP nopass 50 | Signal Stop Print Pass to program Description 51 | SIGHUP Yes Yes No Hangup 52 | (gdb) c 53 | Continuing. 54 | 55 | Program received signal SIGHUP, Hangup. 56 | 0xfeeeae55 in ___nanosleep () from /lib/libc.so.1 57 | (gdb) c 58 | Continuing. 59 | 可以看到,`SIGHUP`信号发生时,程序没有打印“Receive signal: 1”,说明gdb没有把信号丢给程序处理。 60 | 61 | 如果想恢复之前的行为,用“`handle SIGHUP pass`”命令即可。 62 | 63 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Signals.html). 64 | 65 | ## 贡献者 66 | 67 | nanxiao 68 | -------------------------------------------------------------------------------- /src/patch-program.md: -------------------------------------------------------------------------------- 1 | # 修改被调试程序的二进制文件 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | 8 | void drawing (int n) 9 | { 10 | if (n != 0) 11 | puts ("Try again?\nAll you need is a dollar, and a dream."); 12 | else 13 | puts ("You win $3000!"); 14 | } 15 | 16 | int main (void) 17 | { 18 | int n; 19 | 20 | srand (time (0)); 21 | n = rand () % 10; 22 | printf ("Your number is %d\n", n); 23 | drawing (n); 24 | 25 | return 0; 26 | } 27 | 28 | ## 技巧 29 | 30 | gdb不仅可以用来调试程序,还可以修改程序的二进制代码。 31 | 32 | 缺省情况下,gdb是以只读方式加载程序的。可以通过命令行选项指定为可写: 33 | 34 | $ gcc -write ./a.out 35 | (gdb) show write 36 | Writing into executable and core files is on. 37 | 38 | 也可以在gdb中,使用命令设置并重新加载程序: 39 | 40 | (gdb) set write on 41 | (gdb) file ./a.out 42 | 43 | 接下来,查看反汇编: 44 | 45 | (gdb) disassemble /mr drawing 46 | Dump of assembler code for function drawing: 47 | 5 { 48 | 0x0000000000400642 <+0>: 55 push %rbp 49 | 0x0000000000400643 <+1>: 48 89 e5 mov %rsp,%rbp 50 | 0x0000000000400646 <+4>: 48 83 ec 10 sub $0x10,%rsp 51 | 0x000000000040064a <+8>: 89 7d fc mov %edi,-0x4(%rbp) 52 | 53 | 6 if (n != 0) 54 | 0x000000000040064d <+11>: 83 7d fc 00 cmpl $0x0,-0x4(%rbp) 55 | 0x0000000000400651 <+15>: 74 0c je 0x40065f 56 | 57 | 7 puts ("Try again?\nAll you need is a dollar, and a dream."); 58 | 0x0000000000400653 <+17>: bf e0 07 40 00 mov $0x4007e0,%edi 59 | 0x0000000000400658 <+22>: e8 b3 fe ff ff callq 0x400510 60 | 0x000000000040065d <+27>: eb 0a jmp 0x400669 61 | 62 | 8 else 63 | 9 puts ("You win $3000!"); 64 | 0x000000000040065f <+29>: bf 12 08 40 00 mov $0x400812,%edi 65 | 0x0000000000400664 <+34>: e8 a7 fe ff ff callq 0x400510 66 | 67 | 10 } 68 | 0x0000000000400669 <+39>: c9 leaveq 69 | 0x000000000040066a <+40>: c3 retq 70 | 71 | End of assembler dump. 72 | 73 | 修改二进制代码(注意大小端和指令长度): 74 | 75 | (gdb) set variable *(short*)0x400651=0x0ceb 76 | (gdb) disassemble /mr drawing 77 | Dump of assembler code for function drawing: 78 | 5 { 79 | 0x0000000000400642 <+0>: 55 push %rbp 80 | 0x0000000000400643 <+1>: 48 89 e5 mov %rsp,%rbp 81 | 0x0000000000400646 <+4>: 48 83 ec 10 sub $0x10,%rsp 82 | 0x000000000040064a <+8>: 89 7d fc mov %edi,-0x4(%rbp) 83 | 84 | 6 if (n != 0) 85 | 0x000000000040064d <+11>: 83 7d fc 00 cmpl $0x0,-0x4(%rbp) 86 | 0x0000000000400651 <+15>: eb 0c jmp 0x40065f 87 | 88 | 7 puts ("Try again?\nAll you need is a dollar, and a dream."); 89 | 0x0000000000400653 <+17>: bf e0 07 40 00 mov $0x4007e0,%edi 90 | 0x0000000000400658 <+22>: e8 b3 fe ff ff callq 0x400510 91 | 0x000000000040065d <+27>: eb 0a jmp 0x400669 92 | 93 | 8 else 94 | 9 puts ("You win $3000!"); 95 | 0x000000000040065f <+29>: bf 12 08 40 00 mov $0x400812,%edi 96 | 0x0000000000400664 <+34>: e8 a7 fe ff ff callq 0x400510 97 | 98 | 10 } 99 | 0x0000000000400669 <+39>: c9 leaveq 100 | 0x000000000040066a <+40>: c3 retq 101 | 102 | End of assembler dump. 103 | 104 | 可以看到,条件跳转指令“je”已经被改为无条件跳转“jmp”了。 105 | 106 | 退出,运行一下: 107 | 108 | $ ./a.out 109 | Your number is 2 110 | You win $3000! 111 | 112 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Patching.html#Patching) 113 | 114 | ## 贡献者 115 | 116 | xmj 117 | 118 | -------------------------------------------------------------------------------- /src/preprocessor-macro.md: -------------------------------------------------------------------------------- 1 | # 支持预处理器宏信息 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | #define NAME "Joe" 8 | 9 | int main() 10 | { 11 | printf ("Hello %s\n", NAME); 12 | return 0; 13 | } 14 | 15 | ## 技巧 16 | 17 | 使用`gcc -g`编译生成的程序,是不包含预处理器宏信息的: 18 | 19 | (gdb) p NAME 20 | No symbol "NAME" in current context. 21 | 22 | 如果想在gdb中查看宏信息,可以使用`gcc -g3`进行编译: 23 | 24 | (gdb) p NAME 25 | $1 = "Joe" 26 | 27 | 关于预处理器宏的命令,参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Macros.html#Macros) 28 | 29 | ## 贡献者 30 | 31 | xmj 32 | 33 | -------------------------------------------------------------------------------- /src/print-STL-container.md: -------------------------------------------------------------------------------- 1 | # 打印STL容器中的内容 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | int main () 11 | { 12 | vector vec(10); // 10 zero-initialized elements 13 | 14 | for (int i = 0; i < vec.size(); i++) 15 | vec[i] = i; 16 | 17 | cout << "vec contains:"; 18 | for (int i = 0; i < vec.size(); i++) 19 | cout << ' ' << vec[i]; 20 | cout << '\n'; 21 | 22 | return 0; 23 | } 24 | 25 | ## 技巧一 26 | 27 | 在gdb中,如果要打印C++ STL容器的内容,缺省的显示结果可读性很差: 28 | 29 | (gdb) p vec 30 | $1 = { >> = { 31 | _M_impl = {> = {<__gnu_cxx::new_allocator> = {}, }, _M_start = 0x404010, _M_finish = 0x404038, 32 | _M_end_of_storage = 0x404038}}, } 33 | 34 | gdb 7.0之后,可以使用gcc提供的python脚本,来改善显示结果: 35 | 36 | (gdb) p vec 37 | $1 = std::vector of length 10, capacity 10 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 38 | 39 | 某些发行版(Fedora 11+),不需要额外的设置工作。可在gdb命令行下验证(若没有显示,可按下文的方法进行设置)。 40 | 41 | (gdb) info pretty-printer 42 | 43 | 方法如下: 44 | 45 | 1. 获得python脚本,建议使用gcc默认安装的 46 | 47 | sudo find / -name "*libstdcxx*" 48 | 2. 若本机查找不到python脚本,建议下载gcc对应版本源码包 49 | 50 | gcc git 仓库地址:https://github.com/gcc-mirror/gcc 51 | 52 | python 脚本位于 libstdc++-v3/python 目录下 53 | 3. 将如下代码添加到.gdbinit文件中(假设python脚本位于 /home/maude/gdb_printers/ 下) 54 | 55 | python 56 | import sys 57 | sys.path.insert(0, '/home/maude/gdb_printers/python') 58 | from libstdcxx.v6.printers import register_libstdcxx_printers 59 | register_libstdcxx_printers (None) 60 | end 61 | 62 | (源自https://sourceware.org/gdb/wiki/STLSupport) 63 | 64 | ## 技巧二 65 | 66 | `p vec`的输出无法阅读,但能给我们提示,从而得到无需脚本支持的技巧: 67 | 68 | (gdb) p *(vec._M_impl._M_start)@vec.size() 69 | $2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 70 | 71 | ## 技巧三 72 | 73 | 将 [dbinit_stl_views](http://www.yolinux.com/TUTORIALS/src/dbinit_stl_views-1.03.txt ) 下载下来,,执行命令 74 | ```shell 75 | cat dbinit_stl_views-1.03.txt >> ~/.gdbinit 76 | ``` 77 | 即可 78 | 一些常用的容器及其对应的命令关系 79 | ```shell 80 | std::vector pvector stl_variable 81 | std::list plist stl_variable T 82 | std::map pmap stl_variable 83 | std::multimap pmap stl_variable 84 | std::set pset stl_variable T 85 | std::multiset pset stl_variable 86 | std::deque pdequeue stl_variable 87 | std::stack pstack stl_variable 88 | std::queue pqueue stl_variable 89 | std::priority_queue ppqueue stl_variable 90 | std::bitset pbitset stl_variable 91 | std::string pstring stl_variable 92 | std::widestring pwstring stl_variable 93 | ``` 94 | 更多详情,参考配置中的帮助 95 | 96 | 97 | ## 贡献者 98 | 99 | xmj 100 | 101 | xanpeng 102 | 103 | enjolras 104 | -------------------------------------------------------------------------------- /src/print-all-threads-bt.md: -------------------------------------------------------------------------------- 1 | # 打印所有线程的堆栈信息 2 | ## 例子 3 | #include 4 | #include 5 | #include 6 | int a = 0; 7 | 8 | void *thread1_func(void *p_arg) 9 | { 10 | while (1) 11 | { 12 | a++; 13 | sleep(10); 14 | } 15 | } 16 | 17 | int main(int argc, char* argv[]) 18 | { 19 | pthread_t t1, t2; 20 | pthread_create(&t1, NULL, thread1_func, NULL); 21 | pthread_create(&t2, NULL, thread1_func, NULL); 22 | 23 | sleep(1000); 24 | return 0; 25 | } 26 | 27 | ## 技巧 28 | gdb可以使用“`thread apply all bt`”命令打印所有线程的堆栈信息。以上面程序为例: 29 | 30 | (gdb) thread apply all bt 31 | 32 | Thread 3 (Thread -1210868832 (LWP 26975)): 33 | #0 0xb7dcc96c in __gxx_personality_v0 () from /lib/libc.so.6 34 | #1 0xb7dcc77f in sleep () from /lib/libc.so.6 35 | #2 0x08048575 in thread1_func () 36 | #3 0xb7e871eb in start_thread () from /lib/libpthread.so.0 37 | #4 0xb7e007fe in clone () from /lib/libc.so.6 38 | 39 | Thread 2 (Thread -1219257440 (LWP 26976)): 40 | #0 0xb7dcc96c in __gxx_personality_v0 () from /lib/libc.so.6 41 | #1 0xb7dcc77f in sleep () from /lib/libc.so.6 42 | #2 0x08048575 in thread1_func () 43 | #3 0xb7e871eb in start_thread () from /lib/libpthread.so.0 44 | #4 0xb7e007fe in clone () from /lib/libc.so.6 45 | 46 | Thread 1 (Thread -1210866000 (LWP 26974)): 47 | #0 0xb7dcc96c in __gxx_personality_v0 () from /lib/libc.so.6 48 | #1 0xb7dcc77f in sleep () from /lib/libc.so.6 49 | #2 0x08048547 in main () 50 | #0 0xb7dcc96c in __gxx_personality_v0 () from /lib/libc.so.6 51 | 52 | 可以看到,使用“`thread apply all bt`”命令以后,会对所有的线程实施backtrace命令。`thread apply [thread-id-list] [all] args` 也可以对指定的线程ID列表进行执行: 53 | 54 | (gdb) thread apply 1-2 bt 55 | 56 | Thread 1 (Thread -1210866000 (LWP 26974)): 57 | #0 0xb7dcc96c in __gxx_personality_v0 () from /lib/libc.so.6 58 | #1 0xb7dcc77f in sleep () from /lib/libc.so.6 59 | #2 0x08048547 in main () 60 | 61 | Thread 2 (Thread -1219257440 (LWP 26976)): 62 | #0 0xb7dcc96c in __gxx_personality_v0 () from /lib/libc.so.6 63 | #1 0xb7dcc77f in sleep () from /lib/libc.so.6 64 | #2 0x08048575 in thread1_func () 65 | #3 0xb7e871eb in start_thread () from /lib/libpthread.so.0 66 | #4 0xb7e007fe in clone () from /lib/libc.so.6 67 | #0 0xb7dcc96c in __gxx_personality_v0 () from /lib/libc.so.6 68 | 69 | `thread apply`更多用法和`thread-id-list`的格式用法参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Threads.html). 70 | 71 | ## 贡献者 72 | 73 | panzhongxian 74 | -------------------------------------------------------------------------------- /src/print-array-indexes.md: -------------------------------------------------------------------------------- 1 | # 打印数组的索引下标 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int num[10] = { 8 | 1 << 0, 9 | 1 << 1, 10 | 1 << 2, 11 | 1 << 3, 12 | 1 << 4, 13 | 1 << 5, 14 | 1 << 6, 15 | 1 << 7, 16 | 1 << 8, 17 | 1 << 9 18 | }; 19 | 20 | int main (void) 21 | { 22 | int i; 23 | 24 | for (i = 0; i < 10; i++) 25 | printf ("num[%d] = %d\n", i, num[i]); 26 | 27 | return 0; 28 | } 29 | 30 | ## 技巧 31 | 32 | 在gdb中,当打印一个数组时,缺省是不打印索引下标的: 33 | 34 | (gdb) p num 35 | $1 = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512} 36 | 37 | 如果要打印索引下标,则可以通过如下命令进行设置: 38 | 39 | (gdb) set print array-indexes on 40 | 41 | (gdb) p num 42 | $2 = {[0] = 1, [1] = 2, [2] = 4, [3] = 8, [4] = 16, [5] = 32, [6] = 64, [7] = 128, [8] = 256, [9] = 512} 43 | 44 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Print-Settings.html#index-set-print) 45 | 46 | ## 贡献者 47 | 48 | xmj 49 | 50 | -------------------------------------------------------------------------------- /src/print-ascii-and-wide-string.md: -------------------------------------------------------------------------------- 1 | # 打印ASCII和宽字符字符串 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | char str1[] = "abcd"; 9 | wchar_t str2[] = L"abcd"; 10 | 11 | return 0; 12 | } 13 | 14 | ## 技巧 15 | 用gdb调试程序时,可以使用“`x/s`”命令打印ASCII字符串。以上面程序为例: 16 | 17 | Temporary breakpoint 1, main () at a.c:6 18 | 6 char str1[] = "abcd"; 19 | (gdb) n 20 | 7 wchar_t str2[] = L"abcd"; 21 | (gdb) 22 | 9 return 0; 23 | (gdb) x/s str1 24 | 0x804779f: "abcd" 25 | 26 | 可以看到打印出了`str1`字符串的值。 27 | 28 | 打印宽字符字符串时,要根据宽字符的长度决定如何打印。仍以上面程序为例: 29 | 30 | Temporary breakpoint 1, main () at a.c:6 31 | 6 char str1[] = "abcd"; 32 | (gdb) n 33 | 7 wchar_t str2[] = L"abcd"; 34 | (gdb) 35 | 9 return 0; 36 | (gdb) p sizeof(wchar_t) 37 | $1 = 4 38 | (gdb) x/ws str2 39 | 0x8047788: U"abcd" 40 | 由于当前平台宽字符的长度为4个字节,则用“`x/ws`”命令。如果是2个字节,则用“`x/hs`”命令。 41 | 42 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Memory.html). 43 | 44 | ## 贡献者 45 | 46 | nanxiao 47 | -------------------------------------------------------------------------------- /src/print-consecutive-array-elements.md: -------------------------------------------------------------------------------- 1 | # 打印数组中任意连续元素值 2 | 3 | ## 例子 4 | 5 | int main(void) 6 | { 7 | int array[201]; 8 | int i; 9 | 10 | for (i = 0; i < 201; i++) 11 | array[i] = i; 12 | 13 | return 0; 14 | } 15 | 16 | ## 技巧 17 | 18 | 在gdb中,如果要打印数组中任意连续元素的值,可以使用“`p array[index]@num`”命令(`p`是`print`命令的缩写)。其中`index`是数组索引(从0开始计数),`num`是连续多少个元素。以上面代码为例: 19 | 20 | (gdb) p array 21 | $8 = {0, 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, 22 | 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 23 | 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 24 | 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 25 | 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 26 | 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 27 | 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 28 | 195, 196, 197, 198, 199...} 29 | (gdb) p array[60]@10 30 | $9 = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69} 31 | 32 | 33 | 可以看到打印了`array`数组第60~69个元素的值。 34 | 如果要打印从数组开头连续元素的值,也可使用这个命令:“`p *array@num`”: 35 | 36 | (gdb) p *array@10 37 | $2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 38 | 39 | 40 | 详情参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Arrays.html#Arrays) 41 | ## 贡献者 42 | 43 | nanxiao 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/print-derived-type.md: -------------------------------------------------------------------------------- 1 | # 按照派生类型打印对象 2 | 3 | ## 例子 4 | 5 | #include 6 | using namespace std; 7 | 8 | class Shape { 9 | public: 10 | virtual void draw () {} 11 | }; 12 | 13 | class Circle : public Shape { 14 | int radius; 15 | public: 16 | Circle () { radius = 1; } 17 | void draw () { cout << "drawing a circle...\n"; } 18 | }; 19 | 20 | class Square : public Shape { 21 | int height; 22 | public: 23 | Square () { height = 2; } 24 | void draw () { cout << "drawing a square...\n"; } 25 | }; 26 | 27 | void drawShape (class Shape &p) 28 | { 29 | p.draw (); 30 | } 31 | 32 | int main (void) 33 | { 34 | Circle a; 35 | Square b; 36 | drawShape (a); 37 | drawShape (b); 38 | return 0; 39 | } 40 | 41 | ## 技巧 42 | 43 | 在gdb中,当打印一个对象时,缺省是按照声明的类型进行打印: 44 | 45 | (gdb) frame 46 | #0 drawShape (p=...) at object.cxx:25 47 | 25 p.draw (); 48 | (gdb) p p 49 | $1 = (Shape &) @0x7fffffffde90: {_vptr.Shape = 0x400a80 } 50 | 51 | 在这个例子中,p虽然声明为class Shape,但它实际的派生类型可能为class Circle和Square。如果要缺省按照派生类型进行打印,则可以通过如下命令进行设置: 52 | 53 | (gdb) set print object on 54 | 55 | (gdb) p p 56 | $2 = (Circle &) @0x7fffffffde90: { = {_vptr.Shape = 0x400a80 }, radius = 1} 57 | 58 | 当打印对象类型信息时,该设置也会起作用: 59 | 60 | (gdb) whatis p 61 | type = Shape & 62 | (gdb) ptype p 63 | type = class Shape { 64 | public: 65 | virtual void draw(void); 66 | } & 67 | 68 | (gdb) set print object on 69 | (gdb) whatis p 70 | type = /* real type = Circle & */ 71 | Shape & 72 | (gdb) ptype p 73 | type = /* real type = Circle & */ 74 | class Shape { 75 | public: 76 | virtual void draw(void); 77 | } & 78 | 79 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Print-Settings.html#index-set-print) 80 | 81 | ## 贡献者 82 | 83 | xmj 84 | 85 | xanpeng 86 | 87 | -------------------------------------------------------------------------------- /src/print-formatted-array.md: -------------------------------------------------------------------------------- 1 | # 格式化打印数组 2 | 3 | ## 例子 4 | 5 | 利用`call`函数控制数组的输出格式: 6 | 7 | ``` 8 | #include 9 | 10 | int matrix[10][10]; 11 | 12 | /* 格式化输出数组 */ 13 | void print(int matrix[][10], int m, int n) { 14 | int i, j; 15 | for (i = 0; i < m; ++i) { 16 | for (j = 0; j < n; ++j) 17 | printf("%d ", matrix[i][j]); 18 | printf("\n"); 19 | } 20 | } 21 | 22 | int main(int argc, char const* argv[]) { 23 | int i, j; 24 | for (i = 0; i < 10; ++i) 25 | for (j = 0; j < 10; ++j) 26 | matrix[i][j] = i*10 + j; 27 | return 0; 28 | } 29 | ``` 30 | 31 | ## 技巧 32 | 33 | ``` 34 | (gdb) b 20 35 | Breakpoint 1 at 0x40065e: file test.c, line 20. 36 | (gdb) r 37 | Starting program: /home/zhaoyu/codelab/algorithm/a.out 38 | 39 | Breakpoint 1, main (argc=1, argv=0x7fffffffdc88) at test.c:20 40 | 20 return 0; 41 | (gdb) call print(matrix, 10, 10) // 通过函数调用格式化输出数组 42 | 0 1 2 3 4 5 6 7 8 9 43 | 10 11 12 13 14 15 16 17 18 19 44 | 20 21 22 23 24 25 26 27 28 29 45 | 30 31 32 33 34 35 36 37 38 39 46 | 40 41 42 43 44 45 46 47 48 49 47 | 50 51 52 53 54 55 56 57 58 59 48 | 60 61 62 63 64 65 66 67 68 69 49 | 70 71 72 73 74 75 76 77 78 79 50 | 80 81 82 83 84 85 86 87 88 89 51 | 90 91 92 93 94 95 96 97 98 99 52 | ``` 53 | 54 | ## 贡献者 55 | 56 | vimerzhao 57 | -------------------------------------------------------------------------------- /src/print-frame-variables.md: -------------------------------------------------------------------------------- 1 | # 打印调用栈帧中变量的值 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int func1(int a) 8 | { 9 | int b = 1; 10 | return b * a; 11 | } 12 | 13 | int func2(int a) 14 | { 15 | int b = 2; 16 | return b * func1(a); 17 | } 18 | 19 | int func3(int a) 20 | { 21 | int b = 3; 22 | return b * func2(a); 23 | } 24 | 25 | int main(void) 26 | { 27 | printf("%d\n", func3(10)); 28 | return 0; 29 | } 30 | 31 | ## 技巧 32 | 33 | 在gdb中,如果想查看调用栈帧中的变量,可以先切换到该栈帧中,然后打印: 34 | 35 | (gdb) b func1 36 | (gdb) r 37 | (gdb) bt 38 | #0 func1 (a=10) at frame.c:5 39 | #1 0x0000000000400560 in func2 (a=10) at frame.c:12 40 | #2 0x0000000000400582 in func3 (a=10) at frame.c:18 41 | #3 0x0000000000400596 in main () at frame.c:23 42 | (gdb) f 1 43 | (gdb) p b 44 | (gdb) f 2 45 | (gdb) p b 46 | 47 | 也可以不进行切换,直接打印: 48 | 49 | (gdb) p func2::b 50 | $1 = 2 51 | (gdb) p func3::b 52 | $2 = 3 53 | 54 | 同样,对于C++的函数名,需要使用单引号括起来,比如: 55 | 56 | (gdb) p '(anonymous namespace)::SSAA::handleStore'::n->pi->inst->dump() 57 | 58 | 详情参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Variables.html#Variables) 59 | 60 | ## 贡献者 61 | 62 | xmj 63 | 64 | -------------------------------------------------------------------------------- /src/print-large-array.md: -------------------------------------------------------------------------------- 1 | # 打印大数组中的内容 2 | 3 | ## 例子 4 | 5 | int main() 6 | { 7 | int array[201]; 8 | int i; 9 | 10 | for (i = 0; i < 201; i++) 11 | array[i] = i; 12 | 13 | return 0; 14 | } 15 | 16 | ## 技巧 17 | 18 | 在gdb中,如果要打印大数组的内容,缺省最多会显示200个元素: 19 | 20 | (gdb) p array 21 | $1 = {0, 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, 42, 43, 44, 45, 46, 47, 22 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 23 | 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 24 | 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 25 | 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199...} 26 | 27 | 可以使用如下命令,设置这个最大限制数: 28 | 29 |
(gdb) set print elements number-of-elements
30 | 31 | 也可以使用如下命令,设置为没有限制: 32 | 33 | (gdb) set print elements 0 34 | 35 | 或 36 | 37 | (gdb) set print elements unlimited 38 | (gdb) p array 39 | $2 = {0, 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, 42, 43, 44, 45, 46, 47, 40 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 41 | 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 42 | 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 43 | 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200} 44 | 45 | 详情参见[gdb手册](https://sourceware.org/gdb/download/onlinedocs/gdb/Print-Settings.html#Print-Settings) 46 | 47 | ## 贡献者 48 | 49 | xmj 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/print-local-variables.md: -------------------------------------------------------------------------------- 1 | # 打印函数局部变量的值 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | void fun_a(void) 8 | { 9 | int a = 0; 10 | printf("%d\n", a); 11 | } 12 | 13 | void fun_b(void) 14 | { 15 | int b = 1; 16 | fun_a(); 17 | printf("%d\n", b); 18 | } 19 | 20 | void fun_c(void) 21 | { 22 | int c = 2; 23 | fun_b(); 24 | printf("%d\n", c); 25 | } 26 | 27 | void fun_d(void) 28 | { 29 | int d = 3; 30 | fun_c(); 31 | printf("%d\n", d); 32 | } 33 | 34 | int main(void) 35 | { 36 | int var = -1; 37 | fun_d(); 38 | return 0; 39 | } 40 | 41 | ## 技巧一 42 | 43 | 如果要打印函数局部变量的值,可以使用“bt full”命令(bt是backtrace的缩写)。首先我们在函数fun_a里打上断点,当程序断住时,显示调用栈信息: 44 | 45 | (gdb) bt 46 | #0 fun_a () at a.c:6 47 | #1 0x000109b0 in fun_b () at a.c:12 48 | #2 0x000109e4 in fun_c () at a.c:19 49 | #3 0x00010a18 in fun_d () at a.c:26 50 | #4 0x00010a4c in main () at a.c:33 51 | 52 | 53 | 接下来,用“bt full”命令显示各个函数的局部变量值: 54 | 55 | (gdb) bt full 56 | #0 fun_a () at a.c:6 57 | a = 0 58 | #1 0x000109b0 in fun_b () at a.c:12 59 | b = 1 60 | #2 0x000109e4 in fun_c () at a.c:19 61 | c = 2 62 | #3 0x00010a18 in fun_d () at a.c:26 63 | d = 3 64 | #4 0x00010a4c in main () at a.c:33 65 | var = -1 66 | 67 | 68 | 也可以使用如下“bt full n”,意思是从内向外显示n个栈桢,及其局部变量,例如: 69 | 70 | (gdb) bt full 2 71 | #0 fun_a () at a.c:6 72 | a = 0 73 | #1 0x000109b0 in fun_b () at a.c:12 74 | b = 1 75 | (More stack frames follow...) 76 | 77 | 78 | 而“bt full -n”,意思是从外向内显示n个栈桢,及其局部变量,例如: 79 | 80 | (gdb) bt full -2 81 | #3 0x00010a18 in fun_d () at a.c:26 82 | d = 3 83 | #4 0x00010a4c in main () at a.c:33 84 | var = -1 85 | 86 | 87 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Backtrace.html) 88 | 89 | ## 技巧二 90 | 91 | 如果只是想打印当前函数局部变量的值,可以使用如下命令: 92 | 93 | (gdb) info locals 94 | a = 0 95 | 96 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Frame-Info.html#index-info-locals) 97 | 98 | ## 贡献者 99 | 100 | nanxiao 101 | 102 | xmj 103 | 104 | -------------------------------------------------------------------------------- /src/print-malloc-memory.md: -------------------------------------------------------------------------------- 1 | # 打印程序动态分配内存的信息 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | char *p[10]; 9 | int i = 0; 10 | 11 | for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 12 | { 13 | p[i] = malloc(100000); 14 | } 15 | return 0; 16 | } 17 | 18 | ## 技巧 19 | 用gdb调试程序时,可以用下面的自定义命令,打印程序动态分配内存的信息: 20 | 21 | define mallocinfo 22 | set $__f = fopen("/dev/tty", "w") 23 | call malloc_info(0, $__f) 24 | call fclose($__f) 25 | end 26 | 27 | 以上面程序为例: 28 | 29 | Temporary breakpoint 5, main () at a.c:7 30 | 7 int i = 0; 31 | (gdb) mallocinfo 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | $20 = 0 51 | $21 = 0 52 | (gdb) n 53 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 54 | (gdb) 55 | 11 p[i] = malloc(100000); 56 | (gdb) 57 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 58 | (gdb) 59 | 11 p[i] = malloc(100000); 60 | (gdb) 61 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 62 | (gdb) 63 | 11 p[i] = malloc(100000); 64 | (gdb) 65 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 66 | (gdb) 67 | 11 p[i] = malloc(100000); 68 | (gdb) 69 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 70 | (gdb) 71 | 11 p[i] = malloc(100000); 72 | (gdb) mallocinfo 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | $22 = 0 92 | $23 = 0 93 | (gdb) n 94 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 95 | (gdb) 96 | 11 p[i] = malloc(100000); 97 | (gdb) 98 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 99 | (gdb) 100 | 11 p[i] = malloc(100000); 101 | (gdb) 102 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 103 | (gdb) 104 | 11 p[i] = malloc(100000); 105 | (gdb) 106 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 107 | (gdb) 108 | 11 p[i] = malloc(100000); 109 | (gdb) 110 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 111 | (gdb) 112 | 11 p[i] = malloc(100000); 113 | (gdb) 114 | 9 for (i = 0; i < sizeof(p)/sizeof(p[0]); i++) 115 | (gdb) mallocinfo 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | $24 = 0 135 | $25 = 0 136 | 137 | 可以看到gdb输出了动态分配内存的变化信息。 138 | 参见[stackoverflow](http://stackoverflow.com/questions/1471226/most-tricky-useful-commands-for-gdb-debugger). 139 | 140 | ## 贡献者 141 | 142 | nanxiao 143 | -------------------------------------------------------------------------------- /src/print-process-memory.md: -------------------------------------------------------------------------------- 1 | # 打印进程内存信息 2 | 3 | 4 | 5 | 6 | ## 技巧 7 | 用gdb调试程序时,如果想查看进程的内存映射信息,可以使用“i proc mappings”命令(i是info命令缩写),例如: 8 | 9 | (gdb) i proc mappings 10 | process 27676 flags: 11 | PR_STOPPED Process (LWP) is stopped 12 | PR_ISTOP Stopped on an event of interest 13 | PR_RLC Run-on-last-close is in effect 14 | PR_MSACCT Microstate accounting enabled 15 | PR_PCOMPAT Micro-state accounting inherited on fork 16 | PR_FAULTED : Incurred a traced hardware fault FLTBPT: Breakpoint trap 17 | 18 | Mapped address spaces: 19 | 20 | Start Addr End Addr Size Offset Flags 21 | 0x8046000 0x8047fff 0x2000 0xfffff000 -s--rwx 22 | 0x8050000 0x8050fff 0x1000 0 ----r-x 23 | 0x8060000 0x8060fff 0x1000 0 ----rwx 24 | 0xfee40000 0xfef4efff 0x10f000 0 ----r-x 25 | 0xfef50000 0xfef55fff 0x6000 0 ----rwx 26 | 0xfef5f000 0xfef66fff 0x8000 0x10f000 ----rwx 27 | 0xfef67000 0xfef68fff 0x2000 0 ----rwx 28 | 0xfef70000 0xfef70fff 0x1000 0 ----rwx 29 | 0xfef80000 0xfef80fff 0x1000 0 ---sr-- 30 | 0xfef90000 0xfef90fff 0x1000 0 ----rw- 31 | 0xfefa0000 0xfefa0fff 0x1000 0 ----rw- 32 | 0xfefb0000 0xfefb0fff 0x1000 0 ----rwx 33 | 0xfefc0000 0xfefeafff 0x2b000 0 ----r-x 34 | 0xfeff0000 0xfeff0fff 0x1000 0 ----rwx 35 | 0xfeffb000 0xfeffcfff 0x2000 0x2b000 ----rwx 36 | 0xfeffd000 0xfeffdfff 0x1000 0 ----rwx 37 | 38 | 39 | 40 | 41 | 首先输出了进程的flags,接着是进程的内存映射信息。 42 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/SVR4-Process-Information.html#index-info-proc-exe). 43 | 44 | 此外,也可以用"i files"(还有一个同样作用的命令:“i target”)命令,它可以更详细地输出进程的内存信息,包括引用的动态链接库等等,例如: 45 | 46 | (gdb) i files 47 | Symbols from "/data1/nan/a". 48 | Unix /proc child process: 49 | Using the running image of child Thread 1 (LWP 1) via /proc. 50 | While running this, GDB does not access memory from... 51 | Local exec file: 52 | `/data1/nan/a', file type elf32-i386-sol2. 53 | Entry point: 0x8050950 54 | 0x080500f4 - 0x08050105 is .interp 55 | 0x08050108 - 0x08050114 is .eh_frame_hdr 56 | 0x08050114 - 0x08050218 is .hash 57 | 0x08050218 - 0x08050418 is .dynsym 58 | 0x08050418 - 0x080507e6 is .dynstr 59 | 0x080507e8 - 0x08050818 is .SUNW_version 60 | 0x08050818 - 0x08050858 is .SUNW_versym 61 | 0x08050858 - 0x08050890 is .SUNW_reloc 62 | 0x08050890 - 0x080508c8 is .rel.plt 63 | 0x080508c8 - 0x08050948 is .plt 64 | ...... 65 | 0xfef5fb58 - 0xfef5fc48 is .dynamic in /usr/lib/libc.so.1 66 | 0xfef5fc80 - 0xfef650e2 is .data in /usr/lib/libc.so.1 67 | 0xfef650e2 - 0xfef650e2 is .bssf in /usr/lib/libc.so.1 68 | 0xfef650e8 - 0xfef65be0 is .picdata in /usr/lib/libc.so.1 69 | 0xfef65be0 - 0xfef666a7 is .data1 in /usr/lib/libc.so.1 70 | 0xfef666a8 - 0xfef680dc is .bss in /usr/lib/libc.so.1 71 | 72 | 73 | 74 | 75 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Files.html) 76 | 77 | ## 贡献者 78 | 79 | nanxiao 80 | 81 | -------------------------------------------------------------------------------- /src/print-registers.md: -------------------------------------------------------------------------------- 1 | # 打印寄存器的值 2 | 3 | 4 | 5 | 6 | ## 技巧 7 | 用gdb调试程序时,如果想查看寄存器的值,可以使用“i registers”命令(i是info命令缩写),例如: 8 | 9 | (gdb) i registers 10 | rax 0x7ffff7dd9f60 140737351884640 11 | rbx 0x0 0 12 | rcx 0x0 0 13 | rdx 0x7fffffffe608 140737488348680 14 | rsi 0x7fffffffe5f8 140737488348664 15 | rdi 0x1 1 16 | rbp 0x7fffffffe510 0x7fffffffe510 17 | rsp 0x7fffffffe4c0 0x7fffffffe4c0 18 | r8 0x7ffff7dd8300 140737351877376 19 | r9 0x7ffff7deb9e0 140737351956960 20 | r10 0x7fffffffe360 140737488348000 21 | r11 0x7ffff7a68be0 140737348275168 22 | r12 0x4003e0 4195296 23 | r13 0x7fffffffe5f0 140737488348656 24 | r14 0x0 0 25 | r15 0x0 0 26 | rip 0x4004cd 0x4004cd 27 | eflags 0x206 [ PF IF ] 28 | cs 0x33 51 29 | ss 0x2b 43 30 | ds 0x0 0 31 | es 0x0 0 32 | fs 0x0 0 33 | gs 0x0 0 34 | 以上输出不包括浮点寄存器和向量寄存器的内容。使用“i all-registers”命令,可以输出所有寄存器的内容: 35 | 36 | 37 | (gdb) i all-registers 38 | rax 0x7ffff7dd9f60 140737351884640 39 | rbx 0x0 0 40 | rcx 0x0 0 41 | rdx 0x7fffffffe608 140737488348680 42 | rsi 0x7fffffffe5f8 140737488348664 43 | rdi 0x1 1 44 | rbp 0x7fffffffe510 0x7fffffffe510 45 | rsp 0x7fffffffe4c0 0x7fffffffe4c0 46 | r8 0x7ffff7dd8300 140737351877376 47 | r9 0x7ffff7deb9e0 140737351956960 48 | r10 0x7fffffffe360 140737488348000 49 | r11 0x7ffff7a68be0 140737348275168 50 | r12 0x4003e0 4195296 51 | r13 0x7fffffffe5f0 140737488348656 52 | r14 0x0 0 53 | r15 0x0 0 54 | rip 0x4004cd 0x4004cd 55 | eflags 0x206 [ PF IF ] 56 | cs 0x33 51 57 | ss 0x2b 43 58 | ds 0x0 0 59 | es 0x0 0 60 | fs 0x0 0 61 | gs 0x0 0 62 | st0 0 (raw 0x00000000000000000000) 63 | st1 0 (raw 0x00000000000000000000) 64 | st2 0 (raw 0x00000000000000000000) 65 | st3 0 (raw 0x00000000000000000000) 66 | st4 0 (raw 0x00000000000000000000) 67 | st5 0 (raw 0x00000000000000000000) 68 | st6 0 (raw 0x00000000000000000000) 69 | st7 0 (raw 0x00000000000000000000) 70 | ...... 71 | 72 | 要打印单个寄存器的值,可以使用“i registers regname”或者“p $regname”,例如: 73 | 74 | (gdb) i registers eax 75 | eax 0xf7dd9f60 -136470688 76 | (gdb) p $eax 77 | $1 = -136470688 78 | 79 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Registers.html). 80 | 81 | ## 贡献者 82 | 83 | nanxiao 84 | -------------------------------------------------------------------------------- /src/print-signal.md: -------------------------------------------------------------------------------- 1 | # 信号发生时是否打印信号信息 2 | ## 例子 3 | #include 4 | #include 5 | 6 | void handler(int sig); 7 | 8 | void handler(int sig) 9 | { 10 | signal(sig, handler); 11 | printf("Receive signal: %d\n", sig); 12 | } 13 | 14 | int main(void) { 15 | signal(SIGHUP, handler); 16 | 17 | while (1) 18 | { 19 | sleep(1); 20 | } 21 | return 0; 22 | } 23 | 24 | ## 技巧 25 | 用gdb调试程序时,可以用“`handle signal print/noprint`”命令设置当信号发生时,是否打印信号信息,以上面程序为例: 26 | 27 | (gdb) i signals 28 | Signal Stop Print Pass to program Description 29 | 30 | SIGHUP Yes Yes Yes Hangup 31 | ...... 32 | 33 | (gdb) r 34 | Starting program: /data1/nan/test 35 | [Thread debugging using libthread_db enabled] 36 | [New Thread 1 (LWP 1)] 37 | 38 | Program received signal SIGHUP, Hangup. 39 | [Switching to Thread 1 (LWP 1)] 40 | 0xfeeeae55 in ___nanosleep () from /lib/libc.so.1 41 | (gdb) c 42 | Continuing. 43 | Receive signal: 1 44 | 45 | 可以看到,默认情况下,发生`SIGHUP`信号时,gdb会暂停程序的执行,并打印收到信号的信息。此时需要执行`continue`命令继续程序的执行。 46 | 47 | 接下来用“`handle SIGHUP noprint`”命令设置当`SIGHUP`信号发生时,gdb不打印信号信息,执行如下: 48 | 49 | (gdb) handle SIGHUP noprint 50 | Signal Stop Print Pass to program Description 51 | SIGHUP No No Yes Hangup 52 | (gdb) r 53 | Starting program: /data1/nan/test 54 | [Thread debugging using libthread_db enabled] 55 | Receive signal: 1 56 | 57 | 需要注意的是,设置`noprint`的同时,默认也会设置`nostop`。可以看到,程序收到`SIGHUP`信号发生时,没有暂停,也没有打印信号信息。而是继续执行。 58 | 59 | 如果想恢复之前的行为,用“`handle SIGHUP print`”命令即可。 60 | 61 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Signals.html). 62 | 63 | ## 贡献者 64 | 65 | nanxiao 66 | -------------------------------------------------------------------------------- /src/print-source-lines.md: -------------------------------------------------------------------------------- 1 | # 打印源代码行 2 | 3 | ## 例子 4 | 5 | $ gdb -q `which gdb` 6 | (gdb) l 7 | 15 8 | 16 You should have received a copy of the GNU General Public License 9 | 17 along with this program. If not, see . */ 10 | 18 11 | 19 #include "defs.h" 12 | 20 #include "main.h" 13 | 21 #include 14 | 22 #include "interps.h" 15 | 23 16 | 24 int 17 | 18 | ## 技巧 19 | 20 | 如上所示,在gdb中可以使用`list`(简写为l)命令来显示源代码以及行号。`list`命令可以指定行号,函数: 21 | 22 | (gdb) l 24 23 | (gdb) l main 24 | 25 | 还可以指定向前或向后打印: 26 | 27 | (gdb) l - 28 | (gdb) l + 29 | 30 | 还可以指定范围: 31 | 32 | (gdb) l 1,10 33 | 34 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/List.html#List) 35 | 36 | ## 贡献者 37 | 38 | xmj 39 | 40 | -------------------------------------------------------------------------------- /src/print-static-variables.md: -------------------------------------------------------------------------------- 1 | # 打印静态变量的值 2 | 3 | ## 例子 4 | 5 | /* main.c */ 6 | extern void print_var_1(void); 7 | extern void print_var_2(void); 8 | 9 | int main(void) 10 | { 11 | print_var_1(); 12 | print_var_2(); 13 | return 0; 14 | } 15 | 16 | /* static-1.c */ 17 | #include 18 | 19 | static int var = 1; 20 | 21 | void print_var_1(void) 22 | { 23 | printf("var = %d\n", var); 24 | } 25 | 26 | /* static-2.c */ 27 | #include 28 | 29 | static int var = 2; 30 | 31 | void print_var_2(void) 32 | { 33 | printf("var = %d\n", var); 34 | } 35 | 36 | ## 技巧 37 | 38 | 在gdb中,如果直接打印静态变量,则结果并不一定是你想要的: 39 | 40 | $ gcc -g main.c static-1.c static-2.c 41 | $ gdb -q ./a.out 42 | (gdb) start 43 | (gdb) p var 44 | $1 = 2 45 | 46 | $ gcc -g main.c static-2.c static-1.c 47 | $ gdb -q ./a.out 48 | (gdb) start 49 | (gdb) p var 50 | $1 = 1 51 | 52 | 你可以显式地指定文件名(上下文): 53 | 54 | (gdb) p 'static-1.c'::var 55 | $1 = 1 56 | (gdb) p 'static-2.c'::var 57 | $2 = 2 58 | 59 | 详情参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Variables.html#Variables) 60 | 61 | ## 贡献者 62 | 63 | xmj 64 | 65 | -------------------------------------------------------------------------------- /src/print-struct-with-offset.md: -------------------------------------------------------------------------------- 1 | # 打印一个结构体成员的偏移量 2 | 3 | ## 例子 4 | ``` C 5 | #include 6 | #include 7 | 8 | typedef struct 9 | { 10 | long a; 11 | int b; 12 | int c; 13 | int d; 14 | pthread_mutex_t mutex; 15 | }ex_st; 16 | 17 | typedef struct 18 | { 19 | int a; 20 | long b; 21 | int c; 22 | int d; 23 | pthread_mutex_t mutex; 24 | }ex_st2; 25 | 26 | int main(void) { 27 | ex_st st = {1, 2, 3, 4, PTHREAD_MUTEX_INITIALIZER}; 28 | ex_st2 st2 = {1, 2, 3, 4, PTHREAD_MUTEX_INITIALIZER}; 29 | printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 30 | return 0; 31 | } 32 | ``` 33 | 34 | 35 | ## 技巧 36 | 想要知道结构体成员的偏移量,查看内存空洞可以使用该命令。 37 | 38 | ``` gdb 39 | (gdb) ptype /o st 40 | type = struct { 41 | /* 0 | 8 */ long a; 42 | /* 8 | 4 */ int b; 43 | /* 12 | 4 */ int c; 44 | /* 16 | 4 */ int d; 45 | /* XXX 4-byte hole */ 46 | /* 24 | 40 */ pthread_mutex_t mutex; 47 | 48 | /* total size (bytes): 64 */ 49 | } 50 | (gdb) ptype /o st2 51 | type = struct { 52 | /* 0 | 4 */ int a; 53 | /* XXX 4-byte hole */ 54 | /* 8 | 8 */ long b; 55 | /* 16 | 4 */ int c; 56 | /* 20 | 4 */ int d; 57 | /* 24 | 40 */ pthread_mutex_t mutex; 58 | 59 | /* total size (bytes): 64 */ 60 | } 61 | ``` 62 | 63 | 64 | 65 | 详情参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Symbols.html) 66 | 67 | ## 贡献者 68 | 69 | zhuizhuhaomeng 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/print-threads.md: -------------------------------------------------------------------------------- 1 | # 查看线程信息 2 | ## 例子 3 | #include 4 | #include 5 | void *thread_func(void *p_arg) 6 | { 7 | while (1) 8 | { 9 | printf("%s\n", (char*)p_arg); 10 | sleep(10); 11 | } 12 | } 13 | int main(void) 14 | { 15 | pthread_t t1, t2; 16 | 17 | pthread_create(&t1, NULL, thread_func, "Thread 1"); 18 | pthread_create(&t2, NULL, thread_func, "Thread 2"); 19 | 20 | sleep(1000); 21 | return; 22 | } 23 | 24 | ## 技巧 25 | 用gdb调试多线程程序,可以用“i threads”命令(i是info命令缩写)查看所有线程的信息,以上面程序为例(运行平台为Linux,CPU为X86_64): 26 | 27 | (gdb) i threads 28 | Id Target Id Frame 29 | 3 Thread 0x7ffff6e2b700 (LWP 31773) 0x00007ffff7915911 in clone () from /lib64/libc.so.6 30 | 2 Thread 0x7ffff782c700 (LWP 31744) 0x00007ffff78d9bcd in nanosleep () from /lib64/libc.so.6 31 | * 1 Thread 0x7ffff7fe9700 (LWP 31738) main () at a.c:18 32 | 33 | 第一项(Id):是gdb标示每个线程的唯一ID:1,2等等。 34 | 第二项(Target Id):是具体系统平台用来标示每个线程的ID,不同平台信息可能会不同。 像当前Linux平台显示的就是: Thread 0x7ffff6e2b700 (LWP 31773)。 35 | 第三项(Frame):显示的是线程执行到哪个函数。 36 | 前面带“*”表示的是“current thread”,可以理解为gdb调试多线程程序时,选择的一个“默认线程”。 37 | 38 | 再以Solaris平台(CPU为X86_64)为例,可以看到显示信息会略有不同: 39 | 40 | (gdb) i threads 41 | [New Thread 2 (LWP 2)] 42 | [New Thread 3 (LWP 3)] 43 | Id Target Id Frame 44 | 6 Thread 3 (LWP 3) 0xfeec870d in _thr_setup () from /usr/lib/libc.so.1 45 | 5 Thread 2 (LWP 2) 0xfefc9661 in elf_find_sym () from /usr/lib/ld.so.1 46 | 4 LWP 3 0xfeec870d in _thr_setup () from /usr/lib/libc.so.1 47 | 3 LWP 2 0xfefc9661 in elf_find_sym () from /usr/lib/ld.so.1 48 | * 2 Thread 1 (LWP 1) main () at a.c:18 49 | 1 LWP 1 main () at a.c:18 50 | 51 | 52 | 也可以用“i threads [Id...]”指定打印某些线程的信息,例如: 53 | 54 | (gdb) i threads 1 2 55 | Id Target Id Frame 56 | 2 Thread 0x7ffff782c700 (LWP 12248) 0x00007ffff78d9bcd in nanosleep () from /lib64/libc.so.6 57 | * 1 Thread 0x7ffff7fe9700 (LWP 12244) main () at a.c:18 58 | 59 | 使用"thread thread-id"实现不同线程之间的切换,查看指定线程的堆栈信息 60 | 61 | ``` 62 | (gdb) thread 2 63 | [Switching to thread 2 (Thread 0x7ffff782c700 (LWP 12248))]... 64 | ``` 65 | 66 | 使用"thread apply [thread-id-list] [all] args"可以在多个线程上执行命令,例如:`thread apply all bt`可以查看所有线程的堆栈信息。 67 | 68 | ``` 69 | (gdb) thread apply all bt 70 | ``` 71 | 72 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Threads.html). 73 | 74 | ## 贡献者 75 | 76 | nanxiao, shanbaoyin 77 | -------------------------------------------------------------------------------- /src/print-variable-info.md: -------------------------------------------------------------------------------- 1 | # 打印变量的类型和所在文件 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | struct child { 8 | char name[10]; 9 | enum { boy, girl } gender; 10 | }; 11 | 12 | struct child he = { "Tom", boy }; 13 | 14 | int main (void) 15 | { 16 | static struct child she = { "Jerry", girl }; 17 | printf ("Hello %s %s.\n", he.gender == boy ? "boy" : "girl", he.name); 18 | printf ("Hello %s %s.\n", she.gender == boy ? "boy" : "girl", she.name); 19 | return 0; 20 | } 21 | 22 | ## 技巧 23 | 24 | 在gdb中,可以使用如下命令查看变量的类型: 25 | 26 | (gdb) whatis he 27 | type = struct child 28 | 29 | 如果想查看详细的类型信息: 30 | 31 | (gdb) ptype he 32 | type = struct child { 33 | char name[10]; 34 | enum {boy, girl} gender; 35 | } 36 | 37 | 如果想查看定义该变量的文件: 38 | 39 | (gdb) i variables he 40 | All variables matching regular expression "he": 41 | 42 | File variable.c: 43 | struct child he; 44 | 45 | Non-debugging symbols: 46 | 0x0000000000402030 she 47 | 0x00007ffff7dd3380 __check_rhosts_file 48 | 49 | 哦,gdb会显示所有包含(匹配)该表达式的变量。如果只想查看完全匹配给定名字的变量: 50 | 51 | (gdb) i variables ^he$ 52 | All variables matching regular expression "^he$": 53 | 54 | File variable.c: 55 | struct child he; 56 | 57 | 注:`info variables`不会显示局部变量,即使是static的也没有太多的信息。 58 | 59 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Symbols.html) 60 | 61 | ## 贡献者 62 | 63 | xmj 64 | 65 | -------------------------------------------------------------------------------- /src/quit-gdb-silently.md: -------------------------------------------------------------------------------- 1 | # gdb退出时不显示提示信息 2 | 3 | 4 | ## 技巧 5 | gdb在退出时会提示: 6 | 7 | A debugging session is active. 8 | 9 | Inferior 1 [process 29686 ] will be killed. 10 | 11 | Quit anyway? (y or n) n 12 | 13 | 14 | 如果不想显示这个信息,则可以在gdb中使用如下命令把提示信息关掉: 15 | 16 | (gdb) set confirm off 17 | 18 | 也可以把这个命令加到.gdbinit文件里。 19 | 20 | ## 贡献者 21 | 22 | nanxiao 23 | 24 | -------------------------------------------------------------------------------- /src/run-cd-pwd.md: -------------------------------------------------------------------------------- 1 | # 在gdb中执行cd和pwd命令 2 | 3 | ## 技巧 4 | 5 | 是的,gdb确实支持这两个命令,虽然我没有想到它们有什么特别的用处。 6 | 7 | 也许,当你启动gdb之后,发现需要切换工作目录,但又不想退出gdb的时候: 8 | 9 | (gdb) pwd 10 | Working directory /home/xmj. 11 | (gdb) cd tmp 12 | Working directory /home/xmj/tmp. 13 | 14 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Working-Directory.html#Working-Directory) 15 | 16 | ## 贡献者 17 | 18 | xmj 19 | 20 | -------------------------------------------------------------------------------- /src/run-shell-command.md: -------------------------------------------------------------------------------- 1 | # 在gdb中执行shell命令和make 2 | 3 | ## 技巧 4 | 5 | 你可以不离开gdb,直接执行shell命令,比如: 6 | 7 | (gdb) shell ls 8 | 9 | 或 10 | 11 | (gdb) !ls 12 | 13 | 这里,"!"和命令之间不需要有空格(即,有也成)。 14 | 15 | 特别是当你在构建环境(build目录)下调试程序的时候,可以直接运行make: 16 | 17 | (gdb) make CFLAGS="-g -O0" 18 | 19 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Shell-Commands.html#Shell-Commands) 20 | 21 | ## 贡献者 22 | 23 | xmj 24 | 25 | -------------------------------------------------------------------------------- /src/save-breakpoints.md: -------------------------------------------------------------------------------- 1 | # 保存已经设置的断点 2 | 3 | ## 例子 4 | 5 | $ gdb -q `which gdb` 6 | Reading symbols from /home/xmj/install/binutils-trunk/bin/gdb...done. 7 | (gdb) b gdb_main 8 | Breakpoint 1 at 0x5a7af0: file /home/xmj/project/binutils-trunk/gdb/main.c, line 1061. 9 | (gdb) b captured_main 10 | Breakpoint 2 at 0x5a6bd0: file /home/xmj/project/binutils-trunk/gdb/main.c, line 310. 11 | (gdb) b captured_command_loop 12 | Breakpoint 3 at 0x5a68b0: file /home/xmj/project/binutils-trunk/gdb/main.c, line 261. 13 | 14 | ## 技巧 15 | 16 | 在gdb中,可以使用如下命令将设置的断点保存下来: 17 | 18 |
(gdb) save breakpoints file-name-to-save
19 | 20 | 下此调试时,可以使用如下命令批量设置保存的断点: 21 | 22 |
(gdb) source file-name-to-save
23 | 24 | (gdb) info breakpoints 25 | Num Type Disp Enb Address What 26 | 1 breakpoint keep y 0x00000000005a7af0 in gdb_main at /home/xmj/project/binutils-trunk/gdb/main.c:1061 27 | 2 breakpoint keep y 0x00000000005a6bd0 in captured_main at /home/xmj/project/binutils-trunk/gdb/main.c:310 28 | 3 breakpoint keep y 0x00000000005a68b0 in captured_command_loop at /home/xmj/project/binutils-trunk/gdb/main.c:261 29 | 30 | 详情参见[gdb手册](https://sourceware.org/gdb/download/onlinedocs/gdb/Save-Breakpoints.html#Save-Breakpoints) 31 | 32 | ## 贡献者 33 | 34 | xmj 35 | 36 | -------------------------------------------------------------------------------- /src/save-history-commands.md: -------------------------------------------------------------------------------- 1 | # 保存历史命令 2 | 3 | ## 技巧 4 | 5 | 在gdb中,缺省是不保存历史命令的。你可以通过如下命令来设置成保存历史命令: 6 | 7 | (gdb) set history save on 8 | 9 | 但是,历史命令是缺省保存在了当前目录下的.gdb_history文件中。可以通过如下命令来设置要保存的文件名和路径: 10 | 11 | (gdb) set history filename fname 12 | 13 | 现在,我们把这两个命令放到$HOME/.gdbinit文件中: 14 | 15 | set history filename ~/.gdb_history 16 | set history save on 17 | 18 | 好了,下次启动gdb时,你就可以直接查找使用之前的历史命令了。 19 | 20 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Command-History.html#Command-History) 21 | 22 | ## 贡献者 23 | 24 | xmj 25 | 26 | -------------------------------------------------------------------------------- /src/select-frame.md: -------------------------------------------------------------------------------- 1 | # 选择函数堆栈帧 2 | ## 例子 3 | #include 4 | 5 | int func1(int a) 6 | { 7 | return 2 * a; 8 | } 9 | 10 | int func2(int a) 11 | { 12 | int c = 0; 13 | c = 2 * func1(a); 14 | return c; 15 | } 16 | 17 | int func3(int a) 18 | { 19 | int c = 0; 20 | c = 2 * func2(a); 21 | return c; 22 | } 23 | 24 | int main(void) 25 | { 26 | printf("%d\n", func3(10)); 27 | return 0; 28 | } 29 | 30 | ## 技巧 31 | 用gdb调试程序时,当程序暂停后,可以用“`frame n`”命令选择函数堆栈帧,其中`n`是层数。以上面程序为例: 32 | 33 | (gdb) b test.c:5 34 | Breakpoint 1 at 0x40053d: file test.c, line 5. 35 | (gdb) r 36 | Starting program: /home/nanxiao/test 37 | 38 | Breakpoint 1, func1 (a=10) at test.c:5 39 | 5 return 2 * a; 40 | (gdb) bt 41 | #0 func1 (a=10) at test.c:5 42 | #1 0x0000000000400560 in func2 (a=10) at test.c:11 43 | #2 0x0000000000400586 in func3 (a=10) at test.c:18 44 | #3 0x000000000040059e in main () at test.c:24 45 | (gdb) frame 2 46 | #2 0x0000000000400586 in func3 (a=10) at test.c:18 47 | 18 c = 2 * func2(a); 48 | 49 | 可以看到程序断住后,最内层的函数帧为第`0`帧。执行`frame 2`命令后,当前的堆栈帧变成了`fun3`的函数帧。 50 | 51 | 也可以用“`frame addr`”命令选择函数堆栈帧,其中`addr`是堆栈地址。仍以上面程序为例: 52 | 53 | (gdb) frame 2 54 | #2 0x0000000000400586 in func3 (a=10) at test.c:18 55 | 18 c = 2 * func2(a); 56 | (gdb) i frame 57 | Stack level 2, frame at 0x7fffffffe590: 58 | rip = 0x400586 in func3 (test.c:18); saved rip = 0x40059e 59 | called by frame at 0x7fffffffe5a0, caller of frame at 0x7fffffffe568 60 | source language c. 61 | Arglist at 0x7fffffffe580, args: a=10 62 | Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590 63 | Saved registers: 64 | rbp at 0x7fffffffe580, rip at 0x7fffffffe588 65 | (gdb) frame 0x7fffffffe568 66 | #1 0x0000000000400560 in func2 (a=10) at test.c:11 67 | 11 c = 2 * func1(a); 68 | 使用“`i frame`”命令可以知道`0x7fffffffe568`是`func2`的函数堆栈帧地址,使用“`frame 0x7fffffffe568`”可以切换到`func2`的函数堆栈帧。 69 | 70 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Selection.html#Selection). 71 | 72 | ## 贡献者 73 | 74 | nanxiao 75 | -------------------------------------------------------------------------------- /src/send-signal.md: -------------------------------------------------------------------------------- 1 | # 给程序发送信号 2 | ## 例子 3 | #include 4 | #include 5 | 6 | void handler(int sig); 7 | 8 | void handler(int sig) 9 | { 10 | signal(sig, handler); 11 | printf("Receive signal: %d\n", sig); 12 | } 13 | 14 | int main(void) { 15 | signal(SIGHUP, handler); 16 | 17 | while (1) 18 | { 19 | sleep(1); 20 | } 21 | return 0; 22 | } 23 | 24 | ## 技巧 25 | 用gdb调试程序的过程中,当被调试程序停止后,可以用“`signal signal_name`”命令让程序继续运行,但会立即给程序发送信号。以上面程序为例: 26 | 27 | (gdb) r 28 | `/data1/nan/test' has changed; re-reading symbols. 29 | Starting program: /data1/nan/test 30 | [Thread debugging using libthread_db enabled] 31 | ^C[New Thread 1 (LWP 1)] 32 | 33 | Program received signal SIGINT, Interrupt. 34 | [Switching to Thread 1 (LWP 1)] 35 | 0xfeeeae55 in ___nanosleep () from /lib/libc.so.1 36 | (gdb) signal SIGHUP 37 | Continuing with signal SIGHUP. 38 | Receive signal: 1 39 | 40 | 可以看到,当程序暂停后,执行`signal SIGHUP`命令,gdb会发送信号给程序处理。 41 | 42 | 可以使用“`signal 0`”命令使程序重新运行,但不发送任何信号给进程。仍以上面程序为例: 43 | 44 | Program received signal SIGHUP, Hangup. 45 | 0xfeeeae55 in ___nanosleep () from /lib/libc.so.1 46 | (gdb) signal 0 47 | Continuing with no signal. 48 | 49 | 可以看到,`SIGHUP`信号发生时,gdb停住了程序,但是由于执行了“`signal 0`”命令,所以程序重新运行后,并没有收到`SIGHUP`信号。 50 | 51 | 使用`signal`命令和在shell环境使用`kill`命令给程序发送信号的区别在于:在shell环境使用`kill`命令给程序发送信号,gdb会根据当前的设置决定是否把信号发送给进程,而使用`signal`命令则直接把信号发给进程。 52 | 53 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Signaling.html#Signaling). 54 | 55 | ## 贡献者 56 | 57 | nanxiao 58 | -------------------------------------------------------------------------------- /src/set-condition-break.md: -------------------------------------------------------------------------------- 1 | # 设置条件断点 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int main(void) 8 | { 9 | int i = 0; 10 | int sum = 0; 11 | 12 | for (i = 1; i <= 200; i++) 13 | { 14 | sum += i; 15 | } 16 | 17 | printf("%d\n", sum); 18 | return 0; 19 | } 20 | 21 | 22 | 23 | ## 技巧 24 | 25 | gdb可以设置条件断点,也就是只有在条件满足时,断点才会被触发,命令是“`break … if cond`”。以上面程序为例: 26 | 27 | (gdb) start 28 | Temporary breakpoint 1 at 0x4004cc: file a.c, line 5. 29 | Starting program: /data2/home/nanxiao/a 30 | 31 | Temporary breakpoint 1, main () at a.c:5 32 | 5 int i = 0; 33 | (gdb) b 10 if i==101 34 | Breakpoint 2 at 0x4004e3: file a.c, line 10. 35 | (gdb) r 36 | Starting program: /data2/home/nanxiao/a 37 | 38 | Breakpoint 2, main () at a.c:10 39 | 10 sum += i; 40 | (gdb) p sum 41 | $1 = 5050 42 | 43 | 可以看到设定断点只在`i`的值为`101`时触发,此时打印`sum`的值为`5050`。 44 | 45 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Breaks.html) 46 | 47 | ## 贡献者 48 | 49 | nanxiao 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/set-debug-entry-values.md: -------------------------------------------------------------------------------- 1 | # 打印尾调用堆栈帧信息 2 | ## 例子 3 | #include 4 | void a(void) 5 | { 6 | printf("Tail call frame\n"); 7 | } 8 | 9 | void b(void) 10 | { 11 | a(); 12 | } 13 | 14 | void c(void) 15 | { 16 | b(); 17 | } 18 | 19 | int main(void) 20 | { 21 | c(); 22 | return 0; 23 | } 24 | 25 | ## 技巧 26 | 当一个函数最后一条指令是调用另外一个函数时,开启优化选项的编译器常常以最后被调用的函数返回值作为调用者的返回值,这称之为“尾调用(Tail call)”。以上面程序为例,编译程序(使用‘-O’): 27 | 28 | gcc -g -O -o test test.c 29 | 查看`main`函数汇编代码: 30 | 31 | (gdb) disassemble main 32 | Dump of assembler code for function main: 33 | 0x0000000000400565 <+0>: sub $0x8,%rsp 34 | 0x0000000000400569 <+4>: callq 0x400536 35 | 0x000000000040056e <+9>: mov $0x0,%eax 36 | 0x0000000000400573 <+14>: add $0x8,%rsp 37 | 0x0000000000400577 <+18>: retq 38 | 可以看到`main`函数直接调用了函数`a`,根本看不到函数`b`和函数`c`的影子。 39 | 40 | 在函数`a`入口处打上断点,程序停止后,打印堆栈帧信息: 41 | 42 | (gdb) i frame 43 | Stack level 0, frame at 0x7fffffffe590: 44 | rip = 0x400536 in a (test.c:4); saved rip = 0x40056e 45 | called by frame at 0x7fffffffe5a0 46 | source language c. 47 | Arglist at 0x7fffffffe580, args: 48 | Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590 49 | Saved registers: 50 | rip at 0x7fffffffe588 51 | 看不到尾调用的相关信息。 52 | 53 | 可以设置“`debug entry-values`”选项为非0的值,这样除了输出正常的函数堆栈帧信息以外,还可以输出尾调用的相关信息: 54 | 55 | (gdb) set debug entry-values 1 56 | (gdb) b test.c:4 57 | Breakpoint 1 at 0x400536: file test.c, line 4. 58 | (gdb) r 59 | Starting program: /home/nanxiao/test 60 | 61 | Breakpoint 1, a () at test.c:4 62 | 4 { 63 | (gdb) i frame 64 | tailcall: initial: 65 | Stack level 0, frame at 0x7fffffffe590: 66 | rip = 0x400536 in a (test.c:4); saved rip = 0x40056e 67 | called by frame at 0x7fffffffe5a0 68 | source language c. 69 | Arglist at 0x7fffffffe580, args: 70 | Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590 71 | Saved registers: 72 | rip at 0x7fffffffe588 73 | 74 | 可以看到输出了“`tailcall: initial:`”信息。 75 | 76 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Tail-Call-Frames.html). 77 | 78 | ## 贡献者 79 | 80 | nanxiao 81 | -------------------------------------------------------------------------------- /src/set-detach-on-fork.md: -------------------------------------------------------------------------------- 1 | # 同时调试父进程和子进程 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | pid_t pid; 10 | 11 | pid = fork(); 12 | if (pid < 0) 13 | { 14 | exit(1); 15 | } 16 | else if (pid > 0) 17 | { 18 | printf("Parent\n"); 19 | exit(0); 20 | } 21 | printf("Child\n"); 22 | return 0; 23 | } 24 | 25 | 26 | 27 | ## 技巧 28 | 29 | 在调试多进程程序时,gdb默认只会追踪父进程的运行,而子进程会独立运行,gdb不会控制。以上面程序为例: 30 | 31 | (gdb) start 32 | Temporary breakpoint 1 at 0x40055c: file a.c, line 7. 33 | Starting program: /data2/home/nanxiao/a 34 | 35 | Temporary breakpoint 1, main () at a.c:7 36 | 7 pid = fork(); 37 | (gdb) n 38 | 8 if (pid < 0) 39 | (gdb) Child 40 | 41 | 12 else if (pid > 0) 42 | (gdb) 43 | 14 printf("Parent\n"); 44 | (gdb) 45 | Parent 46 | 15 exit(0); 47 | 48 | 可以看到当单步执行到第8行时,程序打印出“Child” ,证明子进程已经开始独立运行。 49 | 50 | 如果要同时调试父进程和子进程,可以使用“`set detach-on-fork off`”(默认`detach-on-fork`是`on`)命令,这样gdb就能同时调试父子进程,并且在调试一个进程时,另外一个进程处于挂起状态。仍以上面程序为例: 51 | 52 | (gdb) set detach-on-fork off 53 | (gdb) start 54 | Temporary breakpoint 1 at 0x40055c: file a.c, line 7. 55 | Starting program: /data2/home/nanxiao/a 56 | 57 | Temporary breakpoint 1, main () at a.c:7 58 | 7 pid = fork(); 59 | (gdb) n 60 | [New process 1050] 61 | 8 if (pid < 0) 62 | (gdb) 63 | 12 else if (pid > 0) 64 | (gdb) i inferior 65 | Num Description Executable 66 | 2 process 1050 /data2/home/nanxiao/a 67 | * 1 process 1046 /data2/home/nanxiao/a 68 | (gdb) n 69 | 14 printf("Parent\n"); 70 | (gdb) n 71 | Parent 72 | 15 exit(0); 73 | (gdb) 74 | [Inferior 1 (process 1046) exited normally] 75 | (gdb) 76 | The program is not being run. 77 | (gdb) i inferiors 78 | Num Description Executable 79 | 2 process 1050 /data2/home/nanxiao/a 80 | * 1 /data2/home/nanxiao/a 81 | (gdb) inferior 2 82 | [Switching to inferior 2 [process 1050] (/data2/home/nanxiao/a)] 83 | [Switching to thread 2 (process 1050)] 84 | #0 0x00007ffff7af6cad in fork () from /lib64/libc.so.6 85 | (gdb) bt 86 | #0 0x00007ffff7af6cad in fork () from /lib64/libc.so.6 87 | #1 0x0000000000400561 in main () at a.c:7 88 | (gdb) n 89 | Single stepping until exit from function fork, 90 | which has no line number information. 91 | main () at a.c:8 92 | 8 if (pid < 0) 93 | (gdb) 94 | 12 else if (pid > 0) 95 | (gdb) 96 | 17 printf("Child\n"); 97 | (gdb) 98 | Child 99 | 18 return 0; 100 | (gdb) 101 | 102 | 103 | 104 | 在使用“`set detach-on-fork off`”命令后,用“`i inferiors`”(`i`是`info`命令缩写)查看进程状态,可以看到父子进程都在被gdb调试的状态,前面显示“*”是正在调试的进程。当父进程退出后,用“`inferior infno`”切换到子进程去调试。 105 | 106 | 这个命令目前Linux支持,其它很多操作系统都不支持,使用时请注意。参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Forks.html) 107 | 108 | 此外,如果想让父子进程都同时运行,可以使用“`set schedule-multiple on`”(默认`schedule-multiple`是`off`)命令,仍以上述代码为例: 109 | 110 | (gdb) set detach-on-fork off 111 | (gdb) set schedule-multiple on 112 | (gdb) start 113 | Temporary breakpoint 1 at 0x40059c: file a.c, line 7. 114 | Starting program: /data2/home/nanxiao/a 115 | 116 | Temporary breakpoint 1, main () at a.c:7 117 | 7 pid = fork(); 118 | (gdb) n 119 | [New process 26597] 120 | Child 121 | 可以看到打印出了“Child”,证明子进程也在运行了。 122 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/All_002dStop-Mode.html#All_002dStop-Mode) 123 | ## 贡献者 124 | 125 | nanxiao 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/set-disassembly-flavor.md: -------------------------------------------------------------------------------- 1 | # 设置汇编指令格式 2 | 3 | ## 例子 4 | 5 | #include 6 | int global_var; 7 | 8 | void change_var(){ 9 | global_var=100; 10 | } 11 | 12 | int main(void){ 13 | change_var(); 14 | return 0; 15 | } 16 | 17 | 18 | ## 技巧 19 | 20 | 在Intel x86处理器上,gdb默认显示汇编指令格式是AT&T格式。例如: 21 | 22 | (gdb) disassemble main 23 | Dump of assembler code for function main: 24 | 0x08050c0f <+0>: push %ebp 25 | 0x08050c10 <+1>: mov %esp,%ebp 26 | 0x08050c12 <+3>: call 0x8050c00 27 | 0x08050c17 <+8>: mov $0x0,%eax 28 | 0x08050c1c <+13>: pop %ebp 29 | 0x08050c1d <+14>: ret 30 | End of assembler dump. 31 | 32 | 33 | 34 | 可以用“set disassembly-flavor”命令将格式改为intel格式: 35 | 36 | (gdb) set disassembly-flavor intel 37 | (gdb) disassemble main 38 | Dump of assembler code for function main: 39 | 0x08050c0f <+0>: push ebp 40 | 0x08050c10 <+1>: mov ebp,esp 41 | 0x08050c12 <+3>: call 0x8050c00 42 | 0x08050c17 <+8>: mov eax,0x0 43 | 0x08050c1c <+13>: pop ebp 44 | 0x08050c1d <+14>: ret 45 | End of assembler dump. 46 | 47 | 48 | 49 | 目前“set disassembly-flavor”命令只能用在Intel x86处理器上,并且取值只有“intel”和“att”。 50 | 51 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Machine-Code.html) 52 | 53 | ## 贡献者 54 | 55 | nanxiao 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/set-follow-fork-mode-child.md: -------------------------------------------------------------------------------- 1 | # 调试子进程 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) { 10 | pid_t pid; 11 | 12 | pid = fork(); 13 | if (pid < 0) 14 | { 15 | exit(1); 16 | } 17 | else if (pid > 0) 18 | { 19 | exit(0); 20 | } 21 | printf("hello world\n"); 22 | return 0; 23 | } 24 | 25 | 26 | ## 技巧 27 | 28 | 在调试多进程程序时,gdb默认会追踪父进程。例如: 29 | 30 | (gdb) start 31 | Temporary breakpoint 1 at 0x40055c: file a.c, line 8. 32 | Starting program: /data2/home/nanxiao/a 33 | 34 | Temporary breakpoint 1, main () at a.c:8 35 | 8 pid = fork(); 36 | (gdb) n 37 | 9 if (pid < 0) 38 | (gdb) hello world 39 | 40 | 13 else if (pid > 0) 41 | (gdb) 42 | 15 exit(0); 43 | (gdb) 44 | [Inferior 1 (process 12786) exited normally] 45 | 46 | 47 | 48 | 49 | 50 | 可以看到程序执行到第15行:父进程退出。 51 | 52 | 如果要调试子进程,要使用如下命令:“set follow-fork-mode child”,例如: 53 | 54 | (gdb) set follow-fork-mode child 55 | (gdb) start 56 | Temporary breakpoint 1 at 0x40055c: file a.c, line 8. 57 | Starting program: /data2/home/nanxiao/a 58 | 59 | Temporary breakpoint 1, main () at a.c:8 60 | 8 pid = fork(); 61 | (gdb) n 62 | [New process 12241] 63 | [Switching to process 12241] 64 | 9 if (pid < 0) 65 | (gdb) 66 | 13 else if (pid > 0) 67 | (gdb) 68 | 17 printf("hello world\n"); 69 | (gdb) 70 | hello world 71 | 18 return 0; 72 | 73 | 74 | 可以看到程序执行到第17行:子进程打印“hello world”。 75 | 76 | 这个命令目前Linux支持,其它很多操作系统都不支持,使用时请注意。参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Forks.html) 77 | 78 | ## 贡献者 79 | 80 | nanxiao 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/set-io-tty.md: -------------------------------------------------------------------------------- 1 | # 指定程序的输入输出设备 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int main(void) 8 | { 9 | int i; 10 | 11 | for (i = 0; i < 100; i++) 12 | { 13 | printf("i = %d\n", i); 14 | } 15 | 16 | return 0; 17 | } 18 | 19 | ## 技巧 20 | 21 | 在gdb中,缺省情况下程序的输入输出是和gdb使用同一个终端。你也可以为程序指定一个单独的输入输出终端。 22 | 23 | 首先,打开一个新终端,使用如下命令获得设备文件名: 24 | 25 | $ tty 26 | /dev/pts/2 27 | 28 | 然后,通过命令行选项指定程序的输入输出设备: 29 | 30 | $ gdb -tty /dev/pts/2 ./a.out 31 | (gdb) r 32 | 33 | 或者,在gdb中,使用命令进行设置: 34 | 35 | (gdb) tty /dev/pts/2 36 | 37 | 详情参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Input_002fOutput.html#index-tty) 38 | 39 | ## 贡献者 40 | 41 | xmj 42 | 43 | -------------------------------------------------------------------------------- /src/set-logging.md: -------------------------------------------------------------------------------- 1 | # 记录执行gdb的过程 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | char str1[] = "abcd"; 9 | wchar_t str2[] = L"abcd"; 10 | 11 | return 0; 12 | } 13 | 14 | ## 技巧 15 | 用gdb调试程序时,可以使用“`set logging on`”命令把执行gdb的过程记录下来,方便以后自己参考或是别人帮忙分析。默认的日志文件是“`gdb.txt`”,也可以用“`set logging file file`”改成别的名字。以上面程序为例: 16 | 17 | (gdb) set logging file log.txt 18 | (gdb) set logging on 19 | Copying output to log.txt. 20 | (gdb) start 21 | Temporary breakpoint 1 at 0x8050abe: file a.c, line 6. 22 | Starting program: /data1/nan/a 23 | [Thread debugging using libthread_db enabled] 24 | [New Thread 1 (LWP 1)] 25 | [Switching to Thread 1 (LWP 1)] 26 | 27 | Temporary breakpoint 1, main () at a.c:6 28 | 6 char str1[] = "abcd"; 29 | (gdb) n 30 | 7 wchar_t str2[] = L"abcd"; 31 | (gdb) x/s str1 32 | 0x804779f: "abcd" 33 | (gdb) n 34 | 9 return 0; 35 | (gdb) x/ws str2 36 | 0x8047788: U"abcd" 37 | (gdb) q 38 | A debugging session is active. 39 | 40 | Inferior 1 [process 9931 ] will be killed. 41 | 42 | Quit anyway? (y or n) y 43 | 44 | 执行完后,查看log.txt文件: 45 | 46 | bash-3.2# cat log.txt 47 | Temporary breakpoint 1 at 0x8050abe: file a.c, line 6. 48 | Starting program: /data1/nan/a 49 | [Thread debugging using libthread_db enabled] 50 | [New Thread 1 (LWP 1)] 51 | [Switching to Thread 1 (LWP 1)] 52 | 53 | Temporary breakpoint 1, main () at a.c:6 54 | 6 char str1[] = "abcd"; 55 | 7 wchar_t str2[] = L"abcd"; 56 | 0x804779f: "abcd" 57 | 9 return 0; 58 | 0x8047788: U"abcd" 59 | A debugging session is active. 60 | 61 | Inferior 1 [process 9931 ] will be killed. 62 | 63 | Quit anyway? (y or n) 64 | 可以看到log.txt详细地记录了gdb的执行过程。 65 | 66 | 此外“`set logging overwrite on`”命令可以让输出覆盖之前的日志文件;而 “`set logging redirect on`”命令会让gdb的日志不会打印在终端。 67 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Logging-Output.html). 68 | 69 | ## 贡献者 70 | 71 | nanxiao 72 | -------------------------------------------------------------------------------- /src/set-pagination-off.md: -------------------------------------------------------------------------------- 1 | # 输出信息多时不会暂停输出 2 | 3 | ## 技巧 4 | 有时当gdb输出信息较多时,gdb会暂停输出,并会打印“`---Type to continue, or q to quit---`”这样的提示信息,如下面所示: 5 | 6 | 81 process 2639102 0xff04af84 in __lwp_park () from /usr/lib/libc.so.1 7 | 80 process 2573566 0xff04af84 in __lwp_park () from /usr/lib/libc.so.1 8 | ---Type to continue, or q to quit---Quit 9 | 10 | 11 | 12 | 解决办法是使用“`set pagination off`”或者“`set height 0`”命令。这样gdb就会全部输出,中间不会暂停。 13 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Screen-Size.html). 14 | 15 | ## 贡献者 16 | 17 | nanxiao 18 | -------------------------------------------------------------------------------- /src/set-print-pretty-on.md: -------------------------------------------------------------------------------- 1 | # 每行打印一个结构体成员 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | 8 | typedef struct 9 | { 10 | int a; 11 | int b; 12 | int c; 13 | int d; 14 | pthread_mutex_t mutex; 15 | }ex_st; 16 | 17 | int main(void) { 18 | ex_st st = {1, 2, 3, 4, PTHREAD_MUTEX_INITIALIZER}; 19 | printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 20 | return 0; 21 | } 22 | 23 | 24 | 25 | ## 技巧 26 | 27 | 默认情况下,gdb以一种“紧凑”的方式打印结构体。以上面代码为例: 28 | 29 | (gdb) n 30 | 15 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 31 | (gdb) p st 32 | $1 = {a = 1, b = 2, c = 3, d = 4, mutex = {__data = {__lock = 0, __count = 0, __owner = 0, __nusers = 0, __kind = 0, 33 | __spins = 0, __list = {__prev = 0x0, __next = 0x0}}, __size = '\000' , __align = 0}} 34 | 35 | 36 | 37 | 38 | 39 | 可以看到结构体的显示很混乱,尤其是结构体里还嵌套着其它结构体时。 40 | 41 | 可以执行“set print pretty on”命令,这样每行只会显示结构体的一名成员,而且还会根据成员的定义层次进行缩进: 42 | 43 | (gdb) set print pretty on 44 | (gdb) p st 45 | $2 = { 46 | a = 1, 47 | b = 2, 48 | c = 3, 49 | d = 4, 50 | mutex = { 51 | __data = { 52 | __lock = 0, 53 | __count = 0, 54 | __owner = 0, 55 | __nusers = 0, 56 | __kind = 0, 57 | __spins = 0, 58 | __list = { 59 | __prev = 0x0, 60 | __next = 0x0 61 | } 62 | }, 63 | __size = '\000' , 64 | __align = 0 65 | } 66 | } 67 | 68 | 69 | 70 | 71 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Print-Settings.html#index-print-settings) 72 | 73 | ## 贡献者 74 | 75 | nanxiao 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/set-program-args.md: -------------------------------------------------------------------------------- 1 | # 设置被调试程序的参数 2 | 3 | ## 技巧 4 | 5 | 可以在gdb启动时,通过选项指定被调试程序的参数,例如: 6 | 7 | $ gdb -args ./a.out a b c 8 | 9 | 也可以在gdb中,通过命令来设置,例如: 10 | 11 | (gdb) set args a b c 12 | (gdb) show args 13 | Argument list to give program being debugged when it is started is "a b c". 14 | 15 | 也可以在运行程序时,直接指定: 16 | 17 | (gdb) r a b 18 | Starting program: /home/xmj/tmp/a.out a b 19 | (gdb) show args 20 | Argument list to give program being debugged when it is started is "a b". 21 | (gdb) r 22 | Starting program: /home/xmj/tmp/a.out a b 23 | 24 | 可以看出,参数已经被保存了,下次运行时直接运行`run`命令,即可。 25 | 26 | 有意的是,如果我接下来,想让参数为空,该怎么办?是的,直接: 27 | 28 | (gdb) set args 29 | 30 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Arguments.html#Arguments) 31 | 32 | ## 贡献者 33 | 34 | xmj 35 | 36 | -------------------------------------------------------------------------------- /src/set-program-env.md: -------------------------------------------------------------------------------- 1 | # 设置被调试程序的环境变量 2 | 3 | ## 例子 4 | 5 | (gdb) u 309 6 | Warning: couldn't activate thread debugging using libthread_db: Cannot find new threads: generic error 7 | Warning: couldn't activate thread debugging using libthread_db: Cannot find new threads: generic error 8 | warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available. 9 | 10 | ## 技巧 11 | 12 | 在gdb中,可以通过命令`set env varname=value`来设置被调试程序的环境变量。对于上面的例子,网上可以搜到一些解决方法,其中一种方法就是设置LD_PRELOAD环境变量: 13 | 14 | set env LD_PRELOAD=/lib/x86_64-linux-gnu/libpthread.so.0 15 | 16 | 注意,这个实际路径在不同的机器环境下可能不一样。把这个命令加到~/.gdbinit文件中,就可以了。 17 | 18 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Environment.html#Environment) 19 | 20 | ## 贡献者 21 | 22 | xmj 23 | 24 | -------------------------------------------------------------------------------- /src/set-prompt.md: -------------------------------------------------------------------------------- 1 | # 设置命令提示符 2 | 3 | ## 例子 4 | 5 | $ gdb -q `which gdb` 6 | Reading symbols from /home/xmj/install/binutils-gdb-git/bin/gdb...done. 7 | (gdb) r -q 8 | Starting program: /home/xmj/install/binutils-gdb-git/bin/gdb -q 9 | [Thread debugging using libthread_db enabled] 10 | Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". 11 | (gdb) 12 | 13 | ## 技巧 14 | 15 | 当你用gdb来调试gdb的时候,通过设置命令提示符,可以帮助你区分这两个gdb: 16 | 17 | $ gdb -q `which gdb` 18 | Reading symbols from /home/xmj/install/binutils-gdb-git/bin/gdb...done. 19 | (gdb) set prompt (main gdb) 20 | (main gdb) r -q 21 | Starting program: /home/xmj/install/binutils-gdb-git/bin/gdb -q 22 | [Thread debugging using libthread_db enabled] 23 | Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". 24 | (gdb) 25 | 26 | 注意,这里`set prompt (main gdb) `结尾处是有一个空格的。 27 | 28 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Prompt.html#Prompt) 29 | 30 | ## 贡献者 31 | 32 | xmj 33 | 34 | -------------------------------------------------------------------------------- /src/set-read-watchpoint.md: -------------------------------------------------------------------------------- 1 | # 设置读观察点 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int a = 0; 7 | 8 | void *thread1_func(void *p_arg) 9 | { 10 | while (1) 11 | { 12 | printf("%d\n", a); 13 | sleep(10); 14 | } 15 | } 16 | 17 | int main(void) 18 | { 19 | pthread_t t1; 20 | 21 | pthread_create(&t1, NULL, thread1_func, "Thread 1"); 22 | 23 | sleep(1000); 24 | return; 25 | } 26 | 27 | 28 | ## 技巧 29 | gdb可以使用“`rwatch`”命令设置读观察点,也就是当发生读取变量行为时,程序就会暂停住。以上面程序为例: 30 | 31 | (gdb) start 32 | Temporary breakpoint 1 at 0x4005f3: file a.c, line 19. 33 | Starting program: /data2/home/nanxiao/a 34 | [Thread debugging using libthread_db enabled] 35 | Using host libthread_db library "/lib64/libthread_db.so.1". 36 | 37 | Temporary breakpoint 1, main () at a.c:19 38 | 19 pthread_create(&t1, NULL, thread1_func, "Thread 1"); 39 | (gdb) rw a 40 | Hardware read watchpoint 2: a 41 | (gdb) c 42 | Continuing. 43 | [New Thread 0x7ffff782c700 (LWP 5540)] 44 | [Switching to Thread 0x7ffff782c700 (LWP 5540)] 45 | Hardware read watchpoint 2: a 46 | 47 | Value = 0 48 | 0x00000000004005c6 in thread1_func (p_arg=0x40071c) at a.c:10 49 | 10 printf("%d\n", a); 50 | (gdb) c 51 | Continuing. 52 | 0 53 | Hardware read watchpoint 2: a 54 | 55 | Value = 0 56 | 0x00000000004005c6 in thread1_func (p_arg=0x40071c) at a.c:10 57 | 10 printf("%d\n", a); 58 | (gdb) c 59 | Continuing. 60 | 0 61 | Hardware read watchpoint 2: a 62 | 63 | Value = 0 64 | 0x00000000004005c6 in thread1_func (p_arg=0x40071c) at a.c:10 65 | 10 printf("%d\n", a); 66 | 67 | 68 | 69 | 可以看到,使用“`rw a`”命令(`rw`是`rwatch`命令的缩写)以后,每次访问`a`的值都会让程序停下来。 70 | 需要注意的是`rwatch`命令只对硬件观察点才生效,参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html). 71 | 72 | ## 贡献者 73 | 74 | nanxiao 75 | -------------------------------------------------------------------------------- /src/set-read-write-watchpoint.md: -------------------------------------------------------------------------------- 1 | # 设置读写观察点 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int a = 0; 7 | 8 | void *thread1_func(void *p_arg) 9 | { 10 | while (1) 11 | { 12 | a++; 13 | sleep(10); 14 | } 15 | } 16 | 17 | void *thread2_func(void *p_arg) 18 | { 19 | while (1) 20 | { 21 | printf("%d\n", a);; 22 | sleep(10); 23 | } 24 | } 25 | 26 | int main(void) 27 | { 28 | pthread_t t1, t2; 29 | 30 | pthread_create(&t1, NULL, thread1_func, "Thread 1"); 31 | pthread_create(&t2, NULL, thread2_func, "Thread 2"); 32 | 33 | sleep(1000); 34 | return; 35 | } 36 | 37 | ## 技巧 38 | gdb可以使用“`awatch`”命令设置读写观察点,也就是当发生读取变量或改变变量值的行为时,程序就会暂停住。以上面程序为例: 39 | 40 | (gdb) aw a 41 | Hardware access (read/write) watchpoint 1: a 42 | (gdb) r 43 | Starting program: /data2/home/nanxiao/a 44 | [Thread debugging using libthread_db enabled] 45 | Using host libthread_db library "/lib64/libthread_db.so.1". 46 | [New Thread 0x7ffff782c700 (LWP 16938)] 47 | [Switching to Thread 0x7ffff782c700 (LWP 16938)] 48 | Hardware access (read/write) watchpoint 1: a 49 | 50 | Value = 0 51 | 0x00000000004005c6 in thread1_func (p_arg=0x40076c) at a.c:10 52 | 10 a++; 53 | (gdb) c 54 | Continuing. 55 | Hardware access (read/write) watchpoint 1: a 56 | 57 | Old value = 0 58 | New value = 1 59 | thread1_func (p_arg=0x40076c) at a.c:11 60 | 11 sleep(10); 61 | (gdb) c 62 | Continuing. 63 | [New Thread 0x7ffff6e2b700 (LWP 16939)] 64 | [Switching to Thread 0x7ffff6e2b700 (LWP 16939)] 65 | Hardware access (read/write) watchpoint 1: a 66 | 67 | Value = 1 68 | 0x00000000004005f2 in thread2_func (p_arg=0x400775) at a.c:19 69 | 19 printf("%d\n", a);; 70 | (gdb) c 71 | Continuing. 72 | 1 73 | [Switching to Thread 0x7ffff782c700 (LWP 16938)] 74 | Hardware access (read/write) watchpoint 1: a 75 | 76 | Value = 1 77 | 0x00000000004005c6 in thread1_func (p_arg=0x40076c) at a.c:10 78 | 10 a++; 79 | 80 | 可以看到,使用“`aw a`”命令(`aw`是`awatch`命令的缩写)以后,每次读取或改变`a`的值都会让程序停下来。 81 | 需要注意的是`awatch`命令只对硬件观察点才生效,参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html). 82 | 83 | ## 贡献者 84 | 85 | nanxiao 86 | -------------------------------------------------------------------------------- /src/set-scheduler-locking-on.md: -------------------------------------------------------------------------------- 1 | # 只允许一个线程运行 2 | ## 例子 3 | #include 4 | #include 5 | int a = 0; 6 | int b = 0; 7 | void *thread1_func(void *p_arg) 8 | { 9 | while (1) 10 | { 11 | a++; 12 | sleep(1); 13 | } 14 | } 15 | 16 | void *thread2_func(void *p_arg) 17 | { 18 | while (1) 19 | { 20 | b++; 21 | sleep(1); 22 | } 23 | } 24 | 25 | int main(void) 26 | { 27 | pthread_t t1, t2; 28 | 29 | pthread_create(&t1, NULL, thread1_func, "Thread 1"); 30 | pthread_create(&t2, NULL, thread2_func, "Thread 2"); 31 | 32 | sleep(1000); 33 | return; 34 | } 35 | 36 | 37 | ## 技巧 38 | 用gdb调试多线程程序时,一旦程序断住,所有的线程都处于暂停状态。此时当你调试其中一个线程时(比如执行“`step`”,“`next`”命令),所有的线程都会同时执行。以上面程序为例: 39 | 40 | (gdb) b a.c:9 41 | Breakpoint 1 at 0x400580: file a.c, line 9. 42 | (gdb) r 43 | Starting program: /data2/home/nanxiao/a 44 | [Thread debugging using libthread_db enabled] 45 | Using host libthread_db library "/lib64/libthread_db.so.1". 46 | [New Thread 0x7ffff782c700 (LWP 17368)] 47 | [Switching to Thread 0x7ffff782c700 (LWP 17368)] 48 | 49 | Breakpoint 1, thread1_func (p_arg=0x400718) at a.c:9 50 | 9 a++; 51 | (gdb) p b 52 | $1 = 0 53 | (gdb) s 54 | 10 sleep(1); 55 | (gdb) s 56 | [New Thread 0x7ffff6e2b700 (LWP 17369)] 57 | 11 } 58 | (gdb) 59 | 60 | Breakpoint 1, thread1_func (p_arg=0x400718) at a.c:9 61 | 9 a++; 62 | (gdb) 63 | 10 sleep(1); 64 | (gdb) p b 65 | $2 = 3 66 | 67 | `thread1_func`更新全局变量`a`的值,`thread2_func`更新全局变量`b`的值。我在`thread1_func`里`a++`语句打上断点,当断点第一次命中时,打印`b`的值是`0`,在单步调试`thread1_func`几次后,`b`的值变成`3`,证明在单步调试`thread1_func`时,`thread2_func`也在执行。 68 | 如果想在调试一个线程时,让其它线程暂停执行,可以使用“`set scheduler-locking on`”命令: 69 | 70 | (gdb) b a.c:9 71 | Breakpoint 1 at 0x400580: file a.c, line 9. 72 | (gdb) r 73 | Starting program: /data2/home/nanxiao/a 74 | [Thread debugging using libthread_db enabled] 75 | Using host libthread_db library "/lib64/libthread_db.so.1". 76 | [New Thread 0x7ffff782c700 (LWP 19783)] 77 | [Switching to Thread 0x7ffff782c700 (LWP 19783)] 78 | 79 | Breakpoint 1, thread1_func (p_arg=0x400718) at a.c:9 80 | 9 a++; 81 | (gdb) set scheduler-locking on 82 | (gdb) p b 83 | $1 = 0 84 | (gdb) s 85 | 10 sleep(1); 86 | (gdb) 87 | 11 } 88 | (gdb) 89 | 90 | Breakpoint 1, thread1_func (p_arg=0x400718) at a.c:9 91 | 9 a++; 92 | (gdb) 93 | 10 sleep(1); 94 | (gdb) 95 | 11 } 96 | (gdb) p b 97 | $2 = 0 98 | 99 | 可以看到在单步调试`thread1_func`几次后,`b`的值仍然为`0`,证明在在单步调试`thread1_func`时,`thread2_func`没有执行。 100 | 101 | 此外,“`set scheduler-locking`”命令除了支持`off`和`on`模式外(默认是`off`),还有一个`step`模式。含义是:当用"`step`"命令调试线程时,其它线程不会执行,但是用其它命令(比如"`next`")调试线程时,其它线程也许会执行。 102 | 103 | 这个命令依赖于具体操作系统的调度策略,使用时需注意。参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/All_002dStop-Mode.html#All_002dStop-Mode). 104 | 105 | ## 贡献者 106 | 107 | nanxiao 108 | -------------------------------------------------------------------------------- /src/set-script-extension.md: -------------------------------------------------------------------------------- 1 | # 按何种方式解析脚本文件 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | typedef struct 8 | { 9 | int a; 10 | int b; 11 | int c; 12 | int d; 13 | }ex_st; 14 | 15 | int main(void) { 16 | ex_st st = {1, 2, 3, 4}; 17 | printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 18 | return 0; 19 | } 20 | 21 | 22 | 23 | ## 技巧 24 | 25 | gdb支持的脚本文件分为两种:一种是只包含gdb自身命令的脚本,例如“.gdbinit”文件,当gdb在启动时,就会执行“.gdbinit”文件中的命令;此外,gdb还支持其它一些语言写的脚本文件(比如python)。 26 | gdb用“`set script-extension`”命令来决定按何种格式来解析脚本文件。它可以取3个值: 27 | a)`off`:所有的脚本文件都解析成gdb的命令脚本; 28 | b)`soft`:根据脚本文件扩展名决定如何解析脚本。如果gdb支持解析这种脚本语言(比如python),就按这种语言解析,否则就按命令脚本解析; 29 | c)`strict`:根据脚本文件扩展名决定如何解析脚本。如果gdb支持解析这种脚本语言(比如python),就按这种语言解析,否则不解析; 30 | 以上面程序为例,进行调试: 31 | 32 | (gdb) start 33 | Temporary breakpoint 1 at 0x4004cd: file a.c, line 12. 34 | Starting program: /data2/home/nanxiao/a 35 | 36 | Temporary breakpoint 1, main () at a.c:12 37 | 12 ex_st st = {1, 2, 3, 4}; 38 | (gdb) q 39 | A debugging session is active. 40 | 41 | Inferior 1 [process 24249] will be killed. 42 | 43 | Quit anyway? (y or n) y 44 | 45 | 46 | 可以看到gdb退出时,默认行为会提示用户是否退出。 47 | 48 | 下面写一个脚本文件(gdb.py),但内容是一个gdb命令,不是真正的python脚本。用途是退出gdb时不提示: 49 | 50 | set confirm off 51 | 再次开始调试: 52 | 53 | (gdb) start 54 | Temporary breakpoint 1 at 0x4004cd: file a.c, line 12. 55 | Starting program: /data2/home/nanxiao/a 56 | 57 | Temporary breakpoint 1, main () at a.c:12 58 | 12 ex_st st = {1, 2, 3, 4}; 59 | (gdb) show script-extension 60 | Script filename extension recognition is "soft". 61 | (gdb) source gdb.py 62 | File "gdb.py", line 1 63 | set confirm off 64 | ^ 65 | SyntaxError: invalid syntax 66 | 67 | 68 | 可以看到“`script-extension`”默认值是`soft`,接下来执行“`source gdb.py`”,会按照pyhton语言解析gdb.py文件,但是由于这个文件实质上是一个gdb命令脚本,所以解析出错。 69 | 再执行一次: 70 | 71 | (gdb) start 72 | Temporary breakpoint 1 at 0x4004cd: file a.c, line 12. 73 | Starting program: /data2/home/nanxiao/a 74 | 75 | Temporary breakpoint 1, main () at a.c:12 76 | 12 ex_st st = {1, 2, 3, 4}; 77 | (gdb) set script-extension off 78 | (gdb) source gdb.py 79 | (gdb) q 80 | [root@linux:~]$ 81 | 这次把“`script-extension`”值改为`off`,所以脚本会按gdb命令脚本去解析,可以看到这次脚本命令生效了。 82 | 83 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Extending-GDB.html) 84 | ## 贡献者 85 | 86 | nanxiao 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/set-step-mode-on.md: -------------------------------------------------------------------------------- 1 | # 进入不带调试信息的函数 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | 8 | typedef struct 9 | { 10 | int a; 11 | int b; 12 | int c; 13 | int d; 14 | pthread_mutex_t mutex; 15 | }ex_st; 16 | 17 | int main(void) { 18 | ex_st st = {1, 2, 3, 4, PTHREAD_MUTEX_INITIALIZER}; 19 | printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 20 | return 0; 21 | } 22 | 23 | 24 | 25 | ## 技巧 26 | 27 | 默认情况下,gdb不会进入不带调试信息的函数。以上面代码为例: 28 | 29 | (gdb) n 30 | 15 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 31 | (gdb) s 32 | 1,2,3,4 33 | 16 return 0; 34 | 35 | 36 | 可以看到由于printf函数不带调试信息,所以“s”命令(s是“step”缩写)无法进入printf函数。 37 | 38 | 可以执行“set step-mode on”命令,这样gdb就不会跳过没有调试信息的函数: 39 | 40 | (gdb) set step-mode on 41 | (gdb) n 42 | 15 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 43 | (gdb) s 44 | 0x00007ffff7a993b0 in printf () from /lib64/libc.so.6 45 | (gdb) s 46 | 0x00007ffff7a993b7 in printf () from /lib64/libc.so.6 47 | 48 | 49 | 可以看到gdb进入了printf函数,接下来可以使用调试汇编程序的办法去调试函数。 50 | 51 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Continuing-and-Stepping.html) 52 | 53 | ## 贡献者 54 | 55 | nanxiao 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/set-tbreak.md: -------------------------------------------------------------------------------- 1 | # 设置临时断点 2 | 3 | ## 例子 4 | 5 | #include 6 | #include 7 | 8 | typedef struct 9 | { 10 | int a; 11 | int b; 12 | int c; 13 | int d; 14 | pthread_mutex_t mutex; 15 | }ex_st; 16 | 17 | int main(void) { 18 | ex_st st = {1, 2, 3, 4, PTHREAD_MUTEX_INITIALIZER}; 19 | printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 20 | return 0; 21 | } 22 | 23 | 24 | 25 | ## 技巧 26 | 27 | 在使用gdb时,如果想让断点只生效一次,可以使用“tbreak”命令(缩写为:tb)。以上面程序为例: 28 | 29 | (gdb) tb a.c:15 30 | Temporary breakpoint 1 at 0x400500: file a.c, line 15. 31 | (gdb) i b 32 | Num Type Disp Enb Address What 33 | 1 breakpoint del y 0x0000000000400500 in main at a.c:15 34 | (gdb) r 35 | Starting program: /data2/home/nanxiao/a 36 | 37 | Temporary breakpoint 1, main () at a.c:15 38 | 15 printf("%d,%d,%d,%d\n", st.a, st.b, st.c, st.d); 39 | (gdb) i b 40 | No breakpoints or watchpoints. 41 | 42 | 43 | 44 | 45 | 首先在文件的第15行设置临时断点,当程序断住后,用“i b”("info breakpoints"缩写)命令查看断点,发现断点没有了。也就是断点命中一次后,就被删掉了。 46 | 47 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Breaks.html) 48 | 49 | ## 贡献者 50 | 51 | nanxiao 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/set-var.md: -------------------------------------------------------------------------------- 1 | # 设置变量的值 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int func(void) 8 | { 9 | int i = 2; 10 | 11 | return i; 12 | } 13 | 14 | int main(void) 15 | { 16 | int a = 0; 17 | 18 | a = func(); 19 | printf("%d\n", a); 20 | return 0; 21 | } 22 | 23 | ## 技巧 24 | 25 | 在gdb中,可以用“`set var variable=expr`”命令设置变量的值,以上面代码为例: 26 | 27 | 28 | Breakpoint 2, func () at a.c:5 29 | 5 int i = 2; 30 | (gdb) n 31 | 7 return i; 32 | (gdb) set var i = 8 33 | (gdb) p i 34 | $4 = 8 35 | 36 | 37 | 38 | 可以看到在`func`函数里用`set`命令把`i`的值修改成为`8`。 39 | 40 | 也可以用“`set {type}address=expr`”的方式,含义是给存储地址在`address`,变量类型为`type`的变量赋值,仍以上面代码为例: 41 | 42 | Breakpoint 2, func () at a.c:5 43 | 5 int i = 2; 44 | (gdb) n 45 | 7 return i; 46 | (gdb) p &i 47 | $5 = (int *) 0x8047a54 48 | (gdb) set {int}0x8047a54 = 8 49 | (gdb) p i 50 | $6 = 8 51 | 52 | 可以看到`i`的值被修改成为`8`。 53 | 54 | 另外寄存器也可以作为变量,因此同样可以修改寄存器的值: 55 | 56 | Breakpoint 2, func () at a.c:5 57 | 5 int i = 2; 58 | (gdb) 59 | (gdb) n 60 | 7 return i; 61 | (gdb) 62 | 8 } 63 | (gdb) set var $eax = 8 64 | (gdb) n 65 | main () at a.c:15 66 | 15 printf("%d\n", a); 67 | (gdb) 68 | 8 69 | 16 return 0; 70 | 71 | 可以看到因为eax寄存器存储着函数的返回值,所以当把eax寄存器的值改为`8`后,函数的返回值也变成了`8`。 72 | 73 | 详情参见[gdb手册](https://sourceware.org/gdb/current/onlinedocs/gdb/Assignment.html#Assignment) 74 | 75 | 76 | 77 | ## 贡献者 78 | 79 | nanxiao 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/set-watchpoint-on-specified-thread.md: -------------------------------------------------------------------------------- 1 | # 设置观察点只针对特定线程生效 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int a = 0; 7 | 8 | void *thread1_func(void *p_arg) 9 | { 10 | while (1) 11 | { 12 | a++; 13 | sleep(10); 14 | } 15 | } 16 | 17 | void *thread2_func(void *p_arg) 18 | { 19 | while (1) 20 | { 21 | a++; 22 | sleep(10); 23 | } 24 | } 25 | 26 | int main(void) 27 | { 28 | pthread_t t1, t2; 29 | 30 | pthread_create(&t1, NULL, thread1_func, "Thread 1"); 31 | pthread_create(&t2, NULL, thread2_func, "Thread 2"); 32 | 33 | sleep(1000); 34 | return; 35 | } 36 | 37 | ## 技巧 38 | gdb可以使用“`watch expr thread threadnum`”命令设置观察点只针对特定线程生效,也就是只有编号为`threadnum`的线程改变了变量的值,程序才会停下来,其它编号线程改变变量的值不会让程序停住。以上面程序为例: 39 | 40 | (gdb) start 41 | Temporary breakpoint 1 at 0x4005d4: file a.c, line 28. 42 | Starting program: /data2/home/nanxiao/a 43 | [Thread debugging using libthread_db enabled] 44 | Using host libthread_db library "/lib64/libthread_db.so.1". 45 | 46 | Temporary breakpoint 1, main () at a.c:28 47 | 28 pthread_create(&t1, NULL, thread1_func, "Thread 1"); 48 | (gdb) n 49 | [New Thread 0x7ffff782c700 (LWP 25443)] 50 | 29 pthread_create(&t2, NULL, thread2_func, "Thread 2"); 51 | (gdb) 52 | [New Thread 0x7ffff6e2b700 (LWP 25444)] 53 | 31 sleep(1000); 54 | (gdb) i threads 55 | Id Target Id Frame 56 | 3 Thread 0x7ffff6e2b700 (LWP 25444) 0x00007ffff7915911 in clone () from /lib64/libc.so.6 57 | 2 Thread 0x7ffff782c700 (LWP 25443) 0x00007ffff78d9bcd in nanosleep () from /lib64/libc.so.6 58 | * 1 Thread 0x7ffff7fe9700 (LWP 25413) main () at a.c:31 59 | (gdb) wa a thread 2 60 | Hardware watchpoint 2: a 61 | (gdb) c 62 | Continuing. 63 | [Switching to Thread 0x7ffff782c700 (LWP 25443)] 64 | Hardware watchpoint 2: a 65 | 66 | Old value = 1 67 | New value = 3 68 | thread1_func (p_arg=0x400718) at a.c:11 69 | 11 sleep(10); 70 | (gdb) c 71 | Continuing. 72 | Hardware watchpoint 2: a 73 | 74 | Old value = 3 75 | New value = 5 76 | thread1_func (p_arg=0x400718) at a.c:11 77 | 11 sleep(10); 78 | (gdb) c 79 | Continuing. 80 | Hardware watchpoint 2: a 81 | 82 | Old value = 5 83 | New value = 7 84 | thread1_func (p_arg=0x400718) at a.c:11 85 | 11 sleep(10); 86 | 87 | 88 | 可以看到,使用“`wa a thread 2`”命令(`wa`是`watch`命令的缩写)以后,只有`thread1_func`改变`a`的值才会让程序停下来。 89 | 需要注意的是这种针对特定线程设置观察点方式只对硬件观察点才生效,参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html). 90 | 91 | ## 贡献者 92 | 93 | nanxiao 94 | -------------------------------------------------------------------------------- /src/set-watchpoint.md: -------------------------------------------------------------------------------- 1 | # 设置观察点 2 | ## 例子 3 | #include 4 | #include 5 | #include 6 | int a = 0; 7 | 8 | void *thread1_func(void *p_arg) 9 | { 10 | while (1) 11 | { 12 | a++; 13 | sleep(10); 14 | } 15 | } 16 | 17 | int main(int argc, char* argv[]) 18 | { 19 | pthread_t t1; 20 | pthread_create(&t1, NULL, thread1_func, NULL); 21 | 22 | sleep(1000); 23 | return 0; 24 | } 25 | 26 | ## 技巧 27 | gdb可以使用“`watch`”命令设置观察点,也就是当一个变量值发生变化时,程序会停下来。以上面程序为例: 28 | 29 | (gdb) start 30 | Temporary breakpoint 1 at 0x4005a8: file a.c, line 19. 31 | Starting program: /data2/home/nanxiao/a 32 | [Thread debugging using libthread_db enabled] 33 | Using host libthread_db library "/lib64/libthread_db.so.1". 34 | 35 | Temporary breakpoint 1, main () at a.c:19 36 | 19 pthread_create(&t1, NULL, thread1_func, "Thread 1"); 37 | (gdb) watch a 38 | Hardware watchpoint 2: a 39 | (gdb) r 40 | Starting program: /data2/home/nanxiao/a 41 | [Thread debugging using libthread_db enabled] 42 | Using host libthread_db library "/lib64/libthread_db.so.1". 43 | [New Thread 0x7ffff782c700 (LWP 8813)] 44 | [Switching to Thread 0x7ffff782c700 (LWP 8813)] 45 | Hardware watchpoint 2: a 46 | 47 | Old value = 0 48 | New value = 1 49 | thread1_func (p_arg=0x4006d8) at a.c:11 50 | 11 sleep(10); 51 | (gdb) c 52 | Continuing. 53 | Hardware watchpoint 2: a 54 | 55 | Old value = 1 56 | New value = 2 57 | thread1_func (p_arg=0x4006d8) at a.c:11 58 | 11 sleep(10); 59 | 60 | 可以看到,使用“`watch a`”命令以后,当`a`的值变化:由`0`变成`1`,由`1`变成`2`,程序都会停下来。 61 | 此外也可以使用“`watch *(data type*)address`”这样的命令,仍以上面程序为例: 62 | 63 | (gdb) p &a 64 | $1 = (int *) 0x6009c8 65 | (gdb) watch *(int*)0x6009c8 66 | Hardware watchpoint 2: *(int*)0x6009c8 67 | (gdb) r 68 | Starting program: /data2/home/nanxiao/a 69 | [Thread debugging using libthread_db enabled] 70 | Using host libthread_db library "/lib64/libthread_db.so.1". 71 | [New Thread 0x7ffff782c700 (LWP 15431)] 72 | [Switching to Thread 0x7ffff782c700 (LWP 15431)] 73 | Hardware watchpoint 2: *(int*)0x6009c8 74 | 75 | Old value = 0 76 | New value = 1 77 | thread1_func (p_arg=0x4006d8) at a.c:11 78 | 11 sleep(10); 79 | (gdb) c 80 | Continuing. 81 | Hardware watchpoint 2: *(int*)0x6009c8 82 | 83 | Old value = 1 84 | New value = 2 85 | thread1_func (p_arg=0x4006d8) at a.c:11 86 | 11 sleep(10); 87 | 88 | 先得到`a`的地址:`0x6009c8`,接着用“`watch *(int*)0x6009c8`”设置观察点,可以看到同“`watch a`”命令效果一样。 89 | 观察点可以通过软件或硬件的方式实现,取决于具体的系统。但是软件实现的观察点会导致程序运行很慢,使用时需注意。参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html). 90 | 91 | 如果系统支持硬件观测的话,当设置观测点是会打印如下信息: 92 | Hardware watchpoint num: expr 93 | 94 | 如果不想用硬件观测点的话可如下设置: 95 | set can-use-hw-watchpoints 96 | 97 | ## 查看断点 98 | 列出当前所设置了的所有观察点: 99 | info watchpoints 100 | 101 | watch 所设置的断点也可以用控制断点的命令来控制。如 disable、enable、delete等 102 | 103 | ## 贡献者 104 | 105 | nanxiao 106 | -------------------------------------------------------------------------------- /src/show-copying-warranty.md: -------------------------------------------------------------------------------- 1 | # 显示gdb版权相关信息 2 | 3 | 4 | ## 技巧 5 | 使用gdb时,如果想查看gdb版权相关信息,可以使用“`show copying`”命令: 6 | 7 | (gdb) show copying 8 | GNU GENERAL PUBLIC LICENSE 9 | Version 3, 29 June 2007 10 | 11 | Copyright (C) 2007 Free Software Foundation, Inc. 12 | Everyone is permitted to copy and distribute verbatim copies 13 | of this license document, but changing it is not allowed. 14 | 15 | Preamble 16 | 17 | The GNU General Public License is a free, copyleft license for 18 | software and other kinds of works. 19 | 20 | The licenses for most software and other practical works are designed 21 | to take away your freedom to share and change the works. By contrast, 22 | the GNU General Public License is intended to guarantee your freedom to 23 | share and change all versions of a program--to make sure it remains free 24 | software for all its use 25 | ...... 26 | 27 | 或者“`show warranty`”命令: 28 | 29 | (gdb) show warranty 30 | 15. Disclaimer of Warranty. 31 | 32 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 33 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 34 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 35 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 36 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 37 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 38 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 39 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 40 | 41 | 16. Limitation of Liability. 42 | 43 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 44 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 45 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 46 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 47 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 48 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 49 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 50 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 51 | SUCH DAMAGES. 52 | 53 | 17. Interpretation of Sections 15 and 16. 54 | 55 | If the disclaimer of warranty and limitation of liability provided 56 | above cannot be given local legal effect according to their terms, 57 | reviewing courts shall apply local law that most closely approximates 58 | an absolute waiver of all civil liability in connection with the 59 | Program, unless a warranty or assumption of liability accompanies a 60 | copy of the Program in return for a fee. 61 | 62 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Help.html#index-GDB-version-number)。 63 | 64 | ## 贡献者 65 | 66 | nanxiao 67 | 68 | -------------------------------------------------------------------------------- /src/show-print-thread-events.md: -------------------------------------------------------------------------------- 1 | # 不显示线程启动和退出信息 2 | ## 例子 3 | #include 4 | #include 5 | 6 | void *thread_func(void *p_arg) 7 | { 8 | sleep(10); 9 | } 10 | 11 | int main(void) 12 | { 13 | pthread_t t1, t2; 14 | 15 | pthread_create(&t1, NULL, thread_func, "Thread 1"); 16 | pthread_create(&t2, NULL, thread_func, "Thread 2"); 17 | 18 | sleep(1000); 19 | return; 20 | } 21 | 22 | 23 | ## 技巧 24 | 默认情况下,gdb检测到有线程产生和退出时,会打印提示信息,以上面程序为例: 25 | 26 | (gdb) r 27 | Starting program: /data/nan/a 28 | [Thread debugging using libthread_db enabled] 29 | [New Thread 1 (LWP 1)] 30 | [New LWP 2 ] 31 | [New LWP 3 ] 32 | [LWP 2 exited] 33 | [New Thread 2 ] 34 | [LWP 3 exited] 35 | [New Thread 3 ] 36 | 37 | 38 | 如果不想显示这些信息,可以使用“`set print thread-events off`”命令,这样当有线程产生和退出时,就不会打印提示信息: 39 | 40 | (gdb) set print thread-events off 41 | (gdb) r 42 | Starting program: /data/nan/a 43 | [Thread debugging using libthread_db enabled] 44 | 45 | 46 | 47 | 可以看到不再打印相关信息。 48 | 49 | 这个命令有些平台不支持,使用时需注意。参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Threads.html). 50 | 51 | ## 贡献者 52 | 53 | nanxiao 54 | -------------------------------------------------------------------------------- /src/show-version.md: -------------------------------------------------------------------------------- 1 | # 显示gdb版本信息 2 | 3 | 4 | ## 技巧 5 | 使用gdb时,如果想查看gdb版本信息,可以使用“`show version`”命令: 6 | 7 | (gdb) show version 8 | GNU gdb (GDB) 7.7.1 9 | Copyright (C) 2014 Free Software Foundation, Inc. 10 | License GPLv3+: GNU GPL version 3 or later 11 | This is free software: you are free to change and redistribute it. 12 | There is NO WARRANTY, to the extent permitted by law. Type "show copying" 13 | and "show warranty" for details. 14 | This GDB was configured as "x86_64-pc-solaris2.10". 15 | Type "show configuration" for configuration details. 16 | For bug reporting instructions, please see: 17 | . 18 | Find the GDB manual and other documentation resources online at: 19 | . 20 | For help, type "help". 21 | Type "apropos word" to search for commands related to "word". 22 | 23 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Help.html#index-GDB-version-number)。 24 | 25 | ## 贡献者 26 | 27 | nanxiao 28 | 29 | -------------------------------------------------------------------------------- /src/show-vtbl-content.md: -------------------------------------------------------------------------------- 1 | # 显示C++ vtable以及相应的内容 2 | 3 | ## 例子 4 | #include 5 | 6 | struct Base { 7 | virtual void f(){ 8 | std::cout << "base\n"; 9 | } 10 | }; 11 | 12 | struct Derived : Base { 13 | void f() override // 'override' is optional 14 | { 15 | std::cout << "derived\n"; 16 | } 17 | }; 18 | 19 | int main() { 20 | Base b; 21 | Derived d; 22 | 23 | // virtual function call through reference 24 | Base& br = b; // the type of br is Base& 25 | Base& dr = d; // the type of dr is Base& as well 26 | } 27 | 28 | ## 技巧 29 | 为了观察一个对象的虚表和相应虚表的内容,可以在启动gdb后设置如下命令 30 | `set print asm-demangle on` 31 | `set print demangle on` 32 | 已下面程序为例子 33 | 34 | (gdb) l 35 | int main() { 36 | Base b; 37 | Derived d; 38 | 39 | // virtual function call through reference 40 | Base& br = b; // the type of br is Base& 41 | Base& dr = d; // the type of dr is Base& as well 42 | (gdb) n 43 | The program is not being run. 44 | (gdb) r 45 | Starting program: /home/qinliansong/pblearn/main 46 | [Thread debugging using libthread_db enabled] 47 | Using host libthread_db library "/lib64/libthread_db.so.1". 48 | 49 | Breakpoint 1, main () at main.cc:21 50 | Base b; 51 | Missing separate debuginfos, use: debuginfo-install libgcc-4.8.5-39.el7.x86_64 libstdc++-4.8.5-39.el7.x86_64 52 | (gdb) n 53 | Derived d; 54 | (gdb) n 55 | Base& br = b; // the type of br is Base& 56 | 57 | 此时设置如下两个命令,几个命令,更友好的观察vtbl 58 | 59 | `set print vtbl on` 60 | `set print object on` 61 | `set print pretty on` 62 | 63 | (gdb) set print vtbl on 64 | (gdb) p d 65 | $1 = { = {_vptr.Base = 0x4009c0 }, } 66 | (gdb) set print object on 67 | (gdb) set print pretty on 68 | (gdb) p d 69 | $2 = (Derived) { 70 | = { 71 | _vptr.Base = 0x4009c0 72 | }, } 73 | 74 | 通过`info vtbl [expr]`观察对象的虚函数 75 | 76 | (gdb) info vtbl d 77 | vtable for 'Derived' @ 0x4009c0 (subobject @ 0x7fffffffde00): 78 | [0]: 0x4008f2 79 | 80 | (gdb) set print demangle on 81 | (gdb) set print asm-demangle on 82 | (gdb) x/28x 0x4009c0 83 | 0x4009c0 : 0x004008f2 0x00000000 0x00000000 0x00000000 84 | 0x4009d0 : 0x00400a08 0x00000000 0x004008d4 0x00000000 85 | 0x4009e0 : 0x00600d90 0x00000000 0x004009f8 0x00000000 86 | 0x4009f0 : 0x00400a08 0x00000000 0x72654437 0x64657669 87 | 0x400a00 : 0x00000000 0x00000000 0x00600d30 0x00000000 88 | 0x400a10 : 0x00400a18 0x00000000 0x73614234 0x00000065 89 | 0x400a20: 0x3b031b01 0x00000054 0x00000009 0xfffffc80 90 | 91 | 92 | (gdb) info variables vtable for Derived 93 | All variables matching regular expression "vtable for Derived": 94 | 95 | Non-debugging symbols: 96 | 0x00000000004009b0 vtable for Derived 97 | (gdb) x /4a 0x00000000004009b0 98 | 0x4009b0 : 0x0 0x4009e0 99 | 0x4009c0 : 0x4008f2 0x0 100 | (gdb) x /10a 0x4009c0 101 | 0x4009c0 : 0x4008f2 0x0 102 | 0x4009d0 : 0x400a08 0x4008d4 103 | 0x4009e0 : 0x600d90 0x4009f8 104 | 0x4009f0 : 0x400a08 0x6465766972654437 105 | 0x400a00 : 0x0 0x600d30 106 | 107 | ## 参考 108 | [gdb手册](https://docs.adacore.com/live/wave/gdb-10/html/gdb/gdb.html) 109 | [vtable-part1](https://shaharmike.com/cpp/vtable-part1/) 110 | 111 | ## 贡献者 112 | 113 | qls152 -------------------------------------------------------------------------------- /src/start-gdb-silently.md: -------------------------------------------------------------------------------- 1 | # 启动时不显示提示信息 2 | 3 | ## 例子 4 | 5 | $ gdb 6 | GNU gdb (GDB) 7.7.50.20140228-cvs 7 | Copyright (C) 2014 Free Software Foundation, Inc. 8 | License GPLv3+: GNU GPL version 3 or later 9 | This is free software: you are free to change and redistribute it. 10 | There is NO WARRANTY, to the extent permitted by law. Type "show copying" 11 | and "show warranty" for details. 12 | This GDB was configured as "x86_64-unknown-linux-gnu". 13 | Type "show configuration" for configuration details. 14 | For bug reporting instructions, please see: 15 | . 16 | Find the GDB manual and other documentation resources online at: 17 | . 18 | For help, type "help". 19 | Type "apropos word" to search for commands related to "word". 20 | 21 | ## 技巧 22 | gdb在启动时会显示如上类似的提示信息。 23 | 24 | 如果不想显示这个信息,则可以使用`-q`选项把提示信息关掉: 25 | 26 | $ gdb -q 27 | (gdb) 28 | 29 | 你可以在~/.bashrc中,为gdb设置一个别名: 30 | 31 | alias gdb="gdb -q" 32 | 33 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Invoking-GDB.html#Invoking-GDB) 34 | 35 | ## 贡献者 36 | 37 | xmj 38 | 39 | -------------------------------------------------------------------------------- /src/step-and-next-function.md: -------------------------------------------------------------------------------- 1 | # 是否进入带调试信息的函数 2 | 3 | ## 例子 4 | 5 | #include 6 | 7 | int func(void) 8 | { 9 | return 3; 10 | } 11 | 12 | int main(void) 13 | { 14 | int a = 0; 15 | 16 | a = func(); 17 | printf("%d\n", a); 18 | return 0; 19 | } 20 | 21 | 22 | 23 | ## 技巧 24 | 25 | 使用gdb调试遇到函数时,使用step命令(缩写为s)可以进入函数(函数必须有调试信息)。以上面代码为例: 26 | 27 | (gdb) n 28 | 12 a = func(); 29 | (gdb) s 30 | func () at a.c:5 31 | 5 return 3; 32 | (gdb) n 33 | 6 } 34 | (gdb) 35 | main () at a.c:13 36 | 13 printf("%d\n", a); 37 | 38 | 39 | 可以看到gdb进入了func函数。 40 | 41 | 可以使用next命令(缩写为n)不进入函数,gdb会等函数执行完,再显示下一行要执行的程序代码: 42 | 43 | (gdb) n 44 | 12 a = func(); 45 | (gdb) n 46 | 13 printf("%d\n", a); 47 | (gdb) n 48 | 3 49 | 14 return 0; 50 | 51 | 52 | 53 | 可以看到gdb没有进入func函数。 54 | 55 | 详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Continuing-and-Stepping.html) 56 | 57 | ## 贡献者 58 | 59 | nanxiao 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/stop-signal.md: -------------------------------------------------------------------------------- 1 | # 信号发生时是否暂停程序 2 | ## 例子 3 | #include 4 | #include 5 | 6 | void handler(int sig); 7 | 8 | void handler(int sig) 9 | { 10 | signal(sig, handler); 11 | printf("Receive signal: %d\n", sig); 12 | } 13 | 14 | int main(void) { 15 | signal(SIGHUP, handler); 16 | 17 | while (1) 18 | { 19 | sleep(1); 20 | } 21 | return 0; 22 | } 23 | 24 | ## 技巧 25 | 用gdb调试程序时,可以用“`handle signal stop/nostop`”命令设置当信号发生时,是否暂停程序的执行,以上面程序为例: 26 | 27 | (gdb) i signals 28 | Signal Stop Print Pass to program Description 29 | 30 | SIGHUP Yes Yes Yes Hangup 31 | ...... 32 | 33 | (gdb) r 34 | Starting program: /data1/nan/test 35 | [Thread debugging using libthread_db enabled] 36 | [New Thread 1 (LWP 1)] 37 | 38 | Program received signal SIGHUP, Hangup. 39 | [Switching to Thread 1 (LWP 1)] 40 | 0xfeeeae55 in ___nanosleep () from /lib/libc.so.1 41 | (gdb) c 42 | Continuing. 43 | Receive signal: 1 44 | 45 | 可以看到,默认情况下,发生`SIGHUP`信号时,gdb会暂停程序的执行,并打印收到信号的信息。此时需要执行`continue`命令继续程序的执行。 46 | 47 | 接下来用“`handle SIGHUP nostop`”命令设置当`SIGHUP`信号发生时,gdb不暂停程序,执行如下: 48 | 49 | (gdb) handle SIGHUP nostop 50 | Signal Stop Print Pass to program Description 51 | SIGHUP No Yes Yes Hangup 52 | (gdb) c 53 | Continuing. 54 | 55 | Program received signal SIGHUP, Hangup. 56 | Receive signal: 1 57 | 可以看到,程序收到`SIGHUP`信号发生时,没有暂停,而是继续执行。 58 | 59 | 如果想恢复之前的行为,用“`handle SIGHUP stop`”命令即可。需要注意的是,设置`stop`的同时,默认也会设置`print`(关于`print`,请参见[信号发生时是否打印信号信息](print-signal.md))。 60 | 61 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Signals.html). 62 | 63 | ## 贡献者 64 | 65 | nanxiao 66 | -------------------------------------------------------------------------------- /src/substitute-path.md: -------------------------------------------------------------------------------- 1 | # 替换查找源文件的目录 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | time_t now = time(NULL); 8 | struct tm local = {0}; 9 | struct tm gmt = {0}; 10 | 11 | localtime_r(&now, &local); 12 | gmtime_r(&now, &gmt); 13 | 14 | return 0; 15 | } 16 | 17 | 18 | 19 | 20 | ## 技巧 21 | 有时调试程序时,源代码文件可能已经移到其它的文件夹了。此时可以用`set substitute-path from to`命令设置新的文件夹(`to`)目录替换旧的(`from`)。以上面程序为例: 22 | 23 | (gdb) start 24 | Temporary breakpoint 1 at 0x400560: file a.c, line 5. 25 | Starting program: /home/nan/a 26 | 27 | Temporary breakpoint 1, main () at a.c:5 28 | 5 a.c: No such file or directory. 29 | (gdb) set substitute-path /home/nan /home/ki 30 | (gdb) n 31 | 6 struct tm local = {0}; 32 | (gdb) 33 | 7 struct tm gmt = {0}; 34 | (gdb) 35 | 9 localtime_r(&now, &local); 36 | (gdb) 37 | 10 gmtime_r(&now, &gmt); 38 | (gdb) 39 | 12 return 0; 40 | 41 | 42 | 43 | 调试时,因为源文件已经移到`/home/ki`这个文件夹下了,所以gdb找不到源文件。使用`set substitute-path /home/nan /home/ki`命令设置源文件的查找目录后,gdb就可以正常地解析源代码了。 44 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Source-Path.html). 45 | 46 | ## 贡献者 47 | 48 | nanxiao 49 | -------------------------------------------------------------------------------- /src/tcatch.md: -------------------------------------------------------------------------------- 1 | # 让catchpoint只触发一次 2 | ## 例子 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | pid_t pid; 10 | int i = 0; 11 | 12 | for (i = 0; i < 2; i++) 13 | { 14 | pid = fork(); 15 | if (pid < 0) 16 | { 17 | exit(1); 18 | } 19 | else if (pid == 0) 20 | { 21 | exit(0); 22 | } 23 | } 24 | printf("hello world\n"); 25 | return 0; 26 | } 27 | 28 | ## 技巧 29 | 使用gdb调试程序时,可以用“`tcatch`”命令设置`catchpoint`只触发一次,以上面程序为例: 30 | 31 | (gdb) tcatch fork 32 | Catchpoint 1 (fork) 33 | (gdb) r 34 | Starting program: /home/nan/a 35 | 36 | Temporary catchpoint 1 (forked process 27377), 0x00000034e42acdbd in fork () from /lib64/libc.so.6 37 | (gdb) c 38 | Continuing. 39 | hello world 40 | [Inferior 1 (process 27373) exited normally] 41 | (gdb) q 42 | 43 | 可以看到当程序只在第一次调用`fork`时暂停。 44 | 45 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.html). 46 | 47 | ## 贡献者 48 | 49 | nanxiao 50 | -------------------------------------------------------------------------------- /src/trace-instructions.md: -------------------------------------------------------------------------------- 1 | # 从起始地址($start_pc)跟踪指令至结束地址($end_pc),并将输出保存在 'gdb.txt' 文件中 2 | 3 | ``` 4 | # set tracing points 5 | set $start_pc= 6 | set $end_pc= 7 | 8 | # disable pagination 9 | set pagination off 10 | 11 | # log output to gdb.txt file instead of standard output 12 | set logging on 13 | set logging redirect on 14 | 15 | # Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled on'. 16 | 17 | # run until $start_pc address hit 18 | tbreak *$start_pc 19 | run 20 | 21 | # for each breakpoint hit print the instruction 22 | display/i $pc 23 | while $while_true 24 | si 25 | if $pc == $end_pc 26 | printf "Breakpoint reached\n" 27 | set $while_true=0 28 | end 29 | end 30 | ``` 31 | 32 | 33 | ## 贡献者 34 | 35 | tin-z 36 | -------------------------------------------------------------------------------- /src/tui-mode.md: -------------------------------------------------------------------------------- 1 | # 进入和退出图形化调试界面 2 | ## 例子 3 | #include 4 | 5 | void fun1(void) 6 | { 7 | int i = 0; 8 | 9 | i++; 10 | i = i * 2; 11 | printf("%d\n", i); 12 | } 13 | 14 | void fun2(void) 15 | { 16 | int j = 0; 17 | 18 | fun1(); 19 | j++; 20 | j = j * 2; 21 | printf("%d\n", j); 22 | } 23 | 24 | int main(void) 25 | { 26 | fun2(); 27 | return 0; 28 | } 29 | 30 | 31 | ## 技巧 32 | 启动gdb时指定“`-tui`”参数(例如:`gdb -tui program`),或者运行gdb过程中使用“`Crtl+X+A`”组合键,都可以进入图形化调试界面。以调试上面程序为例: 33 | 34 | ┌──a.c──────────────────────────────────────────────────────────────────────────────────────────┐ 35 | │17 j++; │ 36 | │18 j = j * 2; │ 37 | │19 printf("%d\n", j); │ 38 | │20 } │ 39 | │21 │ 40 | │22 int main(void) │ 41 | │23 { │ 42 | B+>│24 fun2(); │ 43 | │25 return 0; │ 44 | │26 } │ 45 | │27 │ 46 | │28 │ 47 | │29 │ 48 | │30 │ 49 | │31 │ 50 | │32 │ 51 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 52 | native process 22141 In: main Line: 24 PC: 0x40052b 53 | Type "apropos word" to search for commands related to "word"... 54 | Reading symbols from a...done. 55 | (gdb) start 56 | Temporary breakpoint 1 at 0x40052b: file a.c, line 24. 57 | Starting program: /home/nan/a 58 | 59 | Temporary breakpoint 1, main () at a.c:24 60 | (gdb) 61 | 可以看到,显示了当前的程序的进程号,将要执行的代码行号,`PC`寄存器的值。 62 | 退出图形化调试界面也是用“`Crtl+X+A`”组合键。 63 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/TUI.html). 64 | 65 | ## 贡献者 66 | 67 | nanxiao -------------------------------------------------------------------------------- /src/up-down-select-frame.md: -------------------------------------------------------------------------------- 1 | # 向上或向下切换函数堆栈帧 2 | ## 例子 3 | #include 4 | 5 | int func1(int a) 6 | { 7 | return 2 * a; 8 | } 9 | 10 | int func2(int a) 11 | { 12 | int c = 0; 13 | c = 2 * func1(a); 14 | return c; 15 | } 16 | 17 | int func3(int a) 18 | { 19 | int c = 0; 20 | c = 2 * func2(a); 21 | return c; 22 | } 23 | 24 | int main(void) 25 | { 26 | printf("%d\n", func3(10)); 27 | return 0; 28 | } 29 | 30 | ## 技巧 31 | 用gdb调试程序时,当程序暂停后,可以用“`up n`”或“`down n`”命令向上或向下选择函数堆栈帧,其中`n`是层数。以上面程序为例: 32 | 33 | (gdb) b test.c:5 34 | Breakpoint 1 at 0x40053d: file test.c, line 5. 35 | (gdb) r 36 | Starting program: /home/nanxiao/test 37 | 38 | Breakpoint 1, func1 (a=10) at test.c:5 39 | 5 return 2 * a; 40 | (gdb) bt 41 | #0 func1 (a=10) at test.c:5 42 | #1 0x0000000000400560 in func2 (a=10) at test.c:11 43 | #2 0x0000000000400586 in func3 (a=10) at test.c:18 44 | #3 0x000000000040059e in main () at test.c:24 45 | (gdb) frame 2 46 | #2 0x0000000000400586 in func3 (a=10) at test.c:18 47 | 18 c = 2 * func2(a); 48 | (gdb) up 1 49 | #3 0x000000000040059e in main () at test.c:24 50 | 24 printf("%d\n", func3(10)); 51 | (gdb) down 2 52 | #1 0x0000000000400560 in func2 (a=10) at test.c:11 53 | 11 c = 2 * func1(a); 54 | 55 | 56 | 可以看到程序断住后,先执行“`frame 2`”命令,切换到`fun3`函数。接着执行“`up 1`”命令,此时会切换到`main`函数,也就是会往外层的堆栈帧移动一层。反之,当执行“`down 2`”命令后,又会向内层堆栈帧移动二层。如果不指定`n`,则`n`默认为`1`. 57 | 58 | 还有“`up-silently n`”和“`down-silently n`”这两个命令,与“`up n`”和“`down n`”命令区别在于,切换堆栈帧后,不会打印信息,仍以上面程序为例: 59 | 60 | (gdb) up 61 | #2 0x0000000000400586 in func3 (a=10) at test.c:18 62 | 18 c = 2 * func2(a); 63 | (gdb) bt 64 | #0 func1 (a=10) at test.c:5 65 | #1 0x0000000000400560 in func2 (a=10) at test.c:11 66 | #2 0x0000000000400586 in func3 (a=10) at test.c:18 67 | #3 0x000000000040059e in main () at test.c:24 68 | (gdb) up-silently 69 | (gdb) i frame 70 | Stack level 3, frame at 0x7fffffffe5a0: 71 | rip = 0x40059e in main (test.c:24); saved rip = 0x7ffff7a35ec5 72 | caller of frame at 0x7fffffffe590 73 | source language c. 74 | Arglist at 0x7fffffffe590, args: 75 | Locals at 0x7fffffffe590, Previous frame's sp is 0x7fffffffe5a0 76 | Saved registers: 77 | rbp at 0x7fffffffe590, rip at 0x7fffffffe598 78 | 79 | 可以看到从`func3`切换到`main`函数堆栈帧时,并没有打印出相关信息。 80 | 81 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Selection.html#Selection). 82 | 83 | ## 贡献者 84 | 85 | nanxiao 86 | -------------------------------------------------------------------------------- /src/use-$_-$__-variables.md: -------------------------------------------------------------------------------- 1 | # 使用“$\_”和“$__”变量 2 | ## 例子 3 | #include 4 | 5 | int main(void) 6 | { 7 | int i = 0; 8 | char a[100]; 9 | 10 | for (i = 0; i < sizeof(a); i++) 11 | { 12 | a[i] = i; 13 | } 14 | 15 | return 0; 16 | } 17 | 18 | ## 技巧 19 | "`x`"命令会把最后检查的内存地址值存在“`$_`”这个“convenience variable”中,并且会把这个地址中的内容放在“`$__`”这个“convenience variable”,以上面程序为例: 20 | 21 | (gdb) b a.c:13 22 | Breakpoint 1 at 0x4004a0: file a.c, line 13. 23 | (gdb) r 24 | Starting program: /data2/home/nanxiao/a 25 | 26 | Breakpoint 1, main () at a.c:13 27 | 13 return 0; 28 | (gdb) x/16xb a 29 | 0x7fffffffe4a0: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 30 | 0x7fffffffe4a8: 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 31 | (gdb) p $_ 32 | $1 = (int8_t *) 0x7fffffffe4af 33 | (gdb) p $__ 34 | $2 = 15 35 | 36 | 37 | 可以看到“`$_`”值为`0x7fffffffe4af`,正好是"`x`"命令检查的最后的内存地址。而“`$__`”值为`15`。 38 | 另外要注意有些命令(像“`info line`”和“`info breakpoint`”)会提供一个默认的地址给"`x`"命令检查,而这些命令也会把“`$_`”的值变为那个默认地址值: 39 | 40 | (gdb) p $_ 41 | $5 = (int8_t *) 0x7fffffffe4af 42 | (gdb) info breakpoint 43 | Num Type Disp Enb Address What 44 | 1 breakpoint keep y 0x00000000004004a0 in main at a.c:13 45 | breakpoint already hit 1 time 46 | (gdb) p $_ 47 | $6 = (void *) 0x4004a0 48 | 49 | 50 | 可以看到使用“`info breakpoint`”命令后,“`$_`”值变为`0x4004a0`。 51 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Convenience-Vars.html). 52 | 53 | ## 贡献者 54 | 55 | nanxiao 56 | -------------------------------------------------------------------------------- /src/use-$_exitcode.md: -------------------------------------------------------------------------------- 1 | # 使用“$_exitcode”变量 2 | ## 例子 3 | int main(void) 4 | { 5 | return 0; 6 | } 7 | 8 | 9 | ## 技巧 10 | 当被调试的程序正常退出时,gdb会使用`$_exitcode`这个“`convenience variable`”记录程序退出时的“`exit code`”。以调试上面程序为例: 11 | 12 | [root@localhost nan]# gdb -q a 13 | Reading symbols from a...done. 14 | (gdb) start 15 | Temporary breakpoint 1 at 0x400478: file a.c, line 3. 16 | Starting program: /home/nan/a 17 | 18 | Temporary breakpoint 1, main () at a.c:3 19 | 3 return 0; 20 | (gdb) n 21 | 4 } 22 | (gdb) 23 | 0x00000034e421ed1d in __libc_start_main () from /lib64/libc.so.6 24 | (gdb) 25 | Single stepping until exit from function __libc_start_main, 26 | which has no line number information. 27 | [Inferior 1 (process 1185) exited normally] 28 | (gdb) p $_exitcode 29 | $1 = 0 30 | 31 | 可以看到打印的`$_exitcode`的值为`0`。 32 | 改变程序,返回值改为`1`: 33 | 34 | int main(void) 35 | { 36 | return 1; 37 | } 38 | 接着调试: 39 | 40 | [root@localhost nan]# gdb -q a 41 | Reading symbols from a...done. 42 | (gdb) start 43 | Temporary breakpoint 1 at 0x400478: file a.c, line 3. 44 | Starting program: /home/nan/a 45 | 46 | Temporary breakpoint 1, main () at a.c:3 47 | 3 return 1; 48 | (gdb) 49 | (gdb) n 50 | 4 } 51 | (gdb) 52 | 0x00000034e421ed1d in __libc_start_main () from /lib64/libc.so.6 53 | (gdb) 54 | Single stepping until exit from function __libc_start_main, 55 | which has no line number information. 56 | [Inferior 1 (process 2603) exited with code 01] 57 | (gdb) p $_exitcode 58 | $1 = 1 59 | 60 | 可以看到打印的`$_exitcode`的值变为`1`。 61 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Convenience-Vars.html). 62 | 63 | ## 贡献者 64 | 65 | nanxiao 66 | -------------------------------------------------------------------------------- /src/use-$_siginfo-variable.md: -------------------------------------------------------------------------------- 1 | # 使用“$_siginfo”变量 2 | ## 例子 3 | #include 4 | #include 5 | 6 | void handler(int sig); 7 | 8 | void handler(int sig) 9 | { 10 | signal(sig, handler); 11 | printf("Receive signal: %d\n", sig); 12 | } 13 | 14 | int main(void) { 15 | signal(SIGHUP, handler); 16 | 17 | while (1) 18 | { 19 | sleep(1); 20 | } 21 | return 0; 22 | } 23 | 24 | ## 技巧 25 | 在某些平台上(比如Linux)使用gdb调试程序,当有信号发生时,gdb在把信号丢给程序之前,可以通过`$_siginfo`变量读取一些额外的有关当前信号的信息,这些信息是`kernel`传给信号处理函数的。以上面程序为例: 26 | 27 | Program received signal SIGHUP, Hangup. 28 | 0x00000034e42accc0 in __nanosleep_nocancel () from /lib64/libc.so.6 29 | Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6.x86_64 30 | (gdb) ptype $_siginfo 31 | type = struct { 32 | int si_signo; 33 | int si_errno; 34 | int si_code; 35 | union { 36 | int _pad[28]; 37 | struct {...} _kill; 38 | struct {...} _timer; 39 | struct {...} _rt; 40 | struct {...} _sigchld; 41 | struct {...} _sigfault; 42 | struct {...} _sigpoll; 43 | } _sifields; 44 | } 45 | (gdb) ptype $_siginfo._sifields._sigfault 46 | type = struct { 47 | void *si_addr; 48 | } 49 | (gdb) p $_siginfo._sifields._sigfault.si_addr 50 | $1 = (void *) 0x850e 51 | 52 | 我们可以了解`$_siginfo`变量里每个成员的类型,并且可以读到成员的值。 53 | 54 | 55 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Signaling.html#Signaling). 56 | 57 | ## 贡献者 58 | 59 | nanxiao 60 | -------------------------------------------------------------------------------- /src/use-$_thread-variable.md: -------------------------------------------------------------------------------- 1 | # 使用“$_thread”变量 2 | ## 例子 3 | #include 4 | #include 5 | 6 | int a = 0; 7 | 8 | void *thread1_func(void *p_arg) 9 | { 10 | while (1) 11 | { 12 | a++; 13 | sleep(10); 14 | } 15 | } 16 | 17 | void *thread2_func(void *p_arg) 18 | { 19 | while (1) 20 | { 21 | a++; 22 | sleep(10); 23 | } 24 | } 25 | 26 | int main(void) 27 | { 28 | pthread_t t1, t2; 29 | 30 | pthread_create(&t1, NULL, thread1_func, "Thread 1"); 31 | pthread_create(&t2, NULL, thread2_func, "Thread 2"); 32 | 33 | sleep(1000); 34 | return; 35 | } 36 | 37 | ## 技巧 38 | gdb从7.2版本引入了`$_thread`这个“`convenience variable`”,用来保存当前正在调试的线程号。这个变量在写断点命令或是命令脚本时会很有用。以上面程序为例: 39 | 40 | (gdb) wa a 41 | Hardware watchpoint 2: a 42 | (gdb) command 2 43 | Type commands for breakpoint(s) 2, one per line. 44 | End with a line saying just "end". 45 | >printf "thread id=%d\n", $_thread 46 | >end 47 | 48 | 首先设置了观察点:“wa a”(`wa`是`watch`命令缩写),也就是当`a`的值发生变化时,程序会暂停,接下来在`commands`语句中打印线程号。 49 | 然后继续执行程序: 50 | 51 | (gdb) c 52 | Continuing. 53 | [New Thread 0x7ffff782c700 (LWP 20928)] 54 | [Switching to Thread 0x7ffff782c700 (LWP 20928)] 55 | Hardware watchpoint 2: a 56 | 57 | Old value = 0 58 | New value = 1 59 | thread1_func (p_arg=0x400718) at a.c:11 60 | 11 sleep(10); 61 | thread id=2 62 | (gdb) c 63 | Continuing. 64 | [New Thread 0x7ffff6e2b700 (LWP 20929)] 65 | [Switching to Thread 0x7ffff6e2b700 (LWP 20929)] 66 | Hardware watchpoint 2: a 67 | 68 | Old value = 1 69 | New value = 2 70 | thread2_func (p_arg=0x400721) at a.c:20 71 | 20 sleep(10); 72 | thread id=3 73 | 74 | 可以看到程序暂停时,会打印线程号:“`thread id=2`”或者“`thread id=3`”。 75 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Threads.html). 76 | 77 | ## 贡献者 78 | 79 | nanxiao 80 | -------------------------------------------------------------------------------- /src/use-short-command.md: -------------------------------------------------------------------------------- 1 | # 使用命令的缩写形式 2 | 3 | ## 技巧 4 | 5 | 在gdb中,你不用必须输入完整的命令,只需命令的(前)几个字母即可。规则是,只要这个缩写不会和其它命令有歧义(注,是否有歧义,这个规则从文档上看不出,看起来需要查看gdb的源代码,或者在实际使用中进行总结)。也可以使用tab键进行命令补全。 6 | 7 | 其中许多常用命令只使用第一个字母就可以,比如: 8 | 9 | b -> break 10 | c -> continue 11 | d -> delete 12 | f -> frame 13 | i -> info 14 | j -> jump 15 | l -> list 16 | n -> next 17 | p -> print 18 | r -> run 19 | s -> step 20 | u -> until 21 | 22 | 也有使用两个或几个字母的,比如: 23 | 24 | aw -> awatch 25 | bt -> backtrace 26 | dir -> directory 27 | disas -> disassemble 28 | fin -> finish 29 | ig -> ignore 30 | ni -> nexti 31 | rw -> rwatch 32 | si -> stepi 33 | tb -> tbreak 34 | wa -> watch 35 | win -> winheight 36 | 37 | 另外,如果直接按回车键,会重复执行上一次的命令。 38 | 39 | ## 贡献者 40 | 41 | xmj 42 | 43 | nanxiao 44 | 45 | -------------------------------------------------------------------------------- /src/winheight.md: -------------------------------------------------------------------------------- 1 | # 调整窗口大小 2 | ## 例子 3 | #include 4 | 5 | void fun1(void) 6 | { 7 | int i = 0; 8 | 9 | i++; 10 | i = i * 2; 11 | printf("%d\n", i); 12 | } 13 | 14 | void fun2(void) 15 | { 16 | int j = 0; 17 | 18 | fun1(); 19 | j++; 20 | j = j * 2; 21 | printf("%d\n", j); 22 | } 23 | 24 | int main(void) 25 | { 26 | fun2(); 27 | return 0; 28 | } 29 | 30 | 31 | ## 技巧 32 | 使用gdb图形化调试界面时,可以使用“`winheight [+ | -]count`”命令调整窗口大小(`winheight`缩写为`win`。`win_name`可以是`src`、`cmd`、`asm`和`regs`)。以调试上面程序为例,这是原始的`src`窗口大小: 33 | 34 | ┌──a.c──────────────────────────────────────────────────────────────────────────────────────────┐ 35 | │17 j++; │ 36 | │18 j = j * 2; │ 37 | │19 printf("%d\n", j); │ 38 | │20 } │ 39 | │21 int main(void) 22 40 | │23 { │ 41 | │24 fun2(); │ 42 | B+>│25 │ 43 | │ return 0; │ 44 | │26 } │ 45 | │27 32 46 | │ │ 47 | │ │ 48 | │ │ 49 | │ │ 50 | │ │ 51 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 52 | native process 9667 In: main Line: 24 PC: 0x40052b 53 | Usage: winheight [+ | -] <#lines> 54 | (gdb) start 55 | Temporary breakpoint 1 at 0x40052b: file a.c, line 24. 56 | Starting program: /home/nan/a 57 | 58 | Temporary breakpoint 1, main () at a.c:24 59 | 60 | 执行“`winheight src -5`”命令后: 61 | 62 | ┌──a.c──────────────────────────────────────────────────────────────────────────────────────────┐ 63 | │17 j++; │ 64 | │18 j = j * 2; │ 65 | │19 printf("%d\n", j); │ 66 | │20 } │ 67 | │21 │ 68 | │22 int main(void) │ 69 | │23 { │ 70 | >│24 fun2(); │ 71 | │25 return 0; │ 72 | │26 } │ 73 | │27 │ 74 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 75 | native process 9667 In: main Line: 24 PC: 0x40052b 76 | Usage: winheight [+ | -] <#lines> 77 | (gdb) 78 | 可以看到窗口变小了。 79 | 接着执行“`winheight src +5`”命令: 80 | 81 | ┌──a.c──────────────────────────────────────────────────────────────────────────────────────────┐ 82 | │17 j++; │ 83 | │18 j = j * 2; │ 84 | │19 printf("%d\n", j); │ 85 | │20 } │ 86 | │21 │ 87 | │22 int main(void) │ 88 | │23 { │ 89 | >│24 fun2(); │ 90 | │25 return 0; │ 91 | │26 } │ 92 | │27 │ 93 | │28 │ 94 | │29 │ 95 | │30 │ 96 | │31 │ 97 | │32 │ 98 | └───────────────────────────────────────────────────────────────────────────────────────────────┘ 99 | native process 9667 In: main Line: 24 PC: 0x40052b 100 | Usage: winheight [+ | -] <#lines> 101 | (gdb) 102 | 可以看到窗口恢复了原样。 103 | 参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/TUI-Commands.html). 104 | 105 | ## 贡献者 106 | 107 | nanxiao 108 | -------------------------------------------------------------------------------- /utils/build.go: -------------------------------------------------------------------------------- 1 | // 代码源自https://github.com/astaxie/build-web-application-with-golang.git 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "github.com/fairlyblank/md2min" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "regexp" 12 | "strings" 13 | ) 14 | 15 | // 定义一个访问者结构体 16 | type Visitor struct{} 17 | 18 | func (self *Visitor) md2html(arg map[string]string) error { 19 | from := arg["from"] 20 | to := arg["to"] 21 | err := filepath.Walk(from+"/", func(path string, f os.FileInfo, err error) error { 22 | if f == nil { 23 | return err 24 | } 25 | if f.IsDir() { 26 | return nil 27 | } 28 | if (f.Mode() & os.ModeSymlink) > 0 { 29 | return nil 30 | } 31 | if !strings.HasSuffix(f.Name(), ".md") { 32 | return nil 33 | } 34 | 35 | file, err := os.Open(path) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | input_byte, _ := ioutil.ReadAll(file) 41 | input := string(input_byte) 42 | input = regexp.MustCompile(`\[(.*?)\]\(?\)`).ReplaceAllString(input, "[$1](<$2.html>)") 43 | 44 | if f.Name() == "README.md" { 45 | input = regexp.MustCompile(`https:\/\/github\.com\/astaxie\/build-web-application-with-golang\/blob\/master\/`).ReplaceAllString(input, "") 46 | } 47 | 48 | // 以#开头的行,在#后增加空格 49 | // 以#开头的行, 删除多余的空格 50 | input = FixHeader(input) 51 | 52 | // 删除页面链接 53 | input = RemoveFooterLink(input) 54 | 55 | var out *os.File 56 | filename := strings.Replace(f.Name(), ".md", ".html", -1) 57 | fmt.Println(to + "/" + filename) 58 | if out, err = os.Create(to + "/" + filename); err != nil { 59 | fmt.Fprintf(os.Stderr, "Error creating %s: %v", f.Name(), err) 60 | os.Exit(-1) 61 | } 62 | defer out.Close() 63 | md := md2min.New("none") 64 | err = md.Parse([]byte(input), out) 65 | if err != nil { 66 | fmt.Fprintln(os.Stderr, "Parsing Error", err) 67 | os.Exit(-1) 68 | } 69 | 70 | return nil 71 | }) 72 | return err 73 | } 74 | 75 | func FixHeader(input string) string { 76 | re_header := regexp.MustCompile(`(?m)^#.+$`) 77 | re_sub := regexp.MustCompile(`^(#+)\s*(.+)$`) 78 | fixer := func(header string) string { 79 | s := re_sub.FindStringSubmatch(header) 80 | return s[1] + " " + s[2] 81 | } 82 | return re_header.ReplaceAllStringFunc(input, fixer) 83 | } 84 | 85 | func RemoveFooterLink(input string) string { 86 | re_footer := regexp.MustCompile(`(?m)^#{2,} links.*?\n(.+\n)*`) 87 | return re_footer.ReplaceAllString(input, "") 88 | } 89 | 90 | func main() { 91 | html := os.Getenv("HTML") 92 | if html == "" { 93 | html = "." 94 | } 95 | 96 | src := os.Getenv("WORKDIR") 97 | if src == "" { 98 | src = "." 99 | } 100 | 101 | arg := map[string]string{ 102 | "from": src, 103 | "to": html, 104 | } 105 | 106 | v := &Visitor{} 107 | err := v.md2html(arg) 108 | if err != nil { 109 | fmt.Printf("filepath.Walk() returned %v\n", err) 110 | } 111 | } 112 | --------------------------------------------------------------------------------