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" {