mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-26 06:40:30 -04:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { Extensions as Input, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
|
||||
import { PerfviewContrib, PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser/perfviewEditor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { StartupProfiler } from './startupProfiler';
|
||||
import { StartupTimings } from './startupTimings';
|
||||
|
||||
// -- startup performance view
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
PerfviewContrib,
|
||||
LifecyclePhase.Ready
|
||||
);
|
||||
|
||||
Registry.as<IEditorInputFactoryRegistry>(Input.EditorInputFactories).registerEditorInputFactory(
|
||||
PerfviewInput.Id,
|
||||
class implements IEditorInputFactory {
|
||||
serialize(): string {
|
||||
return '';
|
||||
}
|
||||
deserialize(instantiationService: IInstantiationService): PerfviewInput {
|
||||
return instantiationService.createInstance(PerfviewInput);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
CommandsRegistry.registerCommand('perfview.show', accessor => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const instaService = accessor.get(IInstantiationService);
|
||||
return editorService.openEditor(instaService.createInstance(PerfviewInput));
|
||||
});
|
||||
|
||||
MenuRegistry.addCommand({
|
||||
id: 'perfview.show',
|
||||
category: localize('show.cat', "Developer"),
|
||||
title: localize('show.label', "Startup Performance")
|
||||
});
|
||||
|
||||
|
||||
// -- startup profiler
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
StartupProfiler,
|
||||
LifecyclePhase.Restored
|
||||
);
|
||||
|
||||
// -- startup timings
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
StartupTimings,
|
||||
LifecyclePhase.Eventually
|
||||
);
|
||||
@@ -0,0 +1,375 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { ILifecycleService, LifecyclePhase, StartupKindToString } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/electron-browser/timerService';
|
||||
import { repeat } from 'vs/base/common/strings';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import * as perf from 'vs/base/common/performance';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap';
|
||||
import { mergeSort } from 'vs/base/common/arrays';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
|
||||
export class PerfviewContrib {
|
||||
|
||||
private readonly _registration: IDisposable;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService instaService: IInstantiationService,
|
||||
@ITextModelService textModelResolverService: ITextModelService
|
||||
) {
|
||||
this._registration = textModelResolverService.registerTextModelContentProvider('perf', instaService.createInstance(PerfModelContentProvider));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._registration.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class PerfviewInput extends ResourceEditorInput {
|
||||
|
||||
static readonly Id = 'PerfviewInput';
|
||||
static readonly Uri = URI.from({ scheme: 'perf', path: 'Startup Performance' });
|
||||
|
||||
constructor(
|
||||
@ITextModelService textModelResolverService: ITextModelService
|
||||
) {
|
||||
super(
|
||||
localize('name', "Startup Performance"),
|
||||
null,
|
||||
PerfviewInput.Uri,
|
||||
textModelResolverService
|
||||
);
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
return PerfviewInput.Id;
|
||||
}
|
||||
}
|
||||
|
||||
class PerfModelContentProvider implements ITextModelContentProvider {
|
||||
|
||||
private _model: ITextModel | undefined;
|
||||
private _modelDisposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@ITimerService private readonly _timerService: ITimerService,
|
||||
@IEnvironmentService private readonly _envService: IEnvironmentService,
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
) { }
|
||||
|
||||
provideTextContent(resource: URI): Promise<ITextModel> {
|
||||
|
||||
if (!this._model || this._model.isDisposed()) {
|
||||
dispose(this._modelDisposables);
|
||||
const langId = this._modeService.create('markdown');
|
||||
this._model = this._modelService.getModel(resource) || this._modelService.createModel('Loading...', langId, resource);
|
||||
|
||||
this._modelDisposables.push(langId.onDidChange(e => {
|
||||
if (this._model) {
|
||||
this._model.setMode(e);
|
||||
}
|
||||
}));
|
||||
this._modelDisposables.push(langId);
|
||||
this._modelDisposables.push(this._extensionService.onDidChangeExtensionsStatus(this._updateModel, this));
|
||||
|
||||
writeTransientState(this._model, { forceWordWrap: 'off', forceWordWrapMinified: false }, this._editorService);
|
||||
}
|
||||
this._updateModel();
|
||||
return Promise.resolve(this._model);
|
||||
}
|
||||
|
||||
private _updateModel(): void {
|
||||
|
||||
Promise.all([
|
||||
this._timerService.startupMetrics,
|
||||
this._lifecycleService.when(LifecyclePhase.Eventually),
|
||||
this._extensionService.whenInstalledExtensionsRegistered()
|
||||
]).then(([metrics]) => {
|
||||
if (this._model && !this._model.isDisposed()) {
|
||||
|
||||
let stats = this._envService.args['prof-modules'] ? LoaderStats.get() : undefined;
|
||||
let md = new MarkdownBuilder();
|
||||
this._addSummary(md, metrics);
|
||||
md.blank();
|
||||
this._addSummaryTable(md, metrics, stats);
|
||||
md.blank();
|
||||
this._addExtensionsTable(md);
|
||||
md.blank();
|
||||
this._addRawPerfMarks(md);
|
||||
md.blank();
|
||||
this._addLoaderStats(md, stats);
|
||||
|
||||
this._model.setValue(md.value);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private _addSummary(md: MarkdownBuilder, metrics: IStartupMetrics): void {
|
||||
md.heading(2, 'System Info');
|
||||
md.li(`${product.nameShort}: ${pkg.version} (${product.commit || '0000000'})`);
|
||||
md.li(`OS: ${metrics.platform}(${metrics.release})`);
|
||||
if (metrics.cpus) {
|
||||
md.li(`CPUs: ${metrics.cpus.model}(${metrics.cpus.count} x ${metrics.cpus.speed})`);
|
||||
}
|
||||
if (typeof metrics.totalmem === 'number' && typeof metrics.freemem === 'number') {
|
||||
md.li(`Memory(System): ${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)`);
|
||||
}
|
||||
if (metrics.meminfo) {
|
||||
md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)`);
|
||||
}
|
||||
md.li(`VM(likelyhood): ${metrics.isVMLikelyhood}%`);
|
||||
md.li(`Initial Startup: ${metrics.initialStartup}`);
|
||||
md.li(`Has ${metrics.windowCount - 1} other windows`);
|
||||
md.li(`Screen Reader Active: ${metrics.hasAccessibilitySupport}`);
|
||||
md.li(`Empty Workspace: ${metrics.emptyWorkbench}`);
|
||||
}
|
||||
|
||||
private _addSummaryTable(md: MarkdownBuilder, metrics: IStartupMetrics, stats?: LoaderStats): void {
|
||||
|
||||
const table: Array<Array<string | number | undefined>> = [];
|
||||
table.push(['start => app.isReady', metrics.timers.ellapsedAppReady, '[main]', `initial startup: ${metrics.initialStartup}`]);
|
||||
table.push(['nls:start => nls:end', metrics.timers.ellapsedNlsGeneration, '[main]', `initial startup: ${metrics.initialStartup}`]);
|
||||
table.push(['require(main.bundle.js)', metrics.initialStartup ? perf.getDuration('willLoadMainBundle', 'didLoadMainBundle') : undefined, '[main]', `initial startup: ${metrics.initialStartup}`]);
|
||||
table.push(['app.isReady => window.loadUrl()', metrics.timers.ellapsedWindowLoad, '[main]', `initial startup: ${metrics.initialStartup}`]);
|
||||
table.push(['window.loadUrl() => begin to require(workbench.main.js)', metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)]);
|
||||
table.push(['require(workbench.main.js)', metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${stats ? `, node_modules took ${stats.nodeRequireTotal}ms` : ''}`]);
|
||||
table.push(['require & init workspace storage', metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]', undefined]);
|
||||
table.push(['init workspace service', metrics.timers.ellapsedWorkspaceServiceInit, '[renderer]', undefined]);
|
||||
table.push(['register extensions & spawn extension host', metrics.timers.ellapsedExtensions, '[renderer]', undefined]);
|
||||
table.push(['restore viewlet', metrics.timers.ellapsedViewletRestore, '[renderer]', metrics.viewletId]);
|
||||
table.push(['restore panel', metrics.timers.ellapsedPanelRestore, '[renderer]', metrics.panelId]);
|
||||
table.push(['restore editors', metrics.timers.ellapsedEditorRestore, '[renderer]', `${metrics.editorIds.length}: ${metrics.editorIds.join(', ')}`]);
|
||||
table.push(['overall workbench load', metrics.timers.ellapsedWorkbench, '[renderer]', undefined]);
|
||||
table.push(['workbench ready', metrics.ellapsed, '[main->renderer]', undefined]);
|
||||
table.push(['extensions registered', metrics.timers.ellapsedExtensionsReady, '[renderer]', undefined]);
|
||||
|
||||
md.heading(2, 'Performance Marks');
|
||||
md.table(['What', 'Duration', 'Process', 'Info'], table);
|
||||
}
|
||||
|
||||
private _addExtensionsTable(md: MarkdownBuilder): void {
|
||||
|
||||
const eager: ({ toString(): string })[][] = [];
|
||||
const normal: ({ toString(): string })[][] = [];
|
||||
let extensionsStatus = this._extensionService.getExtensionsStatus();
|
||||
for (let id in extensionsStatus) {
|
||||
const { activationTimes: times } = extensionsStatus[id];
|
||||
if (!times) {
|
||||
continue;
|
||||
}
|
||||
if (times.startup) {
|
||||
eager.push([id, times.startup, times.codeLoadingTime, times.activateCallTime, times.activateResolvedTime, times.activationEvent]);
|
||||
} else {
|
||||
normal.push([id, times.startup, times.codeLoadingTime, times.activateCallTime, times.activateResolvedTime, times.activationEvent]);
|
||||
}
|
||||
}
|
||||
|
||||
const table = eager.concat(normal);
|
||||
if (table.length > 0) {
|
||||
md.heading(2, 'Extension Activation Stats');
|
||||
md.table(
|
||||
['Extension', 'Eager', 'Load Code', 'Call Activate', 'Finish Activate', 'Event'],
|
||||
table
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _addRawPerfMarks(md: MarkdownBuilder): void {
|
||||
md.heading(2, 'Raw Perf Marks');
|
||||
md.value += '```\n';
|
||||
md.value += `Name\tTimestamp\tDelta\tTotal\n`;
|
||||
let lastStartTime = -1;
|
||||
let total = 0;
|
||||
for (const { name, timestamp: startTime } of perf.getEntries()) {
|
||||
let delta = lastStartTime !== -1 ? startTime - lastStartTime : 0;
|
||||
total += delta;
|
||||
md.value += `${name}\t${startTime}\t${delta}\t${total}\n`;
|
||||
lastStartTime = startTime;
|
||||
}
|
||||
md.value += '```\n';
|
||||
}
|
||||
|
||||
private _addLoaderStats(md: MarkdownBuilder, stats?: LoaderStats): void {
|
||||
if (stats) {
|
||||
md.heading(2, 'Loader Stats');
|
||||
md.heading(3, 'Load AMD-module');
|
||||
md.table(['Module', 'Duration'], stats.amdLoad);
|
||||
md.blank();
|
||||
md.heading(3, 'Load commonjs-module');
|
||||
md.table(['Module', 'Duration'], stats.nodeRequire);
|
||||
md.blank();
|
||||
md.heading(3, 'Invoke AMD-module factory');
|
||||
md.table(['Module', 'Duration'], stats.amdInvoke);
|
||||
md.blank();
|
||||
md.heading(3, 'Invoke commonjs-module');
|
||||
md.table(['Module', 'Duration'], stats.nodeEval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class LoaderStats {
|
||||
readonly amdLoad: (string | number)[][];
|
||||
readonly amdInvoke: (string | number)[][];
|
||||
readonly nodeRequire: (string | number)[][];
|
||||
readonly nodeEval: (string | number)[][];
|
||||
readonly nodeRequireTotal: number;
|
||||
|
||||
|
||||
static get(): LoaderStats {
|
||||
|
||||
|
||||
const amdLoadScript = new Map<string, number>();
|
||||
const amdInvokeFactory = new Map<string, number>();
|
||||
const nodeRequire = new Map<string, number>();
|
||||
const nodeEval = new Map<string, number>();
|
||||
|
||||
function mark(map: Map<string, number>, stat: LoaderEvent) {
|
||||
if (map.has(stat.detail)) {
|
||||
// console.warn('BAD events, DOUBLE start', stat);
|
||||
// map.delete(stat.detail);
|
||||
return;
|
||||
}
|
||||
map.set(stat.detail, -stat.timestamp);
|
||||
}
|
||||
|
||||
function diff(map: Map<string, number>, stat: LoaderEvent) {
|
||||
let duration = map.get(stat.detail);
|
||||
if (!duration) {
|
||||
// console.warn('BAD events, end WITHOUT start', stat);
|
||||
// map.delete(stat.detail);
|
||||
return;
|
||||
}
|
||||
if (duration >= 0) {
|
||||
// console.warn('BAD events, DOUBLE end', stat);
|
||||
// map.delete(stat.detail);
|
||||
return;
|
||||
}
|
||||
map.set(stat.detail, duration + stat.timestamp);
|
||||
}
|
||||
|
||||
const stats = mergeSort(require.getStats().slice(0), (a, b) => a.timestamp - b.timestamp);
|
||||
|
||||
for (const stat of stats) {
|
||||
switch (stat.type) {
|
||||
case LoaderEventType.BeginLoadingScript:
|
||||
mark(amdLoadScript, stat);
|
||||
break;
|
||||
case LoaderEventType.EndLoadingScriptOK:
|
||||
case LoaderEventType.EndLoadingScriptError:
|
||||
diff(amdLoadScript, stat);
|
||||
break;
|
||||
|
||||
case LoaderEventType.BeginInvokeFactory:
|
||||
mark(amdInvokeFactory, stat);
|
||||
break;
|
||||
case LoaderEventType.EndInvokeFactory:
|
||||
diff(amdInvokeFactory, stat);
|
||||
break;
|
||||
|
||||
case LoaderEventType.NodeBeginNativeRequire:
|
||||
mark(nodeRequire, stat);
|
||||
break;
|
||||
case LoaderEventType.NodeEndNativeRequire:
|
||||
diff(nodeRequire, stat);
|
||||
break;
|
||||
|
||||
case LoaderEventType.NodeBeginEvaluatingScript:
|
||||
mark(nodeEval, stat);
|
||||
break;
|
||||
case LoaderEventType.NodeEndEvaluatingScript:
|
||||
diff(nodeEval, stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let nodeRequireTotal = 0;
|
||||
nodeRequire.forEach(value => nodeRequireTotal += value);
|
||||
|
||||
function to2dArray(map: Map<string, number>): (string | number)[][] {
|
||||
let res: (string | number)[][] = [];
|
||||
map.forEach((value, index) => res.push([index, value]));
|
||||
return res;
|
||||
}
|
||||
|
||||
return {
|
||||
amdLoad: to2dArray(amdLoadScript),
|
||||
amdInvoke: to2dArray(amdInvokeFactory),
|
||||
nodeRequire: to2dArray(nodeRequire),
|
||||
nodeEval: to2dArray(nodeEval),
|
||||
nodeRequireTotal
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class MarkdownBuilder {
|
||||
|
||||
value: string = '';
|
||||
|
||||
heading(level: number, value: string): this {
|
||||
this.value += `${repeat('#', level)} ${value}\n\n`;
|
||||
return this;
|
||||
}
|
||||
|
||||
blank() {
|
||||
this.value += '\n';
|
||||
return this;
|
||||
}
|
||||
|
||||
li(value: string) {
|
||||
this.value += `* ${value}\n`;
|
||||
return this;
|
||||
}
|
||||
|
||||
table(header: string[], rows: Array<Array<{ toString(): string } | undefined>>) {
|
||||
let lengths: number[] = [];
|
||||
header.forEach((cell, ci) => {
|
||||
lengths[ci] = cell.length;
|
||||
});
|
||||
rows.forEach(row => {
|
||||
row.forEach((cell, ci) => {
|
||||
if (typeof cell === 'undefined') {
|
||||
cell = row[ci] = '-';
|
||||
}
|
||||
const len = cell.toString().length;
|
||||
lengths[ci] = Math.max(len, lengths[ci]);
|
||||
});
|
||||
});
|
||||
|
||||
// header
|
||||
header.forEach((cell, ci) => { this.value += `| ${cell + repeat(' ', lengths[ci] - cell.toString().length)} `; });
|
||||
this.value += '|\n';
|
||||
header.forEach((_cell, ci) => { this.value += `| ${repeat('-', lengths[ci])} `; });
|
||||
this.value += '|\n';
|
||||
|
||||
// cells
|
||||
rows.forEach(row => {
|
||||
row.forEach((cell, ci) => {
|
||||
if (typeof cell !== 'undefined') {
|
||||
this.value += `| ${cell + repeat(' ', lengths[ci] - cell.toString().length)} `;
|
||||
}
|
||||
});
|
||||
this.value += '|\n';
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { dirname, join, basename } from 'vs/base/common/path';
|
||||
import { del, exists, readdir, readFile } from 'vs/base/node/pfs';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser/perfviewEditor';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export class StartupProfiler implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IWindowsService private readonly _windowsService: IWindowsService,
|
||||
@IDialogService private readonly _dialogService: IDialogService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@ITextModelService private readonly _textModelResolverService: ITextModelService,
|
||||
@IClipboardService private readonly _clipboardService: IClipboardService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
) {
|
||||
// wait for everything to be ready
|
||||
Promise.all([
|
||||
lifecycleService.when(LifecyclePhase.Eventually),
|
||||
extensionService.whenInstalledExtensionsRegistered()
|
||||
]).then(() => {
|
||||
this._stopProfiling();
|
||||
});
|
||||
}
|
||||
|
||||
private _stopProfiling(): void {
|
||||
|
||||
const profileFilenamePrefix = this._environmentService.args['prof-startup-prefix'];
|
||||
if (!profileFilenamePrefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dir = dirname(profileFilenamePrefix);
|
||||
const prefix = basename(profileFilenamePrefix);
|
||||
|
||||
const removeArgs: string[] = ['--prof-startup'];
|
||||
const markerFile = readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|')))
|
||||
.then(() => del(profileFilenamePrefix)) // (1) delete the file to tell the main process to stop profiling
|
||||
.then(() => new Promise(resolve => { // (2) wait for main that recreates the fail to signal profiling has stopped
|
||||
const check = () => {
|
||||
exists(profileFilenamePrefix).then(exists => {
|
||||
if (exists) {
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(check, 500);
|
||||
}
|
||||
});
|
||||
};
|
||||
check();
|
||||
}))
|
||||
.then(() => del(profileFilenamePrefix)); // (3) finally delete the file again
|
||||
|
||||
markerFile.then(() => {
|
||||
return readdir(dir).then(files => files.filter(value => value.indexOf(prefix) === 0));
|
||||
}).then(files => {
|
||||
const profileFiles = files.reduce((prev, cur) => `${prev}${join(dir, cur)}\n`, '\n');
|
||||
|
||||
return this._dialogService.confirm({
|
||||
type: 'info',
|
||||
message: localize('prof.message', "Successfully created profiles."),
|
||||
detail: localize('prof.detail', "Please create an issue and manually attach the following files:\n{0}", profileFiles),
|
||||
primaryButton: localize('prof.restartAndFileIssue', "Create Issue and Restart"),
|
||||
secondaryButton: localize('prof.restart', "Restart")
|
||||
}).then(res => {
|
||||
if (res.confirmed) {
|
||||
Promise.all<any>([
|
||||
this._windowsService.showItemInFolder(URI.file(join(dir, files[0]))),
|
||||
this._createPerfIssue(files)
|
||||
]).then(() => {
|
||||
// keep window stable until restart is selected
|
||||
return this._dialogService.confirm({
|
||||
type: 'info',
|
||||
message: localize('prof.thanks', "Thanks for helping us."),
|
||||
detail: localize('prof.detail.restart', "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", this._environmentService.appNameLong),
|
||||
primaryButton: localize('prof.restart', "Restart"),
|
||||
secondaryButton: undefined
|
||||
}).then(() => {
|
||||
// now we are ready to restart
|
||||
this._windowsService.relaunch({ removeArgs });
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
// simply restart
|
||||
this._windowsService.relaunch({ removeArgs });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _createPerfIssue(files: string[]): Promise<void> {
|
||||
return this._textModelResolverService.createModelReference(PerfviewInput.Uri).then(ref => {
|
||||
|
||||
this._clipboardService.writeText(ref.object.textEditorModel.getValue());
|
||||
ref.dispose();
|
||||
|
||||
const body = `
|
||||
1. :warning: We have copied additional data to your clipboard. Make sure to **paste** here. :warning:
|
||||
1. :warning: Make sure to **attach** these files from your *home*-directory: :warning:\n${files.map(file => `-\`${file}\``).join('\n')}
|
||||
`;
|
||||
|
||||
const baseUrl = product.reportIssueUrl;
|
||||
const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&';
|
||||
|
||||
window.open(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { appendFile } from 'fs';
|
||||
import { nfcall, timeout } from 'vs/base/common/async';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IUpdateService } from 'vs/platform/update/common/update';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import * as files from 'vs/workbench/contrib/files/common/files';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { didUseCachedData, ITimerService } from 'vs/workbench/services/timer/electron-browser/timerService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { getEntries } from 'vs/base/common/performance';
|
||||
|
||||
export class StartupTimings implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@ITimerService private readonly _timerService: ITimerService,
|
||||
@IWindowsService private readonly _windowsService: IWindowsService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IViewletService private readonly _viewletService: IViewletService,
|
||||
@IPanelService private readonly _panelService: IPanelService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@IUpdateService private readonly _updateService: IUpdateService,
|
||||
@IEnvironmentService private readonly _envService: IEnvironmentService,
|
||||
) {
|
||||
//
|
||||
this._report().catch(onUnexpectedError);
|
||||
}
|
||||
|
||||
private async _report() {
|
||||
const isStandardStartup = await this._isStandardStartup();
|
||||
this._reportStartupTimes().catch(onUnexpectedError);
|
||||
this._appendStartupTimes(isStandardStartup).catch(onUnexpectedError);
|
||||
this._reportPerfTicks();
|
||||
}
|
||||
|
||||
private async _reportStartupTimes(): Promise<void> {
|
||||
const metrics = await this._timerService.startupMetrics;
|
||||
|
||||
/* __GDPR__
|
||||
"startupTimeVaried" : {
|
||||
"${include}": [
|
||||
"${IStartupMetrics}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('startupTimeVaried', metrics);
|
||||
}
|
||||
|
||||
private async _appendStartupTimes(isStandardStartup: boolean) {
|
||||
let appendTo = this._envService.args['prof-append-timers'];
|
||||
if (!appendTo) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const { sessionId } = await this._telemetryService.getTelemetryInfo();
|
||||
|
||||
Promise.all([
|
||||
this._timerService.startupMetrics,
|
||||
timeout(15000), // wait: cached data creation, telemetry sending
|
||||
]).then(([startupMetrics]) => {
|
||||
return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameShort}\t${(product.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${isStandardStartup ? 'standard_start' : 'NO_standard_start'}\n`);
|
||||
}).then(() => {
|
||||
this._windowsService.quit();
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this._windowsService.quit();
|
||||
});
|
||||
}
|
||||
|
||||
private async _isStandardStartup(): Promise<boolean> {
|
||||
// check for standard startup:
|
||||
// * new window (no reload)
|
||||
// * just one window
|
||||
// * explorer viewlet visible
|
||||
// * one text editor (not multiple, not webview, welcome etc...)
|
||||
// * cached data present (not rejected, not created)
|
||||
if (this._lifecycleService.startupKind !== StartupKind.NewWindow) {
|
||||
return false;
|
||||
}
|
||||
if (await this._windowsService.getWindowCount() !== 1) {
|
||||
return false;
|
||||
}
|
||||
const activeViewlet = this._viewletService.getActiveViewlet();
|
||||
if (!activeViewlet || activeViewlet.getId() !== files.VIEWLET_ID) {
|
||||
return false;
|
||||
}
|
||||
const visibleControls = this._editorService.visibleControls;
|
||||
if (visibleControls.length !== 1 || !isCodeEditor(visibleControls[0].getControl())) {
|
||||
return false;
|
||||
}
|
||||
if (this._panelService.getActivePanel()) {
|
||||
return false;
|
||||
}
|
||||
if (!didUseCachedData()) {
|
||||
return false;
|
||||
}
|
||||
if (!await this._updateService.isLatestVersion()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private _reportPerfTicks(): void {
|
||||
const entries: Record<string, number> = Object.create(null);
|
||||
for (const entry of getEntries()) {
|
||||
entries[entry.name] = entry.timestamp;
|
||||
}
|
||||
/* __GDPR__
|
||||
"startupRawTimers" : {
|
||||
"entries": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('startupRawTimers', { entries });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user