2

I am trying to create LLDB visualizers for classes in my project. The LLDB documentation is... sparse. I have an array class that stores the underlying data in a std::vector and has an extent array to describe the shape. It can also be reshaped later.

By default, the std::vector "data_" is always shown as a linear vector. I would like my provider to create a view hierarchy. In this example, the first level would be the child rows, each row expanding to a list of column values. Similar to viewing a static 2D array (i.e. double[3][2]). You can imagine extending this to N dimensions.

I can't seem to figure out how to use the lldb python object model to impose hierarchical structure onto the linear buffer in std::vector. Nothing seems to be documented, and I have been guessing in the dark for about a week. Here is a simplified example array class that I would like to create a visualizer for.

Any help is greatly appreciated!

#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;
}

As requested: The output I would like to see for the first [3][2] example might look like the following. The first level of 3 children are "rows", with a summary string of the leading elements in the row. The idea is to get a 2D view of the matrix data. Then when a row is expanded, it would be viewed as an array of column values.

LLDB potential synthetic output:

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

The synthetic provider examples for a simple vector implement get_child_at_index something like this, where I determined the count, value_size, and value_type in the update() method:

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

I think I can easily work this out if I could just figure out how to create an SBType to use in place of value_type when calling CreateChildAtOffset. I think I could then lay down any kind of structure that I like. However, with many shots in the dark, I couldn't figure out how to create an SBType object successfully.

Ideas? Does anyone know how to create an SBType from a string that I compose?

Ed Connell
  • 53
  • 5

2 Answers2

1

I am assuming you have already looked over: http://lldb.llvm.org/varformats.html

IIUC, what you want to do is display the elements of the vector in a more hierarchical format.

It's kind of an interesting task, one for which you're probably going to have to craft your own data types - something for which I don't think we have a whole lot of support in our public API currently. As a workaround, you can of course run an expression that generates the struct you care about and hold on to it - however that is going to be slow.

In your example, what exactly is the view you'd like to get? That kind of by example information can actually be helpful in figuring out more details.

EDIT: Currently LLDB doesn't let you create new types through the public API. What you can do to get your hands on an SBType of your own making is use the expression parser, as in this example:

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)

This is going to be fairly slow, especially if you don't cache the foo type you need (which from your example seems to be a T[2] for your template argument T) - but until LLDB has SB API to create types through clang (like we do internally), this is your only approach

Enrico Granata
  • 3,303
  • 18
  • 25
  • Hi Enrico, yes I did review varformats.html. It didn't seem to address my situation. Please see my edit. I added more detail. Thanks! – Ed Connell Mar 21 '14 at 08:12
  • I'm trying your example, but the first line evaluates to "No value", so consequently there is no type. Have you tried this? Are you getting a value and type for "x"? – Ed Connell Mar 24 '14 at 02:38
  • This works for me. Caveat: if you're in a data formatter, lldb.frame will not necessarily be valid - this was meant as example code to try out at the interactive script interpreter. If you're in a data formatter, you're going to need some other way to grab an SBFrame. SBValue has a .frame that you can access, and that will give you a valid SBFrame hopefully – Enrico Granata Mar 24 '14 at 18:49
0

Not sure if this will help, but you can find existing types

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

if you want to create your child with an existing type, that may help.