Commit 69850c1e authored by Kenton Varda's avatar Kenton Varda

Print stack trace on signal for all processes using KJ_MAIN.

parent ab775828
......@@ -27,6 +27,9 @@
#include <stdlib.h>
#include <exception>
#include <new>
#include <signal.h>
#include <sys/mman.h>
#include "io.h"
#if (__linux__ && !__ANDROID__) || __APPLE__
#define KJ_HAS_BACKTRACE 1
......@@ -139,6 +142,71 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) {
#endif
}
namespace {
void crashHandler(int signo, siginfo_t* info, void* context) {
void* traceSpace[32];
auto trace = getStackTrace(traceSpace);
if (trace.size() >= 3) {
// Remove getStackTrace(), crashHandler() and signal trampoline from trace.
trace = trace.slice(3, trace.size());
}
auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo),
"\nstack: ", strArray(trace, " "),
stringifyStackTrace(trace), '\n');
FdOutputStream(STDERR_FILENO).write(message.begin(), message.size());
_exit(1);
}
} // namespace
void printStackTraceOnCrash() {
#if KJ_HAS_BACKTRACE
// Set up alternate signal stack so that stack overflows can be handled.
stack_t stack;
memset(&stack, 0, sizeof(stack));
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#ifndef MAP_GROWSDOWN
#define MAP_GROWSDOWN 0
#endif
stack.ss_size = 65536;
// Note: ss_sp is char* on FreeBSD, void* on Linux and OSX.
stack.ss_sp = reinterpret_cast<char*>(mmap(
nullptr, stack.ss_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0));
KJ_SYSCALL(sigaltstack(&stack, nullptr));
// Catch all relevant signals.
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND;
action.sa_sigaction = &crashHandler;
// Dump stack on common "crash" signals.
KJ_SYSCALL(sigaction(SIGSEGV, &action, nullptr));
KJ_SYSCALL(sigaction(SIGBUS, &action, nullptr));
KJ_SYSCALL(sigaction(SIGFPE, &action, nullptr));
KJ_SYSCALL(sigaction(SIGABRT, &action, nullptr));
// Dump stack on unimplemented syscalls -- useful in seccomp sandboxes.
KJ_SYSCALL(sigaction(SIGSYS, &action, nullptr));
#ifdef KJ_DEBUG
// Dump stack on keyboard interrupt -- useful for infinite loops. Only in debug mode, though,
// because stack traces on ctrl+c can be obnoxious for, say, command-line tools.
KJ_SYSCALL(sigaction(SIGINT, &action, nullptr));
#endif
#endif
}
StringPtr KJ_STRINGIFY(Exception::Type type) {
static const char* TYPE_STRINGS[] = {
"failed",
......
......@@ -302,6 +302,11 @@ String stringifyStackTrace(ArrayPtr<void* const>);
// Convert the stack trace to a string with file names and line numbers. This may involve executing
// suprocesses.
void printStackTraceOnCrash();
// Registers signal handlers on common "crash" signals like SIGSEGV that will (attempt to) print
// a stack trace. You should call this as early as possible on program startup. Programs using
// KJ_MAIN get this automatically.
} // namespace kj
#endif // KJ_EXCEPTION_H_
......@@ -43,7 +43,9 @@ namespace kj {
TopLevelProcessContext::TopLevelProcessContext(StringPtr programName)
: programName(programName),
cleanShutdown(getenv("KJ_CLEAN_SHUTDOWN") != nullptr) {}
cleanShutdown(getenv("KJ_CLEAN_SHUTDOWN") != nullptr) {
printStackTraceOnCrash();
}
StringPtr TopLevelProcessContext::getProgramName() {
return programName;
......
......@@ -59,9 +59,11 @@ namespace _ { // private
bool hasSubstring(kj::StringPtr haystack, kj::StringPtr needle) {
// TODO(perf): This is not the best algorithm for substring matching.
for (size_t i = 0; i <= haystack.size() - needle.size(); i++) {
if (haystack.slice(i).startsWith(needle)) {
return true;
if (needle.size() <= haystack.size()) {
for (size_t i = 0; i <= haystack.size() - needle.size(); i++) {
if (haystack.slice(i).startsWith(needle)) {
return true;
}
}
}
return false;
......@@ -181,68 +183,6 @@ void GlobFilter::applyState(char c, int state) {
namespace {
void crashHandler(int signo, siginfo_t* info, void* context) {
void* traceSpace[32];
auto trace = getStackTrace(traceSpace);
if (trace.size() >= 3) {
// Remove getStackTrace(), crashHandler() and signal trampoline from trace.
trace = trace.slice(3, trace.size());
}
auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo),
"\nstack: ", strArray(trace, " "),
stringifyStackTrace(trace), '\n');
FdOutputStream(STDERR_FILENO).write(message.begin(), message.size());
_exit(1);
}
void registerCrashHandler() {
// Set up alternate signal stack so that stack overflows can be handled.
stack_t stack;
memset(&stack, 0, sizeof(stack));
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#ifndef MAP_GROWSDOWN
#define MAP_GROWSDOWN 0
#endif
stack.ss_size = 65536;
// Note: ss_sp is char* on FreeBSD, void* on Linux and OSX.
stack.ss_sp = reinterpret_cast<char*>(mmap(
nullptr, stack.ss_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0));
KJ_SYSCALL(sigaltstack(&stack, nullptr));
// Catch all relevant signals.
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND;
action.sa_sigaction = &crashHandler;
// Dump stack on common "crash" signals.
KJ_SYSCALL(sigaction(SIGSEGV, &action, nullptr));
KJ_SYSCALL(sigaction(SIGBUS, &action, nullptr));
KJ_SYSCALL(sigaction(SIGFPE, &action, nullptr));
KJ_SYSCALL(sigaction(SIGABRT, &action, nullptr));
// Dump stack on unimplemented syscalls -- useful in seccomp sandboxes.
KJ_SYSCALL(sigaction(SIGSYS, &action, nullptr));
// Dump stack on keyboard interrupt -- useful for infinite loops.
KJ_SYSCALL(sigaction(SIGINT, &action, nullptr));
}
} // namespace
// =======================================================================================
namespace {
class TestExceptionCallback: public ExceptionCallback {
public:
TestExceptionCallback(ProcessContext& context): context(context) {}
......@@ -278,9 +218,7 @@ private:
class TestRunner {
public:
explicit TestRunner(ProcessContext& context)
: context(context), useColor(isatty(STDOUT_FILENO)) {
registerCrashHandler();
}
: context(context), useColor(isatty(STDOUT_FILENO)) {}
MainFunc getMain() {
return MainBuilder(context, "KJ Test Runner (version not applicable)",
......
......@@ -39,6 +39,7 @@ class Thread {
public:
explicit Thread(Function<void()> func);
KJ_DISALLOW_COPY(Thread);
~Thread() noexcept(false);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment