掌握GDB:在Linux 下调试程序

文摘   2024-10-03 18:26   广东  

关注+星号公众号,不容错过精彩

作者: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 启动程序

runr启动运行。

r(run) [args]

使用 run 命令启动程序,括号内可以带上程序的参数。如果程序已经运行过,使用 r 可以重新启动。

3.2 断点相关控制

# 添加断点break mainbreak 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:nb 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# 切换到指定的栈帧nframe n# 显示当前栈的局部变量以价值info locals# 查看当前函数传递的参数info args

4. 常用操作

了解GDB 相关命令,但是在工作中在什么情景会用上,其实当你用上GDB时候,大多数情况是在于应用程序发生了问题出现一些异常,需要调试去查找问题解决问题。比如当程序出现段错误时候,当程序的值成为了自己意向不到的值时候,通过GDB 解决是一种途径。可以依照以下的一些建议或者步骤:

设置断点

设置断点,缩小出错的范围。

查看变量值printdisplay

采用单步调试

(gdb) step    # 进入当前行的函数内部(gdb) next    # 跳过函数调用,执行下一行(gdb) continue    # 继续执行程序,直到遇到下一个断点(gdb) until n    # 跳转至行号

查看堆栈问题

可以通过调用栈了解是在哪个函数出现了问题。

(gdb) backtrace    # 打印当前函数调用栈

查看局部变量和传参

(gdb) info args    # 查看当前函数的参数(gdb) info locals    # 查看局部变量

其他命令

(gdb) finish	# 跳转出某个函数

本章节使用程序案例:

#include <stdio.h>
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下一些调试相关进行分享。



往期推荐



Linux 内核镜像文件种类认识

GNSS篇(五) - GPSD 编译(含交叉编译)详解

嵌入式C语言代码风格简述

嵌入式中优先级反转及其解决方案

探索摄像头MIPI 接口

揭密摄像头DVP接口

认识摄像头模组

模块化代码 - 实例简介

GNSS篇(四) - 整活定位模块(GPSD 使用)

嵌入式软件的编译知识



 

           
“阅读原文”一起来充电吧!


码思途远
一位码农的日常分享,专注嵌入式领域相关知识。
 最新文章