Skip to content

Commit ffbc37e

Browse files
committed
Merge pull request #34 from lavarsicious/master
Initial support for javascript callbacks for node >= 0.12 running on linux
2 parents 7fb9fe3 + d747529 commit ffbc37e

File tree

2 files changed

+153
-9
lines changed

2 files changed

+153
-9
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ var SegfaultHandler = require('segfault-handler');
1010

1111
SegfaultHandler.registerHandler("crash.log"); // With no argument, SegfaultHandler will generate a generic log file name
1212

13+
// Optionally specify a callback function for custom logging. This feature is currently only supported for Node.js >= v0.12 running on Linux.
14+
SegfaultHandler.registerHandler("crash.log", function(signal, address, stack) {
15+
// Do what you want with the signal, address, or stack (array)
16+
// This callback will execute before the signal is forwarded on.
17+
});
18+
1319
SegfaultHandler.causeSegfault(); // simulates a buggy native module that dereferences NULL
1420

1521
```

src/segfault-handler.cpp

+147-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <sys/types.h>
88
#include <v8-debug.h>
99
#include <time.h>
10+
#include <node.h>
11+
#include <uv.h>
1012

1113
#ifdef _WIN32
1214
#include "../includes/StackWalker.h"
@@ -20,6 +22,7 @@
2022
#include <signal.h>
2123
#include <stdarg.h>
2224
#include <unistd.h>
25+
#include <pthread.h>
2326
#endif
2427

2528
using namespace v8;
@@ -49,6 +52,121 @@ using namespace Nan;
4952

5053
#define BUFF_SIZE 128
5154

55+
#ifndef _WIN32
56+
struct callback_helper {
57+
58+
struct callback_args {
59+
60+
v8::Persistent<Function, v8::CopyablePersistentTraits<Function> >* callback;
61+
char **stack;
62+
size_t stack_size;
63+
int signo;
64+
long addr;
65+
pthread_mutex_t mutex;
66+
pthread_cond_t cond;
67+
68+
callback_args(v8::Persistent<Function, v8::CopyablePersistentTraits<Function> >* callback, void * const* stack, size_t stack_size, int signo, long addr) :
69+
callback(callback), stack(backtrace_symbols(stack, stack_size)), stack_size(stack_size), signo(signo), addr(addr) {
70+
pthread_mutex_init(&mutex, NULL);
71+
pthread_cond_init(&cond, NULL);
72+
}
73+
74+
~callback_args() {
75+
free(stack);
76+
pthread_mutex_destroy(&mutex);
77+
pthread_cond_destroy(&cond);
78+
}
79+
};
80+
81+
uv_async_t* handle;
82+
v8::Persistent<Function, v8::CopyablePersistentTraits<Function> > callback;
83+
84+
callback_helper(Handle<Function> func) {
85+
Isolate* isolate = Isolate::GetCurrent();
86+
// set the function reference
87+
callback.Reset(isolate, func);
88+
89+
// create the callback handle
90+
handle = (uv_async_t*) malloc(sizeof (uv_async_t));
91+
92+
// initialize the handle
93+
uv_async_init(uv_default_loop(), handle, make_callback);
94+
}
95+
96+
~callback_helper() {
97+
// reset the function reference
98+
callback.Reset();
99+
100+
// close the callback handle
101+
uv_close((uv_handle_t*) handle, close_callback);
102+
}
103+
104+
void send(void * const* stack, size_t stack_size, int signo, long addr) {
105+
// create the callback arguments
106+
callback_args* args = new callback_args(&callback, stack, stack_size, signo, addr);
107+
108+
// set the handle data so these args are accessible to make_callback
109+
handle->data = (void *) args;
110+
111+
// directly execute the callback if we're on the main thread,
112+
// otherwise have uv send it and await the mutex
113+
if (Isolate::GetCurrent()) {
114+
make_callback(handle);
115+
} else {
116+
// lock the callback mutex
117+
pthread_mutex_lock(&args->mutex);
118+
119+
// trigger the async callback
120+
uv_async_send(handle);
121+
122+
// wait for it to finish
123+
pthread_cond_wait(&args->cond, &args->mutex);
124+
125+
// unlock the callback mutex
126+
pthread_mutex_unlock(&args->mutex);
127+
}
128+
129+
// free the callback args
130+
delete args;
131+
}
132+
133+
static void close_callback(uv_handle_t* handle) {
134+
// free the callback handle
135+
free(handle);
136+
}
137+
138+
static void make_callback(uv_async_t* handle) {
139+
Isolate* isolate = Isolate::GetCurrent();
140+
v8::HandleScope scope(isolate);
141+
142+
struct callback_args* args = (struct callback_args*) handle->data;
143+
144+
// lock the mutex
145+
pthread_mutex_lock(&args->mutex);
146+
147+
// build the stack arguments
148+
Local<Array> argStack = Array::New(isolate, args->stack_size);
149+
for (size_t i = 0; i < args->stack_size; i++) {
150+
argStack->Set(i, String::NewFromUtf8(isolate, args->stack[i]));
151+
}
152+
153+
// collect all callback arguments
154+
Local<Value> argv[3] = {Number::New(isolate, args->signo), Number::New(isolate, args->addr), argStack};
155+
156+
// execute the callback function on the main threaod
157+
Local<Function>::New(isolate, *args->callback)->Call(isolate->GetCurrentContext()->Global(), 3, argv);
158+
159+
// broadcast that we're done with the callback
160+
pthread_cond_broadcast(&args->cond);
161+
162+
// unlock the mutex
163+
pthread_mutex_unlock(&args->mutex);
164+
}
165+
};
166+
167+
struct callback_helper* callback;
168+
#endif
169+
52170
char logPath[BUFF_SIZE];
53171

54172
static void buildFileName(char sbuff[BUFF_SIZE], int pid) {
@@ -112,9 +230,17 @@ SEGFAULT_HANDLER {
112230
backtrace_symbols_fd(array, size, STDERR_FD);
113231
#endif
114232

115-
// Exit violently
116233
CLOSE(fd);
117-
exit(-1);
234+
235+
#ifndef _WIN32
236+
if (callback) {
237+
// execute the callback and wait until it has completed
238+
callback->send(array, size, si->si_signo, (long)si->si_addr);
239+
240+
// release the callback
241+
delete callback;
242+
}
243+
#endif
118244

119245
#ifdef _WIN32
120246
return EXCEPTION_EXECUTE_HANDLER;
@@ -162,19 +288,31 @@ NAN_METHOD(RegisterHandler) {
162288
// if passed a path, we'll set the log name to whatever is provided
163289
// this will allow users to use the logs in error reporting without redirecting
164290
// sdterr
165-
logPath[0] = '\0';
166-
if (info.Length() == 1) {
167-
if (info[0]->IsString()) {
168-
v8::String::Utf8Value utf8Value(info[0]->ToString());
291+
292+
if (info.Length() > 0) {
293+
for (int i = 0; i < info.Length(); i++) {
294+
if (info[i]->IsString()) {
295+
String::Utf8Value utf8Value(info[i]->ToString());
169296

170297
// need to do a copy to make sure the string doesn't become a dangling pointer
171298
int len = utf8Value.length();
172299
len = len > BUFF_SIZE ? BUFF_SIZE : len;
173300

174301
strncpy(logPath, *utf8Value, len);
175302
logPath[127] = '\0';
176-
} else {
177-
return ThrowError("First argument must be a string.");
303+
304+
#ifndef _WIN32
305+
} else if (info[i]->IsFunction()) {
306+
if (callback) {
307+
// release previous callback
308+
delete callback;
309+
}
310+
311+
// create the new callback object
312+
callback = new callback_helper(Handle<Function>::Cast(info[i]));
313+
#endif
314+
315+
}
178316
}
179317
}
180318

@@ -185,7 +323,7 @@ NAN_METHOD(RegisterHandler) {
185323
memset(&sa, 0, sizeof(struct sigaction));
186324
sigemptyset(&sa.sa_mask);
187325
sa.sa_sigaction = segfault_handler;
188-
sa.sa_flags = SA_SIGINFO;
326+
sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
189327
sigaction(SIGSEGV, &sa, NULL);
190328
#endif
191329
}

0 commit comments

Comments
 (0)