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 --versionGNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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 // 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 demoGNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from demo...// 输入 run 运行程序(gdb) runStarting program: /home/jjj/pj/demo两个整数的和为:8[Inferior 1 (process 3022770) exited normally]// 输入 quit 退出(gdb) quitjjj-pc:~/pj$

Tip: ctrl + z 能直接退出gdb

list

如果不知道在哪行或哪个方法打断点,可以通过 list 查看源码。请看示例:

jjj-pc:~/pj$ gdb demoGNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from demo...// 查看源码(gdb) list1       #include 23       // 自定义函数,用于计算两个整数的和4       int add(int a, int b) { // a, b 叫形参5           int sum = a + b;6           return sum;7       }89       int main() {10          int num1 = 3;// 一次显示不完,继续查看后面10行(gdb) list11          int num2 = 5;1213          // 调用自定义函数计算两个整数的和14          int result = add(num1, num2); 1516          printf("两个整数的和为:%d\n", result);1718          return 0;19      }// 到底了。(gdb) listLine number 20 out of range; demo.c has 19 lines.// 查看5到10行(gdb) list 5,105           int sum = a + b;6           return sum;7       }89       int main() {10          int num1 = 3;(gdb)

break 和 info break

break(简写 b) 用于打断点,info break 用于查看打了哪些断点。请看示例:

jjj-pc:~/pj$ gdb demoGNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from demo...// 给 mian 方法打断点(gdb) break mainBreakpoint 1 at 0x1167: file demo.c, line 9.// 给11行打断点(gdb) b 11Breakpoint 2 at 0x117a: file demo.c, line 11.// 查看打了哪些断点(gdb) info bNum     Type           Disp Enb Address            What1       breakpoint     keep y   0x0000000000001167 in main at demo.c:92       breakpoint     keep y   0x000000000000117a in main at demo.c:11// 查看打了哪些断点(gdb) info breakNum     Type           Disp Enb Address            What1       breakpoint     keep y   0x0000000000001167 in main at demo.c:92       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 9Breakpoint 1 at 0x1167: file demo.c, line 9.// 运行(gdb) runStarting program: /home/jjj/pj/demo// 在断点处停止Breakpoint 1, main () at demo.c:99       int main() {// 下一行(gdb) next10          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两个整数的和为:818          return 0;
  • step 进入函数
Type "apropos word" to search for commands related to "word"...Reading symbols from demo...(gdb) b 9Breakpoint 1 at 0x1167: file demo.c, line 9.(gdb) runStarting program: /home/jjj/pj/demoBreakpoint 1, main () at demo.c:99       int main() {(gdb) step10          int num1 = 3;(gdb) step11          int num2 = 5;(gdb) step14          int result = add(num1, num2); (gdb) stepadd (a=21845, b=1431654909) at demo.c:44       int add(int a, int b) { (gdb) step5           int sum = a + b;(gdb) step6           return sum;(gdb) step7       }(gdb) stepmain () at demo.c:1616          printf("两个整数的和为:%d\n", result);(gdb) step两个整数的和为:818          return 0;

continue

next 和 step 会执行下一行,而continue(简写c) 会执行到下一个断点处停止。 请看示例:

Type "apropos word" to search for commands related to "word"...Reading symbols from demo...// 断点(gdb) b mainBreakpoint 1 at 0x1167: file demo.c, line 9.// 断点(gdb) b 16Breakpoint 2 at 0x1193: file demo.c, line 16.// 运行后在第一断点处停止(gdb) runStarting program: /home/jjj/pj/demoBreakpoint 1, main () at demo.c:99       int main() {// 下一个断点(gdb) cContinuing.Breakpoint 2, main () at demo.c:1616          printf("两个整数的和为:%d\n", result);(gdb)

print

print 用于查看表达式的值。请看示例:

// 在11行打断点,查看 num1 (gdb) list 9,119       int main() {10          int num1 = 3;11          int num2 = 5;(gdb) b 11Breakpoint 1 at 0x117a: file demo.c, line 11.(gdb) runStarting program: /home/jjj/pj/demoBreakpoint 1, main () at demo.c:1111          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 llbash: ll:未找到命令// 执行 ls 命令(gdb) shell lsdemo2  demo2.c// 执行 cat 命令(gdb) shell cat demo2.c#include 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 demo2GNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from demo2...// 开启日志功能(gdb) set logging onCopying output to gdb.txt.Copying debug output to gdb.txt.// 以下是一系列操作(gdb) info bNo breakpoints or watchpoints.(gdb) list1       #include 23       int main() {4           int i;56           for (i = 1; i <= 5; i++) {7               printf("Iteration %d\n", i);8           }910          return 0;(gdb) b 4Breakpoint 1 at 0x1155: file demo2.c, line 6.(gdb) runStarting program: /home/jjj/pj/dir1/demo2Breakpoint 1, main () at demo2.c:66           for (i = 1; i <= 5; i++) {(gdb) n7               printf("Iteration %d\n", i);(gdb) nIteration 16           for (i = 1; i <= 5; i++) {(gdb) print i$1 = 1(gdb) cContinuing.Iteration 2Iteration 3Iteration 4Iteration 5[Inferior 1 (process 3040270) exited normally](gdb) quit// 退出 gdb 后,发现同级目录生成一个叫 gdb.txt 文件。jjj-pc:~/pj/dir1$ lsdemo2  demo2.c  gdb.txt// 日志文件内容jjj-pc:~/pj/dir1$ cat gdb.txtNo breakpoints or watchpoints.1       #include 23       int main() {4           int i;56           for (i = 1; i <= 5; i++) {7               printf("Iteration %d\n", i);8           }910          return 0;Breakpoint 1 at 0x1155: file demo2.c, line 6.Starting program: /home/jjj/pj/dir1/demo2Breakpoint 1, main () at demo2.c:66           for (i = 1; i <= 5; i++) {7               printf("Iteration %d\n", i);6           for (i = 1; i <= 5; i++) {$1 = 1Continuing.[Inferior 1 (process 3040270) exited normally]

监视点

GDB中的”watch points”(监视点)是一种调试功能,用于在程序执行期间监视变量内存地址的更改。请看示例:

比如有个大程序,我要查看某个变量变化状态,被谁修改的。

用以下示例模拟,假如要监视变量 i 的变化情况。做法如下:

jjj-pc:~/pj/dir1$ cat demo2.c#include int main() {    int i;    for (i = 1; i <= 5; i++) {        printf("Iteration %d\n", i);    }    return 0;}
  • 方式1:监视变量
jjj-pc:~/pj/dir1$ gdb demo2GNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from demo2...// 在 `int i` 处打断点(gdb) b 4Breakpoint 1 at 0x1155: file demo2.c, line 6.// 运行,并在断点处中断(gdb) runStarting program: /home/jjj/pj/dir1/demo2Breakpoint 1, main () at demo2.c:66           for (i = 1; i <= 5; i++) {// 监视变量 i(gdb) watch iHardware watchpoint 2: i(gdb) info watchpoinstUndefined info command: "watchpoinst".  Try "help info".// 使用 info watchpoints 命令查看所有当前设置的监视点的状态(gdb) info watchpointsNum     Type           Disp Enb Address            What2       hw watchpoint  keep y                      i// 继续。在 i 的值变化时中断,现在是1,是在第 6 行改变的(gdb) cContinuing.Hardware watchpoint 2: i// 旧值 0 变成 新值 1Old value = 0New value = 1main () at demo2.c:66           for (i = 1; i <= 5; i++) {// 继续(gdb) cContinuing.Iteration 1Hardware watchpoint 2: iOld value = 1New value = 20x0000555555555178 in main () at demo2.c:66           for (i = 1; i <= 5; i++) {// 继续(gdb) cContinuing.Iteration 2Hardware watchpoint 2: iOld value = 2New value = 3...
  • 方式2:监视内存地址指向的内容
For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from demo2...(gdb) b 4Breakpoint 1 at 0x1155: file demo2.c, line 6.(gdb) runStarting program: /home/jjj/pj/dir1/demo2Breakpoint 1, main () at demo2.c:66           for (i = 1; i <= 5; i++) {// 取得变量的地址(gdb) print &i$1 = (int *) 0x7fffffffe2bc// 使用 * 操作符是因为你想要监视内存地址 0x7fffffffe2bc 处的内容,而不是该地址本身。(gdb) watch *0x7fffffffe2bcHardware watchpoint 2: *0x7fffffffe2bc// 继续。在 i 的值变化时中断,现在是1,是在第 6 行改变的(gdb) cContinuing.Hardware watchpoint 2: *0x7fffffffe2bcOld value = 0New value = 1main () at demo2.c:66           for (i = 1; i <= 5; i++) {(gdb) cContinuing.Iteration 1Hardware watchpoint 2: *0x7fffffffe2bcOld value = 1New value = 20x0000555555555178 in main () at demo2.c:66           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 -cunlimited

Tip:ulimit 用于修改 shell 资源限制。

jjj-pc:~/pj/dir1$ ulimit --helpulimit: 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// 实时优先级限制为0real-time priority              (-r) 0// 栈大小限制为8192 KB。用于限制每个进程栈的大小stack size              (kbytes, -s) 8192// 用于限制一个进程在CPU上运行的最长时间cpu time               (seconds, -t) unlimited// 最大用户进程数限制为31429max user processes              (-u) 31429// 虚拟内存大小限制为无限制。virtual memory          (kbytes, -v) unlimited// 文件锁定数量限制为无限制。file locks                      (-x) unlimited

创建一个会产生core文件的程序:

#include int main() {    int* ptr = NULL;    *ptr = 10; // 访问空指针将导致段错误    return 0;}

疑惑:笔者在调试台中不能生成 core 文件,暂时终止GDB core 文件。

进程调试

写个一直循环的程序:

jjj-pc:~/pj$ cat dir1/demo.c#include #include 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 3072058GNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word".Attaching to process 3072058Reading 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) list1       #include 2       #include 34       int main() {5           while (1) {6               printf("Hello, World!\n");7               sleep(1); // 暂停1秒钟8           }910          return 0;// next(gdb) nSingle 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) nSingle 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) nSingle stepping until exit from function sleep,which has no line number information.main () at demo.c:66               printf("Hello, World!\n");// next(gdb) n7               sleep(1); // 暂停1秒钟// next(gdb) n6               printf("Hello, World!\n");// next(gdb) n7               sleep(1); // 暂停1秒钟(gdb) n6               printf("Hello, World!\n");(gdb)

Tipman gdb 中也有gdb进程或core的介绍:

You can also start with both an executable program and a core file specified:        gdb program coreYou 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.gitcd 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 --helpwaf [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 --debugSetting top to                           : /home/jjj/pj/glmark2Setting out to                           : /home/jjj/pj/glmark2/buildChecking for 'gcc' (C compiler)          : /usr/bin/gccChecking for 'g++' (C++ compiler)        : /usr/bin/g++Checking for header stdlib.h             : yesChecking for header string.h             : yesChecking for header stdint.h             : yesChecking for header stdio.h              : yesChecking for header dlfcn.h              : yesChecking for header unistd.h             : yesChecking for header jpeglib.h            : yesChecking for header math.h               : yesChecking for header string.h             : yesChecking for library m                   : yesChecking for library jpeg                : yesChecking for function memset             : yesChecking for function sqrt               : yesChecking for program 'pkg-config'        : /usr/bin/pkg-configChecking for 'libpng12'                  : yesChecking for 'x11'                       : yesChecking for 'libdrm'                    : yesChecking for 'gbm'                       : yesChecking for 'libudev'                   : yesChecking for 'wayland-client'            : yesChecking for 'wayland-cursor'            : yesChecking for 'wayland-egl'               : yesPrefix                                   : /usr/localData path                                : /usr/local/share/glmark2Including extras                         : NoBuilding 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 buildWaf: 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/glmark2Waf: 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/glmark2GNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from ./build/src/glmark2...// 查看源码。说明已经是调试版本(gdb) list146     {147         std::stringstream ss;148149         for (int i = 0; i  0) ss << " ";152             ss << argv[i];153         }154155         return ss.str();(gdb) b mainBreakpoint 1 at 0x1b670: file ../src/main.cpp, line 160.(gdb) runStarting program: /home/jjj/pj/glmark2/build/src/glmark2Breakpoint 1, main (argc=1, argv=0x7fffffffe388) at ../src/main.cpp:160160     {(gdb) n162         NativeStateX11 native_state;(gdb) n175         if (!Options::parse_args(argc, argv))(gdb) n179         Log::init(Util::appname_from_path(argv[0]), Options::show_debug);(gdb) list 179174175         if (!Options::parse_args(argc, argv))176             return 1;177178         /* Initialize Log class */179         Log::init(Util::appname_from_path(argv[0]), Options::show_debug);180181         if (!ResultsFile::init(Options::results_file)) {182             Log::error("%s: Could not initialize results file\n", __FUNCTION__);183             return 1;(gdb) list 178173     #endif174175         if (!Options::parse_args(argc, argv))176             return 1;177178         /* Initialize Log class */179         Log::init(Util::appname_from_path(argv[0]), Options::show_debug);180181         if (!ResultsFile::init(Options::results_file)) {182             Log::error("%s: Could not initialize results file\n", __FUNCTION__);(gdb) n80            new_allocator() _GLIBCXX_USE_NOEXCEPT { }(gdb) n179         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 #include int main() {    while (1) {        sleep(1); // 暂停1秒钟    }    return 0;}jjj-pc:~/pj/dir1$ gcc demo.c -o demo -gjjj-pc:~/pj/dir1$ gdb demoGNU gdb (Ubuntu 9.1-0kylin1) 9.1Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from demo...(gdb) list1       #include 2       #include 34       int main() {5           while (1) {6               sleep(1); // 暂停1秒钟7           }89           return 0;10      }// 运行程序(gdb) runStarting program: /home/jjj/pj/dir1/demo

下面我们用 kill 发送信号模拟程序出错

kill 用于给进程发送信号。它有很多信号,平时常写的 kill -9 111等于kill -s SIGKILL 111,也就是强制中止进程。kill -11 用于向进程发送 SIGSEGV信号的命令,SIGSEGV是一种段错误信号,用于指示进程访问了无效的或不允许访问的内存地址,导致了段错误。

// kill - 给进程发送信号jjj-pc:~$ whatis killkill (1)             - send a signal to a process// 有这么多信息// kill -9 111 等于 kill -s SIGKILL 111jjj-pc:~$ kill -l 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR111) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+338) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+843) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+1348) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-1253) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-758) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-263) SIGRTMAX-1  64) SIGRTMAXjjj-pc:~$

查看刚才运行程序的进程号,并给该进程发送段错误信息。

jjj-pc:~$ ps aux |grep demojjj     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/demojjj  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/demoProgram 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
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。