static bool gdbExtendedBacktrace(int const dumpFile) #endif { /* * Pointer to the stackframe of the function containing faulting * instruction (assuming * -fomit-frame-pointer has *not* been used). * * The frame pointer register (x86: %ebp, x64: %rbp) point's to the * local variables of the current function (which are preceded by the * previous frame pointer and the return address). Hence the * additions to the frame-pointer register's content. */ void const * const frame = #if defined(SA_SIGINFO) && __WORDSIZE == 64 sigcontext ? (void*)(sigcontext->uc_mcontext.gregs[REG_RBP] + sizeof(greg_t) + sizeof(void (*)(void))) : NULL; #elif defined(SA_SIGINFO) && __WORDSIZE == 32 sigcontext ? (void*)(sigcontext->uc_mcontext.gregs[REG_EBP] + sizeof(greg_t) + sizeof(void (*)(void))) : NULL; #else NULL; #endif /* * Faulting instruction. */ void (*instruction)(void) = #if defined(SA_SIGINFO) && __WORDSIZE == 64 sigcontext ? (void (*)(void))sigcontext->uc_mcontext.gregs[REG_RIP] : NULL; #elif defined(SA_SIGINFO) && __WORDSIZE == 32 sigcontext ? (void (*)(void))sigcontext->uc_mcontext.gregs[REG_EIP] : NULL; #else NULL; #endif // Spawn a GDB instance and retrieve a pipe to its stdin int gdbPipe; int status; pid_t wpid; // Retrieve a full stack backtrace static const char gdbCommands[] = "backtrace full\n" // Move to the stack frame where we triggered the crash "frame 4\n" // Show the assembly code associated with that stack frame "disassemble /m\n" // Show the content of all registers "info registers\n" "quit\n"; const pid_t pid = execGdb(dumpFile, &gdbPipe); if (pid == 0) { return false; } // Disassemble the faulting instruction (if we know it) if (instruction) { dprintf(gdbPipe, "x/i %p\n", (void*)instruction); } // We have an intelligent guess for the *correct* frame, disassemble *that* one. if (frame) { dprintf(gdbPipe, "frame %p\n" "disassemble /m\n", frame); } write(gdbPipe, gdbCommands, strlen(gdbCommands)); /* Flush our end of the pipe to make sure that GDB has all commands * directly available to it. */ fsync(gdbPipe); // Wait for our child to terminate wpid = waitpid(pid, &status, 0); // Clean up our end of the pipe close(gdbPipe); // waitpid(): on error, -1 is returned if (wpid == -1) { write(dumpFile, "GDB failed\n", strlen("GDB failed\n")); printf("GDB failed\n"); return false; } /* waitpid(): on success, returns the process ID of the child whose * state has changed * * We only have one child, from our fork() call above, thus these PIDs * should match. */ assert(pid == wpid); /* Check wether our child (which presumably was GDB, but doesn't * necessarily have to be) didn't terminate normally or had a non-zero * return code. */ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { write(dumpFile, "GDB failed\n", strlen("GDB failed\n")); printf("GDB failed\n"); return false; } return true; }
/** * Dumps a backtrace of the stack to the given output stream. * * @param dumpFile a POSIX file descriptor to write the resulting backtrace to. * * @return false if any failure occurred, preventing a full "extended" * backtrace. */ static bool gdbExtendedBacktrace(int const dumpFile) { // Spawn a GDB instance and retrieve a pipe to its stdin int gdbPipe; int status; pid_t wpid; // Retrieve a full stack backtrace static const char gdbCommands[] = "backtrace full\n" // Move to the stack frame where we triggered the crash "frame 4\n" // Show the assembly code associated with that stack frame "disassemble\n" // Show the content of all registers "info registers\n" "quit\n"; const pid_t pid = execGdb(dumpFile, &gdbPipe); if (pid == 0) { return false; } write(gdbPipe, gdbCommands, sizeof(gdbCommands)); /* Flush our end of the pipe to make sure that GDB has all commands * directly available to it. */ fsync(gdbPipe); // Wait for our child to terminate wpid = waitpid(pid, &status, 0); // Clean up our end of the pipe close(gdbPipe); // waitpid(): on error, -1 is returned if (wpid == -1) { write(dumpFile, "GDB failed\n", strlen("GDB failed\n")); printf("GDB failed\n"); return false; } /* waitpid(): on success, returns the process ID of the child whose * state has changed * * We only have one child, from our fork() call above, thus these PIDs * should match. */ assert(pid == wpid); /* Check wether our child (which presumably was GDB, but doesn't * necessarily have to be) didn't terminate normally or had a non-zero * return code. */ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { write(dumpFile, "GDB failed\n", strlen("GDB failed\n")); printf("GDB failed\n"); return false; } return true; }