Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { spawn, ChildProcess } from 'child_process';
import { TPromise } from 'vs/base/common/winjs.base';
import { assign } from 'vs/base/common/objects';
import { parseCLIProcessArgv, buildHelpMessage } from 'vs/platform/environment/node/argv';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
@@ -14,11 +13,13 @@ import * as paths from 'path';
import * as os from 'os';
import * as fs from 'fs';
import { whenDeleted } from 'vs/base/node/pfs';
import { findFreePort } from 'vs/base/node/ports';
import { findFreePort, randomPort } from 'vs/base/node/ports';
import { resolveTerminalEncoding } from 'vs/base/node/encoding';
import * as iconv from 'iconv-lite';
import { writeFileAndFlushSync } from 'vs/base/node/extfs';
import { isWindows } from 'vs/base/common/platform';
import { ProfilingSession, Target } from 'v8-inspect-profiler';
import { createWaitMarkerFile } from 'vs/code/node/wait';
function shouldSpawnCliProcess(argv: ParsedArgs): boolean {
return !!argv['install-source']
@@ -28,7 +29,7 @@ function shouldSpawnCliProcess(argv: ParsedArgs): boolean {
}
interface IMainCli {
main: (argv: ParsedArgs) => TPromise<void>;
main: (argv: ParsedArgs) => Thenable<void>;
}
export async function main(argv: string[]): Promise<any> {
@@ -38,7 +39,7 @@ export async function main(argv: string[]): Promise<any> {
args = parseCLIProcessArgv(argv);
} catch (err) {
console.error(err.message);
return TPromise.as(null);
return;
}
// Help
@@ -53,8 +54,9 @@ export async function main(argv: string[]): Promise<any> {
// Extensions Management
else if (shouldSpawnCliProcess(args)) {
const mainCli = new TPromise<IMainCli>(c => require(['vs/code/node/cliProcessMain'], c));
return mainCli.then(cli => cli.main(args));
const cli = await new Promise<IMainCli>((c, e) => require(['vs/code/node/cliProcessMain'], c, e));
await cli.main(args);
return;
}
// Write File
@@ -69,13 +71,13 @@ export async function main(argv: string[]): Promise<any> {
!fs.existsSync(source) || !fs.statSync(source).isFile() || // make sure source exists as file
!fs.existsSync(target) || !fs.statSync(target).isFile() // make sure target exists as file
) {
return TPromise.wrapError(new Error('Using --file-write with invalid arguments.'));
throw new Error('Using --file-write with invalid arguments.');
}
try {
// Check for readonly status and chmod if so if we are told so
let targetMode: number;
let targetMode: number = 0;
let restoreMode = false;
if (!!args['file-chmod']) {
targetMode = fs.statSync(target).mode;
@@ -87,18 +89,17 @@ export async function main(argv: string[]): Promise<any> {
// Write source to target
const data = fs.readFileSync(source);
try {
if (isWindows) {
// On Windows we use a different strategy of saving the file
// by first truncating the file and then writing with r+ mode.
// This helps to save hidden files on Windows
// (see https://github.com/Microsoft/vscode/issues/931) and
// prevent removing alternate data streams
// (see https://github.com/Microsoft/vscode/issues/6363)
fs.truncateSync(target, 0);
writeFileAndFlushSync(target, data, { flag: 'r+' });
} else {
writeFileAndFlushSync(target, data);
} catch (error) {
// On Windows and if the file exists with an EPERM error, we try a different strategy of saving the file
// by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows
// (see https://github.com/Microsoft/vscode/issues/931)
if (isWindows && error.code === 'EPERM') {
fs.truncateSync(target, 0);
writeFileAndFlushSync(target, data, { flag: 'r+' });
} else {
throw error;
}
}
// Restore previous mode as needed
@@ -106,10 +107,9 @@ export async function main(argv: string[]): Promise<any> {
fs.chmodSync(target, targetMode);
}
} catch (error) {
return TPromise.wrapError(new Error(`Using --file-write resulted in an error: ${error}`));
error.message = `Error using --file-write: ${error.message}`;
throw error;
}
return TPromise.as(null);
}
// Just Code
@@ -127,15 +127,15 @@ export async function main(argv: string[]): Promise<any> {
if (verbose) {
env['ELECTRON_ENABLE_LOGGING'] = '1';
processCallbacks.push(child => {
processCallbacks.push(async child => {
child.stdout.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
child.stderr.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
return new TPromise<void>(c => child.once('exit', () => c(null)));
await new Promise(c => child.once('exit', () => c()));
});
}
let stdinWithoutTty: boolean;
let stdinWithoutTty: boolean = false;
try {
stdinWithoutTty = !process.stdin.isTTY; // Via https://twitter.com/MylesBorins/status/782009479382626304
} catch (error) {
@@ -161,7 +161,7 @@ export async function main(argv: string[]): Promise<any> {
stdinFilePath = paths.join(os.tmpdir(), `code-stdin-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 3)}.txt`);
// open tmp file for writing
let stdinFileError: Error;
let stdinFileError: Error | undefined;
let stdinFileStream: fs.WriteStream;
try {
stdinFileStream = fs.createWriteStream(stdinFilePath);
@@ -172,7 +172,7 @@ export async function main(argv: string[]): Promise<any> {
if (!stdinFileError) {
// Pipe into tmp file using terminals encoding
resolveTerminalEncoding(verbose).done(encoding => {
resolveTerminalEncoding(verbose).then(encoding => {
const converterStream = iconv.decodeStream(encoding);
process.stdin.pipe(converterStream).pipe(stdinFileStream);
});
@@ -198,7 +198,7 @@ export async function main(argv: string[]): Promise<any> {
// If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message
// if we detect that data flows into via stdin after a certain timeout.
else if (args._.length === 0) {
processCallbacks.push(child => new TPromise(c => {
processCallbacks.push(child => new Promise(c => {
const dataListener = () => {
if (isWindows) {
console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`);
@@ -226,24 +226,11 @@ export async function main(argv: string[]): Promise<any> {
// and pass it over to the starting instance. We can use this file
// to wait for it to be deleted to monitor that the edited file
// is closed and then exit the waiting process.
let waitMarkerFilePath: string;
let waitMarkerFilePath: string | undefined;
if (args.wait) {
let waitMarkerError: Error;
const randomTmpFile = paths.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10));
try {
fs.writeFileSync(randomTmpFile, '');
waitMarkerFilePath = randomTmpFile;
waitMarkerFilePath = await createWaitMarkerFile(verbose);
if (waitMarkerFilePath) {
argv.push('--waitMarkerFilePath', waitMarkerFilePath);
} catch (error) {
waitMarkerError = error;
}
if (verbose) {
if (waitMarkerError) {
console.error(`Failed to create marker file for --wait: ${waitMarkerError.toString()}`);
} else {
console.log(`Marker file for --wait created: ${waitMarkerFilePath}`);
}
}
}
@@ -252,16 +239,16 @@ export async function main(argv: string[]): Promise<any> {
// to get better profile traces. Last, we listen on stdout for a signal that tells us to
// stop profiling.
if (args['prof-startup']) {
const portMain = await findFreePort(9222, 10, 6000);
const portRenderer = await findFreePort(portMain + 1, 10, 6000);
const portExthost = await findFreePort(portRenderer + 1, 10, 6000);
const portMain = await findFreePort(randomPort(), 10, 3000);
const portRenderer = await findFreePort(portMain + 1, 10, 3000);
const portExthost = await findFreePort(portRenderer + 1, 10, 3000);
if (!portMain || !portRenderer || !portExthost) {
console.error('Failed to find free ports for profiler to connect to do.');
return;
// fail the operation when one of the ports couldn't be accquired.
if (portMain * portRenderer * portExthost === 0) {
throw new Error('Failed to find free ports for profiler. Make sure to shutdown all instances of the editor first.');
}
const filenamePrefix = paths.join(os.homedir(), Math.random().toString(16).slice(-4));
const filenamePrefix = paths.join(os.homedir(), 'prof-' + Math.random().toString(16).slice(-4));
argv.push(`--inspect-brk=${portMain}`);
argv.push(`--remote-debugging-port=${portRenderer}`);
@@ -271,38 +258,81 @@ export async function main(argv: string[]): Promise<any> {
fs.writeFileSync(filenamePrefix, argv.slice(-6).join('|'));
processCallbacks.push(async child => {
processCallbacks.push(async _child => {
// load and start profiler
const profiler = await import('v8-inspect-profiler');
const main = await profiler.startProfiling({ port: portMain });
const renderer = await profiler.startProfiling({ port: portRenderer, tries: 200 });
const extHost = await profiler.startProfiling({ port: portExthost, tries: 300 });
class Profiler {
static async start(name: string, filenamePrefix: string, opts: { port: number, tries?: number, target?: (targets: Target[]) => Target }) {
const profiler = await import('v8-inspect-profiler');
// wait for the renderer to delete the
// marker file
whenDeleted(filenamePrefix);
let session: ProfilingSession;
try {
session = await profiler.startProfiling(opts);
} catch (err) {
console.error(`FAILED to start profiling for '${name}' on port '${opts.port}'`);
}
let profileMain = await main.stop();
let profileRenderer = await renderer.stop();
let profileExtHost = await extHost.stop();
let suffix = '';
return {
async stop() {
if (!session) {
return;
}
let suffix = '';
let profile = await session.stop();
if (!process.env['VSCODE_DEV']) {
// when running from a not-development-build we remove
// absolute filenames because we don't want to reveal anything
// about users. We also append the `.txt` suffix to make it
// easier to attach these files to GH issues
profile = profiler.rewriteAbsolutePaths(profile, 'piiRemoved');
suffix = '.txt';
}
if (!process.env['VSCODE_DEV']) {
// when running from a not-development-build we remove
// absolute filenames because we don't want to reveal anything
// about users. We also append the `.txt` suffix to make it
// easier to attach these files to GH issues
profileMain = profiler.rewriteAbsolutePaths(profileMain, 'piiRemoved');
profileRenderer = profiler.rewriteAbsolutePaths(profileRenderer, 'piiRemoved');
profileExtHost = profiler.rewriteAbsolutePaths(profileExtHost, 'piiRemoved');
suffix = '.txt';
await profiler.writeProfile(profile, `${filenamePrefix}.${name}.cpuprofile${suffix}`);
}
};
}
}
// finally stop profiling and save profiles to disk
await profiler.writeProfile(profileMain, `${filenamePrefix}-main.cpuprofile${suffix}`);
await profiler.writeProfile(profileRenderer, `${filenamePrefix}-renderer.cpuprofile${suffix}`);
await profiler.writeProfile(profileExtHost, `${filenamePrefix}-exthost.cpuprofile${suffix}`);
try {
// load and start profiler
const mainProfileRequest = Profiler.start('main', filenamePrefix, { port: portMain });
const extHostProfileRequest = Profiler.start('extHost', filenamePrefix, { port: portExthost, tries: 300 });
const rendererProfileRequest = Profiler.start('renderer', filenamePrefix, {
port: portRenderer,
tries: 200,
target: function (targets) {
return targets.filter(target => {
if (!target.webSocketDebuggerUrl) {
return false;
}
if (target.type === 'page') {
return target.url.indexOf('workbench/workbench.html') > 0;
} else {
return true;
}
})[0];
}
});
const main = await mainProfileRequest;
const extHost = await extHostProfileRequest;
const renderer = await rendererProfileRequest;
// wait for the renderer to delete the
// marker file
await whenDeleted(filenamePrefix);
// stop profiling
await main.stop();
await renderer.stop();
await extHost.stop();
// re-create the marker file to signal that profiling is done
fs.writeFileSync(filenamePrefix, '');
} catch (e) {
console.error('Failed to profile startup. Make sure to quit Code first.');
}
});
}
@@ -327,13 +357,13 @@ export async function main(argv: string[]): Promise<any> {
const child = spawn(process.execPath, argv.slice(2), options);
if (args.wait && waitMarkerFilePath) {
return new TPromise<void>(c => {
return new Promise<void>(c => {
// Complete when process exits
child.once('exit', () => c(null));
child.once('exit', () => c(void 0));
// Complete when wait marker file is deleted
whenDeleted(waitMarkerFilePath).done(c, c);
whenDeleted(waitMarkerFilePath!).then(c, c);
}).then(() => {
// Make sure to delete the tmp stdin file if we have any
@@ -343,10 +373,8 @@ export async function main(argv: string[]): Promise<any> {
});
}
return TPromise.join(processCallbacks.map(callback => callback(child)));
return Promise.all(processCallbacks.map(callback => callback(child)));
}
return TPromise.as(null);
}
function eventuallyExit(code: number): void {