前端学习 C 语言 —— GDB调试器
GDB调试器
我们在讲指针时用 GDB
调试段错误。
本篇将详细介绍 gdb 的最常用命令
、日志记录
、检测点
,最后介绍如何用 gdb 调试进程
以及用gdb 调试一个开源项目的调试版本
—— glmark2。
gdb介绍
GDB, the GNU Project debugger —— gdb官网
gdb 是一款调试器,能打断点。支持多种语言,例如 c、c++、go。
Tip:有关 GNU Project,请看本篇扩展
。
官网显示最新版本是13.2(20230704)。点击官网顶部[documentation]
可查看文档。
安装GDB
笔者已经用 apt 源安装了gbd:
jjj-pc:~/pj/glmark2$ sudo apt install gdb
正在读取软件包列表... 完成
正在分析软件包的依赖关系树
正在读取状态信息... 完成
gdb 已经是最新版 (9.1-0kylin1)。
下列软件包是自动安装的并且现在不需要了:
archdetect-deb dmeventd libaio1 libdebian-installer4 libdevmapper-event1.02.1 liblvm2cmd2.03 localechooser-data lvm2 user-setup
使用'sudo apt autoremove'来卸载它(它们)。
升级了 0 个软件包,新安装了 0 个软件包,要卸载 0 个软件包,有 6 个软件包未被升级。
笔者gbd版本是 9.1
jjj-pc:~/pj/glmark2$ gdb --version
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
最常用命令
man gdb
告诉我们最常用的命令有:break、run、print、c、next、list、step、quit。
// 以下是一些最常用的GDB命令:
Here are some of the most frequently needed GDB commands:
break [file:]function
Set a breakpoint at function (in file).
run [arglist]
Start your program (with arglist, if specified).
bt Backtrace: display the program stack.
print expr
// 显示表达式的值
Display the value of an expression.
c Continue running your program (after stopping, e.g. at a breakpoint).
next
// 执行下一条程序语句(在停止后);跳过该行中的任何函数调用。
Execute next program line (after stopping); step over any function calls in the line.
edit [file:]function
look at the program line where it is presently stopped.
list [file:]function
type the text of the program in the vicinity of where it is presently stopped.
step
Execute next program line (after stopping); step into any function calls in the line.
help [name]
Show information about GDB command name, or general information about using GDB.
quit
Exit from GDB.
准备一段 C 代码用作gdb命令学习:
#include <stdio.h>
// add 函数
int add(int a, int b) {
int sum = a + b;
return sum;
}
int main() {
int num1 = 3;
int num2 = 5;
int result = add(num1, num2);
printf("两个整数的和为:%d\n", result);
return 0;
}
run 和 quit
通过 gdb demo
运行进入gdb模式,输入 run
运行程序,输入 quit
则退出gdb。详细请看:
// 通过 -g 编译出有调试信息的可执行文件
jjj-pc:~/pj$ gcc demo.c -o demo -g
// gdb 运行
jjj-pc:~/pj$ gdb demo
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo...
// 输入 run 运行程序
(gdb) run
Starting program: /home/jjj/pj/demo
两个整数的和为:8
[Inferior 1 (process 3022770) exited normally]
// 输入 quit 退出
(gdb) quit
jjj-pc:~/pj$
Tip: ctrl + z 能直接退出gdb
list
如果不知道在哪行或哪个方法打断点,可以通过 list 查看源码。请看示例:
jjj-pc:~/pj$ gdb demo
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo...
// 查看源码
(gdb) list
1 #include <stdio.h>
2
3 // 自定义函数,用于计算两个整数的和
4 int add(int a, int b) { // a, b 叫形参
5 int sum = a + b;
6 return sum;
7 }
8
9 int main() {
10 int num1 = 3;
// 一次显示不完,继续查看后面10行
(gdb) list
11 int num2 = 5;
12
13 // 调用自定义函数计算两个整数的和
14 int result = add(num1, num2);
15
16 printf("两个整数的和为:%d\n", result);
17
18 return 0;
19 }
// 到底了。
(gdb) list
Line number 20 out of range; demo.c has 19 lines.
// 查看5到10行
(gdb) list 5,10
5 int sum = a + b;
6 return sum;
7 }
8
9 int main() {
10 int num1 = 3;
(gdb)
break 和 info break
break
(简写 b) 用于打断点,info break
用于查看打了哪些断点。请看示例:
jjj-pc:~/pj$ gdb demo
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo...
// 给 mian 方法打断点
(gdb) break main
Breakpoint 1 at 0x1167: file demo.c, line 9.
// 给11行打断点
(gdb) b 11
Breakpoint 2 at 0x117a: file demo.c, line 11.
// 查看打了哪些断点
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000001167 in main at demo.c:9
2 breakpoint keep y 0x000000000000117a in main at demo.c:11
// 查看打了哪些断点
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000001167 in main at demo.c:9
2 breakpoint keep y 0x000000000000117a in main at demo.c:11
next 和 step
代码中断后,输入 next 和 step 都会执行下一行,然而 next 会跳过函数,step 会进入函数。请看示例:
- next 跳过函数
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo...
// 打断点
(gdb) b 9
Breakpoint 1 at 0x1167: file demo.c, line 9.
// 运行
(gdb) run
Starting program: /home/jjj/pj/demo
// 在断点处停止
Breakpoint 1, main () at demo.c:9
9 int main() {
// 下一行
(gdb) next
10 int num1 = 3;
(gdb) next
// 下一行
11 int num2 = 5;
(gdb) next
// 下一行。跳过函数
14 int result = add(num1, num2);
(gdb) next
// 下一行
16 printf("两个整数的和为:%d\n", result);
(gdb) next
两个整数的和为:8
18 return 0;
- step 进入函数
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo...
(gdb) b 9
Breakpoint 1 at 0x1167: file demo.c, line 9.
(gdb) run
Starting program: /home/jjj/pj/demo
Breakpoint 1, main () at demo.c:9
9 int main() {
(gdb) step
10 int num1 = 3;
(gdb) step
11 int num2 = 5;
(gdb) step
14 int result = add(num1, num2);
(gdb) step
add (a=21845, b=1431654909) at demo.c:4
4 int add(int a, int b) {
(gdb) step
5 int sum = a + b;
(gdb) step
6 return sum;
(gdb) step
7 }
(gdb) step
main () at demo.c:16
16 printf("两个整数的和为:%d\n", result);
(gdb) step
两个整数的和为:8
18 return 0;
continue
next 和 step 会执行下一行,而continue(简写c) 会执行到下一个断点
处停止。 请看示例:
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo...
// 断点
(gdb) b main
Breakpoint 1 at 0x1167: file demo.c, line 9.
// 断点
(gdb) b 16
Breakpoint 2 at 0x1193: file demo.c, line 16.
// 运行后在第一断点处停止
(gdb) run
Starting program: /home/jjj/pj/demo
Breakpoint 1, main () at demo.c:9
9 int main() {
// 下一个断点
(gdb) c
Continuing.
Breakpoint 2, main () at demo.c:16
16 printf("两个整数的和为:%d\n", result);
(gdb)
print 用于查看表达式的值。请看示例:
// 在11行打断点,查看 num1
(gdb) list 9,11
9 int main() {
10 int num1 = 3;
11 int num2 = 5;
(gdb) b 11
Breakpoint 1 at 0x117a: file demo.c, line 11.
(gdb) run
Starting program: /home/jjj/pj/demo
Breakpoint 1, main () at demo.c:11
11 int num2 = 5;
// 查看num1的值
(gdb) print num1
$1 = 3
// 查看num1的地址
(gdb) print &num1
$2 = (int *) 0x7fffffffe2c4
gdb 小技巧
shell
gdb 可以执行 shell 命令。请看示例:
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo2...
// ll 命令没有
(gdb) shell ll
bash: ll:未找到命令
// 执行 ls 命令
(gdb) shell ls
demo2 demo2.c
// 执行 cat 命令
(gdb) shell cat demo2.c
#include <stdio.h>
int main() {
int i;
for (i = 1; i <= 5; i++) {
printf("Iteration %d\n", i);
}
return 0;
}
日志记录
set logging on
用于打开日志记录功能。该命令执行后,GDB将记录所有交互式会话的输入和输出到一个日志文件中。请看示例:
jjj-pc:~/pj/dir1$ gdb demo2
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo2...
// 开启日志功能
(gdb) set logging on
Copying output to gdb.txt.
Copying debug output to gdb.txt.
// 以下是一系列操作
(gdb) info b
No breakpoints or watchpoints.
(gdb) list
1 #include <stdio.h>
2
3 int main() {
4 int i;
5
6 for (i = 1; i <= 5; i++) {
7 printf("Iteration %d\n", i);
8 }
9
10 return 0;
(gdb) b 4
Breakpoint 1 at 0x1155: file demo2.c, line 6.
(gdb) run
Starting program: /home/jjj/pj/dir1/demo2
Breakpoint 1, main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
(gdb) n
7 printf("Iteration %d\n", i);
(gdb) n
Iteration 1
6 for (i = 1; i <= 5; i++) {
(gdb) print i
$1 = 1
(gdb) c
Continuing.
Iteration 2
Iteration 3
Iteration 4
Iteration 5
[Inferior 1 (process 3040270) exited normally]
(gdb) quit
// 退出 gdb 后,发现同级目录生成一个叫 gdb.txt 文件。
jjj-pc:~/pj/dir1$ ls
demo2 demo2.c gdb.txt
// 日志文件内容
jjj-pc:~/pj/dir1$ cat gdb.txt
No breakpoints or watchpoints.
1 #include <stdio.h>
2
3 int main() {
4 int i;
5
6 for (i = 1; i <= 5; i++) {
7 printf("Iteration %d\n", i);
8 }
9
10 return 0;
Breakpoint 1 at 0x1155: file demo2.c, line 6.
Starting program: /home/jjj/pj/dir1/demo2
Breakpoint 1, main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
7 printf("Iteration %d\n", i);
6 for (i = 1; i <= 5; i++) {
$1 = 1
Continuing.
[Inferior 1 (process 3040270) exited normally]
监视点
GDB中的"watch points"(监视点)是一种调试功能,用于在程序执行期间监视变量
或内存地址
的更改。请看示例:
比如有个大程序,我要查看某个变量变化状态,被谁修改的。
用以下示例模拟,假如要监视变量 i 的变化情况。做法如下:
jjj-pc:~/pj/dir1$ cat demo2.c
#include <stdio.h>
int main() {
int i;
for (i = 1; i <= 5; i++) {
printf("Iteration %d\n", i);
}
return 0;
}
- 方式1:监视变量
jjj-pc:~/pj/dir1$ gdb demo2
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo2...
// 在 `int i` 处打断点
(gdb) b 4
Breakpoint 1 at 0x1155: file demo2.c, line 6.
// 运行,并在断点处中断
(gdb) run
Starting program: /home/jjj/pj/dir1/demo2
Breakpoint 1, main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
// 监视变量 i
(gdb) watch i
Hardware watchpoint 2: i
(gdb) info watchpoinst
Undefined info command: "watchpoinst". Try "help info".
// 使用 info watchpoints 命令查看所有当前设置的监视点的状态
(gdb) info watchpoints
Num Type Disp Enb Address What
2 hw watchpoint keep y i
// 继续。在 i 的值变化时中断,现在是1,是在第 6 行改变的
(gdb) c
Continuing.
Hardware watchpoint 2: i
// 旧值 0 变成 新值 1
Old value = 0
New value = 1
main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
// 继续
(gdb) c
Continuing.
Iteration 1
Hardware watchpoint 2: i
Old value = 1
New value = 2
0x0000555555555178 in main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
// 继续
(gdb) c
Continuing.
Iteration 2
Hardware watchpoint 2: i
Old value = 2
New value = 3
...
- 方式2:监视内存地址指向的内容
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo2...
(gdb) b 4
Breakpoint 1 at 0x1155: file demo2.c, line 6.
(gdb) run
Starting program: /home/jjj/pj/dir1/demo2
Breakpoint 1, main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
// 取得变量的地址
(gdb) print &i
$1 = (int *) 0x7fffffffe2bc
// 使用 * 操作符是因为你想要监视内存地址 0x7fffffffe2bc 处的内容,而不是该地址本身。
(gdb) watch *0x7fffffffe2bc
Hardware watchpoint 2: *0x7fffffffe2bc
// 继续。在 i 的值变化时中断,现在是1,是在第 6 行改变的
(gdb) c
Continuing.
Hardware watchpoint 2: *0x7fffffffe2bc
Old value = 0
New value = 1
main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
(gdb) c
Continuing.
Iteration 1
Hardware watchpoint 2: *0x7fffffffe2bc
Old value = 1
New value = 2
0x0000555555555178 in main () at demo2.c:6
6 for (i = 1; i <= 5; i++) {
(gdb) c
...
扩展
GNU项目
GNU(GNU's Not Unix)项目是由Richard Stallman于1983年发起的一个自由软件运动。该项目旨在创建一个完全自由和开源的操作系统,以提供用户自由运行、研究、分发和改进软件的权利。
GNU项目的目标是为用户提供一个类似Unix的操作系统,但不同于Unix,它完全由自由软件组成。自由软件指的是用户拥有运行、复制、分发、研究和修改软件的自由。GNU项目的理念是,用户应该拥有对计算机软件的完全控制权,能够自由地使用、学习和修改软件,从而推动自由软件社区的发展和分享。
为实现这一目标,GNU项目开发了一系列自由软件工具和应用程序,如GNU编译器套件(GCC
)、GNU调试器(GDB
)、GNU Bash shell
、GNU Core Utilities等。此外,GNU项目还推动了GNU宇宙文档计划,提供了丰富的自由文档资源,帮助用户学习和使用自由软件。
总而言之,GNU项目是一个致力于推广自由软件概念的运动,旨在提供一个完全自由和开源的操作系统。通过开发和推广自由软件工具,GNU项目为用户提供了更多选择和控制权,同时促进了自由软件社区的发展。
core文件
Core文件是操作系统在程序崩溃或异常终止时生成的一种特殊文件。它记录了程序发生异常时的内存状态、寄存器状态和其他调试信息,以便后续进行调试。
Core文件通常与可执行文件位于同一目录,并以文件名前缀为 "core" 开头,后跟进程ID号。例如,如果程序的可执行文件名为 "my_program",并且进程ID为1234,则生成的core文件可能命名为 "core.1234"。
操作系统是否允许生成core文件,可以使用ulimit命令。ulimit -c
如果返回值是0,则表示禁止生成core文件。如果返回值不是0,则表示core文件生成被允许,并且返回值表示core文件的最大大小(以KB为单位)。
笔者这里对核文件没有限制:
// 核文件的最大大小无限制
jjj-pc:~/pj/dir1$ ulimit -c
unlimited
Tip:ulimit 用于修改 shell 资源限制。
jjj-pc:~/pj/dir1$ ulimit --help
ulimit: ulimit [-SHabcdefiklmnpqrstuvxPT] [限制]
修改 shell 资源限制。
在允许此类控制的系统上,提供对于 shell 及其创建的进程所可用的
资源的控制。
选项:
-S 使用软 (`soft') 资源限制
-H 使用硬 (`hard') 资源限制
-a 所有当前限制都被报告
-b 套接字缓存尺寸
-c 创建的核文件的最大尺寸
-d 一个进程的数据区的最大尺寸
-e 最高的调度优先级 (`nice')
-f 有 shell 及其子进程可以写的最大文件尺寸
-i 最多的可以挂起的信号数
-k 分配给此进程的最大 kqueue 数量
-l 一个进程可以锁定的最大内存尺寸
-m 最大的内存进驻尺寸
-n 最多的打开的文件描述符个数
-p 管道缓冲区尺寸
-q POSIX 信息队列的最大字节数
-r 实时调度的最大优先级
-s 最大栈尺寸
-t 最大的CPU时间,以秒为单位
-u 最大用户进程数
-v 虚拟内存尺寸
-x 最大的文件锁数量
-P 最大伪终端数量
-T 最大线程数量
// 所有当前限制都被报告
jjj-pc:~/pj/dir1$ ulimit -a
// core文件大小限制为无限制
core file size (blocks, -c) unlimited
// 数据段大小限制为无限制
data seg size (kbytes, -d) unlimited
// 调度优先级限制为0。
scheduling priority (-e) 0
// 文件大小限制为无限制。用于限制单个文件的最大大小
file size (blocks, -f) unlimited
// 待处理信号数量限制为31429。
pending signals (-i) 31429
// 最大锁定内存大小限制为65536 KB。
max locked memory (kbytes, -l) 65536
// 最大内存大小限制为无限制。
max memory size (kbytes, -m) unlimited
// 打开文件数量限制为1024。
open files (-n) 1024
// 管道大小限制为512字节。
pipe size (512 bytes, -p) 8
// POSIX消息队列大小限制为819200字节。
POSIX message queues (bytes, -q) 819200
// 实时优先级限制为0
real-time priority (-r) 0
// 栈大小限制为8192 KB。用于限制每个进程栈的大小
stack size (kbytes, -s) 8192
// 用于限制一个进程在CPU上运行的最长时间
cpu time (seconds, -t) unlimited
// 最大用户进程数限制为31429
max user processes (-u) 31429
// 虚拟内存大小限制为无限制。
virtual memory (kbytes, -v) unlimited
// 文件锁定数量限制为无限制。
file locks (-x) unlimited
创建一个会产生core文件的程序:
#include <stdio.h>
int main() {
int* ptr = NULL;
*ptr = 10; // 访问空指针将导致段错误
return 0;
}
疑惑
:笔者在调试台中不能生成 core 文件,暂时终止GDB core 文件。
进程调试
写个一直循环的程序:
jjj-pc:~/pj$ cat dir1/demo.c
#include <stdio.h>
#include <unistd.h>
int main() {
while (1) {
printf("Hello, World!\n");
sleep(1); // 暂停1秒钟
}
return 0;
}
编译运行,得到进程号:
jjj-pc:~/pj/dir1$ gcc demo.c -o demo -g
// & 只后台运行
jjj-pc:~/pj/dir1$ ./demo &
[1] 3072058
通过 gdb -p 3072058
即可调试进程:
// gdb 进程
jjj-pc:~/pj/dir1$ gdb -p 3072058
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
Attaching to process 3072058
Reading symbols from /home/jjj/pj/dir1/demo...
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
(No debugging symbols found in /lib/x86_64-linux-gnu/libc.so.6)
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
0x00007f3ae9acc1e4 in clock_nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
// 查看源码
(gdb) list
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main() {
5 while (1) {
6 printf("Hello, World!\n");
7 sleep(1); // 暂停1秒钟
8 }
9
10 return 0;
// next
(gdb) n
Single stepping until exit from function clock_nanosleep,
which has no line number information.
0x00007f3ae9ad1ef7 in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) n
Single stepping until exit from function nanosleep,
which has no line number information.
0x00007f3ae9ad1e2e in sleep () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) n
Single stepping until exit from function sleep,
which has no line number information.
main () at demo.c:6
6 printf("Hello, World!\n");
// next
(gdb) n
7 sleep(1); // 暂停1秒钟
// next
(gdb) n
6 printf("Hello, World!\n");
// next
(gdb) n
7 sleep(1); // 暂停1秒钟
(gdb) n
6 printf("Hello, World!\n");
(gdb)
Tip:man gdb
中也有gdb进程或core的介绍:
You can also start with both an executable program and a core file specified:
gdb program core
You can, instead, specify a process ID as a second argument or use option "-p", if you want to debug a running process:
gdb program 1234
gdb -p 1234
glmark2 调试版本
glmark2是一个用于测试OpenGL渲染性能的工具,它通过运行一系列图形场景和基准测试来评估计算机的图形处理能力。
笔者需要调试版本
进行定位,故决定源码编译。
- 首先下载项目
git clone https://github.com/glmark2/glmark2.git
cd glmark2
- 修改 glmark2/wscript 文件,在
def options(
处增加一行
def options(opt):
opt.add_option('--debug', action='store_true', default=False, help='Enable debug mode')
通过 ./waf --help
则会显示 --debug
jjj-pc:~/pj/glmark2$ ./waf --help
waf [commands] [options]
...
Options:
--version show program's version number and exit
...
// debug
--debug Build with debug symbols
- 运行
./waf configure
命令来配置构建环境
jjj-pc:~/pj/glmark2$ ./waf configure --with-flavors=x11-gl --debug
Setting top to : /home/jjj/pj/glmark2
Setting out to : /home/jjj/pj/glmark2/build
Checking for 'gcc' (C compiler) : /usr/bin/gcc
Checking for 'g++' (C++ compiler) : /usr/bin/g++
Checking for header stdlib.h : yes
Checking for header string.h : yes
Checking for header stdint.h : yes
Checking for header stdio.h : yes
Checking for header dlfcn.h : yes
Checking for header unistd.h : yes
Checking for header jpeglib.h : yes
Checking for header math.h : yes
Checking for header string.h : yes
Checking for library m : yes
Checking for library jpeg : yes
Checking for function memset : yes
Checking for function sqrt : yes
Checking for program 'pkg-config' : /usr/bin/pkg-config
Checking for 'libpng12' : yes
Checking for 'x11' : yes
Checking for 'libdrm' : yes
Checking for 'gbm' : yes
Checking for 'libudev' : yes
Checking for 'wayland-client' : yes
Checking for 'wayland-cursor' : yes
Checking for 'wayland-egl' : yes
Prefix : /usr/local
Data path : /usr/local/share/glmark2
Including extras : No
Building flavors : ['x11-gl']
'configure' finished successfully (2.189s)
注
:笔者安装了一些依赖包,由于未做笔记,也就忘记了,请读者自行完成,通常根据报错也能搜索到缺少什么包。
Tip: 关于调试版除了增加 --debug,还可以直接修改 wscript 中如下代码:
// 修改前
if is_win:
configure_win32(ctx)
else:
configure_linux(ctx)
// 修改后
if is_win:
configure_win32(ctx)
ctx.env.append_unique('CXXFLAGS', '/Zi')
ctx.env.append_unique('LINKFLAGS', '/DEBUG')
else:
configure_linux(ctx)
ctx.env.append_unique('CXXFLAGS', '-g')
ctx.env.append_unique('LINKFLAGS', '-g')
- 运行
./waf build
命令来编译glmark2的调试版本
jjj-pc:~/pj/glmark2$ ./waf build
Waf: Entering directory `/home/jjj/pj/glmark2/build'
[ 1/71] Compiling src/main.cpp
[ 2/71] Compiling src/canvas-generic.cpp
[ 3/71] Compiling src/native-state-x11.cpp
[ 4/71] Compiling src/gl-state-glx.cpp
[ 5/71] Compiling src/glad/src/glx.c
...
[64/71] Compiling src/scene.cpp
[65/71] Compiling src/shared-library.cpp
[66/71] Compiling src/text-renderer.cpp
[67/71] Compiling src/texture.cpp
[68/71] Compiling doc/glmark2.1.in
[69/71] Linking build/src/libmatrix-gl.a
[70/71] Linking build/src/libcommon-gl.a
[71/71] Linking build/src/glmark2
Waf: Leaving directory `/home/jjj/pj/glmark2/build'
'build' finished successfully (36.123s)
jjj-pc:~/pj/glmark2$
- 查看生成的 glmark2 可执行文件,并通过 gdb 给 main 函数打断点
// 查找生成的可执行文件位置
jjj-pc:~/pj/glmark2$ find -name glmark2
./android/src/org/linaro/glmark2
./build/src/glmark2
// gdb 可执行文件
jjj-pc:~/pj/glmark2$ gdb ./build/src/glmark2
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./build/src/glmark2...
// 查看源码。说明已经是调试版本
(gdb) list
146 {
147 std::stringstream ss;
148
149 for (int i = 0; i < argc; i++)
150 {
151 if (i > 0) ss << " ";
152 ss << argv[i];
153 }
154
155 return ss.str();
(gdb) b main
Breakpoint 1 at 0x1b670: file ../src/main.cpp, line 160.
(gdb) run
Starting program: /home/jjj/pj/glmark2/build/src/glmark2
Breakpoint 1, main (argc=1, argv=0x7fffffffe388) at ../src/main.cpp:160
160 {
(gdb) n
162 NativeStateX11 native_state;
(gdb) n
175 if (!Options::parse_args(argc, argv))
(gdb) n
179 Log::init(Util::appname_from_path(argv[0]), Options::show_debug);
(gdb) list 179
174
175 if (!Options::parse_args(argc, argv))
176 return 1;
177
178 /* Initialize Log class */
179 Log::init(Util::appname_from_path(argv[0]), Options::show_debug);
180
181 if (!ResultsFile::init(Options::results_file)) {
182 Log::error("%s: Could not initialize results file\n", __FUNCTION__);
183 return 1;
(gdb) list 178
173 #endif
174
175 if (!Options::parse_args(argc, argv))
176 return 1;
177
178 /* Initialize Log class */
179 Log::init(Util::appname_from_path(argv[0]), Options::show_debug);
180
181 if (!ResultsFile::init(Options::results_file)) {
182 Log::error("%s: Could not initialize results file\n", __FUNCTION__);
(gdb) n
80 new_allocator() _GLIBCXX_USE_NOEXCEPT { }
(gdb) n
179 Log::init(Util::appname_from_path(argv[0]), Options::show_debug);
(gdb)
Tip:如果 gdb 对应的可执行文件不是一个调试版本,输入 list 是看不到源码的
gdb 中断
笔者需要排查一个glmark2应用卡住的问题,由于现象是偶现的,所以才有上面编译glmark2调试版本
的需求。
打算用gdb运行glmark2调试版本,当glmark2异常时,gdb会中断,然后分析问题。
注
:如果看起来 glmark2 卡住,但其实并没有卡死,还在好好地跑着,那么这个调试方法就没有用
我们用示例演示一下 glmark2 异常,gdb 会中断。还是上文进程调试
的示例:
gdb 运行程序:
jjj-pc:~/pj/dir1$ cat demo.c
#include <stdio.h>
#include <unistd.h>
int main() {
while (1) {
sleep(1); // 暂停1秒钟
}
return 0;
}
jjj-pc:~/pj/dir1$ gcc demo.c -o demo -g
jjj-pc:~/pj/dir1$ gdb demo
GNU gdb (Ubuntu 9.1-0kylin1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo...
(gdb) list
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main() {
5 while (1) {
6 sleep(1); // 暂停1秒钟
7 }
8
9 return 0;
10 }
// 运行程序
(gdb) run
Starting program: /home/jjj/pj/dir1/demo
下面我们用 kill 发送信号模拟程序出错
。
kill 用于给进程发送信号。它有很多信号,平时常写的 kill -9 111
等于kill -s SIGKILL 111
,也就是强制中止进程。kill -11
用于向进程发送 SIGSEGV信号的命令,SIGSEGV是一种段错误信号,用于指示进程访问了无效的或不允许访问的内存地址,导致了段错误。
// kill - 给进程发送信号
jjj-pc:~$ whatis kill
kill (1) - send a signal to a process
// 有这么多信息
// kill -9 111 等于 kill -s SIGKILL 111
jjj-pc:~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
jjj-pc:~$
查看刚才运行程序的进程号,并给该进程发送段错误信息。
jjj-pc:~$ ps aux |grep demo
jjj 2056 0.0 0.0 240000 4744 ? Sl 6月07 0:00 /usr/libexec/geoclue-2.0/demos/agent
// 我们的程序路径就是 /home/jjj/pj/dir1/
jjj 3119734 0.0 0.0 2356 588 pts/1 S+ 19:57 0:00 /home/jjj/pj/dir1/demo
jjj 3119754 0.0 0.0 12120 720 pts/6 S+ 19:57 0:00 grep --color=auto demo
// 给进程发送段错误的信号
jjj-pc:~$ kill -11 3119734
gdb 接收到信号,于是中断如下:
Starting program: /home/jjj/pj/dir1/demo
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e991e4 in clock_nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
(gdb)
在使用GDB(GNU调试器)运行一个带有调试信息的程序时,除了程序发生段错误导致GDB中断之外,还有其他一些场景可能导致GDB中断:断点、手动暂停(按下Ctrl+C
键)、接收信号(如SIGINT)、异常情况。
出处:https://www.cnblogs.com/pengjiali/p/17526874.html
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。