This commit is contained in:
Nick Hurley 2013-02-27 13:24:20 -08:00
Родитель 066250236c
Коммит 355446c352
10 изменённых файлов: 1475 добавлений и 0 удалений

65
pageloader/README Normal file
Просмотреть файл

@ -0,0 +1,65 @@
Pageload Test Component
=======================
Usage:
./firefox -tp file:///path/to/manifest.txt [-tpargs...]
See ./firefox -help for other arguments.
Manifest file format
====================
Comments in the manifest file start with a #. Each line may be:
* a URL (absolute or relative to the manifest)
This URL is added to the list of tests.
* one or more flags, followed by whitespace, followed by a URL
The only flag supported currently is '%', which indicates that
a test will do its own timing. (See Self-timing Tests below.)
* "include" followed by whitespace, followed by a URL
Parse the given manifest file.
Self-timing Tests
=================
Most timing tests are interested in timing how long it takes the page
to load; that is, from the start of page loading until the 'load'
event is dispatched. By default, this is what the pageloader will
time. However, if a test URL has the % flag, the test is expected to
report its own timing. For this purpose, the pageloader will provide
a function named "tpRecordTime" in the test's global object that it
should call once it has performed whatever timing it wants to do.
The given value will be used as the timing result for this test.
Output format
=============
The result is a dump to stdout via dump() --
browser.dom.window.dump.enabled must be set to true in the profile.
Sample output:
__start_tp_report
_x_x_mozilla_page_load,778.5,NaN,NaN
_x_x_mozilla_page_load_details,avgmedian|778.5|average|766.75|minimum|NaN|maximum|NaN|stddev|NaN|0;file:///c:/proj/mozilla-cvs/perf/tp2/base/www.cnn.com/index.html;778.5;766.75;722;1027;1027;788;777;722;780|...
__end_tp_report
Note that the minimum, maximum, stddev are not calculated; they're
always reported as NaN. (They were the minimum and maximum values of
any sampled value, and the standard deviation across all sampled
values -- not very useful.)
TODO
====
* Command line option to choose whether to run with or without browser chrome. Currently runs without.
* Tinderbox-dropping style output
* better yet would be to teach tinderbox about JSON

Просмотреть файл

@ -0,0 +1,4 @@
content pageloader chrome/
component {8AF052F5-8EFE-4359-8266-E16498A82E8B} components/tp-cmdline.js
contract @mozilla.org/commandlinehandler/general-startup;1?type=tp {8AF052F5-8EFE-4359-8266-E16498A82E8B}
category command-line-handler m-tp @mozilla.org/commandlinehandler/general-startup;1?type=tp

Просмотреть файл

@ -0,0 +1,137 @@
/**
* MozillaFileLogger, a log listener that can write to a local file.
*/
var ipcMode = false; // running in e10s build and need to use IPC?
try {
var ipcsanity = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
ipcsanity.setIntPref("mochitest.ipcmode", 0);
} catch (e) {
ipcMode = true;
}
function contentDispatchEvent(type, data, sync) {
if (typeof(data) === "undefined") {
data = {};
}
var element = document.createEvent("datacontainerevent");
element.initEvent("contentEvent", true, false);
element.setData("sync", sync);
element.setData("type", type);
element.setData("data", JSON.stringify(data));
document.dispatchEvent(element);
}
function contentSyncEvent(type, data) {
contentDispatchEvent(type, data, 1);
}
function contentAsyncEvent(type, data) {
contentDispatchEvent(type, data, 0);
}
try {
if (Cc === undefined) {
var Cc = Components.classes;
var Ci = Components.interfaces;
}
} catch (ex) {} //running in ipcMode-chrome
try {
const FOSTREAM_CID = "@mozilla.org/network/file-output-stream;1";
const LF_CID = "@mozilla.org/file/local;1";
// File status flags. It is a bitwise OR of the following bit flags.
// Only one of the first three flags below may be used.
const PR_READ_ONLY = 0x01; // Open for reading only.
const PR_WRITE_ONLY = 0x02; // Open for writing only.
const PR_READ_WRITE = 0x04; // Open for reading and writing.
// If the file does not exist, the file is created.
// If the file exists, this flag has no effect.
const PR_CREATE_FILE = 0x08;
// The file pointer is set to the end of the file prior to each write.
const PR_APPEND = 0x10;
// If the file exists, its length is truncated to 0.
const PR_TRUNCATE = 0x20;
// If set, each write will wait for both the file data
// and file status to be physically updated.
const PR_SYNC = 0x40;
// If the file does not exist, the file is created. If the file already
// exists, no action and NULL is returned.
const PR_EXCL = 0x80;
} catch (ex) {
// probably not running in the test harness
}
/** Init the file logger with the absolute path to the file.
It will create and append if the file already exists **/
var MozillaFileLogger = {};
MozillaFileLogger.init = function(path) {
if (ipcMode) {
contentAsyncEvent("LoggerInit", {"filename": path});
return;
}
MozillaFileLogger._file = Cc[LF_CID].createInstance(Ci.nsILocalFile);
MozillaFileLogger._file.initWithPath(path);
MozillaFileLogger._foStream = Cc[FOSTREAM_CID].createInstance(Ci.nsIFileOutputStream);
MozillaFileLogger._foStream.init(this._file, PR_WRITE_ONLY | PR_CREATE_FILE | PR_APPEND,
0664, 0);
}
MozillaFileLogger.getLogCallback = function() {
if (ipcMode) {
return function(msg) {
contentAsyncEvent("Logger", {"num": msg.num, "level": msg.level, "info": msg.info.join(' ')});
}
}
return function (msg) {
var data = msg.num + " " + msg.level + " " + msg.info.join(' ') + "\n";
if (MozillaFileLogger._foStream)
MozillaFileLogger._foStream.write(data, data.length);
if (data.indexOf("SimpleTest FINISH") >= 0) {
MozillaFileLogger.close();
}
}
}
// This is only used from chrome space by the reftest harness
MozillaFileLogger.log = function(msg) {
if (MozillaFileLogger._foStream)
MozillaFileLogger._foStream.write(msg, msg.length);
}
MozillaFileLogger.close = function() {
if (ipcMode) {
contentAsyncEvent("LoggerClose");
return;
}
if(MozillaFileLogger._foStream)
MozillaFileLogger._foStream.close();
MozillaFileLogger._foStream = null;
MozillaFileLogger._file = null;
}
if (ipcMode == false) {
try {
var prefs = Components.classes['@mozilla.org/preferences-service;1']
.getService(Components.interfaces.nsIPrefBranch2);
var filename = prefs.getCharPref('talos.logfile');
MozillaFileLogger.init(filename);
} catch (ex) {} //pref does not exist, return empty string
}

Просмотреть файл

@ -0,0 +1,95 @@
var gChildProcess = true;
var gMemCallback = null;
/*
* Initialize memory collector. Determine if we have a child process.
*/
function initializeMemoryCollector(callback, args) {
gMemCallback = function() { return callback(args); };
var os = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
os.addObserver(function () {
var os = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
memTimer.cancel();
memTimer = null;
os.removeObserver(arguments.callee, "child-memory-reporter-update", false);
os.addObserver(collectAndReport, "child-memory-reporter-update", false);
gMemCallback();
}, "child-memory-reporter-update", false);
/*
* Assume we have a child process, but if timer fires before we call the observer
* we will assume there is no child process.
*/
var event = {
notify: function(timer) {
memTimer = null;
gChildProcess = false;
gMemCallback();
}
}
memTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
memTimer.initWithCallback(event, 10000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
os.notifyObservers(null, "child-memory-reporter-request", null);
}
/*
* Collect memory from all processes and callback when done collecting.
*/
function collectMemory(callback, args) {
gMemCallback = function() { return callback(args); };
if (gChildProcess) {
var os = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, "child-memory-reporter-request", null);
} else {
collectAndReport(null, null, null);
}
}
function collectAndReport(aSubject, aTopic, aData) {
dumpLine(collectRSS());
gMemCallback();
}
function collectRSS() {
var mgr = Components.classes["@mozilla.org/memory-reporter-manager;1"].
getService(Components.interfaces.nsIMemoryReporterManager);
var e = mgr.enumerateReporters();
text = "";
while (e.hasMoreElements()) {
var reporter = e.getNext().QueryInterface(Components.interfaces.nsIMemoryReporter);
if (reporter.path == 'resident') {
procName = reporter.process;
if (procName == '')
procName = "Main";
//For content process it is in the format "Content (<PID>)", we just want Content
procName = procName.split(' ')[0];
text += "RSS: " + procName + ": " + reporter.amount + "\n";
}
}
return text;
}
/*
* Cleanup and stop memory collector.
*/
function stopMemCollector() {
if (gChildProcess) {
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.removeObserver(collectAndReport, "child-memory-reporter-update");
}
}

Просмотреть файл

@ -0,0 +1,696 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
try {
if (Cc === undefined) {
var Cc = Components.classes;
var Ci = Components.interfaces;
}
} catch (ex) {}
var NUM_CYCLES = 5;
var numPageCycles = 1;
var pageFilterRegexp = null;
var useBrowser = true;
var winWidth = 1024;
var winHeight = 768;
var doRenderTest = false;
var pages;
var pageIndex;
var start_time;
var cycle;
var pageCycle;
var report;
var noisy = false;
var timeout = -1;
var delay = 250;
var timeoutEvent = -1;
var running = false;
var forceCC = true;
var reportRSS = true;
var useMozAfterPaint = false;
var gPaintWindow = window;
var gPaintListener = false;
//when TEST_DOES_OWN_TIMING, we need to store the time from the page as MozAfterPaint can be slower than pageload
var gTime = -1;
var gStartTime = -1;
var gReference = -1;
var content;
var TEST_DOES_OWN_TIMING = 1;
var browserWindow = null;
var recordedName = null;
var pageUrls;
// the io service
var gIOS = null;
function plInit() {
if (running) {
return;
}
running = true;
cycle = 0;
pageCycle = 1;
try {
var args = window.arguments[0].wrappedJSObject;
var manifestURI = args.manifest;
var startIndex = 0;
var endIndex = -1;
if (args.startIndex) startIndex = parseInt(args.startIndex);
if (args.endIndex) endIndex = parseInt(args.endIndex);
if (args.numCycles) NUM_CYCLES = parseInt(args.numCycles);
if (args.numPageCycles) numPageCycles = parseInt(args.numPageCycles);
if (args.width) winWidth = parseInt(args.width);
if (args.height) winHeight = parseInt(args.height);
if (args.filter) pageFilterRegexp = new RegExp(args.filter);
if (args.noisy) noisy = true;
if (args.timeout) timeout = parseInt(args.timeout);
if (args.delay) delay = parseInt(args.delay);
if (args.mozafterpaint) useMozAfterPaint = true;
if (args.rss) reportRSS = true;
forceCC = !args.noForceCC;
doRenderTest = args.doRender;
if (forceCC &&
!window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.garbageCollect) {
forceCC = false;
}
gIOS = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
if (args.offline)
gIOS.offline = true;
var fileURI = gIOS.newURI(manifestURI, null, null);
pages = plLoadURLsFromURI(fileURI);
if (!pages) {
dumpLine('tp: could not load URLs, quitting');
plStop(true);
}
if (pages.length == 0) {
dumpLine('tp: no pages to test, quitting');
plStop(true);
}
if (startIndex < 0)
startIndex = 0;
if (endIndex == -1 || endIndex >= pages.length)
endIndex = pages.length-1;
if (startIndex > endIndex) {
dumpLine("tp: error: startIndex >= endIndex");
plStop(true);
}
pages = pages.slice(startIndex,endIndex+1);
pageUrls = pages.map(function(p) { return p.url.spec.toString(); });
report = new Report();
if (doRenderTest)
renderReport = new Report();
pageIndex = 0;
if (args.useBrowserChrome) {
var wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Ci.nsIWindowWatcher);
var blank = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
blank.data = "about:blank";
browserWindow = wwatch.openWindow
(null, "chrome://browser/content/", "_blank",
"chrome,all,dialog=no,width=" + winWidth + ",height=" + winHeight, blank);
gPaintWindow = browserWindow;
// get our window out of the way
window.resizeTo(10,10);
var browserLoadFunc = function (ev) {
browserWindow.removeEventListener('load', browserLoadFunc, true);
// do this half a second after load, because we need to be
// able to resize the window and not have it get clobbered
// by the persisted values
setTimeout(function () {
browserWindow.resizeTo(winWidth, winHeight);
browserWindow.moveTo(0, 0);
browserWindow.focus();
content = browserWindow.getBrowser();
// Load the frame script for e10s / IPC message support
if (content.getAttribute("remote") == "true") {
let contentScript = "data:,function _contentLoadHandler(e) { " +
" if (e.originalTarget.defaultView == content) { " +
" content.wrappedJSObject.tpRecordTime = function(t, s) { sendAsyncMessage('PageLoader:RecordTime', { time: t, startTime: s }); }; ";
if (useMozAfterPaint) {
contentScript += "" +
"function _contentPaintHandler() { " +
" var utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils); " +
" if (utils.isMozAfterPaintPending) { " +
" addEventListener('MozAfterPaint', function(e) { " +
" removeEventListener('MozAfterPaint', arguments.callee, true); " +
" sendAsyncMessage('PageLoader:MozAfterPaint', {}); " +
" }, true); " +
" } else { " +
" sendAsyncMessage('PageLoader:MozAfterPaint', {}); " +
" } " +
"}; " +
"content.wrappedJSObject.setTimeout(_contentPaintHandler, 0); ";
} else {
contentScript += " sendAsyncMessage('PageLoader:Load', {}); ";
}
contentScript += "" +
" }" +
"} " +
"addEventListener('load', _contentLoadHandler, true); ";
content.messageManager.loadFrameScript(contentScript, false);
}
if (reportRSS) {
initializeMemoryCollector(plLoadPage, 100);
} else {
setTimeout(plLoadPage, 100);
}
}, 500);
};
browserWindow.addEventListener('load', browserLoadFunc, true);
} else {
gPaintWindow = window;
window.resizeTo(winWidth, winHeight);
content = document.getElementById('contentPageloader');
if (reportRSS) {
initializeMemoryCollector(plLoadPage, delay);
} else {
setTimeout(plLoadPage, delay);
}
}
} catch(e) {
dumpLine(e);
plStop(true);
}
}
function plPageFlags() {
return pages[pageIndex].flags;
}
// load the current page, start timing
var removeLastAddedListener = null;
var removeLastAddedMsgListener = null;
function plLoadPage() {
var pageName = pages[pageIndex].url.spec;
if (removeLastAddedListener)
removeLastAddedListener();
if (removeLastAddedMsgListener)
removeLastAddedMsgListener();
if (plPageFlags() & TEST_DOES_OWN_TIMING) {
// if the page does its own timing, use a capturing handler
// to make sure that we can set up the function for content to call
content.addEventListener('load', plLoadHandlerCapturing, true);
removeLastAddedListener = function() {
content.removeEventListener('load', plLoadHandlerCapturing, true);
if (useMozAfterPaint) {
content.removeEventListener("MozAfterPaint", plPaintedCapturing, true);
gPaintListener = false;
}
};
} else {
// if the page doesn't do its own timing, use a bubbling handler
// to make sure that we're called after the page's own onload() handling
// XXX we use a capturing event here too -- load events don't bubble up
// to the <browser> element. See bug 390263.
content.addEventListener('load', plLoadHandler, true);
removeLastAddedListener = function() {
content.removeEventListener('load', plLoadHandler, true);
if (useMozAfterPaint) {
gPaintWindow.removeEventListener("MozAfterPaint", plPainted, true);
gPaintListener = false;
}
};
}
// If the test browser is remote (e10s / IPC) we need to use messages to watch for page load
if (content.getAttribute("remote") == "true") {
content.messageManager.addMessageListener('PageLoader:Load', plLoadHandlerMessage);
content.messageManager.addMessageListener('PageLoader:RecordTime', plRecordTimeMessage);
if (useMozAfterPaint)
content.messageManager.addMessageListener('PageLoader:MozAfterPaint', plPaintHandler);
removeLastAddedMsgListener = function() {
content.messageManager.removeMessageListener('PageLoader:Load', plLoadHandlerMessage);
content.messageManager.removeMessageListener('PageLoader:RecordTime', plRecordTimeMessage);
if (useMozAfterPaint)
content.messageManager.removeMessageListener('PageLoader:MozAfterPaint', plPaintHandler);
};
}
if (timeout > 0) {
timeoutEvent = setTimeout('loadFail()', timeout);
}
if (reportRSS) {
collectMemory(startAndLoadURI, pageName);
} else {
startAndLoadURI(pageName);
}
}
function startAndLoadURI(pageName) {
start_time = Date.now();
content.loadURI(pageName);
}
function loadFail() {
var pageName = pages[pageIndex].url.spec;
dumpLine("__FAILTimeout exceeded on " + pageName + "__FAIL")
plStop(true);
}
function plNextPage() {
var doNextPage = false;
if (pageCycle < numPageCycles) {
pageCycle++;
doNextPage = true;
} else if (pageIndex < pages.length-1) {
pageIndex++;
recordedName = null;
pageCycle = 1;
doNextPage = true;
}
if (doNextPage == true) {
if (forceCC) {
var tccstart = new Date();
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.garbageCollect();
var tccend = new Date();
report.recordCCTime(tccend - tccstart);
}
setTimeout(plLoadPage, delay);
} else {
plStop(false);
}
}
function plRecordTime(time) {
var pageName = pages[pageIndex].url.spec;
var i = pageIndex
if (i < pages.length-1) {
i++;
} else {
i = 0;
}
var nextName = pages[i].url.spec;
if (!recordedName) {
recordedName = pageUrls[pageIndex];
}
if (typeof(time) == "string") {
var times = time.split(',');
var names = recordedName.split(',');
for (var t = 0; t < times.length; t++) {
if (names.length == 1) {
report.recordTime(names, times[t]);
} else {
report.recordTime(names[t], times[t]);
}
}
} else {
report.recordTime(recordedName, time);
}
if (noisy) {
dumpLine("Cycle " + (cycle+1) + "(" + pageCycle + ")" + ": loaded " + pageName + " (next: " + nextName + ")");
}
}
function plLoadHandlerCapturing(evt) {
// make sure we pick up the right load event
if (evt.type != 'load' ||
evt.originalTarget.defaultView.frameElement)
return;
//set the tpRecordTime function (called from test pages we load to store a global time.
content.contentWindow.wrappedJSObject.tpRecordTime = function (time, startTime, testName) {
gTime = time;
gStartTime = startTime;
recordedName = testName;
setTimeout(plWaitForPaintingCapturing, 0);
}
content.removeEventListener('load', plLoadHandlerCapturing, true);
setTimeout(plWaitForPaintingCapturing, 0);
}
function plWaitForPaintingCapturing() {
if (gPaintListener)
return;
var utils = gPaintWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
if (utils.isMozAfterPaintPending && useMozAfterPaint) {
if (gPaintListener == false)
gPaintWindow.addEventListener("MozAfterPaint", plPaintedCapturing, true);
gPaintListener = true;
return;
}
_loadHandlerCapturing();
}
function plPaintedCapturing() {
gPaintWindow.removeEventListener("MozAfterPaint", plPaintedCapturing, true);
gPaintListener = false;
_loadHandlerCapturing();
}
function _loadHandlerCapturing() {
if (timeout > 0) {
clearTimeout(timeoutEvent);
}
if (!(plPageFlags() & TEST_DOES_OWN_TIMING)) {
dumpLine("tp: Capturing onload handler used with page that doesn't do its own timing?");
plStop(true);
}
if (useMozAfterPaint) {
if (gStartTime != null && gStartTime >= 0) {
gTime = (new Date()) - gStartTime;
gStartTime = -1;
}
}
// set up the function for content to call
if (gTime != -1) {
plRecordTime(gTime);
gTime = -1;
recordedName = null;
setTimeout(plNextPage, delay);
};
}
// the onload handler
function plLoadHandler(evt) {
// make sure we pick up the right load event
if (evt.type != 'load' ||
evt.originalTarget.defaultView.frameElement)
return;
content.removeEventListener('load', plLoadHandler, true);
setTimeout(waitForPainted, 0);
}
// This is called after we have received a load event, now we wait for painted
function waitForPainted() {
var utils = gPaintWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
if (!utils.isMozAfterPaintPending || !useMozAfterPaint) {
_loadHandler();
return;
}
if (gPaintListener == false)
gPaintWindow.addEventListener("MozAfterPaint", plPainted, true);
gPaintListener = true;
}
function plPainted() {
gPaintWindow.removeEventListener("MozAfterPaint", plPainted, true);
gPaintListener = false;
_loadHandler();
}
function _loadHandler() {
if (timeout > 0) {
clearTimeout(timeoutEvent);
}
var docElem;
if (browserWindow)
docElem = browserWindow.frames["content"].document.documentElement;
else
docElem = content.contentDocument.documentElement;
var width;
if ("getBoundingClientRect" in docElem) {
width = docElem.getBoundingClientRect().width;
} else if ("offsetWidth" in docElem) {
width = docElem.offsetWidth;
}
var end_time = Date.now();
var time = (end_time - start_time);
// does this page want to do its own timing?
// if so, we shouldn't be here
if (plPageFlags() & TEST_DOES_OWN_TIMING) {
dumpLine("tp: Bubbling onload handler used with page that does its own timing?");
plStop(true);
}
plRecordTime(time);
if (doRenderTest)
runRenderTest();
plNextPage();
}
// the onload handler used for remote (e10s) browser
function plLoadHandlerMessage(message) {
_loadHandlerMessage();
}
// the mozafterpaint handler for remote (e10s) browser
function plPaintHandler(message) {
_loadHandlerMessage();
}
// the core handler for remote (e10s) browser
function _loadHandlerMessage() {
if (timeout > 0) {
clearTimeout(timeoutEvent);
}
var time = -1;
// does this page want to do its own timing?
if ((plPageFlags() & TEST_DOES_OWN_TIMING)) {
if (typeof(gStartTime) != "number")
gStartTime = Date.parse(gStartTime);
if (gTime >= 0) {
if (useMozAfterPaint && gStartTime >= 0) {
gTime = Date.now() - gStartTime;
gStartTime = -1;
} else if (useMozAfterPaint) {
gTime = -1;
}
time = gTime;
gTime = -1;
}
} else {
var end_time = Date.now();
time = (end_time - start_time);
}
if (time >= 0) {
plRecordTime(time);
if (doRenderTest)
runRenderTest();
plNextPage();
}
}
// the record time handler used for remote (e10s) browser
function plRecordTimeMessage(message) {
gTime = message.json.time;
if (useMozAfterPaint) {
gStartTime = message.json.startTime;
}
_loadHandlerMessage();
}
function runRenderTest() {
const redrawsPerSample = 500;
if (!Ci.nsIDOMWindowUtils)
return;
var win;
if (browserWindow)
win = content.contentWindow;
else
win = window;
var wu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
var start = Date.now();
for (var j = 0; j < redrawsPerSample; j++)
wu.redraw();
var end = Date.now();
renderReport.recordTime(pageIndex, end - start);
}
function plStop(force) {
if (reportRSS) {
collectMemory(plStopAll, force);
} else {
plStopAll(force);
}
}
function plStopAll(force) {
try {
if (force == false) {
pageIndex = 0;
pageCycle = 1;
if (cycle < NUM_CYCLES-1) {
cycle++;
recordedName = null;
setTimeout(plLoadPage, delay);
return;
}
/* output report */
dumpLine(report.getReport());
}
} catch (e) {
dumpLine(e);
}
if (reportRSS) {
stopMemCollector();
}
if (content) {
content.removeEventListener('load', plLoadHandlerCapturing, true);
content.removeEventListener('load', plLoadHandler, true);
if (useMozAfterPaint)
content.removeEventListener("MozAfterPaint", plPaintedCapturing, true);
content.removeEventListener("MozAfterPaint", plPainted, true);
if (content.getAttribute("remote") == "true") {
content.messageManager.removeMessageListener('PageLoader:Load', plLoadHandlerMessage);
content.messageManager.removeMessageListener('PageLoader:RecordTime', plRecordTimeMessage);
if (useMozAfterPaint)
content.messageManager.removeMessageListener('PageLoader:MozAfterPaint', plPaintHandler);
content.messageManager.loadFrameScript("data:,removeEventListener('load', _contentLoadHandler, true);", false);
}
}
if (MozillaFileLogger)
MozillaFileLogger.close();
goQuitApplication();
}
/* Returns array */
function plLoadURLsFromURI(manifestUri) {
var fstream = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
var uriFile = manifestUri.QueryInterface(Ci.nsIFileURL);
fstream.init(uriFile.file, -1, 0, 0);
var lstream = fstream.QueryInterface(Ci.nsILineInputStream);
var d = [];
var lineNo = 0;
var line = {value:null};
var more;
do {
lineNo++;
more = lstream.readLine(line);
var s = line.value;
// strip comments
s = s.replace(/#.*/, '');
// strip leading and trailing whitespace
s = s.replace(/^\s*/, '').replace(/\s*$/, '');
if (!s)
continue;
var flags = 0;
var urlspec = s;
// split on whitespace, and figure out if we have any flags
var items = s.split(/\s+/);
if (items[0] == "include") {
if (items.length != 2) {
dumpLine("tp: Error on line " + lineNo + " in " + manifestUri.spec + ": include must be followed by the manifest to include!");
return null;
}
var subManifest = gIOS.newURI(items[1], null, manifestUri);
if (subManifest == null) {
dumpLine("tp: invalid URI on line " + manifestUri.spec + ":" + lineNo + " : '" + line.value + "'");
return null;
}
var subItems = plLoadURLsFromURI(subManifest);
if (subItems == null)
return null;
d = d.concat(subItems);
} else {
if (items.length == 2) {
if (items[0].indexOf("%") != -1)
flags |= TEST_DOES_OWN_TIMING;
urlspec = items[1];
} else if (items.length != 1) {
dumpLine("tp: Error on line " + lineNo + " in " + manifestUri.spec + ": whitespace must be %-escaped!");
return null;
}
var url = gIOS.newURI(urlspec, null, manifestUri);
if (pageFilterRegexp && !pageFilterRegexp.test(url.spec))
continue;
d.push({ url: url,
flags: flags });
}
} while (more);
return d;
}
function dumpLine(str) {
if (MozillaFileLogger)
MozillaFileLogger.log(str + "\n");
dump(str);
dump("\n");
}

Просмотреть файл

@ -0,0 +1,57 @@
<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is tp.
-
- The Initial Developer of the Original Code is the Mozilla Corporation.
- Portions created by the Initial Developer are Copyright (C) 2007
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Rob Helmer <rhelmer@mozilla.com>
- Vladimir Vukicevic <vladimir@mozilla.com>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="pageloader"
screenX="0" screenY="0"
onload="plInit()">
<script type="application/x-javascript"
src="chrome://global/content/globalOverlay.js"/>
<script type="application/x-javascript" src="MozillaFileLogger.js"></script>
<script type="application/x-javascript" src="report.js"></script>
<script type="application/x-javascript" src="pageloader.js"></script>
<script type="application/x-javascript" src="quit.js"></script>
<script type="application/x-javascript" src="memory.js"></script>
<browser id="contentPageloader" src="about:blank"
type="content" flex="1"/>
</window>

110
pageloader/chrome/quit.js Normal file
Просмотреть файл

@ -0,0 +1,110 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is The Original Code is Mozilla Automated Testing Code
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Bob Clary <bob@bclary.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
From mozilla/toolkit/content
These files did not have a license
*/
function canQuitApplication()
{
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
if (!os)
{
return true;
}
try
{
var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
.createInstance(Components.interfaces.nsISupportsPRBool);
os.notifyObservers(cancelQuit, "quit-application-requested", null);
// Something aborted the quit process.
if (cancelQuit.data)
{
return false;
}
}
catch (ex)
{
}
return true;
}
function goQuitApplication()
{
if (!canQuitApplication())
{
return false;
}
const kAppStartup = '@mozilla.org/toolkit/app-startup;1';
const kAppShell = '@mozilla.org/appshell/appShellService;1';
var appService;
var forceQuit;
if (kAppStartup in Components.classes)
{
appService = Components.classes[kAppStartup].
getService(Components.interfaces.nsIAppStartup);
forceQuit = Components.interfaces.nsIAppStartup.eForceQuit;
}
else if (kAppShell in Components.classes)
{
appService = Components.classes[kAppShell].
getService(Components.interfaces.nsIAppShellService);
forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
}
else
{
throw 'goQuitApplication: no AppStartup/appShell';
}
try
{
appService.quit(forceQuit);
}
catch(ex)
{
throw('goQuitApplication: ' + ex);
}
return true;
}

Просмотреть файл

@ -0,0 +1,94 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// given an array of strings, finds the longest common prefix
function findCommonPrefixLength(strs) {
if (strs.length < 2)
return 0;
var len = 0;
do {
var newlen = len + 1;
var newprefix = null;
var failed = false;
for (var i = 0; i < strs.length; i++) {
if (newlen > strs[i].length) {
failed = true;
break;
}
var s = strs[i].substr(0, newlen);
if (newprefix == null) {
newprefix = s;
} else if (newprefix != s) {
failed = true;
break;
}
}
if (failed)
break;
len++;
} while (true);
return len;
}
// Constructor
function Report() {
this.timeVals = {};
this.totalCCTime = 0;
this.showTotalCCTime = false;
}
Report.prototype.pageNames = function() {
var retval = new Array();
for (var page in this.timeVals) {
retval.push(page);
}
return retval;
}
Report.prototype.getReport = function() {
var report;
var pages = this.pageNames();
var prefixLen = findCommonPrefixLength(pages);
report = "__start_tp_report\n";
report += "_x_x_mozilla_page_load\n";
report += "_x_x_mozilla_page_load_details\n";
report += "|i|pagename|runs|\n";
for (var i=0; i < pages.length; i++) {
report += '|'+
i + ';'+
pages[i].substr(prefixLen) + ';'+
this.timeVals[pages[i]].join(";") +
"\n";
}
report += "__end_tp_report\n";
if (this.showTotalCCTime) {
report += "__start_cc_report\n";
report += "_x_x_mozilla_cycle_collect," + this.totalCCTime + "\n";
report += "__end_cc_report\n";
}
var now = (new Date()).getTime();
report += "__startTimestamp" + now + "__endTimestamp\n"; //timestamp for determning shutdown time, used by talos
return report;
}
Report.prototype.recordTime = function(pageName, ms) {
if (this.timeVals[pageName] == undefined) {
this.timeVals[pageName] = new Array();
}
this.timeVals[pageName].push(ms);
}
Report.prototype.recordCCTime = function(ms) {
this.totalCCTime += ms;
this.showTotalCCTime = true;
}

Просмотреть файл

@ -0,0 +1,197 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is DOM Inspector.
*
* The Initial Developer of the Original Code is
* Christopher A. Aillon <christopher@aillon.com>.
* Portions created by the Initial Developer are Copyright (C) 2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Christopher A. Aillon <christopher@aillon.com>
* L. David Baron, Mozilla Corporation <dbaron@dbaron.org> (modified for reftest)
* Vladimir Vukicevic, Mozilla Corporation <dbaron@dbaron.org> (modified for tp)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// This only implements nsICommandLineHandler, since it needs
// to handle multiple arguments.
const TP_CMDLINE_CONTRACTID = "@mozilla.org/commandlinehandler/general-startup;1?type=tp";
const TP_CMDLINE_CLSID = Components.ID('{8AF052F5-8EFE-4359-8266-E16498A82E8B}');
const CATMAN_CONTRACTID = "@mozilla.org/categorymanager;1";
const nsISupports = Components.interfaces.nsISupports;
const nsICategoryManager = Components.interfaces.nsICategoryManager;
const nsICommandLine = Components.interfaces.nsICommandLine;
const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler;
const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
const nsISupportsString = Components.interfaces.nsISupportsString;
const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;
function PageLoaderCmdLineHandler() {}
PageLoaderCmdLineHandler.prototype =
{
/* nsISupports */
QueryInterface : function handler_QI(iid) {
if (iid.equals(nsISupports))
return this;
if (nsICommandLineHandler && iid.equals(nsICommandLineHandler))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
/* nsICommandLineHandler */
handle : function handler_handle(cmdLine) {
var args = {};
try {
var uristr = cmdLine.handleFlagWithParam("tp", false);
if (uristr == null)
return;
try {
args.manifest = cmdLine.resolveURI(uristr).spec;
} catch (e) {
return;
}
args.numCycles = cmdLine.handleFlagWithParam("tpcycles", false);
args.numPageCycles = cmdLine.handleFlagWithParam("tppagecycles", false);
args.startIndex = cmdLine.handleFlagWithParam("tpstart", false);
args.endIndex = cmdLine.handleFlagWithParam("tpend", false);
args.filter = cmdLine.handleFlagWithParam("tpfilter", false);
args.useBrowserChrome = cmdLine.handleFlag("tpchrome", false);
args.doRender = cmdLine.handleFlag("tprender", false);
args.width = cmdLine.handleFlagWithParam("tpwidth", false);
args.height = cmdLine.handleFlagWithParam("tpheight", false);
args.offline = cmdLine.handleFlag("tpoffline", false);
args.noisy = cmdLine.handleFlag("tpnoisy", false);
args.timeout = cmdLine.handleFlagWithParam("tptimeout", false);
args.delay = cmdLine.handleFlagWithParam("tpdelay", false);
args.noForceCC = cmdLine.handleFlag("tpnoforcecc", false);
args.mozafterpaint = cmdLine.handleFlag("tpmozafterpaint", false);
args.rss = cmdLine.handleFlag("rss", false);
}
catch (e) {
return;
}
// get our data through xpconnect
args.wrappedJSObject = args;
var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(nsIWindowWatcher);
wwatch.openWindow(null, "chrome://pageloader/content/pageloader.xul", "_blank",
"chrome,dialog=no,all", args);
cmdLine.preventDefault = true;
},
helpInfo :
" -tp <file> Run pageload perf tests on given manifest\n" +
" -tpfilter str Only include pages from manifest that contain str (regexp)\n" +
" -tpcycles n Loop through pages n times\n" +
" -tppagecycles n Loop through each page n times before going onto the next page\n" +
" -tpstart n Start at index n in the manifest\n" +
" -tpend n End with index n in the manifest\n" +
" -tpchrome Test with normal browser chrome\n" +
" -tprender Run render-only benchmark for each page\n" +
" -tpwidth width Width of window\n" +
" -tpheight height Height of window\n" +
" -tpoffline Force offline mode\n" +
" -tpnoisy Dump the name of the last loaded page to console\n" +
" -tptimeout Max amount of time given for a page to load, quit if exceeded\n" +
" -tpdelay Amount of time to wait between each pageload\n" +
" -tpnoforcecc Don't force cycle collection between each pageload\n" +
" -tpmozafterpaint Measure Time after recieving MozAfterPaint event instead of load event\n" +
" -rss Dump RSS after each page is loaded\n"
};
var PageLoaderCmdLineFactory =
{
createInstance : function(outer, iid)
{
if (outer != null) {
throw Components.results.NS_ERROR_NO_AGGREGATION;
}
return new PageLoaderCmdLineHandler().QueryInterface(iid);
}
};
function NSGetFactory(cid) {
if (!cid.equals(TP_CMDLINE_CLSID))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
return PageLoaderCmdLineFactory;
}
var PageLoaderCmdLineModule =
{
registerSelf : function(compMgr, fileSpec, location, type)
{
compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
compMgr.registerFactoryLocation(TP_CMDLINE_CLSID,
"PageLoader CommandLine Service",
TP_CMDLINE_CONTRACTID,
fileSpec,
location,
type);
var catman = Components.classes[CATMAN_CONTRACTID].getService(nsICategoryManager);
catman.addCategoryEntry("command-line-handler",
"m-tp",
TP_CMDLINE_CONTRACTID, true, true);
},
unregisterSelf : function(compMgr, fileSpec, location)
{
compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
compMgr.unregisterFactoryLocation(TP_CMDLINE_CLSID, fileSpec);
catman = Components.classes[CATMAN_CONTRACTID].getService(nsICategoryManager);
catman.deleteCategoryEntry("command-line-handler",
"m-tp", true);
},
getClassObject : function(compMgr, cid, iid)
{
return NSGetFactory(cid);
},
canUnload : function(compMgr)
{
return true;
}
};
function NSGetModule(compMgr, fileSpec) {
return PageLoaderCmdLineModule;
}

20
pageloader/install.rdf Normal file
Просмотреть файл

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>pageloader@mozilla.org</em:id>
<em:version>1.0</em:version>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>2.0b3pre</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>PageLoader extension</em:name>
<em:description>Cycles through pages and measures load times</em:description>
<em:creator>Vladimir Vukicevic</em:creator>
</Description>
</RDF>