Бывает так, что ошибка в коде воспроизводится лишь при определенном стечение обстоятельств. Эти обстоятельства могут быть довольно сложными, особенно если приложение распределенное и каждый его экземпляр состоит из N процессов. Подцепиться отладчиком к правильному процессу в правильный момент времени практически невозможно. В подобных случаях я использую один незамысловатый прием, речь о котором и пойдет далее.
Идея очень простая. Она заключается в модификации кода приложения так, чтобы он сам определял возникновение ошибки, сигнализировал об этом, а потом ждал, когда программист придет с отладчиком. Рассмотрим упрощенный пример:
#include <unistd.h>
int main ( ) {
/* if( some condition violated ) { */
int ret ;
printf ( «Attach with the debugger, pid = %d n » , getpid ( ) ) ;
do {
ret = sleep ( 1 ) ;
} while ( ret == 0 ) ;
/* } */
printf ( «sleep() returned %d n » , ret ) ;
return 0 ;
}
В реальном коде вместо printf(...)
будет какой-нибудь log(WARNING, ...)
. Логи приложения можно мониторирить командой watch:
Компилируем с отладочными символами, запускаем, гоняем тесты, на которых воспроизводится проблема, ждем. (Бывает и так, что нет нормальных шагов для воспроизведения проблемы, но подобный сценарий выходит за рамки статьи.) Когда проблема воспроизвелась, цепляемся к процессу отладчиком:
Ставим бряк на while(ret == 0)
:
Breakpoint 1 at 0x10498: file test.c, line 9.
… и возобновляем работу приложения:
Continuing
Когда встали на бряке, модифицируем переменную ret:
9 } while(ret == 0);
(gdb) p ret=1
Поздравляю, мы оказались в правильном процессе в правильный момент времени. Проблема решена. Далее приложение можно отлаживать, как обычно . Кстати, проверок в коде может быть больше одной. Источник ошибки можно быстро локализовать по принципу бинарного поиска.
Fun fact! В MacOS, в отличие от Linux, системный вызов sleep()
сразу возвращает управление при подключении к процессу с отладчиком. На этой платформе можно написать sleep(1000)
и не добавлять цикл.
Уверен, что опытные разработчики не узнали для себя ничего нового. Но я подумал , что кому-то данная информация все же может пригодится. В примерах были использованы C и GDB, но прием работает с любым стеком технологий, где есть отладчик.