#!/usr/local/bin/perl 
use Getopt::Std;

#######################################################################
# checktrace - trace file consistency checker and balancer.  
#
# Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved.
# May not be used, modified, or copied without permission.
#
# This script reads a Malloc Lab trace file, checks it for consistency,
# and outputs a balanced version by appending any necessary free requests.
#
#######################################################################
 
$| = 1; # autoflush output on every print statement

#
# void usage(void) - print help message and terminate
#
sub usage 
{
    printf STDERR "$_[0]\n";
    printf STDERR "Usage: $0 [-hs]\n";
    printf STDERR "Options:\n";
    printf STDERR "  -h          Print this message\n";
    printf STDERR "  -s          Emit only a brief summary\n";
    die "\n" ;
}

##############
# Main routine
##############

# 
# Parse and check the command line arguments
#
getopts('hs');
if ($opt_h) {
    usage("");
}
$summary = $opt_s;

# 
# HASH keeps a running tally of outstanding alloc/realloc 
# requests. When a free request is encountered, the corresponding 
# hash entry is deleted. When we are finished reading the trace,
# what is left are the unmatched alloc/realloc requests.
#
%HASH = (); 

# Read the trace header values
$heap_size = <STDIN>;
chomp($heap_size);

$num_blocks = <STDIN>;
chomp($num_blocks);

$old_num_ops = <STDIN>;
chomp($old_num_ops);

$weight = <STDIN>;
chomp($weight);

# 
# Find any allocate requests that don't have a matching free requests
#
$linenum = 4;
$requestnum = 0;
while ($line = <STDIN>) {
    chomp($line);
    $linenum++;

    ($cmd, $id, $size) = split(" ", $line);

    # ignore blank lines
    if (!$cmd) {
	next;
    }

    # save the line for output later
    $lines[$requestnum++] = $line;

    #ignore realloc requests, as long as they are preceeded by an alloc request
    if ($cmd eq "r") {
	if (!$HASH{$id}) {
	    die "$0: ERROR[$linenum]: realloc without previous alloc\n";
	}
	next;
    }

    if ($cmd eq "a" and $HASH{$id} eq "a") {
	die "$0: ERROR[$linenum]: allocate with no intervening free.\n";
    }

    if ($cmd eq "a" and $HASH{$id} eq "f") {
	die "$0: ERROR[$linenum]: reused ID $id.\n";
    }

    if ($cmd eq "f" and !exists($HASH{$id})) {
	die "$0: ERROR[$linenum]: freeing unallocated block.\n";
	next;
    }

    if ($cmd eq "f" and !$HASH{$id} eq "f") {
	die "$0: ERROR[$linenum]: freeing already freed block.\n";
	next;
    }
    
    if ($cmd eq "f") {
	delete $HASH{$id};
    }
    else {
	$HASH{$id} = $cmd;
    }
}

# 
# If called with -s argument , print a brief balance summary and exit
#
if ($summary) {
    if (!%HASH) {
	print "Balanced trace.\n";
    } 
    else {
	print "Unbalanced tree.\n";
    }
    exit;
}

#
# Output a balanced version of the trace
#
$new_ops = keys %HASH;
$new_num_ops = $old_num_ops + $new_ops;

print "$heap_size\n";
print "$num_blocks\n";
print "$new_num_ops\n";
print "$weight\n";

# print the old requests
foreach $item (@lines) {
    print "$item\n";
}

# print a set of free requests that will balance the trace
foreach $key (sort keys %HASH) {
    if ($HASH{$key} ne "a" and $HASH{$key} ne "r") {
	die "$0: ERROR: Invalid free request in residue.\n";
    }
    print "f $key\n";
}

exit;
