GDB 奇技淫巧

· · 科技·工程

GDB 奇技淫巧

前言

今天是 \text{NOIP 2024 Day 0},但我翻出了我在 \text{CSP-S 2024 Day 0} 写的库存,想着还是分享一下吧。

本文面向读者为使用 NOI Linux 的 OIer,因此只介绍 OIer 可能会使用到的功能,对于工程上的功能不会讲述。

今天是 \text{CSP-S 2024 Day 0},不知道有没有人因为平时依赖 vscode,担心考场上没有合适的调试器呢。

笔者就是这样一个 Joker,但是没有关系,NOI Linux 没有 vscode 插件,我们有 GDB!

GDB 基础运用

你可能以为,GDB 只是一个简陋的 CLI 调试工具,但事实上只需简单的调教和学习,你就可以把它玩出花来。

下面收录了一些 GDB 常用的基础命令(及其简写)

更详细的介绍可以参见 GDB 备忘清单 一文,此处不再赘述。

GDB 高级运用

.gdbinit

在你的用户目录下建立 ~/.gdbinit,它可以在你的 GDB 运行前预输入你自定义的命令。先建立起来,我们后续有用。

TUI 模式

只要给 GDB 传入参数 -tui 就可以打开文本用户界面,这是我们唯一的武器!

使用 TUI 模式,我们可以方便地分屏预览源代码和命令行,并可视化地看到程序运行到了哪里,断点于何处,一切难题迎刃而解!

首先,打开你的 vscode。尽管它只是一个文本编辑器了,但它还可以内置终端,其他软件连这个功能都没有。

给你的程序写好 freopen,并使用 setvbuf(stdout, NULL, _IONBF, 0); 关闭输出缓冲区,这会使你的标准输出的每一个字符被直接写入文件内。这样,在调试的时候,我们直接打开输出的文件,在 vscode 里预览即可。

下面是一个测试程序:

#include <bits/extc++.h>

#define inline __always_inline
template <typename T> inline void read(T &x)
{
    char ch;
    for (ch = getchar(); !isdigit(ch); ch = getchar());
    for (x = 0; isdigit(ch); ch = getchar())    x = x * 10 + (ch - '0');
}

int main()
{
    freopen64("out", "w", stdout);
    setvbuf(stdout, NULL, _IONBF, 0);
    for (int i = 0; i < 10; i++)
        printf("Test\n");
    return 0;
}

为什么要输出到文件这样多此一举?因为 GDB 的 TUI 模式使用 curses 这个库实现,如果再有标准输出会打乱原先文本界面的阵形,导致界面混乱。

然后你可以使用 g++ main.cpp -o main -g 编译你的源文件,并使用 gdb -tui ./main 来试着调试一下,相信你一定很容易学会如何使用。

下图是笔者在 vscode 中调试的实际界面,还是比较人性化的,属于人类可用的范围。

在使用 NOI Linux 时可能没有代码高亮(笔者使用的是 WSL 有代码高亮),但这无伤大雅。

当然,你可能会觉得上下结构的窗口结构浪费屏幕空间,没关系!打开 ~/.gdbinit,输入如下内容:

# 定义新窗口布局为左右布局,src 与 cmd 1:1 分屏。
tui new-layout noi-linux {-horizontal src 1 cmd 1} 1 status 0
# 更改为我们自定义的窗口布局
layout noi-linux
# 更改窗口焦点到 cmd(默认在 src)
focus cmd
# 更改代码的 tab 大小(个人喜好)
set tui tab-width 4

然后重新运行 GDB,如果显示异常就略微改变窗口大小,这样就可以左右分屏了,效果如下:

最后,介绍一下 TUI 模式的一些基本的快捷键:

基本快捷键:

单键命令模式:

GDB CLI 模式的正常的调试命令同样可用。

最后,祝各位 \text{CSP-S 2024 rp++},调试一遍过!

本文是笔者在考试前一天花一个小时赶工的,可能略有粗糙和纰漏,各位看官老爷如发现错误,可于评论区指出。