Skip to content

Commit b7d5b65

Browse files
authored
perf - log slow running workbench contribs (microsoft#159656)
1 parent d00804e commit b7d5b65

File tree

109 files changed

+225
-193
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+225
-193
lines changed

src/vs/workbench/api/browser/extensionHost.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,4 @@ export class ExtensionPoints implements IWorkbenchContribution {
8989
}
9090
}
9191

92-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Starting);
92+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, 'ExtensionPoints', LifecyclePhase.Starting);

src/vs/workbench/api/browser/viewsExtensionPoint.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,4 +643,4 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
643643
}
644644

645645
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
646-
workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, LifecyclePhase.Starting);
646+
workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, 'ViewsExtensionHandler', LifecyclePhase.Starting);

src/vs/workbench/browser/actions/textInputActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,4 @@ export class TextInputActionsProvider extends Disposable implements IWorkbenchCo
9999
}
100100
}
101101

102-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextInputActionsProvider, LifecyclePhase.Ready);
102+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TextInputActionsProvider, 'TextInputActionsProvider', LifecyclePhase.Ready);

src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,4 @@ export class DialogHandlerContribution extends Disposable implements IWorkbenchC
7575
}
7676

7777
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
78-
workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting);
78+
workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, 'DialogHandlerContribution', LifecyclePhase.Starting);

src/vs/workbench/browser/parts/editor/editor.contribution.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEdit
122122

123123
//#region Workbench Contributions
124124

125-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorAutoSave, LifecyclePhase.Ready);
126-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, LifecyclePhase.Ready);
127-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(UntitledTextEditorWorkingCopyEditorHandler, LifecyclePhase.Ready);
128-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicEditorConfigurations, LifecyclePhase.Ready);
125+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorAutoSave, 'EditorAutoSave', LifecyclePhase.Ready);
126+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, 'EditorStatus', LifecyclePhase.Ready);
127+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(UntitledTextEditorWorkingCopyEditorHandler, 'UntitledTextEditorWorkingCopyEditorHandler', LifecyclePhase.Ready);
128+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicEditorConfigurations, 'DynamicEditorConfigurations', LifecyclePhase.Ready);
129129

130130
registerEditorContribution(FloatingClickMenu.ID, FloatingClickMenu);
131131
registerEditorContribution(OpenWorkspaceButtonContribution.ID, OpenWorkspaceButtonContribution);

src/vs/workbench/browser/workbench.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
1818
(function registerConfiguration(): void {
1919

2020
// Migration support
21-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, LifecyclePhase.Eventually);
21+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, 'ConfigurationMigrationWorkbenchContribution', LifecyclePhase.Eventually);
2222

2323
// Workbench
2424
registry.registerConfiguration({

src/vs/workbench/common/contributions.ts

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecyc
88
import { Registry } from 'vs/platform/registry/common/platform';
99
import { runWhenIdle, IdleDeadline } from 'vs/base/common/async';
1010
import { mark } from 'vs/base/common/performance';
11+
import { ILogService } from 'vs/platform/log/common/log';
1112

1213
/**
1314
* A workbench contribution that will be loaded when the workbench starts and disposed when the workbench shuts down.
@@ -22,15 +23,21 @@ export namespace Extensions {
2223

2324
type IWorkbenchContributionSignature<Service extends BrandedService[]> = new (...services: Service) => IWorkbenchContribution;
2425

26+
interface IWorkbenchContributionRegistration {
27+
readonly id: string;
28+
readonly ctor: IConstructorSignature<IWorkbenchContribution>;
29+
}
30+
2531
export interface IWorkbenchContributionsRegistry {
2632

2733
/**
2834
* Registers a workbench contribution to the platform that will be loaded when the workbench starts and disposed when
2935
* the workbench shuts down.
3036
*
37+
* @param id the identifier of the contribution.
3138
* @param phase the lifecycle phase when to instantiate the contribution.
3239
*/
33-
registerWorkbenchContribution<Services extends BrandedService[]>(contribution: IWorkbenchContributionSignature<Services>, phase: LifecyclePhase): void;
40+
registerWorkbenchContribution<Services extends BrandedService[]>(contribution: IWorkbenchContributionSignature<Services>, id: string, phase: LifecyclePhase): void;
3441

3542
/**
3643
* Starts the registry by providing the required services.
@@ -42,14 +49,16 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry
4249

4350
private instantiationService: IInstantiationService | undefined;
4451
private lifecycleService: ILifecycleService | undefined;
52+
private logService: ILogService | undefined;
4553

46-
private readonly toBeInstantiated = new Map<LifecyclePhase, IConstructorSignature<IWorkbenchContribution>[]>();
54+
private readonly toBeInstantiated = new Map<LifecyclePhase, IWorkbenchContributionRegistration[]>();
4755

48-
registerWorkbenchContribution(ctor: IConstructorSignature<IWorkbenchContribution>, phase: LifecyclePhase = LifecyclePhase.Starting): void {
56+
registerWorkbenchContribution(ctor: IConstructorSignature<IWorkbenchContribution>, id: string, phase: LifecyclePhase = LifecyclePhase.Starting): void {
57+
const contribution = { id, ctor };
4958

5059
// Instantiate directly if we are already matching the provided phase
51-
if (this.instantiationService && this.lifecycleService && this.lifecycleService.phase >= phase) {
52-
this.instantiationService.createInstance(ctor);
60+
if (this.instantiationService && this.lifecycleService && this.logService && this.lifecycleService.phase >= phase) {
61+
this.safeCreateInstance(this.instantiationService, this.logService, contribution, phase);
5362
}
5463

5564
// Otherwise keep contributions by lifecycle phase
@@ -60,33 +69,34 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry
6069
this.toBeInstantiated.set(phase, toBeInstantiated);
6170
}
6271

63-
toBeInstantiated.push(ctor as IConstructorSignature<IWorkbenchContribution>);
72+
toBeInstantiated.push(contribution);
6473
}
6574
}
6675

6776
start(accessor: ServicesAccessor): void {
6877
const instantiationService = this.instantiationService = accessor.get(IInstantiationService);
6978
const lifecycleService = this.lifecycleService = accessor.get(ILifecycleService);
79+
const logService = this.logService = accessor.get(ILogService);
7080

71-
[LifecyclePhase.Starting, LifecyclePhase.Ready, LifecyclePhase.Restored, LifecyclePhase.Eventually].forEach(phase => {
72-
this.instantiateByPhase(instantiationService, lifecycleService, phase);
73-
});
81+
for (const phase of [LifecyclePhase.Starting, LifecyclePhase.Ready, LifecyclePhase.Restored, LifecyclePhase.Eventually]) {
82+
this.instantiateByPhase(instantiationService, lifecycleService, logService, phase);
83+
}
7484
}
7585

76-
private instantiateByPhase(instantiationService: IInstantiationService, lifecycleService: ILifecycleService, phase: LifecyclePhase): void {
86+
private instantiateByPhase(instantiationService: IInstantiationService, lifecycleService: ILifecycleService, logService: ILogService, phase: LifecyclePhase): void {
7787

7888
// Instantiate contributions directly when phase is already reached
7989
if (lifecycleService.phase >= phase) {
80-
this.doInstantiateByPhase(instantiationService, phase);
90+
this.doInstantiateByPhase(instantiationService, logService, phase);
8191
}
8292

8393
// Otherwise wait for phase to be reached
8494
else {
85-
lifecycleService.when(phase).then(() => this.doInstantiateByPhase(instantiationService, phase));
95+
lifecycleService.when(phase).then(() => this.doInstantiateByPhase(instantiationService, logService, phase));
8696
}
8797
}
8898

89-
private doInstantiateByPhase(instantiationService: IInstantiationService, phase: LifecyclePhase): void {
99+
private doInstantiateByPhase(instantiationService: IInstantiationService, logService: ILogService, phase: LifecyclePhase): void {
90100
const toBeInstantiated = this.toBeInstantiated.get(phase);
91101
if (toBeInstantiated) {
92102
this.toBeInstantiated.delete(phase);
@@ -98,7 +108,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry
98108
mark(`code/willCreateWorkbenchContributions/${phase}`);
99109

100110
for (const ctor of toBeInstantiated) {
101-
this.safeCreateInstance(instantiationService, ctor); // catch error so that other contributions are still considered
111+
this.safeCreateInstance(instantiationService, logService, ctor, phase); // catch error so that other contributions are still considered
102112
}
103113

104114
mark(`code/didCreateWorkbenchContributions/${phase}`);
@@ -113,7 +123,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry
113123
const instantiateSome = (idle: IdleDeadline) => {
114124
while (i < toBeInstantiated.length) {
115125
const ctor = toBeInstantiated[i++];
116-
this.safeCreateInstance(instantiationService, ctor); // catch error so that other contributions are still considered
126+
this.safeCreateInstance(instantiationService, logService, ctor, phase); // catch error so that other contributions are still considered
117127
if (idle.timeRemaining() < 1) {
118128
// time is up -> reschedule
119129
runWhenIdle(instantiateSome, forcedTimeout);
@@ -126,11 +136,20 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry
126136
}
127137
}
128138

129-
private safeCreateInstance(instantiationService: IInstantiationService, ctor: IConstructorSignature<IWorkbenchContribution>): void {
139+
private safeCreateInstance(instantiationService: IInstantiationService, logService: ILogService, contribution: IWorkbenchContributionRegistration, phase: LifecyclePhase): void {
140+
const now: number | undefined = phase < LifecyclePhase.Restored ? Date.now() : undefined;
141+
130142
try {
131-
instantiationService.createInstance(ctor);
143+
instantiationService.createInstance(contribution.ctor);
132144
} catch (error) {
133-
console.error(`Unable to instantiate workbench contribution ${ctor.name}.`, error);
145+
logService.error(`Unable to instantiate workbench contribution ${contribution.id}.`, error);
146+
}
147+
148+
if (typeof now === 'number') {
149+
const time = Date.now() - now;
150+
if (time > 5) {
151+
logService.warn(`Workbench contribution ${contribution.id} blocked restore phase by ${time}ms.`);
152+
}
134153
}
135154
}
136155
}

src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
1717

1818
registerSingleton(IAudioCueService, AudioCueService, false);
1919

20-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineFeatureContribution, LifecyclePhase.Restored);
21-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineDebuggerContribution, LifecyclePhase.Restored);
20+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineFeatureContribution, 'AudioCueLineFeatureContribution', LifecyclePhase.Restored);
21+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineDebuggerContribution, 'AudioCueLineDebuggerContribution', LifecyclePhase.Restored);
2222

2323
const audioCueFeatureBase: IConfigurationPropertySchema = {
2424
'type': 'string',

src/vs/workbench/contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,5 @@ class BracketPairColorizer2TelemetryContribution {
5151
}
5252
}
5353

54-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BracketPairColorizer2TelemetryContribution, LifecyclePhase.Restored);
54+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BracketPairColorizer2TelemetryContribution, 'BracketPairColorizer2TelemetryContribution', LifecyclePhase.Restored);
5555

src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ registerAction2(class ToggleGrouping extends Action2 {
323323
});
324324

325325
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
326-
BulkEditPreviewContribution, LifecyclePhase.Ready
326+
BulkEditPreviewContribution,
327+
'BulkEditPreviewContribution',
328+
LifecyclePhase.Ready
327329
);
328330

329331
const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codicon.lightbulb, localize('refactorPreviewViewIcon', 'View icon of the refactor preview view.'));

0 commit comments

Comments
 (0)