Merge from vscode 7653d836944892f83ce9e1f95c1204bafa1aec31

This commit is contained in:
ADS Merger
2020-05-08 03:58:34 +00:00
parent dac1970c43
commit fa62ec1f34
209 changed files with 5131 additions and 2480 deletions

View File

@@ -5,7 +5,7 @@
import * as osLib from 'os';
import { virtualMachineHint } from 'vs/base/node/id';
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics';
import { readdir, stat, exists, readFile } from 'fs';
import { readdir, exists, readFile } from 'fs';
import { join, basename } from 'vs/base/common/path';
import { parse, ParseError, getNodeType } from 'vs/base/common/json';
import { listProcesses } from 'vs/base/node/ps';
@@ -17,6 +17,7 @@ import { ProcessItem } from 'vs/base/common/processes';
import { IMainProcessInfo } from 'vs/platform/launch/common/launch';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Iterable } from 'vs/base/common/iterator';
export const ID = 'diagnosticsService';
export const IDiagnosticsService = createDecorator<IDiagnosticsService>(ID);
@@ -42,24 +43,31 @@ export interface ProcessInfo {
name: string;
}
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 }
interface ConfigFilePatterns {
tag: string;
filePattern: RegExp;
relativePathPattern?: RegExp;
}
export async function collectWorkspaceStats(folder: string, filter: string[]): Promise<WorkspaceStats> {
const configFilePatterns: ConfigFilePatterns[] = [
{ tag: 'grunt.js', filePattern: /^gruntfile\.js$/i },
{ tag: 'gulp.js', filePattern: /^gulpfile\.js$/i },
{ tag: 'tsconfig.json', filePattern: /^tsconfig\.json$/i },
{ tag: 'package.json', filePattern: /^package\.json$/i },
{ tag: 'jsconfig.json', filePattern: /^jsconfig\.json$/i },
{ tag: 'tslint.json', filePattern: /^tslint\.json$/i },
{ tag: 'eslint.json', filePattern: /^eslint\.json$/i },
{ tag: 'tasks.json', filePattern: /^tasks\.json$/i },
{ tag: 'launch.json', filePattern: /^launch\.json$/i },
{ tag: 'settings.json', filePattern: /^settings\.json$/i },
{ tag: 'webpack.config.js', filePattern: /^webpack\.config\.js$/i },
{ tag: 'project.json', filePattern: /^project\.json$/i },
{ tag: 'makefile', filePattern: /^makefile$/i },
{ tag: 'sln', filePattern: /^.+\.sln$/i },
{ tag: 'csproj', filePattern: /^.+\.csproj$/i },
{ tag: 'cmake', filePattern: /^.+\.cmake$/i },
{ tag: 'github-actions', filePattern: /^.+\.yml$/i, relativePathPattern: /^\.github(?:\/|\\)workflows$/i }
];
const fileTypes = new Map<string, number>();
@@ -67,123 +75,91 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise
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);
}
function collect(root: string, dir: string, filter: string[], token: { count: number, maxReached: boolean }): Promise<void> {
const relativePath = dir.substring(root.length + 1);
return new Promise(resolve => {
readdir(dir, { withFileTypes: true }, async (err, files) => {
// Ignore folders that can't be read
if (err) {
resolve();
return;
}
if (token.count > MAX_FILES) {
token.count += files.length;
token.maxReached = true;
resolve();
return;
}
let pending = files.length;
if (pending === 0) {
resolve();
return;
}
let filesToRead = files;
if (token.count + files.length > MAX_FILES) {
token.maxReached = true;
pending = MAX_FILES - token.count;
filesToRead = files.slice(0, pending);
}
if (token.count > MAX_FILES) {
token.count += files.length;
token.maxReached = true;
return done(results);
}
let pending = files.length;
if (pending === 0) {
return done(results);
}
for (const file of filesToRead) {
if (file.isDirectory()) {
if (!filter.includes(file.name)) {
await collect(root, join(dir, file.name), filter, token);
}
let filesToRead = files;
if (token.count + files.length > MAX_FILES) {
token.maxReached = true;
pending = MAX_FILES - token.count;
filesToRead = files.slice(0, pending);
}
token.count += files.length;
for (const file of filesToRead) {
stat(join(dir, file), (err, stats) => {
// Ignore files that can't be read
if (err) {
if (--pending === 0) {
return done(results);
resolve();
return;
}
} 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 {
results.push(file);
if (--pending === 0) {
done(results);
const index = file.name.lastIndexOf('.');
if (index >= 0) {
const fileType = file.name.substring(index + 1);
if (fileType) {
fileTypes.set(fileType, (fileTypes.get(fileType) ?? 0) + 1);
}
}
for (const configFile of configFilePatterns) {
if (configFile.relativePathPattern?.test(relativePath) !== false && configFile.filePattern.test(file.name)) {
configFiles.set(configFile.tag, (configFiles.get(configFile.tag) ?? 0) + 1);
}
}
if (--pending === 0) {
resolve();
return;
}
}
});
}
}
});
});
}
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);
const launchConfigs = await collectLaunchConfigs(folder);
resolve({
configFiles: asSortedItems(configFiles),
fileTypes: asSortedItems(fileTypes),
fileCount: token.count,
maxFilesReached: token.maxReached,
launchConfigFiles: launchConfigs
});
});
});
await collect(folder, folder, filter, token);
const launchConfigs = await collectLaunchConfigs(folder);
return {
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);
function asSortedItems(items: Map<string, number>): WorkspaceStatItem[] {
return [
...Iterable.map(items.entries(), ([name, count]) => ({ name: name, count: count }))
].sort((a, b) => b.count - a.count);
}
export function getMachineInfo(): IMachineInfo {
@@ -540,58 +516,61 @@ export class DiagnosticsService implements IDiagnosticsService {
}
public async reportWorkspaceStats(workspace: IWorkspaceInformation): 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 => {
type WorkspaceStatsClassification = {
'workspace.id': { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
rendererSessionId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
type WorkspaceStatsEvent = {
'workspace.id': string | undefined;
rendererSessionId: string;
};
this.telemetryService.publicLog2<WorkspaceStatsEvent, WorkspaceStatsClassification>('workspace.stats', {
'workspace.id': workspace.telemetryId,
rendererSessionId: workspace.rendererSessionId
});
type WorkspaceStatsFileClassification = {
rendererSessionId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
type: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
type WorkspaceStatsFileEvent = {
rendererSessionId: string;
type: string;
count: number;
};
stats.fileTypes.forEach(e => {
this.telemetryService.publicLog2<WorkspaceStatsFileEvent, WorkspaceStatsFileClassification>('workspace.stats.file', {
rendererSessionId: workspace.rendererSessionId,
type: e.name,
count: e.count
});
});
stats.launchConfigFiles.forEach(e => {
this.telemetryService.publicLog2<WorkspaceStatsFileEvent, WorkspaceStatsFileClassification>('workspace.stats.launchConfigFile', {
rendererSessionId: workspace.rendererSessionId,
type: e.name,
count: e.count
});
});
stats.configFiles.forEach(e => {
this.telemetryService.publicLog2<WorkspaceStatsFileEvent, WorkspaceStatsFileClassification>('workspace.stats.configFiles', {
rendererSessionId: workspace.rendererSessionId,
type: e.name,
count: e.count
});
});
}).catch(_ => {
// Report nothing if collecting metadata fails.
});
for (const { uri } of workspace.folders) {
const folderUri = URI.revive(uri);
if (folderUri.scheme !== 'file') {
continue;
}
});
const folder = folderUri.fsPath;
try {
const stats = await collectWorkspaceStats(folder, ['node_modules', '.git']);
type WorkspaceStatsClassification = {
'workspace.id': { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
rendererSessionId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
type WorkspaceStatsEvent = {
'workspace.id': string | undefined;
rendererSessionId: string;
};
this.telemetryService.publicLog2<WorkspaceStatsEvent, WorkspaceStatsClassification>('workspace.stats', {
'workspace.id': workspace.telemetryId,
rendererSessionId: workspace.rendererSessionId
});
type WorkspaceStatsFileClassification = {
rendererSessionId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
type: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
type WorkspaceStatsFileEvent = {
rendererSessionId: string;
type: string;
count: number;
};
stats.fileTypes.forEach(e => {
this.telemetryService.publicLog2<WorkspaceStatsFileEvent, WorkspaceStatsFileClassification>('workspace.stats.file', {
rendererSessionId: workspace.rendererSessionId,
type: e.name,
count: e.count
});
});
stats.launchConfigFiles.forEach(e => {
this.telemetryService.publicLog2<WorkspaceStatsFileEvent, WorkspaceStatsFileClassification>('workspace.stats.launchConfigFile', {
rendererSessionId: workspace.rendererSessionId,
type: e.name,
count: e.count
});
});
stats.configFiles.forEach(e => {
this.telemetryService.publicLog2<WorkspaceStatsFileEvent, WorkspaceStatsFileClassification>('workspace.stats.configFiles', {
rendererSessionId: workspace.rendererSessionId,
type: e.name,
count: e.count
});
});
} catch {
// Report nothing if collecting metadata fails.
}
}
}
}