diff --git a/src/vs/base/parts/sandbox/common/electronTypes.ts b/src/vs/base/parts/sandbox/common/electronTypes.ts index c7729f338af..bfd996a31da 100644 --- a/src/vs/base/parts/sandbox/common/electronTypes.ts +++ b/src/vs/base/parts/sandbox/common/electronTypes.ts @@ -309,3 +309,22 @@ export interface CrashReporterStartOptions { */ globalExtra?: Record; } + +export interface ProcessMemoryInfo { + /** + * The amount of memory not shared by other processes, such as JS heap or HTML + * content in Kilobytes. + */ + private: number; + /** + * The amount of memory currently pinned to actual physical RAM in Kilobytes. + * + * @platform linux,win32 + */ + residentSet: number; + /** + * The amount of memory shared between processes, typically memory consumed by the + * Electron code itself in Kilobytes. + */ + shared: number; +} diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index 18fb9d62422..76d2c33b55a 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -94,6 +94,7 @@ platform: process.platform, env: process.env, versions: process.versions, + _whenEnvResolved: undefined, get whenEnvResolved() { if (!this._whenEnvResolved) { @@ -102,6 +103,15 @@ return this._whenEnvResolved; }, + + getProcessMemoryInfo: + /** + * @returns {Promise} + */ + function () { + return process.getProcessMemoryInfo(); + }, + on: /** * @param {string} type diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index 0c77d5dc453..8fd307ea32d 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ProcessMemoryInfo } from 'vs/base/parts/sandbox/common/electronTypes'; + export const ipcRenderer = (window as any).vscode.ipcRenderer as { /** @@ -95,6 +97,21 @@ export const process = (window as any).vscode.process as { */ on: (type: string, callback: Function) => void; + /** + * Resolves with a ProcessMemoryInfo + * + * Returns an object giving memory usage statistics about the current process. Note + * that all statistics are reported in Kilobytes. This api should be called after + * app ready. + * + * Chromium does not provide `residentSet` value for macOS. This is because macOS + * performs in-memory compression of pages that haven't been recently used. As a + * result the resident set size value is not what one would expect. `private` + * memory is more representative of the actual pre-compression memory usage of the + * process on macOS. + */ + getProcessMemoryInfo: () => ProcessMemoryInfo; + /** * A list of versions for the current node.js/electron configuration. */ diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index 6223277bc0b..ff5d62a5a53 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -293,13 +293,13 @@ class ProcessExplorer { container.append(tableHead); const hasMultipleMachines = Object.keys(processLists).length > 1; - const totalMem = await this.electronService.getTotalMem(); + const { totalmem } = await this.electronService.getOSStatistics(); processLists.forEach((remote, i) => { const isLocal = i === 0; if (isRemoteDiagnosticError(remote.rootProcess)) { this.renderProcessFetchError(remote.name, remote.rootProcess.errorMessage); } else { - this.renderTableSection(remote.name, this.getProcessList(remote.rootProcess, isLocal, totalMem), hasMultipleMachines, isLocal); + this.renderTableSection(remote.name, this.getProcessList(remote.rootProcess, isLocal, totalmem), hasMultipleMachines, isLocal); } }); } diff --git a/src/vs/platform/electron/common/electron.ts b/src/vs/platform/electron/common/electron.ts index 90033410206..e3bf0a2be09 100644 --- a/src/vs/platform/electron/common/electron.ts +++ b/src/vs/platform/electron/common/electron.ts @@ -10,11 +10,23 @@ import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +export interface ICPUProperties { + model: string; + speed: number; +} + export interface IOSProperties { type: string; release: string; arch: string; platform: string; + cpus: ICPUProperties[]; +} + +export interface IOSStatistics { + totalmem: number; + freemem: number; + loadavg: number[]; } export interface ICommonElectronService { @@ -82,8 +94,10 @@ export interface ICommonElectronService { updateTouchBar(items: ISerializableCommandAction[][]): Promise; moveItemToTrash(fullPath: string, deleteOnFail?: boolean): Promise; isAdmin(): Promise; - getTotalMem(): Promise; - getOS(): Promise; + + getOSProperties(): Promise; + getOSStatistics(): Promise; + getOSVirtualMachineHint(): Promise; // Process killProcess(pid: number, code: string): Promise; diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index acde70fb201..08fff59cdc0 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -11,7 +11,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { isMacintosh, isWindows, isRootUser } from 'vs/base/common/platform'; -import { ICommonElectronService, IOSProperties } from 'vs/platform/electron/common/electron'; +import { ICommonElectronService, IOSProperties, IOSStatistics } from 'vs/platform/electron/common/electron'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { AddFirstParameterToFunctions } from 'vs/base/common/types'; @@ -21,8 +21,9 @@ import { URI } from 'vs/base/common/uri'; import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; -import { arch, totalmem, release, platform, type } from 'os'; +import { arch, totalmem, release, platform, type, loadavg, freemem, cpus } from 'os'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { virtualMachineHint } from 'vs/base/node/id'; export interface IElectronMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -334,19 +335,28 @@ export class ElectronMainService implements IElectronMainService { return isAdmin; } - async getTotalMem(): Promise { - return totalmem(); + async getOSStatistics(): Promise { + return { + totalmem: totalmem(), + freemem: freemem(), + loadavg: loadavg() + }; } - async getOS(): Promise { + async getOSProperties(): Promise { return { arch: arch(), platform: platform(), release: release(), - type: type() + type: type(), + cpus: cpus() }; } + async getOSVirtualMachineHint(): Promise { + return virtualMachineHint.value(); + } + //#endregion diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts index 2b3024c77c9..5798ad547a7 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts @@ -19,7 +19,7 @@ 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 } from 'vs/workbench/services/timer/electron-browser/timerService'; +import { didUseCachedData } from 'vs/workbench/services/timer/electron-sandbox/timerService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 74f95a45947..d6f8e16fbf7 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -45,7 +45,6 @@ import { ITunnelProvider, ITunnelService, RemoteTunnel } from 'vs/platform/remot import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IManualSyncTask, IResourcePreview, ISyncResourceHandle, ISyncTask, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, IUserDataSyncStoreManagementService, SyncResource, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncAccount, IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { AbstractTimerService, IStartupMetrics, ITimerService, Writeable } from 'vs/workbench/services/timer/browser/timerService'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ITaskProvider, ITaskService, ITaskSummary, ProblemMatcherRunOptions, Task, TaskFilter, TaskTerminateResponse, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; import { Action } from 'vs/base/common/actions'; @@ -765,20 +764,6 @@ registerSingleton(IUserDataSyncStoreManagementService, SimpleIUserDataSyncStoreM //#endregion -//#region Timer - -class SimpleTimerService extends AbstractTimerService { - protected _isInitialStartup(): boolean { return true; } - protected _didUseCachedData(): boolean { return false; } - protected async _getWindowCount(): Promise { return 1; } - protected async _extendStartupInfo(info: Writeable): Promise { } -} - -registerSingleton(ITimerService, SimpleTimerService); - -//#endregion - - //#region Task class SimpleTaskService implements ITaskService { diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts index 27556f15a6d..4165ad54471 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts @@ -212,7 +212,7 @@ class NativeDialogService implements IDialogService { } const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION; - const os = await this.electronService.getOS(); + const osProps = await this.electronService.getOSProperties(); const detailString = (useAgo: boolean): string => { return nls.localize('aboutDetail', @@ -224,7 +224,7 @@ class NativeDialogService implements IDialogService { process.versions['chrome'], process.versions['node'], process.versions['v8'], - `${os.type} ${os.arch} ${os.release}${isSnap ? ' snap' : ''}` + `${osProps.type} ${osProps.arch} ${osProps.release}${isSnap ? ' snap' : ''}` ); }; diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts similarity index 82% rename from src/vs/workbench/services/timer/electron-browser/timerService.ts rename to src/vs/workbench/services/timer/electron-sandbox/timerService.ts index 9803c10996d..1dda0744c08 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { virtualMachineHint } from 'vs/base/node/id'; -import * as os from 'os'; import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; @@ -18,6 +16,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IStartupMetrics, AbstractTimerService, Writeable } from 'vs/workbench/services/timer/browser/timerService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { context, process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; export class TimerService extends AbstractTimerService { @@ -49,12 +48,18 @@ export class TimerService extends AbstractTimerService { protected async _extendStartupInfo(info: Writeable): Promise { try { - info.totalmem = os.totalmem(); - info.freemem = os.freemem(); - info.platform = os.platform(); - info.release = os.release(); - info.arch = os.arch(); - info.loadavg = os.loadavg(); + const [osProperties, osStatistics, virtualMachineHint] = await Promise.all([ + this._electronService.getOSProperties(), + this._electronService.getOSStatistics(), + this._electronService.getOSVirtualMachineHint() + ]); + + info.totalmem = osStatistics.totalmem; + info.freemem = osStatistics.freemem; + info.platform = osProperties.platform; + info.release = osProperties.release; + info.arch = osProperties.arch; + info.loadavg = osStatistics.loadavg; const processMemoryInfo = await process.getProcessMemoryInfo(); info.meminfo = { @@ -63,9 +68,9 @@ export class TimerService extends AbstractTimerService { sharedBytes: processMemoryInfo.shared }; - info.isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); + info.isVMLikelyhood = Math.round((virtualMachineHint * 100)); - const rawCpus = os.cpus(); + const rawCpus = osProperties.cpus; if (rawCpus && rawCpus.length > 0) { info.cpus = { count: rawCpus.length, speed: rawCpus[0].speed, model: rawCpus[0].model }; } @@ -78,8 +83,12 @@ export class TimerService extends AbstractTimerService { //#region cached data logic export function didUseCachedData(): boolean { + // TODO@Ben TODO@Jo need a different way to figure out if cached data was used + if (context.sandbox) { + return true; + } // We surely don't use cached data when we don't tell the loader to do so - if (!Boolean((global).require.getConfig().nodeCachedData)) { + if (!Boolean((window).require.getConfig().nodeCachedData)) { return false; } // There are loader events that signal if cached data was missing, rejected, diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index decfe69a8c8..98e3e4e2a52 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -40,7 +40,7 @@ import { TestContextService } from 'vs/workbench/test/common/workbenchTestServic import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IOSProperties } from 'vs/platform/electron/common/electron'; +import { IOSProperties, IOSStatistics } from 'vs/platform/electron/common/electron'; import { ColorScheme } from 'vs/platform/theme/common/theme'; export const TestWorkbenchConfiguration: INativeWorkbenchConfiguration = { @@ -198,8 +198,9 @@ export class TestElectronService implements IElectronService { async showItemInFolder(path: string): Promise { } async setRepresentedFilename(path: string): Promise { } async isAdmin(): Promise { return false; } - async getTotalMem(): Promise { return 0; } - async getOS(): Promise { return Object.create(null); } + async getOSProperties(): Promise { return Object.create(null); } + async getOSStatistics(): Promise { return Object.create(null); } + async getOSVirtualMachineHint(): Promise { return 0; } async killProcess(): Promise { } async setDocumentEdited(edited: boolean): Promise { } async openExternal(url: string): Promise { return false; } diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index d88081d0ba9..4654815024c 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -60,13 +60,10 @@ import { ICredentialsService } from 'vs/platform/credentials/common/credentials' import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/platform/remote/node/tunnelService'; -import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; -import { TimerService } from 'vs/workbench/services/timer/electron-browser/timerService'; import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; registerSingleton(ICredentialsService, KeytarCredentialsService, true); registerSingleton(ITunnelService, TunnelService); -registerSingleton(ITimerService, TimerService); registerSingleton(IUserDataInitializationService, UserDataInitializationService); //#endregion diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index 773b20a0a5e..1c47201d9cf 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -41,6 +41,12 @@ import 'vs/workbench/services/path/electron-sandbox/pathService'; import 'vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService'; import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; +import { TimerService } from 'vs/workbench/services/timer/electron-sandbox/timerService'; + +registerSingleton(ITimerService, TimerService); + //#endregion