diff --git a/mozilla/layout/tools/pageloader/README b/mozilla/layout/tools/pageloader/README index e72bed5c0dc..3a89372869b 100644 --- a/mozilla/layout/tools/pageloader/README +++ b/mozilla/layout/tools/pageloader/README @@ -7,11 +7,39 @@ Usage: See ./firefox -help for other arguments. -The manifest file should contain a list of URLs or URL fragments, one -per line. Empty lines or lines starting with # are ignored. If URL -fragments are specified, then -tpprefix must be used to give a prefix -to prepend to each line in the manifest to turn it into a complete -URL. + +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 diff --git a/mozilla/layout/tools/pageloader/pageloader.js b/mozilla/layout/tools/pageloader/pageloader.js index 0ca0c1b5377..f4ae3c4d142 100644 --- a/mozilla/layout/tools/pageloader/pageloader.js +++ b/mozilla/layout/tools/pageloader/pageloader.js @@ -47,13 +47,11 @@ var reportFormat = "js"; var useBrowser = true; var winWidth = 1024; var winHeight = 768; -var urlPrefix = null; var doRenderTest = false; var pages; var pageIndex; -var results; var start_time; var cycle; var report; @@ -62,8 +60,13 @@ var running = false; var content; +var TEST_DOES_OWN_TIMING = 1; + var browserWindow = null; +// the io service +var gIOS = null; + function plInit() { if (running) { return; @@ -71,7 +74,6 @@ function plInit() { running = true; cycle = 0; - results = {}; try { var args = window.arguments[0].wrappedJSObject; @@ -86,14 +88,13 @@ function plInit() { if (args.width) winWidth = parseInt(args.width); if (args.height) winHeight = parseInt(args.height); if (args.filter) pageFilterRegexp = new RegExp(args.filter); - if (args.prefix) urlPrefix = args.prefix; doRenderTest = args.doRender; - var ios = Cc["@mozilla.org/network/io-service;1"] + gIOS = Cc["@mozilla.org/network/io-service;1"] .getService(Ci.nsIIOService); if (args.offline) - ios.offline = true; - var fileURI = ios.newURI(manifestURI, null, null); + gIOS.offline = true; + var fileURI = gIOS.newURI(manifestURI, null, null); pages = plLoadURLsFromURI(fileURI); if (!pages) { @@ -116,18 +117,19 @@ function plInit() { } pages = pages.slice(startIndex,endIndex+1); - report = new Report(pages); + var pageUrls = pages.map(function(p) { return p.url.spec.toString(); }); + report = new Report(pageUrls); if (doRenderTest) - renderReport = new Report(pages); + renderReport = new Report(pageUrls); pageIndex = 0; if (args.useBrowserChrome) { var wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Ci.nsIWindowWatcher); + .getService(Ci.nsIWindowWatcher); var blank = Cc["@mozilla.org/supports-string;1"] - .createInstance(Ci.nsISupportsString); + .createInstance(Ci.nsISupportsString); blank.data = "about:blank"; browserWindow = wwatch.openWindow (null, "chrome://browser/content/", "_blank", @@ -158,7 +160,6 @@ function plInit() { window.resizeTo(winWidth, winHeight); content = document.getElementById('contentPageloader'); - content.addEventListener('load', plLoadHandler, true); setTimeout(plLoadPage, 0); } @@ -168,11 +169,76 @@ function plInit() { } } -function plLoadPage() { - start_time = Date.now(); - content.loadURI(pages[pageIndex]); +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 capturig 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); + }; + } + + start_time = Date.now(); + content.loadURI(pageName); +} + +function plNextPage() { + if (pageIndex < pages.length-1) { + pageIndex++; + + setTimeout(plLoadPage, 0); + } else { + plStop(false); + } +} + +function plRecordTime(time) { + var pageName = pages[pageIndex].url.spec; + report.recordTime(pageIndex, time); +} + +function plLoadHandlerCapturing(evt) { + // make sure we pick up the right load event + if (evt.type != 'load' || + (!evt.originalTarget instanceof Ci.nsIDOMHTMLDocument || + evt.originalTarget.defaultView.frameElement)) + return; + + 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, 0); + }; +} + +// the onload handler function plLoadHandler(evt) { // make sure we pick up the right load event if (evt.type != 'load' || @@ -183,25 +249,23 @@ function plLoadHandler(evt) { var end_time = Date.now(); var time = (end_time - start_time); - var pageName = pages[pageIndex]; + // 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); + } - results[pageName] = time; - report.recordTime(pageIndex, time); + plRecordTime(time); if (doRenderTest) runRenderTest(); - if (pageIndex < pages.length-1) { - pageIndex++; - setTimeout(plLoadPage, 0); - } else { - plStop(false); - } + plNextPage(); } function runRenderTest() { - const redrawsPerSample = 5; - const renderCycles = 10; + const redrawsPerSample = 500; if (!Ci.nsIDOMWindowUtils) return; @@ -214,36 +278,33 @@ function runRenderTest() { win = window; var wu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - for (var i = 0; i < renderCycles; i++) { - var start = Date.now(); - for (var j = 0; j < redrawsPerSample; j++) - wu.redraw(); - var end = Date.now(); + var start = Date.now(); + for (var j = 0; j < redrawsPerSample; j++) + wu.redraw(); + var end = Date.now(); - renderReport.recordTime(pageIndex, end - start); - } + renderReport.recordTime(pageIndex, end - start); } function plStop(force) { try { if (force == false) { pageIndex = 0; - results = {}; if (cycle < NUM_CYCLES-1) { - cycle++; - setTimeout(plLoadPage, 0); - return; + cycle++; + setTimeout(plLoadPage, 0); + return; } var formats = reportFormat.split(","); for each (var fmt in formats) - dumpLine(report.getReport(fmt)); + dumpLine(report.getReport(fmt)); if (renderReport) { - dumpLine ("*************** Render report *******************"); - for each (var fmt in formats) - dumpLine(renderReport.getReport(fmt)); + dumpLine ("*************** Render report *******************"); + for each (var fmt in formats) + dumpLine(renderReport.getReport(fmt)); } } } catch (e) { @@ -257,41 +318,76 @@ function plStop(force) { } /* Returns array */ -function plLoadURLsFromURI(uri) { - var data = ""; +function plLoadURLsFromURI(manifestUri) { var fstream = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream); - var sstream = Cc["@mozilla.org/scriptableinputstream;1"] - .createInstance(Ci.nsIScriptableInputStream); - var uriFile = uri.QueryInterface(Ci.nsIFileURL); + var uriFile = manifestUri.QueryInterface(Ci.nsIFileURL); + fstream.init(uriFile.file, -1, 0, 0); - sstream.init(fstream); - - var str = sstream.read(4096); - while (str.length > 0) { - data += str; - str = sstream.read(4096); - } - - sstream.close(); - fstream.close(); - var p = data.split("\n"); + var lstream = fstream.QueryInterface(Ci.nsILineInputStream); - // get rid of things that start with # (comments), - // or that don't have the load string, if given - p = p.filter(function(s) { - if (s == "" || s.indexOf("#") == 0) - return false; - if (pageFilterRegexp && !pageFilterRegexp.test(s)) - return false; - return true; - }); + var d = []; - // stick urlPrefix to the start if necessary - if (urlPrefix) - p = p.map(function(s) { return urlPrefix + s; }); + var lineNo = 0; + var line = {value:null}; + var more; + do { + lineNo++; + more = lstream.readLine(line); + var s = line.value; - return p; + // 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) { diff --git a/mozilla/layout/tools/pageloader/report.js b/mozilla/layout/tools/pageloader/report.js index 0496b19cefb..a4594166be0 100644 --- a/mozilla/layout/tools/pageloader/report.js +++ b/mozilla/layout/tools/pageloader/report.js @@ -113,7 +113,10 @@ function getArrayStats(ary) { r.median = sorted_ary[(sorted_ary.length-1)/2]; }else{ var n = Math.floor(sorted_ary.length / 2); - r.median = (sorted_ary[n] + sorted_ary[n + 1]) / 2; + if (n >= sorted_ary.length) + r.median = sorted_ary[n]; + else + r.median = (sorted_ary[n] + sorted_ary[n + 1]) / 2; } // ignore max value when computing mean and stddev @@ -161,6 +164,10 @@ function strPad(o, len, left) { return str; } +function strPadFixed0(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(); @@ -174,7 +181,7 @@ Report.prototype.getReport = function(format) { var report; - var prefixLen = findCommonPrefixLength(pages); + var prefixLen = findCommonPrefixLength(this.pages); if (format == "js") { // output "simple" js format; @@ -182,7 +189,7 @@ Report.prototype.getReport = function(format) { report = "(["; for (var i = 0; i < this.timeVals.length; i++) { var stats = getArrayStats(this.timeVals[i]); - report += uneval({ page: pages[i].substr(prefixLen), value: stats.mean, stddev: stats.stdd}); + report += uneval({ page: this.pages[i].substr(prefixLen), value: stats.mean, stddev: stats.stdd}); report += ","; } report += "])"; @@ -194,7 +201,15 @@ Report.prototype.getReport = function(format) { 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(pages[i].substr(prefixLen), 40, false) + strPad(stats.mean.toFixed(0)) + strPad(stats.stdd.toFixed(0)) + strPad(stats.min.toFixed(0)) + strPad(stats.max.toFixed(0)) + this.timeVals[i] + "\n"; + 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"; } report += "============================================================\n"; } else if (format == "tinderbox") { @@ -206,7 +221,7 @@ Report.prototype.getReport = function(format) { var r = getArrayStats(this.timeVals[i]); report += '|'+ i + ';'+ - pages[i].substr(prefixLen) + ';'+ + this.pages[i].substr(prefixLen) + ';'+ r.median + ';'+ r.mean + ';'+ r.min + ';'+ diff --git a/mozilla/layout/tools/pageloader/tp-cmdline.js b/mozilla/layout/tools/pageloader/tp-cmdline.js index 2c545e922bb..e9267bb5c90 100644 --- a/mozilla/layout/tools/pageloader/tp-cmdline.js +++ b/mozilla/layout/tools/pageloader/tp-cmdline.js @@ -88,7 +88,6 @@ PageLoaderCmdLineHandler.prototype = args.doRender = cmdLine.handleFlag("tprender", false); args.width = cmdLine.handleFlagWithParam("tpwidth", false); args.height = cmdLine.handleFlagWithParam("tpheight", false); - args.prefix = cmdLine.handleFlagWithParam("tpprefix", false); args.offline = cmdLine.handleFlag("tpoffline", false); } catch (e) { @@ -116,7 +115,6 @@ PageLoaderCmdLineHandler.prototype = " -tprender Run render-only benchmark for each page\n" + " -tpwidth width Width of window\n" + " -tpheight height Height of window\n" + - " -tpprefix prefix Add 'prefix' to the start of each line in the manifest\n" + " -tpoffline Force offline mode\n" };