Способы отладки gdb’ом или ddd’ом приложения с fork()

Я разрабатываю клиент серверное приложение на языке программирования C (Си). У меня как и у всех разработчиков возникает необходимость запуска приложения под отладчиком. В однопоточном приложении и приложении с thread'ами (нитями) все достаточно стандартно - просто запускаем его через $ gdb мое_приложение. Но в случае если отладчик наткнется на вызов fork() он переключится на parent (родитель) процесс, хотя обычно, в демонах, после первого fork()'а родитель завершает свою работу и по смылу нужно отлаживать child процесс. Для ddd и gbd можно писать скрипты при помощи которых можно указывать отладчику идти по parent пути или по child, также можно цепляться уже к запущенному и работающему приложению. Сейчас мы и разберем все эти способы отладки.

Отладка с выбором пути следования по вызовам fork()

Первый способ, он описан в документации к gdb. Весь смысл метода в том чтобы описать по какой ветке идти после вызова fork() и скормить это описание отладчику. Вот пример кода для запуска под отладчиком:

#include
#include
#include
#include 

#define I_CHILD 1
#define I_PARENT 2

void dummy_function(int who_i) {
    pid_t pid;
    if (who_i == I_CHILD) {
        printf("Child after first fork() call dummy_function() my pid is %i\n", getpid());
    } else {
        printf("Parent after first fork() call dummy_function() my pid is %i\n", getpid());
    }
    switch (pid = fork()) {
    case -1:
        printf("Second fork() ERROR\n");
        if (errno == ENOMEM) {
            printf("Memory is tight\n");
        }
        exit(-1);
    case 0:
        printf("I'm child after second fork() My pid is %i\n", getpid());
        break;
    default:
        printf("I'm parent after second fork() My pid is %i My child's pid is %i\n", getpid(), pid);
        break;
    }
    printf("PID %i exit\n", getpid());
    exit(0);
}

int main(int argc, char *argv[]) {
    pid_t pid;
    printf("Example started with pid %i\n", getpid());
    printf("Commandline args: ");
    while (--argc > 0) {
        printf("%s ", argv[argc]);
    }
    printf("\n");
    switch (pid = fork()) {
    case -1:
        printf("First fork() ERROR\n");
        if (errno == ENOMEM) {
            printf("Memory is tight\n");
        }
        exit(-1);
    case 0:
        printf("I'm child after first fork() My pid is %i\n", getpid());
        dummy_function(I_CHILD);
    default:
        printf("I'm parent after first fork() My pid is %i My child's pid is %i\n", getpid(), pid);
        dummy_function(I_PARENT);
    }
}

Сохраняем в файл fork.c компилим с ключами: -O0 - отключение оптимизации и -g - для сохранения кода в исполняемый файл (они нужны для отладчика):

$ gcc fork.c -O0 -g

запускаем:

$ ./a.out 1 2 3 4 5 6 7 8 9 10 11 12 13

Получаем что-то вроди этого:

Example started with pid 6990
Commandline args: 13 12 11 10 9 8 7 6 5 4 3 2 1
I'm parent after first fork() My pid is 6990 My child's pid is 6991
Parent after first fork() call dummy_function() my pid is 6990
I'm child after first fork() My pid is 6991
Child after first fork() call dummy_function() my pid is 6991
I'm parent after second fork() My pid is 6990 My child's pid is 6992
PID 6990 exit
I'm parent after second fork() My pid is 6991 My child's pid is 6993
PID 6991 exit
user@server-32:~/sandbox/gdb_fork_example$ I'm child after second fork() My pid is 6993
PID 6993 exit
I'm child after second fork() My pid is 6992
PID 6992 exit

Все программа работает, но вот незадача если мы запустим её под отладчиком то сможем отлаживать лишь самый первый процесс, в предыдущем примере у него был pid 6990. И так приступим

Теги: , , , , ,

Дата: 8 ноября 2012

Автор: andreykyz

Комментарии: 0

Оставить комментарий

*

Комментарий:

Ваш e-mail не будет опубликован.
Обязательные поля помечены *

Разрешенные HTML теги:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>