如何在GDB中对STL容器进行漂亮打印?

65

我按照GDB维基上的指示安装了Python漂亮打印机以查看STL容器。 我的~/.gdbinit现在看起来像这样:

python 
import sys 
sys.path.insert(0, '/opt/gdb_prettyprint/python') 
from libstdcxx.v6.printers import register_libstdcxx_printers 
register_libstdcxx_printers (None) 
end 

然而,当我运行GDB并尝试打印STL类型时,我会得到以下结果:

print myString
Python Exception <class 'gdb.error'> No type named std::basic_string<char>::_Rep.: 
$3 = 

有人能解释一下这个问题吗?我正在运行Ubuntu 12.04,其中包括GDB 7.4。


7
可能是因为C++库已经改变了其内部类型和成员变量,而Python模块没有跟上。 - Some programmer dude
请问您能否提供更多信息,例如C++源代码、编译器选项等?我在Ubuntu 12.04上测试过了,它对我有效。 - user1202136
在CEntOS 7上对我有用。要注意的一个潜在错误是在启动gdb时就出现了。它可能会打印一个Python错误,你可能会错过它。 - user666412
广告)我对默认的libstdcxx漂亮打印机有一些不喜欢的地方,所以我进行了一些修改 - user202729
10个回答

14

它只在Ubuntu 17.04上工作

看起来Debian现在终于合理地整合了这些东西:

main.cpp

#include <map>
#include <utility>
#include <vector>

int main() {
    std::vector<int> v;
    v.push_back(0);
    v.push_back(1);
    v.push_back(2);
    std::map<int,int> m;
    m.insert(std::make_pair(0, 0));
    m.insert(std::make_pair(1, -1));
    m.insert(std::make_pair(2, -2));
}

编译:

g++ -O0 -ggdb3 -o main.out -std=c++98 main.cpp

结果:

(gdb) p v
$1 = std::vector of length 3, capacity 4 = {0, 1, 2}
(gdb) p m
$2 = std::map with 3 elements = {[0] = 0, [1] = -1, [2] = -2}

我们可以看到漂亮的打印程序已经安装了:

(gdb) info pretty-printer

其中包含以下行:

global pretty-printers:
  objfile /usr/lib/x86_64-linux-gnu/libstdc++.so.6 pretty-printers:
  libstdc++-v6
    std::map
    std::vector

打印机由文件提供:

/usr/share/gcc-7/python/libstdcxx/v6/printers.py

此功能随着主要的C++库包libstdc++6一起提供,并位于GCC源代码中的libstdc++-v3/python/libstdcxx目录下:https://github.com/gcc-mirror/gcc/blob/releases/gcc-6.3.0/libstdc%2B%2B-v3/python/libstdcxx/v6/printers.py#L244

待办事项:GDB如何找到该文件是最终的谜团,因为它不在我的Python路径python -c "import sys; print('\n'.join(sys.path))",所以肯定是某个地方硬编码了?

自定义类

请参阅如何定义自定义toString方法并在以下位置调用它:Printing C++ class objects with GDB

检查优化代码中的特定元素

上次我尝试很难实现这个功能,你会得到“无法评估函数--可能已内联”C++, STL, GDB: Cannot evaluate function maybe inlined

在未经优化的代码上它可以工作:Inspecting standard container (std::map) contents with gdb


@Evg 我还没有深入研究过。它对我来说是有效的,只是我不知道具体是怎么回事。如果你发现了细节,请告诉我。 - Ciro Santilli OurBigBook.com
1
关于TODO(来自https://gcc.gnu.org/legacy-ml/libstdc++/2009-02/msg00056.html): 打印机可以在gdb读取objfile时由其自动加载。 gdb会寻找一个文件,该文件是objfile的真实名称,并添加“-gdb.py”;例如,libstdc++.so.6.0.11-gdb.py。 - Rosen Matev

12
您可以尝试使用以下的GDB宏(将其附加到您的~/.gdbinit文件中),以打印STL容器类型数据,甚至是它们的数据成员:https://gist.github.com/3978082

6

在尝试解决问题时,我遇到了这个页面。最终我解决了问题,并认为值得分享我的经验。

我使用的是gcc-5.2,因此我从svn仓库下载了gcc-5-branch版本的漂亮打印机。但是,我不得不进行以下两个修改:

  1. when editing the ~/.gdbinit file, the suggested addition is

    python
    import sys
    sys.path.insert(0, '/home/bartgol/.gdb/gdb_printers/python')
    from libstdcxx.v6.printers import register_libstdcxx_printers
    register_libstdcxx_printers (None)
    end
    
然而,我必须注释掉register_libstdcxx_printers (None)这一行,因为我一直收到一个错误,告诉我libstdcxx_printers已经被注册了。显然,在导入阶段它们会被注册。
第二步,我需要编辑printers.py文件,针对std::setstd::map。由于类型_Rep_type在两者中都是私有的,特别地,在std::mapstd::set中替换了children例程为svn仓库上gcc-4_6-branch版本中相应的例程。从那以后就没有任何错误,现在一切都很好地打印出来了。
希望这能有所帮助。

1
这对我来说是有效的,使用在Ubuntu 18.04上自定义编译的GDB和来自ubuntu-toolchain-r/test仓库的gcc-9。我只需要更改路径为/usr/share/gcc-9/python/ - Ruslan

5

4

这里类似, 下面的内容适用于我的~/.gdbinit文件:

python
import sys
sys.path.insert(0, '/usr/share/gcc-8/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end

4

除了您提到的链接中列出的方法,您可以尝试使用此处的脚本。

请按照以下步骤操作:

1) 将脚本下载到/your/path。将其命名为某个名称,例如your_name.conf

2) 如果没有~/.gdbinit文件,请在主目录中添加一个。

3) 在您的~/.gdbinit中添加一行source /your/path/your_name.conf

4) 重新启动gdb。尝试使用pvector

您可以使用像help pvector这样的命令查找帮助信息。

例如:

pvector vec 5      # Prints element[5] in vec
pvector vec 5 10   # Prints elements in range [5, 10] in vec. (5, 6, 7, 8, 9, 10)

FYI,这个脚本会向gdb添加一些命令(例如pvectorplistpmap等),其功能是打印STL的元素。它还添加了print pretty,可以得到漂亮的格式,如下图所示:

enter image description here

另外,如果您想知道在gdb中如何访问STL的元素,请查看这些命令的代码。代码中没有任何秘密哦。^_^
例如, 使用._M_impl._M_start来访问vector。

p vec._M_impl._M_start + 4 # 打印vec[4]


2
如果在Python异常后键入info type _Rep,gdb会通知您有哪些已加载的类与_Rep匹配。该列表可能会帮助您找到python无法找到std :: string class的原因。
我刚遇到了你的问题,在我的情况下是英特尔c编译器(icc)打破了漂亮的打印。特别是未经限定的icc名称适用于std :: string,结果为:
std::basic_string<char, std::char_traits<char>, std::allocator<char> >::std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep;

但是漂亮的打印机正在寻找不合格的gcc名称:

std::basic_string<char, std::char_traits<char>, std::allocator<char>::_Rep;

我解决问题的方法是修改printers.py文件中的StdStringPrinter类,将字符串的未限定名称添加到类型名称中以便在gdb中查找。替换以下行:
reptype = gdb.lookup_type (str (realtype) + '::_Rep').pointer ()

使用以下方法:

reptype = gdb.lookup_type (str (realtype) + '::' + str (realtype) + '::_Rep').pointer ()

通过从 info type 获得的列表,您可以修复您的漂亮打印机使它们正常工作。


2

像您上面发布的错误通常出现在使用clang编译的程序中,并且您尝试通过gdb进行调试(应该用于GCC-build程序)。 理论上,可以使用gdb调试LLVM-build程序,反之亦然。但是为了避免出现以上所述的问题,如果您使用clang,则应该使用lldb,如果您使用g++,则应该使用gdb


1
我认为你正在使用非 GNU STL 库,或者可能是一个非常旧的 GCC libstdc++。在我的编译器上,普通 STL 字符串的类型为:std::basic_string<char, std::char_traits<char>, std::allocator<char> >。请注意,这不是 std::basic_string<char>
Python 代码中包含了这段内容:
reptype = gdb.lookup_type (str (realtype) + '::_Rep').pointer ()

这个查找嵌套类型::Rep,无论基本字符串类型实际上是什么。错误消息表明,您正在使用的奇怪库的字符串类实际上没有一个::Rep嵌套类型。


1

我也在我的系统上遇到了这个问题。

对我来说,解决方法如下:

在~/.gdbinit中进行以下修复

python
import sys
sys.path.insert(0, '<path/to/gcc/dir>/GCCcore/9.3.0/share/gcc-9.3.0/python/')
exec(open("<path/to/gcc/dir>/GCCcore/9.3.0/lib64/libstdc++.so.6.0.28-gdb.py").read())
end

问题在于包含漂亮打印机的python文件夹不是Python搜索path的一部分(就像您通常需要将Python模块位置添加到$PYTHONPATH中一样)。第一个命令完成了这个任务。下一个问题是现在需要加载漂亮的打印机。GCC在我的系统上提供了一个脚本来完成这个任务,位于GCCcore/9.3.0/lib64/libstdc++.so.6.0.28-gdb.py。第二个命令会执行此脚本,从而加载打印机。

附言

在进行此修改之前,我也遇到了以下错误:

warning: File "<path/to/gcc/dir>/GCCcore/9.3.0/lib64/libstdc++.so.6.0.28-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".

可以通过将以下内容添加到gdb init文件中来修复。
add-auto-load-safe-path  <path/to/gcc/dir>/software/GCCcore/9.3.0/

然而,这仍然不能让漂亮的打印机起作用(因为路径仍未设置),并且在上面的Python修复后已不再需要。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接