mirror of https://github.com/nodejs/node.git
report: get stack trace with cross origin contexts
When a new context with a different security token is entered, or when no context is entered, `StackTrace::CurrentStackTrace` need to be explicitly set with flag `kExposeFramesAcrossSecurityOrigins` to avoid crashing. PR-URL: https://github.com/nodejs/node/pull/44398 Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
This commit is contained in:
parent
7f496fefb6
commit
f7896d4671
|
@ -513,6 +513,11 @@ void OOMErrorHandler(const char* location, bool is_heap_oom) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (report_on_fatalerror) {
|
if (report_on_fatalerror) {
|
||||||
|
// Trigger report with the isolate. Environment::GetCurrent may return
|
||||||
|
// nullptr here:
|
||||||
|
// - If the OOM is reported by a young generation space allocation,
|
||||||
|
// Isolate::GetCurrentContext returns an empty handle.
|
||||||
|
// - Otherwise, Isolate::GetCurrentContext returns a non-empty handle.
|
||||||
TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>());
|
TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -470,8 +470,12 @@ static void PrintJavaScriptStack(JSONWriter* writer,
|
||||||
void* samples[MAX_FRAME_COUNT];
|
void* samples[MAX_FRAME_COUNT];
|
||||||
isolate->GetStackSample(state, samples, MAX_FRAME_COUNT, &info);
|
isolate->GetStackSample(state, samples, MAX_FRAME_COUNT, &info);
|
||||||
|
|
||||||
|
constexpr StackTrace::StackTraceOptions stack_trace_options =
|
||||||
|
static_cast<StackTrace::StackTraceOptions>(
|
||||||
|
StackTrace::kDetailed |
|
||||||
|
StackTrace::kExposeFramesAcrossSecurityOrigins);
|
||||||
Local<StackTrace> stack = StackTrace::CurrentStackTrace(
|
Local<StackTrace> stack = StackTrace::CurrentStackTrace(
|
||||||
isolate, MAX_FRAME_COUNT, StackTrace::kDetailed);
|
isolate, MAX_FRAME_COUNT, stack_trace_options);
|
||||||
|
|
||||||
if (stack->GetFrameCount() == 0) {
|
if (stack->GetFrameCount() == 0) {
|
||||||
PrintEmptyJavaScriptStack(writer);
|
PrintEmptyJavaScriptStack(writer);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <node.h>
|
#include <node.h>
|
||||||
#include <v8.h>
|
#include <v8.h>
|
||||||
|
|
||||||
|
using v8::Context;
|
||||||
using v8::FunctionCallbackInfo;
|
using v8::FunctionCallbackInfo;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
|
@ -43,11 +44,37 @@ void TriggerReportNoEnv(const FunctionCallbackInfo<Value>& args) {
|
||||||
Local<Value>());
|
Local<Value>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriggerReportNoContext(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
Local<Context> context = isolate->GetCurrentContext();
|
||||||
|
context->Exit();
|
||||||
|
|
||||||
|
if (isolate->GetCurrentContext().IsEmpty()) {
|
||||||
|
node::TriggerNodeReport(
|
||||||
|
isolate, "FooMessage", "BarTrigger", std::string(), Local<Value>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore current context to avoid crashing in Context::Scope in
|
||||||
|
// SpinEventLoop.
|
||||||
|
context->Enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerReportNewContext(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
Local<Context> context = Context::New(isolate);
|
||||||
|
Context::Scope context_scope(context);
|
||||||
|
|
||||||
|
node::TriggerNodeReport(
|
||||||
|
isolate, "FooMessage", "BarTrigger", std::string(), Local<Value>());
|
||||||
|
}
|
||||||
|
|
||||||
void init(Local<Object> exports) {
|
void init(Local<Object> exports) {
|
||||||
NODE_SET_METHOD(exports, "triggerReport", TriggerReport);
|
NODE_SET_METHOD(exports, "triggerReport", TriggerReport);
|
||||||
NODE_SET_METHOD(exports, "triggerReportNoIsolate", TriggerReportNoIsolate);
|
NODE_SET_METHOD(exports, "triggerReportNoIsolate", TriggerReportNoIsolate);
|
||||||
NODE_SET_METHOD(exports, "triggerReportEnv", TriggerReportEnv);
|
NODE_SET_METHOD(exports, "triggerReportEnv", TriggerReportEnv);
|
||||||
NODE_SET_METHOD(exports, "triggerReportNoEnv", TriggerReportNoEnv);
|
NODE_SET_METHOD(exports, "triggerReportNoEnv", TriggerReportNoEnv);
|
||||||
|
NODE_SET_METHOD(exports, "triggerReportNoContext", TriggerReportNoContext);
|
||||||
|
NODE_SET_METHOD(exports, "triggerReportNewContext", TriggerReportNewContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
|
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
|
||||||
|
|
|
@ -9,7 +9,7 @@ const tmpdir = require('../../common/tmpdir');
|
||||||
const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`);
|
const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`);
|
||||||
const addon = require(binding);
|
const addon = require(binding);
|
||||||
|
|
||||||
function myAddonMain(method, hasJavaScriptFrames) {
|
function myAddonMain(method, { hasIsolate, hasEnv }) {
|
||||||
tmpdir.refresh();
|
tmpdir.refresh();
|
||||||
process.report.directory = tmpdir.path;
|
process.report.directory = tmpdir.path;
|
||||||
|
|
||||||
|
@ -19,26 +19,35 @@ function myAddonMain(method, hasJavaScriptFrames) {
|
||||||
assert.strictEqual(reports.length, 1);
|
assert.strictEqual(reports.length, 1);
|
||||||
|
|
||||||
const report = reports[0];
|
const report = reports[0];
|
||||||
helper.validate(report);
|
helper.validate(report, [
|
||||||
|
['header.event', 'FooMessage'],
|
||||||
|
['header.trigger', 'BarTrigger'],
|
||||||
|
]);
|
||||||
|
|
||||||
const content = require(report);
|
const content = require(report);
|
||||||
assert.strictEqual(content.header.event, 'FooMessage');
|
|
||||||
assert.strictEqual(content.header.trigger, 'BarTrigger');
|
|
||||||
|
|
||||||
// Check that the javascript stack is present.
|
// Check that the javascript stack is present.
|
||||||
if (hasJavaScriptFrames) {
|
if (hasIsolate) {
|
||||||
assert.strictEqual(content.javascriptStack.stack.findIndex((frame) => frame.match('myAddonMain')), 0);
|
assert.strictEqual(content.javascriptStack.stack.findIndex((frame) => frame.match('myAddonMain')), 0);
|
||||||
} else {
|
} else {
|
||||||
assert.strictEqual(content.javascriptStack, undefined);
|
assert.strictEqual(content.javascriptStack, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasEnv) {
|
||||||
|
assert.strictEqual(content.header.threadId, 0);
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(content.header.threadId, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const methods = [
|
const methods = [
|
||||||
['triggerReport', true],
|
['triggerReport', true, true],
|
||||||
['triggerReportNoIsolate', false],
|
['triggerReportNoIsolate', false, false],
|
||||||
['triggerReportEnv', true],
|
['triggerReportEnv', true, true],
|
||||||
['triggerReportNoEnv', false],
|
['triggerReportNoEnv', false, false],
|
||||||
|
['triggerReportNoContext', true, false],
|
||||||
|
['triggerReportNewContext', true, false],
|
||||||
];
|
];
|
||||||
for (const [method, hasJavaScriptFrames] of methods) {
|
for (const [method, hasIsolate, hasEnv] of methods) {
|
||||||
myAddonMain(method, hasJavaScriptFrames);
|
myAddonMain(method, { hasIsolate, hasEnv });
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue