mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 02:48:30 -05:00
Merge from master
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user