使用 Xcode/LLDB 打印/调试 libc++ STL

Posted

技术标签:

【中文标题】使用 Xcode/LLDB 打印/调试 libc++ STL【英文标题】:Printing/Debugging libc++ STL with Xcode/LLDB 【发布时间】:2017-02-02 11:14:00 【问题描述】:

我正在尝试在 Xcode 8 中使用 LLDB 来调试非常基本的 STL。我曾经能够像这样打印矢量:

p myvector[0]

查看第一个向量索引中的内容。现在当我这样做时,我得到了这个错误:

error: Couldn't lookup symbols:
  __ZNSt3__16vectorI9my_classNS_9allocatorIS1_EEEixEm

相反,我必须输入:

p myvector.__begin_[0]

为了得到任何输出。

我尝试从 LLDB svn 存储库中导入 libcxx.py 和 unordered_multi.py 脚本,但这似乎没有任何改变。

有没有人能够使用 libc++ 从 LLDB 获得任何有用的输出?

【问题讨论】:

您是否使用调试信息进行编译?你能提供一个独立的复制器吗? 当然调试信息已启用。 :) 这是一个可以重现该问题的愚蠢项目。只需在 std::cout 行上设置一个断点并在命中时运行 lldb 命令“p myVector[0]”。你会得到一个错误。如果您执行“p myVector.__begin_[0]”,它会打印得很好。 dropbox.com/s/ntjywxabxj3e4mc/Crap.zip?dl=0 【参考方案1】:

类似的问题也发生在我身上:error: Couldn't lookup symbols:

我的解决方案是在源代码的某处明确使用有问题的函数。

#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> a10;
    Vector<XXX> b10;

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

在第 1 行设置断点;并运行它。

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

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

宾果!!该函数是否存在于 TEXT 块中?

(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

我不确定,但我以前学过这个。在调试阶段,而不是生产阶段。如果我们在模板函数的行上设置断点,调试器会做什么?设置断点,实际上用陷阱或跳转替换一些现有的汇编代码,到处都是应用模板?或者只是在函数中设置一个断点?它是作为模板编写的。所以它应该在生产阶段内联。然而,在调试阶段,该函数不是内联的,而是作为普通函数编写的。请不要简单地相信我在这里所说的话。请自行确认。查阅gcc,clang,lldb.的文档

#include &lt;vector&gt; 的 MacOS 10.13.6,Xcode 版本 9.4.1 有一个宏 _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];

_LIBCPP_INLINE_VISIBILITY#include &lt;__config&gt; 中定义为:

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

hidden__always_inline__ 这样的关键字似乎可以控制行为。

当我在上面的示例解决方案代码中添加inline _LIBCPP_INLINE_VISIBILITY 时:

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

导致:

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

我希望帮助和有人更深入地研究。

【讨论】:

这种技术对于内联的自定义容器(不是 STL)来说非常棒!只需强制转换为 void 通过[] 运算符访问的第一个元素。【参考方案2】:

[]std::vector 上的运算符方法,因此要打印您想要的表达式,lldb 必须能够调用[] 方法。这里的问题是,OS X 上的 STL 非常积极地内联它所能做的一切,而不是浪费空间来生产相同功能的离线副本。这对于优化代码非常有用,但对于调试却不是很好,因为它使调试器没有[] 运算符可供调用。这就是您看到的错误消息。

如果您只想查看此向量中的元素,可以使用 lldb “STL 数据格式化程序” 为您完成这项工作。他们知道大多数 STL 类型的布局,并且可以打印大多数容器类型的元素。例如:

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

但是:

(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)

还有另一个命令“帧变量”,它可以检查静态对象,并与数据格式化程序挂钩。它不能调用函数和执行其他更复杂的表达式解析器任务,但它确实知道如何使用 STL 数据格式化程序来检索单个元素:

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

您甚至可以使用 frame var 的 -L 选项来定位向量的元素,然后您可以将地址转换为传递给其他函数:

(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

解决此问题的另一种调试方法(如果您使用的是 C++11)是:

template class std::vector<MyClass>

在您的代码中某处。这将指示编译器为此特化生成所有模板函数的离线副本。这不是一个很好的通用解决方案,您只想在调试构建时这样做,但它确实允许您调用这些函数并在复杂的表达式中使用它们。

【讨论】:

非常彻底的回答吉姆,我很感激! 我在带有 clang/lldb 3.9 的 Linux (Ubuntu 16.10) 上看到了相同的行为。但是 gcc/gdb 对(gdb) p my_vec[0] 没有任何问题。我想知道他们在做什么不同。 IME,clang/STL 的组合在内联方面更具侵略性,即使在 -O0 时,gcc 也往往如此。 根据llvm-dev post:“是的,这是我们的 STL 的问题,我们正在强制内联,我们需要在 libc++ 端解决这个问题,它已计划好,但我们还没有来还没到呢。” 有人可以详细说明最后一点,“模板类 std::vector”以及它有什么帮助?我尝试添加类似的行,但出现编译器错误。

以上是关于使用 Xcode/LLDB 打印/调试 libc++ STL的主要内容,如果未能解决你的问题,请参考以下文章

Xcode/LLDB:如何获取有关刚刚抛出的异常的信息?

Xcode LLDB 打印语句失败 - NSUndoManager

Xcode LLDB 观察点

如何调试:libc++abi.dylib:以 NSException 类型的未捕获异常终止?

Xcode lldb 无法附加到 MacOS 系统程序 /bin/cp -“不允许附加到进程。”

如何使用调试版本的 libc