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 для аналогичной задачи. Возможно, вы можете это выкопать, я не нашел сразу же.