Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enhance the information collected by the function recorder to include the call site and entry/exit counts. This allows fnrec.pl to produce a call tree such as: step (from core/getkey.c:46 = 0x17e90) { ref_increment (from core/process.c:93 = 0x73ec) { } net_step (from core/process.c:96 = 0x73f1) { net_poll (from net/netdevice.c:741 = 0xbce6) { netdev_poll (from net/netdevice.c:700 = 0xbc58) { } netdev_rx_dequeue (from net/netdevice.c:709 = 0xbc65) { } } } ref_decrement (from core/process.c:96 = 0x73f9) { } } Note that inlined functions are reported, confusingly, as extra calls to the *containing* function. Minimise this confusion by adding the attribute "no_instrument_function" to all functions declared as inline. (Static functions that have been inlined autonomously by gcc will still be problematic, but these are far fewer in number.) Signed-off-by: Michael Brown <mcb30@ipxe.org>
- Loading branch information
Showing
4 changed files
with
265 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
#!/usr/bin/perl -w | ||
# | ||
# Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. | ||
# | ||
# This program is free software; you can redistribute it and/or | ||
# modify it under the terms of the GNU General Public License as | ||
# published by the Free Software Foundation; either version 2 of the | ||
# License, or any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, but | ||
# WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
# General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program; if not, write to the Free Software | ||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
|
||
=head1 NAME | ||
fnrec.pl | ||
=head1 SYNOPSIS | ||
fnrec.pl [options] bin/image.xxx < logfile | ||
Decode a function trace produced by building with FNREC=1 | ||
Options: | ||
-m,--max-depth=N Set maximum displayed function depth | ||
=cut | ||
|
||
use IPC::Open2; | ||
use Getopt::Long; | ||
use Pod::Usage; | ||
use strict; | ||
use warnings; | ||
|
||
use constant MAX_OPEN_BRACE => 10; | ||
use constant MAX_COMMON_BRACE => 3; | ||
use constant MAX_CLOSE_BRACE => 10; | ||
|
||
# Parse command-line options | ||
my $max_depth = 16; | ||
Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); | ||
GetOptions ( | ||
'help|h' => sub { pod2usage ( 1 ); }, | ||
'max-depth|m=i' => sub { shift; $max_depth = shift; }, | ||
) or die "Could not parse command-line options\n"; | ||
pod2usage ( 1 ) unless @ARGV == 1; | ||
my $image = shift; | ||
my $elf = $image.".tmp"; | ||
die "ELF file ".$elf." not found\n" unless -e $elf; | ||
|
||
# Start up addr2line | ||
my $addr2line_pid = open2 ( my $addr2line_out, my $addr2line_in, | ||
"addr2line", "-f", "-e", $elf ) | ||
or die "Could not start addr2line: $!\n"; | ||
|
||
# Translate address using addr2line | ||
sub addr2line { | ||
my $address = shift; | ||
|
||
print $addr2line_in $address."\n"; | ||
chomp ( my $name = <$addr2line_out> ); | ||
chomp ( my $file_line = <$addr2line_out> ); | ||
( my $file, my $line ) = ( $file_line =~ /^(.*):(\d+)$/ ); | ||
$file =~ s/^.*\/src\///; | ||
my $location = ( $line ? $file.":".$line." = ".$address : $address ); | ||
return ( $name, $location ); | ||
} | ||
|
||
# Parse logfile | ||
my $depth = 0; | ||
my $depths = []; | ||
while ( my $line = <> ) { | ||
chomp $line; | ||
$line =~ s/\r//g; | ||
( my $called_fn, my $call_site, my $entry_count, my $exit_count ) = | ||
( $line =~ /^(0x[0-9a-f]+)\s+(0x[0-9a-f]+)\s+([0-9]+)\s+([0-9]+)$/ ) | ||
or print $line."\n" and next; | ||
|
||
( my $called_fn_name, undef ) = addr2line ( $called_fn ); | ||
( undef, my $call_site_location ) = addr2line ( $call_site ); | ||
$entry_count = ( $entry_count + 0 ); | ||
$exit_count = ( $exit_count + 0 ); | ||
|
||
if ( $entry_count >= $exit_count ) { | ||
# | ||
# Function entry | ||
# | ||
my $text = ""; | ||
$text .= $called_fn_name." (from ".$call_site_location.")"; | ||
if ( $exit_count <= MAX_COMMON_BRACE ) { | ||
$text .= " { }" x $exit_count; | ||
} else { | ||
$text .= " { } x ".$exit_count; | ||
} | ||
$entry_count -= $exit_count; | ||
if ( $entry_count <= MAX_OPEN_BRACE ) { | ||
$text .= " {" x $entry_count; | ||
} else { | ||
$text .= " { x ".$entry_count; | ||
} | ||
my $indent = " " x $depth; | ||
print $indent.$text."\n"; | ||
$depth += $entry_count; | ||
$depth = $max_depth if ( $depth > $max_depth ); | ||
push @$depths, ( { called_fn => $called_fn, call_site => $call_site } ) x | ||
( $depth - @$depths ); | ||
} else { | ||
# | ||
# Function exit | ||
# | ||
my $text = ""; | ||
if ( $entry_count <= MAX_COMMON_BRACE ) { | ||
$text .= " { }" x $entry_count; | ||
} else { | ||
$text .= " { } x ".$entry_count; | ||
} | ||
$exit_count -= $entry_count; | ||
if ( $exit_count <= MAX_CLOSE_BRACE ) { | ||
$text .= " }" x $exit_count; | ||
} else { | ||
$text .= " } x ".$exit_count; | ||
} | ||
$depth -= $exit_count; | ||
$depth = 0 if ( $depth < 0 ); | ||
if ( ( @$depths == 0 ) || | ||
( $depths->[$depth]->{called_fn} ne $called_fn ) || | ||
( $depths->[$depth]->{call_site} ne $call_site ) ) { | ||
$text .= " (from ".$called_fn_name." to ".$call_site_location.")"; | ||
} | ||
splice ( @$depths, $depth ); | ||
my $indent = " " x $depth; | ||
print substr ( $indent.$text, 1 )."\n"; | ||
} | ||
} | ||
|
||
# Clean up addr2line | ||
close $addr2line_in; | ||
close $addr2line_out; | ||
waitpid ( $addr2line_pid, 0 ); |
Oops, something went wrong.