#!/usr/bin/perl -w

use strict;
use XML::Simple;
use Storable qw(dclone store retrieve);
use Data::Dumper;

my %cmdline_params;

# Load contents from a file and return a hash;
sub load_from_file {
    my $file = shift;

    return unless ( -f $file );

    my $cfile = ".$file.cache";

    # Need to check dates here as well...
    if ( -f $cfile ) {
        my $data = retrieve($cfile);
        return $$data;
    }

    my $xml   = new XML::Simple;
    my @array = qw(pair frame error arg);
    my %attr;

    #$attr{pair} = "unique";
    my $data = $xml->XMLin( $file, ForceArray => \@array, KeyAttr => \%attr );

    if ( check_xml_version($data) ) {

        #store( \$data, $cfile );
        return $data;
    }

    return;
}

my $protocolversion;
my $tool;

# Check that the xml is from memcheck and we know how to read it.
sub check_xml_version {
    my $data = shift;

    if ( $data->{protocolversion} < "3" ) {
        printf "unknown xml version ($data->{protocolversion})";
    }

    $protocolversion = $data->{protocolversion};

    $tool = $data->{tool};
    if (    $tool ne "memcheck"
        and $tool ne "helgrind"
        and $tool ne "exp-ptrcheck" )
    {
        die "unknown tool";
    }
    return 1;
}

# Should this error be reported in-line when it happens
# or as part of the summary at the end of the run.
sub error_post {
    my $error = shift;

    my %post_errors = (
        "Leak_DefinitelyLost" => 1,
        "Leak_PossiblyLost"   => 1,
        "Leak_StillReachable" => 1,
        "Leak_IndirectlyLost" => 1,
    );
    return ( defined $post_errors{$error} );
}

sub find_error {
    my $res  = shift;
    my $kind = shift;
    my $ip   = shift;
    my $str  = shift;

    my $len = length($str);

    foreach my $e ( @{ $res->{all_errors} } ) {
        if (    ( $e->{kind} eq $kind )
            and ( $e->{ip} eq $ip ) )
        {
            if ( $e->{len} == $len ) {
                if ( not( $e->{str} cmp $str ) ) {
                    return $e;
                }
            }
        }
    }
    return undef;
}

sub error_to_string {
    my $e = shift;

    my $str;

    my $what;

    if ( defined $e->{what} ) {
        $what = $e->{what};
    } else {
        $what = $e->{xwhat}{text};
    }

    if ( $what =~ /^(.*) in loss record \d+ of \d+$/ ) {
        $str = "$1 (record numbers have been collated)\n";
    } else {
        $str = "$what\n";
    }

    my $ip;

    if ( ref $e->{stack} eq "ARRAY" ) {
        $str .= show_stack2( $e->{stack}[0]{frame} );
        if ( defined $e->{auxwhat} ) {
            $str .= " $e->{auxwhat}\n";
        }
        $str .= show_stack2( $e->{stack}[1]{frame} );
        $ip = $e->{stack}[0]{frame}[0]{ip};
    } else {
        if ( defined $e->{event} ) {
            $str .= " $e->{event}";
            if ( defined $e->{address} ) {
                $str .= " at address $e->{address}\n";
            }
        }
        $str .= show_stack2( $e->{stack}{frame} );
        if ( defined $e->{auxwhat} ) {
            $str .= " $e->{auxwhat}\n";
        }
        $ip = $e->{stack}{frame}[0]{ip};
    }
    return ( $str, $ip );
}

my %errors;

my $c = 0;

sub error_to_hash {
    my $res  = shift;
    my $data = shift;
    my $e    = shift;

    my $ee;

    my ( $str, $ip ) = error_to_string($e);

    if ( not defined $ip ) {
        $c++;
        $ip = "ima $c";
    }

    if ( not defined $errors{$ip} ) {
        $errors{$ip} = 0;
    }

    $errors{$ip}++;

    if ( $errors{$ip} > 1 ) {
        $ee = find_error( $res, $e->{kind}, $ip, $str );
    }

    # Check if this error is unique and push in onto the array if it isn't.
    if ( not defined $ee ) {
        my %this_error;
        $this_error{str} = $str;
        push @{ $res->{all_errors} }, \%this_error;
        $ee          = \%this_error;
        $ee->{kind}  = $e->{kind};
        $ee->{count} = 0;
        $ee->{ip}    = $ip;
        $ee->{len}   = length( $ee->{str} );
    }

    if ( defined $data->{qualifier} ) {
        push @{ $ee->{qualifiers} }, $data->{qualifier};
    }

    # Increase the counters.

    if ( defined $data->{errorcounts}{hash}{ $e->{unique} } ) {
        $ee->{count} += $data->{errorcounts}{hash}{ $e->{unique} };
    }
}

sub show_stack2 {
    my $stack = shift;

    my $str = "";

    my $pre = "at";
    foreach my $fn ( @{$stack} ) {
        my $file = "";
        if ( defined $fn->{file} and defined $fn->{line} ) {
            $file = "($fn->{file}:$fn->{line})";
        } else {
            if ( defined $fn->{obj} ) {
                if ( defined $fn->{fn} ) {
                    $file = "(in $fn->{obj})";
                } else {
                    $file = "(within $fn->{obj})";
                }
            } else {
                $file = "???";
            }
        }

        if ( defined $fn->{fn} ) {
            $str .= "   $pre $fn->{ip}: $fn->{fn} $file\n";
        } else {
            $str .= "   $pre $fn->{ip}: $file\n";
        }

        $pre = "by";
    }
    return $str;
}

my @args_ignore = qw(suppressions xml log-file xml-file);
my %args_ignore;
map { $args_ignore{$_} = 1 } @args_ignore;

my @args_handled = qw(leak-check track-origins show-reachable q);
my %args_handled;
map { $args_handled{$_} = 1 } @args_handled;

sub parse_valgrind_args {
    my $args = shift;
    my %options;
    $options{"leak-check"}     = "no";
    $options{"track-origins"}  = "no";
    $options{"show-reachable"} = "no";
    foreach my $arg ( @{$args} ) {
        my @parts = split( "=", $arg );
        my $flag = shift(@parts);
        $flag =~ s/^--//;
        $flag =~ s/^-//;
        my $value = join( "=", @parts );
        next if ( defined( $args_ignore{$flag} ) );
        $options{$flag} = $value;
        next if ( defined( $args_handled{$flag} ) );
        next if ( $cmdline_params{"ignore-unknown-options"} eq "yes" );
        printf("Unknown arg to valgrind: $flag = $value\n");
    }
    return \%options;
}

sub make_raw {
    my $res        = shift;
    my $data       = shift;
    my $leak_check = 0;

    $res->{vparams} = parse_valgrind_args( $data->{args}{vargv}{arg} );

    {
        my %proc;
        $proc{pid}  = $data->{pid};
        $proc{ppid} = $data->{ppid};
        $proc{args} = $data->{args};

        # XXX: Should probably define this to something if not already...
        if ( defined $data->{logfilequalifier} ) {
            $proc{logfilequalifier} = $data->{logfilequalifier};
            if ( defined $data->{logfilequalifier}{var} ) {
                $res->{logfilequalifier} = $data->{logfilequalifier}{var};
            }
            $data->{qualifier} = $data->{logfilequalifier}{value};
        } else {
            $data->{qualifier} = $proc{pid};
        }
        push( @{ $res->{procs} }, \%proc );
    }

    my %errorcounts;

    map { $errorcounts{ $_->{unique} } = $_->{count} }
      ( @{ $data->{errorcounts}{pair} } );

    $data->{errorcounts}{hash} = \%errorcounts;

    foreach my $e ( @{ $data->{error} } ) {
        error_to_hash( $res, $data, $e );
    }

    my $supp_errors = 0;
    my $supp_count  = 0;

    foreach my $s ( @{ $data->{suppcounts}{pair} } ) {
        $supp_errors += $s->{count};
        $supp_count++;

        if ( not defined $res->{suppcounts}{ $s->{name} } ) {
            $res->{suppcounts}{ $s->{name} }{count}      = 0;
            $res->{suppcounts}{ $s->{name} }{proc_count} = 0;
        }

        $res->{suppcounts}{ $s->{name} }{count} += $supp_errors;
        $res->{suppcounts}{ $s->{name} }{proc_count}++;

    }

    my $leaks = $res->{leaks};

    if ( defined $data->{malloc_use} ) {
        if ( !defined $res->{malloc_use}{exit}{blocks} ) {
            $res->{malloc_use}{exit}{blocks}  = 0;
            $res->{malloc_use}{exit}{bytes}   = 0;
            $res->{malloc_use}{total}{allocs} = 0;
            $res->{malloc_use}{total}{frees}  = 0;
            $res->{malloc_use}{total}{bytes}  = 0;
        }

        $res->{malloc_use}{exit}{blocks} +=
          $data->{malloc_use}{at_exit}{blocks};
        $res->{malloc_use}{exit}{bytes} += $data->{malloc_use}{at_exit}{bytes};
        $res->{malloc_use}{total}{allocs} += $data->{malloc_use}{total}{allocs};
        $res->{malloc_use}{total}{frees}  += $data->{malloc_use}{total}{frees};
        $res->{malloc_use}{total}{bytes}  += $data->{malloc_use}{total}{bytes};
    }

    if ( defined $data->{leak_summary} ) {
        my $summary;

        # You can get this from client requests...
        if ( ref $data->{leak_summary} eq "ARRAY" ) {
            $summary = $data->{leak_summary}[-1];
        } else {
            $summary = $data->{leak_summary};
        }

        $res->{leaks}{definitely}{bytes} += $summary->{lost}{definitely}{bytes};

        $res->{leaks}{definitely}{blocks} +=
          $summary->{lost}{definitely}{blocks};
        $res->{leaks}{possibly}{bytes}  += $summary->{lost}{possibly}{bytes};
        $res->{leaks}{possibly}{blocks} += $summary->{lost}{possibly}{blocks};

        if ( !defined $res->{leaks}{reachable}{bytes} ) {
            $res->{leaks}{reachable}{bytes}   = 0;
            $res->{leaks}{reachable}{blocks}  = 0;
            $res->{leaks}{suppressed}{bytes}  = 0;
            $res->{leaks}{suppressed}{blocks} = 0;
        }
        $res->{leaks}{reachable}{bytes}   += $summary->{reachable}{bytes};
        $res->{leaks}{reachable}{blocks}  += $summary->{reachable}{blocks};
        $res->{leaks}{suppressed}{bytes}  += $summary->{suppressed}{bytes};
        $res->{leaks}{suppressed}{blocks} += $summary->{suppressed}{blocks};

    } else {
        foreach my $e ( @{ $data->{error} } ) {

            next unless ( error_post( $e->{kind} ) );

            my $what;

            if ( defined $e->{what} ) {
                $what = $e->{what};
            } else {
                $what = $e->{xwhat}{text};
            }

            if ( $e->{kind} eq "Leak_DefinitelyLost" ) {

                if ( $what =~ /([\d,]+) bytes in ([\d,]+) blocks/ ) {
                    $leaks->{definitely}{blocks} += $2;
                    my $bytes = $1;
                    $bytes =~ s/,//g;
                    $leaks->{definitely}{bytes} += $bytes;
                } elsif ( $what =~
/[\d,]+ \(([\d,]+) direct, ([\d,]+) indirect\) bytes in ([\d,]+) blocks/
                  )
                {
                    $leaks->{definitely}{blocks} += $3;
                    my $bytes = $1;
                    $bytes =~ s/,//g;
                    $leaks->{definitely}{bytes} += $bytes;
                    $bytes = $2;
                    $bytes =~ s/,//g;

               # If --show-reachable=yes is on then this is collected elsewhere,
               # if it's not on then this isn't enough information to work with.
               #$leaks->{indirectly}{bytes} += $bytes;
                } else {
                    die "cant get count from $what\n";
                }
            } elsif ( $e->{kind} eq "Leak_PossiblyLost" ) {
                if ( $what =~ /([\d,]+) bytes in ([\d,]+) blocks/ ) {
                    $leaks->{possibly}{blocks} += $2;
                    my $bytes = $1;
                    $bytes =~ s/,//g;
                    $leaks->{possibly}{bytes} += $bytes;
                } elsif ( $what =~
/([\d,]+) \([\d,]+ direct, [\d,]+ indirect\) bytes in ([\d,]+) blocks/
                  )
                {
                    $leaks->{possibly}{blocks} += $2;
                    my $bytes = $1;
                    $bytes =~ s/,//g;
                    $leaks->{possibly}{bytes} += $bytes;
                } else {
                    die "cant get count from $what\n";
                }

            } elsif ( $e->{kind} eq "Leak_StillReachable" ) {
                $leaks->{reachable}{bytes}  += $e->{xwhat}{leakedbytes};
                $leaks->{reachable}{blocks} += $e->{xwhat}{leakedblocks};
            } elsif ( $e->{kind} eq "Leak_IndirectlyLost" ) {
                $leaks->{indirectly}{bytes}  += $e->{xwhat}{leakedbytes};
                $leaks->{indirectly}{blocks} += $e->{xwhat}{leakedblocks};
            } else {
                print Dumper $e;
                die "unknown $e->{kind}";
            }
        }
    }
}

sub sortn {
    map { $$_[0] }
      sort { ( $$a[1] || 0 ) <=> ( $$b[1] || 0 ) } map { [ $_, /(\d*)$/ ] } @_;
}

sub comp {
    my (%i) = ();
    my (%s) = ();

    # turn off warnings here to avoid perl complaints about
    # uninitialized values for members of %i and %s
    local ($^W) = 0;
    push(
        @{
            $s{ $$_[0] }[
              (
                  $s{ $$_[0] }[ $i{ $$_[0] } ]
                    [ $#{ $s{ $$_[0] }[ $i{ $$_[0] } ] } ] == ( $$_[1] - 1 )
              ) ? $i{ $$_[0] } : ++$i{ $$_[0] }
            ]
          },
        ( $$_[1] )
    ) for map { [/(.*?)(\d*)$/] } sortn(@_);

    for my $key ( keys %s ) {
        @{ $s{$key} } =
          map { $#$_ > 0 ? "$$_[0]-$$_[$#$_]" : @{$_} } @{ $s{$key} };
    }

    return %s;
}

# This function is used to process the line tags, it changes fab0 fab1 fab10 into
# fab[0-1,10].
sub compress {
    my %rng  = comp(@_);
    my @list = ();

    # comp returns a hash of arrays, the hash keys are the machines names
    # eg "fab" or "fabi", the arrays have zero or more elements in each one
    # specifies a node-spec.  If there is only one element in the array and
    # it doesn't contain a "-" then don't put square braces around the list
    # contents

    local $" = ",";    # "

    @list = map {
        $_
          . (
            @{ $rng{$_} } > 1 || ${ $rng{$_} }[0] =~ /-/
            ? "[@{$rng{$_}}]"
            : "@{$rng{$_}}"
          )
    } sort keys %rng;

    return wantarray ? @list : "@list";
}

sub show_parallel {
    my $res = shift;

    my $single = 0;

    if ( $#{ $res->{procs} } == 0 ) {
        $single = 1;
    }

    my $str = "";

    if ( not defined( $res->{vparams}{q} ) ) {

        foreach my $l ( @{ $res->{header}{line} } ) {
            $str .= "$l\n";
        }
        if ( $protocolversion < 4 ) {
            $str .= "For more details, rerun with: -v\n\n";
        }

        if ($single) {
            my $proc = $res->{procs}[0];

            if ( $protocolversion < 4 ) {
                $str .=
"My PID = $proc->{pid}, parent PID = $proc->{ppid}.  Prog and args are:\n";
                $str .= "   $proc->{args}{argv}{exe}\n";

                foreach my $arg ( @{ $proc->{args}{argv}{arg} } ) {
                    $str .= "   $arg\n";
                }
                $str .= "\n";

            } else {
                $str .= "Command: $proc->{args}{argv}{exe}\n";
                $str .= "Parent PID: $proc->{ppid}\n\n";
            }

            if ( defined $proc->{logfilequalifier} ) {

                # XXX: This needs fixing, what if we have a name with no value?
                $str .=
"Log file qualifier: var $proc->{logfilequalifier}{var}, value $proc->{logfilequalifier}{value}.\n\n";
            }
        }
    }

    my $errors   = 0;
    my $contexts = 0;

    my $separator = "";
    foreach my $e ( @{ $res->{all_errors} } ) {

        next if ( error_post( $e->{kind} ) );

        $str .= $separator;

        if ( defined $res->{logfilequalifier} and not $single ) {
            my $s = join( ",", compress( @{ $e->{qualifiers} } ) );
            $str .= $res->{logfilequalifier};
            $str .= " $s\n";
        }
        $str .= $e->{str};

        $separator = "\n";

        $errors += $e->{count};
        $contexts++;
    }

    if ($errors) {
        $str .= "\n";
    }

    my $s_count  = 0;
    my $s_errors = 0;

    foreach my $s ( keys %{ $res->{suppcounts} } ) {
        $s_errors += $res->{suppcounts}{$s}{count};
        $s_count++;
    }

    if ( $tool eq "memcheck" and not defined( $res->{vparams}{q} ) ) {
        if ( $protocolversion == 4 ) {
            $str .= "\nHEAP SUMMARY:\n";
            $str .= "    in use at exit: _ bytes in _ blocks\n";
            $str .=
              "  total heap usage: _ allocs, _ frees, _ bytes allocated\n";
        }
    }

    my $leak_check_report = "";

    if ( $res->{vparams}{"leak-check"} eq "full" ) {
        my $separator = "\n";
        foreach my $e ( @{ $res->{all_errors} } ) {
            next unless ( error_post( $e->{kind} ) );

            $leak_check_report .= $separator;
            if ( not $single ) {
                my $s = join( ",", compress( @{ $e->{qualifiers} } ) );
                if ( defined $res->{logfilequalifier} ) {
                    $leak_check_report .= "$res->{logfilequalifier} ";
                    $leak_check_report .= "$s\n";
                }
            }
            $leak_check_report .= $e->{str};
            $separator = "\n";
            if (    $e->{kind} ne "Leak_StillReachable"
                and $e->{kind} ne "Leak_IndirectlyLost" )
            {
                $errors++;
                $contexts++;
            }
        }
    }

    if ( not defined( $res->{vparams}{q} ) ) {

        if ( $protocolversion < 4 ) {
            $str .= "\nERROR SUMMARY: $errors errors from $contexts contexts";
            $str .= " (suppressed: $s_errors from $s_count)\n";
        }

        if ( defined $res->{malloc_use}{exit}{bytes} ) {
            $str .=
"malloc/free: in use at exit: $res->{malloc_use}{exit}{bytes} bytes in $res->{malloc_use}{exit}{blocks} blocks.\n";
            $str .=
"malloc/free: $res->{malloc_use}{total}{allocs} allocs, $res->{malloc_use}{total}{frees} frees, $res->{malloc_use}{total}{bytes} bytes allocated.\n";
            $str .= "For counts of detected errors, rerun with: -v\n";
            if (0) {
                $str .= "searching for pointers to ?? not-freed blocks.\n";
                $str .= "checked ?? bytes.\n";
            }
        }

    }

    if ( $res->{vparams}{"leak-check"} eq "full" ) {
        $str .= $leak_check_report;
    }

    my $leaks = $res->{leaks};

    if ( $tool eq "memcheck" and not defined( $res->{vparams}{q} ) ) {
        my $period = "";
        if ( $protocolversion < 4 ) {
            $period = ".";
        }

        $str .= "\nLEAK SUMMARY:\n";
        $str .=
"   definitely lost: $leaks->{definitely}{bytes} bytes in $leaks->{definitely}{blocks} blocks$period\n";
        if ( $protocolversion == 4 ) {
            $str .=
"   indirectly lost: $leaks->{indirectly}{bytes} bytes in $leaks->{indirectly}{blocks} blocks$period\n";
        }
        $str .=
"     possibly lost: $leaks->{possibly}{bytes} bytes in $leaks->{possibly}{blocks} blocks$period\n";

        if ( !defined $leaks->{reachable}{bytes} ) {
            $leaks->{reachable}{bytes}   = "unknown";
            $leaks->{reachable}{blocks}  = "unknown";
            $leaks->{suppressed}{bytes}  = "unknown";
            $leaks->{suppressed}{blocks} = "unknown";
        }

        $str .=
"   still reachable: $leaks->{reachable}{bytes} bytes in $leaks->{reachable}{blocks} blocks$period\n";
        $str .=
"        suppressed: $leaks->{suppressed}{bytes} bytes in $leaks->{suppressed}{blocks} blocks$period\n";

        if ( $res->{vparams}{"leak-check"} eq "full" ) {
            if ( $protocolversion == 4 ) {
                if ( $res->{vparams}{"show-reachable"} ne "yes" ) {
                    $str .=
"Reachable blocks (those to which a pointer was found) are not shown.\n";
                    $str .=
"To see them, rerun with: --leak-check=full --show-reachable=yes\n";
                }
                $str .=
"\nFor counts of detected and suppressed errors, rerun with: -v\n";
                if ( !$res->{vparams}{"track-origins"} eq "yes" ) {
                    $str .=
"Use --track-origins=yes to see where uninitialised values come from\n";
                }
            } else {
                $str .=
"Reachable blocks (those to which a pointer was found) are not shown.\n";
                $str .= "To see them, rerun with: --show-reachable=yes\n";
            }
        } else {
            if ( $protocolversion < 4 ) {

                $str .=
                  "Use --leak-check=full to see details of leaked memory.\n";
            } else {
                if ( $res->{vparams}{"leak-check"} ne "full" ) {
                    $str .=
"Rerun with --leak-check=full to see details of leaked memory\n\n";
                }
                $str .=
"For counts of detected and suppressed errors, rerun with: -v\n";
                $str .=
"Use --track-origins=yes to see where uninitialised values come from\n";
            }
        }

    }

    if ( $tool eq "helgrind" and not defined( $res->{vparams}{q} ) ) {
        $str .=
          "\nFor counts of detected and suppressed errors, rerun with: -v\n";
        $str .=
          "Use --history-level=approx or =none to gain increased speed, at\n";
        $str .=
          "the cost of reduced accuracy of conflicting-access information\n";
    }

    if ( not defined( $res->{vparams}{q} ) ) {
        if ( $protocolversion == 4 ) {
            $str .=
"ERROR SUMMARY: $errors errors from $contexts contexts (suppressed: $s_errors from $s_count)\n";
        }

    }

    if ( $#{ $res->{procs} } == 0 and $cmdline_params{"pid-prefix"} eq "yes" ) {
        if ( $str eq "" ) {
            return $str;
        }
        my $pid = $res->{procs}[0]{pid};
        my @o = split( "\n", $str );
        $str = join( "\n==$pid== ", @o );
        return "==$pid== $str\n";
    }

    return $str;
}

sub copy_header {
    my $res  = shift;
    my $data = shift;

    $res->{header} = dclone $data->{preamble};

    # This messes up the valgrind test suite :(
    return;
    push @{ $res->{header}{line} },
      "Converted from xml by vg_xmlmerge.pl by ashley pittman";
}

my $stime = time();

sub _log {
    my $str  = shift;
    my $time = time() - $stime;
    if (1) {
        return;
    }
    print("$time: $str\n");
}

sub usage {
    my $error = shift;
    print "Error: $error; aborting\n";
    exit(1);
}

my %known_params;

@{ $known_params{xml} }                      = qw(yes no);
@{ $known_params{"pid-prefix"} }             = qw(yes no);
@{ $known_params{"leak-check"} }             = qw(full no);
@{ $known_params{"ignore-unknown-options"} } = qw(yes no);

$cmdline_params{"pid-prefix"}             = "yes";
$cmdline_params{"ignore-unknown-options"} = "yes";
$cmdline_params{"leak-check"}             = "no";

# Check command line params, abort of error and return a list of files.
sub check_args {
    my @args = @_;
    my @files;
    foreach my $param (@args) {
        if ( $param =~ /--([\w-]+)=(\w+)/ ) {
            my $key   = $1;
            my $value = $2;
            if ( not defined $known_params{$1} ) {
                usage("Unknown paramater: $1");
                exit(1);
            }

            my @ok = @{ $known_params{$key} };

            my %valid;
            map { $valid{$_} = $_ } @{ $known_params{$key} };

            if ( defined $valid{$value} ) {
                $cmdline_params{$key} = $value;
            } else {
                usage("Bad option: $param");
                exit(1);
            }
        } else {
            if ( -f $param ) {
                push @files, $param;
            } else {
                usage("No such file or directory: $param");
                exit(1);
            }
        }
    }

    if ( $#files == -1 ) {
        usage("No input files specified");
        exit(1);
    }
    return @files;
}

sub main {
    my %res;
    my $res;

    # Make sure res is a hash ref the first time we use it.

    $res->{leaks}{definitely}{bytes}  = 0;
    $res->{leaks}{definitely}{blocks} = 0;
    $res->{leaks}{possibly}{bytes}    = 0;
    $res->{leaks}{possibly}{blocks}   = 0;
    $res->{leaks}{indirectly}{bytes}  = 0;
    $res->{leaks}{indirectly}{blocks} = 0;

    # Init this chunk later if we have the data in the xml file.
    $res->{leaks}{reachable}{bytes}   = 0;
    $res->{leaks}{reachable}{blocks}  = 0;
    $res->{leaks}{suppressed}{bytes}  = 0;
    $res->{leaks}{suppressed}{blocks} = 0;

    #$res->{malloc_use}{exit}{blocks} = 0;
    #$res->{malloc_use}{exit}{bytes}  = 0;
    #$res->{malloc_use}{total}{allocs} =0;
    #$res->{malloc_use}{total}{frees} =0;
    #$res->{malloc_use}{total}{bytes} =0;

    my @files = check_args(@ARGV);
    foreach my $file (@files) {

        _log "Loading $file";
        my $data = load_from_file $file;

        next unless $data;

        if ( not defined $res->{header} ) {
            copy_header( $res, $data );
        }

        _log "Parsing $file";

        make_raw $res, $data;
    }

    if ( !defined $res->{logfilequalifier} ) {
        $res->{logfilequalifier} = "pid";
    }

    _log "printing all";

    print show_parallel $res;
}

main();

exit 0;

