gdb中使用python脚本

1、入门案例

首先有1个a.cpp,代码如下:

#include <map>
#include <set>
#include <iostream>
#include <string>

using namespace std;

struct MyStruct {
    std::string mName;
    std::map<int, std::string> mField1;
    std::set<std::string> mField2;
    int mI;
    int mJ;
};

int main() {
    std::map<int,string> lm;
    std::set<std::string> ls;
    MyStruct s = {std::string("student"), lm, ls, 3, 4};
    return 0;
}

上面的C代码中有一个结构体MyStruct,如果想要打印结构体的内容。在GDB 9.X版本中打印出来是这样

$1 = {mName = "student", mField1 = std::map with 0 elements, mField2 = std::set with 0 elements, mI = 3, mJ = 4}

如果使用的是GDB 7.X,打印出来就是这样

$1 = {mName = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fffffffe380 "student"},
    _M_string_length = 7, {_M_local_buf = "student\000\000\000\000\000\000\000\000", _M_allocated_capacity = 32772479054607475}}, mField1 = {_M_t = {
      _M_impl = {<std::allocator<std::_Rb_tree_node<std::pair<int const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<int const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<No data fields>}, <No data fields>}, <std::_Rb_tree_key_compare<std::less<int> >> = {
          _M_key_compare = {<std::binary_function<int, int, bool>> = {<No data fields>}, <No data fields>}}, <std::_Rb_tree_header> = {_M_header = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffe398,
            _M_right = 0x7fffffffe398}, _M_node_count = 0}, <No data fields>}}}, mField2 = {_M_t = {
      _M_impl = {<std::allocator<std::_Rb_tree_node<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {<No data fields>}, <No data fields>}, <std::_Rb_tree_key_compare<std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {
          _M_key_compare = {<std::binary_function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool>> = {<No data fields>}, <No data fields>}}, <std::_Rb_tree_header> = {_M_header = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffe3c8, _M_right = 0x7fffffffe3c8}, _M_node_count = 0}, <No data fields>}}},
  mI = 3, mJ = 4}

可以看到在GDB 9.X中打印出来的要美观很多,主要是由于GDB 9.x自带了一系列标准库的Python Pretty Printer(美观打印器)。如果想要查看原始的数据,可以使用 p /r s命令。

当然我们也可以实现字节的美观打印器。主要需要完成下面几个功能:

(1)自定义打印类,提供to_string()方法,该方法返回希望打印出来的字符串。

(2)创建判断函数,判断一个值是否需要使用自定义的打印类来打印。

(3)将判断函数注册到GDB美观打印函数中。

下面直接看完整的代码,t.py

class MyPrinter:
    def __init__(self, val):
        self.val = val

    def to_string(self):
        return "name: {} integer: {}".format(self.val['mName'], self.val['mI'])

def lookup_pretty_printer(val):
    if val.type.code == gdb.TYPE_CODE_PTR:
        return None
    if 'MyStruct' == val.type.tag:
        return MyPrinter(val)
    return None
gdb.printing.register_pretty_printer(gdb.current_objfile(),lookup_pretty_printer,replace=True)

简单解释下上面的python代码。

1、定义了一个名为 MyPrinter 的类,它有一个构造函数,接收一个 val 参数。参数就是p s中的s, to_string 方法定义了如何将 MyStruct 对象转换为字符串,例子中包含 mNamemI 字段的值。

2、lookup_pretty_printer 这个函数用来决定是否使用 MyPrinter 来打印某个值。如果传入的 val 是指针类型,则直接返回 None,表明不需要特别处理。如果 val 的类型标签是 MyStruct,则返回一个新的 MyPrinter 实例,否则也返回 None

3、最后一行,gdb.printing 是 GDB 的一个 Python 接口模块,它提供了一系列与 pretty printing 相关的功能。Pretty printing 是一种用于美化或格式化输出的技术,尤其是在处理复杂数据结构时,能够使输出更加清晰易读。gdb.printing 模块提供了以下功能:

  • 注册 pretty printers

  • 管理 pretty printer 集合

  • 提供查找 pretty printer 的方法

    gdb.printing.register_pretty_printergdb.printing 模块中的一个函数,用于注册一个 pretty printer。当 GDB 在打印某个变量或表达式时,它会调用已注册的 pretty printer 来美化输出。这个函数允许用户为特定类型的数据结构定制输出格式,使得输出更加直观和有用。

    三个参数分别表示:

    • objfile: gdb.ObjfileNone,表示要为其注册 pretty printer 的目标文件。如果是 None,则表示注册的 pretty printer 对所有目标文件有效。
    • pretty_printer: 函数,接受一个 gdb.Value 对象并返回一个 pretty printer 对象或 None。这个函数负责决定是否使用自定义的 pretty printer。
    • replace: 布尔值,默认为 False,表示是否替换现有的 pretty printer。如果为 True,则新注册的 pretty printer 会覆盖先前为相同类型注册的所有 pretty printer。

使用步骤:

  • 使用g++ -g a.cpp -o a将代码编译成可调试的可执行程序。

  • 将上面的t.py和上面编译出来的a放到同一目录。

  • 使用gdb a使用调试状态执行a

  • 在gdb命令行加载t.py,使用source t.py

  • 添加断点b 22,在a.cpp第22行添加断点。

  • 使用r命令运行程序,会在代码22行的位置停下来;使用p s打印变量s的值,就能看到打印格式就是我们t.pyMyPrinter.to_string指定的输出格式。

    在这里插入图片描述

2、在GDB窗口定义python脚本

除了可以上面的在外部定义python脚本,也可以直接在gdb调试窗口中定义函数。

步骤如下:

  • 在gdb 窗口输入python进入python脚本编辑模式

  • 输入python脚本内容,完成后输入end退回到gdb模式下

  • 使用python前缀调用python脚本中的内容

    (gdb) python
    >def my_function(arg):
    >    print(f"Argument received: {arg}")
    >end
    (gdb) python my_function(100)
    Argument received: 100
    
    

3、使用python控制gdb流程。

比如我们需要监控某个函数中某个变量的值,比如循环中执行多次,如果我们每次在gdb中手动输入命令,那就太繁琐了,我们可以使用python来做到这些。

比如有个c程序b.c

#include <stdio.h>

int sum=0;
void print_message(const int i) {
    sum+=i;
}

int main() {
    for(int i=0; i<100; i++){
        print_message(i);
    }
    printf("sum = %d\n", sum);
    return 0;
}

想要打印每次print_message函数执行时,变量i的值,也可以通过python脚本来实现。

下面是python脚本的内容c1.py

import gdb

class PrintVariable(gdb.Breakpoint):
    def __init__(self, function, variable):
        super(PrintVariable, self).__init__(function, gdb.BP_BREAKPOINT)
        self.variable = variable

    def stop(self):
        frame = gdb.selected_frame()
        if frame:
            try:
                value = frame.read_var(self.variable)
                print(f"Variable '{self.variable}' has value: {value}")
            except gdb.error as e:
                print(f"Error reading variable '{self.variable}': {e}")

# 禁用分页
gdb.execute("set pagination off")

# 要执行的程序   file 是关键字,后文件路径
gdb.execute("file /root/c_debug/b")


# 替换这里的'function_name'和'variable_name'为你的函数名(或者代码行号)和变量名
function_name = "5"
variable_name = "i"

# 创建一个断点在函数f的开始处
bp = PrintVariable(function_name, variable_name)


# 设置断点命令
bp.commands = "print " + variable_name


# 运行程序
gdb.execute("run")

脚本的内容都有注释,也就不再说什么了 。

下面看看怎么执行:

  • 将上面的b.c编译成可调试的可执行程序。

gcc -g b.c -o b,我本地是放在/root/c_debug/b,也是上面脚本中的路径

  • 使用gdb命令进入gdb窗口,执行source c1.py,由于我们是在代码第5行设置断点,就可以看到就会自动执行上面我们编译的b,同时每次打印变量i的值。
root@wbo112:~/c_debug# gdb
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.2) 9.2
Copyright (C) 2020 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 "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) source c1.py
Breakpoint 1 at 0x1154: file b.c, line 5.
Variable 'i' has value: 0
Variable 'i' has value: 1
Variable 'i' has value: 2
Variable 'i' has value: 3
Variable 'i' has value: 4
Variable 'i' has value: 5
Variable 'i' has value: 6
Variable 'i' has value: 7
Variable 'i' has value: 8
Variable 'i' has value: 9
Variable 'i' has value: 10
Variable 'i' has value: 11
Variable 'i' has value: 12
Variable 'i' has value: 13
Variable 'i' has value: 14
Variable 'i' has value: 15
Variable 'i' has value: 16
Variable 'i' has value: 17
Variable 'i' has value: 18
Variable 'i' has value: 19
Variable 'i' has value: 20
Variable 'i' has value: 21
Variable 'i' has value: 22
Variable 'i' has value: 23
Variable 'i' has value: 24
Variable 'i' has value: 25
Variable 'i' has value: 26
Variable 'i' has value: 27
Variable 'i' has value: 28
Variable 'i' has value: 29
Variable 'i' has value: 30
Variable 'i' has value: 31
Variable 'i' has value: 32
Variable 'i' has value: 33
Variable 'i' has value: 34
Variable 'i' has value: 35
Variable 'i' has value: 36
Variable 'i' has value: 37
Variable 'i' has value: 38
Variable 'i' has value: 39
Variable 'i' has value: 40
Variable 'i' has value: 41
Variable 'i' has value: 42
Variable 'i' has value: 43
Variable 'i' has value: 44
Variable 'i' has value: 45
Variable 'i' has value: 46
Variable 'i' has value: 47
Variable 'i' has value: 48
Variable 'i' has value: 49
Variable 'i' has value: 50
Variable 'i' has value: 51
Variable 'i' has value: 52
Variable 'i' has value: 53
Variable 'i' has value: 54
Variable 'i' has value: 55
Variable 'i' has value: 56
Variable 'i' has value: 57
Variable 'i' has value: 58
Variable 'i' has value: 59
Variable 'i' has value: 60
Variable 'i' has value: 61
Variable 'i' has value: 62
Variable 'i' has value: 63
Variable 'i' has value: 64
Variable 'i' has value: 65
Variable 'i' has value: 66
Variable 'i' has value: 67
Variable 'i' has value: 68
Variable 'i' has value: 69
Variable 'i' has value: 70
Variable 'i' has value: 71
Variable 'i' has value: 72
Variable 'i' has value: 73
Variable 'i' has value: 74
Variable 'i' has value: 75
Variable 'i' has value: 76
Variable 'i' has value: 77
Variable 'i' has value: 78
Variable 'i' has value: 79
Variable 'i' has value: 80
Variable 'i' has value: 81
Variable 'i' has value: 82
Variable 'i' has value: 83
Variable 'i' has value: 84
Variable 'i' has value: 85
Variable 'i' has value: 86
Variable 'i' has value: 87
Variable 'i' has value: 88
Variable 'i' has value: 89
Variable 'i' has value: 90
Variable 'i' has value: 91
Variable 'i' has value: 92
Variable 'i' has value: 93
Variable 'i' has value: 94
Variable 'i' has value: 95
Variable 'i' has value: 96
Variable 'i' has value: 97
Variable 'i' has value: 98
Variable 'i' has value: 99
sum = 4950
[Inferior 1 (process 78220) exited normally]
(gdb) q

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值