在调试程序时,我们常常会遇到这样的情况:当我们使用调试器逐步执行代码(step into)进入一个函数时,如果这个函数的参数中包含了其他函数调用,即使这些函数调用非常简单且不重要(例如C++标准模板库中的函数),调试器也会逐层深入这些嵌套的函数调用。这不仅浪费时间,还容易让人分心,尤其是在处理复杂的表达式时。
GDB中的解决方案
以GDB为例,考虑下面这段简单的C++代码:
#include <cstdio>
#include <memory>
#include <vector>
using namespace std;
void foo(int i, int j) {
printf("%d %d\n", i, j);
}
int main() {
auto i = make_unique<int>(3);
vector v{1,2};
foo(*i, v.back()); // step into
}
当我们在foo
函数调用处设置断点并尝试单步执行(s
命令)时,GDB首先会跳入std::vector::back()
和std::unique_ptr::operator*()
这两个辅助函数,尽管它们与我们要调试的核心逻辑无关。虽然可以通过先执行finish
命令退出当前函数,然后再继续单步执行,但这显然不够高效。
为了解决这个问题,GDB提供了一个skip
命令,可以用来跳过那些匹配正则表达式的函数或文件名模式。比如,你可以通过以下命令跳过所有以std::
开头的函数:
skip -rfu ^std::
或者,如果你想要跳过整个C++标准库的实现文件,可以使用如下命令:
skip -gfi /usr/include/c++/*/bits/*
需要注意的是,skip
命令在匹配文件名时使用了fnmatch
函数,并设置了FNM_FILE_NAME
标志,这意味着通配符*
不会匹配斜杠字符。因此,像skip -gfi /usr/*
这样的命令实际上并不会排除/usr/include/c++/14.2.1/bits/stl_vector.h
等路径下的文件。
其他调试器的支持
除了GDB之外,LLDB默认情况下也会避免进入名称以std::
开头的函数内部。这一行为可以通过设置来调整:
(lldb) settings show target.process.thread.step-avoid-regexp
target.process.thread.step-avoid-regexp (regex) = ^std::
Visual Studio则提供了“仅我的代码”功能,它能够自动跳过系统、框架以及其他非用户编写的代码调用。此外,VS还支持“Step Into Specific”命令,允许用户选择性地进入特定的函数。
总之,合理利用调试器提供的跳过功能,可以帮助开发者更专注于核心问题,提高调试效率。