为什么在STL中设置的断点在使用LLDB时是"skipped/ignored"?

Why the breakpoints set in STL are "skipped/ignored" while using LLDB?

我的目标是:我想进入STL的某行代码istream。所以我使用自定义构建 "LIBC++13""Debug" 构建类型(我使用的命令显示在底部),所以那(我认为)我可以获得一个完全可调试的 STL 版本,并且能够进入我想要的一切。但是我遇到了一个问题。

这里是我的断点设置 istream, BREAKPOINT A(Line 1447) 想进入 Line 310:

// -*- C++ -*-
//===--------------------------- istream ----------------------------------===//

// ..................(other).....................

basic_istream<_CharT, _Traits>&
operator>>(basic_istream<_CharT, _Traits>& __is,
           basic_string<_CharT, _Traits, _Allocator>& __str)
{
    ios_base::iostate __state = ios_base::goodbit;
    typename basic_istream<_CharT, _Traits>::sentry __sen(__is);      // BREAKPOINT A (Line 1447)

    if (__sen)                                                   // Line 1448                         
    {
    // ...
}

// ..................(other).....................

template <class _CharT, class _Traits>
basic_istream<_CharT, _Traits>::sentry::sentry(basic_istream<_CharT, _Traits>& __is,
                                               bool __noskipws)
    : __ok_(false)
{

    if (__is.good())                  // Want To Step Into Here   (Line 310)
    {
    // ...
}

和程序:

#include <fstream>
#include <string>
using namespace std;
int main()
{
    ifstream ifs{"testdata.txt"};
    string tmp{};
    ifs >> tmp;     
}

我的问题是: 使用 GDB,当我在 "BREAKPOINT A" 处停止时,我可以进入 “第 310 行”。但是对于 LLDB,当我停在 "BREAKPOINT A" 时,我 cannot 进入 "Line 310" ],并且尝试进入会导致执行停止在 “第 1448 行”,这只是跳过 “第 310 行”。那是为什么?而且,无论是使用 LLDB 还是 GBD,我都无法在 “第 310 行” 处明确设置断点。不知道我的情况是怎么回事。

所以我的问题是:为什么STL中的一些代码行会被LLDB skipped/ignored? (在我的例子中,即 310 行


LIBC++13通过命令构建:(使用Building Libcxx Guides中的示例,/usr/local/myllvm是我的安装位置))

cmake -G Ninja -S llvm -B build \ 
        -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi" \
        -DCMAKE_BUILD_TYPE="Debug" \
        -DCMAKE_INSTALL_PREFIX="/usr/local/myllvm" \
        -DCMAKE_CXX_COMPILER="clang++"

程序使用 recommended 选项编译:

clang++ -nostdinc++ -nostdlib++ \
        -isystem /usr/local/myllvm/include/c++/v1 \
        -L /usr/local/myllvm/lib \
        -Wl,-rpath,/usr/local/myllvm/lib \
        -lc++ -g -O0 test1.cpp

默认情况下,lldb 将 std::: 命名空间中的函数与没有调试信息的函数一样处理,并自动退出而不是在函数中停止。

对于大多数用户而言,您拥有内联 stl 函数的源信息这一事实与其说是对这些函数感兴趣的表示,不如说是实现的意外;进入 STL 函数体是破坏性的,没有帮助。

此行为由 lldb 设置控制 target.process.thread.step-avoid-regex - 如果 lldb 进入与此正则表达式匹配的函数,lldb 将再次自动退出。默认值为:

(lldb) settings show target.process.thread.step-avoid-regexp
target.process.thread.step-avoid-regexp (regex) = ^std::

如果您确实需要单步执行 STL 函数,只需 运行:

(lldb) settings clear target.process.thread.step-avoid-regexp

然后 lldb 将在您有源信息的 stl 函数中停止。

感谢@Jim Ingham 第一次解决我的问题。

除了解决了我大部分问题的之外,我将添加一些补充信息来解决我剩下的部分问题。


  1. Why either with LLDB or GBD, we just cannot explicitly set a breakpoint at "Line 310".

简短的原因:我们可以直接在“1447行”设置断点是因为该行驻留在istream头文件中(即INSTALL_DIR/include/c++/v1/istream ),而“第 310 行”没有,而是驻留在原始 istream 源文件中(即 BUILD_DIR/include/c++/v1/istream)。

实际发生的事情都与可调试可执行目标文件提供的源信息有关,尽管表面上看,这两行应该在同一个源文件中,是分开的,因为它生成的 DWARF 部分包含这两行的不同信息:

########### Using "llvm-dwarfdump" tool
########### Output by: llvm-dwarfdump a.out

...

# This relates to "Line 310"
0x00003a4a:         DW_TAG_class_type
                      DW_AT_name        ("sentry") 
                      DW_AT_declaration (true)

... 

# This relates to "Line 1448"
0x00004ae4:         DW_TAG_variable
                      DW_AT_location    (DW_OP_fbreg -24)
                      DW_AT_name        ("__sen")
                      DW_AT_decl_file   ("/usr/local/myllvm/include/c++/v1/istream")
                      DW_AT_decl_line   (1448)
                      DW_AT_type        (0x00003a4a "sentry")
...

根据 DWARF 2 StandardDW_AT_declaration (true) 意味着相应的 class 类型在别处定义,因此,我们不能设置该断点,除非调试器已经进入与该断点相关的指令class的定义。 (TODO:仍然不知道调试器如何动态找到它

反之,我们可以在需要符号__sen的那一行设置断点,之所以有效,是因为编译器已经为它生成了完全可调试的信息,即DW_AT_decl_file对应“源文件”,DW_AT_decl_line对应“源文件行号”等