【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

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()

.gdbinit 文件中导入 python 文件

layout src
# 导入 python 文件
source test.py
...
...

这样 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

示例

C 代码

#include <stdio.h>

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;
}

.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

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()

效果

在这里插入图片描述

参考

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

猜你喜欢

转载自blog.csdn.net/tyustli/article/details/133420265
GDB