码迷,mamicode.com
首页 > 数据库 > 详细

用gdb调C++标准库

时间:2015-03-17 00:57:53      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:

我一直都是在Linux下做开发的,但是我对GDB的使用并不多。因为平都是用QtCreator调试程序的。因为工作的原因,以后可能不能再依赖QtCreator了。于是我好好研究一下~

之前为什么没有深入使用GDB,QtCreator带来一定的便利是一方面,另一方面是觉得GDB遇到了vector, map, set, list就没办法看数据了。

今天我研究了一下,其实也是Easy的。

示例代码:

#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;

int main()
{
    int i1 = 32;
    int i2 = 45; 
    double d = i1 + i2 / 3;

    vector<string> vstr;
    vstr.push_back("Hello");
    vstr.push_back("World");
    vstr.push_back("!");

    map<string, int> m_si;
    m_si["A"] = 12;
    m_si["D"] = 93;
    m_si["B"] = 77;

    return 0;
}

编译的时候:

$ g++ -o test-gdb test-gdb.cpp -g

在GDB调试中,可以用print指令查看变量或常量的值。

可能是我本机所安装的GDB版本较高的缘故,本机的GDB本谢就很支持STL中的容器。如下是我GDB的版本信息:

$ gdb --version
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.

对STL容器的支持是极好的:

(gdb) p vstr
$1 = std::vector of length 3, capacity 4 = {"Hello", "World", "!"}

(gdb) p m_si
$2 = std::map with 3 elements = {
  ["A"] = 12,
  ["B"] = 77,
  ["D"] = 93
}

能看到这样的信息其实已经很不错了。

但是我公司系统的GDB可没这么智能。打印一下vector,就会蹦出好大堆信息。如果是map的话,那就更吓人了。

<此处展示可怕的提示信息>

其实,在这大堆信息里面只有小部分是我们关注的。GDB很灵活,我们可以在里面自定义函数。我可以在GDB里定义一个函数从vector里提取重要的信息进行显示。

这里 下载一个stl-views.gdb文件。在这个文里定义了很多绪如:pvector, pmap, pstring之类的GDB函数供我们使用。

将这个文件下载到HOME目录,然后:

$ cat stl-views-1.0.3.gdb >> .gdbinit

这样,每次启动 gdb的时候,都会自动加载 ~/.gdbinit 文件中的内容。

13        vector<string> vstr;
(gdb) n
14        vstr.push_back("Hello");
(gdb) n
15        vstr.push_back("World");
(gdb) n
16        vstr.push_back("!");
(gdb) p<TAB><TAB>
passcount     plist_member  print         pstring       python
path          pmap          print-object  ptype         
pbitset       pmap_member   printf        pvector       
pdequeue      ppqueue       pset          pwd           
plist         pqueue        pstack        pwstring

输入了p之后,每次连续按两次TAB键都会列出以p开头的命令。这里我们看到了:pstring, pvector, pwstring, pstack, pmap, pmap_member等等。

我们用一下pvector来查看vstr中的内容:

(gdb) pvector vstr
elem[0]: $3 = "Hello"
elem[1]: $4 = "World"
elem[2]: $5 = "!"
Vector size = 3
Vector capacity = 4
Element type = std::basic_string<char, std::char_traits<char>, std::allocator<char> > *

这个命令果然打印出了很多有价值的信息。

博主有个习惯,我不仅要知其然,我还要知其所以然。于是我研究了一下 .gdbinit文件里的内容。这个函数都是在 ~/.gdbinit 里定义的。我们打开这个文件看一下。

define pvector
    if $argc == 0
        help pvector
    else
        set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start
        set $capacity = $arg0._M_impl._M_end_of_storage - $arg0._M_impl._M_start
        set $size_max = $size - 1
    end
    if $argc == 1
        set $i = 0
        while $i < $size
            printf "elem[%u]: ", $i
            p *($arg0._M_impl._M_start + $i)
            set $i++
        end
    end
    if $argc == 2
        set $idx = $arg1
        if $idx < 0 || $idx > $size_max
            printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
        else
            printf "elem[%u]: ", $idx
            p *($arg0._M_impl._M_start + $idx)
        end
    end
    if $argc == 3
      set $start_idx = $arg1
      set $stop_idx = $arg2
      if $start_idx > $stop_idx
        set $tmp_idx = $start_idx
        set $start_idx = $stop_idx
        set $stop_idx = $tmp_idx
      end
      if $start_idx < 0 || $stop_idx < 0 || $start_idx > $size_max || $stop_idx > $size_max
        printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
      else
        set $i = $start_idx
        while $i <= $stop_idx
            printf "elem[%u]: ", $i
            p *($arg0._M_impl._M_start + $i)
            set $i++
        end
      end
    end
    if $argc > 0
        printf "Vector size = %u\n", $size
        printf "Vector capacity = %u\n", $capacity
        printf "Element "
        whatis $arg0._M_impl._M_start
    end
end

document pvector
    Prints std::vector<T> information.
    Syntax: pvector <vector> <idx1> <idx2>
    Note: idx, idx1 and idx2 must be in acceptable range [0..<vector>.size()-1].
    Examples:
    pvector v - Prints vector content, size, capacity and T typedef
    pvector v 0 - Prints element[idx] from vector
    pvector v 1 2 - Prints elements in range [idx1..idx2] from vector
end

看全部有点多,我们暂且不看多个参数的那部分,我们分析一下只有一个参数的一部分:

define pvector
    if $argc == 0           # 如果没有带参数,那么就打印帮助提示信息
        help pvector
    else                    # 如果有参数,那么接下来准备一下size, capacity, size_max 这三个重要的参数。
        set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start        # arg0 就是第一个参数,也就是vstr数组对象。注重 size 是怎么计算的。
        set $capacity = $arg0._M_impl._M_end_of_storage - $arg0._M_impl._M_start
        set $size_max = $size - 1
    end
    if $argc == 1           # 如果只有一个参数,说明要求打印出vector中所有的元素
        set $i = 0          
        while $i < $size    # 用一个 while 循环,用printf与p,打印出列表中的所有元素
            printf "elem[%u]: ", $i
            p *($arg0._M_impl._M_start + $i)     # 注意看哦!!!!
            set $i++
        end
    end

所有的说明都写在上面的注释中了,自己去悟吧!

如果print命令能像C++里的模板函数那可以对特定类型进行“偏特化”就好了。上面是有个问题的:

在“p *($arg0._M_impl._M_start + $i)”这行命令中,如果vector的成员还是个STL的容器该怎么办?这是个问题~

用gdb调C++标准库

标签:

原文地址:http://my.oschina.net/hevakelcj/blog/387707

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!