blob: 0f3e44882fe9dffae132d14a9cf794c0702ef9f1 [file] [log] [blame]
njnec0c27a2005-12-10 23:11:28 +00001#! @PERL@
2##--------------------------------------------------------------------##
3##--- Valgrind performance testing script vg_perf ---##
4##--------------------------------------------------------------------##
5
6# This file is part of Valgrind, a dynamic binary instrumentation
7# framework.
8#
9# Copyright (C) 2005 Nicholas Nethercote
10# njn@valgrind.org
11#
12# This program is free software; you can redistribute it and/or
13# modify it under the terms of the GNU General Public License as
14# published by the Free Software Foundation; either version 2 of the
15# License, or (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful, but
18# WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20# General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25# 02111-1307, USA.
26#
27# The GNU General Public License is contained in the file COPYING.
28
29#----------------------------------------------------------------------------
njn57781862005-12-14 02:58:23 +000030# usage: see usage message.
njnec0c27a2005-12-10 23:11:28 +000031#
32# The easiest way is to run all tests in valgrind/ with (assuming you installed
33# in $PREFIX):
34#
35# perl perf/vg_perf --all
36#
37# You can specify individual files to test, or whole directories, or both.
38# Directories are traversed recursively, except for ones named, for example,
39# CVS/ or docs/.
40#
41# Each test is defined in a file <test>.vgperf, containing one or more of the
42# following lines, in any order:
43# - prog: <prog to run> (compulsory)
njnec0c27a2005-12-10 23:11:28 +000044# - args: <args for prog> (default: none)
45# - vgopts: <Valgrind options> (default: none)
46# - prereq: <prerequisite command> (default: none)
47# - cleanup: <post-test cleanup cmd to run> (default: none)
48#
49# The prerequisite command, if present, must return 0 otherwise the test is
50# skipped.
51#----------------------------------------------------------------------------
52
53use warnings;
54use strict;
55
56#----------------------------------------------------------------------------
57# Global vars
58#----------------------------------------------------------------------------
njnee45f1d2005-12-13 21:55:16 +000059my $usage = <<END
60usage: vg_perf [options] [files or dirs]
njnec0c27a2005-12-10 23:11:28 +000061
njnee45f1d2005-12-13 21:55:16 +000062 options for the user, with defaults in [ ], are:
63 -h --help show this message
64 --all run all tests under this directory
njn3fbf17a2006-11-22 00:39:08 +000065 --reps=<n> number of repeats for each program [1]
njn768cd5d2006-11-22 00:52:00 +000066 --tools=<t1,t2,t3> tools to run [Nulgrind and Memcheck]
njn57781862005-12-14 02:58:23 +000067 --vg Valgrind(s) to measure (can be specified multiple
68 times). The "in-place" build is used.
69 [Valgrind in the current directory]
njn768cd5d2006-11-22 00:52:00 +000070
71 Any tools named in --tools must be present in all directories specified
72 with --vg. (This is not checked.)
njnee45f1d2005-12-13 21:55:16 +000073END
74;
njnec0c27a2005-12-10 23:11:28 +000075
76# Test variables
77my $vgopts; # valgrind options
78my $prog; # test prog
79my $args; # test prog args
80my $prereq; # prerequisite test to satisfy before running test
81my $cleanup; # cleanup command to run
njnec0c27a2005-12-10 23:11:28 +000082
njn57781862005-12-14 02:58:23 +000083# Command line options
njn3fbf17a2006-11-22 00:39:08 +000084my $n_reps = 1; # Run each test $n_reps times and choose the best one.
85my @vgdirs; # Dirs of the various Valgrinds being measured.
njn768cd5d2006-11-22 00:52:00 +000086my @tools = ("none", "memcheck"); # tools being measured
njnec0c27a2005-12-10 23:11:28 +000087
88my $num_tests_done = 0;
89my $num_timings_done = 0;
90
91# Starting directory
92chomp(my $tests_dir = `pwd`);
93
njnec0c27a2005-12-10 23:11:28 +000094#----------------------------------------------------------------------------
95# Process command line, setup
96#----------------------------------------------------------------------------
97
98# If $prog is a relative path, it prepends $dir to it. Useful for two reasons:
99#
100# 1. Can prepend "." onto programs to avoid trouble with users who don't have
101# "." in their path (by making $dir = ".")
102# 2. Can prepend the current dir to make the command absolute to avoid
103# subsequent trouble when we change directories.
104#
105# Also checks the program exists and is executable.
106sub validate_program ($$$$)
107{
108 my ($dir, $prog, $must_exist, $must_be_executable) = @_;
109
110 # If absolute path, leave it alone. If relative, make it
111 # absolute -- by prepending current dir -- so we can change
112 # dirs and still use it.
113 $prog = "$dir/$prog" if ($prog !~ /^\//);
114 if ($must_exist) {
115 (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n";
116 }
117 if ($must_be_executable) {
118 (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n";
119 }
120
121 return $prog;
122}
123
njn57781862005-12-14 02:58:23 +0000124sub add_vgdir($)
125{
126 my ($vgdir) = @_;
127 if ($vgdir !~ /^\//) { $vgdir = "$tests_dir/$vgdir"; }
128 validate_program($vgdir, "./coregrind/valgrind", 1, 1);
129 push(@vgdirs, $vgdir);
130}
131
njnec0c27a2005-12-10 23:11:28 +0000132sub process_command_line()
133{
134 my $alldirs = 0;
135 my @fs;
136
137 for my $arg (@ARGV) {
138 if ($arg =~ /^-/) {
139 if ($arg =~ /^--all$/) {
140 $alldirs = 1;
njnb2b4f462005-12-13 22:00:17 +0000141 } elsif ($arg =~ /^--reps=(\d+)$/) {
njnee45f1d2005-12-13 21:55:16 +0000142 $n_reps = $1;
njn30e23ac2005-12-15 17:22:37 +0000143 if ($n_reps < 1) { die "bad --reps value: $n_reps\n"; }
njn57781862005-12-14 02:58:23 +0000144 } elsif ($arg =~ /^--vg=(.+)$/) {
145 # Make dir absolute if not already
146 add_vgdir($1);
njn3fbf17a2006-11-22 00:39:08 +0000147 } elsif ($arg =~ /^--tools=(.+)$/) {
148 @tools = split(/,/, $1);
njnec0c27a2005-12-10 23:11:28 +0000149 } else {
150 die $usage;
151 }
152 } else {
153 push(@fs, $arg);
154 }
155 }
njn57781862005-12-14 02:58:23 +0000156
157 # If no --vg options were specified, use the current tree.
158 if (0 == @vgdirs) {
159 add_vgdir($tests_dir);
160 }
njnec0c27a2005-12-10 23:11:28 +0000161
162 if ($alldirs) {
163 @fs = ();
164 foreach my $f (glob "*") {
165 push(@fs, $f) if (-d $f);
166 }
167 }
168
169 (0 != @fs) or die "No test files or directories specified\n";
170
171 return @fs;
172}
173
174#----------------------------------------------------------------------------
175# Read a .vgperf file
176#----------------------------------------------------------------------------
177sub read_vgperf_file($)
178{
179 my ($f) = @_;
180
181 # Defaults.
182 ($vgopts, $prog, $args, $prereq, $cleanup)
183 = ("", undef, "", undef, undef, undef, undef);
184
185 open(INPUTFILE, "< $f") || die "File $f not openable\n";
186
187 while (my $line = <INPUTFILE>) {
188 if ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
189 next;
190 } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) {
191 $vgopts = $1;
192 } elsif ($line =~ /^\s*prog:\s*(.*)$/) {
njnc9582a22005-12-13 21:44:48 +0000193 $prog = validate_program(".", $1, 1, 1);
njnec0c27a2005-12-10 23:11:28 +0000194 } elsif ($line =~ /^\s*args:\s*(.*)$/) {
195 $args = $1;
196 } elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
197 $prereq = $1;
198 } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) {
199 $cleanup = $1;
200 } else {
201 die "Bad line in $f: $line\n";
202 }
203 }
204 close(INPUTFILE);
205
206 if (!defined $prog) {
207 $prog = ""; # allow no prog for testing error and --help cases
208 }
209 if (0 == @tools) {
210 die "vg_perf: missing 'tools' line in $f\n";
211 }
212}
213
214#----------------------------------------------------------------------------
215# Do one test
216#----------------------------------------------------------------------------
217# Since most of the program time is spent in system() calls, need this to
218# propagate a Ctrl-C enabling us to quit.
219sub mysystem($)
220{
njnecb47442005-12-13 16:54:58 +0000221 my ($cmd) = @_;
222 my $retval = system($cmd);
223 if ($retval == 2) {
224 exit 1;
225 } else {
226 return $retval;
227 }
njnec0c27a2005-12-10 23:11:28 +0000228}
229
sewardjb4860be2006-10-17 02:30:17 +0000230# Run program N times, return the best user time. Use the POSIX
231# -p flag on /usr/bin/time so as to get something parseable on AIX.
njnec0c27a2005-12-10 23:11:28 +0000232sub time_prog($$)
233{
234 my ($cmd, $n) = @_;
235 my $tmin = 999999;
236 for (my $i = 0; $i < $n; $i++) {
njnecb47442005-12-13 16:54:58 +0000237 mysystem("echo '$cmd' > perf.cmd");
238 my $retval = mysystem("$cmd > perf.stdout 2> perf.stderr");
239 (0 == $retval) or
njn57781862005-12-14 02:58:23 +0000240 die "\n*** Command returned non-zero ($retval)"
241 . "\n*** See perf.{cmd,stdout,stderr} to determine what went wrong.\n";
njnecb47442005-12-13 16:54:58 +0000242 my $out = `cat perf.stderr`;
njnae7f6822007-02-02 23:23:01 +0000243 ($out =~ /[Uu]ser +([\d\.]+)/) or
njn02383bc2005-12-13 20:23:38 +0000244 die "\n*** missing usertime in perf.stderr\n";
njnae7f6822007-02-02 23:23:01 +0000245 $tmin = $1 if ($1 < $tmin);
njnec0c27a2005-12-10 23:11:28 +0000246 }
njn30e23ac2005-12-15 17:22:37 +0000247 # Avoid divisions by zero!
248 return (0 == $tmin ? 0.01 : $tmin);
njnec0c27a2005-12-10 23:11:28 +0000249}
250
251sub do_one_test($$)
252{
253 my ($dir, $vgperf) = @_;
254 $vgperf =~ /^(.*)\.vgperf/;
255 my $name = $1;
njn30e23ac2005-12-15 17:22:37 +0000256 my %first_tTool; # For doing percentage speedups when comparing
257 # multiple Valgrinds
njnec0c27a2005-12-10 23:11:28 +0000258
259 read_vgperf_file($vgperf);
260
261 if (defined $prereq) {
262 if (system("$prereq") != 0) {
263 printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:");
264 return;
265 }
266 }
267
sewardjb4860be2006-10-17 02:30:17 +0000268 my $timecmd = "/usr/bin/time -p";
njnec0c27a2005-12-10 23:11:28 +0000269
270 # Do the native run(s).
njn57781862005-12-14 02:58:23 +0000271 printf("-- $name --\n") if (@vgdirs > 1);
njnec0c27a2005-12-10 23:11:28 +0000272 my $cmd = "$timecmd $prog $args";
njnee45f1d2005-12-13 21:55:16 +0000273 my $tNative = time_prog($cmd, $n_reps);
njnec0c27a2005-12-10 23:11:28 +0000274
njn57781862005-12-14 02:58:23 +0000275 foreach my $vgdir (@vgdirs) {
276 # Benchmark name
277 printf("%-8s ", $name);
njnec0c27a2005-12-10 23:11:28 +0000278
njn57781862005-12-14 02:58:23 +0000279 # Print the Valgrind version if we are measuring more than one.
280 my $vgdirname = $vgdir;
281 chomp($vgdirname = `basename $vgdir`);
282 printf("%-10s:", $vgdirname);
283
284 # Native execution time
sewardj8e339a12006-10-14 14:04:42 +0000285 printf("%4.2fs", $tNative);
njnec0c27a2005-12-10 23:11:28 +0000286
njn57781862005-12-14 02:58:23 +0000287 foreach my $tool (@tools) {
njn341f98b2006-11-03 19:37:50 +0000288 # First two chars of toolname for abbreviation
289 my $tool_abbrev = $tool;
290 $tool_abbrev =~ s/(..).*/$1/;
njnec0c27a2005-12-10 23:11:28 +0000291
njn57781862005-12-14 02:58:23 +0000292 # Do the tool run(s). Set both VALGRIND_LIB and VALGRIND_LIB_INNER
njn8c20d872006-04-06 22:59:35 +0000293 # in case this Valgrind was configured with --enable-inner. And
294 # also VALGRINDLIB, which was the old name for the variable, to
295 # allow comparison against old Valgrind versions (eg. 2.4.X).
njn341f98b2006-11-03 19:37:50 +0000296 printf(" %s:", $tool_abbrev);
njn8c20d872006-04-06 22:59:35 +0000297 my $vgsetup = "VALGRINDLIB=$vgdir/.in_place "
298 . "VALGRIND_LIB=$vgdir/.in_place "
njn57781862005-12-14 02:58:23 +0000299 . "VALGRIND_LIB_INNER=$vgdir/.in_place ";
300 my $vgcmd = "$vgdir/coregrind/valgrind "
301 . "--command-line-only=yes --tool=$tool -q "
njn276054a2006-11-03 19:30:33 +0000302 . "--memcheck:leak-check=no "
303 . "--trace-children=yes "
njn57781862005-12-14 02:58:23 +0000304 . "$vgopts ";
305 my $cmd = "$vgsetup $timecmd $vgcmd $prog $args";
306 my $tTool = time_prog($cmd, $n_reps);
njn30e23ac2005-12-15 17:22:37 +0000307 printf("%4.1fs (%4.1fx,", $tTool, $tTool/$tNative);
308
309 # If it's the first timing for this tool on this benchmark,
310 # record the time so we can get the percentage speedup of the
311 # subsequent Valgrinds. Otherwise, compute and print
312 # the speedup.
313 if (not defined $first_tTool{$tool}) {
314 $first_tTool{$tool} = $tTool;
njn715cc592006-03-27 00:39:43 +0000315 print(" -----)");
njn30e23ac2005-12-15 17:22:37 +0000316 } else {
317 my $speedup = 100 - (100 * $tTool / $first_tTool{$tool});
njn715cc592006-03-27 00:39:43 +0000318 printf("%5.1f%%)", $speedup);
njn30e23ac2005-12-15 17:22:37 +0000319 }
njn57781862005-12-14 02:58:23 +0000320
321 $num_timings_done++;
322
323 if (defined $cleanup) {
324 (system("$cleanup") == 0) or
325 print(" ($name cleanup operation failed: $cleanup)\n");
326 }
327 }
328 printf("\n");
njnec0c27a2005-12-10 23:11:28 +0000329 }
330
331 $num_tests_done++;
332}
333
334#----------------------------------------------------------------------------
335# Test one directory (and any subdirs)
336#----------------------------------------------------------------------------
337sub test_one_dir($$); # forward declaration
338
339sub test_one_dir($$)
340{
341 my ($dir, $prev_dirs) = @_;
342 $dir =~ s/\/$//; # trim a trailing '/'
343
njn356ffa32006-11-03 19:35:20 +0000344 chomp(my $initial_dir = `pwd`); # record where we started
345
njnec0c27a2005-12-10 23:11:28 +0000346 # Ignore dirs into which we should not recurse.
347 if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; }
348
349 chdir($dir) or die "Could not change into $dir\n";
350
351 # Nb: Don't prepend a '/' to the base directory
352 my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir;
353 my $dashes = "-" x (50 - length $full_dir);
354
355 my @fs = glob "*";
356 my $found_tests = (0 != (grep { $_ =~ /\.vgperf$/ } @fs));
357
358 if ($found_tests) {
359 print "-- Running tests in $full_dir $dashes\n";
360 }
361 foreach my $f (@fs) {
362 if (-d $f) {
363 test_one_dir($f, $full_dir);
364 } elsif ($f =~ /\.vgperf$/) {
365 do_one_test($full_dir, $f);
366 }
367 }
368 if ($found_tests) {
369 print "-- Finished tests in $full_dir $dashes\n";
370 }
371
njn356ffa32006-11-03 19:35:20 +0000372 chdir("$initial_dir");
njnec0c27a2005-12-10 23:11:28 +0000373}
374
375#----------------------------------------------------------------------------
376# Summarise results
377#----------------------------------------------------------------------------
378sub summarise_results
379{
380 printf("\n== %d programs, %d timings =================\n\n",
381 $num_tests_done, $num_timings_done);
382}
383
384#----------------------------------------------------------------------------
385# main()
386#----------------------------------------------------------------------------
387
388# nuke VALGRIND_OPTS
389$ENV{"VALGRIND_OPTS"} = "";
390
391my @fs = process_command_line();
392foreach my $f (@fs) {
393 if (-d $f) {
394 test_one_dir($f, "");
395 } else {
396 # Allow the .vgperf suffix to be given or omitted
397 if ($f =~ /.vgperf$/ && -r $f) {
398 # do nothing
399 } elsif (-r "$f.vgperf") {
400 $f = "$f.vgperf";
401 } else {
402 die "`$f' neither a directory nor a readable test file/name\n"
403 }
404 my $dir = `dirname $f`; chomp $dir;
405 my $file = `basename $f`; chomp $file;
406 chdir($dir) or die "Could not change into $dir\n";
407 do_one_test($dir, $file);
408 chdir($tests_dir);
409 }
410}
411summarise_results();
412
413##--------------------------------------------------------------------##
414##--- end ---##
415##--------------------------------------------------------------------##