Linux的C:容易&'漂亮'转储/结构的打印输出(如gdb) – 从源代码?

我正在构build一个内核模块中的一些结构,所以我认为如果有一个简单的方法可以打印出结构和它们的值,那么结果会很好 – 下面是我的一个小用户区示例。

假设我们有如下简单的C示例(以bash命令的forms给出):

FN=mtest cat > $FN.c <<EOF #include <stdio.h> //printf #include <stdlib.h> //calloc struct person { int age; int height; }; static struct person *johndoe; main () { johndoe = (struct person *)calloc(1, sizeof(struct person)); johndoe->age = 6; asm("int3"); //breakpoint for gdb printf("Hello World - age: %d\n", johndoe->age); free(johndoe); } EOF gcc -g -O0 $FN.c -o $FN # just a run command for gdb cat > ./gdbcmds <<EOF run EOF gdb --command=./gdbcmds ./$FN 

如果我们运行这个例子,程序将会编译,gdb会运行它,并在断点处自动停止。 在这里我们可以做到以下几点:

 Program received signal SIGTRAP, Trace/breakpoint trap. main () at mtest.c:20 20 printf("Hello World - age: %d\n", johndoe->age); (gdb) p johndoe $1 = (struct person *) 0x804b008 (gdb) p (struct person)*0x804b008 $2 = {age = 6, height = 0} (gdb) c Continuing. Hello World - age: 6 Program exited with code 0300. (gdb) q 

如图所示,在gdb中,我们可以打印(转储?)结构指针johndoe的值为{age = 6, height = 0} …我想这样做,但直接从一个C程序; 比如下面的例子:

 #include <stdio.h> //printf #include <stdlib.h> //calloc #include <whatever.h> //for imaginary printout_struct struct person { int age; int height; }; static struct person *johndoe; static char report[255]; main () { johndoe = (struct person *)calloc(1, sizeof(struct person)); johndoe->age = 6; printout_struct(johndoe, report); //imaginary command printf("Hello World - age: %d\nreport: %s", johndoe->age, report); free(johndoe); } 

这会导致一个输出如下:

 Hello World - age: 6 $2 = {age = 6, height = 0} 

所以我的问题是 – 像这个虚构的printout_struct函数是否存在 – 还是有另一种方法来打印这样的可能吗?

提前感谢您的帮助,
干杯!

Solutions Collecting From Web of "Linux的C:容易&'漂亮'转储/结构的打印输出(如gdb) – 从源代码?"

只是想说 – 感谢你所有的好的和非常快的答案,帮助我理解了这个问题(为什么C中没有这样的“本地”功能)!

并且很抱歉回答我自己的问题 – 这样做,以免篡改原始文章,并能够格式化代码

进一步看,我设法找到:

  • 在linux中生成一个核心转储 – Stack Overflow
  • 即时调试? – mlist.linux.kernel | Google网上论坛

这个例子说明了用进程本身的pid调用gdb的技巧,所以我修改了在dumpstack找到的dumpstack函数,得到下面的代码:

 FN=mtest cat > $FN.c <<EOF #include <stdio.h> //printf #include <stdlib.h> //calloc, system extern const char *__progname; struct person { int age; int height; }; static struct person *johndoe; static char report[255]; static void printout_struct(void* invar, char* structname){ /* dumpstack(void) Got this routine from http://www.whitefang.com/unix/faq_toc.html ** Section 6.5. Modified to redirect to file to prevent clutter */ /* This needs to be changed... */ char dbx[160]; sprintf(dbx, "echo 'p (struct %s)*%p\n' > gdbcmds", structname, invar ); system(dbx); sprintf(dbx, "echo 'where\ndetach' | gdb -batch --command=gdbcmds %s %d > struct.dump", __progname, getpid() ); system(dbx); sprintf(dbx, "cat struct.dump"); system(dbx); return; } main () { johndoe = (struct person *)calloc(1, sizeof(struct person)); johndoe->age = 6; printout_struct(johndoe, "person"); johndoe->age = 8; printout_struct(johndoe, "person"); printf("Hello World - age: %d\n:", johndoe->age); free(johndoe); } EOF gcc -g -O0 $FN.c -o $FN ./$FN 

基本结束了显示我想要的:

 0x00740422 in __kernel_vsyscall () $1 = {age = 6, height = 0} 0x00740422 in __kernel_vsyscall () $1 = {age = 8, height = 0} Hello World - age: 8 

虽然,我不确定它将与内核模块一起工作…

再次感谢您的帮助,
干杯!

编辑:我不认为这将为内核模块工作的原因是,在这种情况下,我们有一个进程ID的用户区程序; 我们只需从这个程序中调用gdb ,同时指示它关于我们的PID – 所以gdb可以“附加”到我们的进程; 那么,因为gdb也被指示加载带有调试符号的可执行文件(所以它会'知道'结构是什么),并指示给定的结构变量所在的地址,然后gdb可以打印结构。

对于内核模块 – 首先我不认为它们是具有唯一PID的意义上的“进程”,所以gdb将无法附加到! 实际上,有一个内核调试器kgdb ,它实际上可以分解成一个正在运行的内核,并通过模块源代码; 但是,您需要通过串行连接或虚拟机连接第二台机器,请参阅Linux Hacks:使用kvm / qemu设置kgdb 。

所以在任何情况下, gdb似乎都无法检查当前正在运行的主机内核gdb运行的内存 – 但是我会尝试一下,如果实验显示其他情况,那么我一定会发布:)

有关解析结构的信息,请参阅此相关问题 。 特别是我参考pstruct 。

在你的情况下,你想要从正在运行的程序中获取信息,你必须调用其中一个外部工具,或者从你的可执行文件中解析出调试信息并适当地显示它。

你也可以看看libgdb ,虽然看起来可能有些过时。

您必须添加描述结构的meta-info,以便printout_struct可以完成其工作。 否则,它不能猜测任何东西。 尝试使用gdb删除每个调试信息,你会发现它不能“说”“年龄”或其他什么。

C语言没有元数据,无论是编译时间还是运行时间。 可能有一些供应商特定的扩展来执行此操作。 例如, doxygen会生成一个XML文件,其中包含程序中每个结构类型的所有成员信息(名称和类型),编写程序来处理该XML文件并生成printout_person的代码不会太困难(const struct person *)函数。

最近有人提到

繁华的标签

在一个类似的任务stackoverflow。 也许你可以挖掘出来,但是我没有立即发现。