是否可以将gdb附加到崩溃的进程(也称为"即时"调试)

Tom*_*low 12 crash debugging gdb

当进程崩溃时,我希望有可能在崩溃但未清理的状态下对它调用gdb(或类似的调试器).经常对核心转储进行后期处理可以提供足够的信息,但有时我想进一步探索运行状态,可能会抑制即时故障并进一步运行.从一开始就在gdb下运行进程并不总是合适的(例如,调用很复杂或者错误是时间敏感的)

我所描述的基本上是通过"AEDebug"注册表项在MS Windows上公开的即时调试工具:在执行某些诊断时保留故障线程.在非开发人员的Windows PC上,这通常设置为崩溃诊断机制(以前称为"Dr Watson"),Ubuntu等效物似乎是"apport".

我确实找到了一个旧的邮件线程(2007),它引用了这个问题"偶尔会弹出",所以它可能存在但是以一种逃避我搜索的方式描述?

Ste*_*las 19

我不知道是否存在这样的功能,但作为一个黑客,你可以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
Run Code Online (Sandbox Code Playgroud)

然后运行您的应用程序:

LD_PRELOAD=/path/to/handler.so your-application
Run Code Online (Sandbox Code Playgroud)

然后,在SEGV上,它将gdb在a中运行xterm.如果你在bt那里,你会看到类似的东西:

(gdb) bt
#0  0x00007f8c58152cac in __libc_waitpid (pid=8294,
    stat_loc=stat_loc@entry=0x7fffd6170e40, options=options@entry=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
Run Code Online (Sandbox Code Playgroud)

gdb你也可以暂停自己(kill(getpid(), SIGSTOP或者打电话pause()来开始gdb自己的闲暇时间,而不是跑步.

如果应用程序本身安装SEGV处理程序或setuid/setgid,那么该方法将无效...

这是@yugr用于他的libdebugme工具的方法,你可以在这里使用它:

DEBUGME_OPTIONS='xterm:handle_signals=1' \
  LD_PRELOAD=/path/to/libdebugme.so your-application
Run Code Online (Sandbox Code Playgroud)


Tom*_*low 7

回答我自己的问题,包括我从真实答案得出的充实代码(@Stephane Chazelas上面).只有对原始答案的真正更改是:

  1. 设置PR_SET_PTRACER_ANY以允许gdb附加
  2. 试图避免使用libc代码,希望仍能解决(某些)堆损坏问题
  3. 包括SIGABRT,因为一些崩溃是assert()s

我一直在使用Linux Mint 16(内核3.11.0-12-通用)

/* 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's 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);
}
Run Code Online (Sandbox Code Playgroud)