Linux C: Easy & 'pretty' дамп/распечатка структур (например, в 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 мы можем распечатать (dump?) значение указателя struct 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, или существует ли другой подход, чтобы сделать распечатку такой, как это возможно?

Заранее благодарим за помощь,
Ура!

Ответ 1

Просто хотел сказать - спасибо за все ваши хорошие и невероятно быстрые ответы, помог мне много понять проблему (о том, почему в C нет такой "родной" функции)!

(и жаль, что ответили на мой собственный вопрос - сделайте это, чтобы не усложнять исходное сообщение и не форматировать код)

Пока я смотрел дальше, мне удалось найти:

которые иллюстрируют трюк с вызовом 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

 

Хотя, я не уверен, что он будет работать с модулями ядра...

Еще раз спасибо за помощь,
Ура!

EDIT: причина, по которой я не думаю, что она будет работать для модулей ядра, заключается в том, что в этом случае у нас есть программа userland с идентификатором процесса; и мы просто вызываем gdb из этой программы, инструктируя ее о нашем PID - поэтому gdb может "присоединяться" к нашему процессу; тогда, поскольку gdb также поручено загрузить исполняемый файл с помощью отладочных символов (чтобы он "знал", что представляет собой структура) и проинструктирован об адресе, в котором находится данная структурная переменная, gdb может затем распечатать структуру,

Для модулей ядра - сначала я не думаю, что они являются "процессами" в смысле наличия уникального PID, поэтому gdb не будет иметь никакого отношения к этому! На самом деле, есть отладчик ядра, kgdb, который может фактически перерасти в запущенное ядро ​​и перейти через исходный код модуля; однако вам нужна вторая машина, подключенная через последовательное соединение для этого - или виртуальная машина, см. Linux Hacks: настройка kgdb с использованием kvm/qemu.

Итак, в любом случае, кажется, что gdb не сможет проверить память текущего запущенного ядра ядра gdb, но я попытаюсь поэкспериментировать, и если эксперименты показывают иначе, Я обязательно отправлю сообщение:)

Ответ 2

См. этот связанный вопрос для получения некоторой информации о разборе структур. В частности, моя ссылка на pstruct.

В вашем случае, когда вы хотите получить информацию из запущенной программы, вам придется либо вызывать один из этих внешних инструментов, либо анализировать информацию об отладке из вашего исполняемого файла и отображать его соответствующим образом.

Вы также можете посмотреть libgdb, хотя похоже, что он может быть несколько устаревшим.

Ответ 3

Вам нужно добавить метаинформацию, описывающую структуру, чтобы printout_struct мог выполнять свою работу. В противном случае он ничего не может догадаться. Попробуйте с gdb удалить каждую информацию об отладке, и вы увидите, что она не может "говорить" о "возрасте" или что-то еще.

Ответ 4

Язык C не имеет метаданных - времени компиляции или времени выполнения. Для этого могут быть некоторые расширения для конкретных поставщиков. Например, doxygen сделает файл XML со всей информацией о члене (имя и тип) каждого типа структуры в вашей программе, это было бы нелегко написать программу для обработки этого XML файла и автоматически генерировать код для функции printout_person (const struct person *).

Ответ 5

недавно кто-то упомянул

exuberant ctags

в stackoverflow для аналогичной задачи. Возможно, вы можете это выкопать, я не нашел сразу же.