#!/usr/bin/perl

# **********************************************************
# Copyright (c) 2001-2003 VMware, Inc.  All rights reserved.
# **********************************************************

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.

### Summary
### author: Derek Bruening   April 2001
###
### Produces a table summarizing info in the dynamorio log files
### in some directory tree.

$usage = "Usage: $0 [-times] [-find findcmd] [-noperfctr]\n";

@perfctrnames=("Cycles",
               "Inst._Decoded",
               "Inst._Retired",
               "Micro-ops_Retired",
               "data_mem_refs",
               "DCU_misses",
               "Inst._Fetches",
               "IFU_misses",
               "ITLB_misses",
               "cycles_inst_fetch_stalled",
               "cycles_inst_fetch_decode_stalled",
               "L2_inst_fetches",
               "L2_loads",
               "L2_stores",
               "branches_decoded",
               "branches_retired",
               "branches_mispredicted_retired",
               "branches_taken_retired",
               "branches_taken_mispredicted_retired",
               "BTB_misses",
               "Bogus_branches",
               "misc_resource_stalls",
               "BACLEAR_asserted",
               "DCU_lines_allocated",
               "L2_lines_allocated",
               "prefetch_NTA_dispatched",
               "prefetch_NTA_miss_all_caches"
               );
$numperfctrs=scalar(@perfctrnames);

@args = @ARGV;
$times_only = 0;
$find = "find . -type f -name mainlog.\\* | grep 'dynamorio\\.'";
$noperfctr = 0;
while ($#ARGV >= 0) {
    if ($ARGV[0] eq '-find') {
        if ($#ARGV <= 0) { print $usage; exit; }
        shift;
        $find = $ARGV[0];
        print "$0 @args\n\n";
    } elsif ($ARGV[0] eq '-times') {
        $times_only = 1;
    } elsif ($ARGV[0] eq '-noperfctr') {
        $noperfctr = 1;
    } else {
        print $usage;
        exit;
    }
    shift;
}

unless ($times_only) {
    printf("%-43s  Traces / Frags  Heap  BB Used/Cap   Trace U/Cap  Minutes\n",
           "Path");
    if (!$noperfctr) {
        $have_perfctr = `$find | head -1 | xargs grep \"^Counter 1 \"`;
        $noperfctr = ($have_perfctr eq "");
    }
    if (!$noperfctr) {
        print join "\t",@perfctrnames;
        print "\n";
    }
    printf("%-43s  --------------  ---- ------------- ------------- -------\n",
           "--------------------------------------");
    if (!$noperfctr) {
        for (1..$numperfctrs) {print "----------- ";}
        print "\n";
    }
}

$perfctr_num = 0;

open(FIND, "$find |") || die "Error running $find\n";
while (<FIND>) {
    $have_elapsed = 0;
    $elapsed = 0;
    if (!$noperfctr) {
        @perfctr = (-1)x$numperfctrs;
    }
    chop;
    $path = $_;
    if ($path =~ /(.+\/)dynamorio\.([0-9]*)\/mainlog\.[0-9]*/) {
        $dir = $1;
        $num = $2;
        $logpath = "$dir"."dynamorio."."$num";
        $errs = ""; # use dynamorio log file for elapsed time
    } else {
        # no log files, just want total time
        if (!$times_only) {
            print "Error: no dynamorio log files found\n";
            exit 1;
        }
        if (-d $path) {
            $dir = "$path/";
        } else {
            $path =~ /(.+\/)[^\/]*$/;
            $dir = $1;
        }
        $errs = `ls $dir*.err`;
        if ($errs eq "") {
            print "Error: no dynamorio log files and no .err files found\n";
            next;
        }
    }
    if ($times_only) {
        print "Processing $dir\n";
    }

    unless ($times_only && $errs ne "") {
        # read dynamorio log files
        $error = 0;
        $num_frags = -1;
        $num_traces = -1;
        $heap_used = 0;
        $fcache_bb_used = 0;
        $fcache_bb_cap = 0;
        $fcache_trace_used = 0;
        $fcache_trace_cap = 0;
        # must read mainlog and all thread log files
        open(LOGS, "find $logpath -name \\*log\\* |") || die "Error running $find\n";
        while (<LOGS>) {
            open(IN, "< $_") || die "Error: Couldn't open $path for input\n";
            while (<IN>) {
                chop;
                $l = $_;

                # skip log files for runstats
                if ($l =~ /Running: runstats/) {
                    $runstats = 1;
                } elsif ($l =~ /execve/) {
                    $runstats = 0;
                }

                if ($l =~ /^Fragments generated: ([0-9]+)/) {
                    $num_frags = $1;
                } elsif ($l =~ /^Trace fragments generated: ([0-9]+)/) {
                    $num_traces = $1;
                } elsif ($l =~ /(.+)cache: capacity ([0-9]+) KB, used ([0-9]+) KB/) {
                    $which = $1;
                    $used = $2;
                    $cap = $3;
                    if ($which =~ /Basic/) {
                        $fcache_bb_used += $used;
                        $fcache_bb_cap += $cap;
                    } else {
                        $fcache_trace_used += $used;
                        $fcache_trace_cap += $cap;
                    }
#                } elsif ($l =~ /^Fcache space reserved: ([0-9]+) bytes/) {
#                    $fcache_tot = $1;
                } elsif ($l =~ /Total heap used: ([0-9]+) KB/) {
                    $heap_used += $1;
                } elsif (!$noperfctr && $l =~ /^Counter ([0-9]+) = ([0-9]+)/) {
                    $perfctr[$1] = $2;
                    if ($1 > $perfctr_num) {
                        $perfctr_num = $1;
                    }
                } elsif ($l =~ /Error/) {
                    $error = 1;
                } elsif ($l =~ /^Total running time: ([0-9]+) seconds/) {
                    $elapsed += $1/60.;
                    $have_elapsed = 1;
                    if ($times_only) {
                        print "\tTime from $path\n";
                    }
                }
            }
            close(IN);
            if ($num_frags == -1) {
                $error = 1;
            }
        }
    }

    # skip log files for runstats
    goto nextdir if ($runstats);

    if (!$have_elapsed && $errs ne "") {
        # get elapsed time from .err files
        open(ELAP, "ls $dir*.err |") || die "Error finding $dir*.err\n";
        while (<ELAP>) {
            chop;
            if ($times_only) {
                print "\tTime from $_\n";
            }
            open(IN, "< $_") || die "Error: Couldn't open $_ for input\n";
            while (<IN>) {
                chop;
                if ($_ =~ /Elapsed: ([0-9]+):([0-9]+):([0-9\.]+)/) {
                    $elapsed += $1*60 + $2 + $3/60.; # minutes
                } elsif ($_ =~ /Timeout after ([0-9]+) seconds/) {
                    $elapsed += $1/60.; # minutes
                }
            }
            close(IN);
        }
        close(ELAP);
        $have_elapsed = 1;
    }

    if ($times_only) {
        if ($dir =~ /-[on]o?pt-[tr].+\//) {
            $dir =~ m|\./[^/]+/([^/]+)/[^/]+-([on])o?pt-([tr]).+/|;
            $bmark = $1;
            $opt = $2;
            $dset = $3;
        } else {
            $dir =~ m|\./[^/]+/([^/]+)/[^/]+/|;
            $bmark = $1;
            # assume opt ref!
            $opt = 'o';
            $dset = 'r';
        }
        $val{$bmark,$opt,$dset} += $elapsed;
        $bm{$bmark} = 1;
    } else {
        printf("%-43s %6d / %6d %5d %5d / %5d %5d / %5d  %5.1f",
               "$dir$num", $num_traces, $num_frags, $heap_used,
               $fcache_bb_used, $fcache_bb_cap,
               $fcache_trace_used, $fcache_trace_cap,
               $elapsed);
        if (!$noperfctr) {
            for ($i = 1; $i < $perfctr_num; $i++) {
                printf("\t%15.0f", $perfctr[$i]);
            }
        }
        if ($error) {
            print "  Error";
        }
        print "\n";
    }
  nextdir:
}
close(FIND);

if ($times_only) {
    print("\nBenchmark\tRef,n\tRef,o\tTest,n\tTest,o\n");
    foreach $b (sort (keys %bm)) {
        printf("%10s\t%6.2f\t%6.2f\t%6.2f\t%6.2f\n",$b,
               $val{$b,"n","r"},$val{$b,"o","r"},
               $val{$b,"n","t"},$val{$b,"o","t"});
    }
}

