|
|
1.1 ! root 1: #!/usr/bin/perl -w ! 2: # ! 3: # Copyright (C) 2010 Michael Brown <[email protected]>. ! 4: # ! 5: # This program is free software; you can redistribute it and/or ! 6: # modify it under the terms of the GNU General Public License as ! 7: # published by the Free Software Foundation; either version 2 of the ! 8: # License, or any later version. ! 9: # ! 10: # This program is distributed in the hope that it will be useful, but ! 11: # WITHOUT ANY WARRANTY; without even the implied warranty of ! 12: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 13: # General Public License for more details. ! 14: # ! 15: # You should have received a copy of the GNU General Public License ! 16: # along with this program; if not, write to the Free Software ! 17: # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 18: ! 19: =head1 NAME ! 20: ! 21: fnrec.pl ! 22: ! 23: =head1 SYNOPSIS ! 24: ! 25: fnrec.pl [options] bin/image.xxx < logfile ! 26: ! 27: Decode a function trace produced by building with FNREC=1 ! 28: ! 29: Options: ! 30: ! 31: -m,--max-depth=N Set maximum displayed function depth ! 32: ! 33: =cut ! 34: ! 35: use IPC::Open2; ! 36: use Getopt::Long; ! 37: use Pod::Usage; ! 38: use strict; ! 39: use warnings; ! 40: ! 41: use constant MAX_OPEN_BRACE => 10; ! 42: use constant MAX_COMMON_BRACE => 3; ! 43: use constant MAX_CLOSE_BRACE => 10; ! 44: ! 45: # Parse command-line options ! 46: my $max_depth = 16; ! 47: Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); ! 48: GetOptions ( ! 49: 'help|h' => sub { pod2usage ( 1 ); }, ! 50: 'max-depth|m=i' => sub { shift; $max_depth = shift; }, ! 51: ) or die "Could not parse command-line options\n"; ! 52: pod2usage ( 1 ) unless @ARGV == 1; ! 53: my $image = shift; ! 54: my $elf = $image.".tmp"; ! 55: die "ELF file ".$elf." not found\n" unless -e $elf; ! 56: ! 57: # Start up addr2line ! 58: my $addr2line_pid = open2 ( my $addr2line_out, my $addr2line_in, ! 59: "addr2line", "-f", "-e", $elf ) ! 60: or die "Could not start addr2line: $!\n"; ! 61: ! 62: # Translate address using addr2line ! 63: sub addr2line { ! 64: my $address = shift; ! 65: ! 66: print $addr2line_in $address."\n"; ! 67: chomp ( my $name = <$addr2line_out> ); ! 68: chomp ( my $file_line = <$addr2line_out> ); ! 69: ( my $file, my $line ) = ( $file_line =~ /^(.*):(\d+)$/ ); ! 70: $file =~ s/^.*\/src\///; ! 71: my $location = ( $line ? $file.":".$line." = ".$address : $address ); ! 72: return ( $name, $location ); ! 73: } ! 74: ! 75: # Parse logfile ! 76: my $depth = 0; ! 77: my $depths = []; ! 78: while ( my $line = <> ) { ! 79: chomp $line; ! 80: $line =~ s/\r//g; ! 81: ( my $called_fn, my $call_site, my $entry_count, my $exit_count ) = ! 82: ( $line =~ /^(0x[0-9a-f]+)\s+(0x[0-9a-f]+)\s+([0-9]+)\s+([0-9]+)$/ ) ! 83: or print $line."\n" and next; ! 84: ! 85: ( my $called_fn_name, undef ) = addr2line ( $called_fn ); ! 86: ( undef, my $call_site_location ) = addr2line ( $call_site ); ! 87: $entry_count = ( $entry_count + 0 ); ! 88: $exit_count = ( $exit_count + 0 ); ! 89: ! 90: if ( $entry_count >= $exit_count ) { ! 91: # ! 92: # Function entry ! 93: # ! 94: my $text = ""; ! 95: $text .= $called_fn_name." (from ".$call_site_location.")"; ! 96: if ( $exit_count <= MAX_COMMON_BRACE ) { ! 97: $text .= " { }" x $exit_count; ! 98: } else { ! 99: $text .= " { } x ".$exit_count; ! 100: } ! 101: $entry_count -= $exit_count; ! 102: if ( $entry_count <= MAX_OPEN_BRACE ) { ! 103: $text .= " {" x $entry_count; ! 104: } else { ! 105: $text .= " { x ".$entry_count; ! 106: } ! 107: my $indent = " " x $depth; ! 108: print $indent.$text."\n"; ! 109: $depth += $entry_count; ! 110: $depth = $max_depth if ( $depth > $max_depth ); ! 111: push @$depths, ( { called_fn => $called_fn, call_site => $call_site } ) x ! 112: ( $depth - @$depths ); ! 113: } else { ! 114: # ! 115: # Function exit ! 116: # ! 117: my $text = ""; ! 118: if ( $entry_count <= MAX_COMMON_BRACE ) { ! 119: $text .= " { }" x $entry_count; ! 120: } else { ! 121: $text .= " { } x ".$entry_count; ! 122: } ! 123: $exit_count -= $entry_count; ! 124: if ( $exit_count <= MAX_CLOSE_BRACE ) { ! 125: $text .= " }" x $exit_count; ! 126: } else { ! 127: $text .= " } x ".$exit_count; ! 128: } ! 129: $depth -= $exit_count; ! 130: $depth = 0 if ( $depth < 0 ); ! 131: if ( ( @$depths == 0 ) || ! 132: ( $depths->[$depth]->{called_fn} ne $called_fn ) || ! 133: ( $depths->[$depth]->{call_site} ne $call_site ) ) { ! 134: $text .= " (from ".$called_fn_name." to ".$call_site_location.")"; ! 135: } ! 136: splice ( @$depths, $depth ); ! 137: my $indent = " " x $depth; ! 138: print substr ( $indent.$text, 1 )."\n"; ! 139: } ! 140: } ! 141: ! 142: # Clean up addr2line ! 143: close $addr2line_in; ! 144: close $addr2line_out; ! 145: waitpid ( $addr2line_pid, 0 );
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.