blob: 2188a31ed6786541bfa80c5875aa239c1f4240de [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#
njnec0c27a2005-12-10 23:11:28 +000032# You can specify individual files to test, or whole directories, or both.
33# Directories are traversed recursively, except for ones named, for example,
34# CVS/ or docs/.
35#
36# Each test is defined in a file <test>.vgperf, containing one or more of the
37# following lines, in any order:
38# - prog: <prog to run> (compulsory)
njnec0c27a2005-12-10 23:11:28 +000039# - args: <args for prog> (default: none)
40# - vgopts: <Valgrind options> (default: none)
41# - prereq: <prerequisite command> (default: none)
42# - cleanup: <post-test cleanup cmd to run> (default: none)
43#
44# The prerequisite command, if present, must return 0 otherwise the test is
45# skipped.
46#----------------------------------------------------------------------------
47
48use warnings;
49use strict;
50
51#----------------------------------------------------------------------------
52# Global vars
53#----------------------------------------------------------------------------
njnee45f1d2005-12-13 21:55:16 +000054my $usage = <<END
55usage: vg_perf [options] [files or dirs]
njnec0c27a2005-12-10 23:11:28 +000056
njnee45f1d2005-12-13 21:55:16 +000057 options for the user, with defaults in [ ], are:
58 -h --help show this message
njn3fbf17a2006-11-22 00:39:08 +000059 --reps=<n> number of repeats for each program [1]
njn768cd5d2006-11-22 00:52:00 +000060 --tools=<t1,t2,t3> tools to run [Nulgrind and Memcheck]
njn57781862005-12-14 02:58:23 +000061 --vg Valgrind(s) to measure (can be specified multiple
62 times). The "in-place" build is used.
63 [Valgrind in the current directory]
njn768cd5d2006-11-22 00:52:00 +000064
65 Any tools named in --tools must be present in all directories specified
66 with --vg. (This is not checked.)
njnee45f1d2005-12-13 21:55:16 +000067END
68;
njnec0c27a2005-12-10 23:11:28 +000069
70# Test variables
71my $vgopts; # valgrind options
72my $prog; # test prog
73my $args; # test prog args
74my $prereq; # prerequisite test to satisfy before running test
75my $cleanup; # cleanup command to run
njnec0c27a2005-12-10 23:11:28 +000076
njn57781862005-12-14 02:58:23 +000077# Command line options
njn3fbf17a2006-11-22 00:39:08 +000078my $n_reps = 1; # Run each test $n_reps times and choose the best one.
79my @vgdirs; # Dirs of the various Valgrinds being measured.
njn768cd5d2006-11-22 00:52:00 +000080my @tools = ("none", "memcheck"); # tools being measured
njnec0c27a2005-12-10 23:11:28 +000081
82my $num_tests_done = 0;
83my $num_timings_done = 0;
84
85# Starting directory
86chomp(my $tests_dir = `pwd`);
87
njnec0c27a2005-12-10 23:11:28 +000088#----------------------------------------------------------------------------
89# Process command line, setup
90#----------------------------------------------------------------------------
91
92# If $prog is a relative path, it prepends $dir to it. Useful for two reasons:
93#
94# 1. Can prepend "." onto programs to avoid trouble with users who don't have
95# "." in their path (by making $dir = ".")
96# 2. Can prepend the current dir to make the command absolute to avoid
97# subsequent trouble when we change directories.
98#
99# Also checks the program exists and is executable.
100sub validate_program ($$$$)
101{
102 my ($dir, $prog, $must_exist, $must_be_executable) = @_;
103
104 # If absolute path, leave it alone. If relative, make it
105 # absolute -- by prepending current dir -- so we can change
106 # dirs and still use it.
107 $prog = "$dir/$prog" if ($prog !~ /^\//);
108 if ($must_exist) {
109 (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n";
110 }
111 if ($must_be_executable) {
112 (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n";
113 }
114
115 return $prog;
116}
117
njn57781862005-12-14 02:58:23 +0000118sub add_vgdir($)
119{
120 my ($vgdir) = @_;
121 if ($vgdir !~ /^\//) { $vgdir = "$tests_dir/$vgdir"; }
122 validate_program($vgdir, "./coregrind/valgrind", 1, 1);
123 push(@vgdirs, $vgdir);
124}
125
njnec0c27a2005-12-10 23:11:28 +0000126sub process_command_line()
127{
njnec0c27a2005-12-10 23:11:28 +0000128 my @fs;
129
130 for my $arg (@ARGV) {
131 if ($arg =~ /^-/) {
njn1a95e052009-07-01 04:50:41 +0000132 if ($arg =~ /^--reps=(\d+)$/) {
njnee45f1d2005-12-13 21:55:16 +0000133 $n_reps = $1;
njn30e23ac2005-12-15 17:22:37 +0000134 if ($n_reps < 1) { die "bad --reps value: $n_reps\n"; }
njn57781862005-12-14 02:58:23 +0000135 } elsif ($arg =~ /^--vg=(.+)$/) {
136 # Make dir absolute if not already
137 add_vgdir($1);
njn3fbf17a2006-11-22 00:39:08 +0000138 } elsif ($arg =~ /^--tools=(.+)$/) {
139 @tools = split(/,/, $1);
njnec0c27a2005-12-10 23:11:28 +0000140 } else {
141 die $usage;
142 }
143 } else {
144 push(@fs, $arg);
145 }
146 }
njn57781862005-12-14 02:58:23 +0000147
148 # If no --vg options were specified, use the current tree.
149 if (0 == @vgdirs) {
150 add_vgdir($tests_dir);
151 }
njnec0c27a2005-12-10 23:11:28 +0000152
njnec0c27a2005-12-10 23:11:28 +0000153 (0 != @fs) or die "No test files or directories specified\n";
154
155 return @fs;
156}
157
158#----------------------------------------------------------------------------
159# Read a .vgperf file
160#----------------------------------------------------------------------------
161sub read_vgperf_file($)
162{
163 my ($f) = @_;
164
165 # Defaults.
166 ($vgopts, $prog, $args, $prereq, $cleanup)
167 = ("", undef, "", undef, undef, undef, undef);
168
169 open(INPUTFILE, "< $f") || die "File $f not openable\n";
170
171 while (my $line = <INPUTFILE>) {
172 if ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
173 next;
174 } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) {
175 $vgopts = $1;
176 } elsif ($line =~ /^\s*prog:\s*(.*)$/) {
njnc9582a22005-12-13 21:44:48 +0000177 $prog = validate_program(".", $1, 1, 1);
njnec0c27a2005-12-10 23:11:28 +0000178 } elsif ($line =~ /^\s*args:\s*(.*)$/) {
179 $args = $1;
180 } elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
181 $prereq = $1;
182 } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) {
183 $cleanup = $1;
184 } else {
185 die "Bad line in $f: $line\n";
186 }
187 }
188 close(INPUTFILE);
189
190 if (!defined $prog) {
191 $prog = ""; # allow no prog for testing error and --help cases
192 }
193 if (0 == @tools) {
194 die "vg_perf: missing 'tools' line in $f\n";
195 }
196}
197
198#----------------------------------------------------------------------------
199# Do one test
200#----------------------------------------------------------------------------
201# Since most of the program time is spent in system() calls, need this to
202# propagate a Ctrl-C enabling us to quit.
203sub mysystem($)
204{
njnecb47442005-12-13 16:54:58 +0000205 my ($cmd) = @_;
206 my $retval = system($cmd);
207 if ($retval == 2) {
208 exit 1;
209 } else {
210 return $retval;
211 }
njnec0c27a2005-12-10 23:11:28 +0000212}
213
sewardjb4860be2006-10-17 02:30:17 +0000214# Run program N times, return the best user time. Use the POSIX
215# -p flag on /usr/bin/time so as to get something parseable on AIX.
njnec0c27a2005-12-10 23:11:28 +0000216sub time_prog($$)
217{
218 my ($cmd, $n) = @_;
219 my $tmin = 999999;
220 for (my $i = 0; $i < $n; $i++) {
njnecb47442005-12-13 16:54:58 +0000221 mysystem("echo '$cmd' > perf.cmd");
222 my $retval = mysystem("$cmd > perf.stdout 2> perf.stderr");
223 (0 == $retval) or
njn57781862005-12-14 02:58:23 +0000224 die "\n*** Command returned non-zero ($retval)"
225 . "\n*** See perf.{cmd,stdout,stderr} to determine what went wrong.\n";
njnecb47442005-12-13 16:54:58 +0000226 my $out = `cat perf.stderr`;
njnae7f6822007-02-02 23:23:01 +0000227 ($out =~ /[Uu]ser +([\d\.]+)/) or
njn02383bc2005-12-13 20:23:38 +0000228 die "\n*** missing usertime in perf.stderr\n";
njnae7f6822007-02-02 23:23:01 +0000229 $tmin = $1 if ($1 < $tmin);
njnec0c27a2005-12-10 23:11:28 +0000230 }
njn30e23ac2005-12-15 17:22:37 +0000231 # Avoid divisions by zero!
232 return (0 == $tmin ? 0.01 : $tmin);
njnec0c27a2005-12-10 23:11:28 +0000233}
234
235sub do_one_test($$)
236{
237 my ($dir, $vgperf) = @_;
238 $vgperf =~ /^(.*)\.vgperf/;
239 my $name = $1;
njn30e23ac2005-12-15 17:22:37 +0000240 my %first_tTool; # For doing percentage speedups when comparing
241 # multiple Valgrinds
njnec0c27a2005-12-10 23:11:28 +0000242
243 read_vgperf_file($vgperf);
244
245 if (defined $prereq) {
246 if (system("$prereq") != 0) {
247 printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:");
248 return;
249 }
250 }
251
sewardjb4860be2006-10-17 02:30:17 +0000252 my $timecmd = "/usr/bin/time -p";
njnec0c27a2005-12-10 23:11:28 +0000253
254 # Do the native run(s).
njn57781862005-12-14 02:58:23 +0000255 printf("-- $name --\n") if (@vgdirs > 1);
njnec0c27a2005-12-10 23:11:28 +0000256 my $cmd = "$timecmd $prog $args";
njnee45f1d2005-12-13 21:55:16 +0000257 my $tNative = time_prog($cmd, $n_reps);
njnec0c27a2005-12-10 23:11:28 +0000258
njn57781862005-12-14 02:58:23 +0000259 foreach my $vgdir (@vgdirs) {
260 # Benchmark name
261 printf("%-8s ", $name);
njnec0c27a2005-12-10 23:11:28 +0000262
njn57781862005-12-14 02:58:23 +0000263 # Print the Valgrind version if we are measuring more than one.
264 my $vgdirname = $vgdir;
265 chomp($vgdirname = `basename $vgdir`);
266 printf("%-10s:", $vgdirname);
267
268 # Native execution time
sewardj8e339a12006-10-14 14:04:42 +0000269 printf("%4.2fs", $tNative);
njnec0c27a2005-12-10 23:11:28 +0000270
njn57781862005-12-14 02:58:23 +0000271 foreach my $tool (@tools) {
njn341f98b2006-11-03 19:37:50 +0000272 # First two chars of toolname for abbreviation
273 my $tool_abbrev = $tool;
274 $tool_abbrev =~ s/(..).*/$1/;
njnec0c27a2005-12-10 23:11:28 +0000275
njn57781862005-12-14 02:58:23 +0000276 # Do the tool run(s). Set both VALGRIND_LIB and VALGRIND_LIB_INNER
njn8c20d872006-04-06 22:59:35 +0000277 # in case this Valgrind was configured with --enable-inner. And
278 # also VALGRINDLIB, which was the old name for the variable, to
279 # allow comparison against old Valgrind versions (eg. 2.4.X).
njn341f98b2006-11-03 19:37:50 +0000280 printf(" %s:", $tool_abbrev);
njn8c20d872006-04-06 22:59:35 +0000281 my $vgsetup = "VALGRINDLIB=$vgdir/.in_place "
282 . "VALGRIND_LIB=$vgdir/.in_place "
njn57781862005-12-14 02:58:23 +0000283 . "VALGRIND_LIB_INNER=$vgdir/.in_place ";
284 my $vgcmd = "$vgdir/coregrind/valgrind "
285 . "--command-line-only=yes --tool=$tool -q "
njn276054a2006-11-03 19:30:33 +0000286 . "--memcheck:leak-check=no "
287 . "--trace-children=yes "
njn57781862005-12-14 02:58:23 +0000288 . "$vgopts ";
289 my $cmd = "$vgsetup $timecmd $vgcmd $prog $args";
290 my $tTool = time_prog($cmd, $n_reps);
njn30e23ac2005-12-15 17:22:37 +0000291 printf("%4.1fs (%4.1fx,", $tTool, $tTool/$tNative);
292
293 # If it's the first timing for this tool on this benchmark,
294 # record the time so we can get the percentage speedup of the
295 # subsequent Valgrinds. Otherwise, compute and print
296 # the speedup.
297 if (not defined $first_tTool{$tool}) {
298 $first_tTool{$tool} = $tTool;
njn715cc592006-03-27 00:39:43 +0000299 print(" -----)");
njn30e23ac2005-12-15 17:22:37 +0000300 } else {
301 my $speedup = 100 - (100 * $tTool / $first_tTool{$tool});
njn715cc592006-03-27 00:39:43 +0000302 printf("%5.1f%%)", $speedup);
njn30e23ac2005-12-15 17:22:37 +0000303 }
njn57781862005-12-14 02:58:23 +0000304
305 $num_timings_done++;
306
307 if (defined $cleanup) {
308 (system("$cleanup") == 0) or
309 print(" ($name cleanup operation failed: $cleanup)\n");
310 }
311 }
312 printf("\n");
njnec0c27a2005-12-10 23:11:28 +0000313 }
314
315 $num_tests_done++;
316}
317
318#----------------------------------------------------------------------------
319# Test one directory (and any subdirs)
320#----------------------------------------------------------------------------
321sub test_one_dir($$); # forward declaration
322
323sub test_one_dir($$)
324{
325 my ($dir, $prev_dirs) = @_;
326 $dir =~ s/\/$//; # trim a trailing '/'
327
njn356ffa32006-11-03 19:35:20 +0000328 chomp(my $initial_dir = `pwd`); # record where we started
329
njnec0c27a2005-12-10 23:11:28 +0000330 # Ignore dirs into which we should not recurse.
331 if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; }
332
333 chdir($dir) or die "Could not change into $dir\n";
334
335 # Nb: Don't prepend a '/' to the base directory
336 my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir;
337 my $dashes = "-" x (50 - length $full_dir);
338
339 my @fs = glob "*";
340 my $found_tests = (0 != (grep { $_ =~ /\.vgperf$/ } @fs));
341
342 if ($found_tests) {
343 print "-- Running tests in $full_dir $dashes\n";
344 }
345 foreach my $f (@fs) {
346 if (-d $f) {
347 test_one_dir($f, $full_dir);
348 } elsif ($f =~ /\.vgperf$/) {
349 do_one_test($full_dir, $f);
350 }
351 }
352 if ($found_tests) {
353 print "-- Finished tests in $full_dir $dashes\n";
354 }
355
njn356ffa32006-11-03 19:35:20 +0000356 chdir("$initial_dir");
njnec0c27a2005-12-10 23:11:28 +0000357}
358
359#----------------------------------------------------------------------------
360# Summarise results
361#----------------------------------------------------------------------------
362sub summarise_results
363{
364 printf("\n== %d programs, %d timings =================\n\n",
365 $num_tests_done, $num_timings_done);
366}
367
368#----------------------------------------------------------------------------
369# main()
370#----------------------------------------------------------------------------
371
372# nuke VALGRIND_OPTS
373$ENV{"VALGRIND_OPTS"} = "";
374
375my @fs = process_command_line();
376foreach my $f (@fs) {
377 if (-d $f) {
378 test_one_dir($f, "");
379 } else {
380 # Allow the .vgperf suffix to be given or omitted
381 if ($f =~ /.vgperf$/ && -r $f) {
382 # do nothing
383 } elsif (-r "$f.vgperf") {
384 $f = "$f.vgperf";
385 } else {
386 die "`$f' neither a directory nor a readable test file/name\n"
387 }
388 my $dir = `dirname $f`; chomp $dir;
389 my $file = `basename $f`; chomp $file;
390 chdir($dir) or die "Could not change into $dir\n";
391 do_one_test($dir, $file);
392 chdir($tests_dir);
393 }
394}
395summarise_results();
396
397##--------------------------------------------------------------------##
398##--- end ---##
399##--------------------------------------------------------------------##