mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-27 17:23:21 -05:00
Initial VS Code 1.19 source merge (#571)
* Initial 1.19 xcopy * Fix yarn build * Fix numerous build breaks * Next batch of build break fixes * More build break fixes * Runtime breaks * Additional post merge fixes * Fix windows setup file * Fix test failures. * Update license header blocks to refer to source eula
This commit is contained in:
@@ -153,7 +153,7 @@ export class ConfigWatcher<T> implements IConfigWatcher<T>, IDisposable {
|
||||
|
||||
try {
|
||||
const watcher = extfs.watch(path, (type, file) => this.onConfigFileChange(type, file, isParentFolder));
|
||||
watcher.on('error', (code, signal) => this.options.onError(`Error watching ${path} for configuration changes (${code}, ${signal})`));
|
||||
watcher.on('error', (code: number, signal: string) => this.options.onError(`Error watching ${path} for configuration changes (${code}, ${signal})`));
|
||||
|
||||
this.disposables.push(toDisposable(() => {
|
||||
watcher.removeAllListeners();
|
||||
@@ -209,7 +209,7 @@ export class ConfigWatcher<T> implements IConfigWatcher<T>, IDisposable {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const value = this.cache ? this.cache[key] : void 0;
|
||||
const value = this.cache ? (this.cache as any)[key] : void 0;
|
||||
|
||||
return typeof value !== 'undefined' ? value : fallback;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
import stream = require('vs/base/node/stream');
|
||||
import iconv = require('iconv-lite');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isLinux, isMacintosh } from 'vs/base/common/platform';
|
||||
import { exec } from 'child_process';
|
||||
|
||||
export const UTF8 = 'utf8';
|
||||
export const UTF8_with_bom = 'utf8bom';
|
||||
@@ -42,10 +44,6 @@ export function decodeStream(encoding: string): NodeJS.ReadWriteStream {
|
||||
return iconv.decodeStream(toNodeEncoding(encoding));
|
||||
}
|
||||
|
||||
export function encodeStream(encoding: string): NodeJS.ReadWriteStream {
|
||||
return iconv.encodeStream(toNodeEncoding(encoding));
|
||||
}
|
||||
|
||||
function toNodeEncoding(enc: string): string {
|
||||
if (enc === UTF8_with_bom) {
|
||||
return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it
|
||||
@@ -169,3 +167,88 @@ export function toCanonicalName(enc: string): string {
|
||||
return enc;
|
||||
}
|
||||
}
|
||||
|
||||
// https://ss64.com/nt/chcp.html
|
||||
const windowsTerminalEncodings = {
|
||||
'437': 'cp437', // United States
|
||||
'850': 'cp850', // Multilingual(Latin I)
|
||||
'852': 'cp852', // Slavic(Latin II)
|
||||
'855': 'cp855', // Cyrillic(Russian)
|
||||
'857': 'cp857', // Turkish
|
||||
'860': 'cp860', // Portuguese
|
||||
'861': 'cp861', // Icelandic
|
||||
'863': 'cp863', // Canadian - French
|
||||
'865': 'cp865', // Nordic
|
||||
'866': 'cp866', // Russian
|
||||
'869': 'cp869', // Modern Greek
|
||||
'1252': 'cp1252' // West European Latin
|
||||
};
|
||||
|
||||
export function resolveTerminalEncoding(verbose?: boolean): TPromise<string> {
|
||||
let rawEncodingPromise: TPromise<string>;
|
||||
|
||||
// Support a global environment variable to win over other mechanics
|
||||
const cliEncodingEnv = process.env['VSCODE_CLI_ENCODING'];
|
||||
if (cliEncodingEnv) {
|
||||
if (verbose) {
|
||||
console.log(`Found VSCODE_CLI_ENCODING variable: ${cliEncodingEnv}`);
|
||||
}
|
||||
|
||||
rawEncodingPromise = TPromise.as(cliEncodingEnv);
|
||||
}
|
||||
|
||||
// Linux/Mac: use "locale charmap" command
|
||||
else if (isLinux || isMacintosh) {
|
||||
rawEncodingPromise = new TPromise<string>(c => {
|
||||
if (verbose) {
|
||||
console.log('Running "locale charmap" to detect terminal encoding...');
|
||||
}
|
||||
|
||||
exec('locale charmap', (err, stdout, stderr) => c(stdout));
|
||||
});
|
||||
}
|
||||
|
||||
// Windows: educated guess
|
||||
else {
|
||||
rawEncodingPromise = new TPromise<string>(c => {
|
||||
if (verbose) {
|
||||
console.log('Running "chcp" to detect terminal encoding...');
|
||||
}
|
||||
|
||||
exec('chcp', (err, stdout, stderr) => {
|
||||
if (stdout) {
|
||||
const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings);
|
||||
for (let i = 0; i < windowsTerminalEncodingKeys.length; i++) {
|
||||
const key = windowsTerminalEncodingKeys[i];
|
||||
if (stdout.indexOf(key) >= 0) {
|
||||
return c(windowsTerminalEncodings[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c(void 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return rawEncodingPromise.then(rawEncoding => {
|
||||
if (verbose) {
|
||||
console.log(`Detected raw terminal encoding: ${rawEncoding}`);
|
||||
}
|
||||
|
||||
if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) {
|
||||
return UTF8;
|
||||
}
|
||||
|
||||
const iconvEncoding = toIconvLiteEncoding(rawEncoding);
|
||||
if (iconv.encodingExists(iconvEncoding)) {
|
||||
return iconvEncoding;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
console.log('Unsupported terminal encoding, falling back to UTF-8.');
|
||||
}
|
||||
|
||||
return UTF8;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export function fromEventEmitter<T>(emitter: EventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
|
||||
const fn = (...args) => result.fire(map(...args));
|
||||
const onFirstListenerAdd = () => emitter.on(eventName, fn);
|
||||
const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
|
||||
const result = new Emitter<T>({ onFirstListenerAdd, onLastListenerRemove });
|
||||
|
||||
return result.event;
|
||||
};
|
||||
@@ -12,6 +12,8 @@ import * as flow from 'vs/base/node/flow';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as paths from 'path';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { nfcall } from 'vs/base/common/async';
|
||||
|
||||
const loop = flow.loop;
|
||||
|
||||
@@ -41,80 +43,72 @@ export function readdir(path: string, callback: (error: Error, files: string[])
|
||||
return fs.readdir(path, callback);
|
||||
}
|
||||
|
||||
export function mkdirp(path: string, mode: number, callback: (error: Error) => void): void {
|
||||
fs.exists(path, exists => {
|
||||
if (exists) {
|
||||
return isDirectory(path, (err: Error, itIs?: boolean) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!itIs) {
|
||||
return callback(new Error('"' + path + '" is not a directory.'));
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
mkdirp(paths.dirname(path), mode, (err: Error) => {
|
||||
if (err) { callback(err); return; }
|
||||
|
||||
if (mode) {
|
||||
fs.mkdir(path, mode, error => {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
fs.chmod(path, mode, callback); // we need to explicitly chmod because of https://github.com/nodejs/node/issues/1104
|
||||
});
|
||||
} else {
|
||||
fs.mkdir(path, null, callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isDirectory(path: string, callback: (error: Error, isDirectory?: boolean) => void): void {
|
||||
fs.stat(path, (error, stat) => {
|
||||
if (error) { return callback(error); }
|
||||
|
||||
callback(null, stat.isDirectory());
|
||||
});
|
||||
}
|
||||
|
||||
export function copy(source: string, target: string, callback: (error: Error) => void, copiedSources?: { [path: string]: boolean }): void {
|
||||
if (!copiedSources) {
|
||||
copiedSources = Object.create(null);
|
||||
}
|
||||
|
||||
fs.stat(source, (error, stat) => {
|
||||
if (error) { return callback(error); }
|
||||
if (!stat.isDirectory()) { return pipeFs(source, target, stat.mode & 511, callback); }
|
||||
if (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
if (!stat.isDirectory()) {
|
||||
return pipeFs(source, target, stat.mode & 511, callback);
|
||||
}
|
||||
|
||||
if (copiedSources[source]) {
|
||||
return callback(null); // escape when there are cycles (can happen with symlinks)
|
||||
} else {
|
||||
copiedSources[source] = true; // remember as copied
|
||||
}
|
||||
|
||||
mkdirp(target, stat.mode & 511, err => {
|
||||
copiedSources[source] = true; // remember as copied
|
||||
|
||||
const proceed = function () {
|
||||
readdir(source, (err, files) => {
|
||||
loop(files, (file: string, clb: (error: Error, result: string[]) => void) => {
|
||||
copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, void 0), copiedSources);
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
mkdirp(target, stat.mode & 511).done(proceed, proceed);
|
||||
});
|
||||
}
|
||||
|
||||
export function mkdirp(path: string, mode?: number): TPromise<boolean> {
|
||||
const mkdir = () => nfcall(fs.mkdir, path, mode)
|
||||
.then(null, (err: NodeJS.ErrnoException) => {
|
||||
if (err.code === 'EEXIST') {
|
||||
return nfcall(fs.stat, path)
|
||||
.then((stat: fs.Stats) => stat.isDirectory
|
||||
? null
|
||||
: TPromise.wrapError(new Error(`'${path}' exists and is not a directory.`)));
|
||||
}
|
||||
|
||||
return TPromise.wrapError<boolean>(err);
|
||||
});
|
||||
|
||||
// is root?
|
||||
if (path === paths.dirname(path)) {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
return mkdir().then(null, (err: NodeJS.ErrnoException) => {
|
||||
if (err.code === 'ENOENT') {
|
||||
return mkdirp(paths.dirname(path), mode).then(mkdir);
|
||||
}
|
||||
|
||||
return TPromise.wrapError<boolean>(err);
|
||||
});
|
||||
}
|
||||
|
||||
function pipeFs(source: string, target: string, mode: number, callback: (error: Error) => void): void {
|
||||
let callbackHandled = false;
|
||||
|
||||
let readStream = fs.createReadStream(source);
|
||||
let writeStream = fs.createWriteStream(target, { mode: mode });
|
||||
const readStream = fs.createReadStream(source);
|
||||
const writeStream = fs.createWriteStream(target, { mode: mode });
|
||||
|
||||
let onError = (error: Error) => {
|
||||
const onError = (error: Error) => {
|
||||
if (!callbackHandled) {
|
||||
callbackHandled = true;
|
||||
callback(error);
|
||||
@@ -163,7 +157,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error) =>
|
||||
return rmRecursive(path, callback);
|
||||
}
|
||||
|
||||
let pathInTemp = paths.join(tmpFolder, uuid.generateUuid());
|
||||
const pathInTemp = paths.join(tmpFolder, uuid.generateUuid());
|
||||
fs.rename(path, pathInTemp, (error: Error) => {
|
||||
if (error) {
|
||||
return rmRecursive(path, callback); // if rename fails, delete without tmp dir
|
||||
@@ -200,7 +194,7 @@ function rmRecursive(path: string, callback: (error: Error) => void): void {
|
||||
if (err || !stat) {
|
||||
callback(err);
|
||||
} else if (!stat.isDirectory() || stat.isSymbolicLink() /* !!! never recurse into links when deleting !!! */) {
|
||||
let mode = stat.mode;
|
||||
const mode = stat.mode;
|
||||
if (!(mode & 128)) { // 128 === 0200
|
||||
fs.chmod(path, mode | 128, (err: Error) => { // 128 === 0200
|
||||
if (err) {
|
||||
@@ -369,6 +363,35 @@ export function writeFileAndFlush(path: string, data: string | NodeBuffer, optio
|
||||
});
|
||||
}
|
||||
|
||||
export function writeFileAndFlushSync(path: string, data: string | NodeBuffer, options?: { mode?: number; flag?: string; }): void {
|
||||
if (!canFlush) {
|
||||
return fs.writeFileSync(path, data, options);
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = { mode: 0o666, flag: 'w' };
|
||||
}
|
||||
|
||||
// Open the file with same flags and mode as fs.writeFile()
|
||||
const fd = fs.openSync(path, options.flag, options.mode);
|
||||
|
||||
try {
|
||||
|
||||
// It is valid to pass a fd handle to fs.writeFile() and this will keep the handle open!
|
||||
fs.writeFileSync(fd, data);
|
||||
|
||||
// Flush contents (not metadata) of the file to disk
|
||||
try {
|
||||
fs.fdatasyncSync(fd);
|
||||
} catch (syncError) {
|
||||
console.warn('[node.js fs] fdatasyncSync is now disabled for this session because it failed: ', syncError);
|
||||
canFlush = false;
|
||||
}
|
||||
} finally {
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from: https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83
|
||||
*
|
||||
@@ -384,7 +407,7 @@ export function realcaseSync(path: string): string {
|
||||
return path;
|
||||
}
|
||||
|
||||
const name = paths.basename(path).toLowerCase();
|
||||
const name = (paths.basename(path) /* can be '' for windows drive letters */ || path).toLowerCase();
|
||||
try {
|
||||
const entries = readdirSync(dir);
|
||||
const found = entries.filter(e => e.toLowerCase() === name); // use a case insensitive search
|
||||
@@ -454,11 +477,14 @@ function normalizePath(path: string): string {
|
||||
export function watch(path: string, onChange: (type: string, path: string) => void): fs.FSWatcher {
|
||||
const watcher = fs.watch(path);
|
||||
watcher.on('change', (type, raw) => {
|
||||
let file = raw.toString();
|
||||
if (platform.isMacintosh) {
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
file = strings.normalizeNFC(file);
|
||||
let file: string = null;
|
||||
if (raw) { // https://github.com/Microsoft/vscode/issues/38191
|
||||
file = raw.toString();
|
||||
if (platform.isMacintosh) {
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
file = strings.normalizeNFC(file);
|
||||
}
|
||||
}
|
||||
|
||||
onChange(type, file);
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as getmac from 'getmac';
|
||||
import * as crypto from 'crypto';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
@@ -86,17 +84,22 @@ export function getMachineId(): TPromise<string> {
|
||||
|
||||
function getMacMachineId(): TPromise<string> {
|
||||
return new TPromise<string>(resolve => {
|
||||
try {
|
||||
getmac.getMac((error, macAddress) => {
|
||||
if (!error) {
|
||||
resolve(crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex'));
|
||||
} else {
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
TPromise.join([import('crypto'), import('getmac')]).then(([crypto, getmac]) => {
|
||||
try {
|
||||
getmac.getMac((error, macAddress) => {
|
||||
if (!error) {
|
||||
resolve(crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex'));
|
||||
} else {
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
resolve(undefined);
|
||||
}
|
||||
}, err => {
|
||||
errors.onUnexpectedError(err);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import streams = require('stream');
|
||||
|
||||
import mime = require('vs/base/common/mime');
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
@@ -76,18 +74,6 @@ export interface DetectMimesOption {
|
||||
autoGuessEncoding?: boolean;
|
||||
}
|
||||
|
||||
function doDetectMimesFromStream(instream: streams.Readable, option?: DetectMimesOption): TPromise<IMimeAndEncoding> {
|
||||
return stream.readExactlyByStream(instream, maxBufferLen(option)).then((readResult: stream.ReadResult) => {
|
||||
return detectMimeAndEncodingFromBuffer(readResult, option && option.autoGuessEncoding);
|
||||
});
|
||||
}
|
||||
|
||||
function doDetectMimesFromFile(absolutePath: string, option?: DetectMimesOption): TPromise<IMimeAndEncoding> {
|
||||
return stream.readExactlyByFile(absolutePath, maxBufferLen(option)).then((readResult: stream.ReadResult) => {
|
||||
return detectMimeAndEncodingFromBuffer(readResult, option && option.autoGuessEncoding);
|
||||
});
|
||||
}
|
||||
|
||||
export function detectMimeAndEncodingFromBuffer(readResult: stream.ReadResult, autoGuessEncoding?: false): IMimeAndEncoding;
|
||||
export function detectMimeAndEncodingFromBuffer(readResult: stream.ReadResult, autoGuessEncoding?: boolean): TPromise<IMimeAndEncoding>;
|
||||
export function detectMimeAndEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResult, autoGuessEncoding?: boolean): TPromise<IMimeAndEncoding> | IMimeAndEncoding {
|
||||
@@ -117,57 +103,4 @@ export function detectMimeAndEncodingFromBuffer({ buffer, bytesRead }: stream.Re
|
||||
mimes: isText ? [mime.MIME_TEXT] : [mime.MIME_BINARY],
|
||||
encoding: enc
|
||||
};
|
||||
}
|
||||
|
||||
function filterAndSortMimes(detectedMimes: string[], guessedMimes: string[]): string[] {
|
||||
const mimes = detectedMimes;
|
||||
|
||||
// Add extension based mime as first element as this is the desire of whoever created the file.
|
||||
// Never care about application/octet-stream or application/unknown as guessed mime, as this is the fallback of the guess which is never accurate
|
||||
const guessedMime = guessedMimes[0];
|
||||
if (guessedMime !== mime.MIME_BINARY && guessedMime !== mime.MIME_UNKNOWN) {
|
||||
mimes.unshift(guessedMime);
|
||||
}
|
||||
|
||||
// Remove duplicate elements from array and sort unspecific mime to the end
|
||||
const uniqueSortedMimes = mimes.filter((element, position) => {
|
||||
return element && mimes.indexOf(element) === position;
|
||||
}).sort((mimeA, mimeB) => {
|
||||
if (mimeA === mime.MIME_BINARY) { return 1; }
|
||||
if (mimeB === mime.MIME_BINARY) { return -1; }
|
||||
if (mimeA === mime.MIME_TEXT) { return 1; }
|
||||
if (mimeB === mime.MIME_TEXT) { return -1; }
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return uniqueSortedMimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the given stream to detect its mime type. Returns an array of mime types sorted from most specific to unspecific.
|
||||
* @param instream the readable stream to detect the mime types from.
|
||||
* @param nameHint an additional hint that can be used to detect a mime from a file extension.
|
||||
*/
|
||||
export function detectMimesFromStream(instream: streams.Readable, nameHint: string, option?: DetectMimesOption): TPromise<IMimeAndEncoding> {
|
||||
return doDetectMimesFromStream(instream, option).then(encoding =>
|
||||
handleMimeResult(nameHint, encoding)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the given file to detect its mime type. Returns an array of mime types sorted from most specific to unspecific.
|
||||
* @param absolutePath the absolute path of the file.
|
||||
*/
|
||||
export function detectMimesFromFile(absolutePath: string, option?: DetectMimesOption): TPromise<IMimeAndEncoding> {
|
||||
return doDetectMimesFromFile(absolutePath, option).then(encoding =>
|
||||
handleMimeResult(absolutePath, encoding)
|
||||
);
|
||||
}
|
||||
|
||||
function handleMimeResult(nameHint: string, result: IMimeAndEncoding): IMimeAndEncoding {
|
||||
const filterAndSortedMimes = filterAndSortMimes(result.mimes, mime.guessMimeTypes(nameHint));
|
||||
result.mimes = filterAndSortedMimes;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { dirname, join } from 'path';
|
||||
import { join } from 'path';
|
||||
import { nfcall, Queue } from 'vs/base/common/async';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
@@ -26,32 +26,7 @@ export function chmod(path: string, mode: number): TPromise<boolean> {
|
||||
return nfcall(fs.chmod, path, mode);
|
||||
}
|
||||
|
||||
export function mkdirp(path: string, mode?: number): TPromise<boolean> {
|
||||
const mkdir = () => nfcall(fs.mkdir, path, mode)
|
||||
.then(null, (err: NodeJS.ErrnoException) => {
|
||||
if (err.code === 'EEXIST') {
|
||||
return nfcall(fs.stat, path)
|
||||
.then((stat: fs.Stats) => stat.isDirectory
|
||||
? null
|
||||
: TPromise.wrapError(new Error(`'${path}' exists and is not a directory.`)));
|
||||
}
|
||||
|
||||
return TPromise.wrapError<boolean>(err);
|
||||
});
|
||||
|
||||
// is root?
|
||||
if (path === dirname(path)) {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
return mkdir().then(null, (err: NodeJS.ErrnoException) => {
|
||||
if (err.code === 'ENOENT') {
|
||||
return mkdirp(dirname(path), mode).then(mkdir);
|
||||
}
|
||||
|
||||
return TPromise.wrapError<boolean>(err);
|
||||
});
|
||||
}
|
||||
export import mkdirp = extfs.mkdirp;
|
||||
|
||||
export function rimraf(path: string): TPromise<void> {
|
||||
return lstat(path).then(stat => {
|
||||
|
||||
@@ -11,24 +11,24 @@ import net = require('net');
|
||||
* Given a start point and a max number of retries, will find a port that
|
||||
* is openable. Will return 0 in case no free port can be found.
|
||||
*/
|
||||
export function findFreePort(startPort: number, giveUpAfter: number, timeout: number, clb: (port: number) => void): void {
|
||||
export function findFreePort(startPort: number, giveUpAfter: number, timeout: number): Thenable<number> {
|
||||
let done = false;
|
||||
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
return new Promise(resolve => {
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
return resolve(0);
|
||||
}
|
||||
}, timeout);
|
||||
|
||||
return clb(0);
|
||||
}
|
||||
}, timeout);
|
||||
|
||||
doFindFreePort(startPort, giveUpAfter, (port) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
clearTimeout(timeoutHandle);
|
||||
|
||||
return clb(port);
|
||||
}
|
||||
doFindFreePort(startPort, giveUpAfter, (port) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
clearTimeout(timeoutHandle);
|
||||
return resolve(port);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import * as cp from 'child_process';
|
||||
import ChildProcess = cp.ChildProcess;
|
||||
import exec = cp.exec;
|
||||
import spawn = cp.spawn;
|
||||
import { PassThrough } from 'stream';
|
||||
import { fork } from 'vs/base/node/stdFork';
|
||||
import nls = require('vs/nls');
|
||||
import { PPromise, TPromise, TValueCallback, TProgressCallback, ErrorCallback } from 'vs/base/common/winjs.base';
|
||||
@@ -28,17 +27,6 @@ export interface LineData {
|
||||
source: Source;
|
||||
}
|
||||
|
||||
export interface BufferData {
|
||||
data: Buffer;
|
||||
source: Source;
|
||||
}
|
||||
|
||||
export interface StreamData {
|
||||
stdin: NodeJS.WritableStream;
|
||||
stdout: NodeJS.ReadableStream;
|
||||
stderr: NodeJS.ReadableStream;
|
||||
}
|
||||
|
||||
function getWindowsCode(status: number): TerminateResponseCode {
|
||||
switch (status) {
|
||||
case 0:
|
||||
@@ -212,7 +200,7 @@ export abstract class AbstractProcess<TProgressData> {
|
||||
cc(result);
|
||||
};
|
||||
if (this.shell && Platform.isWindows) {
|
||||
let options: any = Objects.clone(this.options);
|
||||
let options: any = Objects.deepClone(this.options);
|
||||
options.windowsVerbatimArguments = true;
|
||||
options.detached = false;
|
||||
let quotedCommand: boolean = false;
|
||||
@@ -287,7 +275,7 @@ export abstract class AbstractProcess<TProgressData> {
|
||||
// Default is to do nothing.
|
||||
}
|
||||
|
||||
private static regexp = /^[^"].* .*[^"]/;
|
||||
private static readonly regexp = /^[^"].* .*[^"]/;
|
||||
private ensureQuotes(value: string) {
|
||||
if (AbstractProcess.regexp.test(value)) {
|
||||
return {
|
||||
@@ -302,10 +290,6 @@ export abstract class AbstractProcess<TProgressData> {
|
||||
}
|
||||
}
|
||||
|
||||
public isRunning(): boolean {
|
||||
return this.childProcessPromise !== null;
|
||||
}
|
||||
|
||||
public get pid(): TPromise<number> {
|
||||
return this.childProcessPromise.then(childProcess => childProcess.pid, err => -1);
|
||||
}
|
||||
@@ -391,60 +375,6 @@ export class LineProcess extends AbstractProcess<LineData> {
|
||||
}
|
||||
}
|
||||
|
||||
export class BufferProcess extends AbstractProcess<BufferData> {
|
||||
|
||||
public constructor(executable: Executable);
|
||||
public constructor(cmd: string, args: string[], shell: boolean, options: CommandOptions);
|
||||
public constructor(module: string, args: string[], options: ForkOptions);
|
||||
public constructor(arg1: string | Executable, arg2?: string[], arg3?: boolean | ForkOptions, arg4?: CommandOptions) {
|
||||
super(<any>arg1, arg2, <any>arg3, arg4);
|
||||
}
|
||||
|
||||
protected handleExec(cc: TValueCallback<SuccessData>, pp: TProgressCallback<BufferData>, error: Error, stdout: Buffer, stderr: Buffer): void {
|
||||
pp({ data: stdout, source: Source.stdout });
|
||||
pp({ data: stderr, source: Source.stderr });
|
||||
cc({ terminated: this.terminateRequested, error: error });
|
||||
}
|
||||
|
||||
protected handleSpawn(childProcess: ChildProcess, cc: TValueCallback<SuccessData>, pp: TProgressCallback<BufferData>, ee: ErrorCallback, sync: boolean): void {
|
||||
childProcess.stdout.on('data', (data: Buffer) => {
|
||||
pp({ data: data, source: Source.stdout });
|
||||
});
|
||||
childProcess.stderr.on('data', (data: Buffer) => {
|
||||
pp({ data: data, source: Source.stderr });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class StreamProcess extends AbstractProcess<StreamData> {
|
||||
|
||||
public constructor(executable: Executable);
|
||||
public constructor(cmd: string, args: string[], shell: boolean, options: CommandOptions);
|
||||
public constructor(module: string, args: string[], options: ForkOptions);
|
||||
public constructor(arg1: string | Executable, arg2?: string[], arg3?: boolean | ForkOptions, arg4?: CommandOptions) {
|
||||
super(<any>arg1, arg2, <any>arg3, arg4);
|
||||
}
|
||||
|
||||
protected handleExec(cc: TValueCallback<SuccessData>, pp: TProgressCallback<StreamData>, error: Error, stdout: Buffer, stderr: Buffer): void {
|
||||
let stdoutStream = new PassThrough();
|
||||
stdoutStream.end(stdout);
|
||||
let stderrStream = new PassThrough();
|
||||
stderrStream.end(stderr);
|
||||
pp({ stdin: null, stdout: stdoutStream, stderr: stderrStream });
|
||||
cc({ terminated: this.terminateRequested, error: error });
|
||||
}
|
||||
|
||||
protected handleSpawn(childProcess: ChildProcess, cc: TValueCallback<SuccessData>, pp: TProgressCallback<StreamData>, ee: ErrorCallback, sync: boolean): void {
|
||||
if (sync) {
|
||||
process.nextTick(() => {
|
||||
pp({ stdin: childProcess.stdin, stdout: childProcess.stdout, stderr: childProcess.stderr });
|
||||
});
|
||||
} else {
|
||||
pp({ stdin: childProcess.stdin, stdout: childProcess.stdout, stderr: childProcess.stderr });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IQueuedSender {
|
||||
send: (msg: any) => void;
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { join, basename } from 'path';
|
||||
import { writeFile } from 'vs/base/node/pfs';
|
||||
|
||||
export function startProfiling(name: string): TPromise<boolean> {
|
||||
return lazyV8Profiler.value.then(profiler => {
|
||||
profiler.startProfiling(name);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
const _isRunningOutOfDev = process.env['VSCODE_DEV'];
|
||||
|
||||
export function stopProfiling(dir: string, prefix: string): TPromise<string> {
|
||||
return lazyV8Profiler.value.then(profiler => {
|
||||
return profiler.stopProfiling();
|
||||
}).then(profile => {
|
||||
return new TPromise<any>((resolve, reject) => {
|
||||
|
||||
// remove pii paths
|
||||
if (!_isRunningOutOfDev) {
|
||||
removePiiPaths(profile); // remove pii from our users
|
||||
}
|
||||
|
||||
profile.export(function (error, result) {
|
||||
profile.delete();
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
let filepath = join(dir, `${prefix}_${profile.title}.cpuprofile`);
|
||||
if (!_isRunningOutOfDev) {
|
||||
filepath += '.txt'; // github issues must be: txt, zip, png, gif
|
||||
}
|
||||
writeFile(filepath, result).then(() => resolve(filepath), reject);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function removePiiPaths(profile: Profile) {
|
||||
const stack = [profile.head];
|
||||
while (stack.length > 0) {
|
||||
const element = stack.pop();
|
||||
if (element.url) {
|
||||
const shortUrl = basename(element.url);
|
||||
if (element.url !== shortUrl) {
|
||||
element.url = `pii_removed/${shortUrl}`;
|
||||
}
|
||||
}
|
||||
if (element.children) {
|
||||
stack.push(...element.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare interface Profiler {
|
||||
startProfiling(name: string): void;
|
||||
stopProfiling(): Profile;
|
||||
}
|
||||
|
||||
export declare interface Profile {
|
||||
title: string;
|
||||
export(callback: (err, data) => void): void;
|
||||
delete(): void;
|
||||
head: ProfileSample;
|
||||
}
|
||||
|
||||
export declare interface ProfileSample {
|
||||
// bailoutReason:""
|
||||
// callUID:2333
|
||||
// children:Array[39]
|
||||
// functionName:"(root)"
|
||||
// hitCount:0
|
||||
// id:1
|
||||
// lineNumber:0
|
||||
// scriptId:0
|
||||
// url:""
|
||||
url: string;
|
||||
children: ProfileSample[];
|
||||
}
|
||||
|
||||
const lazyV8Profiler = new class {
|
||||
private _value: TPromise<Profiler>;
|
||||
get value() {
|
||||
if (!this._value) {
|
||||
this._value = new TPromise<Profiler>((resolve, reject) => {
|
||||
require(['v8-profiler'], resolve, reject);
|
||||
});
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
};
|
||||
183
src/vs/base/node/ps-win.ps1
Normal file
183
src/vs/base/node/ps-win.ps1
Normal file
@@ -0,0 +1,183 @@
|
||||
################################################################################################
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
################################################################################################
|
||||
|
||||
Param(
|
||||
[string]$ProcessName = "code.exe",
|
||||
[int]$MaxSamples = 10
|
||||
)
|
||||
|
||||
$processLength = "process(".Length
|
||||
|
||||
function Get-MachineInfo {
|
||||
$model = (Get-WmiObject -Class Win32_Processor).Name
|
||||
$memory = (Get-WmiObject -Class Win32_PhysicalMemory | Measure-Object -Property Capacity -Sum).Sum / 1MB
|
||||
$wmi_cs = Get-WmiObject -Class Win32_ComputerSystem
|
||||
return @{
|
||||
"type" = "machineInfo"
|
||||
"model" = $model
|
||||
"processors" = $wmi_cs.NumberOfProcessors
|
||||
"logicalProcessors" = $wmi_cs.NumberOfLogicalProcessors
|
||||
"totalMemory" = $memory
|
||||
|
||||
}
|
||||
}
|
||||
$machineInfo = Get-MachineInfo
|
||||
|
||||
function Get-MachineState {
|
||||
$proc = Get-WmiObject Win32_Processor
|
||||
$os = Get-WmiObject win32_OperatingSystem
|
||||
return @{
|
||||
"type" = 'machineState'
|
||||
"cpuLoad" = $proc.LoadPercentage
|
||||
"handles" = (Get-Process | Measure-Object Handles -Sum).Sum
|
||||
"memory" = @{
|
||||
"total" = $os.TotalVisibleMemorySize
|
||||
"free" = $os.FreePhysicalMemory
|
||||
"swapTotal" = $os.TotalVirtualMemorySize
|
||||
"swapFree" = $os.FreeVirtualMemory
|
||||
}
|
||||
}
|
||||
}
|
||||
$machineState = Get-MachineState
|
||||
|
||||
$processId2CpuLoad = @{}
|
||||
function Get-PerformanceCounters ($logicalProcessors) {
|
||||
$counterError
|
||||
# In a first round we get the performance counters and the process ids.
|
||||
$counters = (Get-Counter ("\Process(*)\% Processor Time", "\Process(*)\ID Process") -ErrorAction SilentlyContinue).CounterSamples
|
||||
$processKey2Id = @{}
|
||||
foreach ($counter in $counters) {
|
||||
if ($counter.Status -ne 0) {
|
||||
continue
|
||||
}
|
||||
$path = $counter.path;
|
||||
$segments = $path.Split("\");
|
||||
$kind = $segments[4];
|
||||
$processKey = $segments[3].Substring($processLength, $segments[3].Length - $processLength - 1)
|
||||
if ($kind -eq "id process") {
|
||||
$processKey2Id[$processKey] = [uint32]$counter.CookedValue
|
||||
}
|
||||
}
|
||||
foreach ($counter in $counters) {
|
||||
if ($counter.Status -ne 0) {
|
||||
continue
|
||||
}
|
||||
$path = $counter.path;
|
||||
$segments = $path.Split("\");
|
||||
$kind = $segments[4];
|
||||
$processKey = $segments[3].Substring($processLength, $segments[3].Length - $processLength - 1)
|
||||
if ($kind -eq "% processor time") {
|
||||
$array = New-Object double[] ($MaxSamples + 1)
|
||||
$array[0] = ($counter.CookedValue / $logicalProcessors)
|
||||
$processId = $processKey2Id[$processKey]
|
||||
if ($processId) {
|
||||
$processId2CpuLoad[$processId] = $array
|
||||
}
|
||||
}
|
||||
}
|
||||
# Now lets sample another 10 times but only the processor time
|
||||
$samples = Get-Counter "\Process(*)\% Processor Time" -SampleInterval 1 -MaxSamples $MaxSamples -ErrorAction SilentlyContinue
|
||||
for ($s = 0; $s -lt $samples.Count; $s++) {
|
||||
$counters = $samples[$s].CounterSamples;
|
||||
foreach ($counter in $counters) {
|
||||
if ($counter.Status -ne 0) {
|
||||
continue
|
||||
}
|
||||
$path = $counter.path;
|
||||
$segments = $path.Split("\");
|
||||
$processKey = $segments[3].Substring($processLength, $segments[3].Length - $processLength - 1)
|
||||
$processKey = $processKey2Id[$processKey];
|
||||
if ($processKey) {
|
||||
$processId2CpuLoad[$processKey][$s + 1] = ($counter.CookedValue / $logicalProcessors)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Get-PerformanceCounters -logicalProcessors $machineInfo.logicalProcessors
|
||||
|
||||
$topElements = New-Object PSObject[] $processId2CpuLoad.Keys.Count;
|
||||
$index = 0;
|
||||
foreach ($key in $processId2CpuLoad.Keys) {
|
||||
$obj = [PSCustomObject]@{
|
||||
ProcessId = $key
|
||||
Load = ($processId2CpuLoad[$key] | Measure-Object -Sum).Sum / ($MaxSamples + 1)
|
||||
}
|
||||
$topElements[$index] = $obj
|
||||
$index++
|
||||
}
|
||||
$topElements = $topElements | Sort-Object Load -Descending
|
||||
|
||||
# Get all code processes
|
||||
$codeProcesses = @{}
|
||||
foreach ($item in Get-WmiObject Win32_Process -Filter "name = '$ProcessName'") {
|
||||
$codeProcesses[$item.ProcessId] = $item
|
||||
}
|
||||
foreach ($item in Get-WmiObject Win32_Process -Filter "name = 'codeHelper.exe'") {
|
||||
$codeProcesses[$item.ProcessId] = $item
|
||||
}
|
||||
$otherProcesses = @{}
|
||||
foreach ($item in Get-WmiObject Win32_Process -Filter "name Like '%'") {
|
||||
if (!($codeProcesses.Contains($item.ProcessId))) {
|
||||
$otherProcesses[$item.ProcessId] = $item
|
||||
}
|
||||
}
|
||||
$modified = $false
|
||||
do {
|
||||
$toDelete = @()
|
||||
$modified = $false
|
||||
foreach ($item in $otherProcesses.Values) {
|
||||
if ($codeProcesses.Contains([uint32]$item.ParentProcessId)) {
|
||||
$codeProcesses[$item.ProcessId] = $item;
|
||||
$toDelete += $item
|
||||
}
|
||||
}
|
||||
foreach ($item in $toDelete) {
|
||||
$otherProcesses.Remove([uint32]$item.ProcessId)
|
||||
$modified = $true
|
||||
}
|
||||
} while ($modified)
|
||||
|
||||
$result = New-Object PSObject[] (2 + [math]::Min(5, $topElements.Count) + $codeProcesses.Count)
|
||||
$result[0] = $machineInfo
|
||||
$result[1] = $machineState
|
||||
$index = 2;
|
||||
for($i = 0; $i -lt 5 -and $i -lt $topElements.Count; $i++) {
|
||||
$element = $topElements[$i]
|
||||
$item = $codeProcesses[[uint32]$element.ProcessId]
|
||||
if (!$item) {
|
||||
$item = $otherProcesses[[uint32]$element.ProcessId]
|
||||
}
|
||||
if ($item) {
|
||||
$cpuLoad = $processId2CpuLoad[[uint32]$item.ProcessId] | % { [pscustomobject] $_ }
|
||||
$result[$index] = [pscustomobject]@{
|
||||
"type" = "topProcess"
|
||||
"name" = $item.Name
|
||||
"processId" = $item.ProcessId
|
||||
"parentProcessId" = $item.ParentProcessId
|
||||
"commandLine" = $item.CommandLine
|
||||
"handles" = $item.HandleCount
|
||||
"cpuLoad" = $cpuLoad
|
||||
"workingSetSize" = $item.WorkingSetSize
|
||||
}
|
||||
$index++
|
||||
}
|
||||
}
|
||||
foreach ($item in $codeProcesses.Values) {
|
||||
# we need to convert this otherwise to JSON with create a value, count object and not an inline array
|
||||
$cpuLoad = $processId2CpuLoad[[uint32]$item.ProcessId] | % { [pscustomobject] $_ }
|
||||
$result[$index] = [pscustomobject]@{
|
||||
"type" = "processInfo"
|
||||
"name" = $item.Name
|
||||
"processId" = $item.ProcessId
|
||||
"parentProcessId" = $item.ParentProcessId
|
||||
"commandLine" = $item.CommandLine
|
||||
"handles" = $item.HandleCount
|
||||
"cpuLoad" = $cpuLoad
|
||||
"workingSetSize" = $item.WorkingSetSize
|
||||
}
|
||||
$index++
|
||||
}
|
||||
|
||||
$result | ConvertTo-Json -Depth 99
|
||||
235
src/vs/base/node/ps.ts
Normal file
235
src/vs/base/node/ps.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { spawn, exec } from 'child_process';
|
||||
import * as path from 'path';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
export interface ProcessItem {
|
||||
name: string;
|
||||
cmd: string;
|
||||
pid: number;
|
||||
ppid: number;
|
||||
load: number;
|
||||
mem: number;
|
||||
|
||||
children?: ProcessItem[];
|
||||
}
|
||||
|
||||
export function listProcesses(rootPid: number): Promise<ProcessItem> {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let rootItem: ProcessItem;
|
||||
const map = new Map<number, ProcessItem>();
|
||||
|
||||
function addToTree(pid: number, ppid: number, cmd: string, load: number, mem: number) {
|
||||
|
||||
const parent = map.get(ppid);
|
||||
if (pid === rootPid || parent) {
|
||||
|
||||
const item: ProcessItem = {
|
||||
name: findName(cmd),
|
||||
cmd,
|
||||
pid,
|
||||
ppid,
|
||||
load,
|
||||
mem
|
||||
};
|
||||
map.set(pid, item);
|
||||
|
||||
if (pid === rootPid) {
|
||||
rootItem = item;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
if (!parent.children) {
|
||||
parent.children = [];
|
||||
}
|
||||
parent.children.push(item);
|
||||
if (parent.children.length > 1) {
|
||||
parent.children = parent.children.sort((a, b) => a.pid - b.pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findName(cmd: string): string {
|
||||
|
||||
const RENDERER_PROCESS_HINT = /--disable-blink-features=Auxclick/;
|
||||
const WINDOWS_WATCHER_HINT = /\\watcher\\win32\\CodeHelper.exe/;
|
||||
const TYPE = /--type=([a-zA-Z-]+)/;
|
||||
|
||||
// find windows file watcher
|
||||
if (WINDOWS_WATCHER_HINT.exec(cmd)) {
|
||||
return 'watcherService';
|
||||
}
|
||||
|
||||
// find "--type=xxxx"
|
||||
let matches = TYPE.exec(cmd);
|
||||
if (matches && matches.length === 2) {
|
||||
if (matches[1] === 'renderer') {
|
||||
if (!RENDERER_PROCESS_HINT.exec(cmd)) {
|
||||
return 'shared-process';
|
||||
}
|
||||
|
||||
return `window`;
|
||||
}
|
||||
return matches[1];
|
||||
}
|
||||
|
||||
// find all xxxx.js
|
||||
const JS = /[a-zA-Z-]+\.js/g;
|
||||
let result = '';
|
||||
do {
|
||||
matches = JS.exec(cmd);
|
||||
if (matches) {
|
||||
result += matches + ' ';
|
||||
}
|
||||
} while (matches);
|
||||
|
||||
if (result) {
|
||||
if (cmd.indexOf('node ') !== 0) {
|
||||
return `electron_node ${result}`;
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
|
||||
interface ProcessInfo {
|
||||
type: 'processInfo';
|
||||
name: string;
|
||||
processId: number;
|
||||
parentProcessId: number;
|
||||
commandLine: string;
|
||||
handles: number;
|
||||
cpuLoad: number[];
|
||||
workingSetSize: number;
|
||||
}
|
||||
|
||||
interface TopProcess {
|
||||
type: 'topProcess';
|
||||
name: string;
|
||||
processId: number;
|
||||
parentProcessId: number;
|
||||
commandLine: string;
|
||||
handles: number;
|
||||
cpuLoad: number[];
|
||||
workingSetSize: number;
|
||||
}
|
||||
|
||||
type Item = ProcessInfo | TopProcess;
|
||||
|
||||
const cleanUNCPrefix = (value: string): string => {
|
||||
if (value.indexOf('\\\\?\\') === 0) {
|
||||
return value.substr(4);
|
||||
} else if (value.indexOf('\\??\\') === 0) {
|
||||
return value.substr(4);
|
||||
} else if (value.indexOf('"\\\\?\\') === 0) {
|
||||
return '"' + value.substr(5);
|
||||
} else if (value.indexOf('"\\??\\') === 0) {
|
||||
return '"' + value.substr(5);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
const execMain = path.basename(process.execPath);
|
||||
const script = URI.parse(require.toUrl('vs/base/node/ps-win.ps1')).fsPath;
|
||||
const commandLine = `& {& '${script}' -ProcessName '${execMain}' -MaxSamples 3}`;
|
||||
const cmd = spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', commandLine]);
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
cmd.stdout.on('data', data => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
cmd.stderr.on('data', data => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
cmd.on('exit', () => {
|
||||
if (stderr.length > 0) {
|
||||
reject(stderr);
|
||||
}
|
||||
let processItems: Map<number, ProcessItem> = new Map();
|
||||
try {
|
||||
const items: Item[] = JSON.parse(stdout);
|
||||
for (const item of items) {
|
||||
if (item.type === 'processInfo') {
|
||||
let load = 0;
|
||||
if (item.cpuLoad) {
|
||||
for (let value of item.cpuLoad) {
|
||||
load += value;
|
||||
}
|
||||
load = load / item.cpuLoad.length;
|
||||
} else {
|
||||
load = -1;
|
||||
}
|
||||
let commandLine = cleanUNCPrefix(item.commandLine);
|
||||
processItems.set(item.processId, {
|
||||
name: findName(commandLine),
|
||||
cmd: commandLine,
|
||||
pid: item.processId,
|
||||
ppid: item.parentProcessId,
|
||||
load: load,
|
||||
mem: item.workingSetSize
|
||||
});
|
||||
}
|
||||
}
|
||||
rootItem = processItems.get(rootPid);
|
||||
if (rootItem) {
|
||||
processItems.forEach(item => {
|
||||
let parent = processItems.get(item.ppid);
|
||||
if (parent) {
|
||||
if (!parent.children) {
|
||||
parent.children = [];
|
||||
}
|
||||
parent.children.push(item);
|
||||
}
|
||||
});
|
||||
processItems.forEach(item => {
|
||||
if (item.children) {
|
||||
item.children = item.children.sort((a, b) => a.pid - b.pid);
|
||||
}
|
||||
});
|
||||
resolve(rootItem);
|
||||
} else {
|
||||
reject(new Error(`Root process ${rootPid} not found`));
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
} else { // OS X & Linux
|
||||
|
||||
const CMD = 'ps -ax -o pid=,ppid=,pcpu=,pmem=,command=';
|
||||
const PID_CMD = /^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/;
|
||||
|
||||
exec(CMD, { maxBuffer: 1000 * 1024 }, (err, stdout, stderr) => {
|
||||
|
||||
if (err || stderr) {
|
||||
reject(err || stderr.toString());
|
||||
} else {
|
||||
|
||||
const lines = stdout.toString().split('\n');
|
||||
for (const line of lines) {
|
||||
let matches = PID_CMD.exec(line.trim());
|
||||
if (matches && matches.length === 6) {
|
||||
addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4]));
|
||||
}
|
||||
}
|
||||
|
||||
resolve(rootItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
39
src/vs/base/node/startupTimers.d.ts
vendored
39
src/vs/base/node/startupTimers.d.ts
vendored
@@ -1,39 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Profile } from './profiler';
|
||||
|
||||
declare interface TickStart {
|
||||
name: string;
|
||||
started: number;
|
||||
}
|
||||
|
||||
export declare class Tick {
|
||||
|
||||
readonly duration: number;
|
||||
readonly name: string;
|
||||
readonly started: number;
|
||||
readonly stopped: number;
|
||||
readonly profile: Profile;
|
||||
|
||||
static compareByStart(a: Tick, b: Tick): number;
|
||||
}
|
||||
|
||||
declare interface TickController {
|
||||
while<T extends Thenable<any>>(t: T): T;
|
||||
stop(stopped?: number): void;
|
||||
}
|
||||
|
||||
export function startTimer(name: string): TickController;
|
||||
|
||||
export function stopTimer(name: string): void;
|
||||
|
||||
export function ticks(): Tick[];
|
||||
|
||||
export function tick(name: string): Tick;
|
||||
|
||||
export function setProfileList(names: string[]): void;
|
||||
|
||||
export function disable(): void;
|
||||
@@ -1,128 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/*global define*/
|
||||
|
||||
var requireProfiler;
|
||||
|
||||
if (typeof define !== "function" && typeof module === "object" && typeof module.exports === "object") {
|
||||
// this is commonjs, fake amd
|
||||
global.define = function (dep, callback) {
|
||||
module.exports = callback();
|
||||
global.define = undefined;
|
||||
};
|
||||
requireProfiler = function () {
|
||||
return require('v8-profiler');
|
||||
};
|
||||
} else {
|
||||
// this is amd
|
||||
requireProfiler = function () {
|
||||
return require.__$__nodeRequire('v8-profiler');
|
||||
};
|
||||
}
|
||||
|
||||
define([], function () {
|
||||
|
||||
function Tick(name, started, stopped, profile) {
|
||||
this.name = name;
|
||||
this.started = started;
|
||||
this.stopped = stopped;
|
||||
this.duration = Math.round(((stopped[0] * 1.e9 + stopped[1]) - (started[0] * 1e9 + started[1])) / 1.e6);
|
||||
this.profile = profile;
|
||||
}
|
||||
Tick.compareByStart = function (a, b) {
|
||||
if (a.started < b.started) {
|
||||
return -1;
|
||||
} else if (a.started > b.started) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// This module can be loaded in an amd and commonjs-context.
|
||||
// Because we want both instances to use the same tick-data
|
||||
// we store them globally
|
||||
global._perfStarts = global._perfStarts || new Map();
|
||||
global._perfTicks = global._perfTicks || new Map();
|
||||
global._perfToBeProfiled = global._perfToBeProfiled || new Set();
|
||||
|
||||
var _starts = global._perfStarts;
|
||||
var _ticks = global._perfTicks;
|
||||
var _toBeProfiled = global._perfToBeProfiled;
|
||||
|
||||
function startTimer(name) {
|
||||
if (_starts.has(name)) {
|
||||
throw new Error("${name}" + " already exists");
|
||||
}
|
||||
if (_toBeProfiled.has(name)) {
|
||||
requireProfiler().startProfiling(name, true);
|
||||
}
|
||||
_starts.set(name, { name: name, started: process.hrtime() });
|
||||
var stop = stopTimer.bind(undefined, name);
|
||||
return {
|
||||
stop: stop,
|
||||
while: function (thenable) {
|
||||
thenable.then(function () { stop(); }, function () { stop(); });
|
||||
return thenable;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function stopTimer(name) {
|
||||
var profile = _toBeProfiled.has(name) ? requireProfiler().stopProfiling(name) : undefined;
|
||||
var start = _starts.get(name);
|
||||
if (start !== undefined) {
|
||||
var tick = new Tick(start.name, start.started, process.hrtime(), profile);
|
||||
_ticks.set(name, tick);
|
||||
_starts.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
function ticks() {
|
||||
var ret = [];
|
||||
_ticks.forEach(function (value) { ret.push(value); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
function tick(name) {
|
||||
var ret = _ticks.get(name);
|
||||
if (!ret) {
|
||||
var now = Date.now();
|
||||
ret = new Tick(name, now, now);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function setProfileList(names) {
|
||||
_toBeProfiled.clear();
|
||||
names.forEach(function (name) { _toBeProfiled.add(name); });
|
||||
}
|
||||
|
||||
var exports = {
|
||||
Tick: Tick,
|
||||
startTimer: startTimer,
|
||||
stopTimer: stopTimer,
|
||||
ticks: ticks,
|
||||
tick: tick,
|
||||
setProfileList: setProfileList,
|
||||
disable: disable,
|
||||
};
|
||||
|
||||
function disable() {
|
||||
var emptyController = Object.freeze({ while: function (t) { return t; }, stop: function () { } });
|
||||
var emptyTicks = Object.create([]);
|
||||
exports.startTimer = function () { return emptyController; };
|
||||
exports.stopTimer = function () { };
|
||||
exports.ticks = function () { return emptyTicks; };
|
||||
|
||||
delete global._perfStarts;
|
||||
delete global._perfTicks;
|
||||
}
|
||||
|
||||
return exports;
|
||||
});
|
||||
151
src/vs/base/node/stats.ts
Normal file
151
src/vs/base/node/stats.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { readdirSync, statSync, existsSync, readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
export interface WorkspaceStatItem {
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface WorkspaceStats {
|
||||
fileTypes: WorkspaceStatItem[];
|
||||
configFiles: WorkspaceStatItem[];
|
||||
fileCount: number;
|
||||
maxFilesReached: boolean;
|
||||
}
|
||||
|
||||
function asSortedItems(map: Map<string, number>): WorkspaceStatItem[] {
|
||||
let a: WorkspaceStatItem[] = [];
|
||||
map.forEach((value, index) => a.push({ name: index, count: value }));
|
||||
return a.sort((a, b) => b.count - a.count);
|
||||
}
|
||||
|
||||
export function collectLaunchConfigs(folder: string): WorkspaceStatItem[] {
|
||||
let launchConfigs = new Map<string, number>();
|
||||
|
||||
let launchConfig = join(folder, '.vscode', 'launch.json');
|
||||
if (existsSync(launchConfig)) {
|
||||
try {
|
||||
const contents = readFileSync(launchConfig).toString();
|
||||
const json = JSON.parse(contents);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
return asSortedItems(launchConfigs);
|
||||
}
|
||||
|
||||
export function collectWorkspaceStats(folder: string, filter: string[]): 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 }
|
||||
];
|
||||
|
||||
let fileTypes = new Map<string, number>();
|
||||
let configFiles = new Map<string, number>();
|
||||
|
||||
const MAX_FILES = 20000;
|
||||
|
||||
let walkSync = (dir: string, acceptFile: (fileName: string) => void, filter: string[], token) => {
|
||||
if (token.maxReached) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let files = readdirSync(dir);
|
||||
for (const file of files) {
|
||||
try {
|
||||
if (statSync(join(dir, file)).isDirectory()) {
|
||||
if (filter.indexOf(file) === -1) {
|
||||
walkSync(join(dir, file), acceptFile, filter, token);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (token.count++ >= MAX_FILES) {
|
||||
token.maxReached = true;
|
||||
return;
|
||||
}
|
||||
acceptFile(file);
|
||||
}
|
||||
} catch {
|
||||
// skip over files for which stat fails
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// skip over folders that cannot be read
|
||||
}
|
||||
};
|
||||
|
||||
let addFileType = (fileType: string) => {
|
||||
if (fileTypes.has(fileType)) {
|
||||
fileTypes.set(fileType, fileTypes.get(fileType) + 1);
|
||||
}
|
||||
else {
|
||||
fileTypes.set(fileType, 1);
|
||||
}
|
||||
};
|
||||
|
||||
let 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let acceptFile = (name: string) => {
|
||||
if (name.lastIndexOf('.') >= 0) {
|
||||
let suffix: string | undefined = name.split('.').pop();
|
||||
if (suffix) {
|
||||
addFileType(suffix);
|
||||
}
|
||||
}
|
||||
addConfigFiles(name);
|
||||
};
|
||||
|
||||
let token: { count: number, maxReached: boolean } = { count: 0, maxReached: false };
|
||||
walkSync(folder, acceptFile, filter, token);
|
||||
|
||||
return {
|
||||
configFiles: asSortedItems(configFiles),
|
||||
fileTypes: asSortedItems(fileTypes),
|
||||
fileCount: token.count,
|
||||
maxFilesReached: token.maxReached
|
||||
|
||||
};
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
'use strict';
|
||||
|
||||
import fs = require('fs');
|
||||
import stream = require('stream');
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
@@ -15,43 +14,6 @@ export interface ReadResult {
|
||||
bytesRead: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to total bytes from the provided stream.
|
||||
*/
|
||||
export function readExactlyByStream(stream: stream.Readable, totalBytes: number): TPromise<ReadResult> {
|
||||
return new TPromise<ReadResult>((complete, error) => {
|
||||
let done = false;
|
||||
let buffer = new Buffer(totalBytes);
|
||||
let bytesRead = 0;
|
||||
|
||||
stream.on('data', (data: NodeBuffer) => {
|
||||
let bytesToRead = Math.min(totalBytes - bytesRead, data.length);
|
||||
data.copy(buffer, bytesRead, 0, bytesToRead);
|
||||
bytesRead += bytesToRead;
|
||||
|
||||
if (bytesRead === totalBytes) {
|
||||
(stream as any).destroy(); // Will trigger the close event eventually
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', (e: Error) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
|
||||
let onSuccess = () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
complete({ buffer, bytesRead });
|
||||
}
|
||||
};
|
||||
|
||||
stream.on('close', onSuccess);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads totalBytes from the provided file.
|
||||
*/
|
||||
@@ -63,7 +25,7 @@ export function readExactlyByFile(file: string, totalBytes: number): TPromise<Re
|
||||
}
|
||||
|
||||
function end(err: Error, resultBuffer: NodeBuffer, bytesRead: number): void {
|
||||
fs.close(fd, (closeError: Error) => {
|
||||
fs.close(fd, closeError => {
|
||||
if (closeError) {
|
||||
return error(closeError);
|
||||
}
|
||||
@@ -76,35 +38,30 @@ export function readExactlyByFile(file: string, totalBytes: number): TPromise<Re
|
||||
});
|
||||
}
|
||||
|
||||
let buffer = new Buffer(totalBytes);
|
||||
let bytesRead = 0;
|
||||
let zeroAttempts = 0;
|
||||
function loop(): void {
|
||||
fs.read(fd, buffer, bytesRead, totalBytes - bytesRead, null, (err, moreBytesRead) => {
|
||||
const buffer = new Buffer(totalBytes);
|
||||
let offset = 0;
|
||||
|
||||
function readChunk(): void {
|
||||
fs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => {
|
||||
if (err) {
|
||||
return end(err, null, 0);
|
||||
}
|
||||
|
||||
// Retry up to N times in case 0 bytes where read
|
||||
if (moreBytesRead === 0) {
|
||||
if (++zeroAttempts === 10) {
|
||||
return end(null, buffer, bytesRead);
|
||||
}
|
||||
|
||||
return loop();
|
||||
if (bytesRead === 0) {
|
||||
return end(null, buffer, offset);
|
||||
}
|
||||
|
||||
bytesRead += moreBytesRead;
|
||||
offset += bytesRead;
|
||||
|
||||
if (bytesRead === totalBytes) {
|
||||
return end(null, buffer, bytesRead);
|
||||
if (offset === totalBytes) {
|
||||
return end(null, buffer, offset);
|
||||
}
|
||||
|
||||
return loop();
|
||||
return readChunk();
|
||||
});
|
||||
}
|
||||
|
||||
loop();
|
||||
readChunk();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -126,7 +83,7 @@ export function readToMatchingString(file: string, matchingString: string, chunk
|
||||
}
|
||||
|
||||
function end(err: Error, result: string): void {
|
||||
fs.close(fd, (closeError: Error) => {
|
||||
fs.close(fd, closeError => {
|
||||
if (closeError) {
|
||||
return error(closeError);
|
||||
}
|
||||
@@ -140,39 +97,34 @@ export function readToMatchingString(file: string, matchingString: string, chunk
|
||||
}
|
||||
|
||||
let buffer = new Buffer(maximumBytesToRead);
|
||||
let bytesRead = 0;
|
||||
let zeroAttempts = 0;
|
||||
function loop(): void {
|
||||
fs.read(fd, buffer, bytesRead, chunkBytes, null, (err, moreBytesRead) => {
|
||||
let offset = 0;
|
||||
|
||||
function readChunk(): void {
|
||||
fs.read(fd, buffer, offset, chunkBytes, null, (err, bytesRead) => {
|
||||
if (err) {
|
||||
return end(err, null);
|
||||
}
|
||||
|
||||
// Retry up to N times in case 0 bytes where read
|
||||
if (moreBytesRead === 0) {
|
||||
if (++zeroAttempts === 10) {
|
||||
return end(null, null);
|
||||
}
|
||||
|
||||
return loop();
|
||||
if (bytesRead === 0) {
|
||||
return end(null, null);
|
||||
}
|
||||
|
||||
bytesRead += moreBytesRead;
|
||||
offset += bytesRead;
|
||||
|
||||
const newLineIndex = buffer.indexOf(matchingString);
|
||||
if (newLineIndex >= 0) {
|
||||
return end(null, buffer.toString('utf8').substr(0, newLineIndex));
|
||||
}
|
||||
|
||||
if (bytesRead >= maximumBytesToRead) {
|
||||
if (offset >= maximumBytesToRead) {
|
||||
return end(new Error(`Could not find ${matchingString} in first ${maximumBytesToRead} bytes of ${file}`), null);
|
||||
}
|
||||
|
||||
return loop();
|
||||
return readChunk();
|
||||
});
|
||||
}
|
||||
|
||||
loop();
|
||||
readChunk();
|
||||
})
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user