chore: add V8 crash information to crashReporter (#24771)

* feat: add V8 crash information to crashReporter

* Address review feedback
This commit is contained in:
Shelley Vohr 2020-07-29 19:04:24 -07:00 committed by GitHub
parent d93bb34ac4
commit 948cc08265
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 0 deletions

View file

@ -128,6 +128,16 @@ std::vector<v8::Local<v8::Value>> GetWeaklyTrackedValues(v8::Isolate* isolate) {
} }
return locals; return locals;
} }
// This causes a fatal error by creating a circular extension dependency.
void TriggerFatalErrorForTesting(v8::Isolate* isolate) {
static const char* aDeps[] = {"B"};
v8::RegisterExtension(std::make_unique<v8::Extension>("A", "", 1, aDeps));
static const char* bDeps[] = {"A"};
v8::RegisterExtension(std::make_unique<v8::Extension>("B", "", 1, bDeps));
v8::ExtensionConfiguration config(1, bDeps);
v8::Context::New(isolate, &config);
}
#endif #endif
void Initialize(v8::Local<v8::Object> exports, void Initialize(v8::Local<v8::Object> exports,
@ -144,6 +154,7 @@ void Initialize(v8::Local<v8::Object> exports,
&RequestGarbageCollectionForTesting); &RequestGarbageCollectionForTesting);
dict.SetMethod("isSameOrigin", &IsSameOrigin); dict.SetMethod("isSameOrigin", &IsSameOrigin);
#ifdef DCHECK_IS_ON #ifdef DCHECK_IS_ON
dict.SetMethod("triggerFatalErrorForTesting", &TriggerFatalErrorForTesting);
dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues); dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues);
dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues); dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues);
dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue); dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue);

View file

@ -24,6 +24,7 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/common/content_paths.h" #include "content/public/common/content_paths.h"
#include "electron/buildflags/buildflags.h" #include "electron/buildflags/buildflags.h"
#include "shell/common/api/electron_bindings.h"
#include "shell/common/electron_command_line.h" #include "shell/common/electron_command_line.h"
#include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/dictionary.h"
@ -33,6 +34,10 @@
#include "shell/common/mac/main_application_bundle.h" #include "shell/common/mac/main_application_bundle.h"
#include "shell/common/node_includes.h" #include "shell/common/node_includes.h"
#if !defined(MAS_BUILD)
#include "shell/common/crash_keys.h"
#endif
#define ELECTRON_BUILTIN_MODULES(V) \ #define ELECTRON_BUILTIN_MODULES(V) \
V(electron_browser_app) \ V(electron_browser_app) \
V(electron_browser_auto_updater) \ V(electron_browser_auto_updater) \
@ -132,6 +137,18 @@ bool IsPackagedApp() {
#endif #endif
} }
void V8FatalErrorCallback(const char* location, const char* message) {
LOG(ERROR) << "Fatal error in V8: " << location << " " << message;
#if !defined(MAS_BUILD)
electron::crash_keys::SetCrashKey("electron.v8-fatal.message", message);
electron::crash_keys::SetCrashKey("electron.v8-fatal.location", location);
#endif
volatile int* zero = nullptr;
*zero = 0;
}
// Initialize Node.js cli options to pass to Node.js // Initialize Node.js cli options to pass to Node.js
// See https://nodejs.org/api/cli.html#cli_options // See https://nodejs.org/api/cli.html#cli_options
void SetNodeCliFlags() { void SetNodeCliFlags() {
@ -397,6 +414,10 @@ node::Environment* NodeBindings::CreateEnvironment(
node::IsolateSettings is; node::IsolateSettings is;
// Use a custom fatal error callback to allow us to add
// crash message and location to CrashReports.
is.fatal_error_callback = V8FatalErrorCallback;
if (browser_env_ == BrowserEnvironment::BROWSER) { if (browser_env_ == BrowserEnvironment::BROWSER) {
// Node.js requires that microtask checkpoints be explicitly invoked. // Node.js requires that microtask checkpoints be explicitly invoked.
is.policy = v8::MicrotasksPolicy::kExplicit; is.policy = v8::MicrotasksPolicy::kExplicit;

View file

@ -28,6 +28,8 @@ type CrashInfo = {
globalParam: 'globalValue' | undefined globalParam: 'globalValue' | undefined
addedThenRemoved: 'to-be-removed' | undefined addedThenRemoved: 'to-be-removed' | undefined
longParam: string | undefined longParam: string | undefined
'electron.v8-fatal.location': string | undefined
'electron.v8-fatal.message': string | undefined
} }
function checkCrash (expectedProcessType: string, fields: CrashInfo) { function checkCrash (expectedProcessType: string, fields: CrashInfo) {
@ -192,6 +194,32 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
expect(crash.rendererSpecific).to.equal('rs'); expect(crash.rendererSpecific).to.equal('rs');
expect(crash.addedThenRemoved).to.be.undefined(); expect(crash.addedThenRemoved).to.be.undefined();
}); });
it('contains v8 crash keys when a v8 crash occurs', async () => {
const { remotely } = await startRemoteControlApp();
const { port, waitForCrash } = await startServer();
await remotely((port: number) => {
require('electron').crashReporter.start({
submitURL: `http://127.0.0.1:${port}`,
ignoreSystemCrashHandler: true
});
}, [port]);
remotely(() => {
const { BrowserWindow } = require('electron');
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
bw.loadURL('about:blank');
bw.webContents.executeJavaScript('process._linkedBinding(\'electron_common_v8_util\').triggerFatalErrorForTesting()');
});
const crash = await waitForCrash();
expect(crash.prod).to.equal('Electron');
expect(crash._productName).to.equal('remote-control');
expect(crash.process_type).to.equal('renderer');
expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()');
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');
});
}); });
}); });

View file

@ -44,6 +44,7 @@ declare namespace NodeJS {
clearWeaklyTrackedValues(): void; clearWeaklyTrackedValues(): void;
getWeaklyTrackedValues(): any[]; getWeaklyTrackedValues(): any[];
addRemoteObjectRef(contextId: string, id: number): void; addRemoteObjectRef(contextId: string, id: number): void;
triggerFatalErrorForTesting(): void;
} }
type AsarFileInfo = { type AsarFileInfo = {