代码之家  ›  专栏  ›  技术社区  ›  sdaau

linuxc:Easy&'pretty'结构转储/打印输出(如gdb)-源代码?

  •  12
  • sdaau  · 技术社区  · 14 年前

    我正在构建的内核模块中的一些结构有一个小问题,所以我想如果有一个简单的方法来打印结构和它们的值就好了——下面是一个小的userland示例来说明我的意思。

    假设我们有如下简单的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 
    

    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中,我们可以打印(dump?)结构指针的值 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 存在-或者有没有其他方法来制作这样的打印输出?

    提前谢谢你的帮助,

    6 回复  |  直到 14 年前
        1
  •  15
  •   Community CDub    8 年前

    我只想说-谢谢你所有的好的和令人难以置信的快速的答案,帮助我理解了很多问题(为什么在C中没有这样一个“本机”函数)!

    ( 很抱歉回答了我自己的问题-这样做,以免混淆原来的帖子,并能够格式化代码 )

    再往前看,我发现:

    说明了打电话的诀窍 gdb 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
    


    干杯!

    EDIT:我认为它不适用于内核模块的原因是,在本例中,我们有一个带有进程ID的userland程序;我们只是打电话 gdb公司 从这个程序,同时指示它关于我们的PID-so 可以“附加”到我们的流程中;那么,自从 还指示用调试符号加载可执行文件(这样它将“知道”结构是什么),并指示给定结构变量所在的地址, gdb公司 然后可以打印出结构。

    gdb公司 不会有什么可依附的!实际上,有一个内核调试器, kgdb 它实际上可以进入一个正在运行的内核 模块 源代码;但是,您需要另一台通过串行连接连接的机器,或者一台虚拟机,请参阅 Linux Hacks: Setting up kgdb using kvm/qemu .

    所以,在任何情况下,似乎 gdb公司 无法检查当前运行的主机内核的内存 gdb公司 正在运行-但我将尝试进行实验,如果实验显示不是这样,我一定会发布:)

        2
  •  2
  •   Ben Voigt    14 年前

    doxygen 将生成一个包含程序中每个结构类型的所有成员信息(名称和类型)的XML文件,编写一个程序来处理该XML文件并自动生成打印输出人员(const struct person*)函数的代码不会太困难。

        3
  •  2
  •   Community CDub    8 年前

    看到了吗 this related question 有关解析结构的一些信息。特别是我提到的 pstruct

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

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

        4
  •  1
  •   ShinTakezou    14 年前

    您必须添加描述结构的元信息,以便打印输出结构可以完成它的工作。否则,它什么也猜不到。尝试用gdb删除每一个调试信息,你会发现它不能“谈论”年龄之类的东西。

        5
  •  0
  •   Jens Gustedt    14 年前

    最近有人提到

    旺盛的CTAG

    类似任务的堆栈溢出。也许你可以把它挖出来,不过我没有马上找到。

        6
  •  0
  •   Costa    6 年前

    #define gdb_print(v)
    
    gdb_print(huge_struct);
    
    
    gdb-print-prepare()
    {
        # usage:
        # gdb-print-prepare $src > app.gdb
        # gdb --batch --quiet --command=app.gdb app
        cat  <<-EOF
        set auto-load safe-path /
        EOF
        grep --with-filename --line-number --recursive '^\s\+gdb_print(.*);' $1 | \
        while IFS=$'\t ;()' read line func var rest; do
            cat  <<-EOF
            break ${line%:}
            commands
            silent
            where 1
            echo \\n$var\\n
            print $var
            cont
            end
            EOF
        done
        cat  <<-EOF
        run
        bt
        echo ---\\n
        EOF
    }
    

    参考文献: https://gitlab.com/makelinux/lib/blob/master/snippets/gdb-print-prepare.md