X Tutup
Skip to content
Closed
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
1 change: 1 addition & 0 deletions modules/benchpress/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export {JsonFileReporter} from './src/reporter/json_file_reporter';
export {SampleDescription} from './src/sample_description';
export {PerflogMetric} from './src/metric/perflog_metric';
export {ChromeDriverExtension} from './src/webdriver/chrome_driver_extension';
export {FirefoxDriverExtension} from './src/webdriver/firefox_driver_extension';
export {IOsDriverExtension} from './src/webdriver/ios_driver_extension';
export {Runner} from './src/runner';
export {Options} from './src/common_options';
Expand Down
25 changes: 21 additions & 4 deletions modules/benchpress/src/firefox_extension/data/installed_script.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
declare var exportFunction;
declare var unsafeWindow;

exportFunction(function() { (<any>self).port.emit('startProfiler'); }, unsafeWindow,
{defineAs: "startProfiler"});
exportFunction(function() {
var curTime = unsafeWindow.performance.now();
(<any>self).port.emit('startProfiler', curTime);
}, unsafeWindow, {defineAs: "startProfiler"});

exportFunction(function(filePath) { (<any>self).port.emit('stopAndRecord', filePath); },
unsafeWindow, {defineAs: "stopAndRecord"});
exportFunction(function() { (<any>self).port.emit('stopProfiler'); }, unsafeWindow,
{defineAs: "stopProfiler"});

exportFunction(function(cb) {
(<any>self).port.once('perfProfile', cb);
(<any>self).port.emit('getProfile');
}, unsafeWindow, {defineAs: "getProfile"});

exportFunction(function() { (<any>self).port.emit('forceGC'); }, unsafeWindow,
{defineAs: "forceGC"});

exportFunction(function(name) {
var curTime = unsafeWindow.performance.now();
(<any>self).port.emit('markStart', name, curTime);
}, unsafeWindow, {defineAs: "markStart"});

exportFunction(function(name) {
var curTime = unsafeWindow.performance.now();
(<any>self).port.emit('markEnd', name, curTime);
}, unsafeWindow, {defineAs: "markEnd"});
72 changes: 43 additions & 29 deletions modules/benchpress/src/firefox_extension/lib/main.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,66 @@
/// <reference path="../../../../angular2/typings/node/node.d.ts" />

var file = require('sdk/io/file');
var {Cc, Ci, Cu} = require("chrome");
var {Cc, Ci, Cu} = require('chrome');
var os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
var ParserUtil = require('./parser_util');

class Profiler {
private _profiler;
constructor() { this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler); }
private _markerEvents: List<any>;
private _profilerStartTime: number;

start(entries, interval, features) {
constructor() { this._profiler = Cc['@mozilla.org/tools/profiler;1'].getService(Ci.nsIProfiler); }

start(entries, interval, features, timeStarted) {
this._profiler.StartProfiler(entries, interval, features, features.length);
this._profilerStartTime = timeStarted;
this._markerEvents = [];
}

stop() { this._profiler.StopProfiler(); }
getProfileData() { return this._profiler.getProfileData(); }
}

getProfilePerfEvents() {
var profileData = this._profiler.getProfileData();
var perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
perfEvents = this._mergeMarkerEvents(perfEvents);
perfEvents.sort(function(event1, event2) { return event1.ts - event2.ts; }); // Sort by ts
return perfEvents;
}

_mergeMarkerEvents(perfEvents: List<any>): List<any> {
this._markerEvents.forEach(function(markerEvent) { perfEvents.push(markerEvent); });
return perfEvents;
}

addStartEvent(name: string, timeStarted: number) {
this._markerEvents.push({ph: 'b', ts: timeStarted - this._profilerStartTime, name: name});
}

function saveToFile(savePath: string, body: string) {
var textWriter = file.open(savePath, 'w');
textWriter.write(body);
textWriter.close();
addEndEvent(name: string, timeEnded: number) {
this._markerEvents.push({ph: 'e', ts: timeEnded - this._profilerStartTime, name: name});
}
}

function forceGC() {
Cu.forceGC();
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.notifyObservers(null, "child-gc-request", null);
}

var profiler = new Profiler();
function startProfiler() {
profiler.start(/* = profiler memory */ 10000000, 1, ['leaf', 'js', "stackwalk", 'gc']);
os.notifyObservers(null, 'child-gc-request', null);
};
function stopAndRecord(filePath) {
var profileData = profiler.getProfileData();
profiler.stop();
saveToFile(filePath, JSON.stringify(profileData, null, 2));
};


var mod = require("sdk/page-mod");
var data = require("sdk/self").data;
var mod = require('sdk/page-mod');
var data = require('sdk/self').data;
var profiler = new Profiler();
mod.PageMod({
include: ['*'],
contentScriptFile: data.url("installed_script.js"),
contentScriptFile: data.url('installed_script.js'),
onAttach: worker => {
worker.port.on('startProfiler', () => startProfiler());
worker.port.on('stopAndRecord', filePath => stopAndRecord(filePath));
worker.port.on('forceGC', () => forceGC());
worker.port.on('startProfiler',
(timeStarted) => profiler.start(/* = profiler memory */ 1000000, 1,
['leaf', 'js', 'stackwalk', 'gc'], timeStarted));
worker.port.on('stopProfiler', () => profiler.stop());
worker.port.on('getProfile',
() => worker.port.emit('perfProfile', profiler.getProfilePerfEvents()));
worker.port.on('forceGC', forceGC);
worker.port.on('markStart', (name, timeStarted) => profiler.addStartEvent(name, timeStarted));
worker.port.on('markEnd', (name, timeEnded) => profiler.addEndEvent(name, timeEnded));
}
});
2 changes: 2 additions & 0 deletions modules/benchpress/src/firefox_extension/lib/parser_util.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
library benchpress.src.firefox_extension.lib.parser_util;
//no dart implementation
84 changes: 84 additions & 0 deletions modules/benchpress/src/firefox_extension/lib/parser_util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/// <reference path="../../../../angular2/typings/node/node.d.ts" />

/**
* @param {Object} perfProfile The perf profile JSON object.
* @return {Array<Object>} An array of recognized events that are captured
* within the perf profile.
*/
export function convertPerfProfileToEvents(perfProfile: any): List<any> {
var inProgressEvents = new Map(); // map from event name to start time
var finishedEvents = []; // Array<Event> finished events
var addFinishedEvent = function(eventName, startTime, endTime) {
var categorizedEventName = categorizeEvent(eventName);
var args = undefined;
if (categorizedEventName == 'gc') {
// TODO: We cannot measure heap size at the moment
args = {usedHeapSize: 0};
}
if (startTime == endTime) {
// Finished instantly
finishedEvents.push({ph: 'X', ts: startTime, name: categorizedEventName, args: args});
} else {
// Has duration
finishedEvents.push({ph: 'B', ts: startTime, name: categorizedEventName, args: args});
finishedEvents.push({ph: 'E', ts: endTime, name: categorizedEventName, args: args});
}
};

var samples = perfProfile.threads[0].samples;
// In perf profile, firefox samples all the frames in set time intervals. Here
// we go through all the samples and construct the start and end time for each
// event.
for (var i = 0; i < samples.length; ++i) {
var sample = samples[i];
var sampleTime = sample.time;

// Add all the frames into a set so it's easier/faster to find the set
// differences
var sampleFrames = new Set();
sample.frames.forEach(function(frame) { sampleFrames.add(frame.location); });

// If an event is in the inProgressEvents map, but not in the current sample,
// then it must have just finished. We add this event to the finishedEvents
// array and remove it from the inProgressEvents map.
var previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time);
inProgressEvents.forEach(function(startTime, eventName) {
if (!(sampleFrames.has(eventName))) {
addFinishedEvent(eventName, startTime, previousSampleTime);
inProgressEvents.delete(eventName);
}
});

// If an event is in the current sample, but not in the inProgressEvents map,
// then it must have just started. We add this event to the inProgressEvents
// map.
sampleFrames.forEach(function(eventName) {
if (!(inProgressEvents.has(eventName))) {
inProgressEvents.set(eventName, sampleTime);
}
});
}

// If anything is still in progress, we need to included it as a finished event
// since recording ended.
var lastSampleTime = samples[samples.length - 1].time;
inProgressEvents.forEach(function(startTime, eventName) {
addFinishedEvent(eventName, startTime, lastSampleTime);
});

// Remove all the unknown categories.
return finishedEvents.filter(function(event) { return event.name != 'unknown'; });
}

// TODO: this is most likely not exhaustive.
export function categorizeEvent(eventName: string): string {
if (eventName.indexOf('PresShell::Paint') > -1) {
return 'render';
} else if (eventName.indexOf('FirefoxDriver.prototype.executeScript') > -1) {
return 'script';
} else if (eventName.indexOf('forceGC') > -1) {
return 'gc';
} else {
return 'unknown';
}
}
6 changes: 1 addition & 5 deletions modules/benchpress/src/firefox_extension/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
{
"version": "0.0.1",
"main": "lib/main.js",
"name": "ffperf-addon"
}
{ "version" : "0.0.1", "main" : "lib/main.js", "name" : "ffperf-addon" }
4 changes: 3 additions & 1 deletion modules/benchpress/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {Validator} from './validator';
import {PerflogMetric} from './metric/perflog_metric';
import {MultiMetric} from './metric/multi_metric';
import {ChromeDriverExtension} from './webdriver/chrome_driver_extension';
import {FirefoxDriverExtension} from './webdriver/firefox_driver_extension';
import {IOsDriverExtension} from './webdriver/ios_driver_extension';
import {WebDriverExtension} from './web_driver_extension';
import {SampleDescription} from './sample_description';
Expand Down Expand Up @@ -62,6 +63,7 @@ var _DEFAULT_BINDINGS = [
RegressionSlopeValidator.BINDINGS,
SizeValidator.BINDINGS,
ChromeDriverExtension.BINDINGS,
FirefoxDriverExtension.BINDINGS,
IOsDriverExtension.BINDINGS,
PerflogMetric.BINDINGS,
SampleDescription.BINDINGS,
Expand All @@ -70,7 +72,7 @@ var _DEFAULT_BINDINGS = [

Reporter.bindTo(MultiReporter),
Validator.bindTo(RegressionSlopeValidator),
WebDriverExtension.bindTo([ChromeDriverExtension, IOsDriverExtension]),
WebDriverExtension.bindTo([ChromeDriverExtension, FirefoxDriverExtension, IOsDriverExtension]),
Metric.bindTo(MultiMetric),

bind(Options.CAPABILITIES)
Expand Down
1 change: 1 addition & 0 deletions modules/benchpress/src/web_driver_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class WebDriverAdapter {

waitFor(callback: Function): Promise<any> { throw new BaseException('NYI'); }
executeScript(script: string): Promise<any> { throw new BaseException('NYI'); }
executeAsyncScript(script: string): Promise<any> { throw new BaseException('NYI'); }
capabilities(): Promise<Map<string, any>> { throw new BaseException('NYI'); }
logs(type: string): Promise<List<any>> { throw new BaseException('NYI'); }
}
4 changes: 4 additions & 0 deletions modules/benchpress/src/webdriver/async_webdriver_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class AsyncWebDriverAdapter extends WebDriverAdapter {
return _driver.execute(script, const []);
}

Future executeAsyncScript(String script) {
return _driver.executeAsync(script, const []);
}

Future<Map> capabilities() {
return new Future.value(_driver.capabilities);
}
Expand Down
49 changes: 49 additions & 0 deletions modules/benchpress/src/webdriver/firefox_driver_extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {bind, Binding} from 'angular2/di';
import {isPresent, StringWrapper} from 'angular2/src/facade/lang';
import {WebDriverExtension, PerfLogFeatures} from '../web_driver_extension';
import {WebDriverAdapter} from '../web_driver_adapter';
import {Promise} from 'angular2/src/facade/async';

export class FirefoxDriverExtension extends WebDriverExtension {
static get BINDINGS(): List<Binding> { return _BINDINGS; }

private _profilerStarted: boolean;

constructor(private _driver: WebDriverAdapter) {
super();
this._profilerStarted = false;
}

gc() { return this._driver.executeScript('window.forceGC()'); }

timeBegin(name: string): Promise<any> {
if (!this._profilerStarted) {
this._profilerStarted = true;
this._driver.executeScript('window.startProfiler();');
}
return this._driver.executeScript('window.markStart("' + name + '");');
}

timeEnd(name: string, restartName: string = null): Promise<any> {
var script = 'window.markEnd("' + name + '");';
if (isPresent(restartName)) {
script += 'window.markStart("' + restartName + '");';
}
return this._driver.executeScript(script);
}

readPerfLog(): Promise<any> {
return this._driver.executeAsyncScript('var cb = arguments[0]; window.getProfile(cb);');
}

perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); }

supports(capabilities: StringMap<string, any>): boolean {
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'firefox');
}
}

var _BINDINGS = [
bind(FirefoxDriverExtension)
.toFactory((driver) => new FirefoxDriverExtension(driver), [WebDriverAdapter])
];
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export class SeleniumWebDriverAdapter extends WebDriverAdapter {
return this._convertPromise(this._driver.executeScript(script));
}

executeAsyncScript(script: string): Promise<any> {
return this._convertPromise(this._driver.executeAsyncScript(script));
}

capabilities(): Promise<any> {
return this._convertPromise(
this._driver.getCapabilities().then((capsObject) => capsObject.toJSON()));
Expand Down
13 changes: 7 additions & 6 deletions modules/benchpress/test/firefox_extension/conf.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/// <reference path="../../../angular2/typings/node/node.d.ts" />
require('traceur/bin/traceur-runtime.js');
require('reflect-metadata');
var testHelper = require('../../src/firefox_extension/lib/test_helper.js');

// Where to save profile results (parent folder must exist)
var PROFILE_SAVE_PATH = './perfProfile.json';

exports.config = {
specs: ['spec.js'],
specs: ['spec.js', 'sample_benchmark.js'],

framework: 'jasmine2',

getMultiCapabilities: function() { return testHelper.getFirefoxProfileWithExtension(); },
jasmineNodeOpts: {showColors: true, defaultTimeoutInterval: 1200000},

params: {profileSavePath: testHelper.getAbsolutePath(PROFILE_SAVE_PATH)}
getMultiCapabilities: function() { return testHelper.getFirefoxProfileWithExtension(); }
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
library benchpress.test.firefox_extension.parser_util_spec;

main() {

}
Loading
X Tutup