Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

* disable strict null check
This commit is contained in:
Anthony Dresser
2019-07-15 22:35:46 -07:00
committed by GitHub
parent f720ec642f
commit 0b7e7ddbf9
2406 changed files with 59140 additions and 35464 deletions

View File

@@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IDiagnosticsService, IRemoteDiagnosticInfo, IRemoteDiagnosticError, SystemInfo, PerformanceInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { Event } from 'vs/base/common/event';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
export class DiagnosticsChannel implements IServerChannel {
constructor(private service: IDiagnosticsService) { }
listen(context: any, event: string): Event<any> {
throw new Error('Invalid listen');
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'getDiagnostics':
return this.service.getDiagnostics(args[0], args[1]);
case 'getSystemInfo':
return this.service.getSystemInfo(args[0], args[1]);
case 'getPerformanceInfo':
return this.service.getPerformanceInfo(args[0], args[1]);
case 'reportWorkspaceStats':
return this.service.reportWorkspaceStats(args);
}
throw new Error('Invalid call');
}
}
export class DiagnosticsService implements IDiagnosticsService {
_serviceBrand: ServiceIdentifier<any>;
constructor(private channel: IChannel) { }
public getDiagnostics(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<string> {
return this.channel.call('getDiagnostics', [mainProcessInfo, remoteInfo]);
}
public getSystemInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<SystemInfo> {
return this.channel.call('getSystemInfo', [mainProcessInfo, remoteInfo]);
}
public getPerformanceInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<PerformanceInfo> {
return this.channel.call('getPerformanceInfo', [mainProcessInfo, remoteInfo]);
}
public reportWorkspaceStats(workspace: IWorkspace): Promise<void> {
return this.channel.call('reportWorkspaceStats', workspace);
}
}

View File

@@ -2,28 +2,33 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from 'os';
import * as osLib 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';
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, IDiagnosticsService, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { readdir, stat, exists, readFile } from 'fs';
import { join, basename } from 'vs/base/common/path';
import { parse, ParseError } from 'vs/base/common/json';
import { listProcesses } from 'vs/base/node/ps';
import product from 'vs/platform/product/node/product';
import pkg from 'vs/platform/product/node/package';
import { repeat, pad } from 'vs/base/common/strings';
import { isWindows } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { ProcessItem } from 'vs/base/common/processes';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export function getMachineInfo(): IMachineInfo {
const MB = 1024 * 1024;
const GB = 1024 * MB;
export interface VersionInfo {
vscodeVersion: string;
os: string;
}
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 interface ProcessInfo {
cpu: number;
memory: number;
pid: number;
name: string;
}
export function collectWorkspaceStats(folder: string, filter: string[]): Promise<WorkspaceStats> {
@@ -145,16 +150,14 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise
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);
const launchConfigs = await collectLaunchConfigs(folder);
resolve({
configFiles: asSortedItems(configFiles),
fileTypes: asSortedItems(fileTypes),
fileCount: token.count,
maxFilesReached: token.maxReached,
// launchConfigFiles: launchConfigs
launchConfigFiles: launchConfigs
});
});
});
@@ -164,4 +167,375 @@ 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);
}
export function getMachineInfo(): IMachineInfo {
const MB = 1024 * 1024;
const GB = 1024 * MB;
const machineInfo: IMachineInfo = {
os: `${osLib.type()} ${osLib.arch()} ${osLib.release()}`,
memory: `${(osLib.totalmem() / GB).toFixed(2)}GB (${(osLib.freemem() / GB).toFixed(2)}GB free)`,
vmHint: `${Math.round((virtualMachineHint.value() * 100))}%`,
};
const cpus = osLib.cpus();
if (cpus && cpus.length > 0) {
machineInfo.cpus = `${cpus[0].model} (${cpus.length} x ${cpus[0].speed})`;
}
return machineInfo;
}
export function collectLaunchConfigs(folder: string): Promise<WorkspaceStatItem[]> {
let launchConfigs = new Map<string, number>();
let launchConfig = join(folder, '.vscode', 'launch.json');
return new Promise((resolve, reject) => {
exists(launchConfig, (doesExist) => {
if (doesExist) {
readFile(launchConfig, (err, contents) => {
if (err) {
return resolve([]);
}
const errors: ParseError[] = [];
const json = parse(contents.toString(), errors);
if (errors.length) {
console.log(`Unable to parse ${launchConfig}`);
return resolve([]);
}
if (json['configurations']) {
for (const each of json['configurations']) {
const type = each['type'];
if (type) {
if (launchConfigs.has(type)) {
launchConfigs.set(type, launchConfigs.get(type)! + 1);
} else {
launchConfigs.set(type, 1);
}
}
}
}
return resolve(asSortedItems(launchConfigs));
});
} else {
return resolve([]);
}
});
});
}
export class DiagnosticsService implements IDiagnosticsService {
_serviceBrand: any;
constructor(@ITelemetryService private readonly telemetryService: ITelemetryService) { }
private formatMachineInfo(info: IMachineInfo): string {
const output: string[] = [];
output.push(`OS Version: ${info.os}`);
output.push(`CPUs: ${info.cpus}`);
output.push(`Memory (System): ${info.memory}`);
output.push(`VM: ${info.vmHint}`);
return output.join('\n');
}
private formatEnvironment(info: IMainProcessInfo): string {
const MB = 1024 * 1024;
const GB = 1024 * MB;
const output: string[] = [];
output.push(`Version: ${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`);
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): ${(osLib.totalmem() / GB).toFixed(2)}GB (${(osLib.freemem() / GB).toFixed(2)}GB free)`);
if (!isWindows) {
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: ${info.screenReader ? 'yes' : 'no'}`);
output.push(`Process Argv: ${info.mainArguments.join(' ')}`);
output.push(`GPU Status: ${this.expandGPUFeatures(info.gpuFeatureStatus)}`);
return output.join('\n');
}
public async getPerformanceInfo(info: IMainProcessInfo, remoteData: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<PerformanceInfo> {
return Promise.all<ProcessItem, string>([listProcesses(info.mainPID), this.formatWorkspaceMetadata(info)]).then(async result => {
let [rootProcess, workspaceInfo] = result;
let processInfo = this.formatProcessList(info, rootProcess);
remoteData.forEach(diagnostics => {
if (isRemoteDiagnosticError(diagnostics)) {
processInfo += `\n${diagnostics.errorMessage}`;
workspaceInfo += `\n${diagnostics.errorMessage}`;
} else {
processInfo += `\n\nRemote: ${diagnostics.hostName}`;
if (diagnostics.processes) {
processInfo += `\n${this.formatProcessList(info, diagnostics.processes)}`;
}
if (diagnostics.workspaceMetadata) {
workspaceInfo += `\n| Remote: ${diagnostics.hostName}`;
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
workspaceInfo += `| Folder (${folder}): ${countMessage}`;
workspaceInfo += this.formatWorkspaceStats(metadata);
}
}
}
});
return {
processInfo,
workspaceInfo
};
});
}
public async getSystemInfo(info: IMainProcessInfo, remoteData: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<SystemInfo> {
const { memory, vmHint, os, cpus } = getMachineInfo();
const systemInfo: SystemInfo = {
os,
memory,
cpus,
vmHint,
processArgs: `${info.mainArguments.join(' ')}`,
gpuStatus: info.gpuFeatureStatus,
screenReader: `${info.screenReader ? 'yes' : 'no'}`,
remoteData
};
if (!isWindows) {
systemInfo.load = `${osLib.loadavg().map(l => Math.round(l)).join(', ')}`;
}
return Promise.resolve(systemInfo);
}
public async getDiagnostics(info: IMainProcessInfo, remoteDiagnostics: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise<string> {
const output: string[] = [];
return listProcesses(info.mainPID).then(async rootProcess => {
// Environment Info
output.push('');
output.push(this.formatEnvironment(info));
// Process List
output.push('');
output.push(this.formatProcessList(info, rootProcess));
// Workspace Stats
if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0 && !window.remoteAuthority)) {
output.push('');
output.push('Workspace Stats: ');
output.push(await this.formatWorkspaceMetadata(info));
}
remoteDiagnostics.forEach(diagnostics => {
if (isRemoteDiagnosticError(diagnostics)) {
output.push(`\n${diagnostics.errorMessage}`);
} else {
output.push('\n\n');
output.push(`Remote: ${diagnostics.hostName}`);
output.push(this.formatMachineInfo(diagnostics.machineInfo));
if (diagnostics.processes) {
output.push(this.formatProcessList(info, diagnostics.processes));
}
if (diagnostics.workspaceMetadata) {
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
output.push(`Folder (${folder}): ${countMessage}`);
output.push(this.formatWorkspaceStats(metadata));
}
}
}
});
output.push('');
output.push('');
return output.join('\n');
});
}
private formatWorkspaceStats(workspaceStats: WorkspaceStats): string {
const output: string[] = [];
const lineLength = 60;
let col = 0;
const appendAndWrap = (name: string, count: number) => {
const item = ` ${name}(${count})`;
if (col + item.length > lineLength) {
output.push(line);
line = '| ';
col = line.length;
}
else {
col += item.length;
}
line += item;
};
// File Types
let line = '| File types:';
const maxShown = 10;
let max = workspaceStats.fileTypes.length > maxShown ? maxShown : workspaceStats.fileTypes.length;
for (let i = 0; i < max; i++) {
const item = workspaceStats.fileTypes[i];
appendAndWrap(item.name, item.count);
}
output.push(line);
// Conf Files
if (workspaceStats.configFiles.length >= 0) {
line = '| Conf files:';
col = 0;
workspaceStats.configFiles.forEach((item) => {
appendAndWrap(item.name, item.count);
});
output.push(line);
}
if (workspaceStats.launchConfigFiles.length > 0) {
let line = '| Launch Configs:';
workspaceStats.launchConfigFiles.forEach(each => {
const item = each.count > 1 ? ` ${each.name}(${each.count})` : ` ${each.name}`;
line += item;
});
output.push(line);
}
return output.join('\n');
}
private expandGPUFeatures(gpuFeatures: any): string {
const longestFeatureName = Math.max(...Object.keys(gpuFeatures).map(feature => feature.length));
// Make columns aligned by adding spaces after feature name
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 || !!window.remoteAuthority) {
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));
const output: string[] = [];
output.push('CPU %\tMem MB\t PID\tProcess');
if (rootProcess) {
this.formatProcessItem(info.mainPID, mapPidToWindowTitle, output, rootProcess, 0);
}
return output.join('\n');
}
private formatProcessItem(mainPid: number, mapPidToWindowTitle: Map<number, string>, output: string[], item: ProcessItem, indent: number): void {
const isRoot = (indent === 0);
const MB = 1024 * 1024;
// Format name with indent
let name: string;
if (isRoot) {
name = item.pid === mainPid ? `${product.applicationName} main` : 'remote agent';
} else {
name = `${repeat(' ', indent)} ${item.name}`;
if (item.name === 'window') {
name = `${name} (${mapPidToWindowTitle.get(item.pid)})`;
}
}
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
if (Array.isArray(item.children)) {
item.children.forEach(child => this.formatProcessItem(mainPid, mapPidToWindowTitle, output, child, indent + 1));
}
}
public async reportWorkspaceStats(workspace: IWorkspace): Promise<void> {
workspace.folders.forEach(folder => {
const folderUri = URI.revive(folder.uri);
if (folderUri.scheme === 'file') {
const folder = folderUri.fsPath;
collectWorkspaceStats(folder, ['node_modules', '.git']).then(stats => {
/* __GDPR__
"workspace.stats" : {
"fileTypes" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"configTypes" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"launchConfigs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('workspace.stats', {
fileTypes: stats.fileTypes,
configTypes: stats.configFiles,
launchConfigs: stats.launchConfigFiles
});
}).catch(_ => {
// Report nothing if collecting metadata fails.
});
}
});
}
}