#!/usr/local/bin/perl

$BY_EVENTS = 'events';
$BY_VOLUME = 'volume';


while (<>) {
	chop;
	next if (/^---|^index/);

	($seq, $time, $rest) = split(" ", $_, 3);

	$dat{$seq} = {'time' => $time, 'msg' => $rest};
}

@all = sort {$dat{$a}->{'time'} <=> $dat{$b}->{'time'}} keys %dat;
foreach $seq (@all) {
	$time = $dat{$seq}->{'time'};
	$msg = $dat{$seq}->{'msg'};
	if ($msg =~ /^contention: (\S+) at ([^: ]+):(\d+) wants (\S+),/) {
		($td, $wantfile, $wantline, $mtx) = ($1, $2, $3, $4);
		$msg =~ /taken by ([^: ]+):(\d+)$/;
		($holdfile, $holdline) = ($1, $2);
		$holdfile =~ s/^.*\/([^\/]+)$/$1/;
		$wantfile =~ s/^.*\/([^\/]+)$/$1/;

		$wait{$td} = {'time' => $time, 'wantfile' => $wantfile,
		    'wantline' => $wantline, 'mtx' => $mtx,
		    'holdfile' => $holdfile, 'holdline' => $holdline};
	} elsif ($msg =~ /^contention end: (\S+) acquired by (\S+)/) {
		($mtx, $td) = ($1, $2);
		$msg =~ / at ([^: ]+):(\d+)$/;
		($wantfile, $wantline) = ($1, $2);
		$wantfile =~ s/^.*\/([^\/]+)$/$1/;

		unless (defined($wait{$td})) {
			# print "no start for: $msg\n";
			next;
		}
		$want = "$wantfile:$wantline";
		$hold = "$wait{$td}->{'holdfile'}:$wait{$td}->{'holdline'}";

		$waittime = $time - $wait{$td}->{'time'};

		stats_add(\%bymutex, $waittime, $mtx, $want, $hold);
		stats_add(\%byholder, $waittime, $hold, $mtx, $want);
		stats_add(\%bywaiter, $waittime, $want, $mtx, $hold);

#		print "$mtx released after $waittime cycles\n";
	} else {
		print "time `$time' msg `$msg'\n";
	}
}

sub stats_add {
	my($data, $volume, @values) = @_;

	$data->{'events'}++;
	$data->{'volume'} += $volume;
	my($next) = shift(@values);

	return unless (defined $next);

	if (!defined($data->{'entries'}->{$next})) {
		$data->{'entries'}->{$next}->{'level'} = $data->{'level'} + 1;
	}

	&stats_add($data->{'entries'}->{$next}, $volume, @values);
}

# Print out hierarchical stats, and try to make $lines lines long. Return
# the number of lines actually printed.
sub stats_print {
	my($data, $totallines, $sort, $title, @names) = @_;

	my($next) = shift(@names);
	my($lines) = $totallines;
	if ($data->{'level'} > 0) {
		my($volume) = $data->{'volume'};
		my($volume_str) = " (" . ($volume + 0) . " ticks)";

		print( ("\t" x ($data->{'level'} - 1)) . sprintf(
		    "%7s %s%s\n",$data->{'events'}, $title, $volume_str));
		$lines--;
	} else {
		print "\n";
		print( ("-" x 79)."\n");
		my($names) = "[". join(", ", $next, @names). "]";
		my($sep,$titlewid) = ("  ", 22);
		if (length($names) > 22 || length($title) > 22) {
			$sep = "\n\t";
			$titlewid = 46;
			$lines--;
		}
		print( sprintf("%-${titlewid}s  Total: %-6d (%s ticks)",
		    $title, $data->{'events'}, $data->{'volume'}) .
		    "$sep$names\n");
		print( ("-" x 79)."\n");
		$lines -=4;
	}
	

	return ($totallines - $lines) if ($lines < 1 || !defined($next));

	my($hashr) = $data->{'entries'};
	my(@keys) = sort {$hashr->{$b}->{$sort} <=> $hashr->{$a}->{$sort}}
	    keys %$hashr;
	my($events) = $data->{'events'};

	return ($totallines - $lines) if ($events < 1);

	foreach (@keys) {
		my ($sublines) = ($events >= 1 ? int( $lines *
		    $data->{'entries'}->{$_}->{'events'} / $events) : 1);
		my($p) = &stats_print($data->{'entries'}->{$_}, $sublines,
		    $sort, $_, @names);
		$lines -= $p;
		$events -= $data->{'entries'}->{$_}->{'events'};
		last if ($lines <= 0);
	}
	return ($totallines - $lines);
}

stats_print(\%bymutex, 50, $BY_VOLUME, "Contention by mutex",
    "Mutex", "Waiter", "Holder");
stats_print(\%bywaiter, 50, $BY_VOLUME, "Contention by waiter",
    "Waiter", "Mutex", "Holder");
stats_print(\%byholder, 50, $BY_VOLUME, "Contention by holder",
    "Holder", "Mutex", "Waiter");

