diff options
author | njn <njn@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2005-12-10 23:11:28 +0000 |
---|---|---|
committer | njn <njn@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2005-12-10 23:11:28 +0000 |
commit | ec0c27a40ae0777cc5c5f96f219d1d72cbcaafd6 (patch) | |
tree | 114d468c48914cd3926c8fec24e3a9eadd70171e /perf | |
parent | 932d5ed2053e80768149a0baa220a51c60889e03 (diff) |
First attempt at some performance tracking tools. Includes a script vg_perf
(use "make perf" to run) that executes test programs and times their
slowdowns under various tools. It works a lot like the vg_regtest script.
It's a bit rough around the edges -- eg. you can't currently directly
compare two different versions of Valgrind, which would be useful -- but it
is a good start.
There are currently two test programs in perf/. More will be added as time
goes on. This stuff will be built on so that performance changes can be
tracked over time.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5323 a5019735-40e9-0310-863c-91ae7b9d1cf9
Diffstat (limited to 'perf')
-rw-r--r-- | perf/Makefile.am | 17 | ||||
-rw-r--r-- | perf/ffbench.c | 382 | ||||
-rw-r--r-- | perf/ffbench.vgperf | 2 | ||||
-rw-r--r-- | perf/sarp.c | 46 | ||||
-rw-r--r-- | perf/sarp.vgperf | 2 | ||||
-rw-r--r-- | perf/vg_perf.in | 368 |
6 files changed, 817 insertions, 0 deletions
diff --git a/perf/Makefile.am b/perf/Makefile.am new file mode 100644 index 00000000..d703a679 --- /dev/null +++ b/perf/Makefile.am @@ -0,0 +1,17 @@ + +noinst_SCRIPTS = vg_perf + +EXTRA_DIST = $(noinst_SCRIPTS) \ + ffbench.vgperf \ + sarp.vgperf + +check_PROGRAMS = \ + ffbench sarp + +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -g -O +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CXXFLAGS = $(AM_CFLAGS) + +# Extra stuff +ffbench_LDADD = -lm + diff --git a/perf/ffbench.c b/perf/ffbench.c new file mode 100644 index 00000000..e7e35233 --- /dev/null +++ b/perf/ffbench.c @@ -0,0 +1,382 @@ +// This small program computes a Fast Fourier Transform. It tests +// Valgrind's handling of FP operations. It is representative of all +// programs that do a lot of FP operations. + +// This program was taken from http://www.fourmilab.ch/. The front page of +// that site says: +// +// "Except for a few clearly-marked exceptions, all the material on this +// site is in the public domain and may be used in any manner without +// permission, restriction, attribution, or compensation." + +/* + + Two-dimensional FFT benchmark + + Designed and implemented by John Walker in April of 1989. + + This benchmark executes a specified number of passes (default + 20) through a loop in which each iteration performs a fast + Fourier transform of a square matrix (default size 256x256) of + complex numbers (default precision double), followed by the + inverse transform. After all loop iterations are performed + the results are checked against known correct values. + + This benchmark is intended for use on C implementations which + define "int" as 32 bits or longer and permit allocation and + direct addressing of arrays larger than one megabyte. + + If CAPOUT is defined, the result after all iterations is + written as a CA Lab pattern file. This is intended for + debugging in case horribly wrong results are obtained on a + given machine. + + Archival timings are run with the definitions below set as + follows: Float = double, Asize = 256, Passes = 20, CAPOUT not + defined. + + Time (seconds) System + + 2393.93 Sun 3/260, SunOS 3.4, C, "-f68881 -O". + (John Walker). + + 1928 Macintosh IIx, MPW C 3.0, "-mc68020 + -mc68881 -elems881 -m". (Hugh Hoover). + + 1636.1 Sun 4/110, "cc -O3 -lm". (Michael McClary). + The suspicion is that this is software + floating point. + + 1556.7 Macintosh II, A/UX, "cc -O -lm" + (Michael McClary). + + 1388.8 Sun 386i/250, SunOS 4.0.1 C + "-O /usr/lib/trig.il". (James Carrington). + + 1331.93 Sun 3/60, SunOS 4.0.1, C, + "-O4 -f68881 /usr/lib/libm.il" + (Bob Elman). + + 1204.0 Apollo Domain DN4000, C, "-cpu 3000 -opt 4". + (Sam Crupi). + + 1174.66 Compaq 386/25, SCO Xenix 386 C. + (Peter Shieh). + + 1068 Compaq 386/25, SCO Xenix 386, + Metaware High C. (Robert Wenig). + + 1064.0 Sun 3/80, SunOS 4.0.3 Beta C + "-O3 -f68881 /usr/lib/libm.il". (James Carrington). + + 1061.4 Compaq 386/25, SCO Xenix, High C 1.4. + (James Carrington). + + 1059.79 Compaq 386/25, 387/25, High C 1.4, + DOS|Extender 2.2, 387 inline code + generation. (Nathan Bender). + + 777.14 Compaq 386/25, IIT 3C87-25 (387 Compatible), + High C 1.5, DOS|Extender 2.2, 387 inline + code generation. (Nathan Bender). + + 751 Compaq DeskPro 386/33, High C 1.5 + DOS|Extender, + 387 code generation. (James Carrington). + + 431.44 Compaq 386/25, Weitek 3167-25, DOS 3.31, + High C 1.4, DOS|Extender, Weitek code generation. + (Nathan Bender). + + 344.9 Compaq 486/25, Metaware High C 1.6, Phar Lap + DOS|Extender, in-line floating point. (Nathan + Bender). + + 324.2 Data General Motorola 88000, 16 Mhz, Gnu C. + + 323.1 Sun 4/280, C, "-O4". (Eric Hill). + + 254 Compaq SystemPro 486/33, High C 1.5 + DOS|Extender, + 387 code generation. (James Carrington). + + 242.8 Silicon Graphics Personal IRIS, MIPS R2000A, + 12.5 Mhz, "-O3" (highest level optimisation). + (Mike Zentner). + + 233.0 Sun SPARCStation 1, C, "-O4", SunOS 4.0.3. + (Nathan Bender). + + 187.30 DEC PMAX 3100, MIPS 2000 chip. + (Robert Wenig). + + 120.46 Sun SparcStation 2, C, "-O4", SunOS 4.1.1. + (John Walker). + + 120.21 DEC 3MAX, MIPS 3000, "-O4". + + 98.0 Intel i860 experimental environment, + OS/2, data caching disabled. (Kern + Sibbald). + + 34.9 Silicon Graphics Indigo², MIPS R4400, + 175 Mhz, IRIX 5.2, "-O". + + 32.4 Pentium 133, Windows NT, Microsoft Visual + C++ 4.0. + + 17.25 Silicon Graphics Indigo², MIPS R4400, + 175 Mhz, IRIX 6.5, "-O3". + + 14.10 Dell Dimension XPS R100, Pentium II 400 MHz, + Windows 98, Microsoft Visual C 5.0. + + 10.7 Hewlett-Packard Kayak XU 450Mhz Pentium II, + Microsoft Visual C++ 6.0, Windows NT 4.0sp3. (Nathan Bender). + + 5.09 Sun Ultra 2, UltraSPARC V9, 300 MHz, gcc -O3. + + 0.846 Dell Inspiron 9100, Pentium 4, 3.4 GHz, gcc -O3. + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> + +/* The program may be run with Float defined as either float or + double. With IEEE arithmetic, the same answers are generated for + either floating point mode. */ + +#define Float double /* Floating point type used in FFT */ + +#define Asize 256 /* Array edge size */ +#define Passes 20 /* Number of FFT/Inverse passes */ + +#define max(a,b) ((a)>(b)?(a):(b)) +#define min(a,b) ((a)<=(b)?(a):(b)) + +#ifndef unix +#ifndef WIN32 +extern char *farmalloc(long s); +#define malloc(x) farmalloc(x) +#endif +#define FWMODE "wb" +#else +#define FWMODE "w" +#endif + +/* + + Multi-dimensional fast Fourier transform + + Adapted from Press et al., "Numerical Recipes in C". + +*/ + +#define SWAP(a,b) tempr=(a); (a)=(b); (b)=tempr + +static void fourn(data, nn, ndim, isign) + Float data[]; + int nn[], ndim, isign; +{ + register int i1, i2, i3; + int i2rev, i3rev, ip1, ip2, ip3, ifp1, ifp2; + int ibit, idim, k1, k2, n, nprev, nrem, ntot; + Float tempi, tempr; + double theta, wi, wpi, wpr, wr, wtemp; + + ntot = 1; + for (idim = 1; idim <= ndim; idim++) + ntot *= nn[idim]; + nprev = 1; + for (idim = ndim; idim >= 1; idim--) { + n = nn[idim]; + nrem = ntot / (n * nprev); + ip1 = nprev << 1; + ip2 = ip1 * n; + ip3 = ip2 * nrem; + i2rev = 1; + for (i2 = 1; i2 <= ip2; i2 += ip1) { + if (i2 < i2rev) { + for (i1 = i2; i1 <= i2 + ip1 - 2; i1 += 2) { + for (i3 = i1; i3 <= ip3; i3 += ip2) { + i3rev = i2rev + i3 - i2; + SWAP(data[i3], data[i3rev]); + SWAP(data[i3 + 1], data[i3rev + 1]); + } + } + } + ibit = ip2 >> 1; + while (ibit >= ip1 && i2rev > ibit) { + i2rev -= ibit; + ibit >>= 1; + } + i2rev += ibit; + } + ifp1 = ip1; + while (ifp1 < ip2) { + ifp2 = ifp1 << 1; + theta = isign * 6.28318530717959 / (ifp2 / ip1); + wtemp = sin(0.5 * theta); + wpr = -2.0 * wtemp * wtemp; + wpi = sin(theta); + wr = 1.0; + wi = 0.0; + for (i3 = 1; i3 <= ifp1; i3 += ip1) { + for (i1 = i3; i1 <= i3 + ip1 - 2; i1 += 2) { + for (i2 = i1; i2 <= ip3; i2 += ifp2) { + k1 = i2; + k2 = k1 + ifp1; + tempr = wr * data[k2] - wi * data[k2 + 1]; + tempi = wr * data[k2 + 1] + wi * data[k2]; + data[k2] = data[k1] - tempr; + data[k2 + 1] = data[k1 + 1] - tempi; + data[k1] += tempr; + data[k1 + 1] += tempi; + } + } + wr = (wtemp = wr) * wpr - wi * wpi + wr; + wi = wi * wpr + wtemp * wpi + wi; + } + ifp1 = ifp2; + } + nprev *= n; + } +} +#undef SWAP + +int main() +{ + int i, j, k, l, m, npasses = Passes, faedge; + Float *fdata /* , *fd */ ; + static int nsize[] = {0, 0, 0}; + long fanum, fasize; + double mapbase, mapscale, /* x, */ rmin, rmax, imin, imax; + + faedge = Asize; /* FFT array edge size */ + fanum = faedge * faedge; /* Elements in FFT array */ + fasize = ((fanum + 1) * 2 * sizeof(Float)); /* FFT array size */ + nsize[1] = nsize[2] = faedge; + + fdata = (Float *) malloc(fasize); + if (fdata == NULL) { + fprintf(stdout, "Can't allocate data array.\n"); + exit(1); + } + + /* Generate data array to process. */ + +#define Re(x,y) fdata[1 + (faedge * (x) + (y)) * 2] +#define Im(x,y) fdata[2 + (faedge * (x) + (y)) * 2] + + memset(fdata, 0, fasize); + for (i = 0; i < faedge; i++) { + for (j = 0; j < faedge; j++) { + if (((i & 15) == 8) || ((j & 15) == 8)) + Re(i, j) = 128.0; + } + } + + for (i = 0; i < npasses; i++) { +/*printf("Pass %d\n", i);*/ + /* Transform image to frequency domain. */ + fourn(fdata, nsize, 2, 1); + + /* Back-transform to image. */ + fourn(fdata, nsize, 2, -1); + } + + { + double r, ij, ar, ai; + rmin = 1e10; rmax = -1e10; + imin = 1e10; imax = -1e10; + ar = 0; + ai = 0; + + for (i = 1; i <= fanum; i += 2) { + r = fdata[i]; + ij = fdata[i + 1]; + ar += r; + ai += ij; + rmin = min(r, rmin); + rmax = max(r, rmax); + imin = min(ij, imin); + imax = max(ij, imax); + } +#ifdef DEBUG + printf("Real min %.4g, max %.4g. Imaginary min %.4g, max %.4g.\n", + rmin, rmax, imin, imax); + printf("Average real %.4g, imaginary %.4g.\n", + ar / fanum, ai / fanum); +#endif + mapbase = rmin; + mapscale = 255 / (rmax - rmin); + } + + /* See if we got the right answers. */ + + m = 0; + for (i = 0; i < faedge; i++) { + for (j = 0; j < faedge; j++) { + k = (Re(i, j) - mapbase) * mapscale; + l = (((i & 15) == 8) || ((j & 15) == 8)) ? 255 : 0; + if (k != l) { + m++; + fprintf(stdout, + "Wrong answer at (%d,%d)! Expected %d, got %d.\n", + i, j, l, k); + } + } + } + if (m == 0) { + fprintf(stdout, "%d passes. No errors in results.\n", npasses); + } else { + fprintf(stdout, "%d passes. %d errors in results.\n", + npasses, m); + } + +#ifdef CAPOUT + + /* Output the result of the transform as a CA Lab pattern + file for debugging. */ + + { +#define SCRX 322 +#define SCRY 200 +#define SCRN (SCRX * SCRY) + unsigned char patarr[SCRY][SCRX]; + FILE *fp; + +/* Map user external state numbers to internal state index */ + +#define UtoI(x) (((((x) >> 1) & 0x7F) | ((x) << 7)) & 0xFF) + + /* Copy data from FFT buffer to map. */ + + memset(patarr, 0, sizeof patarr); + l = (SCRX - faedge) / 2; + m = (faedge > SCRY) ? 0 : ((SCRY - faedge) / 2); + for (i = 1; i < faedge; i++) { + for (j = 0; j < min(SCRY, faedge); j++) { + k = (Re(i, j) - mapbase) * mapscale; + patarr[j + m][i + l] = UtoI(k); + } + } + + /* Dump pattern map to file. */ + + fp = fopen("fft.cap", "w"); + if (fp == NULL) { + fprintf(stdout, "Cannot open output file.\n"); + exit(0); + } + putc(':', fp); + putc(1, fp); + fwrite(patarr, SCRN, 1, fp); + putc(6, fp); + fclose(fp); + } +#endif + + return 0; +} diff --git a/perf/ffbench.vgperf b/perf/ffbench.vgperf new file mode 100644 index 00000000..98dae55c --- /dev/null +++ b/perf/ffbench.vgperf @@ -0,0 +1,2 @@ +prog: ffbench +tools: none memcheck diff --git a/perf/sarp.c b/perf/sarp.c new file mode 100644 index 00000000..04518d61 --- /dev/null +++ b/perf/sarp.c @@ -0,0 +1,46 @@ +// This artificial program allocates and deallocates a lot of large objects +// on the stack. It is a stress test for Memcheck's set_address_range_perms +// (sarp) function. Pretty much all Valgrind versions up to 3.1.X do very +// badly on it, ie. a slowdown of at least 100x. +// +// It is representative of tsim_arch, the simulator for the University of +// Texas's TRIPS processor, whose performance under Valgrind is dominated by +// the handling of one frequently-called function that allocates 8348 bytes +// on the stack. + +#include <assert.h> +#include <time.h> + +#define REPS 1000*1000 + +int f(int i) +{ + // This nonsense is just to ensure that the compiler does not optimise + // away the stack allocation. + char big_array[8348]; + big_array[0] = 12; + big_array[2333] = 34; + big_array[5678] = 56; + big_array[8347] = 78; + assert( 8000 == (&big_array[8100] - &big_array[100]) ); + return big_array[i]; +} + +int main(void) +{ + int i, sum = 0; + + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = 100*1000*1000; // 0.1s + + // Pause for a bit so that the native run-time is not 0.00, which leads + // to ridiculous slow-down figures. + nanosleep(&req, NULL); + + for (i = 0; i < REPS; i++) { + sum += f(i & 0xff); + } + return sum % 256; +} + diff --git a/perf/sarp.vgperf b/perf/sarp.vgperf new file mode 100644 index 00000000..83c634a2 --- /dev/null +++ b/perf/sarp.vgperf @@ -0,0 +1,2 @@ +prog: sarp +tools: none memcheck diff --git a/perf/vg_perf.in b/perf/vg_perf.in new file mode 100644 index 00000000..91991520 --- /dev/null +++ b/perf/vg_perf.in @@ -0,0 +1,368 @@ +#! @PERL@ +##--------------------------------------------------------------------## +##--- Valgrind performance testing script vg_perf ---## +##--------------------------------------------------------------------## + +# This file is part of Valgrind, a dynamic binary instrumentation +# framework. +# +# Copyright (C) 2005 Nicholas Nethercote +# njn@valgrind.org +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. +# +# The GNU General Public License is contained in the file COPYING. + +#---------------------------------------------------------------------------- +# usage: vg_perf [options] <dirs | files> +# +# Options: +# --all: run tests in all subdirs +# --valgrind: valgrind to use (the directory it's in). Default is the one +# in the current tree. +# +# The easiest way is to run all tests in valgrind/ with (assuming you installed +# in $PREFIX): +# +# perl perf/vg_perf --all +# +# You can specify individual files to test, or whole directories, or both. +# Directories are traversed recursively, except for ones named, for example, +# CVS/ or docs/. +# +# Each test is defined in a file <test>.vgperf, containing one or more of the +# following lines, in any order: +# - prog: <prog to run> (compulsory) +# - tools: <Valgrind tools> (compulsory) +# - args: <args for prog> (default: none) +# - vgopts: <Valgrind options> (default: none) +# - prereq: <prerequisite command> (default: none) +# - cleanup: <post-test cleanup cmd to run> (default: none) +# +# The prerequisite command, if present, must return 0 otherwise the test is +# skipped. +#---------------------------------------------------------------------------- + +use warnings; +use strict; + +#---------------------------------------------------------------------------- +# Global vars +#---------------------------------------------------------------------------- +my $usage="vg_perf [--all, --valgrind]\n"; + +my $tmp="vg_perf.tmp.$$"; + +# Test variables +my $vgopts; # valgrind options +my $prog; # test prog +my $args; # test prog args +my $prereq; # prerequisite test to satisfy before running test +my $cleanup; # cleanup command to run +my @tools; # which tools are we measuring the program with + +# Abbreviations used in output +my %toolnames = ( + none => "nl", + memcheck => "mc", + cachegrind => "cg", + massif => "ms" +); + +# We run each program this many times and choose the best time. +my $n_runs = 3; + +my $num_tests_done = 0; +my $num_timings_done = 0; + +# Starting directory +chomp(my $tests_dir = `pwd`); + +# Directory of the Valgrind being measured. Default is the one in the +# current tree. +my $vg_dir = $tests_dir; + +#---------------------------------------------------------------------------- +# Process command line, setup +#---------------------------------------------------------------------------- + +# If $prog is a relative path, it prepends $dir to it. Useful for two reasons: +# +# 1. Can prepend "." onto programs to avoid trouble with users who don't have +# "." in their path (by making $dir = ".") +# 2. Can prepend the current dir to make the command absolute to avoid +# subsequent trouble when we change directories. +# +# Also checks the program exists and is executable. +sub validate_program ($$$$) +{ + my ($dir, $prog, $must_exist, $must_be_executable) = @_; + + # If absolute path, leave it alone. If relative, make it + # absolute -- by prepending current dir -- so we can change + # dirs and still use it. + $prog = "$dir/$prog" if ($prog !~ /^\//); + if ($must_exist) { + (-f $prog) or die "vg_perf: '$prog' not found or not a file ($dir)\n"; + } + if ($must_be_executable) { + (-x $prog) or die "vg_perf: '$prog' not executable ($dir)\n"; + } + + return $prog; +} + +sub validate_tools($) +{ + # XXX: should check they exist! + my ($toolnames) = @_; + my @t = split(/\s+/, $toolnames); + return @t; +} + +sub process_command_line() +{ + my $alldirs = 0; + my @fs; + + for my $arg (@ARGV) { + if ($arg =~ /^-/) { + if ($arg =~ /^--all$/) { + $alldirs = 1; + } elsif ($arg =~ /^--valgrind=(.*)$/) { + $vg_dir = $1; + } else { + die $usage; + } + } else { + push(@fs, $arg); + } + } + # Make $vg_dir absolute if not already + if ($vg_dir !~ /^\//) { $vg_dir = "$tests_dir/$vg_dir"; } + validate_program($vg_dir, "./coregrind/valgrind", 1, 1); + + if ($alldirs) { + @fs = (); + foreach my $f (glob "*") { + push(@fs, $f) if (-d $f); + } + } + + (0 != @fs) or die "No test files or directories specified\n"; + + return @fs; +} + +#---------------------------------------------------------------------------- +# Read a .vgperf file +#---------------------------------------------------------------------------- +sub read_vgperf_file($) +{ + my ($f) = @_; + + # Defaults. + ($vgopts, $prog, $args, $prereq, $cleanup) + = ("", undef, "", undef, undef, undef, undef); + + open(INPUTFILE, "< $f") || die "File $f not openable\n"; + + while (my $line = <INPUTFILE>) { + if ($line =~ /^\s*#/ || $line =~ /^\s*$/) { + next; + } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) { + $vgopts = $1; + } elsif ($line =~ /^\s*prog:\s*(.*)$/) { + $prog = validate_program(".", $1, 0, 0); + } elsif ($line =~ /^\s*tools:\s*(.*)$/) { + @tools = validate_tools($1); + } elsif ($line =~ /^\s*args:\s*(.*)$/) { + $args = $1; + } elsif ($line =~ /^\s*prereq:\s*(.*)$/) { + $prereq = $1; + } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) { + $cleanup = $1; + } else { + die "Bad line in $f: $line\n"; + } + } + close(INPUTFILE); + + if (!defined $prog) { + $prog = ""; # allow no prog for testing error and --help cases + } + if (0 == @tools) { + die "vg_perf: missing 'tools' line in $f\n"; + } +} + +#---------------------------------------------------------------------------- +# Do one test +#---------------------------------------------------------------------------- +# Since most of the program time is spent in system() calls, need this to +# propagate a Ctrl-C enabling us to quit. +sub mysystem($) +{ + (system($_[0]) != 2) or exit 1; # 2 is SIGINT +} + +# Run program N times, return the best wall-clock time. +sub time_prog($$) +{ + my ($cmd, $n) = @_; + my $tmin = 999999; + for (my $i = 0; $i < $n; $i++) { + my $out = `$cmd 2>&1 1>/dev/null`; + $out =~ /walltime: ([\d\.]+)s/; + $tmin = $1 if ($1 < $tmin); + } + return $tmin; +} + +sub do_one_test($$) +{ + my ($dir, $vgperf) = @_; + $vgperf =~ /^(.*)\.vgperf/; + my $name = $1; + + read_vgperf_file($vgperf); + + if (defined $prereq) { + if (system("$prereq") != 0) { + printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:"); + return; + } + } + + printf("%-12s", "$name:"); + + my $timecmd = "/usr/bin/time -f 'walltime: %es'"; + + # Do the native run(s). + printf("nt:"); + my $cmd = "$timecmd $prog $args"; + my $tNative = time_prog($cmd, $n_runs); + printf("%4.1fs ", $tNative); + + foreach my $tool (@tools) { + (defined $toolnames{$tool}) or + die "unknown tool $tool, please add to %toolnames\n"; + + # Do the tool run(s). Set both VALGRIND_LIB and VALGRIND_LIB_INNER + # in case this Valgrind was configured with --enable-inner. + printf("%s:", $toolnames{$tool}); + my $vgsetup = "VALGRIND_LIB=$vg_dir/.in_place " + . "VALGRIND_LIB_INNER=$vg_dir/.in_place "; + my $vgcmd = "$vg_dir/coregrind/valgrind " + . "--command-line-only=yes --tool=$tool -q " + . "--memcheck:leak-check=no --addrcheck:leak-check=no " + . "$vgopts "; + my $cmd = "$vgsetup $timecmd $vgcmd $prog $args"; + my $tTool = time_prog($cmd, $n_runs); + printf("%4.1fs (%4.1fx) ", $tTool, $tTool/$tNative); + + $num_timings_done++; + } + printf("\n"); + + if (defined $cleanup) { + (system("$cleanup") == 0) or + print(" ($name cleanup operation failed: $cleanup)\n"); + } + + $num_tests_done++; +} + +#---------------------------------------------------------------------------- +# Test one directory (and any subdirs) +#---------------------------------------------------------------------------- +sub test_one_dir($$); # forward declaration + +sub test_one_dir($$) +{ + my ($dir, $prev_dirs) = @_; + $dir =~ s/\/$//; # trim a trailing '/' + + # Ignore dirs into which we should not recurse. + if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; } + + chdir($dir) or die "Could not change into $dir\n"; + + # Nb: Don't prepend a '/' to the base directory + my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir; + my $dashes = "-" x (50 - length $full_dir); + + my @fs = glob "*"; + my $found_tests = (0 != (grep { $_ =~ /\.vgperf$/ } @fs)); + + if ($found_tests) { + print "-- Running tests in $full_dir $dashes\n"; + } + foreach my $f (@fs) { + if (-d $f) { + test_one_dir($f, $full_dir); + } elsif ($f =~ /\.vgperf$/) { + do_one_test($full_dir, $f); + } + } + if ($found_tests) { + print "-- Finished tests in $full_dir $dashes\n"; + } + + chdir(".."); +} + +#---------------------------------------------------------------------------- +# Summarise results +#---------------------------------------------------------------------------- +sub summarise_results +{ + printf("\n== %d programs, %d timings =================\n\n", + $num_tests_done, $num_timings_done); +} + +#---------------------------------------------------------------------------- +# main() +#---------------------------------------------------------------------------- + +# nuke VALGRIND_OPTS +$ENV{"VALGRIND_OPTS"} = ""; + +my @fs = process_command_line(); +foreach my $f (@fs) { + if (-d $f) { + test_one_dir($f, ""); + } else { + # Allow the .vgperf suffix to be given or omitted + if ($f =~ /.vgperf$/ && -r $f) { + # do nothing + } elsif (-r "$f.vgperf") { + $f = "$f.vgperf"; + } else { + die "`$f' neither a directory nor a readable test file/name\n" + } + my $dir = `dirname $f`; chomp $dir; + my $file = `basename $f`; chomp $file; + chdir($dir) or die "Could not change into $dir\n"; + do_one_test($dir, $file); + chdir($tests_dir); + } +} +summarise_results(); + +##--------------------------------------------------------------------## +##--- end ---## +##--------------------------------------------------------------------## |