Skip to content

Commit 99ffff6

Browse files
authored
trace - allow to stop tracing via a command (microsoft#160489)
1 parent e57f698 commit 99ffff6

File tree

5 files changed

+94
-54
lines changed

5 files changed

+94
-54
lines changed

src/vs/code/electron-main/app.ts

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { app, BrowserWindow, contentTracing, dialog, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron';
6+
import { app, BrowserWindow, dialog, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron';
77
import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain';
88
import { statSync } from 'fs';
99
import { hostname, release } from 'os';
1010
import { VSBuffer } from 'vs/base/common/buffer';
1111
import { toErrorMessage } from 'vs/base/common/errorMessage';
1212
import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
13-
import { isEqualOrParent, randomPath } from 'vs/base/common/extpath';
13+
import { isEqualOrParent } from 'vs/base/common/extpath';
1414
import { once } from 'vs/base/common/functional';
1515
import { stripComments } from 'vs/base/common/json';
1616
import { getPathLabel, mnemonicButtonLabel } from 'vs/base/common/labels';
1717
import { Disposable } from 'vs/base/common/lifecycle';
1818
import { Schemas } from 'vs/base/common/network';
1919
import { isAbsolute, join, posix } from 'vs/base/common/path';
2020
import { IProcessEnvironment, isLinux, isLinuxSnap, isMacintosh, isWindows, OS } from 'vs/base/common/platform';
21-
import { assertType, withNullAsUndefined } from 'vs/base/common/types';
21+
import { assertType } from 'vs/base/common/types';
2222
import { URI } from 'vs/base/common/uri';
2323
import { generateUuid } from 'vs/base/common/uuid';
2424
import { getMachineId } from 'vs/base/node/id';
@@ -546,16 +546,11 @@ export class CodeApplication extends Disposable {
546546
appInstantiationService.invokeFunction(accessor => this.initChannels(accessor, mainProcessElectronServer, sharedProcessClient));
547547

548548
// Open Windows
549-
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, profile, mainProcessElectronServer));
549+
appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, profile, mainProcessElectronServer));
550550

551551
// Post Open Windows Tasks
552552
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor, sharedProcess));
553553

554-
// Tracing: Stop tracing after windows are ready if enabled
555-
if (this.environmentMainService.args.trace) {
556-
appInstantiationService.invokeFunction(accessor => this.stopTracingEventually(accessor, windows));
557-
}
558-
559554
// Set lifecycle phase to `Eventually` after a short delay and when idle (min 2.5sec, max 5sec)
560555
const eventuallyPhaseScheduler = this._register(new RunOnceScheduler(() => {
561556
this._register(runWhenIdle(() => this.lifecycleMainService.phase = LifecycleMainPhase.Eventually, 2500));
@@ -1281,44 +1276,4 @@ export class CodeApplication extends Disposable {
12811276
this.logService.error(error);
12821277
}
12831278
}
1284-
1285-
private stopTracingEventually(accessor: ServicesAccessor, windows: ICodeWindow[]): void {
1286-
this.logService.info('Tracing: waiting for windows to get ready...');
1287-
1288-
const dialogMainService = accessor.get(IDialogMainService);
1289-
1290-
let recordingStopped = false;
1291-
const stopRecording = async (timeout: boolean) => {
1292-
if (recordingStopped) {
1293-
return;
1294-
}
1295-
1296-
recordingStopped = true; // only once
1297-
1298-
const path = await contentTracing.stopRecording(`${randomPath(this.environmentMainService.userHome.fsPath, this.productService.applicationName)}.trace.txt`);
1299-
1300-
if (!timeout) {
1301-
dialogMainService.showMessageBox({
1302-
title: this.productService.nameLong,
1303-
type: 'info',
1304-
message: localize('trace.message', "Successfully created trace."),
1305-
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
1306-
buttons: [mnemonicButtonLabel(localize({ key: 'trace.ok', comment: ['&& denotes a mnemonic'] }, "&&OK"))],
1307-
defaultId: 0,
1308-
noLink: true
1309-
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
1310-
} else {
1311-
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
1312-
}
1313-
};
1314-
1315-
// Wait up to 30s before creating the trace anyways
1316-
const timeoutHandle = setTimeout(() => stopRecording(true), 30000);
1317-
1318-
// Wait for all windows to get ready and stop tracing then
1319-
Promise.all(windows.map(window => window.ready())).then(() => {
1320-
clearTimeout(timeoutHandle);
1321-
stopRecording(false);
1322-
});
1323-
}
13241279
}

src/vs/platform/issue/electron-main/issueMainService.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { BrowserWindow, BrowserWindowConstructorOptions, Display, IpcMainEvent, screen } from 'electron';
6+
import { BrowserWindow, BrowserWindowConstructorOptions, contentTracing, Display, IpcMainEvent, screen } from 'electron';
77
import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain';
88
import { arch, release, type } from 'os';
99
import { mnemonicButtonLabel } from 'vs/base/common/labels';
@@ -25,6 +25,8 @@ import { IProductService } from 'vs/platform/product/common/productService';
2525
import { IIPCObjectUrl, IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
2626
import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window';
2727
import { IWindowState } from 'vs/platform/window/electron-main/window';
28+
import { randomPath } from 'vs/base/common/extpath';
29+
import { withNullAsUndefined } from 'vs/base/common/types';
2830

2931
export const IIssueMainService = createDecorator<IIssueMainService>('issueMainService');
3032

@@ -35,9 +37,11 @@ interface IBrowserWindowOptions {
3537
alwaysOnTop: boolean;
3638
}
3739

38-
export interface IIssueMainService extends ICommonIssueService { }
40+
export interface IIssueMainService extends ICommonIssueService {
41+
stopTracing(): Promise<void>;
42+
}
3943

40-
export class IssueMainService implements ICommonIssueService {
44+
export class IssueMainService implements IIssueMainService {
4145

4246
declare readonly _serviceBrand: undefined;
4347

@@ -424,4 +428,26 @@ export class IssueMainService implements ICommonIssueService {
424428
throw error;
425429
}
426430
}
431+
432+
async stopTracing(): Promise<void> {
433+
if (!this.environmentMainService.args.trace) {
434+
return; // requires tracing to be on
435+
}
436+
437+
const path = await contentTracing.stopRecording(`${randomPath(this.environmentMainService.userHome.fsPath, this.productService.applicationName)}.trace.txt`);
438+
439+
// Inform user to report an issue
440+
await this.dialogMainService.showMessageBox({
441+
title: this.productService.nameLong,
442+
type: 'info',
443+
message: localize('trace.message', "Successfully created the trace file"),
444+
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
445+
buttons: [mnemonicButtonLabel(localize({ key: 'trace.ok', comment: ['&& denotes a mnemonic'] }, "&&OK"))],
446+
defaultId: 0,
447+
noLink: true
448+
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
449+
450+
// Show item in explorer
451+
this.nativeHostMainService.showItemInFolder(undefined, path);
452+
}
427453
}

src/vs/platform/issue/electron-sandbox/issue.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ import { ICommonIssueService } from 'vs/platform/issue/common/issue';
88

99
export const IIssueService = createDecorator<IIssueService>('issueService');
1010

11-
export interface IIssueService extends ICommonIssueService { }
11+
export interface IIssueService extends ICommonIssueService {
12+
stopTracing(): Promise<void>;
13+
}

src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import product from 'vs/platform/product/common/product';
88
import { MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
99
import { ICommandAction } from 'vs/platform/action/common/action';
1010
import { CATEGORIES } from 'vs/workbench/common/actions';
11-
import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-sandbox/issueActions';
11+
import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer, StopTracing } from 'vs/workbench/contrib/issue/electron-sandbox/issueActions';
1212
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
1313
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
1414
import { WorkbenchIssueService } from 'vs/workbench/services/issue/electron-sandbox/issueService';
@@ -103,6 +103,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
103103
});
104104

105105
registerAction2(OpenProcessExplorer);
106+
registerAction2(StopTracing);
106107

107108
registerSingleton(IWorkbenchIssueService, WorkbenchIssueService, true);
108109

src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import Severity from 'vs/base/common/severity';
67
import { localize } from 'vs/nls';
78
import { Action2 } from 'vs/platform/actions/common/actions';
9+
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
10+
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
811
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
912
import { IssueType } from 'vs/platform/issue/common/issue';
13+
import { IIssueService } from 'vs/platform/issue/electron-sandbox/issue';
14+
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
15+
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
1016
import { CATEGORIES } from 'vs/workbench/common/actions';
1117
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
1218

@@ -49,3 +55,53 @@ export class ReportPerformanceIssueUsingReporterAction extends Action2 {
4955
return issueService.openReporter({ issueType: IssueType.PerformanceIssue });
5056
}
5157
}
58+
59+
export class StopTracing extends Action2 {
60+
61+
static readonly ID = 'workbench.action.stopTracing';
62+
63+
constructor() {
64+
super({
65+
id: StopTracing.ID,
66+
title: { value: localize('stopTracing', "Stop Tracing"), original: 'Stop Tracing' },
67+
category: CATEGORIES.Developer,
68+
f1: true
69+
});
70+
}
71+
72+
override async run(accessor: ServicesAccessor): Promise<void> {
73+
const issueService = accessor.get(IIssueService);
74+
const environmentService = accessor.get(INativeEnvironmentService);
75+
const dialogService = accessor.get(IDialogService);
76+
const nativeHostService = accessor.get(INativeHostService);
77+
const progressService = accessor.get(IProgressService);
78+
79+
if (!environmentService.args.trace) {
80+
const { choice } = await dialogService.show(
81+
Severity.Info,
82+
localize('stopTracing.message', "Tracing requires to launch with a '--trace' argument"),
83+
[
84+
localize({ key: 'stopTracing.button', comment: ['&& denotes a mnemonic'] }, "&&Relaunch and Enable Tracing"),
85+
localize('cancel', "Cancel")
86+
],
87+
{
88+
cancelId: 1
89+
}
90+
);
91+
92+
switch (choice) {
93+
case 0:
94+
return nativeHostService.relaunch({ addArgs: ['--trace'] });
95+
case 1:
96+
return; // canceled
97+
}
98+
}
99+
100+
await progressService.withProgress({
101+
location: ProgressLocation.Dialog,
102+
title: localize('stopTracing.title', "Creating trace file..."),
103+
cancellable: false,
104+
detail: localize('stopTracing.detail', "This can take up to one minute to complete.")
105+
}, () => issueService.stopTracing());
106+
}
107+
}

0 commit comments

Comments
 (0)