Skip to content

Commit 17856f1

Browse files
legendecastargos
authored andcommitted
src: print arbitrary javascript exception value in node report
PR-URL: #38009 Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent dd61a26 commit 17856f1

6 files changed

+118
-35
lines changed

lib/internal/process/execution.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,13 @@ function createOnGlobalUncaughtException() {
152152
try {
153153
const report = internalBinding('report');
154154
if (report != null && report.shouldReportOnUncaughtException()) {
155-
report.writeReport(er ? er.message : 'Exception',
156-
'Exception',
157-
null,
158-
er ? er : {});
155+
report.writeReport(
156+
typeof er?.message === 'string' ?
157+
er.message :
158+
'Exception',
159+
'Exception',
160+
null,
161+
er ? er : {});
159162
}
160163
} catch {} // Ignore the exception. Diagnostic reporting is unavailable.
161164
}

src/node_report.cc

+55-25
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ using node::TIME_TYPE;
3939
using node::worker::Worker;
4040
using v8::Array;
4141
using v8::Context;
42+
using v8::HandleScope;
4243
using v8::HeapSpaceStatistics;
4344
using v8::HeapStatistics;
4445
using v8::Isolate;
46+
using v8::Just;
4547
using v8::Local;
48+
using v8::Maybe;
49+
using v8::MaybeLocal;
50+
using v8::Nothing;
4651
using v8::Object;
4752
using v8::String;
4853
using v8::TryCatch;
@@ -58,16 +63,16 @@ static void WriteNodeReport(Isolate* isolate,
5863
const char* trigger,
5964
const std::string& filename,
6065
std::ostream& out,
61-
Local<Object> error,
66+
Local<Value> error,
6267
bool compact);
6368
static void PrintVersionInformation(JSONWriter* writer);
6469
static void PrintJavaScriptErrorStack(JSONWriter* writer,
6570
Isolate* isolate,
66-
Local<Object> error,
71+
Local<Value> error,
6772
const char* trigger);
6873
static void PrintJavaScriptErrorProperties(JSONWriter* writer,
6974
Isolate* isolate,
70-
Local<Object> error);
75+
Local<Value> error);
7176
static void PrintNativeStack(JSONWriter* writer);
7277
static void PrintResourceUsage(JSONWriter* writer);
7378
static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate);
@@ -84,7 +89,7 @@ std::string TriggerNodeReport(Isolate* isolate,
8489
const char* message,
8590
const char* trigger,
8691
const std::string& name,
87-
Local<Object> error) {
92+
Local<Value> error) {
8893
std::string filename;
8994

9095
// Determine the required report filename. In order of priority:
@@ -169,7 +174,7 @@ void GetNodeReport(Isolate* isolate,
169174
Environment* env,
170175
const char* message,
171176
const char* trigger,
172-
Local<Object> error,
177+
Local<Value> error,
173178
std::ostream& out) {
174179
WriteNodeReport(isolate, env, message, trigger, "", out, error, false);
175180
}
@@ -182,7 +187,7 @@ static void WriteNodeReport(Isolate* isolate,
182187
const char* trigger,
183188
const std::string& filename,
184189
std::ostream& out,
185-
Local<Object> error,
190+
Local<Value> error,
186191
bool compact) {
187192
// Obtain the current time and the pid.
188193
TIME_TYPE tm_struct;
@@ -474,13 +479,14 @@ static void PrintNetworkInterfaceInfo(JSONWriter* writer) {
474479

475480
static void PrintJavaScriptErrorProperties(JSONWriter* writer,
476481
Isolate* isolate,
477-
Local<Object> error) {
482+
Local<Value> error) {
478483
writer->json_objectstart("errorProperties");
479-
if (!error.IsEmpty()) {
484+
if (!error.IsEmpty() && error->IsObject()) {
480485
TryCatch try_catch(isolate);
481-
Local<Context> context = error->GetIsolate()->GetCurrentContext();
486+
Local<Object> error_obj = error.As<Object>();
487+
Local<Context> context = error_obj->GetIsolate()->GetCurrentContext();
482488
Local<Array> keys;
483-
if (!error->GetOwnPropertyNames(context).ToLocal(&keys)) {
489+
if (!error_obj->GetOwnPropertyNames(context).ToLocal(&keys)) {
484490
return writer->json_objectend(); // the end of 'errorProperties'
485491
}
486492
uint32_t keys_length = keys->Length();
@@ -491,7 +497,7 @@ static void PrintJavaScriptErrorProperties(JSONWriter* writer,
491497
}
492498
Local<Value> value;
493499
Local<String> value_string;
494-
if (!error->Get(context, key).ToLocal(&value) ||
500+
if (!error_obj->Get(context, key).ToLocal(&value) ||
495501
!value->ToString(context).ToLocal(&value_string)) {
496502
continue;
497503
}
@@ -505,26 +511,50 @@ static void PrintJavaScriptErrorProperties(JSONWriter* writer,
505511
writer->json_objectend(); // the end of 'errorProperties'
506512
}
507513

514+
static Maybe<std::string> ErrorToString(Isolate* isolate,
515+
Local<Context> context,
516+
Local<Value> error) {
517+
if (error.IsEmpty()) {
518+
return Nothing<std::string>();
519+
}
520+
521+
MaybeLocal<String> maybe_str;
522+
// `ToString` is not available to Symbols.
523+
if (error->IsSymbol()) {
524+
maybe_str = error.As<v8::Symbol>()->ToDetailString(context);
525+
} else if (!error->IsObject()) {
526+
maybe_str = error->ToString(context);
527+
} else if (error->IsObject()) {
528+
MaybeLocal<Value> stack = error.As<Object>()->Get(
529+
context, node::FIXED_ONE_BYTE_STRING(isolate, "stack"));
530+
if (!stack.IsEmpty() && stack.ToLocalChecked()->IsString()) {
531+
maybe_str = stack.ToLocalChecked().As<String>();
532+
}
533+
}
534+
535+
Local<String> js_str;
536+
if (!maybe_str.ToLocal(&js_str)) {
537+
return Nothing<std::string>();
538+
}
539+
String::Utf8Value sv(isolate, js_str);
540+
return Just<>(std::string(*sv, sv.length()));
541+
}
542+
508543
// Report the JavaScript stack.
509544
static void PrintJavaScriptErrorStack(JSONWriter* writer,
510-
Isolate* isolate,
511-
Local<Object> error,
512-
const char* trigger) {
513-
Local<Value> stackstr;
514-
std::string ss = "";
545+
Isolate* isolate,
546+
Local<Value> error,
547+
const char* trigger) {
515548
TryCatch try_catch(isolate);
549+
HandleScope scope(isolate);
550+
Local<Context> context = isolate->GetCurrentContext();
551+
std::string ss = "";
516552
if ((!strcmp(trigger, "FatalError")) ||
517-
(!strcmp(trigger, "Signal"))) {
553+
(!strcmp(trigger, "Signal")) ||
554+
(!ErrorToString(isolate, context, error).To(&ss))) {
518555
ss = "No stack.\nUnavailable.\n";
519-
} else if (!error.IsEmpty() &&
520-
error
521-
->Get(isolate->GetCurrentContext(),
522-
node::FIXED_ONE_BYTE_STRING(isolate,
523-
"stack"))
524-
.ToLocal(&stackstr)) {
525-
String::Utf8Value sv(isolate, stackstr);
526-
ss = std::string(*sv, sv.length());
527556
}
557+
528558
int line = ss.find('\n');
529559
if (line == -1) {
530560
writer->json_keyvalue("message", ss);

src/node_report.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ std::string TriggerNodeReport(v8::Isolate* isolate,
2020
const char* message,
2121
const char* trigger,
2222
const std::string& name,
23-
v8::Local<v8::Object> error);
23+
v8::Local<v8::Value> error);
2424
void GetNodeReport(v8::Isolate* isolate,
2525
node::Environment* env,
2626
const char* message,
2727
const char* trigger,
28-
v8::Local<v8::Object> error,
28+
v8::Local<v8::Value> error,
2929
std::ostream& out);
3030

3131
// Function declarations - utility functions in src/node_report_utils.cc

src/node_report_module.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,18 @@ void WriteReport(const FunctionCallbackInfo<Value>& info) {
3232
Isolate* isolate = env->isolate();
3333
HandleScope scope(isolate);
3434
std::string filename;
35-
Local<Object> error;
35+
Local<Value> error;
3636

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

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

4848
filename = TriggerNodeReport(
4949
isolate, env, *message, *trigger, filename, error);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Flags: --report-uncaught-exception
2+
'use strict';
3+
// Test producing a report on uncaught exception.
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const helper = require('../common/report');
7+
const tmpdir = require('../common/tmpdir');
8+
9+
const exception = 1;
10+
11+
tmpdir.refresh();
12+
process.report.directory = tmpdir.path;
13+
14+
process.on('uncaughtException', common.mustCall((err) => {
15+
assert.strictEqual(err, exception);
16+
const reports = helper.findReports(process.pid, tmpdir.path);
17+
assert.strictEqual(reports.length, 1);
18+
console.log(reports[0]);
19+
helper.validate(reports[0], [
20+
['header.event', 'Exception'],
21+
['javascriptStack.message', `${exception}`],
22+
]);
23+
}));
24+
25+
throw exception;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Flags: --report-uncaught-exception
2+
'use strict';
3+
// Test producing a report on uncaught exception.
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const helper = require('../common/report');
7+
const tmpdir = require('../common/tmpdir');
8+
9+
const exception = Symbol('foobar');
10+
11+
tmpdir.refresh();
12+
process.report.directory = tmpdir.path;
13+
14+
process.on('uncaughtException', common.mustCall((err) => {
15+
assert.strictEqual(err, exception);
16+
const reports = helper.findReports(process.pid, tmpdir.path);
17+
assert.strictEqual(reports.length, 1);
18+
console.log(reports[0]);
19+
helper.validate(reports[0], [
20+
['header.event', 'Exception'],
21+
['javascriptStack.message', 'Symbol(foobar)'],
22+
]);
23+
}));
24+
25+
throw exception;

0 commit comments

Comments
 (0)