Skip to content

Commit

Permalink
src: print arbitrary javascript exception value in node report
Browse files Browse the repository at this point in the history
  • Loading branch information
legendecas committed Mar 31, 2021
1 parent 3dee233 commit 68d2012
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 30 deletions.
67 changes: 43 additions & 24 deletions src/node_report.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ static void WriteNodeReport(Isolate* isolate,
const char* trigger,
const std::string& filename,
std::ostream& out,
Local<Object> error,
Local<Value> error,
bool compact);
static void PrintVersionInformation(JSONWriter* writer);
static void PrintJavaScriptErrorStack(JSONWriter* writer,
Isolate* isolate,
Local<Object> error,
Local<Value> error,
const char* trigger);
static void PrintJavaScriptErrorProperties(JSONWriter* writer,
Isolate* isolate,
Local<Object> error);
Local<Value> error);
static void PrintNativeStack(JSONWriter* writer);
static void PrintResourceUsage(JSONWriter* writer);
static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate);
Expand All @@ -84,7 +84,7 @@ std::string TriggerNodeReport(Isolate* isolate,
const char* message,
const char* trigger,
const std::string& name,
Local<Object> error) {
Local<Value> error) {
std::string filename;

// Determine the required report filename. In order of priority:
Expand Down Expand Up @@ -169,7 +169,7 @@ void GetNodeReport(Isolate* isolate,
Environment* env,
const char* message,
const char* trigger,
Local<Object> error,
Local<Value> error,
std::ostream& out) {
WriteNodeReport(isolate, env, message, trigger, "", out, error, false);
}
Expand All @@ -182,7 +182,7 @@ static void WriteNodeReport(Isolate* isolate,
const char* trigger,
const std::string& filename,
std::ostream& out,
Local<Object> error,
Local<Value> error,
bool compact) {
// Obtain the current time and the pid.
TIME_TYPE tm_struct;
Expand Down Expand Up @@ -474,13 +474,14 @@ static void PrintNetworkInterfaceInfo(JSONWriter* writer) {

static void PrintJavaScriptErrorProperties(JSONWriter* writer,
Isolate* isolate,
Local<Object> error) {
Local<Value> error) {
writer->json_objectstart("errorProperties");
if (!error.IsEmpty()) {
if (!error.IsEmpty() && error->IsObject()) {
TryCatch try_catch(isolate);
Local<Context> context = error->GetIsolate()->GetCurrentContext();
Local<Object> error_obj = error.As<Object>();
Local<Context> context = error_obj->GetIsolate()->GetCurrentContext();
Local<Array> keys;
if (!error->GetOwnPropertyNames(context).ToLocal(&keys)) {
if (!error_obj->GetOwnPropertyNames(context).ToLocal(&keys)) {
return writer->json_objectend(); // the end of 'errorProperties'
}
uint32_t keys_length = keys->Length();
Expand All @@ -491,7 +492,7 @@ static void PrintJavaScriptErrorProperties(JSONWriter* writer,
}
Local<Value> value;
Local<String> value_string;
if (!error->Get(context, key).ToLocal(&value) ||
if (!error_obj->Get(context, key).ToLocal(&value) ||
!value->ToString(context).ToLocal(&value_string)) {
continue;
}
Expand All @@ -505,26 +506,44 @@ static void PrintJavaScriptErrorProperties(JSONWriter* writer,
writer->json_objectend(); // the end of 'errorProperties'
}

static void ErrorToString(Isolate* isolate,
Local<Context> context,
Local<Value> error,
std::string* ss) {
if (error.IsEmpty()) {
return;
}

Local<Value> js_str;
if (error->IsSymbol() &&
error.As<v8::Symbol>()->ToDetailString(context).ToLocal(&js_str)) {
String::Utf8Value sv(isolate, js_str);
*ss = std::string(*sv, sv.length());
} else if (!error->IsObject() && error->ToString(context).ToLocal(&js_str)) {
String::Utf8Value sv(isolate, js_str);
*ss = std::string(*sv, sv.length());
} else if (error->IsObject() &&
error.As<Object>()
->Get(context, node::FIXED_ONE_BYTE_STRING(isolate, "stack"))
.ToLocal(&js_str)) {
String::Utf8Value sv(isolate, js_str);
*ss = std::string(*sv, sv.length());
}
}

// Report the JavaScript stack.
static void PrintJavaScriptErrorStack(JSONWriter* writer,
Isolate* isolate,
Local<Object> error,
const char* trigger) {
Local<Value> stackstr;
std::string ss = "";
Isolate* isolate,
Local<Value> error,
const char* trigger) {
TryCatch try_catch(isolate);
Local<Context> context = isolate->GetCurrentContext();
std::string ss = "";
if ((!strcmp(trigger, "FatalError")) ||
(!strcmp(trigger, "Signal"))) {
ss = "No stack.\nUnavailable.\n";
} else if (!error.IsEmpty() &&
error
->Get(isolate->GetCurrentContext(),
node::FIXED_ONE_BYTE_STRING(isolate,
"stack"))
.ToLocal(&stackstr)) {
String::Utf8Value sv(isolate, stackstr);
ss = std::string(*sv, sv.length());
}
ErrorToString(isolate, context, error, &ss);
int line = ss.find('\n');
if (line == -1) {
writer->json_keyvalue("message", ss);
Expand Down
4 changes: 2 additions & 2 deletions src/node_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ std::string TriggerNodeReport(v8::Isolate* isolate,
const char* message,
const char* trigger,
const std::string& name,
v8::Local<v8::Object> error);
v8::Local<v8::Value> error);
void GetNodeReport(v8::Isolate* isolate,
node::Environment* env,
const char* message,
const char* trigger,
v8::Local<v8::Object> error,
v8::Local<v8::Value> error,
std::ostream& out);

// Function declarations - utility functions in src/node_report_utils.cc
Expand Down
8 changes: 4 additions & 4 deletions src/node_report_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@ void WriteReport(const FunctionCallbackInfo<Value>& info) {
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
std::string filename;
Local<Object> error;
Local<Value> error;

CHECK_EQ(info.Length(), 4);
String::Utf8Value message(isolate, info[0].As<String>());
String::Utf8Value trigger(isolate, info[1].As<String>());

if (info[2]->IsString())
filename = *String::Utf8Value(isolate, info[2]);
if (!info[3].IsEmpty() && info[3]->IsObject())
error = info[3].As<Object>();
if (!info[3].IsEmpty())
error = info[3];
else
error = Local<Object>();
error = Local<Value>();

filename = TriggerNodeReport(
isolate, env, *message, *trigger, filename, error);
Expand Down
24 changes: 24 additions & 0 deletions test/report/test-report-uncaught-exception-primitives.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Flags: --report-uncaught-exception
'use strict';
// Test producing a report on uncaught exception.
const common = require('../common');
const assert = require('assert');
const helper = require('../common/report');
const tmpdir = require('../common/tmpdir');

const exception = 1;

tmpdir.refresh();
process.report.directory = tmpdir.path;

process.on('uncaughtException', common.mustCall((err) => {
assert.strictEqual(err, exception);
const reports = helper.findReports(process.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);
console.log(reports[0]);
helper.validate(reports[0], [
['javascriptStack.message', `${exception}`],
]);
}));

throw exception;
24 changes: 24 additions & 0 deletions test/report/test-report-uncaught-exception-unable-tostring.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Flags: --report-uncaught-exception
'use strict';
// Test producing a report on uncaught exception.
const common = require('../common');
const assert = require('assert');
const helper = require('../common/report');
const tmpdir = require('../common/tmpdir');

const exception = Symbol('foobar');

tmpdir.refresh();
process.report.directory = tmpdir.path;

process.on('uncaughtException', common.mustCall((err) => {
assert.strictEqual(err, exception);
const reports = helper.findReports(process.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);
console.log(reports[0]);
helper.validate(reports[0], [
['javascriptStack.message', "Symbol('foobar')"],
]);
}));

throw exception;

0 comments on commit 68d2012

Please sign in to comment.