Не подтверждена Коммит e0dddec5 создал по автору Connor Peet's avatar Connor Peet
Просмотр файлов

extensions: allow opening extension host CPU profile in the built-in profile viewer

Fixes #227168

Also moves some old Action registrations to Action2 and uses proper
menu registration for context menu display.
владелец 9d28a232
Branches недоступно
Не найдено связанных запросов на слияние
Отображение с 235 дополнений и 230 удаления
+235 -230
......@@ -82,6 +82,7 @@ export class MenuId {
static readonly ExplorerContext = new MenuId('ExplorerContext');
static readonly ExplorerContextShare = new MenuId('ExplorerContextShare');
static readonly ExtensionContext = new MenuId('ExtensionContext');
static readonly ExtensionEditorContextMenu = new MenuId('ExtensionEditorContextMenu');
static readonly GlobalActivity = new MenuId('GlobalActivity');
static readonly CommandCenter = new MenuId('CommandCenter');
static readonly CommandCenterCenter = new MenuId('CommandCenterCenter');
......
......@@ -13,13 +13,12 @@ import { Action, IAction, Separator } from '../../../../base/common/actions.js';
import { isNonEmptyArray } from '../../../../base/common/arrays.js';
import { RunOnceScheduler } from '../../../../base/common/async.js';
import { fromNow } from '../../../../base/common/date.js';
import { memoize } from '../../../../base/common/decorators.js';
import { IDisposable, dispose } from '../../../../base/common/lifecycle.js';
import { Schemas } from '../../../../base/common/network.js';
import './media/runtimeExtensionsEditor.css';
import * as nls from '../../../../nls.js';
import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';
import { Action2, MenuId } from '../../../../platform/actions/common/actions.js';
import { createAndFillInContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
import { Action2, IMenuService, MenuId } from '../../../../platform/actions/common/actions.js';
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
......@@ -35,9 +34,6 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet
import { editorBackground } from '../../../../platform/theme/common/colorRegistry.js';
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
import { EditorPane } from '../../../browser/parts/editor/editorPane.js';
import { errorIcon, warningIcon } from './extensionsIcons.js';
import { IExtension, IExtensionsWorkbenchService } from '../common/extensions.js';
import { RuntimeExtensionsInput } from '../common/runtimeExtensionsInput.js';
import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';
import { IEditorService } from '../../../services/editor/common/editorService.js';
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
......@@ -45,6 +41,10 @@ import { Extensions, IExtensionFeaturesManagementService, IExtensionFeaturesRegi
import { DefaultIconPath, EnablementState } from '../../../services/extensionManagement/common/extensionManagement.js';
import { LocalWebWorkerRunningLocation } from '../../../services/extensions/common/extensionRunningLocation.js';
import { IExtensionHostProfile, IExtensionService, IExtensionsStatus } from '../../../services/extensions/common/extensions.js';
import { IExtension, IExtensionsWorkbenchService } from '../common/extensions.js';
import { RuntimeExtensionsInput } from '../common/runtimeExtensionsInput.js';
import { errorIcon, warningIcon } from './extensionsIcons.js';
import './media/runtimeExtensionsEditor.css';
interface IExtensionProfileInformation {
/**
......@@ -81,7 +81,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService private readonly _extensionService: IExtensionService,
@INotificationService private readonly _notificationService: INotificationService,
......@@ -93,6 +93,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
@IClipboardService private readonly _clipboardService: IClipboardService,
@IExtensionFeaturesManagementService private readonly _extensionFeaturesManagementService: IExtensionFeaturesManagementService,
@IHoverService private readonly _hoverService: IHoverService,
@IMenuService private readonly _menuService: IMenuService,
) {
super(AbstractRuntimeExtensionsEditor.ID, group, telemetryService, themeService, storageService);
......@@ -496,14 +497,9 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
}
actions.push(new Separator());
const profileAction = this._createProfileAction();
if (profileAction) {
actions.push(profileAction);
}
const saveExtensionHostProfileAction = this.saveExtensionHostProfileAction;
if (saveExtensionHostProfileAction) {
actions.push(saveExtensionHostProfileAction);
}
const menuActions = this._menuService.getMenuActions(MenuId.ExtensionEditorContextMenu, this.contextKeyService);
createAndFillInContextMenuActions(menuActions, { primary: [], secondary: actions });
this._contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
......@@ -512,11 +508,6 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
});
}
@memoize
private get saveExtensionHostProfileAction(): IAction | null {
return this._createSaveExtensionHostProfileAction();
}
public layout(dimension: Dimension): void {
this._list?.layout(dimension.height);
}
......@@ -525,8 +516,6 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
protected abstract _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined;
protected abstract _createSlowExtensionAction(element: IRuntimeExtension): Action | null;
protected abstract _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null;
protected abstract _createSaveExtensionHostProfileAction(): Action | null;
protected abstract _createProfileAction(): Action | null;
}
export class ShowRuntimeExtensionsAction extends Action2 {
......
......@@ -29,12 +29,4 @@ export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
}
return null;
}
protected _createSaveExtensionHostProfileAction(): Action | null {
return null;
}
protected _createProfileAction(): Action | null {
return null;
}
}
......@@ -3,62 +3,73 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from '../../../../base/common/actions.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { randomPort } from '../../../../base/common/ports.js';
import * as nls from '../../../../nls.js';
import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';
import { Action2, MenuId } from '../../../../platform/actions/common/actions.js';
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
import { INativeHostService } from '../../../../platform/native/common/native.js';
import { IProductService } from '../../../../platform/product/common/productService.js';
import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js';
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { ActiveEditorContext } from '../../../common/contextkeys.js';
import { IWorkbenchContribution } from '../../../common/contributions.js';
import { IConfig, IDebugService } from '../../debug/common/debug.js';
import { ExtensionHostKind } from '../../../services/extensions/common/extensionHostKind.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
import { IHostService } from '../../../services/host/browser/host.js';
import { IConfig, IDebugService } from '../../debug/common/debug.js';
import { RuntimeExtensionsEditor } from './runtimeExtensionsEditor.js';
export class DebugExtensionHostAction extends Action {
static readonly ID = 'workbench.extensions.action.debugExtensionHost';
static readonly LABEL = nls.localize('debugExtensionHost', "Start Debugging Extension Host In New Window");
static readonly CSS_CLASS = 'debug-extension-host';
constructor(
@INativeHostService private readonly _nativeHostService: INativeHostService,
@IDialogService private readonly _dialogService: IDialogService,
@IExtensionService private readonly _extensionService: IExtensionService,
@IProductService private readonly productService: IProductService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IHostService private readonly _hostService: IHostService,
) {
super(DebugExtensionHostAction.ID, DebugExtensionHostAction.LABEL, DebugExtensionHostAction.CSS_CLASS);
export class DebugExtensionHostAction extends Action2 {
constructor() {
super({
id: 'workbench.extensions.action.debugExtensionHost',
title: { value: nls.localize('debugExtensionHost', "Start Debugging Extension Host In New Window"), original: 'Start Debugging Extension Host In New Window' },
category: Categories.Developer,
f1: true,
icon: Codicon.debugStart,
menu: {
id: MenuId.EditorTitle,
when: ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID),
group: 'navigation',
}
});
}
override async run(_args: unknown): Promise<any> {
const inspectPorts = await this._extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, false);
if (inspectPorts.length === 0) {
const res = await this._dialogService.confirm({
message: nls.localize('restart1', "Debug Extensions"),
detail: nls.localize('restart2', "In order to debug extensions a restart is required. Do you want to restart '{0}' now?", this.productService.nameLong),
primaryButton: nls.localize({ key: 'restart3', comment: ['&& denotes a mnemonic'] }, "&&Restart")
});
if (res.confirmed) {
await this._nativeHostService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] });
}
run(accessor: ServicesAccessor): void {
const nativeHostService = accessor.get(INativeHostService);
const dialogService = accessor.get(IDialogService);
const extensionService = accessor.get(IExtensionService);
const productService = accessor.get(IProductService);
const instantiationService = accessor.get(IInstantiationService);
const hostService = accessor.get(IHostService);
return;
}
extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, false).then(async inspectPorts => {
if (inspectPorts.length === 0) {
const res = await dialogService.confirm({
message: nls.localize('restart1', "Debug Extensions"),
detail: nls.localize('restart2', "In order to debug extensions a restart is required. Do you want to restart '{0}' now?", productService.nameLong),
primaryButton: nls.localize({ key: 'restart3', comment: ['&& denotes a mnemonic'] }, "&&Restart")
});
if (res.confirmed) {
await nativeHostService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] });
}
return;
}
if (inspectPorts.length > 1) {
// TODO
console.warn(`There are multiple extension hosts available for debugging. Picking the first one...`);
}
if (inspectPorts.length > 1) {
// TODO
console.warn(`There are multiple extension hosts available for debugging. Picking the first one...`);
}
const s = this._instantiationService.createInstance(Storage);
s.storeDebugOnNewWindow(inspectPorts[0].port);
const s = instantiationService.createInstance(Storage);
s.storeDebugOnNewWindow(inspectPorts[0].port);
this._hostService.openWindow();
hostService.openWindow();
});
}
}
......
......@@ -23,6 +23,7 @@ import { ExtensionHostKind } from '../../../services/extensions/common/extension
import { IExtensionHostProfile, IExtensionService, ProfileSession } from '../../../services/extensions/common/extensions.js';
import { ExtensionHostProfiler } from '../../../services/extensions/electron-sandbox/extensionHostProfiler.js';
import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js';
import { URI } from '../../../../base/common/uri.js';
export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService {
......@@ -42,6 +43,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
private profilingStatusBarIndicator: IStatusbarEntryAccessor | undefined;
private readonly profilingStatusBarIndicatorLabelUpdater = this._register(new MutableDisposable());
public lastProfileSavedTo: URI | undefined;
public get state() { return this._state; }
public get lastProfile() { return this._profile; }
......@@ -166,6 +168,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
private _setLastProfile(profile: IExtensionHostProfile) {
this._profile = profile;
this.lastProfileSavedTo = undefined;
this._onDidChangeLastProfile.fire(undefined);
}
......
......@@ -3,32 +3,28 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../base/common/lifecycle.js';
import { localize } from '../../../../nls.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { MenuRegistry, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from '../../../common/contributions.js';
import { registerAction2 } from '../../../../platform/actions/common/actions.js';
import { IExtensionRecommendationNotificationService } from '../../../../platform/extensionRecommendations/common/extensionRecommendations.js';
import { ExtensionRecommendationNotificationServiceChannel } from '../../../../platform/extensionRecommendations/common/extensionRecommendationsIpc.js';
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
import { ServicesAccessor, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { ISharedProcessService } from '../../../../platform/ipc/electron-sandbox/services.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js';
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
import { RuntimeExtensionsEditor, StartExtensionHostProfileAction, StopExtensionHostProfileAction, CONTEXT_PROFILE_SESSION_STATE, CONTEXT_EXTENSION_HOST_PROFILE_RECORDED, SaveExtensionHostProfileAction, IExtensionHostProfileService } from './runtimeExtensionsEditor.js';
import { DebugExtensionHostAction, DebugExtensionsContribution } from './debugExtensionHostAction.js';
import { IEditorSerializer, IEditorFactoryRegistry, EditorExtensions } from '../../../common/editor.js';
import { ActiveEditorContext } from '../../../common/contextkeys.js';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js';
import { EditorExtensions, IEditorFactoryRegistry, IEditorSerializer } from '../../../common/editor.js';
import { EditorInput } from '../../../common/editor/editorInput.js';
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
import { RuntimeExtensionsInput } from '../common/runtimeExtensionsInput.js';
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
import { CleanUpExtensionsFolderAction, OpenExtensionsFolderAction } from './extensionsActions.js';
import { IExtensionRecommendationNotificationService } from '../../../../platform/extensionRecommendations/common/extensionRecommendations.js';
import { ISharedProcessService } from '../../../../platform/ipc/electron-sandbox/services.js';
import { ExtensionRecommendationNotificationServiceChannel } from '../../../../platform/extensionRecommendations/common/extensionRecommendationsIpc.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { RemoteExtensionsInitializerContribution } from './remoteExtensionsInit.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
import { DebugExtensionHostAction, DebugExtensionsContribution } from './debugExtensionHostAction.js';
import { ExtensionHostProfileService } from './extensionProfileService.js';
import { CleanUpExtensionsFolderAction, OpenExtensionsFolderAction } from './extensionsActions.js';
import { ExtensionsAutoProfiler } from './extensionsAutoProfiler.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { RemoteExtensionsInitializerContribution } from './remoteExtensionsInit.js';
import { IExtensionHostProfileService, OpenExtensionHostProfileACtion, RuntimeExtensionsEditor, SaveExtensionHostProfileAction, StartExtensionHostProfileAction, StopExtensionHostProfileAction } from './runtimeExtensionsEditor.js';
// Singletons
registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, InstantiationType.Delayed);
......@@ -76,76 +72,12 @@ workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, Lifecyc
workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually);
workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, LifecyclePhase.Restored);
workbenchRegistry.registerWorkbenchContribution(DebugExtensionsContribution, LifecyclePhase.Restored);
// Register Commands
CommandsRegistry.registerCommand(DebugExtensionHostAction.ID, (accessor: ServicesAccessor, ...args) => {
const instantiationService = accessor.get(IInstantiationService);
return instantiationService.createInstance(DebugExtensionHostAction).run(args);
});
CommandsRegistry.registerCommand(StartExtensionHostProfileAction.ID, (accessor: ServicesAccessor) => {
const instantiationService = accessor.get(IInstantiationService);
instantiationService.createInstance(StartExtensionHostProfileAction, StartExtensionHostProfileAction.ID, StartExtensionHostProfileAction.LABEL).run();
});
CommandsRegistry.registerCommand(StopExtensionHostProfileAction.ID, (accessor: ServicesAccessor) => {
const instantiationService = accessor.get(IInstantiationService);
instantiationService.createInstance(StopExtensionHostProfileAction, StopExtensionHostProfileAction.ID, StopExtensionHostProfileAction.LABEL).run();
});
CommandsRegistry.registerCommand(SaveExtensionHostProfileAction.ID, (accessor: ServicesAccessor) => {
const instantiationService = accessor.get(IInstantiationService);
instantiationService.createInstance(SaveExtensionHostProfileAction, SaveExtensionHostProfileAction.ID, SaveExtensionHostProfileAction.LABEL).run();
});
// Running extensions
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: DebugExtensionHostAction.ID,
title: DebugExtensionHostAction.LABEL,
icon: Codicon.debugStart
},
group: 'navigation',
when: ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID)
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: DebugExtensionHostAction.ID,
title: localize('debugExtensionHost', "Debug Extensions In New Window"),
category: localize('developer', "Developer"),
icon: Codicon.debugStart
},
});
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: StartExtensionHostProfileAction.ID,
title: StartExtensionHostProfileAction.LABEL,
icon: Codicon.circleFilled
},
group: 'navigation',
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.notEqualsTo('running'))
});
// Register Commands
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: StopExtensionHostProfileAction.ID,
title: StopExtensionHostProfileAction.LABEL,
icon: Codicon.debugStop
},
group: 'navigation',
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.isEqualTo('running'))
});
registerAction2(DebugExtensionHostAction);
registerAction2(StartExtensionHostProfileAction);
registerAction2(StopExtensionHostProfileAction);
registerAction2(SaveExtensionHostProfileAction);
registerAction2(OpenExtensionHostProfileACtion);
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: SaveExtensionHostProfileAction.ID,
title: SaveExtensionHostProfileAction.LABEL,
icon: Codicon.saveAll,
precondition: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED
},
group: 'navigation',
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID))
});
......@@ -3,35 +3,40 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from '../../../../nls.js';
import { Action } from '../../../../base/common/actions.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { IInstantiationService, createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { IExtensionsWorkbenchService } from '../common/extensions.js';
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
import { IExtensionService, IExtensionHostProfile } from '../../../services/extensions/common/extensions.js';
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
import { Event } from '../../../../base/common/event.js';
import { INotificationService } from '../../../../platform/notification/common/notification.js';
import { IContextKeyService, RawContextKey, IContextKey } from '../../../../platform/contextkey/common/contextkey.js';
import { IStorageService } from '../../../../platform/storage/common/storage.js';
import { ILabelService } from '../../../../platform/label/common/label.js';
import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
import { SlowExtensionAction } from './extensionsSlowActions.js';
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
import { ReportExtensionIssueAction } from '../common/reportExtensionIssueAction.js';
import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from '../browser/abstractRuntimeExtensionsEditor.js';
import { VSBuffer } from '../../../../base/common/buffer.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { Event } from '../../../../base/common/event.js';
import { Schemas } from '../../../../base/common/network.js';
import { joinPath } from '../../../../base/common/resources.js';
import { URI } from '../../../../base/common/uri.js';
import { IFileService } from '../../../../platform/files/common/files.js';
import { IV8Profile, Utils } from '../../../../platform/profiling/common/profiling.js';
import * as nls from '../../../../nls.js';
import { Action2, IMenuService, MenuId } from '../../../../platform/actions/common/actions.js';
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js';
import { Schemas } from '../../../../base/common/network.js';
import { joinPath } from '../../../../base/common/resources.js';
import { IExtensionFeaturesManagementService } from '../../../services/extensionManagement/common/extensionFeatures.js';
import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';
import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
import { IFileService } from '../../../../platform/files/common/files.js';
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
import { IInstantiationService, ServicesAccessor, createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { ILabelService } from '../../../../platform/label/common/label.js';
import { INotificationService } from '../../../../platform/notification/common/notification.js';
import { IV8Profile, Utils } from '../../../../platform/profiling/common/profiling.js';
import { IStorageService } from '../../../../platform/storage/common/storage.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
import { ActiveEditorContext } from '../../../common/contextkeys.js';
import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';
import { IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js';
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
import { IExtensionFeaturesManagementService } from '../../../services/extensionManagement/common/extensionFeatures.js';
import { IExtensionHostProfile, IExtensionService } from '../../../services/extensions/common/extensions.js';
import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from '../browser/abstractRuntimeExtensionsEditor.js';
import { IExtensionsWorkbenchService } from '../common/extensions.js';
import { ReportExtensionIssueAction } from '../common/reportExtensionIssueAction.js';
import { SlowExtensionAction } from './extensionsSlowActions.js';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
......@@ -52,6 +57,7 @@ export interface IExtensionHostProfileService {
readonly state: ProfileSessionState;
readonly lastProfile: IExtensionHostProfile | null;
lastProfileSavedTo: URI | undefined;
startProfiling(): void;
stopProfiling(): void;
......@@ -82,9 +88,10 @@ export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
@IClipboardService clipboardService: IClipboardService,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
@IExtensionFeaturesManagementService extensionFeaturesManagementService: IExtensionFeaturesManagementService,
@IHoverService hoverService: IHoverService
@IHoverService hoverService: IHoverService,
@IMenuService menuService: IMenuService,
) {
super(group, telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService, clipboardService, extensionFeaturesManagementService, hoverService);
super(group, telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService, clipboardService, extensionFeaturesManagementService, hoverService, menuService);
this._profileInfo = this._extensionHostProfileService.lastProfile;
this._extensionsHostRecorded = CONTEXT_EXTENSION_HOST_PROFILE_RECORDED.bindTo(contextKeyService);
this._profileSessionState = CONTEXT_PROFILE_SESSION_STATE.bindTo(contextKeyService);
......@@ -121,83 +128,150 @@ export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
}
return null;
}
protected _createSaveExtensionHostProfileAction(): Action | null {
return this._instantiationService.createInstance(SaveExtensionHostProfileAction, SaveExtensionHostProfileAction.ID, SaveExtensionHostProfileAction.LABEL);
}
protected _createProfileAction(): Action | null {
const state = this._extensionHostProfileService.state;
const profileAction = (
state === ProfileSessionState.Running
? this._instantiationService.createInstance(StopExtensionHostProfileAction, StopExtensionHostProfileAction.ID, StopExtensionHostProfileAction.LABEL)
: this._instantiationService.createInstance(StartExtensionHostProfileAction, StartExtensionHostProfileAction.ID, StartExtensionHostProfileAction.LABEL)
);
return profileAction;
}
}
export class StartExtensionHostProfileAction extends Action {
export class StartExtensionHostProfileAction extends Action2 {
static readonly ID = 'workbench.extensions.action.extensionHostProfile';
static readonly LABEL = nls.localize('extensionHostProfileStart', "Start Extension Host Profile");
constructor(
id: string = StartExtensionHostProfileAction.ID, label: string = StartExtensionHostProfileAction.LABEL,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
) {
super(id, label);
constructor() {
super({
id: StartExtensionHostProfileAction.ID,
title: { value: StartExtensionHostProfileAction.LABEL, original: 'Start Extension Host Profile' },
precondition: CONTEXT_PROFILE_SESSION_STATE.isEqualTo('none'),
icon: Codicon.circleFilled,
menu: [{
id: MenuId.EditorTitle,
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.notEqualsTo('running')),
group: 'navigation',
}, {
id: MenuId.ExtensionEditorContextMenu,
when: CONTEXT_PROFILE_SESSION_STATE.notEqualsTo('running'),
group: 'profiling',
}]
});
}
override run(): Promise<any> {
this._extensionHostProfileService.startProfiling();
run(accessor: ServicesAccessor): Promise<any> {
const extensionHostProfileService = accessor.get(IExtensionHostProfileService);
extensionHostProfileService.startProfiling();
return Promise.resolve();
}
}
export class StopExtensionHostProfileAction extends Action {
export class StopExtensionHostProfileAction extends Action2 {
static readonly ID = 'workbench.extensions.action.stopExtensionHostProfile';
static readonly LABEL = nls.localize('stopExtensionHostProfileStart', "Stop Extension Host Profile");
constructor(
id: string = StartExtensionHostProfileAction.ID, label: string = StartExtensionHostProfileAction.LABEL,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
) {
super(id, label);
constructor() {
super({
id: StopExtensionHostProfileAction.ID,
title: { value: StopExtensionHostProfileAction.LABEL, original: 'Stop Extension Host Profile' },
icon: Codicon.debugStop,
menu: [{
id: MenuId.EditorTitle,
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.isEqualTo('running')),
group: 'navigation',
}, {
id: MenuId.ExtensionEditorContextMenu,
when: CONTEXT_PROFILE_SESSION_STATE.isEqualTo('running'),
group: 'profiling',
}]
});
}
override run(): Promise<any> {
this._extensionHostProfileService.stopProfiling();
run(accessor: ServicesAccessor): Promise<any> {
const extensionHostProfileService = accessor.get(IExtensionHostProfileService);
extensionHostProfileService.stopProfiling();
return Promise.resolve();
}
}
export class SaveExtensionHostProfileAction extends Action {
export class OpenExtensionHostProfileACtion extends Action2 {
static readonly LABEL = nls.localize('openExtensionHostProfile', "Open Extension Host Profile");
static readonly ID = 'workbench.extensions.action.openExtensionHostProfile';
constructor() {
super({
id: OpenExtensionHostProfileACtion.ID,
title: { value: OpenExtensionHostProfileACtion.LABEL, original: 'Open Extension Host Profile' },
precondition: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED,
icon: Codicon.graph,
menu: [{
id: MenuId.EditorTitle,
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID)),
group: 'navigation',
}, {
id: MenuId.ExtensionEditorContextMenu,
when: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED,
group: 'profiling',
}]
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const extensionHostProfileService = accessor.get(IExtensionHostProfileService);
const commandService = accessor.get(ICommandService);
const editorService = accessor.get(IEditorService);
if (!extensionHostProfileService.lastProfileSavedTo) {
await commandService.executeCommand(SaveExtensionHostProfileAction.ID);
}
if (!extensionHostProfileService.lastProfileSavedTo) {
return;
}
await editorService.openEditor({
resource: extensionHostProfileService.lastProfileSavedTo,
options: {
revealIfOpened: true,
override: 'jsProfileVisualizer.cpuprofile.table',
},
}, SIDE_GROUP);
}
}
export class SaveExtensionHostProfileAction extends Action2 {
static readonly LABEL = nls.localize('saveExtensionHostProfile', "Save Extension Host Profile");
static readonly ID = 'workbench.extensions.action.saveExtensionHostProfile';
constructor(
id: string = SaveExtensionHostProfileAction.ID, label: string = SaveExtensionHostProfileAction.LABEL,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
@IFileService private readonly _fileService: IFileService,
@IFileDialogService private readonly _fileDialogService: IFileDialogService,
) {
super(id, label, undefined, false);
this._extensionHostProfileService.onDidChangeLastProfile(() => {
this.enabled = (this._extensionHostProfileService.lastProfile !== null);
constructor() {
super({
id: SaveExtensionHostProfileAction.ID,
title: { value: SaveExtensionHostProfileAction.LABEL, original: 'Save Extension Host Profile' },
precondition: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED,
icon: Codicon.saveAll,
menu: [{
id: MenuId.EditorTitle,
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID)),
group: 'navigation',
}, {
id: MenuId.ExtensionEditorContextMenu,
when: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED,
group: 'profiling',
}]
});
}
override run(): Promise<any> {
return Promise.resolve(this._asyncRun());
run(accessor: ServicesAccessor): Promise<any> {
const environmentService = accessor.get(IWorkbenchEnvironmentService);
const extensionHostProfileService = accessor.get(IExtensionHostProfileService);
const fileService = accessor.get(IFileService);
const fileDialogService = accessor.get(IFileDialogService);
return this._asyncRun(environmentService, extensionHostProfileService, fileService, fileDialogService);
}
private async _asyncRun(): Promise<any> {
const picked = await this._fileDialogService.showSaveDialog({
private async _asyncRun(
environmentService: IWorkbenchEnvironmentService,
extensionHostProfileService: IExtensionHostProfileService,
fileService: IFileService,
fileDialogService: IFileDialogService
): Promise<any> {
const picked = await fileDialogService.showSaveDialog({
title: nls.localize('saveprofile.dialogTitle', "Save Extension Host Profile"),
availableFileSystems: [Schemas.file],
defaultUri: joinPath(await this._fileDialogService.defaultFilePath(), `CPU-${new Date().toISOString().replace(/[\-:]/g, '')}.cpuprofile`),
defaultUri: joinPath(await fileDialogService.defaultFilePath(), `CPU-${new Date().toISOString().replace(/[\-:]/g, '')}.cpuprofile`),
filters: [{
name: 'CPU Profiles',
extensions: ['cpuprofile', 'txt']
......@@ -208,12 +282,12 @@ export class SaveExtensionHostProfileAction extends Action {
return;
}
const profileInfo = this._extensionHostProfileService.lastProfile;
const profileInfo = extensionHostProfileService.lastProfile;
let dataToWrite: object = profileInfo ? profileInfo.data : {};
let savePath = picked.fsPath;
if (this._environmentService.isBuilt) {
if (environmentService.isBuilt) {
// when running from a not-development-build we remove
// absolute filenames because we don't want to reveal anything
// about users. We also append the `.txt` suffix to make it
......@@ -223,6 +297,9 @@ export class SaveExtensionHostProfileAction extends Action {
savePath = savePath + '.txt';
}
return this._fileService.writeFile(URI.file(savePath), VSBuffer.fromString(JSON.stringify(profileInfo ? profileInfo.data : {}, null, '\t')));
const saveURI = URI.file(savePath);
extensionHostProfileService.lastProfileSavedTo = saveURI;
return fileService.writeFile(saveURI, VSBuffer.fromString(JSON.stringify(profileInfo ? profileInfo.data : {}, null, '\t')));
}
}
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать