vg_perf.in revision 715cc59176a4b4a527be690089056c468431f6c4
1#! @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#---------------------------------------------------------------------------- 30# usage: see usage message. 31# 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#---------------------------------------------------------------------------- 60my $usage = <<END 61usage: vg_perf [options] [files or dirs] 62 63 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] 67 --vg Valgrind(s) to measure (can be specified multiple 68 times). The "in-place" build is used. 69 [Valgrind in the current directory] 70END 71; 72 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 81# Abbreviations used in output 82my %toolnames = ( 83 none => "nl", 84 memcheck => "mc", 85 cachegrind => "cg", 86 massif => "ms" 87); 88 89# Command line options 90my $n_reps = 1; # Run each program $n_reps times and choose the best one. 91my @vgdirs; # Dirs of the various Valgrinds being measured. 92 93my $num_tests_done = 0; 94my $num_timings_done = 0; 95 96# Starting directory 97chomp(my $tests_dir = `pwd`); 98 99#---------------------------------------------------------------------------- 100# Process command line, setup 101#---------------------------------------------------------------------------- 102 103# If $prog is a relative path, it prepends $dir to it. Useful for two reasons: 104# 105# 1. Can prepend "." onto programs to avoid trouble with users who don't have 106# "." in their path (by making $dir = ".") 107# 2. Can prepend the current dir to make the command absolute to avoid 108# subsequent trouble when we change directories. 109# 110# Also checks the program exists and is executable. 111sub validate_program ($$$$) 112{ 113 my ($dir, $prog, $must_exist, $must_be_executable) = @_; 114 115 # If absolute path, leave it alone. If relative, make it 116 # absolute -- by prepending current dir -- so we can change 117 # dirs and still use it. 118 $prog = "$dir/$prog" if ($prog !~ /^\//); 119 if ($must_exist) { 120 (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n"; 121 } 122 if ($must_be_executable) { 123 (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n"; 124 } 125 126 return $prog; 127} 128 129sub validate_tools($) 130{ 131 # XXX: should check they exist! 132 my ($toolnames) = @_; 133 my @t = split(/\s+/, $toolnames); 134 return @t; 135} 136 137sub add_vgdir($) 138{ 139 my ($vgdir) = @_; 140 if ($vgdir !~ /^\//) { $vgdir = "$tests_dir/$vgdir"; } 141 validate_program($vgdir, "./coregrind/valgrind", 1, 1); 142 push(@vgdirs, $vgdir); 143} 144 145sub process_command_line() 146{ 147 my $alldirs = 0; 148 my @fs; 149 150 for my $arg (@ARGV) { 151 if ($arg =~ /^-/) { 152 if ($arg =~ /^--all$/) { 153 $alldirs = 1; 154 } elsif ($arg =~ /^--reps=(\d+)$/) { 155 $n_reps = $1; 156 if ($n_reps < 1) { die "bad --reps value: $n_reps\n"; } 157 } elsif ($arg =~ /^--vg=(.+)$/) { 158 # Make dir absolute if not already 159 add_vgdir($1); 160 } else { 161 die $usage; 162 } 163 } else { 164 push(@fs, $arg); 165 } 166 } 167 168 # If no --vg options were specified, use the current tree. 169 if (0 == @vgdirs) { 170 add_vgdir($tests_dir); 171 } 172 173 if ($alldirs) { 174 @fs = (); 175 foreach my $f (glob "*") { 176 push(@fs, $f) if (-d $f); 177 } 178 } 179 180 (0 != @fs) or die "No test files or directories specified\n"; 181 182 return @fs; 183} 184 185#---------------------------------------------------------------------------- 186# Read a .vgperf file 187#---------------------------------------------------------------------------- 188sub read_vgperf_file($) 189{ 190 my ($f) = @_; 191 192 # Defaults. 193 ($vgopts, $prog, $args, $prereq, $cleanup) 194 = ("", undef, "", undef, undef, undef, undef); 195 196 open(INPUTFILE, "< $f") || die "File $f not openable\n"; 197 198 while (my $line = <INPUTFILE>) { 199 if ($line =~ /^\s*#/ || $line =~ /^\s*$/) { 200 next; 201 } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) { 202 $vgopts = $1; 203 } elsif ($line =~ /^\s*prog:\s*(.*)$/) { 204 $prog = validate_program(".", $1, 1, 1); 205 } elsif ($line =~ /^\s*tools:\s*(.*)$/) { 206 @tools = validate_tools($1); 207 } elsif ($line =~ /^\s*args:\s*(.*)$/) { 208 $args = $1; 209 } elsif ($line =~ /^\s*prereq:\s*(.*)$/) { 210 $prereq = $1; 211 } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) { 212 $cleanup = $1; 213 } else { 214 die "Bad line in $f: $line\n"; 215 } 216 } 217 close(INPUTFILE); 218 219 if (!defined $prog) { 220 $prog = ""; # allow no prog for testing error and --help cases 221 } 222 if (0 == @tools) { 223 die "vg_perf: missing 'tools' line in $f\n"; 224 } 225} 226 227#---------------------------------------------------------------------------- 228# Do one test 229#---------------------------------------------------------------------------- 230# Since most of the program time is spent in system() calls, need this to 231# propagate a Ctrl-C enabling us to quit. 232sub mysystem($) 233{ 234 my ($cmd) = @_; 235 my $retval = system($cmd); 236 if ($retval == 2) { 237 exit 1; 238 } else { 239 return $retval; 240 } 241} 242 243# Run program N times, return the best user time. 244sub time_prog($$) 245{ 246 my ($cmd, $n) = @_; 247 my $tmin = 999999; 248 for (my $i = 0; $i < $n; $i++) { 249 mysystem("echo '$cmd' > perf.cmd"); 250 my $retval = mysystem("$cmd > perf.stdout 2> perf.stderr"); 251 (0 == $retval) or 252 die "\n*** Command returned non-zero ($retval)" 253 . "\n*** See perf.{cmd,stdout,stderr} to determine what went wrong.\n"; 254 my $out = `cat perf.stderr`; 255 ($out =~ /usertime: ([\d\.]+)s/) or 256 die "\n*** missing usertime in perf.stderr\n"; 257 $tmin = $1 if ($1 < $tmin); 258 } 259 # Avoid divisions by zero! 260 return (0 == $tmin ? 0.01 : $tmin); 261} 262 263sub do_one_test($$) 264{ 265 my ($dir, $vgperf) = @_; 266 $vgperf =~ /^(.*)\.vgperf/; 267 my $name = $1; 268 my %first_tTool; # For doing percentage speedups when comparing 269 # multiple Valgrinds 270 271 read_vgperf_file($vgperf); 272 273 if (defined $prereq) { 274 if (system("$prereq") != 0) { 275 printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:"); 276 return; 277 } 278 } 279 280 my $timecmd = "/usr/bin/time -f 'usertime: %Us'"; 281 282 # Do the native run(s). 283 printf("-- $name --\n") if (@vgdirs > 1); 284 my $cmd = "$timecmd $prog $args"; 285 my $tNative = time_prog($cmd, $n_reps); 286 287 foreach my $vgdir (@vgdirs) { 288 # Benchmark name 289 printf("%-8s ", $name); 290 291 # Print the Valgrind version if we are measuring more than one. 292 my $vgdirname = $vgdir; 293 chomp($vgdirname = `basename $vgdir`); 294 printf("%-10s:", $vgdirname); 295 296 # Native execution time 297 printf("%4.1fs", $tNative); 298 299 foreach my $tool (@tools) { 300 (defined $toolnames{$tool}) or 301 die "unknown tool $tool, please add to %toolnames\n"; 302 303 # Do the tool run(s). Set both VALGRIND_LIB and VALGRIND_LIB_INNER 304 # in case this Valgrind was configured with --enable-inner. 305 printf(" %s:", $toolnames{$tool}); 306 my $vgsetup = "VALGRIND_LIB=$vgdir/.in_place " 307 . "VALGRIND_LIB_INNER=$vgdir/.in_place "; 308 my $vgcmd = "$vgdir/coregrind/valgrind " 309 . "--command-line-only=yes --tool=$tool -q " 310 . "--memcheck:leak-check=no --addrcheck:leak-check=no " 311 . "$vgopts "; 312 my $cmd = "$vgsetup $timecmd $vgcmd $prog $args"; 313 my $tTool = time_prog($cmd, $n_reps); 314 printf("%4.1fs (%4.1fx,", $tTool, $tTool/$tNative); 315 316 # If it's the first timing for this tool on this benchmark, 317 # record the time so we can get the percentage speedup of the 318 # subsequent Valgrinds. Otherwise, compute and print 319 # the speedup. 320 if (not defined $first_tTool{$tool}) { 321 $first_tTool{$tool} = $tTool; 322 print(" -----)"); 323 } else { 324 my $speedup = 100 - (100 * $tTool / $first_tTool{$tool}); 325 printf("%5.1f%%)", $speedup); 326 } 327 328 $num_timings_done++; 329 330 if (defined $cleanup) { 331 (system("$cleanup") == 0) or 332 print(" ($name cleanup operation failed: $cleanup)\n"); 333 } 334 } 335 printf("\n"); 336 } 337 338 $num_tests_done++; 339} 340 341#---------------------------------------------------------------------------- 342# Test one directory (and any subdirs) 343#---------------------------------------------------------------------------- 344sub test_one_dir($$); # forward declaration 345 346sub test_one_dir($$) 347{ 348 my ($dir, $prev_dirs) = @_; 349 $dir =~ s/\/$//; # trim a trailing '/' 350 351 # 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 377 chdir(".."); 378} 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##--------------------------------------------------------------------## 421