Path: blob/main/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IProductService } from '../../../../platform/product/common/productService.js';6import { Action } from '../../../../base/common/actions.js';7import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';8import { URI } from '../../../../base/common/uri.js';9import { IExtensionHostProfile } from '../../../services/extensions/common/extensions.js';10import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';11import { localize } from '../../../../nls.js';12import { CancellationToken } from '../../../../base/common/cancellation.js';13import { IRequestService, asText } from '../../../../platform/request/common/request.js';14import { joinPath } from '../../../../base/common/resources.js';15import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';16import { IOpenerService } from '../../../../platform/opener/common/opener.js';17import { INativeHostService } from '../../../../platform/native/common/native.js';18import { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-browser/environmentService.js';19import { Utils } from '../../../../platform/profiling/common/profiling.js';20import { IFileService } from '../../../../platform/files/common/files.js';21import { VSBuffer } from '../../../../base/common/buffer.js';22import { IRequestContext } from '../../../../base/parts/request/common/request.js';2324abstract class RepoInfo {25abstract get base(): string;26abstract get owner(): string;27abstract get repo(): string;2829static fromExtension(desc: IExtensionDescription): RepoInfo | undefined {3031let result: RepoInfo | undefined;3233// scheme:auth/OWNER/REPO/issues/34if (desc.bugs && typeof desc.bugs.url === 'string') {35const base = URI.parse(desc.bugs.url);36const match = /\/([^/]+)\/([^/]+)\/issues\/?$/.exec(desc.bugs.url);37if (match) {38result = {39base: base.with({ path: null, fragment: null, query: null }).toString(true),40owner: match[1],41repo: match[2]42};43}44}45// scheme:auth/OWNER/REPO.git46if (!result && desc.repository && typeof desc.repository.url === 'string') {47const base = URI.parse(desc.repository.url);48const match = /\/([^/]+)\/([^/]+)(\.git)?$/.exec(desc.repository.url);49if (match) {50result = {51base: base.with({ path: null, fragment: null, query: null }).toString(true),52owner: match[1],53repo: match[2]54};55}56}5758// for now only GH is supported59if (result && result.base.indexOf('github') === -1) {60result = undefined;61}6263return result;64}65}6667export class SlowExtensionAction extends Action {6869constructor(70readonly extension: IExtensionDescription,71readonly profile: IExtensionHostProfile,72@IInstantiationService private readonly _instantiationService: IInstantiationService,73) {74super('report.slow', localize('cmd.reportOrShow', "Performance Issue"), 'extension-action report-issue');75this.enabled = Boolean(RepoInfo.fromExtension(extension));76}7778override async run(): Promise<void> {79const action = await this._instantiationService.invokeFunction(createSlowExtensionAction, this.extension, this.profile);80if (action) {81await action.run();82}83}84}8586export async function createSlowExtensionAction(87accessor: ServicesAccessor,88extension: IExtensionDescription,89profile: IExtensionHostProfile90): Promise<Action | undefined> {9192const info = RepoInfo.fromExtension(extension);93if (!info) {94return undefined;95}9697const requestService = accessor.get(IRequestService);98const instaService = accessor.get(IInstantiationService);99const url = `https://api.github.com/search/issues?q=is:issue+state:open+in:title+repo:${info.owner}/${info.repo}+%22Extension+causes+high+cpu+load%22`;100let res: IRequestContext;101try {102res = await requestService.request({ url }, CancellationToken.None);103} catch {104return undefined;105}106const rawText = await asText(res);107if (!rawText) {108return undefined;109}110111const data = <{ total_count: number }>JSON.parse(rawText);112if (!data || typeof data.total_count !== 'number') {113return undefined;114} else if (data.total_count === 0) {115return instaService.createInstance(ReportExtensionSlowAction, extension, info, profile);116} else {117return instaService.createInstance(ShowExtensionSlowAction, extension, info, profile);118}119}120121class ReportExtensionSlowAction extends Action {122123constructor(124readonly extension: IExtensionDescription,125readonly repoInfo: RepoInfo,126readonly profile: IExtensionHostProfile,127@IDialogService private readonly _dialogService: IDialogService,128@IOpenerService private readonly _openerService: IOpenerService,129@IProductService private readonly _productService: IProductService,130@INativeHostService private readonly _nativeHostService: INativeHostService,131@INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,132@IFileService private readonly _fileService: IFileService,133) {134super('report.slow', localize('cmd.report', "Report Issue"));135}136137override async run(): Promise<void> {138139// rewrite pii (paths) and store on disk140const data = Utils.rewriteAbsolutePaths(this.profile.data, 'pii_removed');141const path = joinPath(this._environmentService.tmpDir, `${this.extension.identifier.value}-unresponsive.cpuprofile.txt`);142await this._fileService.writeFile(path, VSBuffer.fromString(JSON.stringify(data, undefined, 4)));143144// build issue145const os = await this._nativeHostService.getOSProperties();146const title = encodeURIComponent('Extension causes high cpu load');147const osVersion = `${os.type} ${os.arch} ${os.release}`;148const message = `:warning: Make sure to **attach** this file from your *home*-directory:\n:warning:\`${path}\`\n\nFind more details here: https://github.com/microsoft/vscode/wiki/Explain-extension-causes-high-cpu-load`;149const body = encodeURIComponent(`- Issue Type: \`Performance\`150- Extension Name: \`${this.extension.name}\`151- Extension Version: \`${this.extension.version}\`152- OS Version: \`${osVersion}\`153- VS Code version: \`${this._productService.version}\`\n\n${message}`);154155const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`;156this._openerService.open(URI.parse(url));157158this._dialogService.info(159localize('attach.title', "Did you attach the CPU-Profile?"),160localize('attach.msg', "This is a reminder to make sure that you have not forgotten to attach '{0}' to the issue you have just created.", path.fsPath)161);162}163}164165class ShowExtensionSlowAction extends Action {166167constructor(168readonly extension: IExtensionDescription,169readonly repoInfo: RepoInfo,170readonly profile: IExtensionHostProfile,171@IDialogService private readonly _dialogService: IDialogService,172@IOpenerService private readonly _openerService: IOpenerService,173@INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,174@IFileService private readonly _fileService: IFileService,175176) {177super('show.slow', localize('cmd.show', "Show Issues"));178}179180override async run(): Promise<void> {181182// rewrite pii (paths) and store on disk183const data = Utils.rewriteAbsolutePaths(this.profile.data, 'pii_removed');184const path = joinPath(this._environmentService.tmpDir, `${this.extension.identifier.value}-unresponsive.cpuprofile.txt`);185await this._fileService.writeFile(path, VSBuffer.fromString(JSON.stringify(data, undefined, 4)));186187// show issues188const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues?utf8=✓&q=is%3Aissue+state%3Aopen+%22Extension+causes+high+cpu+load%22`;189this._openerService.open(URI.parse(url));190191this._dialogService.info(192localize('attach.title', "Did you attach the CPU-Profile?"),193localize('attach.msg2', "This is a reminder to make sure that you have not forgotten to attach '{0}' to an existing performance issue.", path.fsPath)194);195}196}197198199