From f25ac8be359f0efd1d264f837199b663c10ce8e0 Mon Sep 17 00:00:00 2001 From: Mitar Date: Sat, 15 Feb 2014 23:49:19 -0800 Subject: [PATCH 1/2] Allow specifying a callback. Don't write files. --- binding.gyp | 5 +-- src/segfault-handler.cpp | 68 ++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/binding.gyp b/binding.gyp index 9f37562..e92ad84 100644 --- a/binding.gyp +++ b/binding.gyp @@ -6,9 +6,10 @@ "src/segfault-handler.cpp" ], "defines": [ "DEBUG", "_DEBUG" ], - "cflags": [ "-O0" ], + "cflags": [ "-O0", "-g" ], "xcode_settings": { - "OTHER_CFLAGS": [ "-O0" ] + "OTHER_CFLAGS": [ "-O0", "-g"], + "OTHER_LFLAGS": [ "-O0", "-g"] } } ] diff --git a/src/segfault-handler.cpp b/src/segfault-handler.cpp index fcb29a9..e9055ec 100644 --- a/src/segfault-handler.cpp +++ b/src/segfault-handler.cpp @@ -18,35 +18,37 @@ using namespace node; #define STDERR_FD 2 +static Persistent callback; +static bool handlersSet = false; + static void segfault_handler(int sig, siginfo_t *si, void *unused) { void *array[32]; // Array to store backtrace symbols size_t size; // To store the size of the stack backtrace char sbuff[128]; int n; // chars written to buffer - int fd; - time_t now; int pid; - // Construct a filename - time(&now); pid = getpid(); - snprintf(sbuff, sizeof(sbuff), "stacktrace-%d-%d.log", (int)now, pid ); - // Open the File - fd = open(sbuff, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IRGRP | S_IROTH); - // Write the header line - n = snprintf(sbuff, sizeof(sbuff), "PID %d received SIGSEGV for address: 0x%lx\n", pid, (long) si->si_addr); - if(fd > 0) write(fd, sbuff, n); + n = snprintf(sbuff, sizeof(sbuff), "PID %d received SIGSEGV/SIGBUS (%i) for address: 0x%lx\n", pid, si->si_signo, (long)si->si_addr); write(STDERR_FD, sbuff, n); - // Write the Backtrace size = backtrace(array, 32); - if(fd > 0) backtrace_symbols_fd(array, size, fd); backtrace_symbols_fd(array, size, STDERR_FD); - // Exit violently - close(fd); - exit(-1); + if (!callback.IsEmpty()) { + char **stack = backtrace_symbols(array, size); + Local argStack = Local::New(Array::New(size)); + for (size_t i = 0; i < size; i++) { + argStack->Set(i, String::New(stack[i])); + } + Local argv[3] = {argStack, Local::New(Number::New(si->si_signo)), Local::New(Number::New((long)si->si_addr))}; + callback->Call(Context::GetCurrent()->Global(), 3, argv); + free(stack); + } + + // Re-send the signal, this time a default handler will be called + kill(pid, si->si_signo); } // create some stack frames to inspect from CauseSegfault @@ -62,7 +64,6 @@ void segfault_stack_frame_1() int *foo = (int*)1; printf("NodeSegfaultHandlerNative: about to dereference NULL (will cause a SIGSEGV)\n"); *foo = 78; // trigger a SIGSEGV - } __attribute__ ((noinline)) @@ -80,13 +81,34 @@ Handle CauseSegfault(const Arguments& args) { } Handle RegisterHandler(const Arguments& args) { - struct sigaction sa; - memset(&sa, 0, sizeof(struct sigaction)); - sigemptyset(&sa.sa_mask); - sa.sa_sigaction = segfault_handler; - sa.sa_flags = SA_SIGINFO; - sigaction(SIGSEGV, &sa, NULL); - return Undefined(); + HandleScope scope; + + if (args.Length() > 0) { + if (!args[0]->IsFunction()) { + ThrowException(Exception::TypeError(String::New("Invalid callback argument"))); + return scope.Close(Undefined()); + } + + if (!callback.IsEmpty()) { + callback.Dispose(); + callback.Clear(); + } + callback = Persistent::New(Handle::Cast(args[0])); + } + + // Set our handler only once + if (!handlersSet) { + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = segfault_handler; + sa.sa_flags = SA_SIGINFO | SA_RESETHAND; // We set SA_RESETHAND so that our handler is called only once + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); + handlersSet = true; + } + + return scope.Close(Undefined()); } extern "C" { From 92bcfbb94ba685e4ff302dd33d29f7e5e82a4c78 Mon Sep 17 00:00:00 2001 From: Mitar Date: Sun, 16 Feb 2014 18:07:45 -0800 Subject: [PATCH 2/2] Get a stack trace on Mac OS X as well. --- src/segfault-handler.cpp | 102 +++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/src/segfault-handler.cpp b/src/segfault-handler.cpp index e9055ec..776cea9 100644 --- a/src/segfault-handler.cpp +++ b/src/segfault-handler.cpp @@ -6,13 +6,17 @@ #include #include #include -#include #include #include -#include #include #include #include + +#ifdef __APPLE__ +#define UNW_LOCAL_ONLY +#include +#endif + using namespace v8; using namespace node; @@ -21,30 +25,86 @@ using namespace node; static Persistent callback; static bool handlersSet = false; -static void segfault_handler(int sig, siginfo_t *si, void *unused) { - void *array[32]; // Array to store backtrace symbols - size_t size; // To store the size of the stack backtrace - char sbuff[128]; - int n; // chars written to buffer - int pid; +static void emptyCallback(siginfo_t *si) { + Local argStack = Local::New(Array::New(0)); + Local argv[3] = {argStack, Local::New(Number::New(si->si_signo)), Local::New(Number::New((unsigned long long)si->si_addr))}; + callback->Call(Context::GetCurrent()->Global(), 3, argv); +} - pid = getpid(); +static void segfaultHandler(int sig, siginfo_t *si, void *unused) { + char buffer[1024 * 10]; - n = snprintf(sbuff, sizeof(sbuff), "PID %d received SIGSEGV/SIGBUS (%i) for address: 0x%lx\n", pid, si->si_signo, (long)si->si_addr); - write(STDERR_FD, sbuff, n); + int pid = getpid(); - size = backtrace(array, 32); - backtrace_symbols_fd(array, size, STDERR_FD); + snprintf(buffer, sizeof(buffer), "PID %d received SIGSEGV/SIGBUS (%i) for address: 0x%llx\n", pid, si->si_signo, (unsigned long long)si->si_addr); + write(STDERR_FD, buffer, strlen(buffer)); if (!callback.IsEmpty()) { - char **stack = backtrace_symbols(array, size); - Local argStack = Local::New(Array::New(size)); - for (size_t i = 0; i < size; i++) { - argStack->Set(i, String::New(stack[i])); + void *stack[32]; + size_t stackSize; + + stackSize = backtrace(stack, 32); + + if (stackSize > 0) { + char **stackSymbols = backtrace_symbols(stack, stackSize); + Local argStack = Local::New(Array::New(stackSize)); + for (size_t i = 0; i < stackSize; i++) { + argStack->Set(i, String::New(stackSymbols[i])); + } + free(stackSymbols); + + Local argv[3] = {argStack, Local::New(Number::New(si->si_signo)), Local::New(Number::New((unsigned long long)si->si_addr))}; + callback->Call(Context::GetCurrent()->Global(), 3, argv); + } + else { +#ifdef __APPLE__ + unw_cursor_t cursor; + unw_context_t context; + unw_word_t pc; + + unw_getcontext(&context); + unw_init_local(&cursor, &context); + + snprintf(buffer, sizeof(buffer), "atos -p %i", pid); + + int frames = 0; + while (unw_step(&cursor) > 0) { + frames++; + unw_get_reg(&cursor, UNW_REG_IP, &pc); + int len = strlen(buffer); + snprintf(buffer + len, sizeof(buffer) - len, " 0x%llx", pc); + } + + if (frames > 0) { + FILE* program = popen(buffer, "r"); + if (program != NULL) { + Local argStack = Local::New(Array::New(frames)); + for (int i = 0; i < frames; i++) { + if (fgets(buffer, sizeof(buffer), program) == NULL) { + buffer[0] = '\0'; + } + int len = strlen(buffer); + if (len > 0 && buffer[len - 1] == '\n') { + buffer[len - 1] = '\0'; + } + argStack->Set(i, String::New(buffer)); + } + pclose(program); + + Local argv[3] = {argStack, Local::New(Number::New(si->si_signo)), Local::New(Number::New((unsigned long long)si->si_addr))}; + callback->Call(Context::GetCurrent()->Global(), 3, argv); + } + else { + emptyCallback(si); + } + } + else { + emptyCallback(si); + } +#else + emptyCallback(si); +#endif } - Local argv[3] = {argStack, Local::New(Number::New(si->si_signo)), Local::New(Number::New((long)si->si_addr))}; - callback->Call(Context::GetCurrent()->Global(), 3, argv); - free(stack); } // Re-send the signal, this time a default handler will be called @@ -101,7 +161,7 @@ Handle RegisterHandler(const Arguments& args) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sigemptyset(&sa.sa_mask); - sa.sa_sigaction = segfault_handler; + sa.sa_sigaction = segfaultHandler; sa.sa_flags = SA_SIGINFO | SA_RESETHAND; // We set SA_RESETHAND so that our handler is called only once sigaction(SIGSEGV, &sa, NULL); sigaction(SIGBUS, &sa, NULL);