2008-09-09 Cameron Zwarich <cwzwarich@uwaterloo.ca>
[WebKit-https.git] / SunSpider / sunspider
1 #!/usr/bin/perl -w
2
3 # Copyright (C) 2007 Apple Inc. All rights reserved.
4 # Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26
27 use strict;
28 use Getopt::Long;
29 use File::Basename;
30 use File::Spec;
31 use Cwd;
32 use POSIX qw(strftime);
33 use Time::HiRes qw(gettimeofday tv_interval);
34
35 my $showHelp = 0;
36 my $runShark = 0;
37 my $runShark20 = 0;
38 my $runSharkCache = 0;
39 my $ubench = 0;
40 my $v8suite = 0;
41 my $jsShellPath;
42 my $jsShellArgs = "";
43 my $setBaseline = 0;
44 my $testsPattern;
45 my $testRuns = 10;
46
47 my $programName = basename($0);
48 my $usage = <<EOF;
49 Usage: $programName --shell=[path] [options]
50   --help            Show this help message
51   --set-baseline    Set baseline for future comparisons
52   --shell           Path to JavaScript shell
53   --args            Arguments to pass to JavaScript shell
54   --runs            Number of times to run tests (default: $testRuns)
55   --tests           Only run tests matching provided pattern
56   --shark           Sample execution time with the Mac OS X "Shark" performance testing tool (implies --runs=1)
57   --shark20         Like --shark, but with a 20 microsecond sampling interval
58   --shark-cache     Like --shark, but performs a L2 cache-miss sample instead of time sample
59   --ubench          Use microbenchmark suite instead of regular tests (to check for core execution regressions)
60   --v8-suite        Use the V8 benchmark suite
61 EOF
62
63 GetOptions('runs=i' => \$testRuns,
64            'set-baseline' => \$setBaseline,
65            'shell=s' => \$jsShellPath,
66            'args=s' => \$jsShellArgs,
67            'shark' => \$runShark,
68            'shark20' => \$runShark20,
69            'shark-cache' => \$runSharkCache,
70            'ubench' => \$ubench,
71            'v8-suite' => \$v8suite,
72            'tests=s' => \$testsPattern,
73            'help' => \$showHelp);
74
75 $runShark = 1 if $runSharkCache;
76 $runShark = 20 if $runShark20;
77 $testRuns = 1 if $runShark;
78 if ($runShark && ! -x "/usr/bin/shark") {
79     die "Please install CHUD tools from http://developer.apple.com/tools/download/\n";
80 }
81
82 my $sharkCacheProfileIndex = 0;
83 if ($runSharkCache) {
84     my $sharkProfileList = `shark -l 2>&1`;
85     for my $profile (split(/\n/, $sharkProfileList)) {
86         $profile =~ /(\d+) - (.+)/;
87         next  unless (defined $1);
88         my $profileIndex = $1;
89         my $profileName = $2;
90         if ($profileName =~ /L2 Cache/) {
91             $sharkCacheProfileIndex = $profileIndex;
92             print "Using Shark L2 Cache Miss Profile: " . $profile . "\n";
93             last;
94         }
95     }
96     die "Failed to find L2 Cache Miss Profile for --shark-cache\n"  unless ($sharkCacheProfileIndex);
97 }
98
99 if (!$jsShellPath || $showHelp) {
100    print STDERR $usage;
101    exit 1;
102 }
103
104 sub dumpToFile($$)
105 {
106     my ($contents, $path) = @_;
107     open FILE, ">", $path or die "Failed to open $path";
108     print FILE $contents;
109     close FILE;
110 }
111
112 my @tests = ();
113 my @categories = ();
114 my %uniqueCategories = ();
115
116 sub loadTestsList()
117 {
118     my $testlist = "LIST";
119     $testlist = "LIST-UBENCH" if ($ubench);
120     $testlist = "LIST-V8" if ($v8suite);
121
122     open TESTLIST, "<", "tests/${testlist}" or die "Can't find ./tests/LIST";
123     while (<TESTLIST>) {
124         chomp;
125         next unless !$testsPattern || /$testsPattern/;
126         
127         push @tests, $_;
128         my $category = $_;
129         $category =~ s/-.*//;
130         if (!$uniqueCategories{$category}) {
131             push @categories, $category;
132             $uniqueCategories{$category} = $category;
133         }
134     }
135     close TESTLIST;
136 }
137
138 my $timeString = strftime "%Y-%m-%d-%H.%M.%S", localtime $^T;
139 my $prefixFile = "tmp/sunspider-test-prefix.js";
140 my $resultsFile = "tmp/sunspider-results-$timeString.js";
141
142 sub writePrefixFile()
143 {
144     my $prefix = "var tests = [ " . join(", ", map { '"' . $_ . '"' } @tests) . " ];\n";
145     $prefix .= "var categories = [ " . join(", ", map { '"' . $_ . '"' } @categories) . " ];\n";
146
147     mkdir "tmp";
148     dumpToFile($prefix, $prefixFile);
149 }
150
151 sub runTestsOnce($)
152 {
153     my ($useShark) = @_;
154     my $shellArgs = $jsShellArgs . " -f $prefixFile -f resources/sunspider-standalone-driver.js 2> /dev/null";
155     my $output;
156     if ($useShark) {
157         my $intervalArg = $useShark == 20 ? "-I 20u" : "";
158         my $cacheArg = $runSharkCache ? "-c $sharkCacheProfileIndex" : "";
159         $output = `shark $intervalArg $cacheArg -i -1-q "$jsShellPath" $shellArgs`;
160     } else {
161         $output = `"$jsShellPath" $shellArgs | grep -v break`;
162     }
163     return $output;
164 }
165
166 sub newestFile($$)
167 {
168     my ($dir, $pattern) = @_;
169
170     my $newestAge;
171     my $newestFile = "";
172     opendir DIR, $dir or die;
173     for my $file (readdir DIR) {
174         if ($file =~ $pattern) {
175             my $age = -M "$dir/$file";
176             if (!defined $newestAge || $age < $newestAge) {
177                 $newestFile = $file;
178                 $newestAge = $age;
179             }
180         }
181     }
182     closedir DIR;
183
184     return "$dir/$newestFile";
185 }
186
187 loadTestsList();
188 if ($testsPattern) {
189     print STDERR "Found " . scalar(@tests) . " tests matching '" . $testsPattern . "'\n";
190 } else {
191     print STDERR "Found " . scalar(@tests) . " tests\n";
192 }
193 die "No tests to run"  unless scalar(@tests);
194 print STDERR "Running SunSpider once for warmup, then " . ($runShark ? "under Shark" : "$testRuns time" . ($testRuns == 1 ? "" : "s")) . "\n";
195 writePrefixFile();
196
197 runTestsOnce(0);
198 print "Discarded first run.\n";
199
200 my $result;
201 my $count = 0;
202 my @results = ();
203 my $total = 0;
204 print "[";
205 while ($count++ < $testRuns) {
206     $result = runTestsOnce($runShark);
207     $result =~ s/\r\n/\n/g;
208     chomp $result;
209     push @results, $result;
210     print $result;
211     print ",\n" unless ($count == $testRuns);
212 }
213 print "]\n";
214
215 my $output = "var output = [\n" . join(",\n", @results) . "\n];\n";
216 dumpToFile($output, $resultsFile);
217 dumpToFile(File::Spec->rel2abs($resultsFile), "tmp/baseline-filename.txt") if $setBaseline;
218
219 system("$jsShellPath", "-f", $prefixFile, "-f", $resultsFile, "-f", "resources/sunspider-analyze-results.js");
220
221 if ($runShark) {
222     my $newestMShark = newestFile(".", qr/\.mshark$/);
223     if ($newestMShark) {
224         my $profileFile = "tmp/sunspider-profile-$timeString.mshark";
225         rename $newestMShark, $profileFile or die;
226         exec "/usr/bin/open", $profileFile;
227     }
228 }