Возможно ли подключить gdb к аварийному процессу (отладка "a.k.a" точно в срок ")
Когда сбой процесса, я хочу, чтобы он вызывал gdb (или аналогичный отладчик) в этом состоянии с разбивкой, но не очищенным. Часто после вскрытия дампа ядра дает достаточно информации, но иногда я хочу продолжить изучение состояния работы, возможно, подавляя немедленную ошибку и работая немного дальше. Не всегда целесообразно запускать процесс под gdb с самого начала (например, когда вызов является сложным или ошибка является абсурдно чувствительной к времени)
То, что я описываю, - это в основном средство отладки в режиме "точно в срок", которое отображается в MS Windows с помощью раздела реестра "AEDebug": оставляя зависающую нить приостановленной, делая что-то диагностическое. На компьютерах с ОС, отличных от разработчиков, обычно устанавливается механизм диагностики сбоев (ранее "Dr Watson" ), для которого эквивалент Ubuntu выглядит "apport" .
Я нашел старый почтовый поток (2007), который ссылается на этот вопрос "время от времени появляющийся", поэтому, возможно, он существует, но описан в некотором роде что ускользает от моих поисков?
Ответы
Ответ 1
Я не знаю, существует ли такая функция, но как хак, вы можете LD_PRELOAD что-то, что добавляет обработчик на SIGSEGV, который вызывает gdb
:
cat >> handler.c << 'EOF'
#include <stdlib.h>
#include <signal.h>
void gdb(int sig) {
system("exec xterm -e gdb -p \"$PPID\"");
abort();
}
void _init() {
signal(SIGSEGV, gdb);
}
EOF
gcc -g -fpic -shared -o handler.so -nostartfiles handler.c
И затем запустите свои приложения с помощью:
LD_PRELOAD=/path/to/handler.so your-application
Затем, после SEGV, он будет работать gdb
в xterm
. Если вы выполните bt
, вы увидите что-то вроде:
(gdb) bt
#0 0x00007f8c58152cac in __libc_waitpid (pid=8294,
[email protected]=0x7fffd6170e40, [email protected]=0)
at ../sysdeps/unix/sysv/linux/waitpid.c:31
#1 0x00007f8c580df01b in do_system (line=<optimized out>)
at ../sysdeps/posix/system.c:148
#2 0x00007f8c58445427 in gdb (sig=11) at ld.c:4
#3 <signal handler called>
#4 strlen () at ../sysdeps/x86_64/strlen.S:106
#5 0x00007f8c5810761c in _IO_puts (str=0x0) at ioputs.c:36
#6 0x000000000040051f in main (argc=1, argv=0x7fffd6171598) at a.c:2
Вместо запуска gdb
вы также можете приостановить себя (kill(getpid(), SIGSTOP
) или вызвать pause()
, чтобы начать gdb
самостоятельно в свободное время.
Этот подход не будет работать, если приложение установит сам обработчик SEGV или будет setuid/setgid...
Этот подход, используемый @yugr для своего libdebugme tool, который вы можете использовать здесь как:
DEBUGME_OPTIONS='xterm:handle_signals=1' \
LD_PRELOAD=/path/to/libdebugme.so your-application
Ответ 2
Отвечая на мой собственный вопрос, чтобы включить код скрещенности, я получил от истинного ответа (@Stephane Chazelas выше). Только реальные изменения исходного ответа:
- настройка PR_SET_PTRACER_ANY, позволяющая подключать gdb
- немного больше (бесполезно?), пытаясь избежать кода libc в надежде на продолжение работы
для (некоторых) сбоев кучи
- включал SIGABRT, потому что некоторые из аварий были assert() s
Я использовал его с Linux Mint 16 (ядро 3.11.0-12-generic)
/* LD_PRELOAD library which launches gdb "just-in-time" in response to a process SIGSEGV-ing
* Compile with:
*
* gcc -g -fpic -shared -nostartfiles -o jitdbg.so jitdbg.c
*
* then put in LD_PRELOAD before running process, e.g.:
*
* LD_PRELOAD=~/scripts/jitdbg.so defective_executable
*/
#include <unistd.h>
#include <signal.h>
#include <sys/prctl.h>
void gdb(int sig) {
if(sig == SIGSEGV || sig == SIGABRT)
{
pid_t cpid = fork();
if(cpid == -1)
return; // fork failed, we can't help, hope core dumps are enabled...
else if(cpid != 0)
{
// Parent
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); // allow any process to ptrace us
raise(SIGSTOP); // wait for child gdb invocation to pick us up
}
else
{
// Child - now try to exec gdb in our place attached to the parent
// Avoiding using libc since that may already have been stomped, so building the
// gdb args the hard way ("gdb dummy PID"), first copy
char cmd[100];
const char* stem = "gdb _dummy_process_name_ "; // 18 trailing spaces to allow for a 64 bit proc id
const char*s = stem;
char* d = cmd;
while(*s)
{
*d++ = *s++;
}
*d-- = '\0';
char* hexppid = d;
// now backfill the trailing space with the hex parent PID - not
// using decimal for fear of libc maths helper functions being dragged in
pid_t ppid = getppid();
while(ppid)
{
*hexppid = ((ppid & 0xF) + '0');
if(*hexppid > '9')
*hexppid += 'a' - '0' - 10;
--hexppid;
ppid >>= 4;
}
*hexppid-- = 'x'; // prefix with 0x
*hexppid = '0';
// system() isn't listed as safe under async signals, nor is execlp,
// or getenv. So ideally we'd already have cached the gdb location, or we
// hardcode the gdb path, or we accept the risk of re-entrancy/library woes
// around the environment fetch...
execlp("mate-terminal", "mate-terminal", "-e", cmd, (char*) NULL);
}
}
}
void _init() {
signal(SIGSEGV, gdb);
signal(SIGABRT, gdb);
}
Ответ 3
Если вы можете ожидать, что какая-то конкретная программа выйдет из строя, вы можете запустить ее под gdb.
gdb /usr/local/bin/foo
> run
Если программа выйдет из строя, gdb поймает ее и позволит продолжить расследование.
Если вы не можете предсказать, когда и какая программа выйдет из строя, тогда вы можете включить основные свалки системы в целом.
ulimit -c unlimited
Принудительный дамп процесса foo
/usr/local/sbin/foo
kill -11 `pidof foo` #kill -3 likely will also work
Должен быть создан основной файл, к которому вы можете подключить gdb к
gdb attach `which foo` -c some.core
Для систем RedHat иногда требуется дополнительная настройка, кроме ulimit, для включения дампов ядра.
http://www.akadia.com/services/ora_enable_core.html