This patch adds a Perl script 'run_valgrind.pl' that runs the test scripts passed as arguments using valgrind. To use valgrind, we use a shell alias like: alias git='valgrind <options> git' and we source a test script in the same system call. The valgrind logs are then parsed for errors and the errors found are hashed so that they appear only once at the end. Signed-off-by: Christian Couder <chriscool@xxxxxxxxxxxxx> --- t/run_valgrind.pl | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 138 insertions(+), 0 deletions(-) create mode 100755 t/run_valgrind.pl diff --git a/t/run_valgrind.pl b/t/run_valgrind.pl new file mode 100755 index 0000000..cb965c9 --- /dev/null +++ b/t/run_valgrind.pl @@ -0,0 +1,138 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +# All arguments should be test scripts. +my @scripts = @ARGV; + +my $vg_vers = valgrind_version(); + +print "Using valgrind version: $vg_vers\n"; + +my $log_base = '/tmp/vg_log'; +my $log_opt = ($vg_vers < '3.3') ? $log_base : "$log_base.%p"; +my $vg_opts = "--log-file=$log_opt --trace-children=yes"; +my $alias = "alias git='valgrind $vg_opts git'"; + +# Remove previous log files. +system("rm -rf $log_base.*"); + +# Run the scripts using an alias for Git. +foreach my $file (@scripts) { + system("$alias \n . ./$file"); +} + +# Error lines from log files. +my @log_files = glob("$log_base.*"); +my @errs = `grep 'ERROR SUMMARY' $log_base.*`; +chomp @errs; + +print "\nNumber of log files: " . scalar(@log_files) . "\n"; +print "Number of error lines: " . scalar(@errs) . "\n"; + +my @logs; +for (@errs) { + if (m/^([^:]+):.*ERROR SUMMARY: (\d+) errors/) { + push @logs, $1 if ($2 > 0); + } else { + print STDERR "strange line: $_\n"; + } +} + +print "\nResulting files with errors:\n\n", join("\n", @logs), "\n\n"; + +# Parse error files. +my @info = (); +for (@logs) { + my @new = parse_error_file($_); + push @info, @new; +} + +# Get only uniq errors. +my @uniq = (); +my %stack = (); +for (@info) { + my $key = stack_info($_, 1); + if (exists $stack{$key}) { + push @{$stack{$key}}, $_->{file}; + } else { + $stack{$key} = [ $_->{file} ]; + $_->{file} = $stack{$key}; + push @uniq, $_; + } +} + +# Print uniq errors. +print "Uniq errors:\n"; +for (@uniq) { + print "\n" . stack_info($_); + print "Files: " . join(", ", @{$_->{file}}) . "\n"; +} + +sub valgrind_version { + system("which valgrind > /dev/null 2>&1") == 0 + or die "'which valgrind' failed."; + + my $version = `valgrind --version`; + chomp $version; + + if ($version =~ m/(\d+)\.(\d+)\.(\d+)/) { + return "$1.$2"; + } else { + die "Could not find valgrind version."; + } +} + +sub stack_info { + $_ = shift; + my ($key) = @_; + + my $msg = $_->{msg}; + $msg =~ s/0x[0-9A-F]+// if ($key); + + my @places = map { $_->[1] } @{$_->{stack}}; + return "$msg\n" . join("\n", @places) . "\n"; +} + +sub clean_line { + my ($line) = @_; + + $line =~ s/^==\d+==\s+//; + + return $line; +} + +sub parse_error_file { + my ($file) = @_; + + # Slurp file. + open(IN, "< $file") or die "Could not open '$file' for reading: $!"; + my @content = <IN>; + chomp @content; + close IN; + + # Parse errors in file. + my @errs = (); + my $stack = 0; + my $prev = ''; + my $cur; + for (@content) { + if (not $stack and m/ at 0x([0-9A-F]+): (.*)$/) { + $stack = 1; + $cur = { msg => $prev, + file => $file, + stack => [ [$1, $2] ] }; + } elsif ($stack) { + if (m/ by 0x([0-9A-F]+): (.*)$/) { + push @{$cur->{stack}}, [$1, $2]; + } else { + push @errs, $cur; + $stack = 0; + } + } + $prev = clean_line($_); + } + + return @errs; +} -- 1.5.3.7.2270.g786cf-dirty - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html