mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 02:32:35 -05:00
Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)
This commit is contained in:
44
src/vs/platform/diagnostics/common/diagnosticsService.ts
Normal file
44
src/vs/platform/diagnostics/common/diagnosticsService.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { UriComponents } from 'vs/base/common/uri';
|
||||
import { ProcessItem } from 'vs/base/common/processes';
|
||||
|
||||
export interface IMachineInfo {
|
||||
os: string;
|
||||
cpus?: string;
|
||||
memory: string;
|
||||
vmHint: string;
|
||||
}
|
||||
|
||||
export interface IDiagnosticInfo {
|
||||
machineInfo: IMachineInfo;
|
||||
workspaceMetadata?: { [key: string]: WorkspaceStats };
|
||||
processes?: ProcessItem;
|
||||
}
|
||||
export interface SystemInfo extends IMachineInfo {
|
||||
processArgs: string;
|
||||
gpuStatus: any;
|
||||
screenReader: string;
|
||||
load?: string;
|
||||
}
|
||||
|
||||
export interface IDiagnosticInfoOptions {
|
||||
includeProcesses?: boolean;
|
||||
folders?: UriComponents[];
|
||||
includeExtensions?: boolean;
|
||||
}
|
||||
|
||||
export interface WorkspaceStatItem {
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface WorkspaceStats {
|
||||
fileTypes: WorkspaceStatItem[];
|
||||
configFiles: WorkspaceStatItem[];
|
||||
fileCount: number;
|
||||
maxFilesReached: boolean;
|
||||
}
|
||||
@@ -3,19 +3,21 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IMainProcessInfo } from 'vs/platform/launch/electron-main/launchService';
|
||||
import { ProcessItem, listProcesses } from 'vs/base/node/ps';
|
||||
import { IMainProcessInfo, ILaunchService } from 'vs/platform/launch/electron-main/launchService';
|
||||
import { listProcesses } from 'vs/base/node/ps';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import * as os from 'os';
|
||||
import * as osLib from 'os';
|
||||
import { virtualMachineHint } from 'vs/base/node/id';
|
||||
import { repeat, pad } from 'vs/base/common/strings';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { app } from 'electron';
|
||||
import { basename, join } from 'vs/base/common/path';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { readdir, stat } from 'fs';
|
||||
import { WorkspaceStats, SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
import { collectWorkspaceStats, getMachineInfo } from 'vs/platform/diagnostics/node/diagnosticsService';
|
||||
import { ProcessItem } from 'vs/base/common/processes';
|
||||
|
||||
export const ID = 'diagnosticsService';
|
||||
export const IDiagnosticsService = createDecorator<IDiagnosticsService>(ID);
|
||||
@@ -23,10 +25,9 @@ export const IDiagnosticsService = createDecorator<IDiagnosticsService>(ID);
|
||||
export interface IDiagnosticsService {
|
||||
_serviceBrand: any;
|
||||
|
||||
formatEnvironment(info: IMainProcessInfo): string;
|
||||
getPerformanceInfo(info: IMainProcessInfo): Promise<PerformanceInfo>;
|
||||
getSystemInfo(info: IMainProcessInfo): SystemInfo;
|
||||
getDiagnostics(info: IMainProcessInfo): Promise<string>;
|
||||
getPerformanceInfo(launchService: ILaunchService): Promise<PerformanceInfo>;
|
||||
getSystemInfo(launchService: ILaunchService): Promise<SystemInfo>;
|
||||
getDiagnostics(launchService: ILaunchService): Promise<string>;
|
||||
}
|
||||
|
||||
export interface VersionInfo {
|
||||
@@ -34,16 +35,6 @@ export interface VersionInfo {
|
||||
os: string;
|
||||
}
|
||||
|
||||
export interface SystemInfo {
|
||||
CPUs?: string;
|
||||
'Memory (System)': string;
|
||||
'Load (avg)'?: string;
|
||||
VM: string;
|
||||
'Screen Reader': string;
|
||||
'Process Argv': string;
|
||||
'GPU Status': Electron.GPUFeatureStatus;
|
||||
}
|
||||
|
||||
export interface ProcessInfo {
|
||||
cpu: number;
|
||||
memory: number;
|
||||
@@ -66,14 +57,14 @@ export class DiagnosticsService implements IDiagnosticsService {
|
||||
|
||||
const output: string[] = [];
|
||||
output.push(`Version: ${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`);
|
||||
output.push(`OS Version: ${os.type()} ${os.arch()} ${os.release()}`);
|
||||
const cpus = os.cpus();
|
||||
output.push(`OS Version: ${osLib.type()} ${osLib.arch()} ${osLib.release()}`);
|
||||
const cpus = osLib.cpus();
|
||||
if (cpus && cpus.length > 0) {
|
||||
output.push(`CPUs: ${cpus[0].model} (${cpus.length} x ${cpus[0].speed})`);
|
||||
}
|
||||
output.push(`Memory (System): ${(os.totalmem() / GB).toFixed(2)}GB (${(os.freemem() / GB).toFixed(2)}GB free)`);
|
||||
output.push(`Memory (System): ${(osLib.totalmem() / GB).toFixed(2)}GB (${(osLib.freemem() / GB).toFixed(2)}GB free)`);
|
||||
if (!isWindows) {
|
||||
output.push(`Load (avg): ${os.loadavg().map(l => Math.round(l)).join(', ')}`); // only provided on Linux/macOS
|
||||
output.push(`Load (avg): ${osLib.loadavg().map(l => Math.round(l)).join(', ')}`); // only provided on Linux/macOS
|
||||
}
|
||||
output.push(`VM: ${Math.round((virtualMachineHint.value() * 100))}%`);
|
||||
output.push(`Screen Reader: ${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`);
|
||||
@@ -83,82 +74,42 @@ export class DiagnosticsService implements IDiagnosticsService {
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
getPerformanceInfo(info: IMainProcessInfo): Promise<PerformanceInfo> {
|
||||
return listProcesses(info.mainPID).then(rootProcess => {
|
||||
const workspaceInfoMessages: string[] = [];
|
||||
|
||||
// Workspace Stats
|
||||
const workspaceStatPromises: Promise<void>[] = [];
|
||||
if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) {
|
||||
info.windows.forEach(window => {
|
||||
if (window.folderURIs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
workspaceInfoMessages.push(`| Window (${window.title})`);
|
||||
|
||||
window.folderURIs.forEach(uriComponents => {
|
||||
const folderUri = URI.revive(uriComponents);
|
||||
if (folderUri.scheme === 'file') {
|
||||
const folder = folderUri.fsPath;
|
||||
workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(async stats => {
|
||||
|
||||
let countMessage = `${stats.fileCount} files`;
|
||||
if (stats.maxFilesReached) {
|
||||
countMessage = `more than ${countMessage}`;
|
||||
}
|
||||
workspaceInfoMessages.push(`| Folder (${basename(folder)}): ${countMessage}`);
|
||||
workspaceInfoMessages.push(this.formatWorkspaceStats(stats));
|
||||
}));
|
||||
} else {
|
||||
workspaceInfoMessages.push(`| Folder (${folderUri.toString()}): RPerformance stats not available.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all(workspaceStatPromises).then(() => {
|
||||
return {
|
||||
processInfo: this.formatProcessList(info, rootProcess),
|
||||
workspaceInfo: workspaceInfoMessages.join('\n')
|
||||
};
|
||||
}).catch(error => {
|
||||
return {
|
||||
processInfo: this.formatProcessList(info, rootProcess),
|
||||
workspaceInfo: `Unable to calculate workspace stats: ${error}`
|
||||
};
|
||||
});
|
||||
async getPerformanceInfo(launchService: ILaunchService): Promise<PerformanceInfo> {
|
||||
const info = await launchService.getMainProcessInfo();
|
||||
return Promise.all<ProcessItem, string>([listProcesses(info.mainPID), this.formatWorkspaceMetadata(info)]).then(result => {
|
||||
const [rootProcess, workspaceInfo] = result;
|
||||
return {
|
||||
processInfo: this.formatProcessList(info, rootProcess),
|
||||
workspaceInfo
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getSystemInfo(info: IMainProcessInfo): SystemInfo {
|
||||
const MB = 1024 * 1024;
|
||||
const GB = 1024 * MB;
|
||||
|
||||
async getSystemInfo(launchService: ILaunchService): Promise<SystemInfo> {
|
||||
const info = await launchService.getMainProcessInfo();
|
||||
const { memory, vmHint, os, cpus } = getMachineInfo();
|
||||
const systemInfo: SystemInfo = {
|
||||
'Memory (System)': `${(os.totalmem() / GB).toFixed(2)}GB (${(os.freemem() / GB).toFixed(2)}GB free)`,
|
||||
VM: `${Math.round((virtualMachineHint.value() * 100))}%`,
|
||||
'Screen Reader': `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`,
|
||||
'Process Argv': `${info.mainArguments.join(' ')}`,
|
||||
'GPU Status': app.getGPUFeatureStatus()
|
||||
os,
|
||||
memory,
|
||||
cpus,
|
||||
vmHint,
|
||||
processArgs: `${info.mainArguments.join(' ')}`,
|
||||
gpuStatus: app.getGPUFeatureStatus(),
|
||||
screenReader: `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`
|
||||
};
|
||||
|
||||
const cpus = os.cpus();
|
||||
if (cpus && cpus.length > 0) {
|
||||
systemInfo.CPUs = `${cpus[0].model} (${cpus.length} x ${cpus[0].speed})`;
|
||||
}
|
||||
|
||||
if (!isWindows) {
|
||||
systemInfo['Load (avg)'] = `${os.loadavg().map(l => Math.round(l)).join(', ')}`;
|
||||
systemInfo.load = `${osLib.loadavg().map(l => Math.round(l)).join(', ')}`;
|
||||
}
|
||||
|
||||
|
||||
return systemInfo;
|
||||
return Promise.resolve(systemInfo);
|
||||
}
|
||||
|
||||
getDiagnostics(info: IMainProcessInfo): Promise<string> {
|
||||
async getDiagnostics(launchService: ILaunchService): Promise<string> {
|
||||
const output: string[] = [];
|
||||
return listProcesses(info.mainPID).then(rootProcess => {
|
||||
const info = await launchService.getMainProcessInfo();
|
||||
return listProcesses(info.mainPID).then(async rootProcess => {
|
||||
|
||||
// Environment Info
|
||||
output.push('');
|
||||
@@ -169,45 +120,16 @@ export class DiagnosticsService implements IDiagnosticsService {
|
||||
output.push(this.formatProcessList(info, rootProcess));
|
||||
|
||||
// Workspace Stats
|
||||
const workspaceStatPromises: Promise<void>[] = [];
|
||||
if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) {
|
||||
output.push('');
|
||||
output.push('Workspace Stats: ');
|
||||
info.windows.forEach(window => {
|
||||
if (window.folderURIs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
output.push(`| Window (${window.title})`);
|
||||
|
||||
window.folderURIs.forEach(uriComponents => {
|
||||
const folderUri = URI.revive(uriComponents);
|
||||
if (folderUri.scheme === 'file') {
|
||||
const folder = folderUri.fsPath;
|
||||
workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(async stats => {
|
||||
let countMessage = `${stats.fileCount} files`;
|
||||
if (stats.maxFilesReached) {
|
||||
countMessage = `more than ${countMessage}`;
|
||||
}
|
||||
output.push(`| Folder (${basename(folder)}): ${countMessage}`);
|
||||
output.push(this.formatWorkspaceStats(stats));
|
||||
|
||||
}).catch(error => {
|
||||
output.push(`| Error: Unable to collect workspace stats for folder ${folder} (${error.toString()})`);
|
||||
}));
|
||||
} else {
|
||||
output.push(`| Folder (${folderUri.toString()}): Workspace stats not available.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
output.push(await this.formatWorkspaceMetadata(info));
|
||||
}
|
||||
|
||||
return Promise.all(workspaceStatPromises).then(() => {
|
||||
output.push('');
|
||||
output.push('');
|
||||
output.push('');
|
||||
output.push('');
|
||||
|
||||
return output.join('\n');
|
||||
});
|
||||
return output.join('\n');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -268,6 +190,43 @@ export class DiagnosticsService implements IDiagnosticsService {
|
||||
return Object.keys(gpuFeatures).map(feature => `${feature}: ${repeat(' ', longestFeatureName - feature.length)} ${gpuFeatures[feature]}`).join('\n ');
|
||||
}
|
||||
|
||||
private formatWorkspaceMetadata(info: IMainProcessInfo): Promise<string> {
|
||||
const output: string[] = [];
|
||||
const workspaceStatPromises: Promise<void>[] = [];
|
||||
|
||||
info.windows.forEach(window => {
|
||||
if (window.folderURIs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
output.push(`| Window (${window.title})`);
|
||||
|
||||
window.folderURIs.forEach(uriComponents => {
|
||||
const folderUri = URI.revive(uriComponents);
|
||||
if (folderUri.scheme === 'file') {
|
||||
const folder = folderUri.fsPath;
|
||||
workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(stats => {
|
||||
let countMessage = `${stats.fileCount} files`;
|
||||
if (stats.maxFilesReached) {
|
||||
countMessage = `more than ${countMessage}`;
|
||||
}
|
||||
output.push(`| Folder (${basename(folder)}): ${countMessage}`);
|
||||
output.push(this.formatWorkspaceStats(stats));
|
||||
|
||||
}).catch(error => {
|
||||
output.push(`| Error: Unable to collect workspace stats for folder ${folder} (${error.toString()})`);
|
||||
}));
|
||||
} else {
|
||||
output.push(`| Folder (${folderUri.toString()}): Workspace stats not available.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(workspaceStatPromises)
|
||||
.then(_ => output.join('\n'))
|
||||
.catch(e => `Unable to collect workspace stats: ${e}`);
|
||||
}
|
||||
|
||||
private formatProcessList(info: IMainProcessInfo, rootProcess: ProcessItem): string {
|
||||
const mapPidToWindowTitle = new Map<number, string>();
|
||||
info.windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title));
|
||||
@@ -299,7 +258,7 @@ export class DiagnosticsService implements IDiagnosticsService {
|
||||
name = `${name} (${mapPidToWindowTitle.get(item.pid)})`;
|
||||
}
|
||||
}
|
||||
const memory = process.platform === 'win32' ? item.mem : (os.totalmem() * (item.mem / 100));
|
||||
const memory = process.platform === 'win32' ? item.mem : (osLib.totalmem() * (item.mem / 100));
|
||||
output.push(`${pad(Number(item.load.toFixed(0)), 5, ' ')}\t${pad(Number((memory / MB).toFixed(0)), 6, ' ')}\t${pad(Number((item.pid).toFixed(0)), 6, ' ')}\t${name}`);
|
||||
|
||||
// Recurse into children if any
|
||||
@@ -309,25 +268,6 @@ export class DiagnosticsService implements IDiagnosticsService {
|
||||
}
|
||||
}
|
||||
|
||||
interface WorkspaceStatItem {
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
interface WorkspaceStats {
|
||||
fileTypes: WorkspaceStatItem[];
|
||||
configFiles: WorkspaceStatItem[];
|
||||
fileCount: number;
|
||||
maxFilesReached: boolean;
|
||||
// launchConfigFiles: WorkspaceStatItem[];
|
||||
}
|
||||
|
||||
function asSortedItems(map: Map<string, number>): WorkspaceStatItem[] {
|
||||
const a: WorkspaceStatItem[] = [];
|
||||
map.forEach((value, index) => a.push({ name: index, count: value }));
|
||||
return a.sort((a, b) => b.count - a.count);
|
||||
}
|
||||
|
||||
// function collectLaunchConfigs(folder: string): Promise<WorkspaceStatItem[]> {
|
||||
// const launchConfigs = new Map<string, number>();
|
||||
|
||||
@@ -368,137 +308,3 @@ function asSortedItems(map: Map<string, number>): WorkspaceStatItem[] {
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
function collectWorkspaceStats(folder: string, filter: string[]): Promise<WorkspaceStats> {
|
||||
const configFilePatterns = [
|
||||
{ 'tag': 'grunt.js', 'pattern': /^gruntfile\.js$/i },
|
||||
{ 'tag': 'gulp.js', 'pattern': /^gulpfile\.js$/i },
|
||||
{ 'tag': 'tsconfig.json', 'pattern': /^tsconfig\.json$/i },
|
||||
{ 'tag': 'package.json', 'pattern': /^package\.json$/i },
|
||||
{ 'tag': 'jsconfig.json', 'pattern': /^jsconfig\.json$/i },
|
||||
{ 'tag': 'tslint.json', 'pattern': /^tslint\.json$/i },
|
||||
{ 'tag': 'eslint.json', 'pattern': /^eslint\.json$/i },
|
||||
{ 'tag': 'tasks.json', 'pattern': /^tasks\.json$/i },
|
||||
{ 'tag': 'launch.json', 'pattern': /^launch\.json$/i },
|
||||
{ 'tag': 'settings.json', 'pattern': /^settings\.json$/i },
|
||||
{ 'tag': 'webpack.config.js', 'pattern': /^webpack\.config\.js$/i },
|
||||
{ 'tag': 'project.json', 'pattern': /^project\.json$/i },
|
||||
{ 'tag': 'makefile', 'pattern': /^makefile$/i },
|
||||
{ 'tag': 'sln', 'pattern': /^.+\.sln$/i },
|
||||
{ 'tag': 'csproj', 'pattern': /^.+\.csproj$/i },
|
||||
{ 'tag': 'cmake', 'pattern': /^.+\.cmake$/i }
|
||||
];
|
||||
|
||||
const fileTypes = new Map<string, number>();
|
||||
const configFiles = new Map<string, number>();
|
||||
|
||||
const MAX_FILES = 20000;
|
||||
|
||||
function walk(dir: string, filter: string[], token: { count: any; maxReached: any; }, done: (allFiles: string[]) => void): void {
|
||||
let results: string[] = [];
|
||||
readdir(dir, async (err, files) => {
|
||||
// Ignore folders that can't be read
|
||||
if (err) {
|
||||
return done(results);
|
||||
}
|
||||
|
||||
let pending = files.length;
|
||||
if (pending === 0) {
|
||||
return done(results);
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (token.maxReached) {
|
||||
return done(results);
|
||||
}
|
||||
|
||||
stat(join(dir, file), (err, stats) => {
|
||||
// Ignore files that can't be read
|
||||
if (err) {
|
||||
if (--pending === 0) {
|
||||
return done(results);
|
||||
}
|
||||
} else {
|
||||
if (stats.isDirectory()) {
|
||||
if (filter.indexOf(file) === -1) {
|
||||
walk(join(dir, file), filter, token, (res: string[]) => {
|
||||
results = results.concat(res);
|
||||
|
||||
if (--pending === 0) {
|
||||
return done(results);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (--pending === 0) {
|
||||
done(results);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (token.count >= MAX_FILES) {
|
||||
token.maxReached = true;
|
||||
}
|
||||
|
||||
token.count++;
|
||||
results.push(file);
|
||||
|
||||
if (--pending === 0) {
|
||||
done(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const addFileType = (fileType: string) => {
|
||||
if (fileTypes.has(fileType)) {
|
||||
fileTypes.set(fileType, fileTypes.get(fileType)! + 1);
|
||||
}
|
||||
else {
|
||||
fileTypes.set(fileType, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const addConfigFiles = (fileName: string) => {
|
||||
for (const each of configFilePatterns) {
|
||||
if (each.pattern.test(fileName)) {
|
||||
if (configFiles.has(each.tag)) {
|
||||
configFiles.set(each.tag, configFiles.get(each.tag)! + 1);
|
||||
} else {
|
||||
configFiles.set(each.tag, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const acceptFile = (name: string) => {
|
||||
if (name.lastIndexOf('.') >= 0) {
|
||||
const suffix: string | undefined = name.split('.').pop();
|
||||
if (suffix) {
|
||||
addFileType(suffix);
|
||||
}
|
||||
}
|
||||
addConfigFiles(name);
|
||||
};
|
||||
|
||||
const token: { count: number, maxReached: boolean } = { count: 0, maxReached: false };
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
walk(folder, filter, token, async (files) => {
|
||||
files.forEach(acceptFile);
|
||||
|
||||
// TODO@rachel commented out due to severe performance issues
|
||||
// see https://github.com/Microsoft/vscode/issues/70563
|
||||
// const launchConfigs = await collectLaunchConfigs(folder);
|
||||
|
||||
resolve({
|
||||
configFiles: asSortedItems(configFiles),
|
||||
fileTypes: asSortedItems(fileTypes),
|
||||
fileCount: token.count,
|
||||
maxFilesReached: token.maxReached,
|
||||
// launchConfigFiles: launchConfigs
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
167
src/vs/platform/diagnostics/node/diagnosticsService.ts
Normal file
167
src/vs/platform/diagnostics/node/diagnosticsService.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as os from 'os';
|
||||
import { virtualMachineHint } from 'vs/base/node/id';
|
||||
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
import { readdir, stat } from 'fs';
|
||||
import { join } from 'vs/base/common/path';
|
||||
|
||||
export function getMachineInfo(): IMachineInfo {
|
||||
const MB = 1024 * 1024;
|
||||
const GB = 1024 * MB;
|
||||
|
||||
const machineInfo: IMachineInfo = {
|
||||
os: `${os.type()} ${os.arch()} ${os.release()}`,
|
||||
memory: `${(os.totalmem() / GB).toFixed(2)}GB (${(os.freemem() / GB).toFixed(2)}GB free)`,
|
||||
vmHint: `${Math.round((virtualMachineHint.value() * 100))}%`,
|
||||
};
|
||||
|
||||
const cpus = os.cpus();
|
||||
if (cpus && cpus.length > 0) {
|
||||
machineInfo.cpus = `${cpus[0].model} (${cpus.length} x ${cpus[0].speed})`;
|
||||
}
|
||||
|
||||
return machineInfo;
|
||||
}
|
||||
|
||||
export function collectWorkspaceStats(folder: string, filter: string[]): Promise<WorkspaceStats> {
|
||||
const configFilePatterns = [
|
||||
{ 'tag': 'grunt.js', 'pattern': /^gruntfile\.js$/i },
|
||||
{ 'tag': 'gulp.js', 'pattern': /^gulpfile\.js$/i },
|
||||
{ 'tag': 'tsconfig.json', 'pattern': /^tsconfig\.json$/i },
|
||||
{ 'tag': 'package.json', 'pattern': /^package\.json$/i },
|
||||
{ 'tag': 'jsconfig.json', 'pattern': /^jsconfig\.json$/i },
|
||||
{ 'tag': 'tslint.json', 'pattern': /^tslint\.json$/i },
|
||||
{ 'tag': 'eslint.json', 'pattern': /^eslint\.json$/i },
|
||||
{ 'tag': 'tasks.json', 'pattern': /^tasks\.json$/i },
|
||||
{ 'tag': 'launch.json', 'pattern': /^launch\.json$/i },
|
||||
{ 'tag': 'settings.json', 'pattern': /^settings\.json$/i },
|
||||
{ 'tag': 'webpack.config.js', 'pattern': /^webpack\.config\.js$/i },
|
||||
{ 'tag': 'project.json', 'pattern': /^project\.json$/i },
|
||||
{ 'tag': 'makefile', 'pattern': /^makefile$/i },
|
||||
{ 'tag': 'sln', 'pattern': /^.+\.sln$/i },
|
||||
{ 'tag': 'csproj', 'pattern': /^.+\.csproj$/i },
|
||||
{ 'tag': 'cmake', 'pattern': /^.+\.cmake$/i }
|
||||
];
|
||||
|
||||
const fileTypes = new Map<string, number>();
|
||||
const configFiles = new Map<string, number>();
|
||||
|
||||
const MAX_FILES = 20000;
|
||||
|
||||
function walk(dir: string, filter: string[], token: { count: number, maxReached: boolean }, done: (allFiles: string[]) => void): void {
|
||||
let results: string[] = [];
|
||||
readdir(dir, async (err, files) => {
|
||||
// Ignore folders that can't be read
|
||||
if (err) {
|
||||
return done(results);
|
||||
}
|
||||
|
||||
let pending = files.length;
|
||||
if (pending === 0) {
|
||||
return done(results);
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (token.maxReached) {
|
||||
return done(results);
|
||||
}
|
||||
|
||||
stat(join(dir, file), (err, stats) => {
|
||||
// Ignore files that can't be read
|
||||
if (err) {
|
||||
if (--pending === 0) {
|
||||
return done(results);
|
||||
}
|
||||
} else {
|
||||
if (stats.isDirectory()) {
|
||||
if (filter.indexOf(file) === -1) {
|
||||
walk(join(dir, file), filter, token, (res: string[]) => {
|
||||
results = results.concat(res);
|
||||
|
||||
if (--pending === 0) {
|
||||
return done(results);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (--pending === 0) {
|
||||
done(results);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (token.count >= MAX_FILES) {
|
||||
token.maxReached = true;
|
||||
}
|
||||
|
||||
token.count++;
|
||||
results.push(file);
|
||||
|
||||
if (--pending === 0) {
|
||||
done(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const addFileType = (fileType: string) => {
|
||||
if (fileTypes.has(fileType)) {
|
||||
fileTypes.set(fileType, fileTypes.get(fileType)! + 1);
|
||||
}
|
||||
else {
|
||||
fileTypes.set(fileType, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const addConfigFiles = (fileName: string) => {
|
||||
for (const each of configFilePatterns) {
|
||||
if (each.pattern.test(fileName)) {
|
||||
if (configFiles.has(each.tag)) {
|
||||
configFiles.set(each.tag, configFiles.get(each.tag)! + 1);
|
||||
} else {
|
||||
configFiles.set(each.tag, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const acceptFile = (name: string) => {
|
||||
if (name.lastIndexOf('.') >= 0) {
|
||||
const suffix: string | undefined = name.split('.').pop();
|
||||
if (suffix) {
|
||||
addFileType(suffix);
|
||||
}
|
||||
}
|
||||
addConfigFiles(name);
|
||||
};
|
||||
|
||||
const token: { count: number, maxReached: boolean } = { count: 0, maxReached: false };
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
walk(folder, filter, token, async (files) => {
|
||||
files.forEach(acceptFile);
|
||||
|
||||
// TODO@rachel commented out due to severe performance issues
|
||||
// see https://github.com/Microsoft/vscode/issues/70563
|
||||
// const launchConfigs = await collectLaunchConfigs(folder);
|
||||
|
||||
resolve({
|
||||
configFiles: asSortedItems(configFiles),
|
||||
fileTypes: asSortedItems(fileTypes),
|
||||
fileCount: token.count,
|
||||
maxFilesReached: token.maxReached,
|
||||
// launchConfigFiles: launchConfigs
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function asSortedItems(map: Map<string, number>): WorkspaceStatItem[] {
|
||||
const a: WorkspaceStatItem[] = [];
|
||||
map.forEach((value, index) => a.push({ name: index, count: value }));
|
||||
return a.sort((a, b) => b.count - a.count);
|
||||
}
|
||||
Reference in New Issue
Block a user