Skip to content

Commit

Permalink
Merge pull request #251 from jan-konczak-cs-put/master
Browse files Browse the repository at this point in the history
enhancing stackcollapse-perf.pl "--inline" option: caching addr2line and getting rid of '??' in graphs
  • Loading branch information
brendangregg authored Aug 31, 2021
2 parents a258e78 + 15c0c57 commit 0961c43
Showing 1 changed file with 92 additions and 7 deletions.
99 changes: 92 additions & 7 deletions stackcollapse-perf.pl
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ sub remember_stack {

my $show_inline = 0;
my $show_context = 0;

my $srcline_in_input = 0; # if there are extra lines with source location (perf script -F+srcline)
GetOptions('inline' => \$show_inline,
'context' => \$show_context,
'srcline' => \$srcline_in_input,
'pid' => \$include_pid,
'kernel' => \$annotate_kernel,
'jit' => \$annotate_jit,
Expand All @@ -106,6 +109,7 @@ sub remember_stack {
--kernel # annotate kernel functions with a _[k]
--jit # annotate jit functions with a _[j]
--context # adds source context to --inline
--srcline # parses output of 'perf script -F+srcline' and adds source context
--addrs # include raw addresses where symbols can't be found
--event-filter=EVENT # event name filter\n
[1] perf script must emit both PID and TIDs for these to work; eg, Linux < 4.1:
Expand All @@ -119,16 +123,49 @@ sub remember_stack {
$annotate_kernel = $annotate_jit = 1;
}

my %inlineCache;

my %nmCache;

sub inlineCacheAdd {
my ($pc, $mod, $result) = @_;
if (defined($inlineCache{$pc})) {
$inlineCache{$pc}{$mod} = $result;
} else {
$inlineCache{$pc} = {$mod => $result};
}
}

# for the --inline option
sub inline {
my ($pc, $mod) = @_;
my ($pc, $rawfunc, $mod) = @_;

return $inlineCache{$pc}{$mod} if defined($inlineCache{$pc}{$mod});

# capture addr2line output
my $a2l_output = `addr2line -a $pc -e $mod -i -f -s -C`;

# remove first line
$a2l_output =~ s/^(.*\n){1}//;

if ($a2l_output =~ /\?\?\n\?\?:0/) {
# if addr2line fails and rawfunc is func+offset, then fall back to it
if ($rawfunc =~ /^(.+)\+0x([0-9a-f]+)$/) {
my $func = $1;
my $addr = hex $2;

$nmCache{$mod}=`nm $mod` unless defined $nmCache{$mod};

if ($nmCache{$mod} =~ /^([0-9a-f]+) . \Q$func\E$/m) {
my $base = hex $1;
my $newPc = sprintf "0x%x", $base+$addr;
my $result = inline($newPc, '', $mod);
inlineCacheAdd($pc, $mod, $result);
return $result;
}
}
}

my @fullfunc;
my $one_item = "";
for (split /^/, $a2l_output) {
Expand All @@ -149,7 +186,11 @@ sub inline {
}
}

return join(";", @fullfunc);
my $result = join ";" , @fullfunc;

inlineCacheAdd($pc, $mod, $result);

return $result;
}

my @stack;
Expand Down Expand Up @@ -256,18 +297,25 @@ sub inline {

my ($pc, $rawfunc, $mod) = ($1, $2, $3);

if ($show_inline == 1 && $mod !~ m/(perf-\d+.map|kernel\.|\[[^\]]+\])/) {
my $inlineRes = inline($pc, $rawfunc, $mod);
# - empty result this happens e.g., when $mod does not exist or is a path to a compressed kernel module
# if this happens, the user will see error message from addr2line written to stderr
# - if addr2line results in "??" , then it's much more sane to fall back than produce a '??' in graph
if($inlineRes ne "" and $inlineRes ne "??" and $inlineRes ne "??:??:0" ) {
unshift @stack, $inlineRes;
next;
}
}

# Linux 4.8 included symbol offsets in perf script output by default, eg:
# 7fffb84c9afc cpu_startup_entry+0x800047c022ec ([kernel.kallsyms])
# strip these off:
$rawfunc =~ s/\+0x[\da-f]+$//;

if ($show_inline == 1 && $mod !~ m/(perf-\d+.map|kernel\.|\[[^\]]+\])/) {
unshift @stack, inline($pc, $mod);
next;
}

next if $rawfunc =~ /^\(/; # skip process names

my $is_unknown=0;
my @inline;
for (split /\->/, $rawfunc) {
my $func = $_;
Expand All @@ -278,6 +326,7 @@ sub inline {
$func =~ s/.*\///;
} else {
$func = "unknown";
$is_unknown=1;
}

if ($include_addrs) {
Expand Down Expand Up @@ -331,6 +380,42 @@ sub inline {
} elsif ($annotate_jit == 1 && $mod =~ m:/tmp/perf-\d+\.map:) {
$func .= "_[j]"; # jitted
}

#
# Source lines
#
#
# Sample outputs:
# | a.out 35081 252436.005167: 667783 cycles:
# | 408ebb some_method_name+0x8b (/full/path/to/a.out)
# | uniform_int_dist.h:300
# | 4069f5 main+0x935 (/full/path/to/a.out)
# | file.cpp:137
# | 7f6d2148eb25 __libc_start_main+0xd5 (/lib64/libc-2.33.so)
# | libc-2.33.so[27b25]
#
# | a.out 35081 252435.738165: 306459 cycles:
# | 7f6d213c2750 [unknown] (/usr/lib64/libkmod.so.2.3.6)
# | libkmod.so.2.3.6[6750]
#
# | a.out 35081 252435.738373: 315813 cycles:
# | 7f6d215ca51b __strlen_avx2+0x4b (/lib64/libc-2.33.so)
# | libc-2.33.so[16351b]
# | 7ffc71ee9580 [unknown] ([unknown])
# |
#
# | a.out 35081 252435.718940: 247984 cycles:
# | ffffffff814f9302 up_write+0x32 ([kernel.kallsyms])
# | [kernel.kallsyms][ffffffff814f9302]
if($srcline_in_input and not $is_unknown){
$_ = <>;
chomp;
s/\[.*?\]//g;
s/^\s*//g;
s/\s*$//g;
$func.=':'.$_ unless $_ eq "";
}

push @inline, $func;
}

Expand Down

0 comments on commit 0961c43

Please sign in to comment.