我对Python不是很熟悉,我只是在发现GDB Python脚本功能;我的问题的动机是增强GDB打印MELT monitor中的值,后者稍后将连接到GCC MELT。但这里有一个更简单的变体。在
我的系统是Linux/Debian/Sid/x86-64。GCC编译器是4.8.2;GDB调试器是7.6.2;python是3.3
我想调试一个具有“区分联合”类型的C程序:
// file tiny.c in the public domain by Basile Starynkevitch
// compile with gcc -g3 -Wall -std=c99 tiny.c -o tiny
// debug with gdb tiny
// under gdb: python tiny-gdb.py
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef union my_un myval_t;
enum tag_en {
tag_none,
tag_int,
tag_string,
tag_sequence
};
struct boxint_st;
struct boxstring_st;
struct boxsequence_st;
union my_un {
void* ptr;
enum tag_en *ptag;
struct boxint_st *pint;
struct boxstring_st *pstr;
struct boxsequence_st *pseq;
};
struct boxint_st {
enum tag_en tag; // for tag_int
int ival;
};
struct boxstring_st {
enum tag_en tag; // for tag_string
char strval[]; // a zero-terminated C string
};
struct boxsequence_st {
enum tag_en tag; // for tag_sequence
unsigned slen;
myval_t valtab[]; // of length slen
};
int main (int argc, char **argv) {
printf ("start %s, argc=%d", argv[0], argc);
struct boxint_st *iv42 = malloc (sizeof (struct boxint_st));
iv42->tag = tag_int;
iv42->ival = 42;
struct boxstring_st *istrhello =
malloc (sizeof (struct boxstring_st) + sizeof ("hello") + 1);
istrhello->tag = tag_string;
strcpy (istrhello->strval, "hello");
struct boxsequence_st *iseq3 =
malloc (sizeof (struct boxsequence_st) + 3 * sizeof (myval_t));
iseq3->tag = tag_sequence;
iseq3->slen = 3;
iseq3->valtab[0] = (myval_t)iv42;
iseq3->valtab[1] = (myval_t)istrhello;
iseq3->valtab[2] = (myval_t)NULL;
printf ("before %s:%d gdb print iseq3\n", __FILE__, __LINE__);
}
这是在gdb下要读取的Python文件
^{pr2}$我有以下几个基本问题。在
union my_un
及其typedef的同义词myval_t
。在struct boxsequence_st
?这意味着检测到指针是非nil,然后取消对其ptag
的引用,将该标记与tag_sequence
进行比较,很好地打印valtab
灵活数组成员。在
我没有足够的gdbpythonapi经验来回答这个问题;我认为这只是一位开发人员的研究笔记。我下面所附的代码也相当粗糙和丑陋。但是,这确实适用于gdb-7.4和python-2.7.3。调试运行示例:
以上都是bog标准的打印输出我的理由是我经常想知道指针是什么,所以我不想覆盖那些。但是,引用指针时使用的pretyprinter如下所示:
^{pr2}$最后一行显示,在调试
tiny
时,同一目录中的tiny-gdb.py
会自动加载(尽管您可以禁用此功能,但我相信这是默认行为)。在用于上述操作的
tiny-gdb.py
文件:我选择的理由如下:
如何将漂亮的打印机安装到gdb?在
这个问题有两个部分:在哪里安装Python文件,以及如何将漂亮的打印机挂接到gdb。在
因为漂亮的打印机选择不能仅仅依赖于推断的类型,而是必须窥视实际的数据字段,所以不能使用正则表达式匹配函数。相反,我选择将我自己的漂亮打印机选择器函数
typefilter()
添加到全局漂亮打印机列表中,如in the documentation所述。我没有实现enable/disable功能,因为我相信只加载/不加载相关的Python脚本更容易。在(
typefilter()
每个变量引用调用一次,除非其他漂亮的打印机已经接受它。)文件位置问题更为复杂。对于特定于应用程序的漂亮打印机,将它们放在一个Python脚本文件中听起来很明智,但对于库来说,似乎应该进行一些拆分。文档recommends将函数打包到一个Python模块中,这样一个简单的}是使用的python版本,那么您只需要在gdb中运行
python import module
就可以启动漂亮的打印机了。幸运的是,Python打包非常简单。如果您要import gdb
到顶部并将其保存到/usr/lib/pythonX.Y/tiny.py
,其中{python import tiny
来启用漂亮的打印机。在当然,适当地packaging漂亮的打印机是一个非常好的主意,特别是如果您打算分发它,但是它可以归结为在脚本的开头添加一些变量等,假设您将其作为一个文件保存。对于更复杂的漂亮打印机,使用目录布局可能是个好主意。
如果您有一个值
val
,那么val.type
是描述其类型的gdb.Type对象;将其转换为字符串将生成一个人类可读的类型名。在val.type.strip_typedefs()
生成实际类型,去掉所有typedef。我甚至添加了.unqualified()
,这样所有const/volatile/etc.type限定符都被删除了。空指针检测有点棘手。在
我发现的最好的方法是检查目标的字符串化
.address
成员gdb.价值对象,并查看它是否是"0x0"
。在为了让生活更简单,我编写了一个简单的
deref()
函数,它试图取消对指针的引用。如果目标指向(void*)0,则返回字符串"NULL"
,否则返回目标gdb.价值对象。在我使用} 方法返回。
deref()
的方法是基于这样一个事实,即"array"
类型的漂亮打印机会产生一个2元组的列表,其中第一项是名称字符串,第二项是gdb.价值对象或字符串。这个列表由pretty printer对象的^{如果泛型实体有一个单独的类型,那么处理“区分的联合”类型会容易得多。如果你有
当
tag
值仍然不确定时,它在任何地方都被使用;而特定的结构类型只在其tag
值固定的地方使用。这将允许更简单的类型推断。在在实际上,在
tiny.c
中,struct box*_st
类型可以互换使用。(或者,更具体地说,我们不能仅依赖于基于类型的特定标记值。)sequence case实际上非常简单,因为
valtab[]
可以简单地当作一个空指针数组来处理。sequence标记用于选择正确的联合成员。实际上,如果valtab[]只是一个空指针数组,那么gdb.Value.cast(gdb.lookup_类型())或gdb.Value.reinterpret_cast(gdb.lookup_类型())可用于根据需要更改每个指针类型,就像我对装箱结构类型所做的那样。递归极限?在
您可以在
print
命令中使用@
运算符来指定打印多少个元素,但这对嵌套没有帮助。在如果将
iseq3->valtab[2] = (myval_t)iseq3;
添加到tiny.c
,则得到一个无限递归序列。gdb确实很好地打印了它,尤其是使用set print array
,但它不注意或不关心递归。在我看来,除了为深度嵌套或递归数据结构编写一个漂亮的打印机之外,您可能还希望编写一个gdb命令。在测试期间,我编写了一个命令,它使用Graphviz直接从gdb中绘制二叉树结构;我确信它比纯文本输出要好。在
添加:如果将以下内容另存为
/usr/lib/pythonX.Y/tree.py
:您可以在gdb中使用它:
如果你有
并且您已经安装了X11(本地或远程)连接和Graphviz,您可以使用
查看树结构。因为它保留了一个已经访问过的节点的列表(作为Python集合),所以它不会对递归结构感到厌烦。在
我可能应该在发布之前清理我的Python代码片段,但没关系。请务必考虑这些只是初始测试版本;使用风险自负。:)
相关问题 更多 >
编程相关推荐