diff options
-rwxr-xr-x | avstack.pl | 251 |
1 files changed, 130 insertions, 121 deletions
@@ -53,7 +53,7 @@ use 5.010; # Configuration: set these as appropriate for your architecture/project. -my $objdump = shift; +my $objdump = shift; my $call_cost = shift; # First, we need to read all object and corresponding .su files. We're @@ -61,72 +61,78 @@ my $call_cost = shift; # sizes. We're just parsing at this stage -- callee name resolution # comes later. -my %frame_size; # "func@file" -> size -my %call_graph; # "func@file" -> {callees} -my %addresses; # "addr@file" -> "func@file" +my %frame_size; # "func@file" -> size +my %call_graph; # "func@file" -> {callees} +my %addresses; # "addr@file" -> "func@file" -my %global_name; # "func" -> "func@file" -my %ambiguous; # "func" -> 1 +my %global_name; # "func" -> "func@file" +my %ambiguous; # "func" -> 1 foreach (@ARGV) { - # Disassemble this object file to obtain a callees. Sources in the - # call graph are named "func@file". Targets in the call graph are - # named either "offset@file" or "funcname". We also keep a list of - # the addresses and names of each function we encounter. - my $objfile = $_; - my $source; - - open(DISASSEMBLY, "$objdump -Cdr $objfile|") || - die "Can't disassemble $objfile"; - while (<DISASSEMBLY>) { - chomp; - - if (/^([0-9a-fA-F]+) <(.*)>:/) { - my $a = $1; - my $name = $2; - - $source = "$name\@$objfile"; - $call_graph{$source} = {}; - $ambiguous{$name} = 1 if defined($global_name{$name}); - $global_name{$name} = "$name\@$objfile"; - - $a =~ s/^0*//; - $addresses{"$a\@$objfile"} = "$name\@$objfile"; - } - if (/: R_[A-Za-z0-9_]+_(?:CALL|PLT32|ABS16)[ \t]+(.*)/) { - my $t = $1; + # Disassemble this object file to obtain a callees. Sources in the + # call graph are named "func@file". Targets in the call graph are + # named either "offset@file" or "funcname". We also keep a list of + # the addresses and names of each function we encounter. + my $objfile = $_; + my $source; + + open( DISASSEMBLY, "$objdump -Cdr $objfile|" ) + || die "Can't disassemble $objfile"; + while (<DISASSEMBLY>) { + chomp; + + if (/^([0-9a-fA-F]+) <(.*)>:/) { + my $a = $1; + my $name = $2; + + $source = "$name\@$objfile"; + $call_graph{$source} = {}; + $ambiguous{$name} = 1 if defined( $global_name{$name} ); + $global_name{$name} = "$name\@$objfile"; + + $a =~ s/^0*//; + $addresses{"$a\@$objfile"} = "$name\@$objfile"; + } + + if (/: R_[A-Za-z0-9_]+_(?:CALL|PLT32|ABS16)[ \t]+(.*)/) { + my $t = $1; - if ($t eq ".text") { - $t = "\@$objfile"; - } elsif ($t =~ /^\.text\+0x(.*)$/) { - $t = "$1\@$objfile"; - } + if ( $t eq ".text" ) { + $t = "\@$objfile"; + } + elsif ( $t =~ /^\.text\+0x(.*)$/ ) { + $t = "$1\@$objfile"; + } - $t =~ s{-0x4$}{}; + $t =~ s{-0x4$}{}; - $call_graph{$source}->{$t} = 1; + $call_graph{$source}->{$t} = 1; + } } - } - close(DISASSEMBLY); + close(DISASSEMBLY); - # Extract frame sizes from the corresponding .su file. - if ($objfile =~ /^(.*).o$/) { - my $sufile = "$1.su"; + # Extract frame sizes from the corresponding .su file. + if ( $objfile =~ /^(.*).o$/ ) { + my $sufile = "$1.su"; - open(SUFILE, "<$sufile") || die "Can't open $sufile"; - while (<SUFILE>) { - if (m{ ^ [^:]+ : \d+ : \d+ : [^\t]+? \s (?<name> \S+ ) \( .*? \) + open( SUFILE, "<$sufile" ) || die "Can't open $sufile"; + while (<SUFILE>) { + if ( + m{ ^ [^:]+ : \d+ : \d+ : [^\t]+? \s (?<name> \S+ ) \( .*? \) (?: \s* \[with [^]]*\])? \t - (?<size> \d+ )}x) { - $frame_size{"$+{name}\@$objfile"} = $+{size} + $call_cost; - } else { - say "No match $_"; + (?<size> \d+ )}x + ) + { + $frame_size{"$+{name}\@$objfile"} = $+{size} + $call_cost; + } + else { + say "No match $_"; + } } + close(SUFILE); } - close(SUFILE); - } } # In this step, we enumerate each list of callees in the call graph and @@ -135,34 +141,37 @@ foreach (@ARGV) { my %unresolved; -foreach (keys %call_graph) { - my $from = $_; - my $callees = $call_graph{$from}; - my %resolved; - - foreach (keys %$callees) { - my $t = $_; - - if (defined($addresses{$t})) { - $resolved{$addresses{$t}} = 1; - } elsif (defined($global_name{$t})) { - $resolved{$global_name{$t}} = 1; - warn "Ambiguous resolution: $t" if defined ($ambiguous{$t}); - } elsif (defined($call_graph{$t})) { - $resolved{$t} = 1; - } else { - $unresolved{$t} = 1; +foreach ( keys %call_graph ) { + my $from = $_; + my $callees = $call_graph{$from}; + my %resolved; + + foreach ( keys %$callees ) { + my $t = $_; + + if ( defined( $addresses{$t} ) ) { + $resolved{ $addresses{$t} } = 1; + } + elsif ( defined( $global_name{$t} ) ) { + $resolved{ $global_name{$t} } = 1; + warn "Ambiguous resolution: $t" if defined( $ambiguous{$t} ); + } + elsif ( defined( $call_graph{$t} ) ) { + $resolved{$t} = 1; + } + else { + $unresolved{$t} = 1; + } } - } - $call_graph{$from} = \%resolved; + $call_graph{$from} = \%resolved; } # Create fake edges and nodes to account for dynamic behaviour. $call_graph{"INTERRUPT"} = {}; -foreach (keys %call_graph) { - $call_graph{"INTERRUPT"}->{$_} = 1 if /^__vector_/; +foreach ( keys %call_graph ) { + $call_graph{"INTERRUPT"}->{$_} = 1 if /^__vector_/; } # Trace the call graph and calculate, for each function: @@ -179,84 +188,84 @@ my %total_cost; my %call_depth; sub trace { - my $f = shift; + my $f = shift; - if ($visited{$f}) { - $visited{$f} = "R" if $visited{$f} eq "?"; - return; - } + if ( $visited{$f} ) { + $visited{$f} = "R" if $visited{$f} eq "?"; + return; + } - $visited{$f} = "?"; + $visited{$f} = "?"; - my $max_depth = 0; - my $max_frame = 0; + my $max_depth = 0; + my $max_frame = 0; - my $targets = $call_graph{$f} || die "Unknown function: $f"; - if (defined($targets)) { - foreach (keys %$targets) { - my $t = $_; + my $targets = $call_graph{$f} || die "Unknown function: $f"; + if ( defined($targets) ) { + foreach ( keys %$targets ) { + my $t = $_; - $has_caller{$t} = 1; - trace($t); + $has_caller{$t} = 1; + trace($t); - my $is = $total_cost{$t}; - my $d = $call_depth{$t}; + my $is = $total_cost{$t}; + my $d = $call_depth{$t}; - $max_frame = $is if $is > $max_frame; - $max_depth = $d if $d > $max_depth; + $max_frame = $is if $is > $max_frame; + $max_depth = $d if $d > $max_depth; + } } - } - $call_depth{$f} = $max_depth + 1; - $total_cost{$f} = $max_frame + ($frame_size{$f} || 0); - $visited{$f} = " " if $visited{$f} eq "?"; + $call_depth{$f} = $max_depth + 1; + $total_cost{$f} = $max_frame + ( $frame_size{$f} || 0 ); + $visited{$f} = " " if $visited{$f} eq "?"; } -foreach (keys %call_graph) { trace $_; } +foreach ( keys %call_graph ) { trace $_; } # Now, print results in a nice table. -printf " %-30s %8s %8s %8s\n", - "Func", "Cost", "Frame", "Height"; +printf " %-30s %8s %8s %8s\n", "Func", "Cost", "Frame", "Height"; print "------------------------------------"; print "------------------------------------\n"; my $max_iv = 0; -my $main = 0; +my $main = 0; -foreach (sort { $total_cost{$b} <=> $total_cost{$a} } keys %visited) { - my $name = $_; +foreach ( sort { $total_cost{$b} <=> $total_cost{$a} } keys %visited ) { + my $name = $_; - if (/^(.*)@(.*)$/) { - $name = $1 unless $ambiguous{$name}; - } + if (/^(.*)@(.*)$/) { + $name = $1 unless $ambiguous{$name}; + } - my $tag = $visited{$_}; - my $cost = $total_cost{$_}; + my $tag = $visited{$_}; + my $cost = $total_cost{$_}; - $name = $_ if $ambiguous{$name}; - $tag = ">" unless $has_caller{$_}; + $name = $_ if $ambiguous{$name}; + $tag = ">" unless $has_caller{$_}; - if (/^__vector_/) { - $max_iv = $cost if $cost > $max_iv; - } elsif (/^main@/) { - $main = $cost; - } + if (/^__vector_/) { + $max_iv = $cost if $cost > $max_iv; + } + elsif (/^main@/) { + $main = $cost; + } - if ($ambiguous{$name}) { $name = $_; } + if ( $ambiguous{$name} ) { $name = $_; } - printf "%s %-30s %8d %8d %8d\n", $tag, $name, $cost, - $frame_size{$_} || 0, $call_depth{$_}; + printf "%s %-30s %8d %8d %8d\n", $tag, $name, $cost, + $frame_size{$_} || 0, $call_depth{$_}; } print "\n"; print "Peak execution estimate (main + worst-case IV):\n"; printf " main = %d, worst IV = %d, total = %d\n", - $total_cost{$global_name{"main"}}, - $total_cost{"INTERRUPT"}, - $total_cost{$global_name{"main"}} + $total_cost{"INTERRUPT"}; + $total_cost{ $global_name{"main"} }, + $total_cost{"INTERRUPT"}, + $total_cost{ $global_name{"main"} } + $total_cost{"INTERRUPT"}; print "\n"; print "The following functions were not resolved:\n"; -foreach (keys %unresolved) { print " $_\n"; } +foreach ( keys %unresolved ) { print " $_\n"; } |