Printing/Debugging libc++ STL with XCode/LLDB

雨燕双飞 提交于 2019-11-29 19:37:18
Jim Ingham

[] is an operator method on std::vector, so to print the expression you want, lldb would have to be able to call the [] method. The problem here is that the STL on OS X is aggressive about inlining everything it can, and not wasting space producing out of line copies of the same functions. That's great for optimized code, but not so good for debugging because it leaves the debugger with no [] operator to call. That's the error message you are seeing.

If you just want to see the elements in this vector, you can use the lldb "STL data formatters" to do this work for you. They know how most STL types are laid out, and can print the elements of most container types. For instance:

(lldb) expr my_vec[0]
error: Couldn't lookup symbols:
  __ZNSt3__16vectorI3FooNS_9allocatorIS1_EEEixEm

but:

(lldb) expr my_vec
(std::__1::vector<Foo, std::__1::allocator<Foo> >) $0 = size=2 {
  [0] = (var1 = 10, var2 = 20)
  [1] = (var1 = 10, var2 = 20)
}

There is also another command "frame variable" which can inspect static objects, and hooks into the data formatters. It can't call functions and do other more complex expression parser tasks, but it does know how to use the STL data formatters to retrieve individual elements:

(lldb) frame var my_vec[1]
(Foo) my_vec[1] = (var1 = 10, var2 = 20)

You can even use frame var's -L option to locate the elements of the vector, and then you can cast the address to pass it to other functions:

(lldb) frame var -L my_vec[1]
0x0000000100100348: (Foo) my_vec[1] = {
0x0000000100100348:   var1 = 10
0x000000010010034c:   var2 = 20
}
(lldb) expr printf("%d\n", ((class Foo *) 0x0000000100100348)->var1)
10
(int) $3 = 3

Another way to work around this for debugging - if you are using C++11 - is by putting:

template class std::vector<MyClass>

in your code somewhere. That will instruct the compiler to emit out-of-line copies of all the template functions for this specialization. That isn't a great general solution, and you only want to do it for debug builds, but it does let you call these functions and use them in complex expressions.

The similar problem also happens with me: error: Couldn't lookup symbols:

My solution is to explicitly use the questioned function somewhere in a source code.

#include <vector>

template<typename T>
struct Vector : std::vector<T>
{
    Vector(size_t n)
    : std::vector<T>{n}
    {}

    T& operator[](size_t n)
    { return std::vector<T>::operator[](n); }
};

struct XXX
{
    int x;
};

void func()
{
    std::vector<XXX> a{10};
    Vector<XXX> b{10};

    auto x = b[0]; // gcc will produce an assembler code of operator[] for debug purpose
    1;  // as a break point
}

Set a breakpoint on the line of 1; and run it.

(lldb) p a[0]
error: Couldn't lookup symbols:
  __ZNSt3__16vectorI3XXXNS_9allocatorIS1_EEEixEm

(lldb) p b[0]
(XXX) $0 = (x = 0)

Bingo!! Does the function exist in a TEXT block?

(lldb) image lookup -r -n 'XXX.*operator'
1 match found in /Users/xxx/Library/Developer/Xcode/DerivedData/xxx:
        Address: sandbox[0x00000001000011f0] (sandbox.__TEXT.__text + 256)
        Summary: sandbox`Vector<XXX>::operator[](unsigned long) at main.cpp:19

I am not sure, but I had learned this before. In a debugging stage, instead of production stage. If we set a breakpoint on a line in a function of a template, what would a debugger do? Setting breakpoints, actually replacing some existing assembler code with trap or jump, here and there everywhere the template is applied? Or just setting a single breakpoint in a function? It is written as a template. So it should be inlined in a production stage. In a debugging stage, however, the function is not inlined and written as a normal function. Please do not simply believe what I say here. Please confirm by yourself. Consult documentation of gcc, clang, and lldb.

#include <vector> of MacOS 10.13.6, Xcode Version 9.4.1 has a macro _LIBCPP_INLINE_VISIBILITY:

template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
typename vector<_Tp, _Allocator>::reference
vector<_Tp, _Allocator>::operator[](size_type __n)
{
    _LIBCPP_ASSERT(__n < size(), "vector[] index out of bounds");
    return this->__begin_[__n];
}

The _LIBCPP_INLINE_VISIBILITY is defined in #include <__config> as:

#define _LIBCPP_INLINE_VISIBILITY __attribute__ ((__visibility__("hidden"), __always_inline__))

Such keywords hidden and __always_inline__ seem to control behavior.

When I added inline _LIBCPP_INLINE_VISIBILITY to the sample solution code above:

    inline _LIBCPP_INLINE_VISIBILITY
    T& operator[](size_t n)
    { return std::vector<T>::operator[](n); }

resulted in:

(lldb) p b[0]
error: Couldn't lookup symbols:
  __ZN6VectorI3XXXEixEm

I hope that help and somebody look into much more deeply.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!