-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathannotate.pl
85 lines (74 loc) · 2.31 KB
/
annotate.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#! /usr/bin/env perl
use strict;
use warnings;
use File::Basename qw(basename);
use Getopt::Long;
use List::Util qw(first);
my ($pid, $dumpfn);
my (@maps, @callsites);
GetOptions(
"pid=i" => \$pid,
"file=s" => \$dumpfn,
) or die "invalid command line arguments";
# read maps into @maps
open my $fh, "<", "/proc/$pid/maps"
or die "failed to open /proc/$pid/maps:$!";
while (my $line = <$fh>) {
chomp $line;
$line =~ /^([0-9a-f]+)-([0-9a-f]+)\s+\S+\s+([0-9a-f]+)\s+\S+\s+\S+\s+/
or die "failed to parse memory mapping line:$line";
if (substr($', 0, 1) eq '/') {
push @maps, {start => bighex($1), end => bighex($2), offset => bighex($3), exe => $'};
}
}
close $fh;
# read dumpfn into @callsites
open $fh, "<", $dumpfn
or die "failed to file $dumpfn:$!";
while (1) {
my $line = <$fh>;
die "unexpected end of line in $dumpfn" unless $line;
chomp $line;
last unless $line; # empty line terminates the input
my ($bytes_alloced, $alloc_cnt, $free_cnt, $collision_cnt, @callers) = split /\s/, $line;
push @callsites, {
callers => [ map { bighex($_) } @callers ],
bytes_alloced => $bytes_alloced,
alloc_cnt => $alloc_cnt,
free_cnt => $free_cnt,
collision_cnt => $collision_cnt,
};
}
# sort the list by bytes_alloced in descending order
@callsites = sort { $b->{bytes_alloced} <=> $a->{bytes_alloced} } @callsites;
# print
for my $cs (@callsites) {
printf "%d bytes at 0x%x, alloc=%d, free=%d, collision=%d\n", $cs->{bytes_alloced}, $cs->{callers}->[0], $cs->{alloc_cnt}, $cs->{free_cnt}, $cs->{collision_cnt};
# resolve addresses
for my $addr (@{$cs->{callers}}) {
my $map = first { $_->{start} <= $addr && $addr <= $_->{end} } @maps;
if ($map) {
my $offset = $addr - $map->{start} + $map->{offset};
my $loc = addr2line($map->{exe}, $offset)
or last;
print $loc;
}
}
print "\n";
}
sub addr2line {
my ($exe, $addr) = @_;
open my $fh, "-|", qw(addr2line -pif -e), $exe, sprintf("%x", $addr)
or return;
my @lines = <$fh>;
pop @lines
if $lines[$#lines] eq '';
@lines = map { " $_" } @lines;
join "", @lines;
}
sub bighex {
my $s = shift;
$s =~ s/^0x//;
no warnings 'portable';
hex $s;
}