Linux C: простой и «красивый» дамп / распечатка структур (как в gdb) - из исходного кода? - PullRequest
12 голосов
/ 22 июля 2010

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

Скажем, у нас есть простой пример 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?) Значение указателя структуры johndoe как {age = 6, height = 0} ...Я хотел бы сделать то же самое, но прямо из программы на Си;скажем как в следующем примере:

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

15 голосов
/ 22 июля 2010

Просто хотел сказать - спасибо за все ваши хорошие и невероятно быстрые ответы, очень помог мне понять проблему (почему в 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

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

Еще раз спасибо за помощь,Приветствия!

РЕДАКТИРОВАТЬ: причина, почему я не думаю, что это будет работать для модулей ядра, в том, что в этом случае у нас есть программа пользователя с идентификатором процесса;и мы просто вызываем gdb из этой программы, инструктируя его о нашем PID - так что gdb может «присоединиться» к нашему процессу;затем, поскольку gdb также получает указание загрузить исполняемый файл с символами отладки (чтобы он «знал», что такое структура), и проинструктирован об адресе, где находится данная переменная структуры, gdb может затем распечатать структуру,

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

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

2 голосов
/ 22 июля 2010

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

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

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

2 голосов
/ 22 июля 2010

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

1 голос
/ 22 июля 2010

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

0 голосов
/ 18 октября 2018

Вот более эффективное решение для пользовательского режима, а не для ядра, которое готовит скрипт gdb. Затем приложение выполняется через GDB со сценарием. Скрипт содержит точки останова с командами. В исходном коде необходимо помечать точки останова пустым 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
}

Ссылка: https://gitlab.com/makelinux/lib/blob/master/snippets/gdb-print-prepare.md

0 голосов
/ 22 июля 2010

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

обильные ctags

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

...