report: generates report on threads with no isolates

PR-URL: https://github.com/nodejs/node/pull/38994
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
legendecas 2021-06-10 23:55:54 +08:00 committed by Danielle Adams
parent 4174f139b6
commit 4c6193fea1
No known key found for this signature in database
GPG Key ID: D3A89613643B6201
4 changed files with 89 additions and 49 deletions

View File

@ -425,13 +425,10 @@ void OnFatalError(const char* location, const char* message) {
}
Isolate* isolate = Isolate::GetCurrent();
// TODO(legendecas): investigate failures on triggering node-report with
// nullptr isolates.
if (isolate == nullptr) {
fflush(stderr);
ABORT();
Environment* env = nullptr;
if (isolate != nullptr) {
env = Environment::GetCurrent(isolate);
}
Environment* env = Environment::GetCurrent(isolate);
bool report_on_fatalerror;
{
Mutex::ScopedLock lock(node::per_process::cli_options_mutex);

View File

@ -272,20 +272,22 @@ static void WriteNodeReport(Isolate* isolate,
PrintVersionInformation(&writer);
writer.json_objectend();
writer.json_objectstart("javascriptStack");
// Report summary JavaScript error stack backtrace
PrintJavaScriptErrorStack(&writer, isolate, error, trigger);
if (isolate != nullptr) {
writer.json_objectstart("javascriptStack");
// Report summary JavaScript error stack backtrace
PrintJavaScriptErrorStack(&writer, isolate, error, trigger);
// Report summary JavaScript error properties backtrace
PrintJavaScriptErrorProperties(&writer, isolate, error);
writer.json_objectend(); // the end of 'javascriptStack'
// Report summary JavaScript error properties backtrace
PrintJavaScriptErrorProperties(&writer, isolate, error);
writer.json_objectend(); // the end of 'javascriptStack'
// Report V8 Heap and Garbage Collector information
PrintGCStatistics(&writer, isolate);
}
// Report native stack backtrace
PrintNativeStack(&writer);
// Report V8 Heap and Garbage Collector information
PrintGCStatistics(&writer, isolate);
// Report OS and current thread resource usage
PrintResourceUsage(&writer);

View File

@ -54,10 +54,10 @@ function validateContent(report, fields = []) {
function _validateContent(report, fields = []) {
const isWindows = process.platform === 'win32';
const isJavaScriptThreadReport = report.javascriptStack != null;
// Verify that all sections are present as own properties of the report.
const sections = ['header', 'javascriptStack', 'nativeStack',
'javascriptHeap', 'libuv', 'environmentVariables',
const sections = ['header', 'nativeStack', 'libuv', 'environmentVariables',
'sharedObjects', 'resourceUsage', 'workers'];
if (!isWindows)
sections.push('userLimits');
@ -65,6 +65,9 @@ function _validateContent(report, fields = []) {
if (report.uvthreadResourceUsage)
sections.push('uvthreadResourceUsage');
if (isJavaScriptThreadReport)
sections.push('javascriptStack', 'javascriptHeap');
checkForUnknownFields(report, sections);
sections.forEach((section) => {
assert(report.hasOwnProperty(section));
@ -163,19 +166,6 @@ function _validateContent(report, fields = []) {
});
assert.strictEqual(header.host, os.hostname());
// Verify the format of the javascriptStack section.
checkForUnknownFields(report.javascriptStack,
['message', 'stack', 'errorProperties']);
assert.strictEqual(typeof report.javascriptStack.errorProperties,
'object');
assert.strictEqual(typeof report.javascriptStack.message, 'string');
if (report.javascriptStack.stack !== undefined) {
assert(Array.isArray(report.javascriptStack.stack));
report.javascriptStack.stack.forEach((frame) => {
assert.strictEqual(typeof frame, 'string');
});
}
// Verify the format of the nativeStack section.
assert(Array.isArray(report.nativeStack));
report.nativeStack.forEach((frame) => {
@ -186,26 +176,41 @@ function _validateContent(report, fields = []) {
assert.strictEqual(typeof frame.symbol, 'string');
});
// Verify the format of the javascriptHeap section.
const heap = report.javascriptHeap;
const jsHeapFields = ['totalMemory', 'totalCommittedMemory', 'usedMemory',
'availableMemory', 'memoryLimit', 'heapSpaces'];
checkForUnknownFields(heap, jsHeapFields);
assert(Number.isSafeInteger(heap.totalMemory));
assert(Number.isSafeInteger(heap.totalCommittedMemory));
assert(Number.isSafeInteger(heap.usedMemory));
assert(Number.isSafeInteger(heap.availableMemory));
assert(Number.isSafeInteger(heap.memoryLimit));
assert(typeof heap.heapSpaces === 'object' && heap.heapSpaces !== null);
const heapSpaceFields = ['memorySize', 'committedMemory', 'capacity', 'used',
'available'];
Object.keys(heap.heapSpaces).forEach((spaceName) => {
const space = heap.heapSpaces[spaceName];
checkForUnknownFields(space, heapSpaceFields);
heapSpaceFields.forEach((field) => {
assert(Number.isSafeInteger(space[field]));
if (isJavaScriptThreadReport) {
// Verify the format of the javascriptStack section.
checkForUnknownFields(report.javascriptStack,
['message', 'stack', 'errorProperties']);
assert.strictEqual(typeof report.javascriptStack.errorProperties,
'object');
assert.strictEqual(typeof report.javascriptStack.message, 'string');
if (report.javascriptStack.stack !== undefined) {
assert(Array.isArray(report.javascriptStack.stack));
report.javascriptStack.stack.forEach((frame) => {
assert.strictEqual(typeof frame, 'string');
});
}
// Verify the format of the javascriptHeap section.
const heap = report.javascriptHeap;
const jsHeapFields = ['totalMemory', 'totalCommittedMemory', 'usedMemory',
'availableMemory', 'memoryLimit', 'heapSpaces'];
checkForUnknownFields(heap, jsHeapFields);
assert(Number.isSafeInteger(heap.totalMemory));
assert(Number.isSafeInteger(heap.totalCommittedMemory));
assert(Number.isSafeInteger(heap.usedMemory));
assert(Number.isSafeInteger(heap.availableMemory));
assert(Number.isSafeInteger(heap.memoryLimit));
assert(typeof heap.heapSpaces === 'object' && heap.heapSpaces !== null);
const heapSpaceFields = ['memorySize', 'committedMemory', 'capacity',
'used', 'available'];
Object.keys(heap.heapSpaces).forEach((spaceName) => {
const space = heap.heapSpaces[spaceName];
checkForUnknownFields(space, heapSpaceFields);
heapSpaceFields.forEach((field) => {
assert(Number.isSafeInteger(space[field]));
});
});
});
}
// Verify the format of the resourceUsage section.
const usage = report.resourceUsage;

View File

@ -0,0 +1,36 @@
'use strict';
const common = require('../../common');
const helper = require('../../common/report.js');
const tmpdir = require('../../common/tmpdir');
const assert = require('assert');
const child_process = require('child_process');
const test_fatal = require(`./build/${common.buildType}/test_fatal`);
if (common.buildType === 'Debug')
common.skip('as this will currently fail with a Debug check ' +
'in v8::Isolate::GetCurrent()');
// Test in a child process because the test code will trigger a fatal error
// that crashes the process.
if (process.argv[2] === 'child') {
test_fatal.TestThread();
// Busy loop to allow the work thread to abort.
while (true) {}
}
tmpdir.refresh();
const p = child_process.spawnSync(
process.execPath,
[ '--report-on-fatalerror', __filename, 'child' ],
{ cwd: tmpdir.path });
assert.ifError(p.error);
assert.ok(p.stderr.toString().includes(
'FATAL ERROR: work_thread foobar'));
assert.ok(p.status === 134 || p.signal === 'SIGABRT');
const reports = helper.findReports(p.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);
const report = reports[0];
helper.validate(report);