source: memlog2dot @ 6bfe6a1

Revision 6bfe6a1, 5.1 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

don't print raw proc names by default

  • Property mode set to 100755
RevLine 
[ebd57f8]1#! /usr/bin/env perl
2use strict;
3use File::Basename;
4
[81dea5b]5my $memlog_fn = $ARGV[0];
6my $out_dir = $ARGV[1] || '.';
7
[6bfe6a1]8my $print_raw_proc_name = 0;
9
[81dea5b]10if (! -f $memlog_fn) {
11  print "Usage: $0 <memlog file> [<output directory>]\n";
12  exit 1;
13}
14
15open(MEMLOG, $memlog_fn) || die "Can't open $memlog_fn: $!";
16
[ebd57f8]17my $total_size = 0;
18my %roots;
[c4f89cf]19my %all_nodes;
[81dea5b]20foreach my $line (<MEMLOG>) {
[ebd57f8]21  chomp($line);
22  my @parts = split(/\t/, $line);
23
24  my $op = shift(@parts);
25  my $state = shift(@parts);
26
27  # Only dealing with allocations here...
28  if ($op !~ /^M:/) {
29    next;
30  }
31
32  my ($size, $ptr) = ($op =~ /^M: (\d+) 0x(\w+)/);
33  my ($time, $maxrss, $tid) = split(/\s+/, $state);
34
35  $total_size += $size;
36
37  sub level_parts($) {
38    my $level = @_[0];
39    my ($file_name, $proc_name, $off, $pc, $relpc) =
40      ($level =~ /^(.*) \((.*)\+0x(\w+)\) \[0x(\w+) \(0x(\w+)\)\]/);
41
42    return ($file_name, $proc_name, $off, $pc, $relpc);
43  }
44
45  # Put the top of the stack first.
46  @parts = reverse(@parts);
47
48  my $parent = \%roots;
49  for (my $i = 0; $i < scalar(@parts); ++$i) {
50    my $level = $parts[$i];
51    my ($file_name, $proc_name, $off, $pc, $relpc) = level_parts($level);
52
53    # Skip this level if we don't even know from what file it came.
54    if ($file_name eq '?') {
55      next;
56    }
57
58    # print STDERR "parsed: $file_name, $proc_name, $off, $pc, $relpc\n";
59
[c4f89cf]60    if (!exists $all_nodes{$pc}) {
61      $all_nodes{$pc}->{'file_name'} = $file_name;
62      $all_nodes{$pc}->{'proc_name'} = $proc_name;
63      $all_nodes{$pc}->{'off'} = $off;
64      $all_nodes{$pc}->{'pc'} = $pc;
65      $all_nodes{$pc}->{'relpc'} = $relpc;
66    }
[ebd57f8]67
[c4f89cf]68    if (!exists $parent->{$pc}) {
69      $parent->{$pc} = $all_nodes{$pc};
[ebd57f8]70    }
71
72    $parent->{$pc}->{'size'} += $size;
73
74    my ($next_file_name, $next_proc_name, $next_off, $next_pc, $next_relpc);
75    if ($i < scalar(@parts)-1) {
76     my $next_level = $parts[$i+1];
77     ($next_file_name, $next_proc_name, $next_off, $next_pc, $next_relpc) =
78       level_parts($next_level);
79      $parent->{$pc}->{'child_sizes'}->{$next_pc} += $size;
80    }
81
82    if (!exists $parent->{'children'}) {
83      $parent->{'children'} = {};
84    }
85
86    $parent = $parent->{'children'};
87  }
88}
89
[81dea5b]90close(MEMLOG);
91
92my $dot_fn = "$out_dir/" . basename($memlog_fn) . ".dot";
93my $ps_fn = "$out_dir/" . basename($memlog_fn) . ".ps";
94my $pdf_fn = "$out_dir/" . basename($memlog_fn) . ".pdf";
95
96open(DOT, ">$dot_fn") || die "Can't open $dot_fn: $!";
97
[ebd57f8]98sub format_bytes($) {
99  my @sizes = qw( B KB MB GB TB PB );
100  my $size = $_[0];
101
102  my $i = 0;
103  while ($size > 1024) {
104    $size /= 1024;
105    ++$i;
106  }
107
[4bd7a88]108  return sprintf("%.3f $sizes[$i]", $size);
[ebd57f8]109}
110
[81dea5b]111printf DOT ("digraph \"memlog %s\" {\n", format_bytes($total_size));
112print DOT ("size=\"8,11\";\n");
113print DOT ("node [width=0.375,height=0.25];\n");
[ebd57f8]114
115my %cached_names;
116sub get_name($) {
117  my $node = $_[0];
118  my $pc = $node->{'pc'};
119
120  if (exists $cached_names{$pc}) {
121    return $cached_names{$pc};
122  }
123
124  my $ret = '';
125
126  # Prefer the relative offset (that is what we want for shared libraries), but
127  # if is not available, use the full offset (which is what we want for the
128  # base executable).
129  my $exe_off = $node->{'relpc'};
130  if (!$exe_off) {
131    $exe_off = $pc;
132  }
133
134  my $file_name = $node->{'file_name'};
135  my ($func, $loc) = `addr2line -e $file_name -f 0x$exe_off`;
136  chomp($func);
137  chomp($loc);
138
139  if ($func !~ /^\?/) {
[6520ef5]140    # In general, this function name might look something like:
141    #   00000329.plt_call.wcsnrtombs@@GLIBC_2.3+0
142    $func =~ s/@.*//;
[ebd57f8]143    $func =~ s/.*\.//;
144    $func = `c++filt '$func'`;
145    chomp($func);
146
147    $ret .= $func . '\n' . $loc . '\n';
[4bd7a88]148  } elsif ($node->{'proc_name'} ne '?') {
149    my $proc_name = $node->{'proc_name'};
150    $proc_name = `c++filt '$proc_name'`;
151    chomp($proc_name);
152
153    $ret .= $proc_name . '\n';
[ebd57f8]154  }
155
[6bfe6a1]156  $ret .= $node->{'file_name'};
157  if ($print_raw_proc_name and $node->{'proc_name'} ne '?') {
158    $ret .= '\n' . $node->{'proc_name'} . '+0x' . $node->{'off'};
[ebd57f8]159  }
160
161  $cached_names{$pc} = $ret;
162  return $ret;
163}
164
[4bd7a88]165my $skip_frac = 0.01;
166my %skipped;
167
[c4f89cf]168foreach my $pc (keys %all_nodes) {
169  my $node = $all_nodes{$pc};
[ebd57f8]170  my $name = get_name($node);
171
172  my $local_size = $node->{'size'};
[4bd7a88]173  if ($local_size * 1.0 / $total_size < $skip_frac) {
[c4f89cf]174    $skipped{$pc} = 1;
[4bd7a88]175    next;
176  }
[ebd57f8]177
178  my $fs = 8.0;
179  if ($local_size > 0) {
[4bd7a88]180    $fs = 50.0 * (abs($local_size * 1.0 / $total_size))**0.125;
[ebd57f8]181  }
182
[81dea5b]183  printf DOT ("N%s [label=\"%s\\n%s\", shape=box, fontsize=%.1f%s];\n",
[c4f89cf]184    $pc, $name, format_bytes($local_size), $fs);
[ebd57f8]185}
186
[c4f89cf]187foreach my $pc (keys %all_nodes) {
188  my $node = $all_nodes{$pc};
189
[ebd57f8]190  my $local_size = $node->{'size'};
[c4f89cf]191  if ($skipped{$pc}) {
[4bd7a88]192    next;
193  }
[ebd57f8]194
195  foreach my $cpc (keys %{$node->{'child_sizes'}}) {
[4bd7a88]196    if ($skipped{$cpc}) {
197      next;
198    }
199
[ebd57f8]200    my $child_size = $node->{'child_sizes'}->{$cpc};
201    my $frac = $child_size * 1.0 / $local_size;
202
[4bd7a88]203    my $weight = 100.0 * sqrt($frac);
[6520ef5]204    my $style = sprintf("setlinewidth(%f)", 8.0 * sqrt($frac));
[ebd57f8]205
[6520ef5]206    my $fs = 40.0 * $frac**0.125;
207
[81dea5b]208    printf DOT ("N%s -> N%s [label=\"%s\", weight=%d, style=\"%s\", fontsize=%.1f];\n",
[6520ef5]209      $pc, $cpc, format_bytes($child_size), $weight, $style, $fs);
[ebd57f8]210  }
211}
212
[81dea5b]213print DOT ("}\n");
214
215close(DOT);
216
217system("dot -Tps2 < '$dot_fn' > '$ps_fn'");
218system("ps2pdf '$ps_fn' '$pdf_fn'");
219
220exit 0;
[ebd57f8]221
Note: See TracBrowser for help on using the repository browser.