【问题标题】:Why the breakpoints set in STL are "skipped/ignored" while using LLDB?为什么在使用 LLDB 时 STL 中设置的断点被“跳过/忽略”?
【发布时间】:2022-01-02 09:02:49
【问题描述】:

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

这是我对istreamBREAKPOINT 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” 时,我可以踏入 “Line 310”。但是对于 LLDB,当我停在 “BREAKPOINT A” 时,我无法 踏入 “Line 310”,而尝试踏入会导致执行停止在“Line 1448”,它只是跳过了“Line 310”。那是为什么?而且,无论是使用 LLDB 还是 GBD,我都无法在 “Line 310” 处显式设置断点。不知道我的情况发生了什么。

所以我的问题是:为什么 STL 中的某些代码行会被 LLDB 跳过/忽略? (就我而言,即第 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

【问题讨论】:

  • 你想进入good()函数吗?不了解 LLDB,但有些函数非常简单,以至于它们可能即使在调试模式下也会被内联。 good() 只是检查错误状态标志是否仍然为零,所以无论如何也没什么可看的。
  • @BoP 是的,但事实上我什至无法到达那条线,即将调用good() 的线。如果它们即使在调试模式下也是内联的,我们是否有一些方法来改变这种强制行为?

标签: c++ debugging gdb lldb libc++


【解决方案1】:

默认情况下,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 函数中。

【讨论】:

    【解决方案2】:

    感谢@Jim Ingham 提供关于我的问题的第一个地址。

    除了解决了我大部分问题的@Jim Ingham's Answer之外,我将添加一些补充信息以解决我的部分问题。


    1. 为什么使用 LLDB 或 GBD,我们只是不能在“第 310 行”显式设置断点。

    简短的原因:我们可以直接在“Line 1447”设置断点是因为该行驻留在istream头文件中(即INSTALL_DIR/include/c++/v1/istream),而“Line 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)表示相应的类类型在别处定义,因此,我们不能设置断点,除非调试器已经进入与该类定义相关的指令。 (TODO:仍然不知道调试器如何动态找到它

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-30
      • 2015-01-15
      • 2023-03-30
      • 1970-01-01
      • 2013-02-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多