Navigating STL types

How can I navigate STL types using their overloaded operators and member functions (e.g., “” and “.first()” for vectors) ? For example, take a C++ source file with:

std::vectorstd::string v;
v.push_back("foo”);

Breaking after this statement in lldb and typing “p v[0]” would be reasonably expected to return “foo”, but it gives a symbol lookup error. Similarly, “p v.first()” gives an error that there’s no member named “first”. I’m seeing this issue with clang/llvm 3.9 and 4.0 nightlies on Ubuntu 16.10 and with Apple’s versions on MacOS Sierra.

Internet rumor (e.g., this discussion) says this is aggressive inlining of STL code. I’m compiling in clang++ with “-O0 -g -glldb”.

In comparison, gdb prints the value of v[0] just fine when compiled with gdb.

What am I doing wrong?

Yes, this is a problem with our STL, we are forcing inlining and we need to fix this on libc++ side, it is scheduled, but we haven’t come to it yet.

Mehdi,

Yes, this is a problem with our STL, we are forcing inlining and we need to fix this on libc++ side, it is scheduled, but we haven’t come to it yet.

Any guesstimate as to the timeframe? Seems like being able to navigate STL types would be a useful thing.

Thanks,

Andreas

Mehdi,

Yes, this is a problem with our STL, we are forcing inlining and we need to fix this on libc++ side, it is scheduled, but we haven’t come to it yet.

Any guesstimate as to the timeframe?

Long time overdue, keep being punted because other higher priorities issues keep showing up, “soon” hopefully.

Seems like being able to navigate STL types would be a useful thing.

I don’t think anyone would disagree :slight_smile:

lldb has "synthetic child providers" for most of the STL collection types, so we do show all the child values, for instance my_vec is a std::vector<int> and:

(lldb) expr my_vec
(std::__1::vector<int, std::__1::allocator<int> >) $0 = size=10 {
  [0] = 0
  [1] = 1
  [2] = 2
  [3] = 3
  [4] = 4
  [5] = 5
  [6] = 6
  [7] = 7
  [8] = 8
  [9] = 9
}

Those values were printed by the std::vector data formatter. To learn more about data formatters, see:

http://lldb.llvm.org/varformats.html

You can access individual members of the array using the "frame variable" command, which prints local variables & arguments it looks up by name in the debug info:

(lldb) frame variable my_vec[5]
(int) my_vec[5] = 5

You can't do that using the "print" command, because it will actually try to run this as an expression, which fails without the accessor functions.

OTOH, the "frame variable" command doesn't run real expressions, it just looks up the variable by name and then accesses the synthetic children that the data formatter provides. But for many things this works well enough to get you going. You can combine "frame var" with the -L option and the expression parser if you need to pass a particular element to some function:

(lldb) frame variable -L my_vec[5]
0x00000001002000e4: (int) my_vec[5] = 5
(lldb) expr printf("%d\n", *((int *) 0x00000001002000e4))
5
(int) $1 = 2

That's not ideal, for sure, and it will be great when the STL leaves all these functions around for the debugger at -O0. But you can usually get your hands on what you need this way...

BTW, another even more horrible trick when you are desperate is to put:

template class std::vector<int>;

in your C++ sources somewhere (replace std::vector<int> with the instantiation you need to print.) That will cause clang to emit a full copy of the template instantiation in your binary, and then everything works as expected:

(lldb) expr my_vec.size()
(std::__1::vector<int, std::__1::allocator<int> >::size_type) $0 = 10
(lldb) expr my_vec[5]
(std::__1::__vector_base<int, std::__1::allocator<int> >::value_type) $1 = 5

etc. Obviously, this isn't a great long-term solution, but if you can afford the time to rebuild it does get the job done.

Jim

You could add an explicit instantiation of your template for C++ types you need.

Example without:

#include
#include

int main (int argc, char const *argv)
{
std::vector ints = { 1,2,3,4 };
for (auto i: ints)
printf(“%i\n”, i);
return 0;
}

(lldb) target create “a.out”
(lldb) b /auto i/
(lldb) r
(lldb) p ints.size()
error: Couldn’t lookup symbols:
__ZNKSt3__16vectorIiNS_9allocatorIiEEE4sizeEv

But add the explicit instantiation:

#include
#include

template class std::vector; /// <<<<<<<<<

int main (int argc, char const *argv)
{
std::vector ints = { 1,2,3,4 };
for (auto i: ints)
printf(“%i\n”, i);
return 0;
}

(lldb) target create “a.out”
(lldb) b /auto i/
(lldb) r
(lldb) p ints.size()
(std::__1::vector<int, std::__1::allocator >::size_type) $0 = 4

So you could have some piece of code somewhere in your project:

#ifndef NDEBUG
/// Explicitly instantiate any STL stuff you need in order to debug
#endif

GDB is probably working around this by doing things for you without running the code that doesn’t exist.

Greg