2010-07-22 67 views
10

我有一个内核模块我建立在一些结构一个小问题,所以我认为这将是很好,如果有打印出结构和值的简单方法 - 以下是小用户态例如什么我的意思是。Linux C:轻松&'漂亮'转储/结构的打印输出(如在gdb中) - 从源代码?

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

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是否存在 - 还是有另一种方法来打印这样的可能吗?

在此先感谢您的帮助,
干杯!

回答

12

只是想说 - 谢谢你的所有好的和令人难以置信的快速答案,帮助我了解了很多问题(为什么C中没有这样的“原生”功能)!

和回答我的问题对不起 - 这样做,以免断章取义原岗位,并能格式化代码)虽然看着

进一步我设法找到:

其示出了三CK与调用gdb与过程本身的PID,所以我修改了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: Setting up kgdb using kvm/qemu

因此,在任何情况下,似乎gdb将无法​​检查当前运行的主机内核的内存gdb正在运行 - 但我会尝试尝试,如果实验显示其他情况,我会一定要发布:)

+1

不要道歉回答你自己的问题,这是在这里鼓励。 – dmckee 2010-07-22 20:07:36

+0

SO的自我回答很好;在这种情况下,我会说如果您尝试更新此答案,以便让我们知道即使在内核模块中该解决方案是否正常也会更好。 – ShinTakezou 2010-07-22 20:12:29

+0

好吧 - 谢谢你;我刚刚注意到“你确定要回答你自己的问题”部分,当我试图发布它:)此外,添加了一个为什么我认为内核模块将无法工作的编辑.. – sdaau 2010-07-24 07:43:59

1

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

+0

我的意思是,当然关于结构的元信息必须作为参数给出printout_struct – ShinTakezou 2010-07-22 16:43:26

1

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

+3

许多(大多数?)编译器提供了一个“包含调试符号”选项,它可以精确地生成这样的元数据。在'gcc'的情况下,该选项是'-g'也就是说,在那里'gdb'获取信息来完成这个.. – dmckee 2010-07-22 16:56:33

+0

确实。查看我的答案获取信息的几种可能性。 – bstpierre 2010-07-22 17:04:20

0

最近有人提到

旺盛的ctags

计算器上对类似的任务。也许你可以挖掘出来,但我没有立即发现。

2

对结构解析一些信息见this related question。具体我参考pstruct

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

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