blob: cf17db62db235b760be62223a95d7fd051e9b72e [file] [log] [blame] [edit]
#include "env.h"
#include "node_errors.h"
#include "node_internals.h"
#include "node_options.h"
#include "node_report.h"
#include "util.h"
#include "env-inl.h"
#include "handle_wrap.h"
#include "node_buffer.h"
#include "stream_base-inl.h"
#include "stream_wrap.h"
#include "util-inl.h"
#include <v8.h>
#include <atomic>
#include <sstream>
namespace report {
using node::Environment;
using node::FIXED_ONE_BYTE_STRING;
using node::PerIsolateOptions;
using node::Utf8Value;
using v8::Array;
using v8::Boolean;
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::V8;
using v8::Value;
// External JavaScript API for triggering a report
void TriggerReport(const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
std::string filename;
Local<String> stackstr;
CHECK_EQ(info.Length(), 4);
String::Utf8Value message(isolate, info[0].As<String>());
String::Utf8Value trigger(isolate, info[1].As<String>());
stackstr = info[3].As<String>();
if (info[2]->IsString())
filename = *String::Utf8Value(isolate, info[2]);
filename = TriggerNodeReport(
isolate, env, *message, *trigger, filename, stackstr);
// Return value is the report filename
info.GetReturnValue().Set(
String::NewFromUtf8(isolate, filename.c_str(), v8::NewStringType::kNormal)
.ToLocalChecked());
}
// External JavaScript API for returning a report
void GetReport(const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
std::ostringstream out;
GetNodeReport(
isolate, env, "JavaScript API", __func__, info[0].As<String>(), out);
// Return value is the contents of a report as a string.
info.GetReturnValue().Set(String::NewFromUtf8(isolate,
out.str().c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
}
// A method to sync up data elements in the JS land with its
// corresponding elements in the C++ world. Required because
// (i) the tunables are first intercepted through the CLI but
// later modified via APIs. (ii) the report generation events
// are controlled partly from C++ and partly from JS.
void SyncConfig(const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
Local<Context> context = env->context();
std::shared_ptr<PerIsolateOptions> options = env->isolate_data()->options();
CHECK_EQ(info.Length(), 2);
Local<Object> obj;
if (!info[0]->ToObject(context).ToLocal(&obj)) return;
bool sync = info[1].As<Boolean>()->Value();
// Events array
Local<String> eventskey = FIXED_ONE_BYTE_STRING(env->isolate(), "events");
Local<Value> events_unchecked;
if (!obj->Get(context, eventskey).ToLocal(&events_unchecked)) return;
Local<Array> events;
if (events_unchecked->IsUndefined() || events_unchecked->IsNull()) {
events_unchecked = Array::New(env->isolate(), 0);
if (obj->Set(context, eventskey, events_unchecked).IsNothing()) return;
}
events = events_unchecked.As<Array>();
// Signal
Local<String> signalkey = env->signal_string();
Local<Value> signal_unchecked;
if (!obj->Get(context, signalkey).ToLocal(&signal_unchecked)) return;
Local<String> signal;
if (signal_unchecked->IsUndefined() || signal_unchecked->IsNull())
signal_unchecked = signalkey;
signal = signal_unchecked.As<String>();
Utf8Value signalstr(env->isolate(), signal);
// Report file
Local<String> filekey = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
Local<Value> file_unchecked;
if (!obj->Get(context, filekey).ToLocal(&file_unchecked)) return;
Local<String> file;
if (file_unchecked->IsUndefined() || file_unchecked->IsNull())
file_unchecked = filekey;
file = file_unchecked.As<String>();
Utf8Value filestr(env->isolate(), file);
// Report file path
Local<String> pathkey = FIXED_ONE_BYTE_STRING(env->isolate(), "path");
Local<Value> path_unchecked;
if (!obj->Get(context, pathkey).ToLocal(&path_unchecked)) return;
Local<String> path;
if (path_unchecked->IsUndefined() || path_unchecked->IsNull())
path_unchecked = pathkey;
path = path_unchecked.As<String>();
Utf8Value pathstr(env->isolate(), path);
if (sync) {
static const std::string e = "exception";
static const std::string s = "signal";
static const std::string f = "fatalerror";
for (uint32_t i = 0; i < events->Length(); i++) {
Local<Value> v;
if (!events->Get(context, i).ToLocal(&v)) return;
Local<String> elem;
if (!v->ToString(context).ToLocal(&elem)) return;
String::Utf8Value buf(env->isolate(), elem);
if (*buf == e) {
options->report_uncaught_exception = true;
} else if (*buf == s) {
options->report_on_signal = true;
} else if (*buf == f) {
options->report_on_fatalerror = true;
}
}
CHECK_NOT_NULL(*signalstr);
options->report_signal = *signalstr;
CHECK_NOT_NULL(*filestr);
options->report_filename = *filestr;
CHECK_NOT_NULL(*pathstr);
options->report_directory = *pathstr;
} else {
int i = 0;
if (options->report_uncaught_exception &&
events
->Set(context,
i++,
FIXED_ONE_BYTE_STRING(env->isolate(), "exception"))
.IsNothing())
return;
if (options->report_on_signal &&
events
->Set(context, i++, FIXED_ONE_BYTE_STRING(env->isolate(), "signal"))
.IsNothing())
return;
if (options->report_on_fatalerror &&
events
->Set(
context, i, FIXED_ONE_BYTE_STRING(env->isolate(), "fatalerror"))
.IsNothing())
return;
Local<Value> signal_value;
Local<Value> file_value;
Local<Value> path_value;
std::string signal = options->report_signal;
if (signal.empty()) signal = "SIGUSR2";
if (!node::ToV8Value(context, signal).ToLocal(&signal_value))
return;
if (!obj->Set(context, signalkey, signal_value).FromJust()) return;
if (!node::ToV8Value(context, options->report_filename)
.ToLocal(&file_value))
return;
if (!obj->Set(context, filekey, file_value).FromJust()) return;
if (!node::ToV8Value(context, options->report_directory)
.ToLocal(&path_value))
return;
if (!obj->Set(context, pathkey, path_value).FromJust()) return;
}
}
static void Initialize(Local<Object> exports,
Local<Value> unused,
Local<Context> context) {
Environment* env = Environment::GetCurrent(context);
env->SetMethod(exports, "triggerReport", TriggerReport);
env->SetMethod(exports, "getReport", GetReport);
env->SetMethod(exports, "syncConfig", SyncConfig);
}
} // namespace report
NODE_MODULE_CONTEXT_AWARE_INTERNAL(report, report::Initialize)