#!/usr/bin/perl -w
# Copyright Steven Rostedt, Red Hat Inc.
#
# Licensed under GPL v2

my $start_state = 0;
my $function_state = 1;
my $frame_state = 2;

my $state = -1;
my $depth = 0;
my $func;

if ($#ARGV >= 0) {
    open IN, $ARGV[0] or die "Can't open $ARGV[0]\n";
    close STDIN;
    *STDIN = *IN;
}

my $err = 0;

sub process_num {
    my ($num) = @_;

    my $val;

    # check if it is negative
    if ($num =~ s/^0xffffffff/0x/) {
	$val = unpack('l', pack('L', oct($num)));
    } else {
	$val = oct $num;
    }

    return $val;
}

while (<>) {
    if (/^[[:xdigit:]]+\s(<\S+>):/) {
	$state = $function_state;
	$func = $1;
	next;
    }

    if (/\sretq/) {
	$state = $start_state;
	next;
    }

    if (/\smov\s+\%rsp,\%rbp/) {
	$state = $frame_state;
	$depth = 0;
	next;
    }

    next if ($state != $frame_state);

    if (/\spush\s+\%/ || /\spushfq\s*$/ || /\spushq\s*(-)?0x/) {
	$depth -= 8;
	next;
    }

    if (/\spop\s+\%/ || /\spopfq\s*$/) {
	$depth += 8;
	next;
    }

    if (/\ssub\s+\$(0x[[:xdigit:]]+),\%rsp/) {
	$depth -= process_num($1);
	next;
    }

    if (/\slea\s/) {
	# ignore address calculations
	next;
    }

    if (/\sadd\s+\$(0x[[:xdigit:]]+),\%rsp/) {
	$depth += process_num($1);
	next;
    }

    if (/-0x([[:xdigit:]]+)\(\%rbp\)/) {
	my $offset = hex $1;
	if ($offset + $depth > 0) {
	    print "ERROR: $func: depth=$depth offset=$offset at $.\n";
	    print;
	    $err++;
	}
    }
}

if (!$err) {
    print "All clean!\n";
}
