debug: make DEBUG_WAIT support more robust

PTRACE_ME doesn't work as we thought, instead just deliver a suitable
signal depending on if we're currently under a debugger.
This commit is contained in:
Danny Robson 2016-03-21 14:19:08 +11:00
parent eacd2f6072
commit c15794d4b3
2 changed files with 55 additions and 7 deletions

View File

@ -85,3 +85,13 @@ util::debug::init (void)
if (getenv ("DEBUG_WAIT"))
await_debugger ();
}
///////////////////////////////////////////////////////////////////////////////
static void
debug_wait [[gnu::constructor]] (void) {
if (auto val = getenv ("DEBUG_WAIT")) {
if (std::string ("0") != val)
await_debugger ();
}
}

View File

@ -21,18 +21,53 @@
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
///////////////////////////////////////////////////////////////////////////////
// Will return true if it is known we are under a debugger. This isn't
// designed to exhaustively check for debuggers, only to offer conveniences.
// Specifically: errors will return false.
static
bool
is_debugged (void)
{
auto pid = fork ();
if (pid == -1)
return false;
// We are the child
if (pid == 0) {
auto ppid = getppid ();
int res;
// attempt to trace our parent. this will fail if we're being debugged.
if (ptrace (PTRACE_ATTACH, ppid, nullptr, nullptr) == 0) {
waitpid (ppid, nullptr, 0);
ptrace (PTRACE_DETACH, ppid, nullptr, nullptr);
res = 0;
} else {
res = 1;
}
_exit (res);
// We are the parent. Wait for our child to tell us the result.
} else {
int status;
waitpid (pid, &status, 0);
return WEXITSTATUS (status);
}
}
///////////////////////////////////////////////////////////////////////////////
void
breakpoint (void)
{
#if defined (__x86_64) || defined (__i386)
__asm__ ("int $3;");
#else
raise (SIGINT);
#endif
raise (SIGTRAP);
}
@ -48,8 +83,11 @@ prepare_debugger (void)
void
await_debugger (void)
{
if (ptrace (PTRACE_TRACEME))
LOG_CRITICAL ("unable to wait for debugger");
if (is_debugged ())
breakpoint ();
else
raise (SIGSTOP);
}