sigframe construction for x86-linux: ensure that ESP is correctly aligned before...
[valgrind.git] / perf / vg_perf.in
blobd3e8ce9270e5c25b2631d93125e48121df7c0f68
1 #! @PERL@
2 ##--------------------------------------------------------------------##
3 ##--- Valgrind performance testing script                  vg_perf ---##
4 ##--------------------------------------------------------------------##
6 #  This file is part of Valgrind, a dynamic binary instrumentation
7 #  framework.
9 #  Copyright (C) 2005-2017 Nicholas Nethercote
10 #     njn@valgrind.org
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.
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.
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.
27 #  The GNU General Public License is contained in the file COPYING.
29 #----------------------------------------------------------------------------
30 # usage: see usage message.
32 # 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/.
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)
39 #   - 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)
44 # The prerequisite command, if present, must return 0 otherwise the test is
45 # skipped.
46 # 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.
51 #----------------------------------------------------------------------------
53 use warnings;
54 use strict;
56 #----------------------------------------------------------------------------
57 # Global vars
58 #----------------------------------------------------------------------------
59 my $usage = <<END
60 usage: vg_perf [options] [files or dirs]
62   options for the user, with defaults in [ ], are:
63     -h --help             show this message
64     --reps=<n>            number of repeats for each program [1]
65     --tools=<t1,t2,t3>    tools to run [Nulgrind and Memcheck]
66     --vg=<dir>            top-level directory containing Valgrind to measure
67                           [Valgrind in the current directory, i.e. --vg=.]
68                           Can be specified multiple times.
69                           The "in-place" build is used.
70     --terse: terse output. Prints only program name and speedup's for specified
71       tools.
73     --outer-valgrind: run these Valgrind(s) under the given outer valgrind.
74       These Valgrind(s) must be configured with --enable-inner.
75     --outer-tool: tool to use by the outer valgrind (default cachegrind).
76     --outer-args: use this as outer tool args. If the outer args are starting
77       with +, the given outer args are appended to the outer args predefined
78       by vg_perf.
80   Any tools named in --tools must be present in all directories specified
81   with --vg.  (This is not checked.)
82   Use EXTRA_REGTEST_OPTS to supply extra args for all tests
83 END
86 # Test variables
87 my $vgopts;             # valgrind options
88 my $prog;               # test prog
89 my $args;               # test prog args
90 my $prereq;             # prerequisite test to satisfy before running test
91 my $cleanup;            # cleanup command to run
93 # Command line options
94 my $n_reps = 1;         # Run each test $n_reps times and choose the best one.
95 my @vgdirs;             # Dirs of the various Valgrinds being measured.
96 my @tools = ("none", "memcheck");   # tools being measured
97 my $terse = 0;          # Terse output.
99 # Outer valgrind to use, and args to use for it.
100 # If this is set, --valgrind should be set to the installed inner valgrind,
101 # and --valgrind-lib will be ignore
102 my $outer_valgrind;
103 my $outer_tool = "cachegrind";
104 my $outer_args;
107 my $num_tests_done   = 0;
108 my $num_timings_done = 0;
110 # Starting directory
111 chomp(my $tests_dir = `pwd`);
113 #----------------------------------------------------------------------------
114 # Process command line, setup
115 #----------------------------------------------------------------------------
117 # If $prog is a relative path, it prepends $dir to it.  Useful for two reasons:
119 # 1. Can prepend "." onto programs to avoid trouble with users who don't have
120 #    "." in their path (by making $dir = ".")
121 # 2. Can prepend the current dir to make the command absolute to avoid
122 #    subsequent trouble when we change directories.
124 # Also checks the program exists and is executable.
125 sub validate_program ($$$$) 
127     my ($dir, $prog, $must_exist, $must_be_executable) = @_;
129     # If absolute path, leave it alone.  If relative, make it
130     # absolute -- by prepending current dir -- so we can change
131     # dirs and still use it.
132     $prog = "$dir/$prog" if ($prog !~ /^\//);
133     if ($must_exist) {
134         (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n";
135     }
136     if ($must_be_executable) { 
137         (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n";
138     }
140     return $prog;
143 sub add_vgdir($)
145     my ($vgdir) = @_;
146     if ($vgdir !~ /^\//) { $vgdir = "$tests_dir/$vgdir"; }
147     push(@vgdirs, $vgdir);
150 sub process_command_line() 
152     my @fs;
153     
154     for my $arg (@ARGV) {
155         if ($arg =~ /^-/) {
156             if ($arg =~ /^--reps=(\d+)$/) {
157                 $n_reps = $1;
158                 if ($n_reps < 1) { die "bad --reps value: $n_reps\n"; }
159             } elsif ($arg =~ /^--vg=(.+)$/) {
160                 # Make dir absolute if not already
161                 add_vgdir($1);
162             } elsif ($arg =~ /^--tools=(.+)$/) {
163                 @tools = split(/,/, $1);
164             } elsif ($arg =~ /^--terse$/) {
165                 $terse = 1;
166             } elsif ($arg =~ /^--outer-valgrind=(.*)$/) {
167                 $outer_valgrind = $1;
168             } elsif ($arg =~ /^--outer-tool=(.*)$/) {
169                 $outer_tool = $1;
170             } elsif ($arg =~ /^--outer-args=(.*)$/) {
171                 $outer_args = $1;
172             } else {
173                 die $usage;
174             }
175         } else {
176             push(@fs, $arg);
177         }
178     }
180     # If no --vg options were specified, use the current tree.
181     if (0 == @vgdirs) {
182         add_vgdir($tests_dir);
183     }
185     (0 != @fs) or die "No test files or directories specified\n";
187     return @fs;
190 #----------------------------------------------------------------------------
191 # Read a .vgperf file
192 #----------------------------------------------------------------------------
193 sub read_vgperf_file($)
195     my ($f) = @_;
197     # Defaults.
198     ($vgopts, $prog, $args, $prereq, $cleanup)
199       = ("", undef, "", undef, undef, undef, undef);
201     open(INPUTFILE, "< $f") || die "File $f not openable\n";
203     while (my $line = <INPUTFILE>) {
204         if      ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
205             next;
206         } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) {
207             $vgopts = $1;
208         } elsif ($line =~ /^\s*prog:\s*(.*)$/) {
209             $prog = validate_program(".", $1, 1, 1);
210         } elsif ($line =~ /^\s*args:\s*(.*)$/) {
211             $args = $1;
212         } elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
213             $prereq = $1;
214         } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) {
215             $cleanup = $1;
216         } else {
217             die "Bad line in $f: $line\n";
218         }
219     }
220     close(INPUTFILE);
222     if (!defined $prog) {
223         $prog = "";     # allow no prog for testing error and --help cases
224     }
225     if (0 == @tools) {
226         die "vg_perf: missing 'tools' line in $f\n";
227     }
230 #----------------------------------------------------------------------------
231 # Do one test
232 #----------------------------------------------------------------------------
233 # Since most of the program time is spent in system() calls, need this to
234 # propagate a Ctrl-C enabling us to quit.
235 sub mysystem($) 
237     my ($cmd) = @_;
238     my $retval = system($cmd);
239     if ($retval == 2) { 
240         exit 1; 
241     } else {
242         return $retval;
243     }
246 # Run program N times, return the best user time.  Use the POSIX
247 # -p flag on /usr/bin/time so as to get something parseable on AIX.
248 sub time_prog($$)
250     my ($cmd, $n) = @_;
251     my $tmin = 999999;
252     for (my $i = 0; $i < $n; $i++) {
253         mysystem("echo '$cmd' > perf.cmd");
254         my $retval = mysystem("$cmd > perf.stdout 2> perf.stderr");
255         (0 == $retval) or 
256             die "\n*** Command returned non-zero ($retval)"
257               . "\n*** See perf.{cmd,stdout,stderr} to determine what went wrong.\n";
258         my $out = `cat perf.stderr`;
259         ($out =~ /[Uu]ser +([\d\.]+)/) or 
260             die "\n*** missing usertime in perf.stderr\n";
261         $tmin = $1 if ($1 < $tmin);
262     }
264     # Successful run; cleanup
265     unlink("perf.cmd");
266     unlink("perf.stderr");
267     unlink("perf.stdout");
269     # Avoid divisions by zero!
270     return (0 == $tmin ? 0.01 : $tmin);
273 sub do_one_test($$) 
275     my ($dir, $vgperf) = @_;
276     $vgperf =~ /^(.*)\.vgperf/;
277     my $name = $1;
278     my %first_tTool;    # For doing percentage speedups when comparing
279                         # multiple Valgrinds
281     read_vgperf_file($vgperf);
283     if (defined $prereq) {
284         if (system("$prereq") != 0) {
285             printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:");
286             return;
287         }
288     }
290     my $timecmd = "/usr/bin/time -p";
292     # Do the native run(s).
293     printf("-- $name --\n") if (@vgdirs > 1);
294     my $cmd     = "$timecmd $prog $args";
295     my $tNative = time_prog($cmd, $n_reps);
297     if (defined $outer_valgrind) {
298         $outer_valgrind = validate_program($tests_dir, $outer_valgrind, 1, 1);
299         foreach my $vgdir (@vgdirs) {
300             validate_program($vgdir, "./coregrind/valgrind", 1, 1);
301         }
302     } else {
303         foreach my $vgdir (@vgdirs) {
304             validate_program($vgdir, "./coregrind/valgrind", 1, 1);
305         }
306     }
308     # Pull any extra options (for example, --sanity-level=4)
309     # from $EXTRA_REGTEST_OPTS.
310     my $maybe_extraopts = $ENV{"EXTRA_REGTEST_OPTS"};
311     my $extraopts = $maybe_extraopts ?  $maybe_extraopts  : "";
313     foreach my $vgdir (@vgdirs) {
314         # Benchmark name
315         if (!$terse) {
316             printf("%-8s ", $name);
317         }
319         # Print the Valgrind version if we are measuring more than one.
320         my $vgdirname = $vgdir;
321         chomp($vgdirname = `basename $vgdir`);
322         printf("%-10s:", $vgdirname);
324         # Native execution time
325         if (!$terse) {
326             printf("%4.2fs", $tNative);
327         }
329         foreach my $tool (@tools) {
330             # First two chars of toolname for abbreviation
331             my $tool_abbrev = $tool;
332             $tool_abbrev =~ s/(..).*/$1/;
333             printf("  %s:", $tool_abbrev);
334             my $run_outer_args = "";
335             if ((not defined $outer_args) || ($outer_args =~ /^\+/)) {
336                 $run_outer_args = 
337                       " -v --command-line-only=yes"
338                     . " --run-libc-freeres=no --sim-hints=enable-outer"
339                     . " --smc-check=all-non-file"
340                     . " --vgdb=no --trace-children=yes --read-var-info=no"
341                     . " --suppressions=../tests/outer_inner.supp"
342                     . " --memcheck:leak-check=full --memcheck:show-reachable=no"
343                     . " --cachegrind:cache-sim=yes --cachegrind:branch-sim=yes"
344                     . " --cachegrind:cachegrind-out-file=cachegrind.out.$vgdirname.$tool_abbrev.$name.%p"
345                     . " --callgrind:cache-sim=yes --callgrind:branch-sim=yes"
346                     . " --callgrind:dump-instr=yes --callgrind:collect-jumps=yes"
347                     . " --callgrind:callgrind-out-file=callgrind.out.$vgdirname.$tool_abbrev.$name.%p"
348                     . " ";
349                  if (defined $outer_args) {
350                     $outer_args =~ s/^\+(.*)/$1/;
351                     $run_outer_args = $run_outer_args . $outer_args;
352                  }
353             } else {
354                 $run_outer_args = $outer_args;
355             }
357             my $vgsetup = "";
358             my $vgcmd   = "$vgdir/coregrind/valgrind "
359                         . "--command-line-only=yes --tool=$tool  $extraopts -q "
360                         . "--memcheck:leak-check=no "
361                         . "--trace-children=yes "
362                         . "$vgopts ";
363             # Do the tool run(s).
364             if (defined $outer_valgrind ) {
365                 # in an outer-inner setup, only set VALGRIND_LIB_INNER
366                 $vgsetup = "VALGRIND_LIB_INNER=$vgdir/.in_place ";
367                 $vgcmd   = "$outer_valgrind "
368                          . "--tool=" . $outer_tool . " "
369                          . "--log-file=" . "$outer_tool.outer.log.$vgdirname.$tool_abbrev.$name.%p "
370                          . "$run_outer_args "
371                          . $vgcmd;
372             } else {
373                 # Set both VALGRIND_LIB and VALGRIND_LIB_INNER
374                 # in case this Valgrind was configured with --enable-inner.  And
375                 # also VALGRINDLIB, which was the old name for the variable, to
376                 # allow comparison against old Valgrind versions (eg. 2.4.X).
377                 $vgsetup = "VALGRINDLIB=$vgdir/.in_place "
378                          . "VALGRIND_LIB=$vgdir/.in_place "
379                          . "VALGRIND_LIB_INNER=$vgdir/.in_place ";
380             }
381             my $cmd     = "$vgsetup $timecmd $vgcmd $prog $args";
382             my $tTool   = time_prog($cmd, $n_reps);
383             if (!$terse) {
384                 printf("%4.1fs (%4.1fx,", $tTool, $tTool/$tNative);
385             }
387             # If it's the first timing for this tool on this benchmark,
388             # record the time so we can get the percentage speedup of the
389             # subsequent Valgrinds.  Otherwise, compute and print
390             # the speedup.
391             if (not defined $first_tTool{$tool}) {
392                 $first_tTool{$tool} = $tTool;
393                 print(" -----");
394             } else {
395                 my $speedup = 100 - (100 * $tTool / $first_tTool{$tool});
396                 printf("%5.1f%%", $speedup);
397             }
398             if (!$terse) {
399                print(")");
400             }
402             $num_timings_done++;
404             if (defined $cleanup) {
405                 (system("$cleanup") == 0) or 
406                     print("  ($name cleanup operation failed: $cleanup)\n");
407             }
408         }
409         printf("\n");
410     }
412     $num_tests_done++;
415 #----------------------------------------------------------------------------
416 # Test one directory (and any subdirs)
417 #----------------------------------------------------------------------------
418 sub test_one_dir($$);    # forward declaration
420 sub test_one_dir($$) 
422     my ($dir, $prev_dirs) = @_;
423     $dir =~ s/\/$//;    # trim a trailing '/'
425     chomp(my $initial_dir = `pwd`);     # record where we started
427     # Ignore dirs into which we should not recurse.
428     if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; }
430     chdir($dir) or die "Could not change into $dir\n";
432     # Nb: Don't prepend a '/' to the base directory
433     my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir;
434     my $dashes = "-" x (50 - length $full_dir);
436     my @fs = glob "*";
437     my $found_tests = (0 != (grep { $_ =~ /\.vgperf$/ } @fs));
439     if ($found_tests) {
440         print "-- Running  tests in $full_dir $dashes\n";
441     }
442     foreach my $f (@fs) {
443         if (-d $f) {
444             test_one_dir($f, $full_dir);
445         } elsif ($f =~ /\.vgperf$/) {
446             do_one_test($full_dir, $f);
447         }
448     }
449     if ($found_tests) {
450         print "-- Finished tests in $full_dir $dashes\n";
451     }
453     chdir("$initial_dir");
456 #----------------------------------------------------------------------------
457 # Summarise results
458 #----------------------------------------------------------------------------
459 sub summarise_results 
461     printf("\n== %d programs, %d timings =================\n\n", 
462            $num_tests_done, $num_timings_done);
465 #----------------------------------------------------------------------------
466 # main()
467 #----------------------------------------------------------------------------
468 sub warn_about_EXTRA_REGTEST_OPTS()
470     print "WARNING: \$EXTRA_REGTEST_OPTS is set.  You probably don't want\n";
471     print "to run the perf tests with it set, unless you are doing some\n";
472     print "strange experiment, and/or you really know what you are doing.\n";
473     print "\n";
476 # nuke VALGRIND_OPTS
477 $ENV{"VALGRIND_OPTS"} = "";
479 if ($ENV{"EXTRA_REGTEST_OPTS"}) {
480     print "\n";
481     warn_about_EXTRA_REGTEST_OPTS();
484 my @fs = process_command_line();
485 foreach my $f (@fs) {
486     if (-d $f) {
487         test_one_dir($f, "");
488     } else { 
489         # Allow the .vgperf suffix to be given or omitted
490         if ($f =~ /.vgperf$/ && -r $f) {
491             # do nothing
492         } elsif (-r "$f.vgperf") {
493             $f = "$f.vgperf";
494         } else {
495             die "`$f' neither a directory nor a readable test file/name\n"
496         }
497         my $dir  = `dirname  $f`;   chomp $dir;
498         my $file = `basename $f`;   chomp $file;
499         chdir($dir) or die "Could not change into $dir\n";
500         do_one_test($dir, $file);
501         chdir($tests_dir);
502     }
504 summarise_results();
506 if ($ENV{"EXTRA_REGTEST_OPTS"}) {
507     warn_about_EXTRA_REGTEST_OPTS();
510 ##--------------------------------------------------------------------##
511 ##--- end                                                          ---##
512 ##--------------------------------------------------------------------##