Skip to content

add --prof-startup flag #21631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build/gulpfile.vscode.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ function packageTask(platform, arch, opts) {
.pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node']))
.pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
.pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js']))
.pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/**']));
.pipe(util.cleanNodeModule('v8-profiler', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js']))
.pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/**']));

let all = es.merge(
packageJsonStream,
Expand Down
5 changes: 5 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"native-keymap": "0.4.0",
"node-pty": "0.6.2",
"semver": "4.3.6",
"v8-profiler": "jrieken/v8-profiler#vscode",
"vscode-debugprotocol": "1.17.0",
"vscode-textmate": "^3.1.1",
"winreg": "1.2.0",
Expand Down
7 changes: 7 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

'use strict';

if (process.argv.indexOf('--prof-startup') >= 0) {
var profiler = require('v8-profiler');
var prefix = require('crypto').randomBytes(2).toString('hex');
process.env.VSCODE_PROFILES_PREFIX = prefix;
profiler.startProfiling('main', true);
}

// Perf measurements
global.perfStartTime = Date.now();

Expand Down
95 changes: 95 additions & 0 deletions src/vs/base/node/profiler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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;
});
}

export function stopProfiling(dir: string, prefix: string, keepPaths: boolean = false): TPromise<string> {
return lazyV8Profiler.value.then(profiler => {
return profiler.stopProfiling();
}).then(profile => {
return new TPromise<any>((resolve, reject) => {

// remove pii paths
if (!keepPaths) {
removePiiPaths(profile);
}

profile.export(function (error, result) {
profile.delete();
if (error) {
reject(error);
return;
}
const filepath = join(dir, `${prefix}_${profile.title}.cpuprofile.txt`);
writeFile(filepath, result).then(() => resolve(filepath), reject);
});
});
});
}

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);
stopProfiling(): Profile;
}

declare interface Profile {
title: string;
export(callback: (err, data) => void);
delete();
head: ProfileSample;
}

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((resolve, reject) => {
require(['v8-profiler'], resolve, reject);
});
}
return this._value;
}
};
8 changes: 8 additions & 0 deletions src/vs/code/electron-main/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import * as path from 'path';
import * as platform from 'vs/base/common/platform';
import * as objects from 'vs/base/common/objects';
import { stopProfiling } from 'vs/base/node/profiler';
import nls = require('vs/nls');
import { IStorageService } from 'vs/code/electron-main/storage';
import { shell, screen, BrowserWindow, systemPreferences, app } from 'electron';
Expand Down Expand Up @@ -466,6 +467,13 @@ export class VSCodeWindow {
}
}, 10000);
}

// (--prof-startup) save profile to disk
const { profileStartup } = this.environmentService;
if (profileStartup) {
stopProfiling(profileStartup.dir, profileStartup.prefix)
.done(undefined, err => console.error(err));
}
}

public reload(cli?: ParsedArgs): void {
Expand Down
2 changes: 2 additions & 0 deletions src/vs/platform/environment/common/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface ParsedArgs {
locale?: string;
'user-data-dir'?: string;
performance?: boolean;
'prof-startup'?: string;
verbose?: boolean;
logExtensionHostCommunication?: boolean;
'disable-extensions'?: boolean;
Expand Down Expand Up @@ -75,6 +76,7 @@ export interface IEnvironmentService {
verbose: boolean;
wait: boolean;
performance: boolean;
profileStartup: { prefix: string, dir: string } | undefined;

mainIPCHandle: string;
sharedIPCHandle: string;
Expand Down
2 changes: 2 additions & 0 deletions src/vs/platform/environment/node/argv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const options: minimist.Opts = {
'unity-launch',
'reuse-window',
'performance',
'prof-startup',
'verbose',
'logExtensionHostCommunication',
'disable-extensions',
Expand Down Expand Up @@ -112,6 +113,7 @@ export const optionsHelp: { [name: string]: string; } = {
'--locale <locale>': localize('locale', "The locale to use (e.g. en-US or zh-TW)."),
'-n, --new-window': localize('newWindow', "Force a new instance of Code."),
'-p, --performance': localize('performance', "Start with the 'Developer: Startup Performance' command enabled."),
'--prof-startup': localize('prof-startup', "Run CPU profiler during startup"),
'-r, --reuse-window': localize('reuseWindow', "Force opening a file or folder in the last active window."),
'--user-data-dir <dir>': localize('userDataDir', "Specifies the directory that user data is kept in, useful when running as root."),
'--verbose': localize('verbose', "Print verbose output (implies --wait)."),
Expand Down
15 changes: 14 additions & 1 deletion src/vs/platform/environment/node/environmentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,22 @@ export class EnvironmentService implements IEnvironmentService {
get isBuilt(): boolean { return !process.env['VSCODE_DEV']; }
get verbose(): boolean { return this._args.verbose; }
get wait(): boolean { return this._args.wait; }
get performance(): boolean { return this._args.performance; }
get logExtensionHostCommunication(): boolean { return this._args.logExtensionHostCommunication; }

get performance(): boolean { return this._args.performance; }

@memoize
get profileStartup(): { prefix: string, dir: string } | undefined {
if (this._args['prof-startup']) {
return {
prefix: process.env.VSCODE_PROFILES_PREFIX,
dir: os.homedir()
};
} else {
return undefined;
}
}

@memoize
get mainIPCHandle(): string { return getIPCHandle(this.userDataPath, 'main'); }

Expand Down
3 changes: 2 additions & 1 deletion src/vs/platform/windows/common/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface IWindowsService {
unmaximizeWindow(windowId: number): TPromise<void>;
setDocumentEdited(windowId: number, flag: boolean): TPromise<void>;
quit(): TPromise<void>;
relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise<void>;

// Global methods
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void>;
Expand Down Expand Up @@ -95,4 +96,4 @@ export interface IWindowSettings {
autoDetectHighContrast: boolean;
menuBarVisibility: MenuBarVisibility;
newWindowDimensions: 'default' | 'inherit' | 'maximized' | 'fullscreen';
}
}
5 changes: 5 additions & 0 deletions src/vs/platform/windows/common/windowsIpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'showWindow': return this.service.showWindow(arg);
case 'getWindows': return this.service.getWindows();
case 'getWindowCount': return this.service.getWindowCount();
case 'relaunch': return this.service.relaunch(arg[0]);
case 'quit': return this.service.quit();
case 'log': return this.service.log(arg[0], arg[1]);
case 'closeExtensionHostWindow': return this.service.closeExtensionHostWindow(arg);
Expand Down Expand Up @@ -175,6 +176,10 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('quit');
}

relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise<void> {
return this.channel.call('relaunch', [options]);
}

openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void> {
return this.channel.call('openWindow', [paths, options]);
}
Expand Down
18 changes: 18 additions & 0 deletions src/vs/platform/windows/electron-main/windowsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,24 @@ export class WindowsService implements IWindowsService, IDisposable {
return TPromise.as(null);
}

relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise<void> {
const args = process.argv.slice(1);
if (options.addArgs) {
args.push(...options.addArgs);
}
if (options.removeArgs) {
for (const a of options.removeArgs) {
const idx = args.indexOf(a);
if (idx >= 0) {
args.splice(idx, 1);
}
}
}
app.quit();
app.once('quit', () => app.relaunch({ args }));
return TPromise.as(null);
}

private openFileForURI(filePath: string): TPromise<void> {
const cli = assign(Object.create(null), this.environmentService.args, { goto: true });
const pathsToOpen = [filePath];
Expand Down
21 changes: 13 additions & 8 deletions src/vs/workbench/electron-browser/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,17 +738,25 @@ export class ReportPerformanceIssueAction extends Action {
super(id, label);
}

public run(): TPromise<boolean> {
public run(appendix?: string): TPromise<boolean> {
return this.integrityService.isPure().then(res => {
const issueUrl = this.generatePerformanceIssueUrl(product.reportIssueUrl, pkg.name, pkg.version, product.commit, product.date, res.isPure);
const issueUrl = this.generatePerformanceIssueUrl(product.reportIssueUrl, pkg.name, pkg.version, product.commit, product.date, res.isPure, appendix);

window.open(issueUrl);

return TPromise.as(true);
});
}

private generatePerformanceIssueUrl(baseUrl: string, name: string, version: string, commit: string, date: string, isPure: boolean): string {
private generatePerformanceIssueUrl(baseUrl: string, name: string, version: string, commit: string, date: string, isPure: boolean, appendix?: string): string {

if (!appendix) {
appendix = `Additional Steps to Reproduce (if any):

1.
2.`;
}

let nodeModuleLoadTime: number;
if (this.environmentService.performance) {
nodeModuleLoadTime = this.computeNodeModulesLoadTime();
Expand All @@ -775,10 +783,7 @@ ${this.generatePerformanceTable(nodeModuleLoadTime)}

---

Additional Steps to Reproduce (if any):

1.
2.`
${appendix}`
);

return `${baseUrl}${queryStringPrefix}body=${body}`;
Expand Down Expand Up @@ -899,4 +904,4 @@ export class OpenIntroductoryVideosUrlAction extends Action {
window.open(OpenIntroductoryVideosUrlAction.URL);
return null;
}
}
}
5 changes: 5 additions & 0 deletions src/vs/workbench/electron-browser/bootstrap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

'use strict';

if (window.location.search.indexOf('prof-startup') >= 0) {
var profiler = require('v8-profiler');
profiler.startProfiling('renderer', true);
}

/*global window,document,define*/

const path = require('path');
Expand Down
Loading