GDB配置(打印STL容器、VS code配置、远程调试debug)

来源:互联网 发布:淘宝用的什么web服务器 编辑:程序博客网 时间:2024/06/07 21:55

  • 前言
  • gdb定义打印函数
  • 配置pretty printer
  • gdb gdbserver远程调试
  • gdb其他配置


0.前言

  Linux开发必须要把gdb玩的666呀,建议先阅读下《C++ vector STL实现详解》。gdb玩的666可以摆脱调试中各种使用std::cout打印输出变量的恶习,尤其对于大型项目,每次调试都需要增加std::cout,单单编译时长都能让你发狂,严重影响开发效率。

1.gdb定义打印函数

  gdb原生支持定义打印函数,了解类型后,可以自行定制打印函数。目前网上有提供针对stl标准容器的现成文件.gdbinit,该文件会定义string、vector、map、set、deque、priority_queue等等,需要可以直接下载这里, 把文件保存为.gdbinit, 放在用户目录下~/.gdbinit,每次执行gdb调试会自动加载.gdbinit初始文件。

文件中vector定义如下

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    endend

  现在打印vector就很简单了,比如 :pvector vvector变量), 上述定义支持:

pvector v        # 打印所有内容pvector v 0      # 打印位置为0的内容pvector v 1 2    # 打印[1, 2]的内容

2.配置pretty printer

  上述的配置无法支持unorder map等C++11新增的容器,除非自己定义新的打印函数,不过也可以通过配置pretty printer来解决,默认安装gcc高版本(>4.6.0)情况下会自带pretty printer,本文基于gcc-6自带的pretty printer进行配置,否则可以通过svn来下载最新版本: svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python,注意:libstdc++版本要与gcc版本对齐,否则不会正常工作。pretty printer也支持用户的自定制,具体例子点击此处,实现变量具体打印类,已经gdb类型名字到打印类的映射。下面给出.gdbinitpretty printer配置内容:

.gdbinit增加配置

pythonimport syssys.path.insert(0, '/usr/local/Cellar/gcc/6.4.0/share/gcc-6.4.0/python')from libstdcxx.v6.printers import register_libstdcxx_printersregister_libstdcxx_printers (None)end

  其中需要在自己目录下找到libstdcxx对应的路径,填写在sys.path.insert变量里,如果不使用pretty printer, 可以使用/r参数原生打印。

p /r xxx

  注意,在Mac系统中,如果使用gdb版本很高,笔者gdb版本为8.0.1,生成调试程序必须带有-gdwarf-3参数,只有使用该参数,pretty printer才能生效,调试信息兼容到高版本。Mac 中高版本gdb调试gcc高版本编译程序,需要编译使用-gdwarf-3。更具体的解释是:gcc版本大于4.8,缺省生成dwarf4版本的调试信息,pretty printer基于dwarf3版本,因此gdb中pretty printer可能无法识别相应类型符号,需要通过选项-gdwarf-3来生成低版本调试信息,简单编译例子:

g++-6 -g -gdwarf-3 -O0 -o test test.cpp

  gdb可以装备VS Code:正常情况下VS Code调试控制台能够直接使用,比如打印map<string, string> cfg_; 需要在控制台输入:

-exec p cfg_

  VS Code调试控制台运行gdb命令都需要加上-exec,VS Code打印与原生gdb打印一致,如下图所示:


这里写图片描述

  要想在debug过程中,鼠标停靠在变量上,打印变量使用pretty print时,需要在debug配置文件launch.json加入,github官方论坛上解释:VS Code为了追求性能,鼠标停靠变量上、监控区变量时会关闭pretty print,通过配置launch.json可以强行打开,配置如下:

"setupCommands": [{    "description": "Enable pretty-printing for gdb",    "text": "-enable-pretty-printing",    "ignoreFailures": true}],

  笔者launch.json配置为

{    "version": "0.2.0",    "configurations": [        {            "name": "gdb",            "type": "cppdbg",            "request": "launch",            "program": "${workspaceRoot}/xgboost",            // "args": ["${workspaceRoot}/demo/regression/machine.conf"],            "args": ["${workspaceRoot}/demo/binary_classification/mushroom.conf"],            "stopAtEntry": false,            "cwd": "${workspaceRoot}",            "environment": [],            "externalConsole": true,            "preLaunchTask": "make",   // 代码有修改,先执行make编译任务            "setupCommands": [{        // 鼠标悬挂查看变量值,使用pretty print配置                "description": "Enable pretty-printing for gdb",                "text": "-enable-pretty-printing",                "ignoreFailures": true            }],            // VS Code debug的日志信息,能清楚gdb加载和运行的各种过程,如果没有其他用途可以注释            "logging": {                "moduleLoad": false,                "trace": true,                "engineLogging": true,                "exceptions": true                // "traceResponse": true             },            "MIMode": "gdb"        }    ]}

  效果如下图所示,可见代码变量与监控区的变量显示都是pretty print形式。


这里写图片描述

3.gdb + gdbserver远程调试

  实际工作中,公司内部库都是基于linux,需要在linux环境下编译程序,经常想快速了解公司内大神们写的模块源码,最好的方式就是debug整个流程。然而对于笔者来说,gdb用的还不是非常6,有时还需要查看文本源码,目前来说调试gdb不太友好,笔者还是喜欢基于IDE的debug调试过程。而公司开发机子系统为Mac,而Mac系统编译不了代码,为了方便debug,需要gdb远程调试功能,也就是说可以支持不同操作系统下的debug。gdb远程调试模式是本地gdb+远程gdbserver形式,这种形式也经常用于嵌入式的调试。
  首先,安装gdb必须使用--with-all-targets参数,因为默认安装是基于机子操作系统的结构体系,而远程调试的机子不一定与本机相同,使用该参数主要是适配远程各种平台的结构体系,当然可以下载gdb源码,修改配置中结构体系配置编译安装,这部分读者自行google,同时远程需要安装gdbserver

# 本地主机安装gdbbrew install gdb --with-all-targets  # 远程主机下安装gdb、gdbserverapt-get install gdbapt-get install gdbserver

  远程主机编译程序,运行gdbserver服务监听特定端口:

g++ -g -O0 -o test test.cppgdbserver localhost:9999 test  # localhost也可以改成远程主机IP:10.101.8.8# killall gdbserver  # kill进程

  本地主机执行gdb,在gdb命令行中输入:

target remote 10.101.8.8:9999 # 连接远程symbol-file test    # 加入符号文件,可执行文件包含符号文件,test为远程主机编译,通过scp来拷贝到本机continue            # 执行调试过程,不是run,因为gdbserver已经启动程序了,后续就可以使用gdb过程。

  特殊说明,symbol-file给定符号文件即可,可以使用objcopy对可执行文件进行符号文件剥离:

objcopy --only-keep-debug test test.symbol # 对应objcopy --strip-debug test test.bin  只剥离出可执行文件# gdb 程序内加载symbol文件symbol-file test.symbol  # 使用符号文件即可

  scp文件优化:远程debug需要拷贝远程编译文件或者符号文件,对于大型程序100MB+而言,除非网快,否则需要较长等待时间。优化scp拷贝回来的符号文件大小,符号文件剥离本身是个优化,但对于-O0 -g生成某大型程序150MB,笔者发现剥离出的符号文件138MB,几乎占据整个可执行文件,约90%。使用-O2高度优化的是可执行文件大小,符号文件并不减少,而且高度优化会使用分支预测,局部变量优化,函数内联等优化技术而造成代码运行逻辑不一致,尽管运行结果一致,不利于人的debug阅读逻辑。g++编译生成debug信息是可配置的,-g后面可以加入level等级,默认是2,level越高信息越全,最低level是1,能减少30%生成的符号文件大小,但是使用该符号文件无法调试,部分变量symbol会被删除。最后笔者只能通过压缩语句来减少文件大小的传输,能优化掉2/3的大小,这样一比较,也没太多必要剥离出符号文件。

tar -jcvf test.tar testscp user@remote_host:xxx/test.tar .tar -jxvf test.tar

  VS Code也支持远程gdb调试,需要配置miDebuggerServerAddress"miDebuggerServerAddress": "10.101.8.8:9999", 同时必须给定可执行文件"program": "${workspaceRoot}/test", 但是该文件可以拷贝于远程,而不需要本地编译,这样就可以基于VS Code边调试,边浏览代码,同时可以设置断点。

{    "version": "0.2.0",    "configurations": [        {            "name":"gdb Launch",            "type": "cppdbg",            "request": "launch",            "program": "${workspaceRoot}/test",            "miDebuggerServerAddress": "10.101.8.8:9999",            "setupCommands": [{                "description": "Enable pretty-printing for gdb",                "text": "-enable-pretty-printing",                "ignoreFailures": true            },            {                "text": "set sysroot"    //不加载远程so文件,不调试动态链接库,跳过read xxx.so from remote target,能减少每次调试准备时间            }            ],            "args": [],            "stopAtEntry": false,            "cwd": "${workspaceRoot}",            "environment": [],            "externalConsole": true,            "MIMode": "gdb"        }    ]}

4.gdb其他配置

  这部分内容会持续更新,笔者会加入gdb实用配置。

1) gdb命令历史保存

set history filename ~/.gdb_historyset history save on

2) 断点保存与恢复

define bsave    shell rm -f brestore.txt    set logging file brestore.txt    set logging on    info break    set logging off    # reformat on-the-fly to a valid gdb command file    shell perl -n -e 'print "break $1\n" if /^\d+.+?(\S+)$/g' brestore.txt > brestore.gdbenddefine brestore  source brestore.gdbend

  每次退出前使用bsave命令,每次进入gdb想恢复上一次设置的gdb设置断点,可以输入brestore

3) mac系统gdb额外设置

set startup-with-shell off
原创粉丝点击