如何在 LLDB 中打印实际(派生)对象属性

How to print actual(derived) object properties in LLDB

代码示例:

class IA
{
public:
    virtual int getA() = 0;
};

class A:public IA
{
public:
    int getA() override
    {
        return m_a;
    }
private:
    int m_a = 10;
};

void main()
{
    A* a = new A();
    IA* ia = a;
}

在带有 set print object on 的 GDB 中,我可以使用 ia 指针轻松打印 a 对象内容。

//with print object on
p *ia
 = (A) {<IA> = {_vptr$IA = 0xf330c8 <vtable for A+16>}, m_a = 10}

//without print object on
p *ia
 = {_vptr$IA = 0xf330c8 <vtable for A+16>}

是否可以在 lldb 中做同样的事情? 我在官方文档中找不到任何内容。 这就是我在 lldb 中得到的:

p *ia
(IA) [=12=] = {}
p *a
(A)  = (m_a = 10)

lldb 有两种方法用于从目标程序访问类型化值,expr(别名为 p)和 frame variable(别名为 v)。

expr 是一个完整的表达式求值器,它使用 clang 前端和后端来解析和求值您传递给它的表达式,“就好像它是在您停止的地方插入到代码中一样”。

frame var 是一种 C-ish 伪语言,用于访问变量、寄存器或内存区域的元素。

然后通过一个公共系统呈现任一方法的结果值,首先通过获取结果的“动态类型”(相当于设置 'print object' on),然后通过“数据”传递它格式化程序”架构。

处理时:

(lldb) p *ia

指针在作为值从表达式返回到“动态类型”检测阶段时已被取消引用,因此无法判断它确实来自指向 A 的指针。

lldb 选择让 expr 尽可能地遵循源语言的规则,这会妨碍这里,但这意味着您可以将相当复杂的表达式传递给命令并让它们工作就像他们在代码中一样。

除其他外,它还可以执行 C++ 强制转换,因此如果您需要动态类型可用(例如,如果您想要调用仅在完整类型中的方法),您可以手动完成:

(lldb) p ((A *)ia)->some_A_method()

gdb 的 print 使用手工构建的解析器从最深的子表达式开始计算表达式,因此它可以停下来获取每个子表达式的动态类型。但是,这样做会使成为准确的 C++ 解析器的工作变得更加困难。总有取舍...

OTOH,

(lldb) v *ia
(A) *ia = (m_a = 10)

可以获取动态类型,因为 v 解析器 returns 一个对象:“SBValue”代表局部变量 ia 的“SBValue”的解引用值 - 所以result 知道值的来源,并且可以在打印阶段沿途获取所有动态类型。我们还没有教 v 进行转换或函数调用 - 主要是为了保持其解析器简单和可预测。但除此之外,v通常比p.

更方便变量打印

当你只是打印局部变量或它们的子变量时,v 也更高效,更不脆弱,因为它只需要了解值的类型并获取其内存,而不是模拟当前上下文足以满足clang.