NXP i.MX8系列平台开发讲解 - 5.1 调试篇(一) - 掌握GDB:在Linux 下高效调试程序

专栏文章目录传送门返回专栏目录

Hi, 我是你们的老朋友,主要专注于嵌入式软件开发,有兴趣不要忘记点击关注【码思途远】


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

文章目录

分享关于作为在Linux应用程序下必备掌握调试工具,类似这种工具就Keil 在线调试,只不过GDB并没有图形界面,功能上都还差不多。对于调优和排错会使用上该工具。

1 认识GDB

1.1 什么是 GDB

GDB 是一款功能强大的命令行调试器,它可以帮助开发者跟踪程序的运行,排查代码中的问题。使用 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 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 解决是一种途径。可以依照以下的一些建议或者步骤:

设置断点

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

查看变量值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这个工具,这个也是对于开发人员一个必备技能。对于已经接触过带图形界面的调试都比较熟悉,后续有机会对于Linux下一些调试相关进行分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码思途远

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值