我可以在gdb美化打印程序中直接调用`operator[]`吗?
我正在尝试使用GDB的美化打印功能来显示一个自定义的C++矩阵类。
这个类很常见,你在任何地方都能找到。它是一个模板类,可以根据类型来使用,并且可以用C语言的方式访问,比如用mat[i][j]。这个表达式首先会隐式返回另一个模板类“Slice”,它代表一行或一列的数据,我们可以再次用[]操作符来提取数据。这个类本身使用的是普通的C数组来存储数据,但它实现了一些技巧,比如可以预先分配一个更大的矩阵、支持非零起始点、使用步幅等。这个类没有自带的打印接口,我也不能轻易修改它或链接自己的代码。
这些自定义的特性让用Python重现直接访问数据的代码变得很麻烦。但这真的有必要吗?一般来说:为什么美化打印要重现访问数据的逻辑呢?我不能直接用C++的调用和[]操作符来打印第i,j个元素吗?在GDB中,Slice类在这种请求时是一个临时对象,这让事情变得更加复杂。
我对Python和GDB脚本也还是个初学者。我尝试修改示例代码,把数据访问替换成gdb.execute调用,但我不知道如何从to_string函数中获取对象名称,这样我才能用类似gdb.execute(??? + '[]+str(i)+']', False, True)的方式。
我想知道最有效的做法是什么。
3 个回答
我不知道有没有办法在 to_string
函数中获取变量的名字,但你可以获取这个值的地址(如果它是可以被寻址的),同时你也可以知道它的类型。(想了解更多,可以查看这份文档。)
有了这个地址,你可以把它转换成指向特定类型的指针,然后访问它。例如,我用下面的代码在参数上调用了 toStyledString().c_str()
:
eval_string = f"(({self.val.type.name}*)self.val.address})->toStyledString().c_str()"
return gdb.parse_and_eval(eval_string).string()
在你的情况下,你可能想要类似下面的东西:
eval_string = f"(*(({self.val.type.name}*)self.val.address}))[{i}]"
这不是Python脚本,而是一些简单的GDB扩展命令序列。我正在定义一个新的命令,叫做print_matrix
。
(gdb) define print_matrix
Type commands for definition of "print_matrix".
End with a line saying just "end".
>set $s_arr = $arg0
>set $i=0
>while($i < $arg1)
>p (int [][$arg2]) *($s_arr + $i)
>set $i = $i + 1
>end
>end
(gdb) print_matrix arr 10 10
$90 = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}
$91 = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}
$92 = {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}
$93 = {{3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}
$94 = {{4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}
$95 = {{5, 6, 7, 8, 9, 10, 11, 12, 13, 14}}
$96 = {{6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}
$97 = {{7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}
$98 = {{8, 9, 10, 11, 12, 13, 14, 15, 16, 17}}
$99 = {{9, 10, 11, 12, 13, 14, 15, 16, 17, 18}}
(gdb)
你也可以把这些命令保存成一个脚本,然后使用-x选项来运行它。
gdb -x <commands file name> binary.out
我能不能直接用C++的调用和[]运算符来打印第i,j个元素呢?
你可以通过gdb.parse_and_eval
从美化打印工具调用正在调试的程序(也就是被调试的进程),具体可以参考这个文档,不过这样做有几个缺点:
- 你需要一个“活着”的进程来执行这个操作(换句话说,当你在调试一个崩溃的程序时,这个美化打印工具是无法工作的)
- 如果被调试的程序有问题,调用它里面的函数可能会让问题变得更严重
- 如果被调试的程序是多线程的,而美化打印工具调用了一个可能需要锁的函数(比如
malloc
),那么很有可能会导致美化打印工具出现死锁,这种情况下是无法恢复的。