From ca717b7c4cc1533f0ad02ea9b21e232a40fa5a3c Mon Sep 17 00:00:00 2001
From: "jrgm%netscape.com"
Date: Mon, 6 Aug 2001 03:47:27 +0000
Subject: [PATCH] Initial checking.
git-svn-id: svn://10.0.0.236/trunk@100382 18797224-902f-48f8-a5cc-f745e15eee43
---
mozilla/tools/page-loader/PageData.pm | 240 +++++++
mozilla/tools/page-loader/README.txt | 108 ++++
mozilla/tools/page-loader/URLTimingDataSet.pm | 261 ++++++++
mozilla/tools/page-loader/URLTimingGraph.pm | 104 +++
mozilla/tools/page-loader/echo.pl | 130 ++++
mozilla/tools/page-loader/graph.pl | 111 ++++
mozilla/tools/page-loader/loader.pl | 605 ++++++++++++++++++
mozilla/tools/page-loader/report.pl | 96 +++
mozilla/tools/page-loader/urllist.txt | 82 +++
9 files changed, 1737 insertions(+)
create mode 100644 mozilla/tools/page-loader/PageData.pm
create mode 100644 mozilla/tools/page-loader/README.txt
create mode 100644 mozilla/tools/page-loader/URLTimingDataSet.pm
create mode 100644 mozilla/tools/page-loader/URLTimingGraph.pm
create mode 100755 mozilla/tools/page-loader/echo.pl
create mode 100755 mozilla/tools/page-loader/graph.pl
create mode 100755 mozilla/tools/page-loader/loader.pl
create mode 100755 mozilla/tools/page-loader/report.pl
create mode 100644 mozilla/tools/page-loader/urllist.txt
diff --git a/mozilla/tools/page-loader/PageData.pm b/mozilla/tools/page-loader/PageData.pm
new file mode 100644
index 00000000000..b012eaa37b6
--- /dev/null
+++ b/mozilla/tools/page-loader/PageData.pm
@@ -0,0 +1,240 @@
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+package PageData;
+use strict;
+use vars qw($MagicString $ClientJS); # defined at end of file
+
+#
+# contains a set of URLs and other meta information about them
+#
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {
+ ClientJS => $ClientJS,
+ MagicString => $MagicString,
+ PageHash => {},
+ PageList => [],
+ Length => undef,
+ FileBase => undef,
+ HTTPBase => undef
+ };
+ bless ($self, $class);
+ $self->_init();
+ return $self;
+}
+
+
+#
+# Parse a config file in the current directory for info.
+# All requests to the current cgi-bin path will use the same info;
+# to set up specialized lists, create a separate cgi-bin subdir
+#
+sub _init {
+
+ my $self = shift;
+
+ my $file = "urllist.txt";
+ open(FILE, "< $file") ||
+ die "Can't open file $file: $!";
+
+ while () {
+ next if /^$/;
+ next if /^#|^\s+#/;
+ s/\s+#.*$//;
+ if (/^HTTPBASE:\s+(.*)$/i) {
+ $self->{HTTPBase} = $1;
+ } elsif (/^FILEBASE:\s+(.*)$/i) {
+ $self->{FileBase} = $1;
+ } else {
+ my @ary = split(/\s+/, $_);
+ $ary[1] ||= 'index.html';
+ push @{$self->{PageList}}, { Name => $ary[0], URL => join('/', @ary) };
+ }
+ }
+
+ # check that we have enough to go on
+ die "Did not read any URLs" unless scalar(@{$self->{PageList}});
+ die "Did not read a value for the http base" unless $self->{HTTPBase};
+ die "Did not read a value for the file base" unless $self->{FileBase};
+
+ $self->{Length} = scalar(@{$self->{PageList}});
+ $self->_createHashView();
+
+}
+
+
+sub _createHashView {
+ # repackages the array, so it can be referenced by name
+ my $self = shift;
+ for my $i (0..$self->lastidx) {
+ my $hash = $self->{PageList}[$i];
+ #warn $i, " ", $hash, " ", %$hash;
+ $self->{PageHash}{$hash->{Name}} = {
+ Index => $i,
+ URL => $hash->{URL},
+ };
+ }
+}
+
+
+sub filebase { my $self = shift; return $self->{FileBase}; }
+sub httpbase { my $self = shift; return $self->{HTTPBase}; }
+sub length { my $self = shift; return $self->{Length}; }
+sub lastidx { my $self = shift; return $self->{Length} - 1; }
+sub magicString { my $self = shift; return $self->{MagicString}; }
+sub clientJS { my $self = shift; return $self->{ClientJS}; }
+
+
+sub url {
+ # get the relative url by index or by name
+ my $self = shift;
+ my $arg = shift;
+ if ($arg =~ /^\d+$/) {
+ return $self->_checkIndex($arg) ? $self->{PageList}[$arg]{URL} : "";
+ } else {
+ return $self->{PageHash}{$arg}{URL};
+ }
+}
+
+
+sub name {
+ my $self = shift;
+ my $arg = shift;
+ if ($arg =~ /^\d+$/) {
+ return $self->_checkIndex($arg) ? $self->{PageList}[$arg]{Name} : "";
+ } else {
+ #warn "You looked up the name using a name.";
+ return $arg;
+ }
+}
+
+
+sub index {
+ my $self = shift;
+ my $arg = shift;
+ if ($arg =~ /^\d+$/) {
+ #warn "You looked up the index using an index.";
+ return $arg;
+ } else {
+ return $self->{PageHash}{$arg}{Index};
+ }
+}
+
+
+sub _checkIndex {
+ my $self = shift;
+ my $idx = shift;
+ die "Bogus index passed to PageData: $idx"
+ unless defined($idx) &&
+ $idx =~ /^\d+$/ &&
+ $idx >= 0 &&
+ $idx < $self->{Length};
+ return 1;
+}
+
+
+#
+# JS to insert in the static HTML pages to trigger client timimg and reloading.
+# You must escape any '$', '@', '\n' contained in the JS code fragment. Otherwise,
+# perl will attempt to interpret them, and silently convert " $foo " to " ".
+#
+# JS globals have been intentionally "uglified" with 'moztest_', to avoid collision
+# with existing content in the page
+#
+$MagicString = '';
+$ClientJS =<<"ENDOFJS";
+
+function moztest_tokenizeQuery() {
+ var query = {};
+ var pairs = document.location.search.substring(1).split('&');
+ for (var i=0; i < pairs.length; i++) {
+ var pair = pairs[i].split('=');
+ query[pair[0]] = unescape(pair[1]);
+ }
+ return query;
+}
+
+function moztest_setLocationHref(href, useReplace) {
+ // false => "Location.href=url", not ".replace(url)"
+ if (useReplace) {
+ document.location.replace(href);
+ } else {
+ document.location.href = href;
+ }
+}
+
+var g_moztest_Href;
+function moztest_nextRequest(c_part) {
+ function getValue(arg,def) {
+ return !isNaN(arg) ? parseInt(Number(arg)) : def;
+ }
+ var q = moztest_tokenizeQuery();
+ var index = getValue(q['index'], 0);
+ var cycle = getValue(q['cycle'], 0);
+ var maxcyc = getValue(q['maxcyc'], 1);
+ var replace = getValue(q['replace'], 0);
+ var nocache = getValue(q['nocache'], 0);
+ var delay = getValue(q['delay'], 0);
+ var timeout = getValue(q['timeout'], 30000);
+ var c_ts = getValue(q['c_ts'], Number.NaN);
+
+ // check for times
+ var now = (new Date()).getTime();
+ var c_intvl = now - c_ts;
+ var c_ts = now + delay; // adjust for delay time
+
+ // Now make the request ...
+ g_moztest_Href = document.location.href.split('?')[0] +
+ "?c_part=" + c_part +
+ "&index=" + ++index + // increment the request index
+ "&id=" + q['id'] +
+ "&maxcyc=" + maxcyc +
+ "&replace=" + replace +
+ "&nocache=" + nocache +
+ "&delay=" + delay +
+ "&timeout=" + timeout +
+ "&c_intvl=" + c_intvl +
+ "&s_ts=" + g_moztest_ServerTime +
+ "&c_ts=" + c_ts +
+ "&content=" + g_moztest_Content;
+ window.setTimeout("moztest_setLocationHref(g_moztest_Href,false);", delay);
+ return true;
+}
+
+function moztest_onDocumentLoad() {
+ var loadTime = (new Date()).getTime() - g_moztest_Start;
+ window.clearTimeout(g_moztest_safetyTimer); // the onload has fired, clear the safety
+ moztest_nextRequest(loadTime);
+}
+
+function moztest_safetyValve() {
+ moztest_nextRequest(Number.NaN); // if the onload never fires
+}
+
+// normal processing is to calculate load time and fetch another URL
+window.onload = moztest_onDocumentLoad;
+
+ENDOFJS
+
+1; # return true from module
diff --git a/mozilla/tools/page-loader/README.txt b/mozilla/tools/page-loader/README.txt
new file mode 100644
index 00000000000..0488c9e0f93
--- /dev/null
+++ b/mozilla/tools/page-loader/README.txt
@@ -0,0 +1,108 @@
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+Rough notes on setting up this test app. jrgm@netscape.com 2001/08/05
+
+1) this is intended to be run as a mod_perl application under an Apache web
+ server. [It is possible to run it as a cgi-bin, but then you will be paying
+ the cost of forking perl and re-compiling all the required modules on each
+ page load].
+
+2) it should be possible to run this under Apache on win32, but I expect that
+ there are *nix-oriented assumptions that have crept in. You would also need
+ a replacement for Time::HiRes, probably by using Win32::API to directly
+ call into the system to Windows 'GetLocalTime()'.
+
+3) You need to have a few "non-standard" Perl Modules installed. This script
+ will tell you which ones are not installed (let me know if I have left some
+ out of this test).
+
+--8<--------------------------------------------------------------------
+#!/usr/bin/perl
+my @modules = qw{
+ LWP::UserAgent SQL::Statement Text::CSV_XS DBD::CSV
+ DBI Time::HiRes CGI::Request URI
+ MIME::Base64 HTML::Parser HTML::Tagset Digest::MD5
+ };
+for (@modules) {
+ printf "%20s", $_;
+ eval "use $_;";
+ if ($@) {
+ print ", I don't have that.\n";
+ } else {
+ print ", version: ", eval "\$" . "$_" . "::VERSION", "\n";
+ }
+}
+--8<--------------------------------------------------------------------
+
+ For modules that are missing, you can find them at http://www.cpan.org/.
+ Download the .tar.gz files you need, and then (for the most part) just
+ do 'perl Makefile.PL; make; make test; make install'.
+
+4) There is code to draw a sorted graph of the final results, but I have
+ disabled the place in 'report.pl' where its use would be triggered (look
+ for the comment). This is so that you can run this without having gone
+ through the additional setup of the 'gd' library, and the modules GD and
+ GD::Graph. If you have those in place, you can turn this on by just
+ reenabling the print statement in report.pl
+
+5) To set this up with Apache, create a directory in the cgi-bin for the web
+ server called e.g. 'page-loader' and then place this in the Apache
+ httpd.conf file to enable this for mod_perl (and then restart Apache).
+
+--8<--------------------------------------------------------------------
+Alias /page-loader/ /var/www/cgi-bin/page-loader/
+
+SetHandler perl-script
+PerlHandler Apache::Registry
+PerlSendHeader On
+Options +ExecCGI
+
+--8<--------------------------------------------------------------------
+
+ So, now you can run this as 'http://yourserver.domain.com/page-loader/loader.pl'
+
+6) You need to create a subdirectory call 'db' under the 'page-loader'
+ directory. This subdirectory 'db' must be writeable by UID that Apache
+ executes as (e.g., 'nobody' or 'apache'). [You may want to figure out some
+ other way to do this if this web server is not behind a firewall].
+
+7) You need to assemble a set of content pages, with all images, included JS and
+ CSS pulled to the same directory. These pages can live anywhere on the same
+ HTTP server that is running this app. The app assumes that each page is in
+ its own sub-directory, with included content below that directory. You can
+ set the location and the list of pages in the file 'urllist.txt'. There are
+ various tools that will pull in complete copies of web pages (e.g. 'wget' or
+ something handrolled from LWP::UserAgent). You should edit the pages to remove
+ any redirects, popup windows, and possibly any platform specific JS rules
+ (e.g., Mac specific CSS included with 'document.write("LINK...'). You should
+ also check that for missing content, or URLs that did not get changed to point
+ to the local content. [One way to check for this is tweak this simple proxy server
+ to check your links: http://www.stonehenge.com/merlyn/WebTechniques/col34.listing.txt)
+
+8) The "hook" into the content is a single line in each top-level document like this:
+
+ which should be placed immediately after the opening element. The script uses
+ this as the way to substitute a BASE HREF and some JS into the page which will control
+ the exectution of the test.
+
+9) I've probably left some stuff out. Bug jrgm@netscape.com for the missing stuff.
diff --git a/mozilla/tools/page-loader/URLTimingDataSet.pm b/mozilla/tools/page-loader/URLTimingDataSet.pm
new file mode 100644
index 00000000000..2087826a64a
--- /dev/null
+++ b/mozilla/tools/page-loader/URLTimingDataSet.pm
@@ -0,0 +1,261 @@
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+package URLTimingDataSet;
+use DBI;
+use PageData; # list of test pages, etc.
+use strict;
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {
+ dataset => [],
+ results => [],
+ sorted => [],
+ average => undef,
+ avgmedian => undef, # note: average of individual medians
+ maximum => undef,
+ minimum => undef,
+ };
+ $self->{id} = shift || die "No id supplied";
+ $self->{table} = shift || "t" . $self->{id};
+ $self->{pages} = PageData->new;
+ bless ($self, $class);
+ $self->_grok();
+ return $self;
+}
+
+
+sub _grok {
+ my $self = shift;
+ my ($res);
+
+ # select the dataset from the db
+ $self->_select();
+
+ for (my $i=0; $i < $self->{pages}->length; $i++) {
+ my $name = $self->{pages}->name($i);
+ my $count = 0;
+ my @times = ();
+ my $nan = 0;
+ foreach my $ref (@{$self->{dataset}}) {
+ next if ($name ne $ref->{content});
+ $count++;
+ if ($ref->{c_part} eq "NaN") {
+ # we bailed out of this page load
+ $res = "NaN";
+ $nan = 1;
+ }
+ else {
+ my $s_intvl = $ref->{s_intvl};
+ my $c_intvl = $ref->{c_intvl};
+ my $errval = abs($s_intvl-$c_intvl)/(($s_intvl+$c_intvl)/2);
+ if ($errval > 0.08) { # one of them went wrong and stalled out (see [1] below)
+ $res = ($s_intvl <= $c_intvl) ? $s_intvl : $c_intvl;
+ } else {
+ $res = int(($s_intvl + $c_intvl)/2);
+ }
+ }
+ push @times, $res;
+ }
+
+ my $avg = int(_avg(@times));
+ my $med = _med(@times);
+ my $max = $nan ? "NaN" : _max(@times);
+ my $min = _min(@times);
+ push @{$self->{results}}, [ $i, $name, $count, $avg, $med, $max, $min, @times ];
+ }
+
+ $self->_get_summary();
+ $self->_sort_result_set();
+
+}
+
+sub _select {
+ my $self = shift;
+
+ my $dbh = DBI->connect("DBI:CSV:f_dir=./db", {RaiseError => 1, AutoCommit => 1})
+ or die "Cannot connect: " . $DBI::errstr;
+
+ my $sql = qq{
+ SELECT INDEX, S_INTVL, C_INTVL, C_PART, CONTENT, ID
+ FROM $self->{table}
+ WHERE ID = "$self->{id}"
+ };
+
+ my $sth = $dbh->prepare($sql);
+ $sth->execute();
+
+ while (my @data = $sth->fetchrow_array()) {
+ push @{$self->{dataset}},
+ {index => $data[0],
+ s_intvl => $data[1],
+ c_intvl => $data[2],
+ c_part => $data[3],
+ content => $data[4],
+ id => $data[5]
+ };
+ }
+ $sth->finish();
+ $dbh->disconnect();
+}
+
+sub _get_summary {
+ my $self = shift;
+ my (@avg, @med, @max, @min);
+
+ # how many pages were loaded in total ('sampled')
+ $self->{samples} = scalar(@{$self->{dataset}});
+
+ # how many cycles (should I get this from test parameters instead?)
+ $self->{count} = int(_avg( map($_->[2], @{$self->{results}}) ));
+ warn $self->{count};
+
+ # calculate overall average, average median, maximum, minimum, (RMS Error?)
+ for (@{$self->{results}}) {
+ push @avg, $_->[3];
+ push @med, $_->[4];
+ push @max, $_->[5];
+ push @min, $_->[6];
+ }
+ $self->{average} = int(_avg(@avg));
+ $self->{avgmedian} = int(_avg(@med)); # note: averaging individual medians
+ $self->{maximum} = _max(@max);
+ $self->{minimum} = _min(@min);
+}
+
+sub _sort_result_set {
+ my $self = shift;
+ # sort by median load time
+ # @{$self->{sorted}} = sort {$a->[4] <=> $b->[4]} @{$self->{results}};
+ # might be "NaN", but this is lame of me to be carrying around a string instead of undef
+ @{$self->{sorted}} =
+ sort {
+ if ($a->[4] eq "NaN" || $b->[4] eq "NaN") {
+ return $a->[4] cmp $b->[4];
+ } else {
+ return $a->[4] <=> $b->[4];
+ }
+ } @{$self->{results}};
+}
+
+sub as_string {
+ my $self = shift;
+ return $self->_as_string();
+}
+
+sub as_string_sorted {
+ my $self = shift;
+ return $self->_as_string(@{$self->{sorted}});
+}
+
+
+sub _as_string {
+ my $self = shift;
+ my @ary = @_ ? @_ : @{$self->{results}};
+ my $str;
+ for (@ary) {
+ my ($index, $path, $count, $avg, $med, $max, $min, @times) = @$_;
+ $str .= sprintf "%3s %-26s\t", $index, $path;
+ if ($count > 0) {
+ $str .= sprintf "%6s %6s %6s %6s ", $avg, $med, $max, $min;
+ foreach my $time (@times) {
+ $str .= sprintf "%6s ", $time;
+ }
+ }
+ $str .= "\n";
+ }
+ return $str;
+}
+
+#
+# package internal helper functions
+#
+sub _num {
+ my @array = ();
+ for (@_) { push @array, $_ if /^[+-]?\d+\.?\d*$/o; }
+ return @array;
+}
+
+sub _avg {
+ my @array = _num(@_);
+ return "NaN" unless scalar(@array);
+ my $sum = 0;
+ for (@array) { $sum += $_; }
+ return $sum/scalar(@array);
+}
+
+sub _max {
+ my @array = _num(@_);
+ return "NaN" unless scalar(@array);
+ my $max = $array[0];
+ for (@array) { $max = ($max > $_) ? $max : $_; }
+ return $max;
+}
+
+sub _min {
+ my @array = _num(@_);
+ return "NaN" unless scalar(@array);
+ my $min = $array[0];
+ for (@array) { $min = ($min < $_) ? $min : $_; }
+ return $min;
+}
+
+# returns the floor(N/2) element of a sorted ascending array
+sub _med {
+ my @array = _num(@_);
+ return "NaN" unless scalar(@array);
+ my $index = int((scalar(@array)-1)/2);
+ @array = sort {$a <=> $b} @array;
+ return $array[$index];
+}
+
+1; # return true
+
+################################################################################
+#
+# [1] in looking at the test results, in almost all cases, the
+# round-trip time measured by the server logic and the client logic
+# would be almost the same value (which is what one would
+# expect). However, on occasion, one of the them would be "out of
+# whack", and inconsistent with the additional "layout" measure by the
+# client.
+#
+# i.e., a set of numbers like these:
+# c_part c_intvl s_intvl
+# 800 1003 997
+# 804 1007 1005
+# 801 1001 1325 <--
+# 803 1318 998 <--
+# 799 1002 1007
+# ...
+#
+# which looks like the server side would stall in doing the accept or
+# in running the mod-perl handler (possibly a GC?). (The following
+# c_intvl would then be out of whack by a matching amount on the next
+# cycle).
+#
+# At any rate, since it was clear from comparing with the 'c_part'
+# measure, which of the times was bogus, I just use an arbitrary error
+# measure to determine when to toss out the "bad" value.
+#
diff --git a/mozilla/tools/page-loader/URLTimingGraph.pm b/mozilla/tools/page-loader/URLTimingGraph.pm
new file mode 100644
index 00000000000..cd8d33e52df
--- /dev/null
+++ b/mozilla/tools/page-loader/URLTimingGraph.pm
@@ -0,0 +1,104 @@
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+package URLTimingGraph;
+use strict;
+use GD;
+use GD::Graph::linespoints;
+use GD::Graph::points;
+use GD::Graph::lines;
+use GD::Graph::mixed;
+use GD::Graph::colour;
+use GD::Graph::Data;
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->{data} = shift || die "No data.";
+ my $args = shift || {};
+ $self->{cgimode} = $args->{cgimode} || 0;
+ $self->{title} = $args->{title} || "";
+ $self->{types} = $args->{types} || ['lines', undef, undef, undef, undef, undef, undef];
+ $self->{dclrs} = $args->{dclrs} || [qw(lred)];
+ $self->{legend} = $args->{legend} || [qw(undef)];
+ $self->{y_max_value} = $args->{y_max_value} || 10000;
+ $self->{width} = $args->{width} || 800;
+ $self->{height} = $args->{height} || 720;
+ return $self;
+}
+
+sub _set_standard_options {
+ my $self = shift;
+ $self->{graph}->set(
+ x_label => '',
+ y_label => 'Page Load Time (msec)',
+ default_type => 'points',
+ x_labels_vertical => 1,
+ y_long_ticks => 1,
+ x_tick_length => 8,
+ x_long_ticks => 0,
+ line_width => 2,
+ marker_size => 3,
+ markers => [8],
+ show_values => 0,
+ transparent => 0,
+ interlaced => 1,
+ skip_undef => 1,
+ )
+ || warn $self->{graph}->error;
+ $self->{graph}->set_title_font(GD::Font->Giant);
+ $self->{graph}->set_x_label_font(GD::Font->Large);
+ $self->{graph}->set_y_label_font(GD::Font->Large);
+ $self->{graph}->set_x_axis_font(GD::Font->Large);
+ $self->{graph}->set_y_axis_font(GD::Font->Large);
+ $self->{graph}->set_legend_font(GD::Font->Giant);
+}
+
+sub plot {
+ my $self = shift;
+ $self->{graph} = new GD::Graph::mixed($self->{width},
+ $self->{height});
+ $self->_set_standard_options();
+
+ $self->{graph}->set(title => $self->{title},
+ types => $self->{types},
+ y_max_value => $self->{y_max_value},
+ dclrs => $self->{dclrs},
+ )
+ || warn $self->{graph}->error;
+
+ $self->{graph}->set_legend( @{$self->{legend}} );
+
+ # draw the graph image
+ $self->{graph}->plot($self->{data}) ||
+ die $self->{graph}->error;
+
+ # send it back to stdout (or browser)
+ print "Content-type: image/png\n\n" if $self->{cgimode};
+ binmode STDOUT;
+ print $self->{graph}->gd->png();
+}
+
+
+1; #return true
diff --git a/mozilla/tools/page-loader/echo.pl b/mozilla/tools/page-loader/echo.pl
new file mode 100755
index 00000000000..2f4d8df9803
--- /dev/null
+++ b/mozilla/tools/page-loader/echo.pl
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+use Time::HiRes qw(gettimeofday tv_interval);
+
+sub encodeHiResTime {
+ my $timeref = shift;
+ return unless ref($timeref);
+ my $time = $$timeref[0] . "-" . $$timeref[1];
+ return $time;
+}
+
+my $time = encodeHiResTime([gettimeofday()]);
+
+print "Content-type: text/html\n\n";
+print <<"ENDOFHTML";
+
+
+
+
+
+
+ This page starts the test.
+
+
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+
+
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+
+
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+ dummy page dummy page dummy page dummy page dummy page dummy page
+
+
+
+
+ENDOFHTML
+
+exit 0;
diff --git a/mozilla/tools/page-loader/graph.pl b/mozilla/tools/page-loader/graph.pl
new file mode 100755
index 00000000000..56a0cdbe03e
--- /dev/null
+++ b/mozilla/tools/page-loader/graph.pl
@@ -0,0 +1,111 @@
+#!/usr/bin/perl
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+use CGI::Carp qw(fatalsToBrowser);
+use CGI::Request;
+use URLTimingDataSet;
+use URLTimingGraph;
+
+my $request = new CGI::Request;
+
+my $id = $request->param('id'); #XXX need to check for valid parameter id
+my $id2 = $request->param('id2') || undef; # possible comparison test
+
+# set up the data for the first graph
+my $rs = URLTimingDataSet->new($id);
+my @data = ();
+push @data, [ map($_->[1], @{$rs->{sorted}}) ]; # URL
+push @data, [ map($_->[4], @{$rs->{sorted}}) ]; # median
+# '7' is the first slot for individual test run data
+for (my $idx = 7; $idx < (7+$rs->{count}); $idx++) {
+ push @data, [ map($_->[$idx], @{$rs->{sorted}}) ];
+}
+
+
+# set up the data for the second graph, if requested a second id
+# need to sort according to the first chart's ordering
+my $rs2;
+if ($id2) {
+ $rs2 = URLTimingDataSet->new($id2);
+ my @order = map($_->[0], @{$rs->{sorted}}); # get the first chart's order
+ my @resort = ();
+ for my $i (@order) {
+ for (@{$rs2->{sorted}}) {
+ if ($i == $_->[0]) {
+ push @resort, $_;
+ last;
+ }
+ }
+ }
+ push @data, [ map($_->[4], @resort) ]; # median
+ for (my $idx = 7; $idx < (7+$rs2->{count}); $idx++) {
+ push @data, [ map($_->[$idx], @resort) ];
+ }
+}
+
+# and now convert 'NaN' to undef, if they exist in the data.
+for (@data) { for (@$_) { $_ = undef if $_ eq "NaN"; } }
+
+# set up the chart parameters
+my $args = {};
+$args->{cgimode} = 1;
+$args->{title} = "id=$id";
+
+# need to draw first visit as dotted with points
+my $types = ['lines','lines']; for (1..$rs->{count}-1) { push @$types, undef; }
+my $dclrs = []; for (0..$rs->{count}) { push @$dclrs, 'lred'; }
+my $legend = [$id]; for (1..$rs->{count}) { push @$legend, undef; }
+if ($id2) {
+ push @$types, 'lines'; for (1..$rs2->{count}) { push @$types, undef; }
+ for (0..$rs2->{count}) { push @$dclrs, 'lblue'; }
+ push @$legend, $id2; for (1..$rs2->{count}) { push @$legend, undef; }
+}
+$args->{types} = $types;
+$args->{dclrs} = $dclrs;
+$args->{legend} = $legend;
+
+#XXX set min to zero, and round max to 1000
+$args->{y_max_value} = maxDataOrCap();
+## nope $args->{y_min_value} = 1000;
+$args->{width} = 800;
+$args->{height} = 720;
+
+my $g = URLTimingGraph->new(\@data, $args);
+$g->plot();
+
+exit;
+
+
+sub maxDataOrCap {
+ my $max;
+ warn $rs->{maximum};
+ if ($rs2 && ($rs->{maximum} < $rs2->{maximum})) {
+ $max = $rs2->{maximum};
+ } else {
+ $max = $rs->{maximum};
+ }
+ warn $max;
+ #return $max > 10000 ? 10000 : 1000*int($max/1000)+1000;
+ # just return whatever, rounded to 1000
+ return 1000*int($max/1000)+1000;
+}
diff --git a/mozilla/tools/page-loader/loader.pl b/mozilla/tools/page-loader/loader.pl
new file mode 100755
index 00000000000..be02afc9770
--- /dev/null
+++ b/mozilla/tools/page-loader/loader.pl
@@ -0,0 +1,605 @@
+#!/usr/bin/perl
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+
+use strict;
+use CGI::Request;
+use CGI::Carp qw(fatalsToBrowser);
+use Time::HiRes qw(gettimeofday tv_interval);
+use POSIX qw(strftime);
+use DBI;
+
+# list of test pages, JS to insert, httpbase, filebase, etc.
+use PageData;
+
+use vars qw(%params $req $cgi $dbh $pagedata
+ $gStartNow $gStartNowStr
+ $gResponseNow $gLogging);
+
+$gStartNow = [gettimeofday]; # checkpoint the time
+$gStartNowStr = strftime "%Y%m%d%H%M%S", localtime;
+$gLogging = 1;
+
+$req = new CGI::Request; # get the HTTP/CGI request
+$cgi = $req->cgi;
+
+$pagedata = PageData->new;
+
+setDefaultParams();
+
+#XXXdebugcrap
+#warn $params{index}, " ", $params{maxidx};
+
+if (!defined($req->param('delay'))) {
+ # give the user a form to pick options (but note that going
+ # to "loader.pl?delay=1000" immediately starts the test run
+ outputForm();
+}
+elsif (!$req->param('id')) {
+ initialize(); # do redirect to start the cycle
+}
+elsif ($params{index} > $params{maxidx}) {
+ redirectToReport(); # the test is over; spit out a summary
+ markTestAsComplete(); # close the meta table entry
+}
+elsif (!isRequestStale()) {
+ outputPage(); # otherwise, keep dishing out pages
+ updateDataBase(); # client has the response; now write out stats to db
+}
+
+# cleanup
+$req = undef;
+$dbh->disconnect() if $dbh; # not strictly required (ignored in some cases anyways)
+
+logMessage(sprintf("Page load server responded in %3d msec, total time %3d msec, pid: %d",
+ 1000*tv_interval($gStartNow, $gResponseNow), 1000*tv_interval($gStartNow), $$))
+ if $gResponseNow; # log only when a test page has been dished out
+
+exit 0;
+
+#######################################################################
+
+sub logMessage {
+ print STDERR strftime("[%a %b %d %H:%M:%S %Y] ", localtime), @_, "\n"
+ if $gLogging;
+}
+
+
+sub isRequestStale {
+ my $limit = 30*60; # 30 minutes, although if we never stalled on mac I'd make it 3 minutes
+ my $ts = decodeHiResTime($params{s_ts});
+ my $delta = tv_interval($ts, $gStartNow);
+ return undef if $delta < $limit;
+ # otherwise, punt this request
+ print "Content-type: text/html\n\n";
+ print <<"ENDOFHTML";
+Page Loading Times Test
+The timestamp on the request is too old to continue:
+s_ts=$params{s_ts} was $delta seconds ago. Limit is $limit seconds.
+
+ENDOFHTML
+ return 1; # it's stale
+}
+
+
+sub initialize {
+ updateMetaTable();
+ createDataSetTable();
+
+ # start the test by bouncing off of an echo page
+ my $script = $cgi->var("SCRIPT_NAME");
+ my $server = $cgi->var("SERVER_NAME");
+ my $prot = "http://"; #XXX need a way to check/set 'https'
+ my $me = $prot . $server . $script;
+ $script =~ /^(.*\/).*$/;
+ my $loc = "Location: ". $prot . $server . $1 . "echo.pl?";
+ for (qw(id index maxcyc delay replace nocache timeout)) {
+ $loc .= "$_=$params{$_}\&";
+ }
+ $loc .= "url=" . $me;
+ print $loc, "\n\n";
+}
+
+
+sub redirectToReport {
+ # n.b., can also add '&sort=1' to get a time sorted list
+ my $prot = "http://";
+ my $loc = "Location: " . $prot . $cgi->var("SERVER_NAME");
+ $cgi->var("SCRIPT_NAME") =~ /^(.*\/).*$/;
+ $loc .= $1 . "report.pl?id=" . $params{id};
+ print $loc, "\n\n";
+}
+
+
+sub generateTestId {
+ # use the epoch time, in hex, plus a two-character random.
+ return sprintf "%8X%02X", time(), int(256*rand());
+}
+
+
+sub setDefaultParams {
+ $params{id} = $req->param('id') || generateTestId(); # "unique" id for this run
+ $params{index} = $req->param('index') || 0; # request index for the test
+ $params{maxcyc} = defined($req->param('maxcyc')) ?
+ $req->param('maxcyc') : 3; # max visits (zero-based count)
+ $params{delay} = $req->param('delay') || 1000; # setTimeout on the next request (msec)
+ $params{replace} = $req->param('replace') || 0; # use Location.replace (1) or Location.href (0)
+ $params{nocache} = $req->param('nocache') || 0; # serve content via uncacheable path
+ $params{c_part} = $req->param('c_part') || 0; # client time elapsed; page head to onload (msec)
+ $params{c_intvl} = $req->param('c_intvl') || 0; # client time elapsed; onload to onload event (msec)
+ $params{c_ts} = $req->param('c_ts') || 0; # client timestamp (.getTime()) (msec)
+ $params{content} = $req->param('content') || "UNKNOWN"; # name of content page for this data
+ $params{s_ts} = $req->param('s_ts') || undef; # server timestamp; no default
+ $params{timeout} = $req->param('timeout') || 30000; # msec; timer will cancel stalled page loading
+ $params{maxidx} = ($params{maxcyc}+1) * $pagedata->length; # total pages loads to be done
+ $params{curidx} = $params{index} % $pagedata->length; # current request index into page list
+ $params{curcyc} = int(($params{index}-1) / $pagedata->length); # current "cycle" (visit)
+}
+
+
+sub outputPage {
+ my $relpath = $pagedata->url($params{curidx});
+ my $file = $pagedata->filebase . $relpath;
+ open (HTML, "<$file") ||
+ die "Can't open file: $file, $!";
+
+ my $hook = "\n";
+
+ my $basepath = $pagedata->httpbase;
+ $basepath =~ s#^(.*?)(/base/)$#$1/nocache$2# if ($params{nocache});
+ $hook .= "";
+
+ my $magic = $pagedata->magicString;
+ my $content = "";
+ while () {
+ s/$magic/$hook/;
+ $content .= $_;
+ }
+
+ # N.B., these two cookie headers are obsolete, since I pass server info in
+ # JS now, to work around a bug in winEmbed with document.cookie. But
+ # since I _was_ sending two cookies as part of the test, I have to keep
+ # sending two cookies (at least for now, and it's not a bad thing to test)
+ #XXX other headers to test/use?
+
+ $gResponseNow = [gettimeofday]; # for logging
+ { # turn on output autoflush, locally in this block
+ local $| = 1;
+ print "Set-Cookie: moztest_SomeRandomCookie1=somerandomstring\n";
+ print "Set-Cookie: moztest_SomeRandomCookie2=somerandomstring\n";
+ print "Content-type: text/html\n\n";
+ print $content;
+ }
+
+ return;
+}
+
+
+sub encodeHiResTime {
+ my $timeref = shift;
+ return unless ref($timeref);
+ return $$timeref[0] . "-" . $$timeref[1];
+}
+
+
+sub decodeHiResTime {
+ my $timestr = shift;
+ return [ split('-', $timestr) ];
+}
+
+
+sub elapsedMilliSeconds {
+ my ($r_time, $timestr) = @_;
+ return "NaN" unless $timestr;
+ my $delta = tv_interval( [ split('-', $timestr) ], $r_time );
+ my $delta = int(($delta*1000) - $params{delay}); # adjust for delay (in msec)
+ return $delta;
+}
+
+
+sub updateDataBase {
+ connectToDataBase(); # (may already be cached)
+ updateMetaTable();
+ updateDataSetTable() unless $params{c_part} == -1; # the initial request
+}
+
+
+sub connectToDataBase {
+ # don't reconnect if already connected. (Other drivers provide this
+ # for free I think, but not this one).
+ if (!ref($dbh)) {
+ $dbh = DBI->connect("DBI:CSV:f_dir=./db", {RaiseError => 1, AutoCommit => 1})
+ || die "Cannot connect: " . $DBI::errstr;
+ }
+}
+
+
+#
+# Holds the individual page load data for this id.
+#
+# (Of course, this should really be a single table for all datasets, but
+# that was becoming punitively slow with DBD::CSV. I could have moved to
+# a "real" database, but I didn't want to make that a requirement for
+# installing this on another server and using this test (e.g., install a
+# few modules and you can run this; no sql installation/maintenance required).
+# At some point though, I may switch to some sql db, but hopefully still allow
+# this to be used with a simple flat file db. (Hmm, maybe I should try a *dbm
+# as a compromise (disk based but indexed)).
+#
+sub createDataSetTable {
+ my $table = "t" . $params{id};
+ return if -f "db/$table"; # don't create it if it exists
+ logMessage("createDataSetTable:\tdb/$table");
+ connectToDataBase(); # cached
+
+ my ($sth, $sql);
+ $sql = qq{
+ CREATE TABLE $table
+ (DATETIME CHAR(14),
+ ID CHAR(10),
+ INDEX INTEGER,
+ CUR_IDX INTEGER,
+ CUR_CYC INTEGER,
+ C_PART INTEGER,
+ S_INTVL INTEGER,
+ C_INTVL INTEGER,
+ CONTENT CHAR(128)
+ )
+ };
+ $sth = $dbh->prepare($sql);
+ $sth->execute();
+ $sth->finish();
+ return 1;
+}
+
+
+#
+# holds the information about all test runs
+#
+sub createMetaTable {
+ my $table = shift;
+ return if -f "db/$table"; # don't create it if it exists
+ logMessage("createMetaTable:\tdb/$table");
+
+ my ($sth, $sql);
+
+ $sql = qq{
+ CREATE TABLE $table
+ (DATETIME CHAR(14),
+ LASTPING CHAR(14),
+ ID CHAR(8),
+ INDEX INTEGER,
+ CUR_IDX INTEGER,
+ CUR_CYC INTEGER,
+ CUR_CONTENT CHAR(128),
+ STATE INTEGER,
+ BLESSED INTEGER,
+ MAXCYC INTEGER,
+ MAXIDX INTEGER,
+ REPLACE INTEGER,
+ NOCACHE INTEGER,
+ DELAY INTEGER,
+ REMOTE_USER CHAR(16),
+ HTTP_USER_AGENT CHAR(128),
+ REMOTE_ADDR CHAR(15),
+ USER_EMAIL CHAR(32),
+ USER_COMMENT CHAR(256)
+ )
+ };
+ $sth = $dbh->prepare($sql);
+ $sth->execute();
+ $sth->finish();
+ warn 'created meta table';
+ return 1;
+}
+
+
+sub updateMetaTable {
+
+ connectToDataBase(); # if not already connected
+
+ my $table = "tMetaTable";
+ createMetaTable($table); # just returns if already created
+
+ my ($sth, $sql);
+
+ $sql = qq{
+ SELECT INDEX, MAXCYC, MAXIDX, REPLACE, NOCACHE,
+ DELAY, REMOTE_USER, HTTP_USER_AGENT, REMOTE_ADDR
+ FROM $table
+ WHERE ID = "$params{id}"
+ };
+ $sth = $dbh->prepare($sql);
+ $sth->execute();
+
+ my @dataset = ();
+ while (my @data = $sth->fetchrow_array()) {
+ push @dataset, {index => shift @data,
+ maxcyc => shift @data,
+ maxidx => shift @data,
+ replace => shift @data,
+ nocache => shift @data,
+ delay => shift @data,
+ remote_user => shift @data,
+ http_user_agent => shift @data,
+ remote_addr => shift @data
+ };
+ }
+ $sth->finish();
+ warn "More than one ID: $params{id} ??" if scalar(@dataset) > 1;
+
+ if (scalar(@dataset) == 0) {
+ # this is a new dataset and id
+ initMetaTableRecord($table);
+ return;
+ }
+
+ #XXX need to check that values are sane, and not update if they don't
+ # match certain params. This should not happen in a normal test run.
+ # However, if a test url was bookmarked or in history, I might get bogus
+ # data collected after the fact. But I have a stale date set on the URL,
+ # so that is good enough for now.
+ # my $ref = shift @dataset; # check some $ref->{foo}
+
+ $sql = qq{
+ UPDATE $table
+ SET LASTPING = ?,
+ INDEX = ?,
+ CUR_IDX = ?,
+ CUR_CYC = ?,
+ CUR_CONTENT = ?,
+ STATE = ?
+ WHERE ID = "$params{id}"
+ };
+ $sth = $dbh->prepare($sql);
+ $sth->execute($gStartNowStr,
+ $params{index}-1, # (index-1) is complete; (index) in progress
+ ($params{curidx}-1) % $pagedata->length,
+ $params{curcyc},
+ $params{content},
+ 'OPEN'
+ );
+ $sth->finish();
+
+}
+
+
+sub markTestAsComplete {
+ connectToDataBase(); # if not already connected
+ my $table = "tMetaTable";
+ createMetaTable($table); # just returns if already created
+ my ($sth, $sql);
+ #XXX should probably check if this ID exists first
+ $sql = qq{
+ UPDATE $table
+ SET STATE = "COMPLETE"
+ WHERE ID = "$params{id}"
+ };
+ $sth = $dbh->prepare($sql);
+ $sth->execute();
+ $sth->finish();
+}
+
+
+sub initMetaTableRecord {
+ # we know this record doesn't exist, so put in the initial values
+ my $table = shift;
+ my ($sth, $sql);
+ $sql = qq{
+ INSERT INTO $table
+ (DATETIME,
+ LASTPING,
+ ID,
+ INDEX,
+ CUR_IDX,
+ CUR_CYC,
+ CUR_CONTENT,
+ STATE,
+ BLESSED,
+ MAXCYC,
+ MAXIDX,
+ REPLACE,
+ NOCACHE,
+ DELAY,
+ REMOTE_USER,
+ HTTP_USER_AGENT,
+ REMOTE_ADDR,
+ USER_EMAIL,
+ USER_COMMENT
+ )
+ VALUES (?,?,?,?,
+ ?,?,?,?,
+ ?,?,?,?,
+ ?,?,?,?,
+ ?,?,?)
+ };
+ $sth = $dbh->prepare($sql);
+ $sth->execute($gStartNowStr,
+ $gStartNowStr,
+ $params{id},
+ $params{index}-1,
+ ($params{curidx}-1) % $pagedata->length,
+ $params{curcyc},
+ $params{content},
+ "INIT",
+ 0,
+ $params{maxcyc},
+ $params{maxidx},
+ $params{replace},
+ $params{nocache},
+ $params{delay},
+ $cgi->var("REMOTE_USER"),
+ $cgi->var("HTTP_USER_AGENT"),
+ $cgi->var("REMOTE_ADDR"),
+ "",
+ ""
+ );
+ $sth->finish();
+}
+
+
+sub updateDataSetTable {
+ my $table = shift;
+ my $table = "t" . $params{id};
+
+ my ($sth, $sql);
+ $sql = qq{
+ INSERT INTO $table
+ (DATETIME,
+ ID,
+ INDEX,
+ CUR_IDX,
+ CUR_CYC,
+ C_PART,
+ S_INTVL,
+ C_INTVL,
+ CONTENT
+ )
+ VALUES (?,?,?,?,
+ ?,?,?,?,?)
+ };
+
+ my $s_intvl = elapsedMilliSeconds( $gStartNow, $params{s_ts} );
+
+ $sth = $dbh->prepare($sql);
+ $sth->execute($gStartNowStr,
+ $params{id},
+ $params{index}-1,
+ ($params{curidx}-1) % $pagedata->length,
+ $params{curcyc},
+ $params{c_part},
+ $s_intvl,
+ $params{c_intvl},
+ $req->param('content'),
+ );
+ $sth->finish();
+
+}
+
+
+sub outputForm {
+ my @prog = split('/', $0); my $prog = $prog[$#prog];
+ print "Content-type: text/html\n\n";
+ print <<"ENDOFHTML";
+
+
+ Page Loading Times Test
+
+
+ Page Loading Times Test
+
+
+ Last updated: Jan 03, 2001 Questions/Author:
+ John Morrison
+
+
+ \n";
+ return;
+}
+
diff --git a/mozilla/tools/page-loader/report.pl b/mozilla/tools/page-loader/report.pl
new file mode 100755
index 00000000000..41eb5865cc4
--- /dev/null
+++ b/mozilla/tools/page-loader/report.pl
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+use CGI::Carp qw(fatalsToBrowser);
+use CGI::Request;
+use URLTimingDataSet;
+use strict;
+
+my $request = new CGI::Request;
+my $id = $request->param('id'); #XXX need to check for valid parameter id
+
+print "Content-type: text/html\n\n";
+
+print "See Notes at the bottom of this page for some details.
\n";
+print "\n";
+my $rs = URLTimingDataSet->new($id);
+
+print "Test id: $id
Avg. Median : ", $rs->{avgmedian},
+ " msec\t\tMinimum : ", $rs->{minimum}, " msec\n";
+print "Average : ", $rs->{average},
+ " msec\t\tMaximum : ", $rs->{maximum}, " msec
\n\n\n";
+
+#XXX print more info (test id, ua, start time, user, IP, etc.)
+
+# draw the chart sorted
+# XXX enable this line to draw a chart, sorted by time. However, in order
+# to draw the chart, you will need to have installed the 'gd' drawing library,
+# and the GD and GD::Graph Perl modules.
+###print "\n
\n";
+
+
+print "
\nIDX PATH AVG MED MAX MIN TIMES ...\n";
+
+if ($request->param('sort')) {
+ print $rs->as_string_sorted();
+} else {
+ print $rs->as_string();
+}
+print "\n";
+printEndNotes();
+
+exit;
+
+
+sub printEndNotes {
+ print <<"EndOfNotes";
+
+
+
+
+- Times are in milliseconds.
+
+
- AVG, MED, MIN and MAX are the average, median, maximum and
+minimum of the (non-NaN) test results for a given page.
+
+
- If a page fails to fire the onload event within 30 seconds,
+the test for that page is "aborted": a different JS function kicks in,
+cleans up, and reports the time as "NaN". (The rest of the pages in
+the series should still be loaded normally after this).
+
+
- The value for AVG reported for 'All Pages' is the average of
+the averages for all the pages loaded.
+
+
- The value for MAX and MIN reported for 'All Pages' are the
+overall maximum and minimum for all pages loaded (keeping in mind that
+a load that never finishes is recorded as "NaN".)
+
+
- The value for MED reported for 'All Pages' is the _average_ of
+the medians for all the pages loaded (i.e., it is not the median of
+the medians).
+
+
+
+
+EndOfNotes
+}
diff --git a/mozilla/tools/page-loader/urllist.txt b/mozilla/tools/page-loader/urllist.txt
new file mode 100644
index 00000000000..128406acd38
--- /dev/null
+++ b/mozilla/tools/page-loader/urllist.txt
@@ -0,0 +1,82 @@
+#
+# 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 Mozilla page-loader test, released Aug 5, 2001
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 2001 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# John Morrison , original author
+#
+#
+#
+# Config file for page loading test
+#
+# HTTPBASE: is the URL to the root of the content pages
+# FILEBASE: is the file path to the same location (I need both)
+#
+# Remaining lines are the names of top level directories under FILEBASE
+# which contain the content files, followed by an optional filename in
+# that directory (index.html is assumed if no filename given).
+#
+# Warning: you don't want to casually changing the set of urls that you are
+# testing against, if you want to be able to make any reasonable comparison over
+# time. And don't change this file while a test is in progress, as it will competely
+# scramble the results for that test.
+#
+#
+
+HTTPBASE: http://cowtools.mcom.com/perf/content/base/
+FILEBASE: /var/www/html/perf/content/base/
+
+home.netscape.com index.html # optionally specify a filename
+my.netscape.com
+www.aol.com
+www.mapquest.com
+www.moviefone.com
+www.digitalcity.com
+www.iplanet.com
+web.icq.com
+www.compuserve.com
+www.msnbc.com
+www.yahoo.com
+bugzilla.mozilla.org
+www.msn.com
+slashdot.org
+www.nytimes.com
+www.nytimes.com_Table
+www.w3.org_DOML2Core
+lxr.mozilla.org
+espn.go.com
+www.voodooextreme.com
+www.wired.com
+hotwired.lycos.com
+www.ebay.com
+www.apple.com
+www.amazon.com
+www.altavista.com
+www.zdnet.com_Gamespot.com
+www.spinner.com
+www.microsoft.com
+www.time.com
+www.travelocity.com
+www.expedia.com
+www.quicken.com
+www.zdnet.com
+www.excite.com
+www.google.com
+www.tomshardware.com
+www.cnn.com
+news.cnet.com
+www.sun.com