用gdb调C++标准库

简介:

我一直都是在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入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
3月前
|
消息中间件 存储 开发工具
消息队列 MQ产品使用合集之C++如何使用Paho MQTT库进行连接、发布和订阅消息
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
6天前
|
存储 计算机视觉 C++
在C++中实现Armadillo库与OpenCV库之间的数据格式转换
在C++中实现Armadillo库与OpenCV库之间的数据格式转换是一项常见且实用的技能。上述步骤提供了一种标准的方法来进行这种转换,可以帮助开发者在两个库之间高效地转移和处理数据。虽然转换过程相对直接,但开发者应留意数据类型匹配和性能优化等关键细节。
23 11
|
7天前
|
存储 计算机视觉 C++
在C++中实现Armadillo库与OpenCV库之间的数据格式转换
在C++中实现Armadillo库与OpenCV库之间的数据格式转换是一项常见且实用的技能。上述步骤提供了一种标准的方法来进行这种转换,可以帮助开发者在两个库之间高效地转移和处理数据。虽然转换过程相对直接,但开发者应留意数据类型匹配和性能优化等关键细节。
14 3
|
18天前
|
C++
fedora安装静态c和c++库
fedora安装静态c和c++库
|
1月前
|
算法 数据可视化 机器人
Pinocchio - 开源多刚体动力学 C++、Python库
Pinocchio - 开源多刚体动力学 C++、Python库
48 2
|
19天前
|
C++
C/C++静态链接pthread库的坑【-static -pthread】
C/C++静态链接pthread库的坑【-static -pthread】
|
26天前
|
C++
C++标准库探索
C++标准库探索
32 0
|
2月前
|
C++
C++一分钟之-文件系统库(fs)的使用
【7月更文挑战第18天】C++17的`&lt;filesystem&gt;`库简化了文件系统操作,包括`path`类和`directory_iterator`。`path`用于表示路径,`directory_iterator`用于遍历目录。常用功能有路径拼接、分解,创建/删除目录,以及遍历。错误处理、跨平台兼容性和性能是使用时需考虑的关键点。示例代码展示了如何初始化`path`、创建目录、删除目录以及处理异常。
66 1
|
1月前
|
编译器 C++ 开发者
Visual Studio属性表:在新项目中加入已配置好的C++库
通过以上步骤可以确保Visual Studio中新项目成功地加入了之前已配置好的C++库。这个过程帮助开发者有效地管理多个项目中共享的库文件,提升开发效率。
45 0
|
2月前
|
存储 安全 Linux
网络请求的高效处理:C++ libmicrohttpd库详解
网络请求的高效处理:C++ libmicrohttpd库详解