blob: 088f79b26717bb587b59a85104a0f371bda2fa8e [file] [log] [blame]
njn4f9c9342002-04-29 16:03:24 +00001#! /usr/bin/perl -w
2##--------------------------------------------------------------------##
3##--- The cache simulation framework: instrumentation, recording ---##
4##--- and results printing. ---##
5##--- vg_annotate ---##
6##--------------------------------------------------------------------##
7
8# This file is part of Valgrind, an x86 protected-mode emulator
9# designed for debugging and profiling binaries on x86-Unixes.
10#
11# Copyright (C) 2000-2002 Julian Seward
12# jseward@acm.org
13# Julian_Seward@muraroa.demon.co.uk
14#
15# This program is free software; you can redistribute it and/or
16# modify it under the terms of the GNU General Public License as
17# published by the Free Software Foundation; either version 2 of the
18# License, or (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful, but
21# WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23# General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program; if not, write to the Free Software
27# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28# 02111-1307, USA.
29#
30# The GNU General Public License is contained in the file LICENSE.
31
32#----------------------------------------------------------------------------
33# Annotator for valgrind --cachesim=yes.
34#
35# Input file has the following format:
36# <file> ::= <desc_line>* <cmd_line> <events_line>
37# <data_line>+ <summary_line>
38# <desc_line> ::= "desc:" <ws>? <non_nl_string>
39# <cmd_line> ::= "cmd:" <ws>? <cmd>
40# <events_line> ::= "events:" <ws>? (<event> <ws>)+
41# <data_line> ::= <file_line> | <fn_line> | <count_line>
42# <file_line> ::= ("fl=" | "fi=" | "fe=" ) <filename>
43# <fn_line> ::= "fn="<filename>
44# <count_line> ::= <line_num> <ws>? (<count> <ws>)+
45# <summary_line> ::= "summary:" <ws>? (<count> <ws>)+
46# <count> ::= <num> | "."
47#
48# where
49#
50# <non_nl_string> is any string not containing a newline
51# <cmd> is a command line invocation
52# <filename> and <fn_name> can be anything
53# <num> and <line_num> are decimal numbers
54# <ws> is whitespace.
55# <nl> is a newline
56#
57# The contents of the "desc:" lines is printed out at the top of the summary.
58# This is a generic way of providing simulation specific information, eg. for
59# giving the cache configuration for cache simulation.
60#
61# Counts can be ".", to represent "N/A".
62#
63# The number of counts in each <line> and the <summary_line> should not exceed
64# the number of events in the <event_line>. If the number in each <line> is
65# less, we use "." for the the missing counts (the last however-many).
66#
67# A <file_line> changes the current file name. A <fn_line> changes the current
68# function name. A <count_line> contains counts that pertain to the current
69# filename/fn_name. A "fn=" <file_line> and a <fn_line> must appear before any
70# <count_line>s to give the context of the first <count_line>s.
71#
72# Each <file_line> should be immediately followed by a <fn_line>. "fi="
73# <file_lines> are used to switch filenames for inlined functions; "fe="
74# <file_lines> are similar, but are put at the end of a basic block in which
75# the file name hasn't been switched back to the original file name. (fi and
76# fe lines behave the same, they are only distinguished to help debugging.)
77#
78#----------------------------------------------------------------------------
79# Performance improvements record, using cachegrind.out for cacheprof, doing no
80# source annotation (irrelevant ones removed):
81# user time
82# 1. turned off warnings in add_hash_a_to_b() 3.81 --> 3.48s
83# [now add_array_a_to_b()]
84# 6. make line_to_CC() return a ref instead of a hash 3.01 --> 2.77s
85#
86#10. changed file format to avoid file/fn name repetition 2.40s
87# (not sure why higher; maybe due to new '.' entries?)
88#11. changed file format to drop unnecessary end-line "."s 2.36s
89# (shrunk file by about 37%)
90#12. switched from hash CCs to array CCs 1.61s
91#13. only adding b[i] to a[i] if b[i] defined (was doing it if
92# either a[i] or b[i] was defined, but if b[i] was undefined
93# it just added 0) 1.48s
94#14. Stopped converting "." entries to undef and then back 1.16s
95#15. Using foreach $i (x..y) instead of for ($i = 0...) in
96# add_array_a_to_b() 1.11s
97#
98# Auto-annotating primes:
99#16. Finding count lengths by int((length-1)/3), not by
100# commifying (halves the number of commify calls) 1.68s --> 1.47s
101
102use strict;
103
104#----------------------------------------------------------------------------
105# Overview: the running example in the comments is for:
106# - events = A,B,C,D
107# - --show=C,A,D
108# - --sort=D,C
109#----------------------------------------------------------------------------
110
111#----------------------------------------------------------------------------
112# Global variables, main data structures
113#----------------------------------------------------------------------------
114# CCs are arrays, the counts corresponding to @events, with 'undef'
115# representing '.'. This makes things fast (faster than using hashes for CCs)
116# but we have to use @sort_order and @show_order below to handle the --sort and
117# --show options, which is a bit tricky.
118#----------------------------------------------------------------------------
119
120# Total counts for summary (an array reference).
121my $summary_CC;
122
123# Totals for each function, for overall summary.
124# hash(filename:fn_name => CC array)
125my %fn_totals;
126
127# Individual CCs, organised by filename and line_num for easy annotation.
128# hash(filename => hash(line_num => CC array))
129my %all_ind_CCs;
130
131# Files chosen for annotation on the command line.
132# key = basename (trimmed of any directory), value = full filename
133my %user_ann_files;
134
135# Generic description string.
136my $desc = "";
137
138# Command line of profiled program.
139my $cmd;
140
141# Events in input file, eg. (A,B,C,D)
142my @events;
143
144# Events to show, from command line, eg. (C,A,D)
145my @show_events;
146
147# Map from @show_events indices to @events indices, eg. (2,0,3). Gives the
148# order in which we must traverse @events in order to show the @show_events,
149# eg. (@events[$show_order[1]], @events[$show_order[2]]...) = @show_events.
150# (Might help to think of it like a hash (0 => 2, 1 => 0, 2 => 3).)
151my @show_order;
152
153# Print out the function totals sorted by these events, eg. (D,C).
154my @sort_events;
155
156# Map from @sort_events indices to @events indices, eg. (3,2). Same idea as
157# for @show_order
158my @sort_order;
159
160# Threshold; whatever event is the primary sort, we print out functions
161# representing more than this proportion of 'event' events.
162my $threshold = 99;
163
164# If on, automatically annotates all files that are involved in getting over
165# the threshold count of the primary sort event.
166my $auto_annotate = 0;
167
168# Number of lines to show around each annotated line.
169my $context = 8;
170
171# Directories in which to look for annotation files.
172my @include_dirs = ("");
173
174# Input file name
175my $input_file = "cachegrind.out";
176
177# Version number
178my $version = "@VERSION@";
179
180# Usage message.
181my $usage = <<END
182usage: vg_annotate [options] [source-files]
183
184 options for the user, with defaults in [ ], are:
185 -h --help show this message
186 -v --version show version
187 --show=A,B,C only show figures for events A,B,C [all]
188 --sort=A,B,C sort columns by events A,B,C [event column order]
189 --threshold=<0--100> percentage of counts (of primary sort event) we
190 are interested in [$threshold%]
191 --auto=yes|no annotate all source files containing functions
192 that helped reach the event count threshold [no]
193 --context=N print N lines of context before and after
194 annotated lines [8]
195 -I --include=<dir> add <dir> to list of directories to search for
196 source files
197
198 Valgrind is Copyright (C) 2000-2002 Julian Seward
199 and licensed under the GNU General Public License, version 2.
200 Bug reports, feedback, admiration, abuse, etc, to: jseward\@acm.org.
201
202END
203;
204
205# Used in various places of output.
206my $fancy = '-' x 80 . "\n";
207
208#-----------------------------------------------------------------------------
209# Argument and option handling
210#-----------------------------------------------------------------------------
211sub process_cmd_line()
212{
213 for my $arg (@ARGV) {
214
215 # Option handling
216 if ($arg =~ /^-/) {
217
218 # --version
219 if ($arg =~ /^-v$|^--version$/) {
sewardjaa6fecc2002-04-29 17:27:07 +0000220 die("vg_annotate-$version\n");
njn4f9c9342002-04-29 16:03:24 +0000221
222 # --show=A,B,C
223 } elsif ($arg =~ /^--show=(.*)$/) {
224 @show_events = split(/,/, $1);
225
226 # --sort=A,B,C
227 } elsif ($arg =~ /^--sort=(.*)$/) {
228 @sort_events = split(/,/, $1);
229
230 # --threshold=X (tolerates a trailing '%')
231 } elsif ($arg =~ /^--threshold=([\d\.]+)%?$/) {
232 $threshold = $1;
233 if ($threshold < 0 || $threshold > 100) {
234 die($usage);
235 }
236
237 # --auto=yes|no
238 } elsif ($arg =~ /^--auto=(yes|no)$/) {
239 $auto_annotate = 1 if ($1 eq "yes");
240 $auto_annotate = 0 if ($1 eq "no");
241
242 # --context=N
243 } elsif ($arg =~ /^--context=([\d\.]+)$/) {
244 $context = $1;
245 if ($context < 0) {
246 die($usage);
247 }
248
249 # --include=A,B,C
250 } elsif ($arg =~ /^(-I|--include)=(.*)$/) {
251 my $inc = $2;
252 $inc =~ s|/$||; # trim trailing '/'
253 push(@include_dirs, "$inc/");
254
255 } else { # -h and --help fall under this case
256 die($usage);
257 }
258
259 # Argument handling -- annotation file checking and selection.
260 # Stick filenames into a hash for quick 'n easy lookup throughout
261 } else {
262 my $readable = 0;
263 foreach my $include_dir (@include_dirs) {
264 if (-r $include_dir . $arg) {
265 $readable = 1;
266 }
267 }
268 $readable or die("File $arg not found in any of: @include_dirs\n");
269 $user_ann_files{$arg} = 1;
270 }
271 }
272}
273
274#-----------------------------------------------------------------------------
275# Reading of input file
276#-----------------------------------------------------------------------------
277sub max ($$)
278{
279 my ($x, $y) = @_;
280 return ($x > $y ? $x : $y);
281}
282
283# Add the two arrays; any '.' entries are ignored. Two tricky things:
284# 1. If $a2->[$i] is undefined, it defaults to 0 which is what we want; we turn
285# off warnings to allow this. This makes things about 10% faster than
286# checking for definedness ourselves.
287# 2. We don't add a ".", even though it's value is 0, because we don't want to
288# make an $a2->[$i] that is undef become 0 unnecessarily.
289sub add_array_a_to_b ($$)
290{
291 my ($a1, $a2) = @_;
292
293 my $n = max(scalar @$a1, scalar @$a2);
294 $^W = 0;
295 foreach my $i (0 .. $n-1) {
296 $a2->[$i] += $a1->[$i] if ("." ne $a1->[$i]);
297 }
298 $^W = 1;
299}
300
301# Add each event count to the CC array. '.' counts become undef, as do
302# missing entries (implicitly).
303sub line_to_CC ($)
304{
305 my @CC = (split /\s+/, $_[0]);
306 (@CC <= @events) or die("Line $.: too many event counts\n");
307 return \@CC;
308}
309
310sub read_input_file()
311{
312 open(INPUTFILE, "< $input_file") || die "File $input_file not opened\n";
313
314 # Read "desc:" lines.
315 my $line;
316 # This gives a "uninitialized value in substitution (s///)" warning; hmm...
317 #while ($line = <INPUTFILE> && $line =~ s/desc:\s+//) {
318 # $desc .= "$line\n";
319 #}
320 while (1) {
321 $line = <INPUTFILE>;
322 if ($line =~ s/desc:\s+//) {
323 $desc .= $line;
324 } else {
325 last;
326 }
327 }
328
329 # Read "cmd:" line (Nb: will already be in $line from "desc:" loop above).
330 ($line =~ s/cmd:\s+//) or die("Line $.: missing command line\n");
331 $cmd = $line;
332 chomp($cmd); # Remove newline
333
334 # Read "events:" line. We make a temporary hash in which the Nth event's
335 # value is N, which is useful for handling --show/--sort options below.
336 $line = <INPUTFILE>;
337 ($line =~ s/events:\s+//) or die("Line $.: missing events line\n");
338 @events = split(/\s+/, $line);
339 my %events;
340 my $n = 0;
341 foreach my $event (@events) {
342 $events{$event} = $n;
343 $n++
344 }
345
346 # If no --show arg give, default to showing all events in the file.
347 # If --show option is used, check all specified events appeared in the
348 # "events:" line. Then initialise @show_order.
349 if (@show_events) {
350 foreach my $show_event (@show_events) {
351 (defined $events{$show_event}) or
352 die("--show event `$show_event' did not appear in input\n");
353 }
354 } else {
355 @show_events = @events;
356 }
357 foreach my $show_event (@show_events) {
358 push(@show_order, $events{$show_event});
359 }
360
361 # Do as for --show, but if no --sort arg given, default to sorting by
362 # column order (ie. first column event is primary sort key, 2nd column is
363 # 2ndary key, etc).
364 if (@sort_events) {
365 foreach my $sort_event (@sort_events) {
366 (defined $events{$sort_event}) or
367 die("--sort event `$sort_event' did not appear in input\n");
368 }
369 } else {
370 @sort_events = @events;
371 }
372 foreach my $sort_event (@sort_events) {
373 push(@sort_order, $events{$sort_event});
374 }
375
376 my $curr_file;
377 my $curr_fn;
378 my $curr_name;
379
380 my $curr_fn_CC = [];
381 my $curr_file_ind_CCs = {}; # hash(line_num => CC)
382
383 # Read body of input file.
384 while (<INPUTFILE>) {
385 s/#.*$//; # remove comments
386 if (s/^(\d+)\s+//) {
387 my $line_num = $1;
388 my $CC = line_to_CC($_);
389 add_array_a_to_b($CC, $curr_fn_CC);
390
391 # If curr_file is selected, add CC to curr_file list. We look for
392 # full filename matches; or, if auto-annotating, we have to
393 # remember everything -- we won't know until the end what's needed.
394 if ($auto_annotate || defined $user_ann_files{$curr_file}) {
395 my $tmp = $curr_file_ind_CCs->{$line_num};
396 $tmp = [] unless defined $tmp;
397 add_array_a_to_b($CC, $tmp);
398 $curr_file_ind_CCs->{$line_num} = $tmp;
399 }
400
401 } elsif (s/^fn=(.*)$//) {
402 # Commit result from previous function
403 $fn_totals{$curr_name} = $curr_fn_CC if (defined $curr_name);
404
405 # Setup new one
406 $curr_fn = $1;
407 $curr_name = "$curr_file:$curr_fn";
408 $curr_fn_CC = $fn_totals{$curr_name};
409 $curr_fn_CC = [] unless (defined $curr_fn_CC);
410
411 } elsif (s/^fl=(.*)$//) {
412 $all_ind_CCs{$curr_file} = $curr_file_ind_CCs
413 if (defined $curr_file);
414
415 $curr_file = $1;
416 $curr_file_ind_CCs = $all_ind_CCs{$curr_file};
417 $curr_file_ind_CCs = {} unless (defined $curr_file_ind_CCs);
418
419 } elsif (s/^(fi|fe)=(.*)$//) {
420 (defined $curr_name) or die("Line $.: Unexpected fi/fe line\n");
421 $fn_totals{$curr_name} = $curr_fn_CC;
422 $all_ind_CCs{$curr_file} = $curr_file_ind_CCs;
423
424 $curr_file = $2;
425 $curr_name = "$curr_file:$curr_fn";
426 $curr_file_ind_CCs = $all_ind_CCs{$curr_file};
427 $curr_file_ind_CCs = {} unless (defined $curr_file_ind_CCs);
428 $curr_fn_CC = $fn_totals{$curr_name};
429 $curr_fn_CC = [] unless (defined $curr_fn_CC);
430
431 } elsif (s/^\s*$//) {
432 # blank, do nothing
433
434 } elsif (s/^summary:\s+//) {
435 # Finish up handling final filename/fn_name counts
436 $fn_totals{"$curr_file:$curr_fn"} = $curr_fn_CC
437 if (defined $curr_file && defined $curr_fn);
438 $all_ind_CCs{$curr_file} =
439 $curr_file_ind_CCs if (defined $curr_file);
440
441 $summary_CC = line_to_CC($_);
442 (scalar(@$summary_CC) == @events)
443 or die("Line $.: summary event and total event mismatch\n");
444
445 } else {
446 warn("WARNING: line $. malformed, ignoring\n");
447 }
448 }
449
450 # Check if summary line was present
451 if (not defined $summary_CC) {
452 warn("WARNING: missing final summary line, no summary will be printed\n");
453 }
454
455 close(INPUTFILE);
456}
457
458#-----------------------------------------------------------------------------
459# Print options used
460#-----------------------------------------------------------------------------
461sub print_options ()
462{
463 print($fancy);
464 print($desc);
465 print("Command: $cmd\n");
466 print("Events recorded: @events\n");
467 print("Events shown: @show_events\n");
468 print("Event sort order: @sort_events\n");
469 print("Threshold: $threshold%\n");
470
471 my @include_dirs2 = @include_dirs; # copy @include_dirs
472 shift(@include_dirs2); # remove "" entry, which is always the first
473 unshift(@include_dirs2, "") if (0 == @include_dirs2);
474 my $include_dir = shift(@include_dirs2);
475 print("Include dirs: $include_dir\n");
476 foreach my $include_dir (@include_dirs2) {
477 print(" $include_dir\n");
478 }
479
480 my @user_ann_files = keys %user_ann_files;
481 unshift(@user_ann_files, "") if (0 == @user_ann_files);
482 my $user_ann_file = shift(@user_ann_files);
483 print("User annotated: $user_ann_file\n");
484 foreach $user_ann_file (@user_ann_files) {
485 print(" $user_ann_file\n");
486 }
487
488 my $is_on = ($auto_annotate ? "on" : "off");
489 print("Auto-annotation: $is_on\n");
490 print("\n");
491}
492
493#-----------------------------------------------------------------------------
494# Print summary and sorted function totals
495#-----------------------------------------------------------------------------
496sub mycmp ($$)
497{
498 my ($c, $d) = @_;
499
500 # Iterate through sort events (eg. 3,2); return result if two are different
501 foreach my $i (@sort_order) {
502 my ($x, $y);
503 $x = $c->[$i];
504 $y = $d->[$i];
505 $x = -1 unless defined $x;
506 $y = -1 unless defined $y;
507
508 my $cmp = $y <=> $x; # reverse sort
509 if (0 != $cmp) {
510 return $cmp;
511 }
512 }
513 # Exhausted events, equal
514 return 0;
515}
516
517sub commify ($) {
518 my ($val) = @_;
519 1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/);
520 return $val;
521}
522
523# Because the counts can get very big, and we don't want to waste screen space
524# and make lines too long, we compute exactly how wide each column needs to be
525# by finding the widest entry for each one.
526sub compute_CC_col_widths (@)
527{
528 my @CCs = @_;
529 my $CC_col_widths = [];
530
531 # Initialise with minimum widths (from event names)
532 foreach my $event (@events) {
533 push(@$CC_col_widths, length($event));
534 }
535
536 # Find maximum width count for each column. @CC_col_width positions
537 # correspond to @CC positions.
538 foreach my $CC (@CCs) {
539 foreach my $i (0 .. scalar(@$CC)-1) {
540 if (defined $CC->[$i]) {
541 # Find length, accounting for commas that will be added
542 my $length = length $CC->[$i];
543 my $clength = $length + int(($length - 1) / 3);
544 $CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength);
545 }
546 }
547 }
548 return $CC_col_widths;
549}
550
551# Print the CC with each column's size dictated by $CC_col_widths.
552sub print_CC ($$)
553{
554 my ($CC, $CC_col_widths) = @_;
555
556 foreach my $i (@show_order) {
557 my $count = (defined $CC->[$i] ? commify($CC->[$i]) : ".");
558 my $space = ' ' x ($CC_col_widths->[$i] - length($count));
559 print("$space$count ");
560 }
561}
562
563sub print_events ($)
564{
565 my ($CC_col_widths) = @_;
566
567 foreach my $i (@show_order) {
568 my $event = $events[$i];
569 my $event_width = length($event);
570 my $col_width = $CC_col_widths->[$i];
571 my $space = ' ' x ($col_width - $event_width);
njn602392b2002-04-30 11:34:54 +0000572 print("$space$event ");
njn4f9c9342002-04-29 16:03:24 +0000573 }
574}
575
576# Prints summary and function totals (with separate column widths, so that
577# function names aren't pushed over unnecessarily by huge summary figures).
578# Also returns a hash containing all the files that are involved in getting the
579# events count above the threshold (ie. all the interesting ones).
580sub print_summary_and_fn_totals ()
581{
582 my @fn_fullnames = keys %fn_totals;
583
584 # Work out the size of each column for printing (summary and functions
585 # separately).
586 my $summary_CC_col_widths = compute_CC_col_widths($summary_CC);
587 my $fn_CC_col_widths = compute_CC_col_widths(values %fn_totals);
588
589 # Header and counts for summary
590 print($fancy);
591 print_events($summary_CC_col_widths);
592 print("\n");
593 print($fancy);
594 print_CC($summary_CC, $summary_CC_col_widths);
595 print(" PROGRAM TOTALS\n");
596 print("\n");
597
598 # Header for functions
599 print($fancy);
600 print_events($fn_CC_col_widths);
601 print(" file:function\n");
602 print($fancy);
603
604 # Sort function names into order dictated by --sort option.
605 @fn_fullnames = sort {
606 mycmp($fn_totals{$a}, $fn_totals{$b})
607 } @fn_fullnames;
608
609 # The thresholded event is the one that is the primary sort event.
610 my $threshold_files = {};
611 my $threshold_event_index = $sort_order[0];
612 my $threshold_total = $summary_CC->[$threshold_event_index];
613 my $curr_total = 0;
614
615 # Print functions, stopping when the threshold has been reached.
616 foreach my $fn_name (@fn_fullnames) {
617
618 # Stop when we've reached the threshold
619 last if ($curr_total * 100 / $threshold_total >= $threshold);
620
621 # Print function results
622 my $fn_CC = $fn_totals{$fn_name};
623 print_CC($fn_CC, $fn_CC_col_widths);
624 print(" $fn_name\n");
625
626 # Update the threshold counting
627 my $filename = $fn_name;
daywalkerd722c202002-05-01 21:52:05 +0000628 $filename =~ s/:.+$//; # remove function name
njn4f9c9342002-04-29 16:03:24 +0000629 $threshold_files->{$filename} = 1;
630 $curr_total += $fn_CC->[$threshold_event_index]
631 if (defined $fn_CC->[$threshold_event_index]);
632 }
633 print("\n");
634
635 return $threshold_files;
636}
637
638#-----------------------------------------------------------------------------
639# Annotate selected files
640#-----------------------------------------------------------------------------
641
642# Issue a warning that the source file is more recent than the input file.
643sub warning_on_src_more_recent_than_inputfile ($)
644{
645 my $src_file = $_[0];
646
647 my $warning = <<END
648@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
649@@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@
650@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
651@ Source file '$src_file' is more recent than input file '$input_file'.
652@ Annotations may not be correct.
653@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
654
655END
656;
657 print($warning);
658}
659
660# If there is information about lines not in the file, issue a warning
661# explaining possible causes.
662sub warning_on_nonexistent_lines ($$$)
663{
664 my ($src_more_recent_than_inputfile, $src_file, $excess_line_nums) = @_;
665 my $cause_and_solution;
666
667 if ($src_more_recent_than_inputfile) {
668 $cause_and_solution = <<END
669@@ cause: '$src_file' has changed since information was gathered.
670@@ If so, a warning will have already been issued about this.
671@@ solution: Recompile program and rerun under "valgrind --cachesim=yes" to
672@@ gather new information.
673END
674 # We suppress warnings about .h files
675 } elsif ($src_file =~ /\.h$/) {
676 $cause_and_solution = <<END
677@@ cause: bug in the Valgrind's debug info reader that screws up with .h
678@@ files sometimes
679@@ solution: none, sorry
680END
681 } else {
682 $cause_and_solution = <<END
683@@ cause: not sure, sorry
684END
685 }
686
687 my $warning = <<END
688@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
689@@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@
690@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
691@@
692@@ Information recorded about lines past the end of '$src_file'.
693@@
694@@ Probable cause and solution:
695$cause_and_solution@@
696@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
697END
698;
699 print($warning);
700}
701
702sub annotate_ann_files($)
703{
704 my ($threshold_files) = @_;
705
706 my %all_ann_files;
707 my @unfound_auto_annotate_files;
708 my $printed_totals_CC = [];
709
710 # If auto-annotating, add interesting files (but not "???")
711 if ($auto_annotate) {
712 delete $threshold_files->{"???"};
713 %all_ann_files = (%user_ann_files, %$threshold_files)
714 } else {
715 %all_ann_files = %user_ann_files;
716 }
717
718 # Track if we did any annotations.
719 my $did_annotations = 0;
720
721 LOOP:
722 foreach my $src_file (keys %all_ann_files) {
723
724 my $opened_file = "";
725 my $full_file_name = "";
726 foreach my $include_dir (@include_dirs) {
727 my $try_name = $include_dir . $src_file;
728 if (open(INPUTFILE, "< $try_name")) {
729 $opened_file = $try_name;
730 $full_file_name = ($include_dir eq ""
731 ? $src_file
732 : "$include_dir + $src_file");
733 last;
734 }
735 }
736
737 if (not $opened_file) {
738 # Failed to open the file. If chosen on the command line, die.
739 # If arose from auto-annotation, print a little message.
740 if (defined $user_ann_files{$src_file}) {
741 die("File $src_file not opened in any of: @include_dirs\n");
742
743 } else {
744 push(@unfound_auto_annotate_files, $src_file);
745 }
746
747 } else {
748 # File header (distinguish between user- and auto-selected files).
749 print("$fancy");
750 my $ann_type =
751 (defined $user_ann_files{$src_file} ? "User" : "Auto");
752 print("-- $ann_type-annotated source: $full_file_name\n");
753 print("$fancy");
754
755 # Get file's CCs
756 my $src_file_CCs = $all_ind_CCs{$src_file};
757 if (!defined $src_file_CCs) {
758 print(" No information has been collected for $src_file\n\n");
759 next LOOP;
760 }
761
762 $did_annotations = 1;
763
764 # Numeric, not lexicographic sort!
765 my @line_nums = sort {$a <=> $b} keys %$src_file_CCs;
766
767 # If $src_file more recent than cachegrind.out, issue warning
768 my $src_more_recent_than_inputfile = 0;
769 if ((stat $opened_file)[9] > (stat $input_file)[9]) {
770 $src_more_recent_than_inputfile = 1;
771 warning_on_src_more_recent_than_inputfile($src_file);
772 }
773
774 # Work out the size of each column for printing
775 my $CC_col_widths = compute_CC_col_widths(values %$src_file_CCs);
776
777 # Events header
778 print_events($CC_col_widths);
779 print("\n\n");
780
781 # Shift out 0 if it's in the line numbers (from unknown entries,
782 # likely due to bugs in Valgrind's stabs debug info reader)
783 shift(@line_nums) if (0 == $line_nums[0]);
784
785 # Finds interesting line ranges -- all lines with a CC, and all
786 # lines within $context lines of a line with a CC.
787 my $n = @line_nums;
788 my @pairs;
789 for (my $i = 0; $i < $n; $i++) {
790 push(@pairs, $line_nums[$i] - $context); # lower marker
791 while ($i < $n-1 &&
792 $line_nums[$i] + 2*$context >= $line_nums[$i+1]) {
793 $i++;
794 }
795 push(@pairs, $line_nums[$i] + $context); # upper marker
796 }
797
798 # Annotate chosen lines, tracking total counts of lines printed
799 $pairs[0] = 1 if ($pairs[0] < 1);
800 while (@pairs) {
801 my $low = shift @pairs;
802 my $high = shift @pairs;
803 while ($. < $low-1) {
804 my $tmp = <INPUTFILE>;
805 last unless (defined $tmp); # hack to detect EOF
806 }
807 my $src_line;
808 # Print line number, unless start of file
809 print("-- line $low " . '-' x 40 . "\n") if ($low != 1);
810 while (($. < $high) && ($src_line = <INPUTFILE>)) {
811 if (defined $line_nums[0] && $. == $line_nums[0]) {
812 print_CC($src_file_CCs->{$.}, $CC_col_widths);
813 add_array_a_to_b($src_file_CCs->{$.},
814 $printed_totals_CC);
815 shift(@line_nums);
816
817 } else {
818 print_CC( [], $CC_col_widths);
819 }
820
821 print(" $src_line");
822 }
823 # Print line number, unless EOF
824 if ($src_line) {
825 print("-- line $high " . '-' x 40 . "\n");
826 } else {
827 last;
828 }
829 }
830
831 # If there was info on lines past the end of the file...
832 if (@line_nums) {
833 foreach my $line_num (@line_nums) {
834 print_CC($src_file_CCs->{$line_num}, $CC_col_widths);
835 print(" <bogus line $line_num>\n");
836 }
837 print("\n");
838 warning_on_nonexistent_lines($src_more_recent_than_inputfile,
839 $src_file, \@line_nums);
840 }
841 print("\n");
842
843 # Print summary of counts attributed to file but not to any
844 # particular line (due to incomplete debug info).
845 if ($src_file_CCs->{0}) {
846 print_CC($src_file_CCs->{0}, $CC_col_widths);
847 print(" <counts for unidentified lines in $src_file>\n\n");
848 }
849
850 close(INPUTFILE);
851 }
852 }
853
854 # Print list of unfound auto-annotate selected files.
855 if (@unfound_auto_annotate_files) {
856 print("$fancy");
857 print("The following files chosen for auto-annotation could not be found:\n");
858 print($fancy);
859 foreach my $f (@unfound_auto_annotate_files) {
860 print(" $f\n");
861 }
862 print("\n");
863 }
864
865 # If we did any annotating, print what proportion of events were covered by
866 # annotated lines above.
867 if ($did_annotations) {
868 my $percent_printed_CC;
869 foreach (my $i = 0; $i < @$summary_CC; $i++) {
870 $percent_printed_CC->[$i] =
871 sprintf("%.0f",
872 $printed_totals_CC->[$i] / $summary_CC->[$i] * 100);
873 }
874 my $pp_CC_col_widths = compute_CC_col_widths($percent_printed_CC);
875 print($fancy);
876 print_events($pp_CC_col_widths);
877 print("\n");
878 print($fancy);
879 print_CC($percent_printed_CC, $pp_CC_col_widths);
880 print(" percentage of events annotated\n\n");
881 }
882}
883
884#----------------------------------------------------------------------------
885# "main()"
886#----------------------------------------------------------------------------
887process_cmd_line();
888read_input_file();
889print_options();
890my $threshold_files = print_summary_and_fn_totals();
891annotate_ann_files($threshold_files);
892