tools/page-loader/dump.pl

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rwxr-xr-x

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 #!/usr/bin/perl
michael@0 2 #
michael@0 3 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6 use DBI;
michael@0 7 use CGI::Carp qw(fatalsToBrowser);
michael@0 8 use CGI::Request;
michael@0 9 use URLTimingDataSet;
michael@0 10 use File::Copy ();
michael@0 11 use strict;
michael@0 12
michael@0 13 use vars qw($dbh $arc $dbroot); # current db, and db/archive
michael@0 14
michael@0 15 use constant STALE_AGE => 5 * 60; # seconds
michael@0 16
michael@0 17 # show a chart of this run; turned off in automated tests, and where
michael@0 18 # an installation hasn't set up the required modules and libraries
michael@0 19 use constant SHOW_CHART => 0;
michael@0 20
michael@0 21 sub createArchiveMetaTable {
michael@0 22 my $table = "tMetaTable";
michael@0 23 return if -e "$dbroot/archive/$table"; # don't create it if it exists
michael@0 24 warn "createMetaTable:\t$dbroot/archive/$table";
michael@0 25 mkdir "$dbroot/archive" unless -d "$dbroot/archive";
michael@0 26 my ($sth, $sql);
michael@0 27 $sql = qq{
michael@0 28 CREATE TABLE tMetaTable
michael@0 29 (DATETIME CHAR(14), LASTPING CHAR(14),
michael@0 30 ID CHAR(8), INDEX INTEGER,
michael@0 31 CUR_IDX INTEGER, CUR_CYC INTEGER,
michael@0 32 CUR_CONTENT CHAR(128), STATE INTEGER,
michael@0 33 BLESSED INTEGER, MAXCYC INTEGER,
michael@0 34 MAXIDX INTEGER, REPLACE INTEGER,
michael@0 35 NOCACHE INTEGER, DELAY INTEGER,
michael@0 36 REMOTE_USER CHAR(16), HTTP_USER_AGENT CHAR(128),
michael@0 37 REMOTE_ADDR CHAR(15), USER_EMAIL CHAR(32),
michael@0 38 USER_COMMENT CHAR(256)
michael@0 39 )
michael@0 40 };
michael@0 41 $sth = $arc->prepare($sql);
michael@0 42 $sth->execute();
michael@0 43 $sth->finish();
michael@0 44 warn 'created archive meta table';
michael@0 45 return 1;
michael@0 46 }
michael@0 47
michael@0 48
michael@0 49 sub purgeStaleEntries {
michael@0 50 my $id = shift;
michael@0 51 my $metatable = "tMetaTable";
michael@0 52
michael@0 53 # first, remove dead stuff
michael@0 54 my $sql = qq{SELECT * FROM $metatable
michael@0 55 WHERE STATE = "INIT" OR STATE = "OPEN"};
michael@0 56 my $sth = $dbh->prepare($sql);
michael@0 57 $sth->execute();
michael@0 58 my $now = time();
michael@0 59 my $status;
michael@0 60 while (my @data = $sth->fetchrow_array()) {
michael@0 61 my $age = $now - timestamp2Time($data[1]);
michael@0 62 # if OPEN or INIT, and not heard from in 10 minutes, then it's never coming
michael@0 63 # back here again. Delete the entry. Whine in the error_log.
michael@0 64 if ($age > STALE_AGE) {
michael@0 65 warn "deleting stale record+table, id = $data[2], last = $data[1], @data";
michael@0 66 $dbh->do( qq(DELETE FROM $metatable WHERE ID = "$data[2]") );
michael@0 67 $dbh->do("DROP TABLE t" . $data[2]);
michael@0 68 }
michael@0 69 $status .= "$age @data\n";
michael@0 70 }
michael@0 71 $sth->finish();
michael@0 72
michael@0 73 # now move any COMPLETE records to archive
michael@0 74 $sql = qq{SELECT * FROM $metatable};
michael@0 75 $sth = $dbh->prepare($sql);
michael@0 76 $sth->execute();
michael@0 77 $now = time();
michael@0 78 while (my @data = $sth->fetchrow_array()) {
michael@0 79 my $age = $now - timestamp2Time($data[1]);
michael@0 80 # This keeps the "live" entries from growing too slow.
michael@0 81 # If COMPLETE and older than 10 minutes, move to archive.
michael@0 82 if ($age > STALE_AGE) {
michael@0 83 warn "moving COMPLETE record+table, id = $data[2], last = $data[1], @data";
michael@0 84 moveRecordToArchive($data[2], \@data);
michael@0 85 $dbh->do( qq(DELETE FROM $metatable WHERE ID = "$data[2]") );
michael@0 86 }
michael@0 87 }
michael@0 88 $sth->finish();
michael@0 89
michael@0 90
michael@0 91 if (!SHOW_CHART) {
michael@0 92 # Don't move it if showing a chart. (Otherwise, if showing a
michael@0 93 # a chart, I'd have to do a little extra work to make sure I
michael@0 94 # didn't yank the record away from the IMG request)
michael@0 95 $sql = qq{SELECT * FROM $metatable WHERE ID = "$id"};
michael@0 96 $sth = $dbh->prepare($sql);
michael@0 97 $sth->execute();
michael@0 98 while (my @data = $sth->fetchrow_array()) {
michael@0 99 warn "moving COMPLETE record+table, id = $id, @data\n";
michael@0 100 moveRecordToArchive($data[2], \@data);
michael@0 101 $dbh->do( qq(DELETE FROM $metatable WHERE ID = "$data[2]") );
michael@0 102 }
michael@0 103 }
michael@0 104 $sth->finish();
michael@0 105 }
michael@0 106
michael@0 107
michael@0 108 sub moveRecordToArchive {
michael@0 109 my $id = shift || die "no id";
michael@0 110 my $dataref = shift || die "no dataref";
michael@0 111 createArchiveMetaTable(); # if it doesn't exist
michael@0 112 insertIntoMetaTable($dataref);
michael@0 113 File::Copy::move("$dbroot/t$id", "$dbroot/archive/t$id");
michael@0 114 }
michael@0 115
michael@0 116
michael@0 117 sub insertIntoMetaTable {
michael@0 118 my $dataref = shift || die "no dataref";
michael@0 119 my $table = "tMetaTable";
michael@0 120 my ($sth, $sql);
michael@0 121 $sql = qq{
michael@0 122 INSERT INTO $table
michael@0 123 (DATETIME, LASTPING, ID,
michael@0 124 INDEX, CUR_IDX, CUR_CYC,
michael@0 125 CUR_CONTENT, STATE, BLESSED,
michael@0 126 MAXCYC, MAXIDX, REPLACE,
michael@0 127 NOCACHE, DELAY, REMOTE_USER,
michael@0 128 HTTP_USER_AGENT, REMOTE_ADDR, USER_EMAIL,
michael@0 129 USER_COMMENT
michael@0 130 )
michael@0 131 VALUES (?,?,?,?,
michael@0 132 ?,?,?,?,
michael@0 133 ?,?,?,?,
michael@0 134 ?,?,?,?,
michael@0 135 ?,?,?)
michael@0 136 };
michael@0 137 $sth = $arc->prepare($sql);
michael@0 138 $sth->execute(@$dataref);
michael@0 139 $sth->finish();
michael@0 140 }
michael@0 141
michael@0 142
michael@0 143 sub timestamp2Time ($) {
michael@0 144 my $str = shift;
michael@0 145 use Time::Local ();
michael@0 146 my @datetime = reverse unpack 'A4A2A2A2A2A2', $str;
michael@0 147 --$datetime[4]; # month: 0-11
michael@0 148 return Time::Local::timelocal(@datetime);
michael@0 149 }
michael@0 150
michael@0 151
michael@0 152 sub serializeDataSet {
michael@0 153 # package up this data for storage elsewhere
michael@0 154 my $rs = shift;
michael@0 155 my $data = "avgmedian|" . $rs->{avgmedian};
michael@0 156 $data .= "|average|" . $rs->{average};
michael@0 157 $data .= "|minimum|" . $rs->{minimum};
michael@0 158 $data .= "|maximum|" . $rs->{maximum};
michael@0 159 $_ = $rs->as_string;
michael@0 160 s/^\s+//gs;
michael@0 161 s/\s+\n$//gs;
michael@0 162 s/\s*\n/\|/gs; # fold newlines
michael@0 163 s/\|\s+/\|/gs;
michael@0 164 s/\s+/;/gs;
michael@0 165 return $data . ":" . $_;
michael@0 166 }
michael@0 167
michael@0 168 #
michael@0 169 # handle the request
michael@0 170 #
michael@0 171 my $request = new CGI::Request;
michael@0 172 my $id = $request->param('id'); #XXX need to check for valid parameter id
michael@0 173 my $rs = URLTimingDataSet->new($id);
michael@0 174
michael@0 175 print "Content-type: text/html\n\n";
michael@0 176
michael@0 177 # This sucks: we'll let the test time out to avoid crash-on-shutdown bugs
michael@0 178 print "<html><body onload='window.close();'>";
michael@0 179 #
michael@0 180 # dump some stats for tinderbox to snarf
michael@0 181 #
michael@0 182 print "<script>\n";
michael@0 183 print "if (window.dump) dump('";
michael@0 184 print "Starting Page Load Test\\n\\\n";
michael@0 185 print "Test id: $id\\n\\\n";
michael@0 186 print "Avg. Median : ", $rs->{avgmedian}, " msec\\n\\\n";
michael@0 187 print "Average : ", $rs->{average}, " msec\\n\\\n";
michael@0 188 print "Minimum : ", $rs->{minimum}, " msec\\n\\\n";
michael@0 189 print "Maximum : ", $rs->{maximum}, " msec\\n\\\n";
michael@0 190 print "IDX PATH AVG MED MAX MIN TIMES ...\\n\\\n";
michael@0 191 if ($request->param('sort')) {
michael@0 192 $_ = $rs->as_string_sorted();
michael@0 193 } else {
michael@0 194 $_ = $rs->as_string();
michael@0 195 }
michael@0 196 #
michael@0 197 # Terminate raw newlines with '\n\' so we don't have an unterminated string literal.
michael@0 198 #
michael@0 199 s/\n/\\n\\\n/g;
michael@0 200 print $_;
michael@0 201 print "(tinderbox dropping follows)\\n\\\n";
michael@0 202 print "_x_x_mozilla_page_load," , $rs->{avgmedian}, ",", $rs->{maximum}, ",", $rs->{minimum}, "\\n\\\n";
michael@0 203 #
michael@0 204 # package up this data for storage elsewhere
michael@0 205 #
michael@0 206 my $data = serializeDataSet($rs);
michael@0 207 print "_x_x_mozilla_page_load_details,", $data, "\\n\\\n";
michael@0 208 #
michael@0 209 # average median
michael@0 210 #
michael@0 211 #print "TinderboxPrint:<a title=\"Avg. of the median per url pageload time.\" href=\"http://tegu.mozilla.org/graph/query.cgi?tbox=spider&testname=pageload&autoscale=1&days=7&avg=1\">Tp:", $rs->{avgmedian}, "ms</a>", "\\n\\\n";
michael@0 212 print "');";
michael@0 213 print "</script></body></html>\n";
michael@0 214
michael@0 215
michael@0 216 #
michael@0 217 # If this is SurfingSafari, then catch a wave and you're sitting on top of the world!!
michael@0 218 # (and also blat this out to tegu, cause we got no 'dump' statement.
michael@0 219 #
michael@0 220 if ($request->cgi->var("HTTP_USER_AGENT") =~ /Safari/) {
michael@0 221 my %machineMap =
michael@0 222 (
michael@0 223 "10.169.105.26" => "boxset",
michael@0 224 "10.169.105.21" => "pawn"
michael@0 225 );
michael@0 226 my $ip = $request->cgi->var('REMOTE_ADDR');
michael@0 227 my $machine = $machineMap{$ip};
michael@0 228 my $res = eval q{
michael@0 229 use LWP::UserAgent;
michael@0 230 use HTTP::Request::Common qw(POST);
michael@0 231 my $ua = LWP::UserAgent->new;
michael@0 232 $ua->timeout(10); # seconds
michael@0 233 my $req = POST('http://tegu.mozilla.org/graph/collect.cgi',
michael@0 234 [testname => 'pageload',
michael@0 235 tbox => "$machine" . "-aux",
michael@0 236 value => $rs->{avgmedian},
michael@0 237 data => $data]);
michael@0 238 my $res = $ua->request($req);
michael@0 239 return $res;
michael@0 240 };
michael@0 241 if ($@) {
michael@0 242 warn "Failed to submit startup results: $@";
michael@0 243 } else {
michael@0 244 warn "Startup results submitted to server: \n",
michael@0 245 $res->status_line, "\n", $res->content, "\n";
michael@0 246 }
michael@0 247 }
michael@0 248
michael@0 249
michael@0 250 if ($request->param('purge')) {
michael@0 251 # now move any old stuff into archive and clean stale entries
michael@0 252 # just going with the simple approach of "whoever sees old entries
michael@0 253 # first, cleans em up, whether they 'own' them or not". Hopefully,
michael@0 254 # the default locking will be sufficient to prevent a race.
michael@0 255 close(STDOUT);
michael@0 256 sleep(1);
michael@0 257 $dbroot = "db";
michael@0 258 $dbh = DBI->connect("DBI:CSV:f_dir=./$dbroot",
michael@0 259 {RaiseError => 1, AutoCommit => 1})
michael@0 260 || die "Cannot connect: " . $DBI::errstr;
michael@0 261 $arc = DBI->connect("DBI:CSV:f_dir=./$dbroot/archive",
michael@0 262 {RaiseError => 1, AutoCommit => 1})
michael@0 263 || die "Cannot connect: " . $DBI::errstr;
michael@0 264 purgeStaleEntries($id);
michael@0 265 $dbh->disconnect();
michael@0 266 $arc->disconnect();
michael@0 267 }
michael@0 268
michael@0 269 exit 0;

mercurial