blob: 727857f2d403dd5b1f5cacb37ea2df81ceaceb07 [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#
Elliott Hughesed398002017-06-21 14:41:24 -07009# Copyright (C) 2005-2017 Nicholas Nethercote
njnec0c27a2005-12-10 23:11:28 +000010# 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.
philippe215b2972012-07-24 19:47:46 +000046# Sometimes it is useful to run all the tests at a high sanity check
47# level or with arbitrary other flags. To make this simple, extra
48# options, applied to all tests run, are read from $EXTRA_REGTEST_OPTS,
49# and handed to valgrind prior to any other flags specified by the
50# .vgperf file. Note: the env var is the same as vg_regtest.
njnec0c27a2005-12-10 23:11:28 +000051#----------------------------------------------------------------------------
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
njn3fbf17a2006-11-22 00:39:08 +000064 --reps=<n> number of repeats for each program [1]
njn768cd5d2006-11-22 00:52:00 +000065 --tools=<t1,t2,t3> tools to run [Nulgrind and Memcheck]
florian74c37d82013-01-24 21:32:32 +000066 --vg=<dir> top-level directory containing Valgrind to measure
67 [Valgrind in the current directory, i.e. --vg=.]
68 Can be specified multiple times.
philippe14ab1a32012-04-08 19:52:38 +000069 The "in-place" build is used.
70
71 --outer-valgrind: run these Valgrind(s) under the given outer valgrind.
72 These Valgrind(s) must be configured with --enable-inner.
73 --outer-tool: tool to use by the outer valgrind (default cachegrind).
Elliott Hughesed398002017-06-21 14:41:24 -070074 --outer-args: use this as outer tool args. If the outer args are starting
75 with +, the given outer args are appended to the outer args predefined
76 by vg_perf.
njn768cd5d2006-11-22 00:52:00 +000077
78 Any tools named in --tools must be present in all directories specified
79 with --vg. (This is not checked.)
philippe215b2972012-07-24 19:47:46 +000080 Use EXTRA_REGTEST_OPTS to supply extra args for all tests
njnee45f1d2005-12-13 21:55:16 +000081END
82;
njnec0c27a2005-12-10 23:11:28 +000083
84# Test variables
85my $vgopts; # valgrind options
86my $prog; # test prog
87my $args; # test prog args
88my $prereq; # prerequisite test to satisfy before running test
89my $cleanup; # cleanup command to run
njnec0c27a2005-12-10 23:11:28 +000090
njn57781862005-12-14 02:58:23 +000091# Command line options
njn3fbf17a2006-11-22 00:39:08 +000092my $n_reps = 1; # Run each test $n_reps times and choose the best one.
93my @vgdirs; # Dirs of the various Valgrinds being measured.
njn768cd5d2006-11-22 00:52:00 +000094my @tools = ("none", "memcheck"); # tools being measured
njnec0c27a2005-12-10 23:11:28 +000095
philippe14ab1a32012-04-08 19:52:38 +000096# Outer valgrind to use, and args to use for it.
97# If this is set, --valgrind should be set to the installed inner valgrind,
98# and --valgrind-lib will be ignore
99my $outer_valgrind;
100my $outer_tool = "cachegrind";
101my $outer_args;
102
103
njnec0c27a2005-12-10 23:11:28 +0000104my $num_tests_done = 0;
105my $num_timings_done = 0;
106
107# Starting directory
108chomp(my $tests_dir = `pwd`);
109
njnec0c27a2005-12-10 23:11:28 +0000110#----------------------------------------------------------------------------
111# Process command line, setup
112#----------------------------------------------------------------------------
113
114# If $prog is a relative path, it prepends $dir to it. Useful for two reasons:
115#
116# 1. Can prepend "." onto programs to avoid trouble with users who don't have
117# "." in their path (by making $dir = ".")
118# 2. Can prepend the current dir to make the command absolute to avoid
119# subsequent trouble when we change directories.
120#
121# Also checks the program exists and is executable.
122sub validate_program ($$$$)
123{
124 my ($dir, $prog, $must_exist, $must_be_executable) = @_;
125
126 # If absolute path, leave it alone. If relative, make it
127 # absolute -- by prepending current dir -- so we can change
128 # dirs and still use it.
129 $prog = "$dir/$prog" if ($prog !~ /^\//);
130 if ($must_exist) {
131 (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n";
132 }
133 if ($must_be_executable) {
134 (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n";
135 }
136
137 return $prog;
138}
139
njn57781862005-12-14 02:58:23 +0000140sub add_vgdir($)
141{
142 my ($vgdir) = @_;
143 if ($vgdir !~ /^\//) { $vgdir = "$tests_dir/$vgdir"; }
njn57781862005-12-14 02:58:23 +0000144 push(@vgdirs, $vgdir);
145}
146
njnec0c27a2005-12-10 23:11:28 +0000147sub process_command_line()
148{
njnec0c27a2005-12-10 23:11:28 +0000149 my @fs;
150
151 for my $arg (@ARGV) {
152 if ($arg =~ /^-/) {
njn1a95e052009-07-01 04:50:41 +0000153 if ($arg =~ /^--reps=(\d+)$/) {
njnee45f1d2005-12-13 21:55:16 +0000154 $n_reps = $1;
njn30e23ac2005-12-15 17:22:37 +0000155 if ($n_reps < 1) { die "bad --reps value: $n_reps\n"; }
njn57781862005-12-14 02:58:23 +0000156 } elsif ($arg =~ /^--vg=(.+)$/) {
157 # Make dir absolute if not already
158 add_vgdir($1);
njn3fbf17a2006-11-22 00:39:08 +0000159 } elsif ($arg =~ /^--tools=(.+)$/) {
160 @tools = split(/,/, $1);
philippe14ab1a32012-04-08 19:52:38 +0000161 } elsif ($arg =~ /^--outer-valgrind=(.*)$/) {
162 $outer_valgrind = $1;
163 } elsif ($arg =~ /^--outer-tool=(.*)$/) {
164 $outer_tool = $1;
165 } elsif ($arg =~ /^--outer-args=(.*)$/) {
166 $outer_args = $1;
njnec0c27a2005-12-10 23:11:28 +0000167 } else {
168 die $usage;
169 }
170 } else {
171 push(@fs, $arg);
172 }
173 }
njn57781862005-12-14 02:58:23 +0000174
175 # If no --vg options were specified, use the current tree.
176 if (0 == @vgdirs) {
177 add_vgdir($tests_dir);
178 }
njnec0c27a2005-12-10 23:11:28 +0000179
njnec0c27a2005-12-10 23:11:28 +0000180 (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*(.*)$/) {
njnc9582a22005-12-13 21:44:48 +0000204 $prog = validate_program(".", $1, 1, 1);
njnec0c27a2005-12-10 23:11:28 +0000205 } elsif ($line =~ /^\s*args:\s*(.*)$/) {
206 $args = $1;
207 } elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
208 $prereq = $1;
209 } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) {
210 $cleanup = $1;
211 } else {
212 die "Bad line in $f: $line\n";
213 }
214 }
215 close(INPUTFILE);
216
217 if (!defined $prog) {
218 $prog = ""; # allow no prog for testing error and --help cases
219 }
220 if (0 == @tools) {
221 die "vg_perf: missing 'tools' line in $f\n";
222 }
223}
224
225#----------------------------------------------------------------------------
226# Do one test
227#----------------------------------------------------------------------------
228# Since most of the program time is spent in system() calls, need this to
229# propagate a Ctrl-C enabling us to quit.
230sub mysystem($)
231{
njnecb47442005-12-13 16:54:58 +0000232 my ($cmd) = @_;
233 my $retval = system($cmd);
234 if ($retval == 2) {
235 exit 1;
236 } else {
237 return $retval;
238 }
njnec0c27a2005-12-10 23:11:28 +0000239}
240
sewardjb4860be2006-10-17 02:30:17 +0000241# Run program N times, return the best user time. Use the POSIX
242# -p flag on /usr/bin/time so as to get something parseable on AIX.
njnec0c27a2005-12-10 23:11:28 +0000243sub time_prog($$)
244{
245 my ($cmd, $n) = @_;
246 my $tmin = 999999;
247 for (my $i = 0; $i < $n; $i++) {
njnecb47442005-12-13 16:54:58 +0000248 mysystem("echo '$cmd' > perf.cmd");
249 my $retval = mysystem("$cmd > perf.stdout 2> perf.stderr");
250 (0 == $retval) or
njn57781862005-12-14 02:58:23 +0000251 die "\n*** Command returned non-zero ($retval)"
252 . "\n*** See perf.{cmd,stdout,stderr} to determine what went wrong.\n";
njnecb47442005-12-13 16:54:58 +0000253 my $out = `cat perf.stderr`;
njnae7f6822007-02-02 23:23:01 +0000254 ($out =~ /[Uu]ser +([\d\.]+)/) or
njn02383bc2005-12-13 20:23:38 +0000255 die "\n*** missing usertime in perf.stderr\n";
njnae7f6822007-02-02 23:23:01 +0000256 $tmin = $1 if ($1 < $tmin);
njnec0c27a2005-12-10 23:11:28 +0000257 }
florian7ac82602012-09-17 21:20:06 +0000258
259 # Successful run; cleanup
260 unlink("perf.cmd");
261 unlink("perf.stderr");
262 unlink("perf.stdout");
263
njn30e23ac2005-12-15 17:22:37 +0000264 # Avoid divisions by zero!
265 return (0 == $tmin ? 0.01 : $tmin);
njnec0c27a2005-12-10 23:11:28 +0000266}
267
268sub do_one_test($$)
269{
270 my ($dir, $vgperf) = @_;
271 $vgperf =~ /^(.*)\.vgperf/;
272 my $name = $1;
njn30e23ac2005-12-15 17:22:37 +0000273 my %first_tTool; # For doing percentage speedups when comparing
274 # multiple Valgrinds
njnec0c27a2005-12-10 23:11:28 +0000275
276 read_vgperf_file($vgperf);
277
278 if (defined $prereq) {
279 if (system("$prereq") != 0) {
280 printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:");
281 return;
282 }
283 }
284
sewardjb4860be2006-10-17 02:30:17 +0000285 my $timecmd = "/usr/bin/time -p";
njnec0c27a2005-12-10 23:11:28 +0000286
287 # Do the native run(s).
njn57781862005-12-14 02:58:23 +0000288 printf("-- $name --\n") if (@vgdirs > 1);
njnec0c27a2005-12-10 23:11:28 +0000289 my $cmd = "$timecmd $prog $args";
njnee45f1d2005-12-13 21:55:16 +0000290 my $tNative = time_prog($cmd, $n_reps);
njnec0c27a2005-12-10 23:11:28 +0000291
philippe14ab1a32012-04-08 19:52:38 +0000292 if (defined $outer_valgrind) {
293 $outer_valgrind = validate_program($tests_dir, $outer_valgrind, 1, 1);
294 foreach my $vgdir (@vgdirs) {
295 validate_program($vgdir, "./coregrind/valgrind", 1, 1);
296 }
297 } else {
298 foreach my $vgdir (@vgdirs) {
299 validate_program($vgdir, "./coregrind/valgrind", 1, 1);
300 }
301 }
302
philippe215b2972012-07-24 19:47:46 +0000303 # Pull any extra options (for example, --sanity-level=4)
304 # from $EXTRA_REGTEST_OPTS.
305 my $maybe_extraopts = $ENV{"EXTRA_REGTEST_OPTS"};
306 my $extraopts = $maybe_extraopts ? $maybe_extraopts : "";
307
njn57781862005-12-14 02:58:23 +0000308 foreach my $vgdir (@vgdirs) {
309 # Benchmark name
310 printf("%-8s ", $name);
njnec0c27a2005-12-10 23:11:28 +0000311
njn57781862005-12-14 02:58:23 +0000312 # Print the Valgrind version if we are measuring more than one.
313 my $vgdirname = $vgdir;
314 chomp($vgdirname = `basename $vgdir`);
315 printf("%-10s:", $vgdirname);
316
317 # Native execution time
sewardj8e339a12006-10-14 14:04:42 +0000318 printf("%4.2fs", $tNative);
njnec0c27a2005-12-10 23:11:28 +0000319
njn57781862005-12-14 02:58:23 +0000320 foreach my $tool (@tools) {
njn341f98b2006-11-03 19:37:50 +0000321 # First two chars of toolname for abbreviation
322 my $tool_abbrev = $tool;
323 $tool_abbrev =~ s/(..).*/$1/;
njn341f98b2006-11-03 19:37:50 +0000324 printf(" %s:", $tool_abbrev);
philippe14ab1a32012-04-08 19:52:38 +0000325 my $run_outer_args = "";
Elliott Hughesed398002017-06-21 14:41:24 -0700326 if ((not defined $outer_args) || ($outer_args =~ /^\+/)) {
philippe14ab1a32012-04-08 19:52:38 +0000327 $run_outer_args =
328 " -v --command-line-only=yes"
329 . " --run-libc-freeres=no --sim-hints=enable-outer"
330 . " --smc-check=all-non-file"
331 . " --vgdb=no --trace-children=yes --read-var-info=no"
332 . " --suppressions=../tests/outer_inner.supp"
333 . " --memcheck:leak-check=full --memcheck:show-reachable=no"
334 . " --cachegrind:cache-sim=yes --cachegrind:branch-sim=yes"
335 . " --cachegrind:cachegrind-out-file=cachegrind.out.$vgdirname.$tool_abbrev.$name.%p"
336 . " --callgrind:cache-sim=yes --callgrind:branch-sim=yes"
337 . " --callgrind:dump-instr=yes --callgrind:collect-jumps=yes"
338 . " --callgrind:callgrind-out-file=callgrind.out.$vgdirname.$tool_abbrev.$name.%p"
339 . " ";
Elliott Hughesed398002017-06-21 14:41:24 -0700340 if (defined $outer_args) {
341 $outer_args =~ s/^\+(.*)/$1/;
342 $run_outer_args = $run_outer_args . $outer_args;
343 }
philippe14ab1a32012-04-08 19:52:38 +0000344 } else {
345 $run_outer_args = $outer_args;
346 }
347
348 my $vgsetup = "";
njn57781862005-12-14 02:58:23 +0000349 my $vgcmd = "$vgdir/coregrind/valgrind "
philippe215b2972012-07-24 19:47:46 +0000350 . "--command-line-only=yes --tool=$tool $extraopts -q "
njn276054a2006-11-03 19:30:33 +0000351 . "--memcheck:leak-check=no "
352 . "--trace-children=yes "
njn57781862005-12-14 02:58:23 +0000353 . "$vgopts ";
philippe14ab1a32012-04-08 19:52:38 +0000354 # Do the tool run(s).
355 if (defined $outer_valgrind ) {
356 # in an outer-inner setup, only set VALGRIND_LIB_INNER
357 $vgsetup = "VALGRIND_LIB_INNER=$vgdir/.in_place ";
358 $vgcmd = "$outer_valgrind "
359 . "--tool=" . $outer_tool . " "
philippe14ab1a32012-04-08 19:52:38 +0000360 . "--log-file=" . "$outer_tool.outer.log.$vgdirname.$tool_abbrev.$name.%p "
Elliott Hughesed398002017-06-21 14:41:24 -0700361 . "$run_outer_args "
philippe14ab1a32012-04-08 19:52:38 +0000362 . $vgcmd;
363 } else {
364 # Set both VALGRIND_LIB and VALGRIND_LIB_INNER
365 # in case this Valgrind was configured with --enable-inner. And
366 # also VALGRINDLIB, which was the old name for the variable, to
367 # allow comparison against old Valgrind versions (eg. 2.4.X).
368 $vgsetup = "VALGRINDLIB=$vgdir/.in_place "
369 . "VALGRIND_LIB=$vgdir/.in_place "
370 . "VALGRIND_LIB_INNER=$vgdir/.in_place ";
371 }
njn57781862005-12-14 02:58:23 +0000372 my $cmd = "$vgsetup $timecmd $vgcmd $prog $args";
373 my $tTool = time_prog($cmd, $n_reps);
njn30e23ac2005-12-15 17:22:37 +0000374 printf("%4.1fs (%4.1fx,", $tTool, $tTool/$tNative);
375
376 # If it's the first timing for this tool on this benchmark,
377 # record the time so we can get the percentage speedup of the
378 # subsequent Valgrinds. Otherwise, compute and print
379 # the speedup.
380 if (not defined $first_tTool{$tool}) {
381 $first_tTool{$tool} = $tTool;
njn715cc592006-03-27 00:39:43 +0000382 print(" -----)");
njn30e23ac2005-12-15 17:22:37 +0000383 } else {
384 my $speedup = 100 - (100 * $tTool / $first_tTool{$tool});
njn715cc592006-03-27 00:39:43 +0000385 printf("%5.1f%%)", $speedup);
njn30e23ac2005-12-15 17:22:37 +0000386 }
njn57781862005-12-14 02:58:23 +0000387
388 $num_timings_done++;
389
390 if (defined $cleanup) {
391 (system("$cleanup") == 0) or
392 print(" ($name cleanup operation failed: $cleanup)\n");
393 }
394 }
395 printf("\n");
njnec0c27a2005-12-10 23:11:28 +0000396 }
397
398 $num_tests_done++;
399}
400
401#----------------------------------------------------------------------------
402# Test one directory (and any subdirs)
403#----------------------------------------------------------------------------
404sub test_one_dir($$); # forward declaration
405
406sub test_one_dir($$)
407{
408 my ($dir, $prev_dirs) = @_;
409 $dir =~ s/\/$//; # trim a trailing '/'
410
njn356ffa32006-11-03 19:35:20 +0000411 chomp(my $initial_dir = `pwd`); # record where we started
412
njnec0c27a2005-12-10 23:11:28 +0000413 # Ignore dirs into which we should not recurse.
414 if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; }
415
416 chdir($dir) or die "Could not change into $dir\n";
417
418 # Nb: Don't prepend a '/' to the base directory
419 my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir;
420 my $dashes = "-" x (50 - length $full_dir);
421
422 my @fs = glob "*";
423 my $found_tests = (0 != (grep { $_ =~ /\.vgperf$/ } @fs));
424
425 if ($found_tests) {
426 print "-- Running tests in $full_dir $dashes\n";
427 }
428 foreach my $f (@fs) {
429 if (-d $f) {
430 test_one_dir($f, $full_dir);
431 } elsif ($f =~ /\.vgperf$/) {
432 do_one_test($full_dir, $f);
433 }
434 }
435 if ($found_tests) {
436 print "-- Finished tests in $full_dir $dashes\n";
437 }
438
njn356ffa32006-11-03 19:35:20 +0000439 chdir("$initial_dir");
njnec0c27a2005-12-10 23:11:28 +0000440}
441
442#----------------------------------------------------------------------------
443# Summarise results
444#----------------------------------------------------------------------------
445sub summarise_results
446{
447 printf("\n== %d programs, %d timings =================\n\n",
448 $num_tests_done, $num_timings_done);
449}
450
451#----------------------------------------------------------------------------
452# main()
453#----------------------------------------------------------------------------
philippe215b2972012-07-24 19:47:46 +0000454sub warn_about_EXTRA_REGTEST_OPTS()
455{
456 print "WARNING: \$EXTRA_REGTEST_OPTS is set. You probably don't want\n";
457 print "to run the perf tests with it set, unless you are doing some\n";
458 print "strange experiment, and/or you really know what you are doing.\n";
459 print "\n";
460}
njnec0c27a2005-12-10 23:11:28 +0000461
462# nuke VALGRIND_OPTS
463$ENV{"VALGRIND_OPTS"} = "";
464
philippe215b2972012-07-24 19:47:46 +0000465if ($ENV{"EXTRA_REGTEST_OPTS"}) {
466 print "\n";
467 warn_about_EXTRA_REGTEST_OPTS();
468}
469
njnec0c27a2005-12-10 23:11:28 +0000470my @fs = process_command_line();
471foreach my $f (@fs) {
472 if (-d $f) {
473 test_one_dir($f, "");
474 } else {
475 # Allow the .vgperf suffix to be given or omitted
476 if ($f =~ /.vgperf$/ && -r $f) {
477 # do nothing
478 } elsif (-r "$f.vgperf") {
479 $f = "$f.vgperf";
480 } else {
481 die "`$f' neither a directory nor a readable test file/name\n"
482 }
483 my $dir = `dirname $f`; chomp $dir;
484 my $file = `basename $f`; chomp $file;
485 chdir($dir) or die "Could not change into $dir\n";
486 do_one_test($dir, $file);
487 chdir($tests_dir);
488 }
489}
490summarise_results();
491
philippe215b2972012-07-24 19:47:46 +0000492if ($ENV{"EXTRA_REGTEST_OPTS"}) {
493 warn_about_EXTRA_REGTEST_OPTS();
494}
495
njnec0c27a2005-12-10 23:11:28 +0000496##--------------------------------------------------------------------##
497##--- end ---##
498##--------------------------------------------------------------------##