Skip to content

Commit

Permalink
Start crash reporter inside child processes (#27180)
Browse files Browse the repository at this point in the history
* Use service to get crash reporter start options

* some refactorings and fixes

* Move crashesDirectory to the main payload from extra bag
  • Loading branch information
ramya-rao-a authored and dbaeumer committed May 26, 2017
1 parent f962f52 commit 29a7a86
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 97 deletions.
12 changes: 12 additions & 0 deletions src/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,16 @@ if (process.env['VSCODE_PARENT_PID']) {
}
}

const crashReporterOptionsRaw = process.env['CRASH_REPORTER_START_OPTIONS'];
if (typeof crashReporterOptionsRaw === 'string') {
try {
const crashReporterOptions = JSON.parse(crashReporterOptionsRaw);
if (crashReporterOptions) {
process.crashReporter.start(crashReporterOptions);
}
} catch (error) {
console.error(error);
}
}

require('./bootstrap-amd').bootstrap(process.env['AMD_ENTRYPOINT']);
7 changes: 7 additions & 0 deletions src/typings/electron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2066,6 +2066,13 @@ declare namespace Electron {
* Only string properties are sent correctly, nested objects are not supported.
*/
extra?: { [prop: string]: string };

/**
* Path to a folder where the crashes will be temporarily stored by the electron crash reporter
* Applies only to child processes that need crash reporting.
* Electron figures out the crashesDirectory on its own for Main and Renderer process
*/
crashesDirectory?: string;
}

interface CrashReport {
Expand Down
71 changes: 0 additions & 71 deletions src/vs/workbench/electron-browser/crashReporter.ts

This file was deleted.

12 changes: 10 additions & 2 deletions src/vs/workbench/electron-browser/extensionHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import Event, { Emitter } from 'vs/base/common/event';
import { IInitData } from 'vs/workbench/api/node/extHost.protocol';
import { MainProcessExtensionService } from 'vs/workbench/api/electron-browser/mainThreadExtensionService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { ICrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService';

export const EXTENSION_LOG_BROADCAST_CHANNEL = 'vscode:extensionLog';
export const EXTENSION_ATTACH_BROADCAST_CHANNEL = 'vscode:extensionAttach';
Expand Down Expand Up @@ -92,7 +93,9 @@ export class ExtensionHostProcessWorker {
@IInstantiationService private instantiationService: IInstantiationService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService
@ITelemetryService private telemetryService: ITelemetryService,
@ICrashReporterService private crashReporterService: ICrashReporterService

) {
// handle extension host lifecycle a bit special when we know we are developing an extension that runs inside
this.isExtensionDevelopmentHost = environmentService.isExtensionDevelopment;
Expand All @@ -111,7 +114,7 @@ export class ExtensionHostProcessWorker {
const [server, hook] = <[Server, string]>data[0];
const port = <number>data[1];

let opts = {
const opts = {
env: objects.mixin(objects.clone(process.env), {
AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess',
PIPE_LOGGING: 'true',
Expand All @@ -130,6 +133,11 @@ export class ExtensionHostProcessWorker {
: undefined
};

const crashReporterOptions = this.crashReporterService.getChildProcessStartOptions('extensionHost');
if (crashReporterOptions) {
opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions);
}

// Run Extension Host as fork of current process
this.extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts);

Expand Down
32 changes: 8 additions & 24 deletions src/vs/workbench/electron-browser/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { WorkbenchModeServiceImpl } from 'vs/workbench/services/mode/common/workbenchModeService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { CrashReporter } from 'vs/workbench/electron-browser/crashReporter';
import { ICrashReporterService, NullCrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService';
import { CrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService';
import { NodeCachedDataManager } from 'vs/workbench/electron-browser/nodeCachedDataManager';
import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc';
import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net';
Expand Down Expand Up @@ -168,29 +169,6 @@ export class WorkbenchShell {
// Instantiation service with services
const [instantiationService, serviceCollection] = this.initServiceCollection(parent.getHTMLElement());

//crash reporting
if (product.crashReporter && product.hockeyApp) {
let submitURL: string;

if (platform.isWindows) {
submitURL = product.hockeyApp[`win32-${process.arch}`];
} else if (platform.isMacintosh) {
submitURL = product.hockeyApp.darwin;
} else if (platform.isLinux) {
submitURL = product.hockeyApp[`linux-${process.arch}`];
}

if (submitURL) {
const opts: Electron.CrashReporterStartOptions = {
companyName: product.crashReporter.companyName,
productName: product.crashReporter.productName,
submitURL
};

instantiationService.createInstance(CrashReporter, opts);
}
}

// Workbench
this.workbench = instantiationService.createInstance(Workbench, parent.getHTMLElement(), workbenchContainer.getHTMLElement(), this.options, serviceCollection);
this.workbench.startup({
Expand Down Expand Up @@ -333,6 +311,12 @@ export class WorkbenchShell {
serviceCollection.set(ITelemetryService, this.telemetryService);
disposables.add(configurationTelemetry(this.telemetryService, this.configurationService));

let crashReporterService = NullCrashReporterService;
if (product.crashReporter && product.hockeyApp) {
crashReporterService = instantiationService.createInstance(CrashReporterService);
}
serviceCollection.set(ICrashReporterService, crashReporterService);

this.messageService = instantiationService.createInstance(MessageService, container);
serviceCollection.set(IMessageService, this.messageService);
serviceCollection.set(IChoiceService, this.messageService);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import nls = require('vs/nls');
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/platform';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';

export const ICrashReporterService = createDecorator<ICrashReporterService>('crashReporterService');

export const TELEMETRY_SECTION_ID = 'telemetry';

export interface ICrashReporterConfig {
enableCrashReporter: boolean;
}

const configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
configurationRegistry.registerConfiguration({
'id': TELEMETRY_SECTION_ID,
'order': 110,
title: nls.localize('telemetryConfigurationTitle', "Telemetry"),
'type': 'object',
'properties': {
'telemetry.enableCrashReporter': {
'type': 'boolean',
'description': nls.localize('telemetry.enableCrashReporting', "Enable crash reports to be sent to Microsoft.\nThis option requires restart to take effect."),
'default': true
}
}
});

export interface ICrashReporterService {
_serviceBrand: any;
getChildProcessStartOptions(processName: string): Electron.CrashReporterStartOptions;
}

export const NullCrashReporterService: ICrashReporterService = {
_serviceBrand: undefined,
getChildProcessStartOptions(processName: string) { return undefined; }
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import { onUnexpectedError } from 'vs/base/common/errors';
import { assign, clone } from 'vs/base/common/objects';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { crashReporter } from 'electron';
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
import * as os from 'os';
import { ICrashReporterService, TELEMETRY_SECTION_ID, ICrashReporterConfig } from "vs/workbench/services/crashReporter/common/crashReporterService";
import { isWindows, isMacintosh, isLinux } from "vs/base/common/platform";

export class CrashReporterService implements ICrashReporterService {

public _serviceBrand: any;

private options: Electron.CrashReporterStartOptions;

constructor(
@ITelemetryService private telemetryService: ITelemetryService,
@IWindowsService private windowsService: IWindowsService,
@IConfigurationService configurationService: IConfigurationService
) {
const config = configurationService.getConfiguration<ICrashReporterConfig>(TELEMETRY_SECTION_ID);
if (config.enableCrashReporter) {
this.startCrashReporter();
}
}

private startCrashReporter(): void {

// base options
this.options = {
companyName: product.crashReporter.companyName,
productName: product.crashReporter.productName,
submitURL: this.getSubmitURL()
};

// mixin telemetry info and product info
this.telemetryService.getTelemetryInfo()
.then(info => {
assign(this.options, {
extra: {
vscode_sessionId: info.sessionId,
vscode_version: pkg.version,
vscode_commit: product.commit,
vscode_machineId: info.machineId
}
});

// start crash reporter right here
crashReporter.start(clone(this.options));

// start crash reporter in the main process
return this.windowsService.startCrashReporter(this.options);
})
.done(null, onUnexpectedError);
}

private getSubmitURL(): string {
let submitURL: string;
if (isWindows) {
submitURL = product.hockeyApp[`win32-${process.arch}`];
} else if (isMacintosh) {
submitURL = product.hockeyApp.darwin;
} else if (isLinux) {
submitURL = product.hockeyApp[`linux-${process.arch}`];
}

return submitURL;
}

public getChildProcessStartOptions(name: string): Electron.CrashReporterStartOptions {

// Experimental attempt on Mac only for now
if (isMacintosh) {
const childProcessOptions = clone(this.options);
childProcessOptions.extra.processName = name;
childProcessOptions.crashesDirectory = os.tmpdir();
return childProcessOptions;
}

return void 0;
}
}

0 comments on commit 29a7a86

Please sign in to comment.