一般来说 GDB 主要调试的是 C/C++的程序。要调试 C/C++的程序,首先在
编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)
的 -g 参数可以做到这一点,如:
gcc -g main.cc -o main
如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。
启动 GDB 的方法有以下几种:
- gdb <program>
program 也就是你的执行文件,一般在当然目录下。
- gdb <program> core
用 gdb 同时调试一个运行程序和 core 文件,core 是程序非法执行后 core dump 后产生的文件。
- gdb <program> <PID>
如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb 会自动 attach 上去,并调试他。program 应该在 PATH 环境变量中搜索得到。
调试已经运行的程序两种方法:
方法1:在 UNIX 下用 ps 查看正在运行的程序的 PID(进程 ID),然后用 gdb <program> PID 格式挂接正在运行的程序;
方法2:先用 gdb <program>关联上源代码,并进行 gdb,在 gdb 中用 attach,命令来挂接进程的 PID,并用 detach 来取消挂接的进程。
GDB 启动时,可以加上一些 GDB 的启动开关,详细的开关可以用 gdb -help查看,一些比较常用的参数:
-symbols <file>
解释:从指定文件中读取符号表。
-se file
解释:从指定文件中读取符号表信息,并把他用在可执行文件中
-core <file> 或者 -c <file>
解释:调试时 core dump 的 core 文件
-directory <directory>或者-d <directory>
解释:加入一个源文件的搜索路径。默认搜索路径是环境变量中 PATH 所定义的路
径。
名词解释:
- GDB符号表
在编译可执行文件时,编译器将生成一个包含程序符号信息的文件,这个文件被称为符号表。GDB(GNU Debugger)使用符号表来解释可执行文件中的符号,例如函数名、变量名和类型信息。符号表包含了各种符号的相关信息,这些信息包括函数和变量的名称、类型、作用域以及符号的位置等。在使用 GDB 进行调试时,它会尝试加载可执行文件的符号表。加载符号表后,GDB 将能够根据符号名称查找对应的函数和变量,并提供符号级别的调试信息,如断点设置、变量监视和栈跟踪等。如,gcc -g main.cc -o main,则符号表在main中。
案例1:
drwxrwx--- 2 427631 domain_users 4096 2024-03-02 13:56 ./
drwxrwx--- 3 427631 domain_users 4096 2024-03-02 11:29 ../
-rwxr-xr-x 1 427631 domain_users 8340 2024-03-02 13:56 01_test*
-rwxrw---- 1 427631 domain_users 359 2024-03-02 13:55 tes1.cc*
[root]$ gdb
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) shell ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:49 pts/0 00:00:00 /bin/bash -c groupadd -g 1000513 domain_users 2>/dev/null ; export SHADOW_ALLOW_ALL_NUMERIC_USER=1 && useradd -d
root 23 1 0 13:49 pts/0 00:00:00 su - 427631
root 24 23 0 13:49 pts/0 00:00:00 -bash
root 59 24 0 13:56 pts/0 00:00:00 ./01_test
root 60 0 0 13:57 pts/1 00:00:00 su - 427631
root 66 60 0 13:57 pts/1 00:00:00 -bash
root 115 66 0 14:36 pts/1 00:00:00 gdb
root 117 115 0 14:36 pts/1 00:00:00 ps -ef
(gdb) attach 59
Attaching to process 59
ptrace: Operation not permitted.
(gdb) file 01_test
Reading symbols from /home/root/code_study/01_test/01_test...done.
(gdb) l
9 sum+=i;
10 }
11 return sum;
12 }
13
14
15 int main(void)
16 {
17 int i;
18 long result = 0;
(gdb)
19 while(true)
20 {
21 result ++;
22 sleep(1000);
23 }
24
25 printf("result[1-100] = %d \n", result );
26 printf("result[1-250] = %d \n", func(250) );
27 }(gdb) b 21
Breakpoint 1 at 0x4005c7: file tes1.cc, line 21.
(gdb) r
Starting program: /home/m/code_study/01_test/01_test
warning: Error disabling address space randomization: Operation not permitted
Breakpoint 1, main () at tes1.cc:21
案例2:
假设你正在调试一个项目,该项目有以下文件结构:
project/
├── src/
│ ├── main.c
│ ├── utils/
│ │ └── helper.c
│ └── include/
│ ├── main.h
│ └── utils/
│ └── helper.h
└── build/
├── main.o
└── utils/
└── helper.o
你在 build
目录下编译生成了可执行文件 a.out
。接下来,你要使用 GDB 进行调试。由于 GDB 默认在当前工作目录下搜索源文件,你可以通过设置源文件路径来使 GDB 能够正确找到源文件。
gdb a.out
(gdb) dir /path/to/project/src
现在,GDB 已经被告知在 /path/to/project/src
目录下查找源文件。
假设你在 main.c
中设置了一个断点,现在 GDB 就能找到正确的源文件并在调试会话中显示相应的源代码。
设置源文件路径可以简化调试多个源文件的项目,使得 GDB 能够更准确地定位和显示源代码,提供更好的调试体验