|
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 $suite = ""; |
|
42 my $parseOnly = 0; |
|
43 my $jsShellPath; |
|
44 my $jsShellArgs = ""; |
|
45 my $setBaseline = 0; |
|
46 my $testsPattern; |
|
47 my $testRuns = 10; |
|
48 |
|
49 my $programName = basename($0); |
|
50 my $usage = <<EOF; |
|
51 Usage: $programName --shell=[path] [options] |
|
52 --help Show this help message |
|
53 --set-baseline Set baseline for future comparisons |
|
54 --shell Path to JavaScript shell |
|
55 --args Arguments to pass to JavaScript shell |
|
56 --runs Number of times to run tests (default: $testRuns) |
|
57 --tests Only run tests matching provided pattern |
|
58 --shark Sample execution time with the Mac OS X "Shark" performance testing tool (implies --runs=1) |
|
59 --shark20 Like --shark, but with a 20 microsecond sampling interval |
|
60 --shark-cache Like --shark, but performs a L2 cache-miss sample instead of time sample |
|
61 --suite Select a specific benchmark suite. The default is sunspider-0.9.1 |
|
62 --ubench Use microbenchmark suite instead of regular tests. Same as --suite=ubench |
|
63 --v8-suite Use the V8 benchmark suite. Same as --suite=v8-v4 |
|
64 --parse-only Use the parse-only benchmark suite. Same as --suite=parse-only |
|
65 EOF |
|
66 |
|
67 GetOptions('runs=i' => \$testRuns, |
|
68 'set-baseline' => \$setBaseline, |
|
69 'shell=s' => \$jsShellPath, |
|
70 'args=s' => \$jsShellArgs, |
|
71 'shark' => \$runShark, |
|
72 'shark20' => \$runShark20, |
|
73 'shark-cache' => \$runSharkCache, |
|
74 'suite=s' => \$suite, |
|
75 'ubench' => \$ubench, |
|
76 'v8-suite' => \$v8suite, |
|
77 'parse-only' => \$parseOnly, |
|
78 'tests=s' => \$testsPattern, |
|
79 'help' => \$showHelp); |
|
80 |
|
81 |
|
82 $suite = "ubench" if ($ubench); |
|
83 $suite = "v8-v4" if ($v8suite); |
|
84 $suite = "parse-only" if ($parseOnly); |
|
85 $suite = "sunspider-0.9.1" if (!$suite); |
|
86 |
|
87 my $resultDirectory = "${suite}-results"; |
|
88 |
|
89 $runShark = 1 if $runSharkCache; |
|
90 $runShark = 20 if $runShark20; |
|
91 $testRuns = 1 if $runShark; |
|
92 if ($runShark && ! -x "/usr/bin/shark") { |
|
93 die "Please install CHUD tools from http://developer.apple.com/tools/download/\n"; |
|
94 } |
|
95 |
|
96 my $sharkCacheProfileIndex = 0; |
|
97 if ($runSharkCache) { |
|
98 my $sharkProfileList = `shark -l 2>&1`; |
|
99 for my $profile (split(/\n/, $sharkProfileList)) { |
|
100 $profile =~ /(\d+) - (.+)/; |
|
101 next unless (defined $1); |
|
102 my $profileIndex = $1; |
|
103 my $profileName = $2; |
|
104 if ($profileName =~ /L2 Cache/) { |
|
105 $sharkCacheProfileIndex = $profileIndex; |
|
106 print "Using Shark L2 Cache Miss Profile: " . $profile . "\n"; |
|
107 last; |
|
108 } |
|
109 } |
|
110 die "Failed to find L2 Cache Miss Profile for --shark-cache\n" unless ($sharkCacheProfileIndex); |
|
111 } |
|
112 |
|
113 if (!$jsShellPath || $showHelp) { |
|
114 print STDERR $usage; |
|
115 exit 1; |
|
116 } |
|
117 |
|
118 sub dumpToFile($$) |
|
119 { |
|
120 my ($contents, $path) = @_; |
|
121 open FILE, ">", $path or die "Failed to open $path"; |
|
122 print FILE $contents; |
|
123 close FILE; |
|
124 } |
|
125 |
|
126 my @tests = (); |
|
127 my @categories = (); |
|
128 my %uniqueCategories = (); |
|
129 |
|
130 sub loadTestsList() |
|
131 { |
|
132 open TESTLIST, "<", "tests/${suite}/LIST" or die "Can't find ./tests/${suite}/LIST"; |
|
133 while (<TESTLIST>) { |
|
134 chomp; |
|
135 next unless !$testsPattern || /$testsPattern/; |
|
136 |
|
137 push @tests, $_; |
|
138 my $category = $_; |
|
139 $category =~ s/-.*//; |
|
140 if (!$uniqueCategories{$category}) { |
|
141 push @categories, $category; |
|
142 $uniqueCategories{$category} = $category; |
|
143 } |
|
144 } |
|
145 close TESTLIST; |
|
146 } |
|
147 |
|
148 my $timeString = strftime "%Y-%m-%d-%H.%M.%S", localtime $^T; |
|
149 my $prefixFile = "$resultDirectory/sunspider-test-prefix.js"; |
|
150 my $resultsFile = "$resultDirectory/sunspider-results-$timeString.js"; |
|
151 |
|
152 sub writePrefixFile() |
|
153 { |
|
154 my $prefix = "var suiteName = " . '"' . $suite . '"' . ";\n"; |
|
155 $prefix .= "var tests = [ " . join(", ", map { '"' . $_ . '"' } @tests) . " ];\n"; |
|
156 $prefix .= "var categories = [ " . join(", ", map { '"' . $_ . '"' } @categories) . " ];\n"; |
|
157 |
|
158 mkdir "$resultDirectory"; |
|
159 dumpToFile($prefix, $prefixFile); |
|
160 } |
|
161 |
|
162 sub runTestsOnce($) |
|
163 { |
|
164 my ($useShark) = @_; |
|
165 my $shellArgs = $jsShellArgs . " -f $prefixFile -f resources/sunspider-standalone-driver.js 2> " . File::Spec->devnull(); |
|
166 my $output; |
|
167 if ($useShark) { |
|
168 my $intervalArg = $useShark == 20 ? "-I 20u" : ""; |
|
169 my $cacheArg = $runSharkCache ? "-c $sharkCacheProfileIndex" : ""; |
|
170 $output = `shark $intervalArg $cacheArg -i -1-q "$jsShellPath" $shellArgs`; |
|
171 } else { |
|
172 $output = `"$jsShellPath" $shellArgs | grep -v break`; |
|
173 } |
|
174 return $output; |
|
175 } |
|
176 |
|
177 sub newestFile($$) |
|
178 { |
|
179 my ($dir, $pattern) = @_; |
|
180 |
|
181 my $newestAge; |
|
182 my $newestFile = ""; |
|
183 opendir DIR, $dir or die; |
|
184 for my $file (readdir DIR) { |
|
185 if ($file =~ $pattern) { |
|
186 my $age = -M "$dir/$file"; |
|
187 if (!defined $newestAge || $age < $newestAge) { |
|
188 $newestFile = $file; |
|
189 $newestAge = $age; |
|
190 } |
|
191 } |
|
192 } |
|
193 closedir DIR; |
|
194 |
|
195 return "$dir/$newestFile"; |
|
196 } |
|
197 |
|
198 loadTestsList(); |
|
199 if ($testsPattern) { |
|
200 print STDERR "Found " . scalar(@tests) . " tests matching '" . $testsPattern . "'\n"; |
|
201 } else { |
|
202 print STDERR "Found " . scalar(@tests) . " tests\n"; |
|
203 } |
|
204 die "No tests to run" unless scalar(@tests); |
|
205 print STDERR "Running SunSpider once for warmup, then " . ($runShark ? "under Shark" : "$testRuns time" . ($testRuns == 1 ? "" : "s")) . "\n"; |
|
206 writePrefixFile(); |
|
207 |
|
208 runTestsOnce(0); |
|
209 print "Discarded first run.\n"; |
|
210 |
|
211 my $result; |
|
212 my $count = 0; |
|
213 my @results = (); |
|
214 my $total = 0; |
|
215 print "["; |
|
216 while ($count++ < $testRuns) { |
|
217 $result = runTestsOnce($runShark); |
|
218 $result =~ s/\r\n/\n/g; |
|
219 chomp $result; |
|
220 push @results, $result; |
|
221 print $result; |
|
222 print ",\n" unless ($count == $testRuns); |
|
223 } |
|
224 print "]\n"; |
|
225 |
|
226 my $output = "var output = [\n" . join(",\n", @results) . "\n];\n"; |
|
227 dumpToFile($output, $resultsFile); |
|
228 dumpToFile(File::Spec->rel2abs($resultsFile), "$resultDirectory/baseline-filename.txt") if $setBaseline; |
|
229 |
|
230 system("$jsShellPath", "-f", $prefixFile, "-f", $resultsFile, "-f", "resources/sunspider-analyze-results.js"); |
|
231 |
|
232 print("\nResults are located at $resultsFile\n"); |
|
233 |
|
234 if ($runShark) { |
|
235 my $newestMShark = newestFile(".", qr/\.mshark$/); |
|
236 if ($newestMShark) { |
|
237 my $profileFile = "$resultDirectory/sunspider-profile-$timeString.mshark"; |
|
238 rename $newestMShark, $profileFile or die; |
|
239 exec "/usr/bin/open", $profileFile; |
|
240 } |
|
241 } |