开发者

Linux C: Easy & 'pretty' dump/printout of structs (like in gdb) - from source code?

开发者 https://www.devze.com 2023-01-08 05:15 出处:网络
I am having a slight problem with some structs in a kernel module I\'m building, so I thought it would be nice if there was an easy way to print out structs and their values - and below is a small use

I am having a slight problem with some structs in a kernel module I'm building, so I thought it would be nice if there was an easy way to print out structs and their values - and below is a small userland example of what I mean.

Say we have the simple C example as below (given in form of a bash commands):

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 

 

If we run this example, the program will compile, and gdb will run it, and automatically stop at the breakpoint. Here we can do the following:

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

 

As shown, in gdb we can printout (dump?) the value of the struct pointer johndoe as {age = 6, height = 0} ... I would like to do the same, but directly from a C program; say as in the following example:

#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);
}

 

which would result with an output like:

Hello World - age: 6
$2 = {age = 6, height = 开发者_JS百科0}

 

So my question is - does a function like that imaginary printout_struct exist - or is there another approach to make a printout like this possible?

Thanks in advance for any help,

Cheers!


Just wanted to say - thanks for all your good and incredibly fast answers, helped me a lot to understand the problem (of why there isn't such a 'native' function in C)!

(and sorry for answering my own question - doing that, so as not to garble the original post, and being able to format code)

While looking further I managed to find:

  • generate a core dump in linux - Stack Overflow
  • just-in-time debugging? - mlist.linux.kernel | Google Groups

which illustrate the trick with calling gdb with the pid of the process itself, and so I modified the dumpstack function found there, to get the following code:

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

  which basically ends up displaying what I wanted:

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

 

Though, I'm not sure it will work with kernel modules ...

Thanks again for the help,
Cheers!

EDIT: The reason why I don't think it will work for kernel modules, is that in this case, we have a userland program with a process ID; and we simply call gdb from this program, while instructing it about our PID - so gdb can "attach" to our process; then, since gdb is also instructed to load the executable with debug symbols (so it will 'know' what the struct is), and instructed about the address where a given struct variable is located, gdb can then printout the struct.

For kernel modules - first I don't think they are 'processes' in the sense of having a unique PID, so gdb will have nothing to attach to! In fact, there is a kernel debugger, kgdb which can in fact break into a running kernel and step through module source code; however, you need a second machine connected through a serial connection for that - or a virtual machine, see Linux Hacks: Setting up kgdb using kvm/qemu.

So, in any case, it seems that gdb would not be able to inspect memory of the currently running host kernel gdb is running in - but I'll try to experiment, and if the experiments show otherwise, I'll be sure to post :)


The C language doesn't have metadata, either compile-time or run-time. There might be some vendor-specific extensions to do this. For example, doxygen will make an XML file with all the member information (name and type) of each struct type in your program, it wouldn't be too difficult to write a program to process that XML file and generate the code for a printout_person(const struct person*) function automatically.


See this related question for some info on parsing structs. Specifically my reference there to pstruct.

In your case, where you want to get info from inside a running program, you'd have to either invoke one of those external tools, or parse out the debug info from your executable and display it appropriately.

You might also look at libgdb, though it looks like it might be somewhat dated.


You have to add meta-info describing the structure, so that printout_struct can do its job. Otherwise, it can't guess anything. Try with gdb removing every debugging info, and you will see that it can't "speak" about "age" or whatever.


recently somebody mentioned

exuberant ctags

on stackoverflow for a similar task. Perhaps you can dig that out, I didn't find immediately, though.


Here is more efficient solution for user mode, not kernel, which prepares gdb script. Then an application is executed via gdb with the script. The script contains breakpoints with commands. In a source code need to mark breakpoints with empty define:

#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
}

Reference: https://gitlab.com/makelinux/lib/blob/master/snippets/gdb-print-prepare.md

0

精彩评论

暂无评论...
验证码 换一张
取 消