Linux调试器 ——gdb/cgdb

目录

  • [1. 调试前的准备:Debug 模式](#1. 调试前的准备:Debug 模式)
  • [2. 安装 CGDB](#2. 安装 CGDB)
  • [3. GDB 常用命令](#3. GDB 常用命令)
    • [3.1. list (l):查看源代码](#3.1. list (l):查看源代码)
    • [3.2. 程序执行控制:run, next, step, continue, finish, until](#3.2. 程序执行控制:run, next, step, continue, finish, until)
    • [3.3. 断点管理:break, info, delete, disable, enable](#3.3. 断点管理:break, info, delete, disable, enable)
    • [3.4. 变量与状态查看:print, p, set var, display, undisplay, info locals](#3.4. 变量与状态查看:print, p, set var, display, undisplay, info locals)
  • [4. 监视变量 (Watch)](#4. 监视变量 (Watch))
    • [4.1 在 main 函数打断点并运行,进入 Sum 函数内部,让 result 完成初始化](#4.1 在 main 函数打断点并运行,进入 Sum 函数内部,让 result 完成初始化)
    • [4.2 设置监视点 (Watch),继续执行 (c) 并观察变化](#4.2 设置监视点 (Watch),继续执行 (c) 并观察变化)
  • [5. 条件断点](#5. 条件断点)
    • [5.1 新增断点时直接添加条件](#5.1 新增断点时直接添加条件)
    • [5.2 给已有断点追加条件](#5.2 给已有断点追加条件)

GDB(GNU 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 stripped

2. 安装 CGDB

cgdb在上方显示源代码,下方保留 GDB 命令行,支持语法高亮。

Ubuntu:

复制代码
sudo apt-get install -y cgdb

CentOS:

复制代码
sudo yum install -y cgdb

基本操作:

  • 启动:gdb ./mycmd 或 cgdb ./mycmd

  • 退出:quit 或 Ctrl + D

  • CGDB 分屏技巧 :按 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 start+end 让 GDB 帮你计算表达式的结果。

  • set var 变量=值 (强制修改变量)

    • 操作步骤 :输入 set var i=10。

    • 效果 :在程序运行期间,强行把内存中变量 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 result(print)去查看,不仅费时费力,而且很容易错过变化的那一瞬间。

watch 命令可以帮助我们实时监控变量的内存变化。一旦该变量的值发生改变,GDB 就会自动暂停程序,并告诉你"旧值"和"新值"分别是什么。

示例:

4.1 在 main 函数打断点并运行,进入 Sum 函数内部,让 result 完成初始化

首先,我们需要让程序跑起来,并在进入 Sum 函数前停下来。

4.2 设置监视点 (Watch),继续执行 (c) 并观察变化

现在 result 已经初始化完毕,正是建立监视的好时机。

  1. 观察点编号:18,监视变量result
  2. Old value:修改之前 result 的值
  3. New value:修改完成后 result 的值
  4. 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 [断点编号] [条件]
相关推荐
IanSkunk1 小时前
从检查设备到训练方案:眼科与视光中心设备联动的数据通路、字段口径与校验规则
服务器·数据库·负载均衡
天远Date Lab1 小时前
零信任架构实战:基于天远名下车辆车牌查询A构建自动化社区车位摇号核验网关
运维·人工智能·架构·自动化
chenlance1 小时前
PADS灌铜、覆铜过程和技巧小汇总
linux·服务器·网络
xixiaoyunya1 小时前
服务器自动备份怎么做:十几分钟搭建一套稳定可监控的方案
运维·服务器
ue星空1 小时前
ALS V4 中文拆解手册
运维·nginx
HideF1 小时前
【无标题】
linux
高山有多高2 小时前
【Linux笔记】NAT与内网穿透
linux
迷途之人不知返2 小时前
自主实现一个简单的shell命令行
linux
Hello_Pyhx2 小时前
挖兔硬盘精灵 WatuDisk:从首次检测到日常检查的使用指南
运维·windows·硬盘·维护