GDB如何在运行时评估C++表达式

10

当我在调试程序时,最近注意到 GDB 有能力在调试程序时评估“复杂”的表达式,我想知道它是如何做到的。例如,给定下面的代码:

int main() {
    std::vector<int> v = {1, 2, 3};
    int k = 0;
    std::cin >> k;
    v.push_back(k);
    return v.at(0);
}

我可以编译程序g++ -g myprogram.cpp并在 GDB 中进行调试,这使我能够输入类似print v.at(4);(在动态输入 k 后打印正确的值)和 print v.at(2) == 3这样的东西,它会计算出真值。

我想知道 GDB 是如何做到这一点的。这个 SO 问题暗示它是"在内存中编译"的某些东西,但没有进一步解释,所以我想知道它是使用了某种 JIT 还是其他什么?他们是在我输入时内联编译代码并运行吗?他们有一个框架来在调试上下文中动态评估 C++ 吗?本质上,我想在我正在编写的调试器中重现此功能,在断点处评估表达式,这就是为什么我很好奇 GDB 如何做到这一点。


1
我不清楚你希望得到什么样的答案。Gdb能够在被调试的程序的上下文中分析C和C++表达式,借助于二进制文件中包含的调试信息,以及可能使用源代码(当可用时)。但您已经知道这一点,详细信息会使这个场所变得冗长。 - John Bollinger
@JohnBollinger 我知道我必须“查看源代码”,但我想知道的是他们如何评估这些表达式。他们是否在我输入代码时将其内联编译并运行?他们是否有一个框架,在调试上下文中动态评估C++?本质上,我想在我编写的调试器中复制这个功能,以便在断点处评估表达式,这就是为什么我很好奇GDB是如何实现的。谢谢! - llk
你的问题似乎更偏向于C++。我建议你移除C标签。 - tambre
我已经对GDB的源代码进行了一些调查。希望它能对你有所帮助。 - Keyu Gan
2个回答

12

简短回答:它不会编译代码。

详细回答:

  1. 您调用print命令,该过程在printcmd.c中发生。
  2. 它调用evaluate_expression,定义在eval.c中,通过读取目标内存并在gdb内部计算常规运算符来评估表达式,否则使用call_function_by_hand
  3. call_function_by_handinfcall.c中定义。调用时,该过程会停止目标执行(有时不会,因此可能会使用此功能崩溃多线程程序)。
  4. 注入代码到正在调试的程序中。
  5. 通过读取内存检索结果,并在必要时取消暂停。

您可以关注call_function_by_hand的代码以更好地理解。

注意:编译print/call是不同的事情。


值得一提的是,GDB还具有“编译代码”命令,可以在运行时编译和注入代码,例如请参见:https://dev59.com/1VXTa4cB1Zd3GeqP4LRs#31709579。 - Ciro Santilli OurBigBook.com

10

这使我能够输入像print v.at(4);这样的东西。

gdb可以调用编译成二进制文件的函数。这正是这里发生的事情。gdb调用了std::vector成员函数at()并为您打印结果,请参见文档

此外请注意,这是因为您在代码中使用了v.at(0)。如果您删除此部分代码,则v.at()将不会被实例化,并且将不会在生成的二进制文件中可用,以便gdb无法调用它。


那么GDB并不编译任何东西,而是运行现有代码的部分来评估表达式?GDB使用的是库还是完全以独立方式编码? - llk
1
是的,在您发布的示例中可以。但它还可以编译和注入代码,请参阅https://sourceware.org/gdb/onlinedocs/gdb/Compiling-and-Injecting-Code.html#Compiling-and-Injecting-Code,尽管我没有使用过这个功能。 - ks1322

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接