Skip to content

Commit

Permalink
Initial drop of support for Node.js 6 on zOS
Browse files Browse the repository at this point in the history
  • Loading branch information
rnchamberlain committed Sep 29, 2017
1 parent dfee598 commit 7e1506a
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and calls to a JavaScript API.

Supports Node.js versions 4, 6 and 8 on AIX, Linux, MacOS, SmartOS and Windows.

**This fork of node-report also supports Node.js version 6 on zOS**

## Usage

```bash
Expand Down
83 changes: 80 additions & 3 deletions src/module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#include <sstream>

// Ignore the code-page convert pragma on platforms other than zOS
#pragma warning (disable: 4068)

namespace nodereport {

// Internal/static function declarations
Expand Down Expand Up @@ -43,6 +46,10 @@ static v8::Isolate* node_isolate;
extern std::string version_string;
extern std::string commandline_string;

#ifdef __MVS__
static bool terminate_loop = false;
#endif

/*******************************************************************************
* External JavaScript API for triggering a report
*
Expand Down Expand Up @@ -92,8 +99,20 @@ NAN_METHOD(GetReport) {
}

GetNodeReport(isolate, kJavaScript, "JavaScript API", __func__, error, out);

#ifdef __MVS__
// Convert the report back from EBCDIC to ASCII before returning across JS API
std::string report_string = out.str();
char* buffer = (char*) malloc(report_string.length() + 1);
strcpy(buffer, report_string.c_str());
__etoa(buffer);
// Return value is the contents of a report as a string.
info.GetReturnValue().Set(Nan::New(buffer).ToLocalChecked());
free(buffer);
#else
// Return value is the contents of a report as a string.
info.GetReturnValue().Set(Nan::New(out.str()).ToLocalChecked());
#endif
}

/*******************************************************************************
Expand Down Expand Up @@ -163,6 +182,32 @@ NAN_METHOD(SetVerbose) {
* external signals
******************************************************************************/
static void OnFatalError(const char* location, const char* message) {
#pragma convert("IBM-1047")
#ifdef __MVS__
// Convert location and message strings to EBCDIC
char* location_ebcdic = nullptr;
char* message_ebcdic = (char*) malloc(strlen(message) + 1);
strcpy(message_ebcdic, message);
__atoe(message_ebcdic);

if (location) {
char* location_ebcdic = (char*) malloc(strlen(location) + 1);
strcpy(location_ebcdic, location);
__atoe(location_ebcdic);
fprintf(stderr, "FATAL ERROR: %s %s\n", location_ebcdic, message_ebcdic);
} else {
fprintf(stderr, "FATAL ERROR: %s\n", message_ebcdic);
}
// Trigger report if requested
if (nodereport_events & NR_FATALERROR) {
// Note: pass the message string in ASCII here, as it gets converted inside TriggerNodeReport()
TriggerNodeReport(Isolate::GetCurrent(), kFatalError, message, location_ebcdic, nullptr, MaybeLocal<Value>());
}
if (location) {
free(location_ebcdic);
}
free(message_ebcdic);
#else
if (location) {
fprintf(stderr, "FATAL ERROR: %s %s\n", location, message);
} else {
Expand All @@ -172,24 +217,36 @@ static void OnFatalError(const char* location, const char* message) {
if (nodereport_events & NR_FATALERROR) {
TriggerNodeReport(Isolate::GetCurrent(), kFatalError, message, location, nullptr, MaybeLocal<Value>());
}
#endif
fflush(stderr);
raise(SIGABRT);
#pragma convert(pop)
}

bool OnUncaughtException(v8::Isolate* isolate) {
// Trigger report if requested
if (nodereport_events & NR_EXCEPTION) {
TriggerNodeReport(isolate, kException, "exception", __func__, nullptr, MaybeLocal<Value>());
}
#pragma convert("IBM-1047")
if ((commandline_string.find("abort-on-uncaught-exception") != std::string::npos) ||
(commandline_string.find("abort_on_uncaught_exception") != std::string::npos)) {
return true; // abort required
}
// On versions earlier than 5.4, V8 does not provide the default behaviour
// for uncaught exception on return from this callback. Default behaviour is
// to print a stack trace and exit with rc=1, so we need to mimic that here.
int v8_major, v8_minor;
if (sscanf(v8::V8::GetVersion(), "%d.%d", &v8_major, &v8_minor) == 2) {
int v8_major, v8_minor, rc;
#ifdef __MVS__
char* buffer = (char*) malloc(strlen(v8::V8::GetVersion()) + 1);
strcpy(buffer, v8::V8::GetVersion());
__atoe(buffer);
rc = sscanf(buffer, "%d.%d", &v8_major, &v8_minor);
free(buffer);
#else
rc = sscanf(v8::V8::GetVersion(), "%d.%d", &v8_major, &v8_minor);
#endif
if (rc == 2) { // Got the major and minor version values
if (v8_major < 5 || (v8_major == 5 && v8_minor < 4)) {
fprintf(stderr, "\nUncaught exception at:\n");
#ifdef _WIN32
Expand All @@ -204,6 +261,7 @@ bool OnUncaughtException(v8::Isolate* isolate) {
}
}
return false;
#pragma convert(pop)
}

#ifdef _WIN32
Expand Down Expand Up @@ -236,6 +294,7 @@ static void PrintStackFromStackTrace(Isolate* isolate, FILE* fp) {
#else
// Signal handling functions, not supported on Windows
static void SignalDumpInterruptCallback(Isolate* isolate, void* data) {
#pragma convert("IBM-1047")
if (report_signal != 0) {
if (nodereport_verbose) {
fprintf(stdout, "node-report: SignalDumpInterruptCallback handling signal\n");
Expand All @@ -249,8 +308,10 @@ static void SignalDumpInterruptCallback(Isolate* isolate, void* data) {
}
report_signal = 0;
}
#pragma convert(pop)
}
static void SignalDumpAsyncCallback(uv_async_t* handle) {
#pragma convert("IBM-1047")
if (report_signal != 0) {
if (nodereport_verbose) {
fprintf(stdout, "node-report: SignalDumpAsyncCallback handling signal\n");
Expand All @@ -264,6 +325,7 @@ static void SignalDumpAsyncCallback(uv_async_t* handle) {
}
report_signal = 0;
}
#pragma convert(pop)
}

/*******************************************************************************
Expand Down Expand Up @@ -299,6 +361,7 @@ static void SignalDump(int signo) {

// Utility function to start a watchdog thread - used for processing signals
static int StartWatchdogThread(void* (*thread_main)(void* unused)) {
#pragma convert("IBM-1047")
pthread_attr_t attr;
pthread_attr_init(&attr);
// Minimise the stack size, except on FreeBSD where the minimum is too low
Expand All @@ -320,10 +383,12 @@ static int StartWatchdogThread(void* (*thread_main)(void* unused)) {
return -err;
}
return 0;
#pragma convert(pop)
}

// Watchdog thread implementation for signal-triggered report
inline void* ReportSignalThreadMain(void* unused) {
#pragma convert("IBM-1047")
for (;;) {
uv_sem_wait(&report_semaphore);
if (nodereport_verbose) {
Expand All @@ -337,27 +402,37 @@ inline void* ReportSignalThreadMain(void* unused) {
uv_async_send(&nodereport_trigger_async);
}
uv_mutex_unlock(&node_isolate_mutex);
#ifdef __MVS__
if (terminate_loop) break; // avoids WARNING CCN1109: Infinite loop detected
#endif
}
return nullptr;
#pragma convert(pop)
}

// Utility function to initialise signal handlers and threads
static void SetupSignalHandler() {
int rc = uv_sem_init(&report_semaphore, 0);
if (rc != 0) {
#pragma convert("IBM-1047")
fprintf(stderr, "node-report: initialization failed, uv_sem_init() returned %d\n", rc);
#pragma convert(pop)
Nan::ThrowError("node-report: initialization failed, uv_sem_init() returned error\n");
}
rc = uv_mutex_init(&node_isolate_mutex);
if (rc != 0) {
#pragma convert("IBM-1047")
fprintf(stderr, "node-report: initialization failed, uv_mutex_init() returned %d\n", rc);
#pragma convert(pop)
Nan::ThrowError("node-report: initialization failed, uv_mutex_init() returned error\n");
}

if (StartWatchdogThread(ReportSignalThreadMain) == 0) {
rc = uv_async_init(uv_default_loop(), &nodereport_trigger_async, SignalDumpAsyncCallback);
if (rc != 0) {
#pragma convert("IBM-1047")
fprintf(stderr, "node-report: initialization failed, uv_async_init() returned %d\n", rc);
#pragma convert(pop)
Nan::ThrowError("node-report: initialization failed, uv_async_init() returned error\n");
}
uv_unref(reinterpret_cast<uv_handle_t*>(&nodereport_trigger_async));
Expand All @@ -377,7 +452,7 @@ void Initialize(v8::Local<v8::Object> exports) {

SetLoadTime();
SetVersionString(isolate);
SetCommandLine();
SetCommandLine(isolate);

const char* verbose_switch = secure_getenv("NODEREPORT_VERBOSE");
if (verbose_switch != nullptr) {
Expand Down Expand Up @@ -437,6 +512,7 @@ void Initialize(v8::Local<v8::Object> exports) {
exports->Set(Nan::New("setVerbose").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(SetVerbose)->GetFunction());

#pragma convert("IBM-1047")
if (nodereport_verbose) {
#ifdef _WIN32
fprintf(stdout, "node-report: initialization complete, event flags: %#x\n",
Expand All @@ -446,6 +522,7 @@ void Initialize(v8::Local<v8::Object> exports) {
nodereport_events, nodereport_signal);
#endif
}
#pragma convert(pop)
}

NODE_MODULE(nodereport, Initialize)
Expand Down
Loading

0 comments on commit 7e1506a

Please sign in to comment.