| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a76fa60 commit d3737a1
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -153,7 +153,7 @@ function createOnGlobalUncaughtException() { | |||
| 153 | 153 | report.writeReport(er ? er.message : 'Exception', | |
| 154 | 154 | 'Exception', | |
| 155 | 155 | null, | |
| 156 | - er ? er.stack : undefined); | ||
| 156 | + er ? er : {}); | ||
| 157 | 157 | } | |
| 158 | 158 | } catch {} // Ignore the exception. Diagnostic reporting is unavailable. | |
| 159 | 159 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,15 +25,15 @@ const report = { | |||
| 25 | 25 | throw new ERR_INVALID_ARG_TYPE('err', 'Object', err); | |
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | - return nr.writeReport('JavaScript API', 'API', file, err.stack); | ||
| 28 | + return nr.writeReport('JavaScript API', 'API', file, err); | ||
| 29 | 29 | }, | |
| 30 | 30 | getReport(err) { | |
| 31 | 31 | if (err === undefined) | |
| 32 | 32 | err = new ERR_SYNTHETIC(); | |
| 33 | 33 | else if (err === null || typeof err !== 'object') | |
| 34 | 34 | throw new ERR_INVALID_ARG_TYPE('err', 'Object', err); | |
| 35 | 35 | ||
| 36 | - return JSONParse(nr.getReport(err.stack)); | ||
| 36 | + return JSONParse(nr.getReport(err)); | ||
| 37 | 37 | }, | |
| 38 | 38 | get directory() { | |
| 39 | 39 | return nr.getDirectory(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -418,7 +418,7 @@ void OnFatalError(const char* location, const char* message) { | |||
| 418 | 418 | ||
| 419 | 419 | if (report_on_fatalerror) { | |
| 420 | 420 | report::TriggerNodeReport( | |
| 421 | - isolate, env, message, "FatalError", "", Local<String>()); | ||
| 421 | + isolate, env, message, "FatalError", "", Local<Object>()); | ||
| 422 | 422 | } | |
| 423 | 423 | ||
| 424 | 424 | fflush(stderr); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -37,11 +37,18 @@ using node::Mutex; | |||
| 37 | 37 | using node::NativeSymbolDebuggingContext; | |
| 38 | 38 | using node::TIME_TYPE; | |
| 39 | 39 | using node::worker::Worker; | |
| 40 | + using v8::Array; | ||
| 41 | + using v8::Context; | ||
| 40 | 42 | using v8::HeapSpaceStatistics; | |
| 41 | 43 | using v8::HeapStatistics; | |
| 42 | 44 | using v8::Isolate; | |
| 43 | 45 | using v8::Local; | |
| 46 | + using v8::Number; | ||
| 47 | + using v8::Object; | ||
| 48 | + using v8::StackTrace; | ||
| 44 | 49 | using v8::String; | |
| 50 | + using v8::TryCatch; | ||
| 51 | + using v8::Value; | ||
| 45 | 52 | using v8::V8; | |
| 46 | 53 | ||
| 47 | 54 | namespace per_process = node::per_process; | |
@@ -53,13 +60,16 @@ static void WriteNodeReport(Isolate* isolate, | |||
| 53 | 60 | const char* trigger, | |
| 54 | 61 | const std::string& filename, | |
| 55 | 62 | std::ostream& out, | |
| 56 | - Local<String> stackstr, | ||
| 63 | + Local<Object> error, | ||
| 57 | 64 | bool compact); | |
| 58 | 65 | static void PrintVersionInformation(JSONWriter* writer); | |
| 59 | - static void PrintJavaScriptStack(JSONWriter* writer, | ||
| 60 | - Isolate* isolate, | ||
| 61 | - Local<String> stackstr, | ||
| 62 | - const char* trigger); | ||
| 66 | + static void PrintJavaScriptErrorStack(JSONWriter* writer, | ||
| 67 | + Isolate* isolate, | ||
| 68 | + Local<Object> error, | ||
| 69 | + const char* trigger); | ||
| 70 | + static void PrintJavaScriptErrorProperties(JSONWriter* writer, | ||
| 71 | + Isolate* isolate, | ||
| 72 | + Local<Object> error); | ||
| 63 | 73 | static void PrintNativeStack(JSONWriter* writer); | |
| 64 | 74 | static void PrintResourceUsage(JSONWriter* writer); | |
| 65 | 75 | static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate); | |
@@ -76,7 +86,7 @@ std::string TriggerNodeReport(Isolate* isolate, | |||
| 76 | 86 | const char* message, | |
| 77 | 87 | const char* trigger, | |
| 78 | 88 | const std::string& name, | |
| 79 | - Local<String> stackstr) { | ||
| 89 | + Local<Object> error) { | ||
| 80 | 90 | std::string filename; | |
| 81 | 91 | ||
| 82 | 92 | // Determine the required report filename. In order of priority: | |
@@ -142,7 +152,7 @@ std::string TriggerNodeReport(Isolate* isolate, | |||
| 142 | 152 | compact = per_process::cli_options->report_compact; | |
| 143 | 153 | } | |
| 144 | 154 | WriteNodeReport(isolate, env, message, trigger, filename, *outstream, | |
| 145 | - stackstr, compact); | ||
| 155 | + error, compact); | ||
| 146 | 156 | ||
| 147 | 157 | // Do not close stdout/stderr, only close files we opened. | |
| 148 | 158 | if (outfile.is_open()) { | |
@@ -161,9 +171,9 @@ void GetNodeReport(Isolate* isolate, | |||
| 161 | 171 | Environment* env, | |
| 162 | 172 | const char* message, | |
| 163 | 173 | const char* trigger, | |
| 164 | - Local<String> stackstr, | ||
| 174 | + Local<Object> error, | ||
| 165 | 175 | std::ostream& out) { | |
| 166 | - WriteNodeReport(isolate, env, message, trigger, "", out, stackstr, false); | ||
| 176 | + WriteNodeReport(isolate, env, message, trigger, "", out, error, false); | ||
| 167 | 177 | } | |
| 168 | 178 | ||
| 169 | 179 | // Internal function to coordinate and write the various | |
@@ -174,7 +184,7 @@ static void WriteNodeReport(Isolate* isolate, | |||
| 174 | 184 | const char* trigger, | |
| 175 | 185 | const std::string& filename, | |
| 176 | 186 | std::ostream& out, | |
| 177 | - Local<String> stackstr, | ||
| 187 | + Local<Object> error, | ||
| 178 | 188 | bool compact) { | |
| 179 | 189 | // Obtain the current time and the pid. | |
| 180 | 190 | TIME_TYPE tm_struct; | |
@@ -259,8 +269,13 @@ static void WriteNodeReport(Isolate* isolate, | |||
| 259 | 269 | PrintVersionInformation(&writer); | |
| 260 | 270 | writer.json_objectend(); | |
| 261 | 271 | ||
| 262 | - // Report summary JavaScript stack backtrace | ||
| 263 | - PrintJavaScriptStack(&writer, isolate, stackstr, trigger); | ||
| 272 | + writer.json_objectstart("javascriptStack"); | ||
| 273 | + // Report summary JavaScript error stack backtrace | ||
| 274 | + PrintJavaScriptErrorStack(&writer, isolate, error, trigger); | ||
| 275 | + | ||
| 276 | + // Report summary JavaScript error properties backtrace | ||
| 277 | + PrintJavaScriptErrorProperties(&writer, isolate, error); | ||
| 278 | + writer.json_objectend(); // the end of 'javascriptStack' | ||
| 264 | 279 | ||
| 265 | 280 | // Report native stack backtrace | |
| 266 | 281 | PrintNativeStack(&writer); | |
@@ -301,7 +316,7 @@ static void WriteNodeReport(Isolate* isolate, | |||
| 301 | 316 | env, | |
| 302 | 317 | "Worker thread subreport", | |
| 303 | 318 | trigger, | |
| 304 | - Local<String>(), | ||
| 319 | + Local<Object>(), | ||
| 305 | 320 | os); | |
| 306 | 321 | ||
| 307 | 322 | Mutex::ScopedLock lock(workers_mutex); | |
@@ -455,18 +470,56 @@ static void PrintNetworkInterfaceInfo(JSONWriter* writer) { | |||
| 455 | 470 | } | |
| 456 | 471 | } | |
| 457 | 472 | ||
| 473 | + static void PrintJavaScriptErrorProperties(JSONWriter* writer, | ||
| 474 | + Isolate* isolate, | ||
| 475 | + Local<Object> error) { | ||
| 476 | + writer->json_objectstart("errorProperties"); | ||
| 477 | + if (!error.IsEmpty()) { | ||
| 478 | + TryCatch try_catch(isolate); | ||
| 479 | + Local<Context> context = error->GetIsolate()->GetCurrentContext(); | ||
| 480 | + Local<Array> keys; | ||
| 481 | + if (!error->GetOwnPropertyNames(context).ToLocal(&keys)) { | ||
| 482 | + return writer->json_objectend(); // the end of 'errorProperties' | ||
| 483 | + } | ||
| 484 | + uint32_t keys_length = keys->Length(); | ||
| 485 | + for (uint32_t i = 0; i < keys_length; i++) { | ||
| 486 | + Local<Value> key; | ||
| 487 | + if (!keys->Get(context, i).ToLocal(&key) || !key->IsString()) { | ||
| 488 | + continue; | ||
| 489 | + } | ||
| 490 | + Local<Value> value; | ||
| 491 | + Local<String> value_string; | ||
| 492 | + if (!error->Get(context, key).ToLocal(&value) || | ||
| 493 | + !value->ToString(context).ToLocal(&value_string)) { | ||
| 494 | + continue; | ||
| 495 | + } | ||
| 496 | + String::Utf8Value k(isolate, key); | ||
| 497 | + if (!strcmp(*k, "stack") || !strcmp(*k, "message")) continue; | ||
| 498 | + String::Utf8Value v(isolate, value_string); | ||
| 499 | + writer->json_keyvalue(std::string(*k, k.length()), | ||
| 500 | + std::string(*v, v.length())); | ||
| 501 | + } | ||
| 502 | + } | ||
| 503 | + writer->json_objectend(); // the end of 'errorProperties' | ||
| 504 | + } | ||
| 505 | + | ||
| 458 | 506 | // Report the JavaScript stack. | |
| 459 | - static void PrintJavaScriptStack(JSONWriter* writer, | ||
| 507 | + static void PrintJavaScriptErrorStack(JSONWriter* writer, | ||
| 460 | 508 | Isolate* isolate, | |
| 461 | - Local<String> stackstr, | ||
| 509 | + Local<Object> error, | ||
| 462 | 510 | const char* trigger) { | |
| 463 | - writer->json_objectstart("javascriptStack"); | ||
| 464 | - | ||
| 465 | - std::string ss; | ||
| 511 | + Local<Value> stackstr; | ||
| 512 | + std::string ss = ""; | ||
| 513 | + TryCatch try_catch(isolate); | ||
| 466 | 514 | if ((!strcmp(trigger, "FatalError")) || | |
| 467 | 515 | (!strcmp(trigger, "Signal"))) { | |
| 468 | 516 | ss = "No stack.\nUnavailable.\n"; | |
| 469 | - } else { | ||
| 517 | + } else if (!error.IsEmpty() && | ||
| 518 | + error | ||
| 519 | + ->Get(isolate->GetCurrentContext(), | ||
| 520 | + node::FIXED_ONE_BYTE_STRING(isolate, | ||
| 521 | + "stack")) | ||
| 522 | + .ToLocal(&stackstr)) { | ||
| 470 | 523 | String::Utf8Value sv(isolate, stackstr); | |
| 471 | 524 | ss = std::string(*sv, sv.length()); | |
| 472 | 525 | } | |
@@ -490,7 +543,6 @@ static void PrintJavaScriptStack(JSONWriter* writer, | |||
| 490 | 543 | } | |
| 491 | 544 | writer->json_arrayend(); | |
| 492 | 545 | } | |
| 493 | - writer->json_objectend(); | ||
| 494 | 546 | } | |
| 495 | 547 | ||
| 496 | 548 | // Report a native stack backtrace | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -20,12 +20,12 @@ std::string TriggerNodeReport(v8::Isolate* isolate, | |||
| 20 | 20 | const char* message, | |
| 21 | 21 | const char* trigger, | |
| 22 | 22 | const std::string& name, | |
| 23 | - v8::Local<v8::String> stackstr); | ||
| 23 | + v8::Local<v8::Object> error); | ||
| 24 | 24 | void GetNodeReport(v8::Isolate* isolate, | |
| 25 | 25 | node::Environment* env, | |
| 26 | 26 | const char* message, | |
| 27 | 27 | const char* trigger, | |
| 28 | - v8::Local<v8::String> stackstr, | ||
| 28 | + v8::Local<v8::Object> error, | ||
| 29 | 29 | std::ostream& out); | |
| 30 | 30 | ||
| 31 | 31 | // Function declarations - utility functions in src/node_report_utils.cc | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,18 +32,21 @@ void WriteReport(const FunctionCallbackInfo<Value>& info) { | |||
| 32 | 32 | Isolate* isolate = env->isolate(); | |
| 33 | 33 | HandleScope scope(isolate); | |
| 34 | 34 | std::string filename; | |
| 35 | - Local<String> stackstr; | ||
| 35 | + Local<Object> error; | ||
| 36 | 36 | ||
| 37 | 37 | CHECK_EQ(info.Length(), 4); | |
| 38 | 38 | String::Utf8Value message(isolate, info[0].As<String>()); | |
| 39 | 39 | String::Utf8Value trigger(isolate, info[1].As<String>()); | |
| 40 | - stackstr = info[3].As<String>(); | ||
| 41 | 40 | ||
| 42 | 41 | if (info[2]->IsString()) | |
| 43 | 42 | filename = *String::Utf8Value(isolate, info[2]); | |
| 43 | + if (!info[3].IsEmpty() && info[3]->IsObject()) | ||
| 44 | + error = info[3].As<Object>(); | ||
| 45 | + else | ||
| 46 | + error = Local<Object>(); | ||
| 44 | 47 | ||
| 45 | 48 | filename = TriggerNodeReport( | |
| 46 | - isolate, env, *message, *trigger, filename, stackstr); | ||
| 49 | + isolate, env, *message, *trigger, filename, error); | ||
| 47 | 50 | // Return value is the report filename | |
| 48 | 51 | info.GetReturnValue().Set( | |
| 49 | 52 | String::NewFromUtf8(isolate, filename.c_str(), v8::NewStringType::kNormal) | |
@@ -55,10 +58,17 @@ void GetReport(const FunctionCallbackInfo<Value>& info) { | |||
| 55 | 58 | Environment* env = Environment::GetCurrent(info); | |
| 56 | 59 | Isolate* isolate = env->isolate(); | |
| 57 | 60 | HandleScope scope(isolate); | |
| 61 | + Local<Object> error; | ||
| 58 | 62 | std::ostringstream out; | |
| 59 | 63 | ||
| 64 | + CHECK_EQ(info.Length(), 1); | ||
| 65 | + if (!info[0].IsEmpty() && info[0]->IsObject()) | ||
| 66 | + error = info[0].As<Object>(); | ||
| 67 | + else | ||
| 68 | + error = Local<Object>(); | ||
| 69 | + | ||
| 60 | 70 | GetNodeReport( | |
| 61 | - isolate, env, "JavaScript API", __func__, info[0].As<String>(), out); | ||
| 71 | + isolate, env, "JavaScript API", __func__, error, out); | ||
| 62 | 72 | ||
| 63 | 73 | // Return value is the contents of a report as a string. | |
| 64 | 74 | info.GetReturnValue().Set(String::NewFromUtf8(isolate, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,16 +24,16 @@ function findReports(pid, dir) { | |||
| 24 | 24 | return results; | |
| 25 | 25 | } | |
| 26 | 26 | ||
| 27 | - function validate(filepath) { | ||
| 27 | + function validate(filepath, fields) { | ||
| 28 | 28 | const report = fs.readFileSync(filepath, 'utf8'); | |
| 29 | 29 | if (process.report.compact) { | |
| 30 | 30 | const end = report.indexOf('\n'); | |
| 31 | 31 | assert.strictEqual(end, report.length - 1); | |
| 32 | 32 | } | |
| 33 | - validateContent(JSON.parse(report)); | ||
| 33 | + validateContent(JSON.parse(report), fields); | ||
| 34 | 34 | } | |
| 35 | 35 | ||
| 36 | - function validateContent(report) { | ||
| 36 | + function validateContent(report, fields = []) { | ||
| 37 | 37 | if (typeof report === 'string') { | |
| 38 | 38 | try { | |
| 39 | 39 | report = JSON.parse(report); | |
@@ -43,7 +43,7 @@ function validateContent(report) { | |||
| 43 | 43 | } | |
| 44 | 44 | } | |
| 45 | 45 | try { | |
| 46 | - _validateContent(report); | ||
| 46 | + _validateContent(report, fields); | ||
| 47 | 47 | } catch (err) { | |
| 48 | 48 | try { | |
| 49 | 49 | err.stack += util.format('\n------\nFailing Report:\n%O', report); | |
@@ -52,7 +52,7 @@ function validateContent(report) { | |||
| 52 | 52 | } | |
| 53 | 53 | } | |
| 54 | 54 | ||
| 55 | - function _validateContent(report) { | ||
| 55 | + function _validateContent(report, fields = []) { | ||
| 56 | 56 | const isWindows = process.platform === 'win32'; | |
| 57 | 57 | ||
| 58 | 58 | // Verify that all sections are present as own properties of the report. | |
@@ -71,6 +71,26 @@ function _validateContent(report) { | |||
| 71 | 71 | assert(typeof report[section] === 'object' && report[section] !== null); | |
| 72 | 72 | }); | |
| 73 | 73 | ||
| 74 | + fields.forEach((field) => { | ||
| 75 | + function checkLoop(actual, rest, expect) { | ||
| 76 | + actual = actual[rest.shift()]; | ||
| 77 | + if (rest.length === 0 && actual !== undefined) { | ||
| 78 | + assert.strictEqual(actual, expect); | ||
| 79 | + } else { | ||
| 80 | + assert(actual); | ||
| 81 | + checkLoop(actual, rest, expect); | ||
| 82 | + } | ||
| 83 | + } | ||
| 84 | + let actual, expect; | ||
| 85 | + if (Array.isArray(field)) { | ||
| 86 | + [actual, expect] = field; | ||
| 87 | + } else { | ||
| 88 | + actual = field; | ||
| 89 | + expect = undefined; | ||
| 90 | + } | ||
| 91 | + checkLoop(report, actual.split('.'), expect); | ||
| 92 | + }); | ||
| 93 | + | ||
| 74 | 94 | // Verify the format of the header section. | |
| 75 | 95 | const header = report.header; | |
| 76 | 96 | const headerFields = ['event', 'trigger', 'filename', 'dumpEventTime', | |
@@ -144,7 +164,10 @@ function _validateContent(report) { | |||
| 144 | 164 | assert.strictEqual(header.host, os.hostname()); | |
| 145 | 165 | ||
| 146 | 166 | // Verify the format of the javascriptStack section. | |
| 147 | - checkForUnknownFields(report.javascriptStack, ['message', 'stack']); | ||
| 167 | + checkForUnknownFields(report.javascriptStack, | ||
| 168 | + ['message', 'stack', 'errorProperties']); | ||
| 169 | + assert.strictEqual(typeof report.javascriptStack.errorProperties, | ||
| 170 | + 'object'); | ||
| 148 | 171 | assert.strictEqual(typeof report.javascriptStack.message, 'string'); | |
| 149 | 172 | if (report.javascriptStack.stack !== undefined) { | |
| 150 | 173 | assert(Array.isArray(report.javascriptStack.stack)); | |
@@ -262,7 +285,7 @@ function _validateContent(report) { | |||
| 262 | 285 | ||
| 263 | 286 | // Verify the format of the workers section. | |
| 264 | 287 | assert(Array.isArray(report.workers)); | |
| 265 | - report.workers.forEach(_validateContent); | ||
| 288 | + report.workers.forEach((worker) => _validateContent(worker)); | ||
| 266 | 289 | } | |
| 267 | 290 | ||
| 268 | 291 | function checkForUnknownFields(actual, expected) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,6 +23,13 @@ const helper = require('../common/report'); | |||
| 23 | 23 | assert.deepStrictEqual(helper.findReports(process.pid, process.cwd()), []); | |
| 24 | 24 | } | |
| 25 | 25 | ||
| 26 | + { | ||
| 27 | + const error = new Error(); | ||
| 28 | + error.foo = 'goo'; | ||
| 29 | + helper.validateContent(process.report.getReport(error), | ||
| 30 | + [['javascriptStack.errorProperties.foo', 'goo']]); | ||
| 31 | + } | ||
| 32 | + | ||
| 26 | 33 | // Test with an invalid error argument. | |
| 27 | 34 | [null, 1, Symbol(), function() {}, 'foo'].forEach((error) => { | |
| 28 | 35 | assert.throws(() => { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,7 @@ process.report.directory = tmpdir.path; | |||
| 15 | 15 | function validate() { | |
| 16 | 16 | const reports = helper.findReports(process.pid, tmpdir.path); | |
| 17 | 17 | assert.strictEqual(reports.length, 1); | |
| 18 | - helper.validate(reports[0]); | ||
| 18 | + helper.validate(reports[0], arguments[0]); | ||
| 19 | 19 | fs.unlinkSync(reports[0]); | |
| 20 | 20 | return reports[0]; | |
| 21 | 21 | } | |
@@ -40,6 +40,13 @@ function validate() { | |||
| 40 | 40 | validate(); | |
| 41 | 41 | } | |
| 42 | 42 | ||
| 43 | + { | ||
| 44 | + const error = new Error(); | ||
| 45 | + error.foo = 'goo'; | ||
| 46 | + process.report.writeReport(error); | ||
| 47 | + validate([['javascriptStack.errorProperties.foo', 'goo']]); | ||
| 48 | + } | ||
| 49 | + | ||
| 43 | 50 | { | |
| 44 | 51 | // Test with a file argument. | |
| 45 | 52 | const file = process.report.writeReport('custom-name-1.json'); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments