GDB本身不支持将STL容器中的各个元素打印出来,如果使用p命令,只能打印容器的成员。

STLSupport - GDB Wiki 一文中列举了三种让GDB支持STL容器的方式。 我试过前两种,第一种最简单,打印的结果也最清楚,然而此方式需要GDB支持Python(7.0)以上版本,而且貌似对GCC编译器也有版本要求,我试过GDB 7.8调试GCC 4.1编译的程序,第一种方式能正常显示vector等容器,但是显示map的时候总是出现如下错误。

Python Exception exceptions.ValueError Cannot find type CarInfoContainer::_Rep_type: 
$1 = std::map with 2 elements

由于时间有限,没有深入研究是何原因引起。总之,在现实的大型项目开发团队中,往往开发所用的各种工具如GDB,GCC等版本都偏老(原因你懂的:-)),很可能第一种方式在开发环境中无法使用。本文就举一个简单的例子阐述一下如何用第二种方式显示很复杂的STL容器。

方式二演示

如果容器类型非常简单,比如std::map<int, int>,那么第二种方式可以很简单地用命令“pmap varialbe-name int int”打印出各个元素。但是如果容器很复杂,特别是容器里面还包含容器的时候,输入的命令就很有讲究了,一旦有一点点错误就会无法正常显示结果。以下是一个包含复杂STL容器的例子。
#include <map>
#include <string>
#include <vector>
using namespace std;
struct Car
    Car (const std::string& model, int32_t price)
      : _model(model),
        _price(price)
    std::string _model;
    int32_t _price;
typedef std::vector<Car> CarContainer;
typedef std::map<std::string, CarContainer> CarInfoContainer;
int main()
    CarContainer fordCars;
    fordCars.push_back(Car("Focus", 120000));
    fordCars.push_back(Car("Mondeo", 200000));
    CarContainer vwCars;
    vwCars.push_back(Car("Passat", 210000));
    CarInfoContainer cars;
    cars["US"] = fordCars;
    cars["German"] = vwCars;
    return 0;                                                  
 
当cars map插入两个元素之后,如何用GDB打印其中的内容呢?又如何更进一步,通过map的value type而不是fordCars变量打印出fordCars vector中包含的元素呢? 方法1:直接输入“pmap cars 'std::string' CarContainer”命令,但是GDB会显示如下结果:
elem[0].left: No symbol "std::string" in current context.
我们的程序是打开“-g”调试命令的,GDB已经有了所有的symbol,为何还无法解析“std::string”呢?原因是"std::string"是未经模板实例化的原始类型,模板实例化是编译时发生的,GDB只能识别运行时的变量类型。那么如何得到"std::string"模板实例化后的真正类型?可以用“p cars”显示出cars map的成员类型信息。
(gdb) p cars
$1 = {
  _M_t = {
    _M_impl = {
      <std::allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >> = {
        <__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >> = {<No data fields>}, <No data fields>}, 
      members of std::_Rb_tree<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > >,std::_Select1st<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > >,std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >::_Rb_tree_impl<std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >,false>: 
      _M_key_compare = {
        <std::binary_function<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,std::basic_string<char, std::char_traits<char>, std::allocator<char> >,bool>> = {<No data fields>}, <No data fields>}, 
      _M_header = {
        _M_color = std::_S_red, 
        _M_parent = 0x606140, 
        _M_left = 0x606210, 
        _M_right = 0x606140
      _M_node_count = 2
 
其中“std::_Rb_tree_node”中的“std::pair”第一个元素就是"std::string"实例化后的类型,即”std::basic_string<char, std::char_traits<char>, std::allocator<char> >“。
(gdb) pmap cars 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >' CarInfoContainer
elem[0].left: $8 = {
  static npos = 18446744073709551615, 
  _M_dataplus = {
    <std::allocator<char>> = {
      <__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
    members of std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Alloc_hider: 
    _M_p = 0x6061f8 "German"
elem[0].right: $9 = {
  _M_t = {
    _M_impl = {
      <std::allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >> = {
        <__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >> = {<No data fields>}, <No data fields>}, 
      members of std::_Rb_tree<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > >,std::_Select1st<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > >,std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >::_Rb_tree_impl<std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >,false>: 
      _M_key_compare = {
        <std::binary_function<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,std::basic_string<char, std::char_traits<char>, std::allocator<char> >,bool>> = {<No data fields>}, <No data fields>}, 
      _M_header = {
        _M_color = 6316448, 
        _M_parent = 0x6061a0, 
        _M_left = 0x0, 
        _M_right = 0x20db1
      _M_node_count = 0
elem[1].left: $10 = {
  static npos = 18446744073709551615, 
  _M_dataplus = {
    <std::allocator<char>> = {
      <__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
    members of std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Alloc_hider: 
    _M_p = 0x606108 "US"
elem[1].right: $11 = {
  _M_t = {
    _M_impl = {
      <std::allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >> = {
        <__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >> = {<No data fields>}, <No data fields>}, 
      members of std::_Rb_tree<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > >,std::_Select1st<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > >,std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Car, std::allocator<Car> > > > >::_Rb_tree_impl<std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >,false>: 
      _M_key_compare = {
---Type <return> to continue, or q <return> to quit---
        <std::binary_function<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,std::basic_string<char, std::char_traits<char>, std::allocator<char> >,bool>> = {<No data fields>}, <No data fields>}, 
      _M_header = {
        _M_color = 6316496, 
        _M_parent = 0x6061d0, 
        _M_left = 0x0, 
        _M_right = 0x21
      _M_node_count = 6316248
Map size = 2

显然是可以的,这说明typedef定义的类型GDB是可以识别的。当然,如果输入typedef原本的类型也是可以的。比如”pmap cars 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >' 'std::vector<Car, std::allocator<Car> >'“命令和上面的命令等价。 _M_dataplus = { <std::allocator<char>> = { <__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, members of std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Alloc_hider: _M_p = 0x606028 "Focus" _price = 120000 elem[1]: $13 = { _model = { static npos = 18446744073709551615, _M_dataplus = { <std::allocator<char>> = { <__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, members of std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Alloc_hider: _M_p = 0x606078 "Mondeo" _price = 200000 Vector size = 2 Vector capacity = 2 Element type = Car *
至此,我们就成功地把复杂的STL容器元素全部打印出来了。 1. GDB中打印出正确的STL容器的关键是传入准确的变量类型。此处的变量类型必须是GDB识别的类型,而不一定是C++代码里面的直接类型。对于简单类型和typedef定义的类型,GDB都能识别,但是模板必须是实例化之后的真正类型。 2. 输入复杂类型的时候有时候需要用单引号括起来,比如”pmap cars 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >' CarInfoContainer“,不然GDB会无法识别。 3. 有时候不知道一个变量的准确类型怎么办?用"whatis variable-name"命令可以显示正确的类型,而且此类型肯定是GDB可以识别的。 4. 在复杂的STL类型实例化后往往很长很复杂,带有很多尖括号,在一堆尖括号中,比如以上std::pair,找到正确匹配的一对尖括号很重要,万一找错就会导致无法正常打印结果,有什么编辑器可以高亮显示匹配的尖括号呢?答案是常用的编辑器都只能匹配大小括号,无法匹配尖括号,默认的vim也是如此,但是vim还是够强大,可以用”:set mps+=<:>“命令支持高亮匹配尖括号。 # The following STL containers are currently supported: # std::vector -- via pvector command # std::list -- via plist or plist_member command # std::map -- via pmap or pmap_member command # std::multimap -- via pmap or pmap_member command # std::set -- via pset command # std::multiset -- via pset command # std::deque -- via pdequeue command # std::stack -- via pstack command # std::queue -- via pqueue command # std::priority_queue -- via ppqueue command # std::bitset -- via pbitset command # std::string -- via pstring command # std::widestring -- via pwstring command 使用gdb调试C++程序时,无法使用命令p 变量名输出STL容器的元素数据。例如有一个std::vector<int> datas变量, 执行p datas,输出如下: (gdb) p datas $2 = { <std::_Vector_base<int, std::allocator<int> >> = { _M_impl = { <std::allocator<int>> = {
用c++11实现了一个线程池。测试程序中,主线程不停的向任务队列中添加任务,工作线程不停的从任务队列中取任务。 通过打印发现,任务队列的长度不停的在增长,工作队列并没有及时的取到任务去执行。 接下来通过gdb调试,查看线程状态,发现工作线程停止在lock_wait中: (gdb) Id Target Id Frame 3 Thread 0x7fcb2d868700 (LWP 16522) "worker 0" 0x00007fcb2e66954d in __lll_lo
C++内存越界导致的std::map异常      前段时间在定位一个程序崩溃的问题,虽然有dump文件,能够看到出问题的具体代码行数,问题都出在同一个map上。      dump1显示map下标插入数据时异常。      dump2显示调用ma...
最近在一个Ubuntu的机器上,调试程序,每步调试都输出几条Python Exception <type 'exceptions.NameError'> Installation error: gdb.execute_unwinders function is missing:,看着很不习惯,于是找了个方法解决了下这个问题。 出现这个问题的原因一般是G...
print(&amp;quot;可能产生异常的代码&amp;quot;) except Exception as e: print(&amp;quot;处理异常错误的代码&amp;quot;) else: print(&amp;quot;没有捕获到异常&amp 1.有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这种错误我们通常称之为bug,bug是必须修复的。 2.有的错误是用户输入造成的,比如让用户输入email地址,结果得到一个空字符串,这种错误可以通过检查用户输入来做相应的处理。 3.还有一类错误是完全无法在程序运行过程中... (gdb) bt #0 SafeTimer::add_event_after (this=0x7efcfb7f23c0, seconds=0.9989011287689209, callback=callback@entry=0x7efc5c014be0) at /home/ceph/ceph/src/common/Timer.cc:118 #1 0x00007efcf... 这将在内核启动时设置断点,并继续执行内核。当内核执行到断点时,gdb将停止执行并等待命令。 以上是使用gdb调试Linux内核的基本步骤,具体调试方法和命令可以参考gdb文档和Linux内核调试文档。 ### 回答2: gdb是一款功能强大的调试器,在日常的编程开发中得到了广泛应用。然而,gdb调试Linux内核时与调试用户态应用程序时有所不同。调试内核需要使用gdb的特殊功能来处理调试内核的问题。在下面的几个方面中,我将解释如何使用gdb调试Linux内核。 1. 准备gdb环境 首先需要将gdb环境设置为可以使用内核符号。在编译内核时,需要在Makefile中添加CONFIG_DEBUG_INFO和CONFIG_DEBUG_KERNEL选项,以支持调试信息。此外,还需要安装所需的内核符号,然后通过"sudo sysctl -w kernel.yama.ptrace_scope=0"以解决防止调试器附加的安全机制问题。 2. 加载内核映像 通过gdb来加载内核映像。使用gdb命令"file vmlinux"来加载内核映像,其中vmlinux是含有调试符号的内核镜像文件。 3. 内核断点调试 可以使用gdb设置内核断点,以调试内核时确定内核程序执行过程中的问题。使用gdb命令"b <function>"设置函数断点,而使用"b * <address>"设置指定地址的断点。 4. 调试内核panic 当内核执行时发生错误时,系统会进入panic状态。如果需要调试内核panic,可以使用gdb命令"handle SIGTRAP noprint pass"来设置中断处理。使用"monitor halt"或直接ctrl+c可以停止内核,查看是什么出问题了,并且使用"cont"命令让内核继续运行。 5. 查看内核堆栈 可以使用gdb命令"bt"来查看内核的堆栈,以确定调试内核时的问题。在通过gdb调试内核处理内核问题时,内核堆栈非常有用。 总的来说,使用gdb调试Linux内核需要更多的操作方式和技巧,但是如果熟练掌握gdb的某些功能和命令,并且了解内核基本结构和运行机制,就可以高效地调试内核出现的问题。 ### 回答3: GDB(GNU调试器)是一个强大的调试工具,也可以用来调试Linux内核。但是,与调试应用程序或用户空间程序相比,内核调试可能会更加复杂。下面是关于如何使用GDB调试Linux内核的一些指南和步骤。 1.编译内核 为了调试内核,首先需要编译内核并安装它。在编译内核时,需要启用符号表。使用如下命令: $ make menuconfig 在Kernel hacking中设置Debug kernel,这将启用符号表的编译。然后使用命令“make”编译内核并安装它。 2.配置调试环境 在内核启动时,启用调试合适的调试器非常重要。例如,可以使用串线调试器而不是控制台输出,因为调试信息可能无法立即打印到控制台。可以通过UART控制台或JTAG调试器进行调试,这通常比控制台输出更可靠。 3.使用GDB连接到内核 使用GDB的第一步是启动GDB,同时指定内核映像文件作为第一个参数。例如,如果内核映像文件为”vmlinuz”,可以使用如下命令连接GDB到内核: $ gdb vmlinuz 然后,需要设置GDB的默认连接地址: add-symbol-file /path/to/kernel/vmlinux 0xADDRESS 这里的ADDRESS 应该是编译内核时已知的地址。可以在内核映像文件的/System.map文件中找到地址信息。 4.使用GDB调试 在连接GDB到内核后,可以使用GDB来单步执行内核代码或设置断点。可以使用GDB的“step”命令来进入下一个函数调用,并使用“next”命令来执行下一行代码。还可以使用“break”命令设置断点,以捕获特定的行动或事件。 也可以在调试内核时使用一些GDB调试命令,例如:“watch”命令用于设置监视点,以监视变量的值,而不必停止调试器。可以使用“info” 命令,以获取关于调试目标状态的信息。 总之,调试Linux内核需要仔细的计划和准备。在内核源代码中随时插入代码并编译并不是一个高效的方法。使用GDB可以更轻松地捕获和解决内核的问题。通过使用GDB,可以提高软件开发的效率,并确保Linux内核的稳定性。