cg_annotate.in revision aa6feccbe4ff707eceb21a79eafda247e08e5f46
1#! /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$/) {
220                die("vg_annotate-$version\n");
221
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);
572        print("$event$space ");
573    }
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;
628        $filename =~ s/:[^:]+$//;    # remove function name
629        $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
893