# HG changeset patch # User Benjamin Smedberg # Date 1279742259 14400 # Node ID dd0018bc27de69e72b7c7e4cded5e1197afbe7d4 Copy the pageloader from CVS and into a bundle format (single chrome.manifest). diff -r 000000000000 -r dd0018bc27de README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Wed Jul 21 15:57:39 2010 -0400 @@ -0,0 +1,81 @@ +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. A +number of output formats can be specified via the -tpformat command +line option, currently 'js', 'text', and 'tinderbox' are supported. + +Sample 'js' format output: + +([({page:"1280x1024-PNG/index.html", value:133, stddev:20.049937655763422}),({page:"bugzilla.mozilla.org/index.html", value:233, stddev:36.66606060105176}),({page:"espn.go.com/index.html", value:117.6, stddev:1.2}),({page:"home.netscape.com/index.html", value:97.8, stddev:47.41898354035017}),]) + +Sample 'text' format output: + +============================================================ + Page mean stdd min max raw + 0 1280x1024-PNG/index.html 133 20 121 297 297,173,122,121,124,125 + 1 bugzilla.mozilla.org/index.html 233 37 192 395 395,273,223,192,198,279 + 2 espn.go.com/index.html 118 1 116 254 254,117,116,119,119,117 + 3 home.netscape.com/index.html 98 47 3 124 3,121,120,124,124,121 +============================================================ + +Sample 'tinderbox' format 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 diff -r 000000000000 -r dd0018bc27de chrome.manifest --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chrome.manifest Wed Jul 21 15:57:39 2010 -0400 @@ -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 diff -r 000000000000 -r dd0018bc27de chrome/pageloader.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chrome/pageloader.js Wed Jul 21 15:57:39 2010 -0400 @@ -0,0 +1,441 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** 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 + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Rob Helmer + * Vladimir Vukicevic + * + * 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 ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +var NUM_CYCLES = 5; + +var pageFilterRegexp = null; +var reportFormat = "js"; +var useBrowser = true; +var winWidth = 1024; +var winHeight = 768; + +var doRenderTest = false; + +var pages; +var pageIndex; +var start_time; +var cycle; +var report; +var renderReport; +var noisy = false; +var timeout = -1; +var timeoutEvent = -1; +var running = false; +var forceCC = true; + +var content; + +var TEST_DOES_OWN_TIMING = 1; + +var browserWindow = null; + +// the io service +var gIOS = null; + +function plInit() { + if (running) { + return; + } + running = true; + + cycle = 0; + + 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.format) reportFormat = args.format; + 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); + 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); + var pageUrls = pages.map(function(p) { return p.url.spec.toString(); }); + report = new Report(pageUrls); + + if (doRenderTest) + renderReport = new Report(pageUrls); + + 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,dialog=no,width=" + winWidth + ",height=" + winHeight, blank); + + // 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(); + setTimeout(plLoadPage, 100); + }, 500); + }; + + browserWindow.addEventListener('load', browserLoadFunc, true); + } else { + window.resizeTo(winWidth, winHeight); + + content = document.getElementById('contentPageloader'); + + setTimeout(plLoadPage, 250); + } + } catch(e) { + dumpLine(e); + plStop(true); + } +} + +function plPageFlags() { + return pages[pageIndex].flags; +} + +// load the current page, start timing +var removeLastAddedListener = null; +function plLoadPage() { + var pageName = pages[pageIndex].url.spec; + + if (removeLastAddedListener) + removeLastAddedListener(); + + 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); + }; + } 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 element. See bug 390263. + content.addEventListener('load', plLoadHandler, true); + removeLastAddedListener = function() { + content.removeEventListener('load', plLoadHandler, true); + }; + } + + if (timeout > 0) { + timeoutEvent = setTimeout('loadFail()', timeout); + } + 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() { + if (pageIndex < pages.length-1) { + pageIndex++; + + 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, 250); + } 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; + report.recordTime(pageIndex, time); + if (noisy) { + dumpLine("Cycle " + (cycle+1) + ": 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; + 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); + } + + // set up the function for content to call + content.contentWindow.wrappedJSObject.tpRecordTime = function (time) { + plRecordTime(time); + setTimeout(plNextPage, 250); + }; +} + +// the onload handler +function plLoadHandler(evt) { + // make sure we pick up the right load event + if (evt.type != 'load' || + evt.originalTarget.defaultView.frameElement) + return; + if (timeout > 0) { + clearTimeout(timeoutEvent); + } + 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(); +} + +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) { + try { + if (force == false) { + pageIndex = 0; + if (cycle < NUM_CYCLES-1) { + cycle++; + setTimeout(plLoadPage, 250); + return; + } + + var formats = reportFormat.split(","); + + if (!renderReport) { + for each (var fmt in formats) + dumpLine(report.getReport(fmt)); + } + else { + dumpLine ("*************** Render report *******************"); + for each (var fmt in formats) + dumpLine(renderReport.getReport(fmt)); + } + } + } catch (e) { + dumpLine(e); + } + + if (content) + content.removeEventListener('load', plLoadHandler, true); + + 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) { + dump(str); + dump("\n"); +} diff -r 000000000000 -r dd0018bc27de chrome/pageloader.xul --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chrome/pageloader.xul Wed Jul 21 15:57:39 2010 -0400 @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + diff -r 000000000000 -r dd0018bc27de chrome/quit.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chrome/quit.js Wed Jul 21 15:57:39 2010 -0400 @@ -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 + * + * 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; +} + diff -r 000000000000 -r dd0018bc27de chrome/report.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chrome/report.js Wed Jul 21 15:57:39 2010 -0400 @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** 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 + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * Rob Helmer + * Vladimir Vukicevic + * + * 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 ***** */ + +// Constructor +function Report(pages) { + this.pages = pages; + this.timeVals = new Array(pages.length); // matrix of times + for (var i = 0; i < this.timeVals.length; ++i) { + this.timeVals[i] = new Array(); + } + this.totalCCTime = 0; + this.showTotalCCTime = false; +} + +// 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; +} + +function compareNumbers(a, b) { + return a - b; +} + +// returns an object with the following properties: +// min : min value of array elements +// max : max value of array elements +// mean : mean value of array elements +// vari : variance computation +// stdd : standard deviation, sqrt(vari) +// indexOfMax : index of max element (the element that is +// removed from the mean computation) +function getArrayStats(ary) { + var r = {}; + r.min = ary[0]; + r.max = ary[0]; + r.indexOfMax = 0; + var sum = 0; + for (var i = 0; i < ary.length; ++i) { + if (ary[i] < r.min) { + r.min = ary[i]; + } else if (ary[i] > r.max) { + r.max = ary[i]; + r.indexOfMax = i; + } + sum = sum + ary[i]; + } + + // median + if (ary.length > 1) { + sorted_ary = ary.concat(); + sorted_ary.sort(compareNumbers); + // remove longest run + sorted_ary.pop(); + if (sorted_ary.length%2) { + r.median = sorted_ary[(sorted_ary.length-1)/2]; + }else{ + var n = Math.floor(sorted_ary.length / 2); + if (n >= sorted_ary.length) + r.median = sorted_ary[n]; + else + r.median = (sorted_ary[n-1] + sorted_ary[n]) / 2; + } + }else{ + r.median = ary[0]; + } + + // ignore max value when computing mean and stddev + if (ary.length > 1) + r.mean = (sum - r.max) / (ary.length - 1); + else + r.mean = ary[0]; + + r.vari = 0; + for (var i = 0; i < ary.length; ++i) { + if (i == r.indexOfMax) + continue; + var d = r.mean - ary[i]; + r.vari = r.vari + d * d; + } + + if (ary.length > 1) { + r.vari = r.vari / (ary.length - 1); + r.stdd = Math.sqrt(r.vari); + } else { + r.vari = 0.0; + r.stdd = 0.0; + } + return r; +} + +function strPad(o, len, left) { + var str = o.toString(); + if (!len) + len = 6; + if (left == null) + left = true; + + if (str.length < len) { + len -= str.length; + while (--len) { + if (left) + str = " " + str; + else + str += " "; + } + } + + str += " "; + return str; +} + +function strPadFixed(n, len, left) { + return strPad(n.toFixed(0), len, left); +} + +Report.prototype.getReport = function(format) { + // avg and avg median are cumulative for all the pages + var avgs = new Array(); + var medians = new Array(); + for (var i = 0; i < this.timeVals.length; ++i) { + avgs[i] = getArrayStats(this.timeVals[i]).mean; + medians[i] = getArrayStats(this.timeVals[i]).median; + } + var avg = getArrayStats(avgs).mean; + var avgmed = getArrayStats(medians).mean; + + var report; + + var prefixLen = findCommonPrefixLength(this.pages); + + if (format == "js") { + // output "simple" js format; + // array of { page: "str", value: 123.4, stddev: 23.3 } objects + report = "(["; + for (var i = 0; i < this.timeVals.length; i++) { + var stats = getArrayStats(this.timeVals[i]); + report += uneval({ page: this.pages[i].substr(prefixLen), value: stats.mean, stddev: stats.stdd}); + report += ","; + } + report += "])"; + } else if (format == "jsfull") { + // output "full" js format, with raw values + } else if (format == "text") { + // output text format suitable for dumping + report = "============================================================\n"; + report += " " + strPad("Page", 40, false) + strPad("mean") + strPad("stdd") + strPad("min") + strPad("max") + "raw" + "\n"; + for (var i = 0; i < this.timeVals.length; i++) { + var stats = getArrayStats(this.timeVals[i]); + report += + strPad(i, 4, true) + + strPad(this.pages[i].substr(prefixLen), 40, false) + + strPadFixed(stats.mean) + + strPadFixed(stats.stdd) + + strPadFixed(stats.min) + + strPadFixed(stats.max) + + this.timeVals[i] + + "\n"; + } + if (this.showTotalCCTime) { + report += "Cycle collection: " + this.totalCCTime + "\n" + } + report += "============================================================\n"; + } else if (format == "tinderbox") { + report = "__start_tp_report\n"; + report += "_x_x_mozilla_page_load,"+avgmed+",NaN,NaN\n"; // max and min are just 0, ignored + report += "_x_x_mozilla_page_load_details,avgmedian|"+avgmed+"|average|"+avg.toFixed(2)+"|minimum|NaN|maximum|NaN|stddev|NaN\n"; + report += "|i|pagename|median|mean|min|max|runs|\n"; + + for (var i = 0; i < this.timeVals.length; i++) { + var r = getArrayStats(this.timeVals[i]); + report += '|'+ + i + ';'+ + this.pages[i].substr(prefixLen) + ';'+ + r.median + ';'+ + r.mean + ';'+ + r.min + ';'+ + r.max + ';'+ + this.timeVals[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 + } else { + report = "Unknown report format"; + } + + return report; +} + +Report.prototype.recordTime = function(pageIndex, ms) { + this.timeVals[pageIndex].push(ms); +} + +Report.prototype.recordCCTime = function(ms) { + this.totalCCTime += ms; + this.showTotalCCTime = true; +} diff -r 000000000000 -r dd0018bc27de components/tp-cmdline.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/tp-cmdline.js Wed Jul 21 15:57:39 2010 -0400 @@ -0,0 +1,191 @@ +/* ***** 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 . + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Christopher A. Aillon + * L. David Baron, Mozilla Corporation (modified for reftest) + * Vladimir Vukicevic, Mozilla Corporation (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.startIndex = cmdLine.handleFlagWithParam("tpstart", false); + args.endIndex = cmdLine.handleFlagWithParam("tpend", false); + args.filter = cmdLine.handleFlagWithParam("tpfilter", false); + args.format = cmdLine.handleFlagWithParam("tpformat", 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.noForceCC = cmdLine.handleFlag("tpnoforcecc", 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 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" + + " -tpstart n Start at index n in the manifest\n" + + " -tpend n End with index n in the manifest\n" + + " -tpformat f1,f2,.. Report format(s) to use\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" + + " -tpnoforcecc Don't force cycle collection between each pageload\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; +}