关注+星号公众号,不容错过精彩
作者:HywelStar
Hi, 在这国庆假期分享关于作为在Linux应用程序下必备掌握调试工具GDB,类似这种工具就Keil 在线调试,只不过GDB并没有图形界面,功能上都还差不多。对于调优和排错可能会使用上该工具。
1 认识GDB
1.1 什么是 GDB
GDB(GNU symbolic debugger) 是一款功能强大的命令行调试器,它可以帮助开发者跟踪程序的运行,排查代码中的问题。使用 GDB,开发者可以查看程序崩溃时的上下文信息,设定断点,逐步执行代码,检查变量的值,并通过回溯函数调用栈分析问题。
1.2 GDB的作用
对于嵌入式开发,尤其是在 Linux 环境下,GDB 是调试 C/C++ 程序的利器:
定位崩溃:当程序由于段错误 (Segmentation fault) 或内存非法访问而崩溃时,GDB 可以提供有用的堆栈信息,帮助开发者迅速定位问题。 调试嵌入式系统:在嵌入式环境中,程序可能会在资源受限的设备上运行,GDB 可以通过 远程调试(remote debugging) 在开发机器上调试目标设备的程序,极大提升开发效率。 灵活性高:GDB 允许你在程序的不同执行阶段进行精确控制,从而对代码进行逐步分析。
2. 基础操作
在 Linux 系统中,要使用 GDB 调试程序,首先需要编译程序时加上 -g
选项,以便生成调试符号。以 GCC 为例,编译命令如下:
gcc -g -o my_program my_program.c
编译完成后,可以通过以下方式启动 GDB:
gdb ./my_program
这里需要提示下关于-g
和不带-g
加入-g
表示在编译时候生成调试信息,使得调试器能够关联源代码和生成的二进制文件。它不会对程序的功能造成影响,但会增加二进制文件的大小。
其实-g
有点像是在生成的二级制叫做Debug
版本,没有加-g
并且进行优化后生成的二进制版本程序relase
版本,相信在使用过keil
类似软件都会有涉及这个问题。关于大小也是Debug
版本偏大。
3. GDB常用命令
GDB 调试过程常用命令:
3.1 启动程序
run
或r
启动运行。
r(run) [args]
使用 run
命令启动程序,括号内可以带上程序的参数。如果程序已经运行过,使用 r
可以重新启动。
3.2 断点相关控制
# 添加断点
break main
break factorial
# 多文件设置断点 < 设置my_program.c第10行设置断点 >
break my_program.c:10
# 查询断点
info b
# 删除某处断点 < 删除my_program.c第10行设置断点 >
d my_program.c:10
# 删除某个断点,根据编号 < n 序号(info b中的序号)>
d n
# 删除所有断点
d breakpoints
# 关闭所有断点,使它无效,n可以代表编号,不带n表示所有
disable b [n]
# 使所有断点有效, n可以代表编号,不带n表示所有
enable b [n]
3.3 逐行执行
# 执行当前行代码,并跳过函数调用。n或者next(类似F10)
n(next)
# 当前行代码,并且如果当前行包含函数调用,step 会进入该函数内部,逐行调试该函数内部的代码s 或者step (类似F11)
s(step)
3.4 文件相关查看
# 查看源文件的地方, xxx.c 文件,n为行号
b xxx.c:n
b my_program.c:10
# 随机查看
l
# l 0 或者 l 1第一行开始的10行代码
l <1 or 0>
3.5 变量查看
print
命令用于显示当前作用域下的变量值,info locals
可以查看当前作用域内的所有局部变量。
# 查看变量
p(print) var
# 查看当前作用域内的所有局部变量。
info locals
# 修改变量
set var
# 跟踪某个变量,每次停下都打印值
display <var>
# 取消跟踪
undisplay n # 取消编号为 n 的跟踪
跟踪sum
变量;
3.6 堆栈查看
堆栈(stack) 是非常重要的工具,尤其是当你需要了解程序在某个时刻的调用顺序、函数参数传递和返回值时。堆栈保存了函数的调用历史,可以帮助你追踪程序的执行路径。
# 查看堆栈信息
bt
# 切换到指定的栈帧n
frame n
# 显示当前栈的局部变量以价值
info locals
# 查看当前函数传递的参数
info args
4. 常用操作
了解GDB 相关命令,但是在工作中在什么情景会用上,其实当你用上GDB时候,大多数情况是在于应用程序发生了问题出现一些异常,需要调试去查找问题解决问题。比如当程序出现段错误时候,当程序的值成为了自己意向不到的值时候,通过GDB 解决是一种途径。可以依照以下的一些建议或者步骤:
设置断点
设置断点,缩小出错的范围。
查看变量值(print
和display
)
采用单步调试
(gdb) step # 进入当前行的函数内部
(gdb) next # 跳过函数调用,执行下一行
(gdb) continue # 继续执行程序,直到遇到下一个断点
(gdb) until n # 跳转至行号
查看堆栈问题
可以通过调用栈了解是在哪个函数出现了问题。
(gdb) backtrace # 打印当前函数调用栈
查看局部变量和传参
(gdb) info args # 查看当前函数的参数
(gdb) info locals # 查看局部变量
其他命令
(gdb) finish # 跳转出某个函数
本章节使用程序案例:
int global_sum = 0;
unsigned long long factorial(int n) {
if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
int add(int a, int b) {
return a + b;
}
int add_multiple(int a, int b, int c, int d) {
int temp_sum = a + b;
int final_sum = temp_sum + c + d;
return final_sum;
}
int main() {
int num;
int x = 5, y = 8, z = 12, w = 20;
printf("Enter a number(0~19): ");
scanf("%d", &num);
if (num < 0) {
printf("Factorial is not defined for negative numbers.\n");
return 1;
} else if (num > 20) {
printf("Input too large to compute factorial!\n");
return 1;
}
unsigned long long result = factorial(num);
printf("Factorial of %d is: %llu\n", num, result);
int sum = add(5, 8);
printf("5 + 8 sum is : %d\n", sum);
global_sum = add_multiple(x, y, z, w);
printf("%d + %d + %d + %d sum is : %d\n", x, y, z, w, global_sum);
return 0;
}
5. 总结
本章节主要对GDB 相关的使用简单介绍,在实际开发过程中,大家应该都偏向于打印调试,或许在排错优化过程中才会接触到GDB这个工具,相信在实际开发过程中没有像Ddemo那么简单,这个需要开发人员细心调试,这个也是对于开发人员一个必备技能。对于已经接触过带图形界面的调试都比较熟悉,后续有机会对于Linux下一些调试相关进行分享。
往期推荐