Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IWorkbenchContribution } from '../../../common/contributions.js';
7
import { localize } from '../../../../nls.js';
8
import { dirname, basename } from '../../../../base/common/resources.js';
9
import { ITextModelService } from '../../../../editor/common/services/resolverService.js';
10
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
11
import { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-browser/environmentService.js';
12
import { ILifecycleService, LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
13
import { PerfviewContrib } from '../browser/perfviewEditor.js';
14
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
15
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
16
import { URI } from '../../../../base/common/uri.js';
17
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
18
import { INativeHostService } from '../../../../platform/native/common/native.js';
19
import { IProductService } from '../../../../platform/product/common/productService.js';
20
import { IFileService } from '../../../../platform/files/common/files.js';
21
import { ILabelService } from '../../../../platform/label/common/label.js';
22
23
export class StartupProfiler implements IWorkbenchContribution {
24
25
constructor(
26
@IDialogService private readonly _dialogService: IDialogService,
27
@INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,
28
@ITextModelService private readonly _textModelResolverService: ITextModelService,
29
@IClipboardService private readonly _clipboardService: IClipboardService,
30
@ILifecycleService lifecycleService: ILifecycleService,
31
@IExtensionService extensionService: IExtensionService,
32
@IOpenerService private readonly _openerService: IOpenerService,
33
@INativeHostService private readonly _nativeHostService: INativeHostService,
34
@IProductService private readonly _productService: IProductService,
35
@IFileService private readonly _fileService: IFileService,
36
@ILabelService private readonly _labelService: ILabelService,
37
) {
38
// wait for everything to be ready
39
Promise.all([
40
lifecycleService.when(LifecyclePhase.Eventually),
41
extensionService.whenInstalledExtensionsRegistered()
42
]).then(() => {
43
this._stopProfiling();
44
});
45
}
46
47
private _stopProfiling(): void {
48
49
if (!this._environmentService.args['prof-startup-prefix']) {
50
return;
51
}
52
const profileFilenamePrefix = URI.file(this._environmentService.args['prof-startup-prefix']);
53
54
const dir = dirname(profileFilenamePrefix);
55
const prefix = basename(profileFilenamePrefix);
56
57
const removeArgs: string[] = ['--prof-startup'];
58
const markerFile = this._fileService.readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|')))
59
.then(() => this._fileService.del(profileFilenamePrefix, { recursive: true })) // (1) delete the file to tell the main process to stop profiling
60
.then(() => new Promise<void>(resolve => { // (2) wait for main that recreates the fail to signal profiling has stopped
61
const check = () => {
62
this._fileService.exists(profileFilenamePrefix).then(exists => {
63
if (exists) {
64
resolve();
65
} else {
66
setTimeout(check, 500);
67
}
68
});
69
};
70
check();
71
}))
72
.then(() => this._fileService.del(profileFilenamePrefix, { recursive: true })); // (3) finally delete the file again
73
74
markerFile.then(() => {
75
return this._fileService.resolve(dir).then(stat => {
76
return (stat.children ? stat.children.filter(value => value.resource.path.includes(prefix)) : []).map(stat => stat.resource);
77
});
78
}).then(files => {
79
const profileFiles = files.reduce((prev, cur) => `${prev}${this._labelService.getUriLabel(cur)}\n`, '\n');
80
81
return this._dialogService.confirm({
82
type: 'info',
83
message: localize('prof.message', "Successfully created profiles."),
84
detail: localize('prof.detail', "Please create an issue and manually attach the following files:\n{0}", profileFiles),
85
primaryButton: localize({ key: 'prof.restartAndFileIssue', comment: ['&& denotes a mnemonic'] }, "&&Create Issue and Restart"),
86
cancelButton: localize('prof.restart', "Restart")
87
}).then(res => {
88
if (res.confirmed) {
89
Promise.all<any>([
90
this._nativeHostService.showItemInFolder(files[0].fsPath),
91
this._createPerfIssue(files.map(file => basename(file)))
92
]).then(() => {
93
// keep window stable until restart is selected
94
return this._dialogService.confirm({
95
type: 'info',
96
message: localize('prof.thanks', "Thanks for helping us."),
97
detail: localize('prof.detail.restart', "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", this._productService.nameLong),
98
primaryButton: localize({ key: 'prof.restart.button', comment: ['&& denotes a mnemonic'] }, "&&Restart")
99
}).then(res => {
100
// now we are ready to restart
101
if (res.confirmed) {
102
this._nativeHostService.relaunch({ removeArgs });
103
}
104
});
105
});
106
107
} else {
108
// simply restart
109
this._nativeHostService.relaunch({ removeArgs });
110
}
111
});
112
});
113
}
114
115
private async _createPerfIssue(files: string[]): Promise<void> {
116
const reportIssueUrl = this._productService.reportIssueUrl;
117
if (!reportIssueUrl) {
118
return;
119
}
120
121
const contrib = PerfviewContrib.get();
122
const ref = await this._textModelResolverService.createModelReference(contrib.getInputUri());
123
try {
124
await this._clipboardService.writeText(ref.object.textEditorModel.getValue());
125
} finally {
126
ref.dispose();
127
}
128
129
const body = `
130
1. :warning: We have copied additional data to your clipboard. Make sure to **paste** here. :warning:
131
1. :warning: Make sure to **attach** these files from your *home*-directory: :warning:\n${files.map(file => `-\`${file}\``).join('\n')}
132
`;
133
134
const baseUrl = reportIssueUrl;
135
const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&';
136
137
this._openerService.open(URI.parse(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`));
138
}
139
}
140
141