]>
pere.pagekite.me Git - homepage.git/blob - linux/gprof-callgraph/gprof-callgraph.pl
3 # Author: Petter Reinholdtsen <pere@td.org.uit.no>
6 # Parse output from gprof, and generate graph output for daVinci or xvcg.
8 # This is alpha quality code, and indended as proof of consept. The
9 # idea is based on the C++ implementation cg by ? (look up name).
12 # - Encode # calls and time used into graph (color, edge width, dashed, etc)
13 # - Add daVinci output mode
14 # - Detect non-GNU gprof, and correct flags for this case
16 # - Use getopt to choose output mode and if unused functions should be
20 # 2000-12-30 Petter Reinholdtsen <pere@td.org.uit.no>
21 # Changed to skip unused functions.
22 # 2001-11-13 Kaupo Palo <kaupo.palo@ebi.ee>
23 # Added dot format and spent time
27 my $url = "http://www.student.uit.no/~pere/linux/";
32 my $gprofopt = "-c -z";
34 # Which program to profile
37 # Only show called functions (flag) [dont work yet]
40 # Map from function name, to list of funcion calling this function
43 # Map from function name, to list of function called
46 # Map from function id to function name
49 # Map from function id to function name
56 parse_gnu_gprof
($program);
66 # Flag set when parsing gprof call graph output
69 open(GPROF
, "gprof $gprofopt $program |") || die "Unable to run gprof";
73 $ingraph = 1 if (/Call graph/);
74 $ingraph = 0 if (/This table describes the call tree/);
77 if (/^-+$/) { # End of current entry
78 debugmsg
(1, "Registering \"$current\"\n") if defined $current;
81 $pre{$current} = [@parents] if (@parents);
82 $post{$current}= [@children] if (@children);
84 debugmsg
(1, "No current func!\n");
94 $current = parse_current_func
($_);
97 # Match the parent and child functions. Examples:
98 # 2 get_ll_tempfile <cycle 3> [36]
99 # 0.00 0.00 0/5 __getproperty [891]
100 if (/^\s+[\d.]+\s/) {
101 parse_parent_or_child
($current, $_);
108 return $func{$a} cmp $func{$b}
111 # Match current function, ie lines like these (notice the GNU
112 # gprof bug in the last line):
113 # Skip unused functions.
115 # [1] 0.0 0.00 0.00 0 <cycle 1 as a whole> [1]
116 # [4] 0.0 0.00 0.00 9 incache [4]
117 # [10000 0.0 0.00 0.00 9 incache [10000
118 # [10] 0.0 0.00 0.00 7 fndentry [10]
119 # [11] 0.0 0.00 0.00 7 get_property <cycle 3> [11]
120 sub parse_current_func
{
122 if(/^\[(\d+)\]?\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d\+]*)\s+(.+)\s+\[(\d+)\]$/) {
123 # Name the extracted variables
124 my ($id, $ptime, $selftime, $chtime, $called, $name, $index) =
125 ($1, $2, $3, $4, $5, $6, $7);
127 # I'm not quite sure if this would ever happen
128 print "Bad index or id: $id != $index\n " if ($id != $index);
130 # If this function wasn't called, skip to the next one
131 return undef if ($used_only && !$called);
133 if ($name =~ m/^\<.+\>$/) {
134 # cycle, not a function
135 debugmsg
(1, "Cycle \"$name\" index $index\n");
138 save_func_spent
($ptime, $id);
139 return save_func_name
($name, $id);
141 debugmsg
(0, "Bad formatting of line \"$line\"\n");
147 # Match parents and child of the current function.
148 # Skip unused functions.
150 sub parse_parent_or_child
152 my ($current, $line) = @_;
153 my ($selftime, $chtime, $countinfo, $name, $index) = $line =~
154 m
%^\s
+([\d
.]*)\s
+([\d
.]*)\s
+([\d
/\
+]+)\s
+(.+)\s\
[(\d
+)\
]$%;
155 if (!defined $name) {
156 debugmsg
(0, "Unable to parse \"$_\"\n");
160 # Check if this function was called at all
162 my ($this, $total) = split(/\//, $countinfo);
163 return unless (defined $total && 0 < $this);
166 debugmsg
(1, "Called: $countinfo\n");
168 $name = save_func_name
($name, $index);
171 push(@children, $name);
173 push(@parents, $name);
178 # Store decoded function name in hash with the function index as key
181 my ($name, $id) = @_;
184 # Remove cycle information
185 $current =~ s/\s*\<.+\>//;
187 $func{$id} = $current;
188 debugmsg
(1, "Name: \"$name\" -> \"$current\"\n");
193 # Store time spent in function with function index as key
195 sub save_func_spent
{
196 my ($spent, $id) = @_;
198 $spent{$id} = $spent;
199 # debugmsg(1, "Name: \"$name\" -> \"$current\"\n");
204 # Output current graph database
208 for $current (sort sort_func_name
keys %func) {
209 my $name = $func{$current};
210 print "Func: \"" . $name . "\" [$current]\n";
213 if (defined $pre{$current}) {
215 for $f (sort sort_func_name
@{$pre{$current}}) {
216 print " " . $func{$f} . " [$f] -> \"$name\"\n";
221 if (defined $post{$current}) {
223 for $f (sort sort_func_name
@{$post{$current}}) {
224 print " \"$name\" -> \"" . $func{$f} . "\" [$f]\n";
232 my ($src, $dst, $thickness, $comment) = @_;
233 my $buffer = " edge: {".
234 " sourcename: \"$src\"".
235 " targetname: \"$dst\"" .
236 " thickness: $thickness }";
237 $buffer .= " /* $comment */\n" if ($comment);
242 # Output current graph database in vcg format
251 * VCG formatted call graph for program "$program" based on gprof output.
253 * Check <URL:$url> for more information.
257 title: "Profiling $program"
258 orientation: left_to_right
262 # First print all the nodes
263 for $current (sort sort_func_name
keys %func) {
264 my $name = $func{$current};
265 print " node: { title: \"$current\" label: \"$name\" borderwidth: 0}\n"
270 # Then print all the edges
271 for $current (sort sort_func_name
keys %func) {
272 my $name = $func{$current};
274 if (defined $pre{$current}) {
276 my $buffer = " /* \"$name\" callers */\n";
278 for $f (sort sort_func_name
@{$pre{$current}}) {
279 if (!exists $printed{"$f:$current"}) {
280 my $prename = $func{$f};
282 $buffer .= vcg_edge
($f, $current, $thickness, $prename);
283 $printed{"$f:$current"} = 1;
288 print $buffer if (0 < $content);
291 if (defined $post{$current}) {
293 my $buffer = " /* \"$name\" called */\n";
295 for $f (sort sort_func_name
@{$post{$current}}) {
296 if (!exists $printed{"$current:$f"}) {
297 my $postname = $func{$f};
299 $buffer .= vcg_edge
($current, $f, $thickness, $postname);
300 $printed{"$current:$f"} = 1;
305 print $buffer if (0 < $content);
313 # Output current graph database in daVinci format
315 sub dump_graph_daVinci
{
322 # First print all the nodes
323 for $current (sort sort_func_name
keys %func) {
324 my $name = $func{$current};
325 print " l(\"$current\",n(\"$name\",[],[\n";
327 if (defined $pre{$current}) {
329 for $f (sort sort_func_name
@{$pre{$current}}) {
330 print "l(\"$f\"->\"$current\",e(\"B\",[],";
334 print " edge: { sourcename: \"$f\" targetname: \"$current\"" .
340 if (defined $post{$current}) {
342 for $f (sort sort_func_name
@{$post{$current}}) {
343 print " edge: { sourcename: \"$current\" targetname: \"$f\"" .
353 # Output current graph database in dot format
362 * DOT formatted call graph for program "$program" based on gprof output.
369 orientation=landscape;
376 # First print all the nodes
377 for $current (sort sort_func_name
keys %func) {
378 my $name = "\"" . $func{$current};
380 if( defined($spent{$current}) ){
381 $val = $spent{$current};
383 $name .= "\\n" . $val . "\",style=filled";
393 print " n$current\[label=$name]\n"
398 # Then print all the edges
399 for $current (sort sort_func_name
keys %func) {
400 my $name = $func{$current};
402 if (defined $pre{$current}) {
406 for $f (sort sort_func_name
@{$pre{$current}}) {
407 if (!exists $printed{"$f:$current"}) {
408 $buffer .= " n$f -> n$current;\n";
409 $printed{"$f:$current"} = 1;
413 print $buffer if (0 < $content);
416 if (defined $post{$current}) {
420 for $f (sort sort_func_name
@{$post{$current}}) {
421 if (!exists $printed{"$current:$f"}) {
422 $buffer .= " n$current -> n$f;\n";
423 $printed{"$current:$f"} = 1;
427 print $buffer if (0 < $content);
432 node0[label="Profiling $program"];
439 my ($level, $msg) = @_;
440 print STDERR
$msg if ($level <= $debug);