如何为std::vector数据编写LLDB合成提供者以实现形状视图

2 投票
2 回答
1325 浏览
提问于 2025-04-17 23:05

我正在尝试为我项目中的类创建 LLDB 可视化工具,但 LLDB 的文档实在是太少了。我有一个数组类,它使用 std::vector 来存储底层数据,并且有一个扩展数组来描述形状。这个数组还可以在之后进行重塑。

默认情况下,std::vector 中的 "data_" 总是以线性向量的方式显示。我希望我的提供者能够创建一个视图层次结构。在这个例子中,第一层将是子行,每一行展开后会显示一列的值。就像查看一个静态的二维数组(比如 double[3][2])。你可以想象把这个扩展到 N 维。

我似乎无法弄清楚如何使用 lldb 的 Python 对象模型来在 std::vector 的线性缓冲区上强加层次结构。文档中没有相关信息,我已经在黑暗中摸索了大约一周。这里有一个我想为其创建可视化工具的简化数组类的例子。

任何帮助都非常感谢!

#include <vector>
#include <cassert>

template <typename T>
class myarray {
    int extent_[2];
    std::vector<T> data_;

public:
    myarray(int r, int c, const T* data) {
        extent_[0] = r;
        extent_[1] = c;
        data_.resize(r * c);
        for(size_t i = 0; i < data_.size(); ++i) data_[i] = data[i];
    }

    void reshape(int r, int c) {
        assert(r * c == data_.size());
        extent_[0] = r;
        extent_[1] = c;
    }
};


int main(int argc, const char * argv[])
{
    double initdata[6] = { 0, 1, 2, 3, 4, 5 };
    myarray<double> mydata(3, 2, initdata);

    mydata.reshape(1, 6);

    return 0;
}

根据要求:我希望看到的第一个 [3][2] 例子的输出可能看起来像这样。第一层有 3 个子项,代表 "行",每一行都有一个关于行中首个元素的摘要字符串。这个想法是获取矩阵数据的二维视图。当一行被展开时,它将被视为一组列值的数组。

LLDB 可能的合成输出:

mydata
[0]
   [0] = 0 <-- expanded contents
   [1] = 1
[1] = {2, 3} <-- summary string of row contents. First N elements, then ...
[2] = {4, 5}

对于一个简单向量的合成提供者示例,get_child_at_index 的实现大致如下,我在 update() 方法中确定了计数、值大小和值类型:

def get_child_at_index(self,index):
    logger = lldb.formatters.Logger.Logger()
    logger >> "get_child_at_index: " + str(index)
    if index < 0: return None;
    if index >= self.count: return None;
    try:
        offset = index * self.value_size
        return self.data.CreateChildAtOffset('['+str(index)+']',offset,self.value_type)
    except:
        return None

我觉得如果能弄清楚如何创建一个 SBType 来替代调用 CreateChildAtOffset 时的 value_type,我就能很容易地解决这个问题。这样我就可以构建任何我想要的结构。然而,经过多次尝试,我还是没能成功创建一个 SBType 对象。

有没有什么想法?有没有人知道如何从我构造的字符串中创建一个 SBType?

2 个回答

0

不确定这是否有用,但你可以找到已有的类型。

  target = lldb.debugger.GetSelectedTarget()
  type_list = target.FindTypes('base::Value')

如果你想用一个已有的类型来创建你的子类,这可能会对你有帮助。

1

我假设你已经看过这个链接:http://lldb.llvm.org/varformats.html

如果我理解得没错,你想要以更层次化的格式显示向量中的元素。

这个任务挺有意思的,不过你可能需要自己定义一些数据类型。目前在我们的公共API中,对这方面的支持似乎不多。作为一种变通办法,你当然可以运行一个表达式来生成你关心的结构体并保存它,但这样会比较慢。

在你的例子中,你具体想要得到什么样的视图呢?这种通过示例提供的信息实际上能帮助我们更好地理解细节。

补充说明:目前LLDB不允许你通过公共API创建新类型。你可以使用表达式解析器来获取你自己创建的SBType,像这个例子:

x = lldb.frame.EvaluateExpression("struct foo { int x; }; foo myfoo = {12}; myfoo")
data = lldb.SBData.CreateDataFromSInt32Array(lldb.eByteOrderLittle,8,[24])
x_type = x.GetType()
myOtherFoo = x.CreateValueFromData("myOtherFoo",data,x_type)
print myOtherFoo
OUTPUT: (foo) myOtherFoo = (x = 24)

这样做会比较慢,尤其是如果你不缓存你需要的foo类型(从你的例子来看,似乎是你模板参数T的T[2])的话。不过在LLDB有SB API可以通过clang创建类型之前(就像我们内部做的那样),这就是你唯一的办法。

撰写回答