blob: 64e4e7c536a5550bb8c54c2e4b06f14f34213eda [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)
44# - tools: <Valgrind tools> (compulsory)
45# - args: <args for prog> (default: none)
46# - vgopts: <Valgrind options> (default: none)
47# - prereq: <prerequisite command> (default: none)
48# - cleanup: <post-test cleanup cmd to run> (default: none)
49#
50# The prerequisite command, if present, must return 0 otherwise the test is
51# skipped.
52#----------------------------------------------------------------------------
53
54use warnings;
55use strict;
56
57#----------------------------------------------------------------------------
58# Global vars
59#----------------------------------------------------------------------------
njnee45f1d2005-12-13 21:55:16 +000060my $usage = <<END
61usage: vg_perf [options] [files or dirs]
njnec0c27a2005-12-10 23:11:28 +000062
njnee45f1d2005-12-13 21:55:16 +000063 options for the user, with defaults in [ ], are:
64 -h --help show this message
65 --all run all tests under this directory
66 --reps number of repeats for each program [3]
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]
njnee45f1d2005-12-13 21:55:16 +000070END
71;
njnec0c27a2005-12-10 23:11:28 +000072
73# Test variables
74my $vgopts; # valgrind options
75my $prog; # test prog
76my $args; # test prog args
77my $prereq; # prerequisite test to satisfy before running test
78my $cleanup; # cleanup command to run
79my @tools; # which tools are we measuring the program with
80
njn57781862005-12-14 02:58:23 +000081# Command line options
njnfbfbd4f2005-12-23 23:08:04 +000082my $n_reps = 1; # Run each program $n_reps times and choose the best one.
njn57781862005-12-14 02:58:23 +000083my @vgdirs; # Dirs of the various Valgrinds being measured.
njnec0c27a2005-12-10 23:11:28 +000084
85my $num_tests_done = 0;
86my $num_timings_done = 0;
87
88# Starting directory
89chomp(my $tests_dir = `pwd`);
90
njnec0c27a2005-12-10 23:11:28 +000091#----------------------------------------------------------------------------
92# Process command line, setup
93#----------------------------------------------------------------------------
94
95# If $prog is a relative path, it prepends $dir to it. Useful for two reasons:
96#
97# 1. Can prepend "." onto programs to avoid trouble with users who don't have
98# "." in their path (by making $dir = ".")
99# 2. Can prepend the current dir to make the command absolute to avoid
100# subsequent trouble when we change directories.
101#
102# Also checks the program exists and is executable.
103sub validate_program ($$$$)
104{
105 my ($dir, $prog, $must_exist, $must_be_executable) = @_;
106
107 # If absolute path, leave it alone. If relative, make it
108 # absolute -- by prepending current dir -- so we can change
109 # dirs and still use it.
110 $prog = "$dir/$prog" if ($prog !~ /^\//);
111 if ($must_exist) {
112 (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n";
113 }
114 if ($must_be_executable) {
115 (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n";
116 }
117
118 return $prog;
119}
120
121sub validate_tools($)
122{
123 # XXX: should check they exist!
124 my ($toolnames) = @_;
125 my @t = split(/\s+/, $toolnames);
126 return @t;
127}
128
njn57781862005-12-14 02:58:23 +0000129sub add_vgdir($)
130{
131 my ($vgdir) = @_;
132 if ($vgdir !~ /^\//) { $vgdir = "$tests_dir/$vgdir"; }
133 validate_program($vgdir, "./coregrind/valgrind", 1, 1);
134 push(@vgdirs, $vgdir);
135}
136
njnec0c27a2005-12-10 23:11:28 +0000137sub process_command_line()
138{
139 my $alldirs = 0;
140 my @fs;
141
142 for my $arg (@ARGV) {
143 if ($arg =~ /^-/) {
144 if ($arg =~ /^--all$/) {
145 $alldirs = 1;
njnb2b4f462005-12-13 22:00:17 +0000146 } elsif ($arg =~ /^--reps=(\d+)$/) {
njnee45f1d2005-12-13 21:55:16 +0000147 $n_reps = $1;
njn30e23ac2005-12-15 17:22:37 +0000148 if ($n_reps < 1) { die "bad --reps value: $n_reps\n"; }
njn57781862005-12-14 02:58:23 +0000149 } elsif ($arg =~ /^--vg=(.+)$/) {
150 # Make dir absolute if not already
151 add_vgdir($1);
njnec0c27a2005-12-10 23:11:28 +0000152 } else {
153 die $usage;
154 }
155 } else {
156 push(@fs, $arg);
157 }
158 }
njn57781862005-12-14 02:58:23 +0000159
160 # If no --vg options were specified, use the current tree.
161 if (0 == @vgdirs) {
162 add_vgdir($tests_dir);
163 }
njnec0c27a2005-12-10 23:11:28 +0000164
165 if ($alldirs) {
166 @fs = ();
167 foreach my $f (glob "*") {
168 push(@fs, $f) if (-d $f);
169 }
170 }
171
172 (0 != @fs) or die "No test files or directories specified\n";
173
174 return @fs;
175}
176
177#----------------------------------------------------------------------------
178# Read a .vgperf file
179#----------------------------------------------------------------------------
180sub read_vgperf_file($)
181{
182 my ($f) = @_;
183
184 # Defaults.
185 ($vgopts, $prog, $args, $prereq, $cleanup)
186 = ("", undef, "", undef, undef, undef, undef);
187
188 open(INPUTFILE, "< $f") || die "File $f not openable\n";
189
190 while (my $line = <INPUTFILE>) {
191 if ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
192 next;
193 } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) {
194 $vgopts = $1;
195 } elsif ($line =~ /^\s*prog:\s*(.*)$/) {
njnc9582a22005-12-13 21:44:48 +0000196 $prog = validate_program(".", $1, 1, 1);
njnec0c27a2005-12-10 23:11:28 +0000197 } elsif ($line =~ /^\s*tools:\s*(.*)$/) {
198 @tools = validate_tools($1);
199 } elsif ($line =~ /^\s*args:\s*(.*)$/) {
200 $args = $1;
201 } elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
202 $prereq = $1;
203 } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) {
204 $cleanup = $1;
205 } else {
206 die "Bad line in $f: $line\n";
207 }
208 }
209 close(INPUTFILE);
210
211 if (!defined $prog) {
212 $prog = ""; # allow no prog for testing error and --help cases
213 }
214 if (0 == @tools) {
215 die "vg_perf: missing 'tools' line in $f\n";
216 }
217}
218
219#----------------------------------------------------------------------------
220# Do one test
221#----------------------------------------------------------------------------
222# Since most of the program time is spent in system() calls, need this to
223# propagate a Ctrl-C enabling us to quit.
224sub mysystem($)
225{
njnecb47442005-12-13 16:54:58 +0000226 my ($cmd) = @_;
227 my $retval = system($cmd);
228 if ($retval == 2) {
229 exit 1;
230 } else {
231 return $retval;
232 }
njnec0c27a2005-12-10 23:11:28 +0000233}
234
sewardjb4860be2006-10-17 02:30:17 +0000235# Run program N times, return the best user time. Use the POSIX
236# -p flag on /usr/bin/time so as to get something parseable on AIX.
njnec0c27a2005-12-10 23:11:28 +0000237sub time_prog($$)
238{
239 my ($cmd, $n) = @_;
240 my $tmin = 999999;
241 for (my $i = 0; $i < $n; $i++) {
njnecb47442005-12-13 16:54:58 +0000242 mysystem("echo '$cmd' > perf.cmd");
243 my $retval = mysystem("$cmd > perf.stdout 2> perf.stderr");
244 (0 == $retval) or
njn57781862005-12-14 02:58:23 +0000245 die "\n*** Command returned non-zero ($retval)"
246 . "\n*** See perf.{cmd,stdout,stderr} to determine what went wrong.\n";
njnecb47442005-12-13 16:54:58 +0000247 my $out = `cat perf.stderr`;
sewardjb4860be2006-10-17 02:30:17 +0000248 ($out =~ /[Uu]ser([ ]+)([\d\.]+)/) or
njn02383bc2005-12-13 20:23:38 +0000249 die "\n*** missing usertime in perf.stderr\n";
sewardjb4860be2006-10-17 02:30:17 +0000250 $tmin = $2 if ($2 < $tmin);
njnec0c27a2005-12-10 23:11:28 +0000251 }
njn30e23ac2005-12-15 17:22:37 +0000252 # Avoid divisions by zero!
253 return (0 == $tmin ? 0.01 : $tmin);
njnec0c27a2005-12-10 23:11:28 +0000254}
255
256sub do_one_test($$)
257{
258 my ($dir, $vgperf) = @_;
259 $vgperf =~ /^(.*)\.vgperf/;
260 my $name = $1;
njn30e23ac2005-12-15 17:22:37 +0000261 my %first_tTool; # For doing percentage speedups when comparing
262 # multiple Valgrinds
njnec0c27a2005-12-10 23:11:28 +0000263
264 read_vgperf_file($vgperf);
265
266 if (defined $prereq) {
267 if (system("$prereq") != 0) {
268 printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:");
269 return;
270 }
271 }
272
sewardjb4860be2006-10-17 02:30:17 +0000273 my $timecmd = "/usr/bin/time -p";
njnec0c27a2005-12-10 23:11:28 +0000274
275 # Do the native run(s).
njn57781862005-12-14 02:58:23 +0000276 printf("-- $name --\n") if (@vgdirs > 1);
njnec0c27a2005-12-10 23:11:28 +0000277 my $cmd = "$timecmd $prog $args";
njnee45f1d2005-12-13 21:55:16 +0000278 my $tNative = time_prog($cmd, $n_reps);
njnec0c27a2005-12-10 23:11:28 +0000279
njn57781862005-12-14 02:58:23 +0000280 foreach my $vgdir (@vgdirs) {
281 # Benchmark name
282 printf("%-8s ", $name);
njnec0c27a2005-12-10 23:11:28 +0000283
njn57781862005-12-14 02:58:23 +0000284 # Print the Valgrind version if we are measuring more than one.
285 my $vgdirname = $vgdir;
286 chomp($vgdirname = `basename $vgdir`);
287 printf("%-10s:", $vgdirname);
288
289 # Native execution time
sewardj8e339a12006-10-14 14:04:42 +0000290 printf("%4.2fs", $tNative);
njnec0c27a2005-12-10 23:11:28 +0000291
njn57781862005-12-14 02:58:23 +0000292 foreach my $tool (@tools) {
njn341f98b2006-11-03 19:37:50 +0000293 # First two chars of toolname for abbreviation
294 my $tool_abbrev = $tool;
295 $tool_abbrev =~ s/(..).*/$1/;
njnec0c27a2005-12-10 23:11:28 +0000296
njn57781862005-12-14 02:58:23 +0000297 # Do the tool run(s). Set both VALGRIND_LIB and VALGRIND_LIB_INNER
njn8c20d872006-04-06 22:59:35 +0000298 # in case this Valgrind was configured with --enable-inner. And
299 # also VALGRINDLIB, which was the old name for the variable, to
300 # allow comparison against old Valgrind versions (eg. 2.4.X).
njn341f98b2006-11-03 19:37:50 +0000301 printf(" %s:", $tool_abbrev);
njn8c20d872006-04-06 22:59:35 +0000302 my $vgsetup = "VALGRINDLIB=$vgdir/.in_place "
303 . "VALGRIND_LIB=$vgdir/.in_place "
njn57781862005-12-14 02:58:23 +0000304 . "VALGRIND_LIB_INNER=$vgdir/.in_place ";
305 my $vgcmd = "$vgdir/coregrind/valgrind "
306 . "--command-line-only=yes --tool=$tool -q "
njn276054a2006-11-03 19:30:33 +0000307 . "--memcheck:leak-check=no "
308 . "--trace-children=yes "
njn57781862005-12-14 02:58:23 +0000309 . "$vgopts ";
310 my $cmd = "$vgsetup $timecmd $vgcmd $prog $args";
311 my $tTool = time_prog($cmd, $n_reps);
njn30e23ac2005-12-15 17:22:37 +0000312 printf("%4.1fs (%4.1fx,", $tTool, $tTool/$tNative);
313
314 # If it's the first timing for this tool on this benchmark,
315 # record the time so we can get the percentage speedup of the
316 # subsequent Valgrinds. Otherwise, compute and print
317 # the speedup.
318 if (not defined $first_tTool{$tool}) {
319 $first_tTool{$tool} = $tTool;
njn715cc592006-03-27 00:39:43 +0000320 print(" -----)");
njn30e23ac2005-12-15 17:22:37 +0000321 } else {
322 my $speedup = 100 - (100 * $tTool / $first_tTool{$tool});
njn715cc592006-03-27 00:39:43 +0000323 printf("%5.1f%%)", $speedup);
njn30e23ac2005-12-15 17:22:37 +0000324 }
njn57781862005-12-14 02:58:23 +0000325
326 $num_timings_done++;
327
328 if (defined $cleanup) {
329 (system("$cleanup") == 0) or
330 print(" ($name cleanup operation failed: $cleanup)\n");
331 }
332 }
333 printf("\n");
njnec0c27a2005-12-10 23:11:28 +0000334 }
335
336 $num_tests_done++;
337}
338
339#----------------------------------------------------------------------------
340# Test one directory (and any subdirs)
341#----------------------------------------------------------------------------
342sub test_one_dir($$); # forward declaration
343
344sub test_one_dir($$)
345{
346 my ($dir, $prev_dirs) = @_;
347 $dir =~ s/\/$//; # trim a trailing '/'
348
njn356ffa32006-11-03 19:35:20 +0000349 chomp(my $initial_dir = `pwd`); # record where we started
350
njnec0c27a2005-12-10 23:11:28 +0000351 # Ignore dirs into which we should not recurse.
352 if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; }
353
354 chdir($dir) or die "Could not change into $dir\n";
355
356 # Nb: Don't prepend a '/' to the base directory
357 my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir;
358 my $dashes = "-" x (50 - length $full_dir);
359
360 my @fs = glob "*";
361 my $found_tests = (0 != (grep { $_ =~ /\.vgperf$/ } @fs));
362
363 if ($found_tests) {
364 print "-- Running tests in $full_dir $dashes\n";
365 }
366 foreach my $f (@fs) {
367 if (-d $f) {
368 test_one_dir($f, $full_dir);
369 } elsif ($f =~ /\.vgperf$/) {
370 do_one_test($full_dir, $f);
371 }
372 }
373 if ($found_tests) {
374 print "-- Finished tests in $full_dir $dashes\n";
375 }
376
njn356ffa32006-11-03 19:35:20 +0000377 chdir("$initial_dir");
njnec0c27a2005-12-10 23:11:28 +0000378}
379
380#----------------------------------------------------------------------------
381# Summarise results
382#----------------------------------------------------------------------------
383sub summarise_results
384{
385 printf("\n== %d programs, %d timings =================\n\n",
386 $num_tests_done, $num_timings_done);
387}
388
389#----------------------------------------------------------------------------
390# main()
391#----------------------------------------------------------------------------
392
393# nuke VALGRIND_OPTS
394$ENV{"VALGRIND_OPTS"} = "";
395
396my @fs = process_command_line();
397foreach my $f (@fs) {
398 if (-d $f) {
399 test_one_dir($f, "");
400 } else {
401 # Allow the .vgperf suffix to be given or omitted
402 if ($f =~ /.vgperf$/ && -r $f) {
403 # do nothing
404 } elsif (-r "$f.vgperf") {
405 $f = "$f.vgperf";
406 } else {
407 die "`$f' neither a directory nor a readable test file/name\n"
408 }
409 my $dir = `dirname $f`; chomp $dir;
410 my $file = `basename $f`; chomp $file;
411 chdir($dir) or die "Could not change into $dir\n";
412 do_one_test($dir, $file);
413 chdir($tests_dir);
414 }
415}
416summarise_results();
417
418##--------------------------------------------------------------------##
419##--- end ---##
420##--------------------------------------------------------------------##