news 2026/7/14 4:44:46

gdb调试基础

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
gdb调试基础

简介

笔者此前一直做互联网应用层开发,最近换工作,新工作的方向更加偏向底层,以前通过Xcode、Visual Studio进行调试的伎俩在这里不能再用,故有了这篇文章

希望看完这篇文章大家可以跟着笔者一起入门gdb调试,熟悉gdb调试的一些基础概念和调试手法,在碰到相关问题时能够使用掌握的调试手段进行解决

调试编译配置

并不是任何应用在编译完成后即可进行调试,你需要对一些编译选项进行配置,最终的编译产物才能很好的被调试器跟踪。下面笔者将会通过cmake进行编译前的编译选项配置

  • 配置Debug模式:在cmake中可以通过CMAKE_BUILD_TYPE=Debug设置编译产物为Debug包,这样产物中将会有大量调试信息,这很重要
  • 通过CMAKE_CXX_FLAGS_DEBUG和CMAKE_C_FLAGS_DEBUG这两个cmake变量添加编译选项
    • -g3:生成最详细的调试信息(包括宏定义)
    • -O0:禁止进行代码优化,保证源码和指令的一致性
    • -fno-omit-frame-pointer:强制编译器保留函数调用时的“栈帧指针”,栈帧指针是一个指向当前函数栈帧起始位置的指针,调试器通过栈帧指针可以方便的进行调用链回溯。不过GCC在某些优化等级下会使用-fomit-frame-pointer,忽略栈帧指针,通过其它方式计算得到起始地址,这样可以优化性能,但是不利于调试器进行调试
    • -fno-inline:禁止编译器对所有函数进行内联优化。编译器在对某些短小的函数进行内联优化之后,无法在内联函数内添加断点,也看不到对该函数的调用栈帧,所以在编译调试版本时需要关闭内联优化

完成以上编译配置后即可编译生成产物

调试

笔者这里在main函数中写了13个调试的demo,这些demo里面涉及各个方面的调试手段,我将会选择一些相对重要的调试手段进行分享

加载程序

gdb build/gdb_lab

笔者这里的被调试的程序为./build/gdb_lab,以上命令为加载被调试程序,它作用有以下三个

  • 启动GDB调试器
  • 读取./build/gdb_lab的符号表和调试信息
  • 进入GDB的交互式命令行提示符

除此之外它什么也不做。我们将会看到如下输出

程序不会运行,等待我们触发

触发程序运行

在进入GDB交互式命令行之后,需要我们去触发程序运行。一般有如下命令

  • run(r):run命令(或者其缩写r),这会让被调试程序直接从头开始运行,它不会等你
  • start:start命令,这会让执行流在main函数入口处暂停,等候你的发落
  • next(n):进行下一步,这很简单,执行流会运行到下一个指令处,然后在控制台输出这期间的打印信息
  • step(s):步入命令,和next类似,不同点在于,next遇到函数时不会进入函数,但是step步入指令遇到函数时会进入函数;s 5表示连续执行5次步入
  • continue(c):continue命令,如果是通过start命令触发的可执行程序,在next一段代码之后,如果想快速的执行完后续的代码或者快速的运行到下一个断点处,那么这个命令符合你的需求,它将快速执行完剩下的代码,或者执行到下一个断点处

断点

gdb中,通过break(b)设置断点,设置断点的方式有多种

  • break 行号:在指定行号处设置断点。如果该行是一个函数调用,那么执行流不会进入函数,next时执行流会执行该行后面的代码。值得注意的是,如果指定的行没有代码,那么gdb会将断点设置到后面最近的代码行处
  • break 函数名:在指定函数处设置断点,这会让执行流进入该函数,然后不执行任何其它指令立刻停止,等待后续命令
  • break 文件名:行号:和上一个指令效果相同,不同点在于这里指定了行号所属的文件,而上一个命令默认为当前文件中的行号
  • break 位置 if 条件:即在某个位置满足某个条件时执行流才停止,如果想在循环中进行断点,这将会很有用。值得注意的是,if条件中的变量需要在指定位置是可见的

以上是断点的设置,在调试中,难免想删除一些断点,或者临时开关一些断点,这在gdb中也有对应的工具可以用

  • info break:列出所有已经设置好的断点,Num列表示断点编号Enb列表示断点是否启用,y表示打开,n表示关闭Address表示断点地址What表示断点所在文件的具体位置Type表示断点类型,普通断点类型为breakpoint
  • delete:删除所有断点
  • delete 断点编号:delete或者d删除指定编号的断点
  • disable 断点编号:临时关闭指定断点
  • enable 断点编号:重新打开某个断点
  • clear 函数名:删除通过break 函数名设置的断点

在进入断点后,如果想查看变量信息,可以使用print(p)指令查看

  • p i:打印变量i的值
  • p &i:打印变量i的地址
  • p array[5]:打印数组array的第五个元素
  • p *ptr:打印指针ptr指向的内容
  • p ds.id:打印某个结构体的成员变量,ds为该结构体的一个实例
  • p func():调用函数,并打印函数的返回值。这很有意思,这里只是print调用了函数,真正的执行流其实还没有变化,同时你还可以通过print多次调用func(),如果函数内有静态变量,对于静态变量的修改也会表现出来

断点后,如果想查看当前的调用栈,那么可以使用backtrace(bt)进行查看

另外bt full命令在bt命令的基础上会额外打印每一层栈帧中局部变量的当前值

进入断点后,还可以通过list命令查看当前断点位置附近的代码

  • list:列出当前断点附近的代码
  • list 行号:列出指定行附近的代码
  • list 文件名:行号:同上
  • list 函数名:列出指定函数附近的代码
  • list ,:继续显示后面的代码,即翻页

栈帧切换

当函数调用栈层次比较深时,调用栈往往比较复杂,在print当前的变量时,我们可能又忘了再上一层某个变量传入时的值为多少,这个时候就需要有栈帧切换的能力,这能够方便的让我们看到多层调用栈中,不同层次的上下文情况

下面笔者以一个递归调用为例讲解栈帧切换

上图为一个递归函数,现在笔者经过多次步入之后,调用栈如下

可以看到,已经有很多层的调用栈了,这个时候使用print打印变量n时,n在当前调用层次中为1。这是一个简单的例子,当我们现实中想要查看多层调用栈中,不同层次中的变量值时,就需要进行栈帧切换,主要涉及up、down和 frame这三个命令

  • down:down命令会切换到下一层的栈帧,例如当前栈帧为0,这已经是最下一层的栈帧了(栈地址由上到下进行增长),所以不能再切换、这个时候p n将会输出1
  • up:up命令会切换到上一层的栈帧,例如当前栈帧为0,所以上一层栈帧为1。这个时候p n将会输出2
  • frame 指定层:这将会切换到指定层,切换完成后print就可以打印对应栈帧中的局部变量

以上三个命令即为栈帧切换的核心命令,值得注意的是,刚才提到的切换栈帧都是建立在不改变程序运行状态的前提下,再此前提下让你“穿越”到调用链的不同层次中去观察对应层次中的变量和上下文

观察点(watch)

观察点(watch)类似一种断点,但是和普通断点(break)不同,观察点不是监视某一行代码,而是监视某个变量或者内存地址,当这个变量发生变化时,程序会立即停止进入断点状态,然后打印新值和旧值

  • watch global_counter:监控global_counter变量被写
  • watch g_sensor.temperature:监控某个结构体实例的某个字段被写
  • rwatch:读观察点,监控变量被读取
  • awatch:读写观察点,监控变量被读或者写

总结

这篇文章主要一起学习了gdb入门的基础概念,包括断点和观察点、栈帧切换等内容

还有信号的调试、多线程调试、内存问题( ASan)的调试以及Core Dump离线分析的内容笔者还在学习中,这部分后续会继续更新,一起无限进步!!!


PS
今年对于笔者来说注定是不平凡的一年,笔者毅然结束了九年的杭州生活和学习之旅,来到重庆开启下一段旅行。九年间发生了好多事情

那年吃完三个菜的“年夜饭”去逛街,过年的街上空荡荡的也很冷,一家人被突如其来的经济变故打击得手足无措,沉默。就这样毫无目地逛着,也不知道要到哪里去,要怎么走;
那年母亲为了省一两块钱不坐公交,走高速公路四个小时上下班的日子;
那年母亲第一次出门打工,上夜班,白天睡不着,但是每次视频都说很适应,都说挺好的;
那年全家在外打工,妹妹没钥匙回家进不了屋,楼下远远的哭着看了一眼这个苦苦支撑的家,就又回了学校;
那年为了能够把这个家拉回正轨,不允许自己有一刻的歇息,毕业典礼结束两天马上进入工作;换工作只谈待遇,不谈强度,中间只让自己休息一周;手术后一个月回到杭州每天杵拐上下班,下班后腿已经肿了一圈
那堪回首!

时间来到2026年,母亲有了自己的小事业,妹妹找到了稳定体面待遇可观的工作,我紧绷的弦可以松了
做好自己当下力所能及的一切,剩下的就交给时间,下个九年、二十年、亦或一生,才不会后悔,我有尽力,没有让生命中最重要的人失望

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 4:43:51

C++封装libpng与SDL2实现轻量PNG图像处理类

1. 项目概述最近在整理一些老项目,发现很多C程序在处理图像时,要么依赖庞大的OpenCV库,要么就只能处理简单的BMP格式。对于只需要读取、显示和保存PNG图片的场景,引入整个OpenCV显得有点“杀鸡用牛刀”。于是,我决定自…

作者头像 李华
网站建设 2026/7/14 4:42:45

C盘清理全攻略:从基础操作到进阶技巧,安全释放30G+空间

你的C盘是不是又红了?每次看到那个刺眼的红色进度条,是不是既焦虑又无从下手?很多人以为C盘清理就是简单删删文件,结果一不小心系统崩溃,重要数据丢失。其实,真正有效的C盘清理不是蛮干,而是有策…

作者头像 李华
网站建设 2026/7/14 4:42:39

项目实战:从零构建Java+MySQL图书借阅系统(Swing版)

1. 项目概述与需求分析图书借阅系统是图书馆日常运营的核心工具,也是Java初学者练手的经典项目。这个Swing版系统主要面向高校课程设计或毕业设计场景,需要实现以下核心功能:用户角色分离:管理员负责图书/用户管理,读者…

作者头像 李华
网站建设 2026/7/14 4:40:59

智能代理技能(Agent Skills)开发指南与技术解析

1. Agent Skills概述与核心价值Agent Skills(智能代理技能)是当前人工智能领域最前沿的技术方向之一,它通过模块化封装特定领域的能力,使智能体能够像人类专家一样完成复杂任务。不同于传统程序化的固定流程,Agent Ski…

作者头像 李华
网站建设 2026/7/14 4:40:48

企业级AI Agent落地:从模型能力到工程体系的跨越

上周,一位做企业数字化转型的朋友深夜发来消息:“我们花大价钱接入了号称最强的 AI 模型,但团队用起来还是像在开盲盒——单次演示效果惊艳,一到真实业务流程就各种不稳定。到底差在哪一步?”这个问题,几乎…

作者头像 李华
网站建设 2026/7/14 4:39:51

单片机DAY_2

1.GPIOGPIO:通用目的输入输出两种工作模式输出模式:LED控制,数码管控制,蜂鸣器控制输入模式:检测引脚电平变化(检测外部电平信号 按键检测、检测ADC变化)2.中断中断的概念:CPU在处理…

作者头像 李华