为什么在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 第一次解决我的问题。
除了解决了我大部分问题的之外,我将添加一些补充信息来解决我剩下的部分问题。
- 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 Standard,DW_AT_declaration (true)
意味着相应的 class 类型在别处定义,因此,我们不能设置该断点,除非调试器已经进入与该断点相关的指令class的定义。 (TODO:仍然不知道调试器如何动态找到它)
反之,我们可以在需要符号__sen
的那一行设置断点,之所以有效,是因为编译器已经为它生成了完全可调试的信息,即DW_AT_decl_file
对应“源文件”,DW_AT_decl_line
对应“源文件行号”等
我的目标是:我想进入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 第一次解决我的问题。
除了解决了我大部分问题的
- 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 Standard,DW_AT_declaration (true)
意味着相应的 class 类型在别处定义,因此,我们不能设置该断点,除非调试器已经进入与该断点相关的指令class的定义。 (TODO:仍然不知道调试器如何动态找到它)
反之,我们可以在需要符号__sen
的那一行设置断点,之所以有效,是因为编译器已经为它生成了完全可调试的信息,即DW_AT_decl_file
对应“源文件”,DW_AT_decl_line
对应“源文件行号”等