blob: 07344813037cc9de4e61152a2cc3beadbdcab7ee [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#----------------------------------------------------------------------------
30# usage: vg_perf [options] <dirs | files>
31#
32# Options:
33# --all: run tests in all subdirs
34# --valgrind: valgrind to use (the directory it's in). Default is the one
35# in the current tree.
36#
37# The easiest way is to run all tests in valgrind/ with (assuming you installed
38# in $PREFIX):
39#
40# perl perf/vg_perf --all
41#
42# You can specify individual files to test, or whole directories, or both.
43# Directories are traversed recursively, except for ones named, for example,
44# CVS/ or docs/.
45#
46# Each test is defined in a file <test>.vgperf, containing one or more of the
47# following lines, in any order:
48# - prog: <prog to run> (compulsory)
49# - tools: <Valgrind tools> (compulsory)
50# - args: <args for prog> (default: none)
51# - vgopts: <Valgrind options> (default: none)
52# - prereq: <prerequisite command> (default: none)
53# - cleanup: <post-test cleanup cmd to run> (default: none)
54#
55# The prerequisite command, if present, must return 0 otherwise the test is
56# skipped.
57#----------------------------------------------------------------------------
58
59use warnings;
60use strict;
61
62#----------------------------------------------------------------------------
63# Global vars
64#----------------------------------------------------------------------------
njnee45f1d2005-12-13 21:55:16 +000065my $usage = <<END
66usage: vg_perf [options] [files or dirs]
njnec0c27a2005-12-10 23:11:28 +000067
njnee45f1d2005-12-13 21:55:16 +000068 options for the user, with defaults in [ ], are:
69 -h --help show this message
70 --all run all tests under this directory
71 --reps number of repeats for each program [3]
72
73END
74;
njnec0c27a2005-12-10 23:11:28 +000075
76# Test variables
77my $vgopts; # valgrind options
78my $prog; # test prog
79my $args; # test prog args
80my $prereq; # prerequisite test to satisfy before running test
81my $cleanup; # cleanup command to run
82my @tools; # which tools are we measuring the program with
83
84# Abbreviations used in output
85my %toolnames = (
86 none => "nl",
87 memcheck => "mc",
88 cachegrind => "cg",
89 massif => "ms"
90);
91
92# We run each program this many times and choose the best time.
njnee45f1d2005-12-13 21:55:16 +000093my $n_reps = 3;
njnec0c27a2005-12-10 23:11:28 +000094
95my $num_tests_done = 0;
96my $num_timings_done = 0;
97
98# Starting directory
99chomp(my $tests_dir = `pwd`);
100
101# Directory of the Valgrind being measured. Default is the one in the
102# current tree.
103my $vg_dir = $tests_dir;
104
105#----------------------------------------------------------------------------
106# Process command line, setup
107#----------------------------------------------------------------------------
108
109# If $prog is a relative path, it prepends $dir to it. Useful for two reasons:
110#
111# 1. Can prepend "." onto programs to avoid trouble with users who don't have
112# "." in their path (by making $dir = ".")
113# 2. Can prepend the current dir to make the command absolute to avoid
114# subsequent trouble when we change directories.
115#
116# Also checks the program exists and is executable.
117sub validate_program ($$$$)
118{
119 my ($dir, $prog, $must_exist, $must_be_executable) = @_;
120
121 # If absolute path, leave it alone. If relative, make it
122 # absolute -- by prepending current dir -- so we can change
123 # dirs and still use it.
124 $prog = "$dir/$prog" if ($prog !~ /^\//);
125 if ($must_exist) {
126 (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n";
127 }
128 if ($must_be_executable) {
129 (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n";
130 }
131
132 return $prog;
133}
134
135sub validate_tools($)
136{
137 # XXX: should check they exist!
138 my ($toolnames) = @_;
139 my @t = split(/\s+/, $toolnames);
140 return @t;
141}
142
143sub process_command_line()
144{
145 my $alldirs = 0;
146 my @fs;
147
148 for my $arg (@ARGV) {
149 if ($arg =~ /^-/) {
150 if ($arg =~ /^--all$/) {
151 $alldirs = 1;
152 } elsif ($arg =~ /^--valgrind=(.*)$/) {
153 $vg_dir = $1;
njnee45f1d2005-12-13 21:55:16 +0000154 } elsif ($arg =~ /^--reps=(\d)+$/) {
155 $n_reps = $1;
njnec0c27a2005-12-10 23:11:28 +0000156 } else {
157 die $usage;
158 }
159 } else {
160 push(@fs, $arg);
161 }
162 }
163 # Make $vg_dir absolute if not already
164 if ($vg_dir !~ /^\//) { $vg_dir = "$tests_dir/$vg_dir"; }
165 validate_program($vg_dir, "./coregrind/valgrind", 1, 1);
166
167 if ($alldirs) {
168 @fs = ();
169 foreach my $f (glob "*") {
170 push(@fs, $f) if (-d $f);
171 }
172 }
173
174 (0 != @fs) or die "No test files or directories specified\n";
175
176 return @fs;
177}
178
179#----------------------------------------------------------------------------
180# Read a .vgperf file
181#----------------------------------------------------------------------------
182sub read_vgperf_file($)
183{
184 my ($f) = @_;
185
186 # Defaults.
187 ($vgopts, $prog, $args, $prereq, $cleanup)
188 = ("", undef, "", undef, undef, undef, undef);
189
190 open(INPUTFILE, "< $f") || die "File $f not openable\n";
191
192 while (my $line = <INPUTFILE>) {
193 if ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
194 next;
195 } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) {
196 $vgopts = $1;
197 } elsif ($line =~ /^\s*prog:\s*(.*)$/) {
njnc9582a22005-12-13 21:44:48 +0000198 $prog = validate_program(".", $1, 1, 1);
njnec0c27a2005-12-10 23:11:28 +0000199 } elsif ($line =~ /^\s*tools:\s*(.*)$/) {
200 @tools = validate_tools($1);
201 } elsif ($line =~ /^\s*args:\s*(.*)$/) {
202 $args = $1;
203 } elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
204 $prereq = $1;
205 } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) {
206 $cleanup = $1;
207 } else {
208 die "Bad line in $f: $line\n";
209 }
210 }
211 close(INPUTFILE);
212
213 if (!defined $prog) {
214 $prog = ""; # allow no prog for testing error and --help cases
215 }
216 if (0 == @tools) {
217 die "vg_perf: missing 'tools' line in $f\n";
218 }
219}
220
221#----------------------------------------------------------------------------
222# Do one test
223#----------------------------------------------------------------------------
224# Since most of the program time is spent in system() calls, need this to
225# propagate a Ctrl-C enabling us to quit.
226sub mysystem($)
227{
njnecb47442005-12-13 16:54:58 +0000228 my ($cmd) = @_;
229 my $retval = system($cmd);
230 if ($retval == 2) {
231 exit 1;
232 } else {
233 return $retval;
234 }
njnec0c27a2005-12-10 23:11:28 +0000235}
236
njn02383bc2005-12-13 20:23:38 +0000237# Run program N times, return the best user time.
njnec0c27a2005-12-10 23:11:28 +0000238sub time_prog($$)
239{
240 my ($cmd, $n) = @_;
241 my $tmin = 999999;
242 for (my $i = 0; $i < $n; $i++) {
njnecb47442005-12-13 16:54:58 +0000243 mysystem("echo '$cmd' > perf.cmd");
244 my $retval = mysystem("$cmd > perf.stdout 2> perf.stderr");
245 (0 == $retval) or
njn02383bc2005-12-13 20:23:38 +0000246 die "\n*** Command returned non-zero: $cmd"
njnecb47442005-12-13 16:54:58 +0000247 . "\n*** See perf.{cmd,stdout,stderr} to diagnose what went wrong.\n";
248 my $out = `cat perf.stderr`;
njn02383bc2005-12-13 20:23:38 +0000249 ($out =~ /usertime: ([\d\.]+)s/) or
250 die "\n*** missing usertime in perf.stderr\n";
njnec0c27a2005-12-10 23:11:28 +0000251 $tmin = $1 if ($1 < $tmin);
252 }
253 return $tmin;
254}
255
256sub do_one_test($$)
257{
258 my ($dir, $vgperf) = @_;
259 $vgperf =~ /^(.*)\.vgperf/;
260 my $name = $1;
261
262 read_vgperf_file($vgperf);
263
264 if (defined $prereq) {
265 if (system("$prereq") != 0) {
266 printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:");
267 return;
268 }
269 }
270
271 printf("%-12s", "$name:");
272
njn02383bc2005-12-13 20:23:38 +0000273 my $timecmd = "/usr/bin/time -f 'usertime: %Us'";
njnec0c27a2005-12-10 23:11:28 +0000274
275 # Do the native run(s).
276 printf("nt:");
277 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 printf("%4.1fs ", $tNative);
280
281 foreach my $tool (@tools) {
282 (defined $toolnames{$tool}) or
283 die "unknown tool $tool, please add to %toolnames\n";
284
285 # Do the tool run(s). Set both VALGRIND_LIB and VALGRIND_LIB_INNER
286 # in case this Valgrind was configured with --enable-inner.
287 printf("%s:", $toolnames{$tool});
288 my $vgsetup = "VALGRIND_LIB=$vg_dir/.in_place "
289 . "VALGRIND_LIB_INNER=$vg_dir/.in_place ";
290 my $vgcmd = "$vg_dir/coregrind/valgrind "
291 . "--command-line-only=yes --tool=$tool -q "
292 . "--memcheck:leak-check=no --addrcheck:leak-check=no "
293 . "$vgopts ";
294 my $cmd = "$vgsetup $timecmd $vgcmd $prog $args";
njnee45f1d2005-12-13 21:55:16 +0000295 my $tTool = time_prog($cmd, $n_reps);
njnec0c27a2005-12-10 23:11:28 +0000296 printf("%4.1fs (%4.1fx) ", $tTool, $tTool/$tNative);
297
298 $num_timings_done++;
299 }
300 printf("\n");
301
302 if (defined $cleanup) {
303 (system("$cleanup") == 0) or
304 print(" ($name cleanup operation failed: $cleanup)\n");
305 }
306
307 $num_tests_done++;
308}
309
310#----------------------------------------------------------------------------
311# Test one directory (and any subdirs)
312#----------------------------------------------------------------------------
313sub test_one_dir($$); # forward declaration
314
315sub test_one_dir($$)
316{
317 my ($dir, $prev_dirs) = @_;
318 $dir =~ s/\/$//; # trim a trailing '/'
319
320 # Ignore dirs into which we should not recurse.
321 if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; }
322
323 chdir($dir) or die "Could not change into $dir\n";
324
325 # Nb: Don't prepend a '/' to the base directory
326 my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir;
327 my $dashes = "-" x (50 - length $full_dir);
328
329 my @fs = glob "*";
330 my $found_tests = (0 != (grep { $_ =~ /\.vgperf$/ } @fs));
331
332 if ($found_tests) {
333 print "-- Running tests in $full_dir $dashes\n";
334 }
335 foreach my $f (@fs) {
336 if (-d $f) {
337 test_one_dir($f, $full_dir);
338 } elsif ($f =~ /\.vgperf$/) {
339 do_one_test($full_dir, $f);
340 }
341 }
342 if ($found_tests) {
343 print "-- Finished tests in $full_dir $dashes\n";
344 }
345
346 chdir("..");
347}
348
349#----------------------------------------------------------------------------
350# Summarise results
351#----------------------------------------------------------------------------
352sub summarise_results
353{
354 printf("\n== %d programs, %d timings =================\n\n",
355 $num_tests_done, $num_timings_done);
356}
357
358#----------------------------------------------------------------------------
359# main()
360#----------------------------------------------------------------------------
361
362# nuke VALGRIND_OPTS
363$ENV{"VALGRIND_OPTS"} = "";
364
365my @fs = process_command_line();
366foreach my $f (@fs) {
367 if (-d $f) {
368 test_one_dir($f, "");
369 } else {
370 # Allow the .vgperf suffix to be given or omitted
371 if ($f =~ /.vgperf$/ && -r $f) {
372 # do nothing
373 } elsif (-r "$f.vgperf") {
374 $f = "$f.vgperf";
375 } else {
376 die "`$f' neither a directory nor a readable test file/name\n"
377 }
378 my $dir = `dirname $f`; chomp $dir;
379 my $file = `basename $f`; chomp $file;
380 chdir($dir) or die "Could not change into $dir\n";
381 do_one_test($dir, $file);
382 chdir($tests_dir);
383 }
384}
385summarise_results();
386
387##--------------------------------------------------------------------##
388##--- end ---##
389##--------------------------------------------------------------------##