ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux调试器 ——gdb/cgdb

Linux调试器 ——gdb/cgdb 目录1. 调试前的准备Debug 模式2. 安装 CGDB3. GDB 常用命令3.1. list (l)查看源代码3.2. 程序执行控制run, next, step, continue, finish, until3.3. 断点管理break, info, delete, disable, enable3.4. 变量与状态查看print, p, set var, display, undisplay, info locals4. 监视变量 (Watch)4.1 在 main 函数打断点并运行进入 Sum 函数内部让 result 完成初始化4.2 设置监视点 (Watch)继续执行 (c) 并观察变化5. 条件断点5.1 新增断点时直接添加条件5.2 给已有断点追加条件GDBGNU Debugger是 GNU 项目下的命令行级程序调试器CGDB 是 GDB 的前端封装底层仍然调用 GDB提供类似 Vim 的分屏界面解决原生 GDB 纯命令行操作不直观的痛点。1. 调试前的准备Debug 模式在开始调试之前必须确保你的程序包含调试信息。Linux 下gcc/g默认生成的是 Release 模式不支持调试。我们需要在编译时加上-g选项。示例#include stdio.h int Sum(int s, int e) { int result 0; for(int i s; i e; i) { result i; } return result; } int main() { int start 1; int end 100; printf(I will begin\n); int n Sum(start, end); printf(running done, result is: [%d-%d]%d\n, start, end, n); return 0; }编译对比默认模式不支持调试$ gcc mycmd.c -o mycmd $ file mycmd # 输出... not stripped (但没有 debug_info)Debug 模式支持调试$ gcc mycmd.c -o mycmd -g $ file mycmd # 输出... with debug_info, not stripped2. 安装 CGDBcgdb在上方显示源代码下方保留 GDB 命令行支持语法高亮。Ubuntu:sudo apt-get install -y cgdbCentOS:sudo yum install -y cgdb基本操作启动gdb ./mycmd或cgdb ./mycmd退出quit或Ctrl DCGDB 分屏技巧按ESC进入上方代码屏可浏览代码按i回到下方 GDB 命令屏。3. GDB 常用命令3.1.list (l)查看源代码作用在命令行中显示源代码方便对照行号打断点。默认每次显示 10 行。操作步骤list / l(显示源码)操作步骤在(gdb)提示符下直接输入l并回车。效果GDB 会默认以当前文件为中心每次列出 10 行代码。如果继续敲回车会接着上一次的位置继续往下显示 10 行。示例list / l 10从第 10 行开始显示代码list / l 函数名(列出指定函数源码)操作步骤输入l main或l Sum。效果直接跳到该函数的开头显示其上下文代码。非常适合在打断点前查看函数逻辑。list / l 文件名:行号(列出指定文件的源码)操作步骤如果在多文件项目中输入l mycmd.c:1。效果直接打开mycmd.c文件并定位到第 1 行。3.2. 程序执行控制run, next, step, continue, finish, until这是 GDB 最核心的控制流命令决定了程序如何一步步往下走。run / r(开始运行)操作步骤输入r并回车。效果程序从头开始执行。如果之前设置了断点程序会在第一个断点处暂停如果没有断点程序会直接运行到结束。next / n(单步执行 - 逐过程F10)操作步骤程序暂停时输入n。效果执行当前行代码。关键特性如果当前行是一个函数调用如int n Sum(start, end);按n不会进入Sum函数内部而是把整个函数执行完直接停在下一行。相当于 VS 中的 F10 (Step Over)。step / s(单步执行 - 逐语句F11)操作步骤程序暂停时输入s。效果执行当前行代码。关键特性如果遇到函数调用按s会进入被调用函数的内部比如进入Sum函数的第一行。相当于 VS 中的 F11 (Step Into)。continue / c(继续执行)操作步骤输入c。效果让暂停的程序恢复运行。它会一直往下跑直到遇到下一个断点或者程序结束finish(跳出当前函数)操作步骤当你使用s进入了Sum函数但发现内部逻辑太长了不想单步看了输入finish。效果程序会瞬间执行完Sum函数剩余的代码并返回到调用它的main函数处暂停同时打印出函数的返回值。until X行号(运行至指定行)操作步骤比如当前在第 11 行的for循环处你想直接跳到第 15 行输入until 15。效果程序会自动连续执行直到到达指定行号才停下。常用于快速跳出一个循环。3.3. 断点管理break, info, delete, disable, enable在大型程序中我们需要精确控制程序在哪里停下。break / b [文件名:]行号(行号打断点)操作步骤输入b 10在当前文件第10行打断点或者b test.c:10在指定文件打断点。效果GDB 提示Breakpoint 1 at 0x...: file mycmd.c, line 10.。break / b 函数名(函数入口打断点)操作步骤输入b main或b Sum。效果在该函数的第一行代码处自动设置断点。info breakpoints / info b(查看断点列表)操作步骤输入info b。效果以表格形式列出当前所有的断点包含断点编号Num、是否启用Enb、位置和命中的次数。delete / d breakpoints和delete / d breakpoints n(删除断点)操作步骤输入d breakpoints删除所有断点。输入d 1删除编号为1的断点。disable breakpoints和enable breakpoints(禁用与启用)操作步骤输入disable breakpoints。效果保留断点但不生效Enb 列变为 n程序执行时不会在这些断点停下。调试完后输入enable breakpoints可以重新激活它们。这比删了重打要方便得多。3.4. 变量与状态查看print, p, set var, display, undisplay, info locals停下来之后查看内存状态是找 Bug 的关键。print / p 表达式和p 变量(打印)操作步骤输入p start查看变量值输入p startend让 GDB 帮你计算表达式的结果。set var 变量值(强制修改变量)操作步骤输入set var i10。效果在程序运行期间强行把内存中变量i的值修改为 10。这是验证逻辑假设的终极利器。display 变量名(跟踪显示变量)操作步骤输入display x。效果这相当于设置了“监视”。之后你每次执行n或s导致程序暂停时GDB 都会自动打印出x的当前值不需要你每次都敲p x。undisplay 编号(取消跟踪显示)操作步骤输入undisplay 1。效果取消编号为 1 的自动显示设置。编号可以通过info display查看。info locals(查看当前局部变量)操作步骤当程序停在函数内部时输入info locals可简写为i locals。效果GDB 会一次性列出当前栈帧当前函数中所有局部变量的名字和当前的值。非常适合快速了解函数内部的全局状态。4. 监视变量 (Watch)在调试循环时例如for循环累加我们常想知道某个变量如result到底是在哪一次循环、什么时候发生了改变。如果我们靠手动不断敲p resultprint去查看不仅费时费力而且很容易错过变化的那一瞬间。watch命令可以帮助我们实时监控变量的内存变化。一旦该变量的值发生改变GDB 就会自动暂停程序并告诉你“旧值”和“新值”分别是什么。示例4.1 在main函数打断点并运行,进入Sum函数内部让result完成初始化首先我们需要让程序跑起来并在进入Sum函数前停下来。4.2 设置监视点 (Watch),继续执行 (c) 并观察变化现在result已经初始化完毕正是建立监视的好时机。观察点编号18监视变量resultOld value修改之前 result 的值New value修改完成后 result 的值mycmd.c:5在代码第 5 行发生这次赋值result i;总结变量异动的捕捉神器如果你有一些变量比如全局的状态标志位、关键结构体成员不应该被修改但是你怀疑某段代码有 Bug偷偷修改导致了问题你就可以用watch盯住它。一旦变化立刻通知程序会停在被修改后的第一现场方便你通过bt(查看调用栈) 找出是哪个函数干的“坏事”。日常调试的取舍虽然watch很好用但硬件观察点数量有限通常只有 4 个。如果需要监视大量变量建议使用display命令每次单步停下时自动打印变量值。5. 条件断点如果我们怀疑循环中间某个时刻出了问题比如我想看看当i等于 30 的时候result累加到了多少如果在第 6 行result i;打一个普通的断点那么程序将会疯狂暂停 100 次你需要绝望地敲击 100 次continue (c)才能跑到最后。这不仅极其繁琐而且极易让人崩溃。为了解决这个问题我们需要使用条件断点——让断点只在满足特定条件时才生效。5.1 新增断点时直接添加条件b [行号/函数名] if [条件]程序在后台自动跑了前 29 次循环完全没有打扰你。直到i变成 30 的那一瞬间条件满足程序才如你所愿地停在第六行。此时打印变量result恰好是 1 到 29 的累加和 (435)。5.2 给已有断点追加条件condition [断点编号] [条件]
返回列表