• 【GDB】用 python 扩展 gdb


    python 扩展 GDB

    .gdbinit 文件中实现自定义命令 mv 代码如下

    define mv
        if $argc == 2
            delete $arg0
            # 注意新创建的断点编号和被删除断点的编号不同
            break $arg1
        else
            print "输入参数数目不对,help mv 以获得用法"
        end
    end
    
    # (gdb) help mv 会输出以下帮助文档
    document mv
    Move breakpoint.
    Usage: mv old_breakpoint_num new_breakpoint
    Example:
        (gdb) mv 1 binary_search -- move breakpoint 1 to `b binary_search`
    
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    python 实现代码如下

    # move.py
    # 1. 导入 gdb 模块来访问 gdb 提供的 python 接口
    import gdb
    
    
    # 2. 用户自定义命令需要继承自 gdb.Command 类
    class Move(gdb.Command):
    
        # 3. docstring 里面的文本是不是很眼熟?gdb 会提取该类的__doc__属性作为对应命令的文档
        """Move breakpoint
        Usage: mv old_breakpoint_num new_breakpoint
        Example:
            (gdb) mv 1 binary_search -- move breakpoint 1 to `b binary_search`
        """
    
        def __init__(self):
            # 4. 在构造函数中注册该命令的名字
            super(self.__class__, self).__init__("mv", gdb.COMMAND_USER)
    
        # 5. 在 invoke 方法中实现该自定义命令具体的功能
        # args 表示该命令后面所衔接的参数,这里通过 string_to_argv 转换成数组
        def invoke(self, args, from_tty):
            argv = gdb.string_to_argv(args)
            if len(argv) != 2:
                raise gdb.GdbError('输入参数数目不对,help mv 以获得用法')
            # 6. 使用 gdb.execute 来执行具体的命令
            gdb.execute('delete' + argv[0])
            gdb.execute('break' + argv[1])
    
    # 7. 向 gdb 会话注册该自定义命令
    Move()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    .gdbinit 文件中导入 python 文件

    layout src
    # 导入 python 文件
    source test.py
    ...
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样 gdb 每次启动时都会替我们 source 一下

    gdb 的 python 接口

    gdb 通过 gdb 模块提供了不少 python 接口。其中最为常用的是 gdb.executegdb.parse_and_eval

    • gdb.execute 可用于执行一个 gdb 命令。默认情况下,结果会输出到 gdb 界面上。如果想把输出结果转存到字符串中,设置 to_string 为 True:gdb.execute(cmd, to_string=True)。

    • gdb.parse_and_eval 接受一个字符串作为表达式,并以 gdb.Value 的形式返回表达式求值的结果。举例说,gdb 当前上下文中有一个变量 i,i 等于 3。那么 gdb.parse_and_eval(‘i + 1’) 的结果是一个 gdb.Value 的实例,其 value 属性的值为 4。这跟 (gdb) i + 1 是等价的。

    何为 gdb.Value?在 gdb 会话里,我们可以访问 C/C++ 类型的值。当我们通过 python 接口跟这些值打交道时,gdb 会把它们包装成一个 gdb.Value 对象。

    举个例子,struct Point 有 x 跟 y 两个成员。现在假设当前上下文中有一个 Point 类型的变量 point 和指向该变量的 Point 指针 p,就意味着:

    point = gdb.parse_and_eval('point')
    point['x'] # 等价于point.x
    point['y'] # 等价于point.y
    point.referenced_value() # 等价于&point
    
    p = gdb.parse_and_eval('p')
    point2 = p.dereference() # 等价于*p
    point2['x'] # 等价于(*p).x,也即p->x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    示例

    C 代码

    #include 
    
    typedef struct
    {
        int x;
        int y;
    } point_t;
    
    int binary_search(int *ary, unsigned int ceiling, int target)
    {
        unsigned int floor = 0;
        while (ceiling > floor)
        {
            unsigned int pivot = (ceiling + floor) / 2;
            if (ary[pivot] < target)
                floor = pivot + 1;
            else if (ary[pivot] > target)
                ceiling = pivot;
            else
                return pivot;
        }
        return -1;
    }
    
    void set_point_data(point_t *point_in, point_t *p_point_in, int x, int y)
    {
        point_in->x = x;
        point_in->y = y;
    
        return;
    }
    
    int main(int argc, char *argv)
    {
        int a[] = {1, 2, 4, 5, 6};
    
        point_t point1;
        point_t *p_point;
    
        printf("%d\r\n", binary_search(a, 5, 7)); /* -1 */
        printf("%d\r\n", binary_search(a, 5, 6)); /* 4 */
        printf("%d\r\n", binary_search(a, 5, 5)); /* 3 */
    
        p_point = &point1;
        point1.x = 78;
        point1.y = 78;
    
        set_point_data(&point1, p_point, 1, 2);
    
        printf("%d %d\r\n", point1.x, point1.y);
        printf("%d %d\r\n", p_point->x, p_point->y);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    .gdbinit 代码

    layout src
    # 导入 python 文件
    source test.py
    
    b main
    b binary_search if target == 5
    
    b set_point_data
    # set_point_data 触发的命令
    comm
    # 向命令 print_point 传递的参数是 *point_in 和 p_point_in
    print_point *point_in p_point_in
    end
    
    # 断点 1 触发执行的命令
    command 1
    i locals
    i args
    end
    
    # 断点 2 触发执行的命令
    comm 2
    i locals
    i args
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    python 代码

    # 1. 导入gdb模块来访问gdb提供的python接口
    import gdb
    
    # 2. 用户自定义命令需要继承自gdb.Command类
    class Move(gdb.Command):
    
        # 3. docstring里面的文本是不是很眼熟?gdb会提取该类的__doc__属性作为对应命令的文档
        """Move breakpoint
        Usage: mv old_breakpoint_num new_breapkpoint
        Example:
            (gdb) mv 1 binary_search -- move breakpoint 1 to `b binary_search`
        """
    
        def __init__(self):
            # 4. 在构造函数中注册该命令的名字
            super(self.__class__, self).__init__("mv", gdb.COMMAND_USER)
    
        # 5. 在invoke方法中实现该自定义命令具体的功能
        # args表示该命令后面所衔接的参数,这里通过string_to_argv转换成数组
        def invoke(self, args, from_tty):
            argv = gdb.string_to_argv(args)
            if len(argv) != 2:
                raise gdb.GdbError('输入参数数目不对,help mv以获得用法')
            # 6. 使用gdb.execute来执行具体的命令
            gdb.execute('delete ' + argv[0])
            gdb.execute('break ' + argv[1])
    
    
    # 7. 向gdb会话注册该自定义命令
    Move()
    
    class print_point(gdb.Command):
        def __init__(self):
            super(self.__class__, self).__init__("print_point", gdb.COMMAND_USER)
    
        def invoke(self, args, from_tty):
            point = gdb.parse_and_eval('point_in')
            print(point['x']) # 等价于 point.x
            print(point['y']) # 等价于 point.y
            point.referenced_value() # 等价于 &point
    
            p = gdb.parse_and_eval('p_point_in')
            point2 = p.dereference() # 等价于 *p
            print(point2['x']) # 等价于 (*p).x,也即 p->x
            print(point2['y']) # 等价于 (*p).y,也即 p->y
    print_point()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    效果

    在这里插入图片描述

    参考

    https://github.com/kfggww/algorithms-in-c/tree/main
    https://segmentfault.com/a/1190000005718889

  • 相关阅读:
    Spring注解@NonNull作用 Spring注解@Nullable作用 Spring NonNull 注解作用 Spring Nullable注解作用
    day19学习总结
    【毕设教程】单片机RFID模块的使用 - 物联网 嵌入式 毕业设计 stm32
    二叉树Ⅰ · 树型结构 · 二叉树 · 满二叉树 · 完全二叉树 · 二叉树的性质 · 二叉树的存储
    运算符 - Go语言从入门到实战
    2022软考高项十大管理知识领域论文骨架
    使用微PE工具箱制作winU盘启动盘~重装系统
    android compose Canvas 绘制图案居中展示
    SpringSecurity详解,实现自定义登录接口
    spring security(二)--授权
  • 原文地址:https://blog.csdn.net/tyustli/article/details/133420265