Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / util / fnrec.pl
1 #!/usr/bin/perl -w
2 #
3 # Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
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., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19
20 =head1 NAME
21
22 fnrec.pl
23
24 =head1 SYNOPSIS
25
26 fnrec.pl [options] bin/image.xxx < logfile
27
28 Decode a function trace produced by building with FNREC=1
29
30 Options:
31
32         -m,--max-depth=N        Set maximum displayed function depth
33
34 =cut
35
36 use IPC::Open2;
37 use Getopt::Long;
38 use Pod::Usage;
39 use strict;
40 use warnings;
41
42 use constant MAX_OPEN_BRACE => 10;
43 use constant MAX_COMMON_BRACE => 3;
44 use constant MAX_CLOSE_BRACE => 10;
45
46 # Parse command-line options
47 my $max_depth = 16;
48 Getopt::Long::Configure ( 'bundling', 'auto_abbrev' );
49 GetOptions (
50   'help|h' => sub { pod2usage ( 1 ); },
51   'max-depth|m=i' => sub { shift; $max_depth = shift; },
52 ) or die "Could not parse command-line options\n";
53 pod2usage ( 1 ) unless @ARGV == 1;
54 my $image = shift;
55 my $elf = $image.".tmp";
56 die "ELF file ".$elf." not found\n" unless -e $elf;
57
58 # Start up addr2line
59 my $addr2line_pid = open2 ( my $addr2line_out, my $addr2line_in,
60                             "addr2line", "-f", "-e", $elf )
61     or die "Could not start addr2line: $!\n";
62
63 # Translate address using addr2line
64 sub addr2line {
65   my $address = shift;
66
67   print $addr2line_in $address."\n";
68   chomp ( my $name = <$addr2line_out> );
69   chomp ( my $file_line = <$addr2line_out> );
70   ( my $file, my $line ) = ( $file_line =~ /^(.*):(\d+)$/ );
71   $file =~ s/^.*\/src\///;
72   my $location = ( $line ? $file.":".$line." = ".$address : $address );
73   return ( $name, $location );
74 }
75
76 # Parse logfile
77 my $depth = 0;
78 my $depths = [];
79 while ( my $line = <> ) {
80   chomp $line;
81   $line =~ s/\r//g;
82   ( my $called_fn, my $call_site, my $entry_count, my $exit_count ) =
83       ( $line =~ /^(0x[0-9a-f]+)\s+(0x[0-9a-f]+)\s+([0-9]+)\s+([0-9]+)$/ )
84       or print $line."\n" and next;
85
86   ( my $called_fn_name, undef ) = addr2line ( $called_fn );
87   ( undef, my $call_site_location ) = addr2line ( $call_site );
88   $entry_count = ( $entry_count + 0 );
89   $exit_count = ( $exit_count + 0 );
90
91   if ( $entry_count >= $exit_count ) {
92     #
93     # Function entry
94     #
95     my $text = "";
96     $text .= $called_fn_name." (from ".$call_site_location.")";
97     if ( $exit_count <= MAX_COMMON_BRACE ) {
98       $text .= " { }" x $exit_count;
99     } else {
100       $text .= " { } x ".$exit_count;
101     }
102     $entry_count -= $exit_count;
103     if ( $entry_count <= MAX_OPEN_BRACE ) {
104       $text .= " {" x $entry_count;
105     } else {
106       $text .= " { x ".$entry_count;
107     }
108     my $indent = "  " x $depth;
109     print $indent.$text."\n";
110     $depth += $entry_count;
111     $depth = $max_depth if ( $depth > $max_depth );
112     push @$depths, ( { called_fn => $called_fn, call_site => $call_site } ) x
113         ( $depth - @$depths );
114   } else {
115     #
116     # Function exit
117     #
118     my $text = "";
119     if ( $entry_count <= MAX_COMMON_BRACE ) {
120       $text .= " { }" x $entry_count;
121     } else {
122       $text .= " { } x ".$entry_count;
123     }
124     $exit_count -= $entry_count;
125     if ( $exit_count <= MAX_CLOSE_BRACE ) {
126       $text .= " }" x $exit_count;
127     } else {
128       $text .= " } x ".$exit_count;
129     }
130     $depth -= $exit_count;
131     $depth = 0 if ( $depth < 0 );
132     if ( ( @$depths == 0 ) ||
133          ( $depths->[$depth]->{called_fn} ne $called_fn ) ||
134          ( $depths->[$depth]->{call_site} ne $call_site ) ) {
135       $text .= " (from ".$called_fn_name." to ".$call_site_location.")";
136     }
137     splice ( @$depths, $depth );
138     my $indent = "  " x $depth;
139     print substr ( $indent.$text, 1 )."\n";
140   }
141 }
142
143 # Clean up addr2line
144 close $addr2line_in;
145 close $addr2line_out;
146 waitpid ( $addr2line_pid, 0 );