Home | History | Annotate | Download | only in callgrind
      1 #! /usr/bin/perl -w
      2 ##--------------------------------------------------------------------##
      3 ##--- The cache simulation framework: instrumentation, recording   ---##
      4 ##--- and results printing.                                        ---##
      5 ##---                                           callgrind_annotate ---##
      6 ##--------------------------------------------------------------------##
      7 
      8 #  This file is part of Callgrind, a cache-simulator and call graph
      9 #  tracer built on Valgrind.
     10 #
     11 #  Copyright (C) 2003-2017 Josef Weidendorfer
     12 #     Josef.Weidendorfer (at] gmx.de
     13 #
     14 #  This file is based heavily on cg_annotate, part of Valgrind.
     15 #  Copyright (C) 2002-2017 Nicholas Nethercote
     16 #     njn (at] valgrind.org
     17 #
     18 #  This program is free software; you can redistribute it and/or
     19 #  modify it under the terms of the GNU General Public License as
     20 #  published by the Free Software Foundation; either version 2 of the
     21 #  License, or (at your option) any later version.
     22 #
     23 #  This program is distributed in the hope that it will be useful, but
     24 #  WITHOUT ANY WARRANTY; without even the implied warranty of
     25 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     26 #  General Public License for more details.
     27 #
     28 #  You should have received a copy of the GNU General Public License
     29 #  along with this program; if not, write to the Free Software
     30 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
     31 #  02111-1307, USA.
     32 #
     33 #  The GNU General Public License is contained in the file COPYING.
     34 
     35 #----------------------------------------------------------------------------
     36 # Annotator for cachegrind/callgrind. 
     37 #
     38 # File format is described in /docs/techdocs.html.
     39 #
     40 # Performance improvements record, using cachegrind.out for cacheprof, doing no
     41 # source annotation (irrelevant ones removed):
     42 #                                                               user time
     43 # 1. turned off warnings in add_hash_a_to_b()                   3.81 --> 3.48s
     44 #    [now add_array_a_to_b()]
     45 # 6. make line_to_CC() return a ref instead of a hash           3.01 --> 2.77s
     46 #
     47 #10. changed file format to avoid file/fn name repetition       2.40s
     48 #    (not sure why higher;  maybe due to new '.' entries?)
     49 #11. changed file format to drop unnecessary end-line "."s      2.36s
     50 #    (shrunk file by about 37%)
     51 #12. switched from hash CCs to array CCs                        1.61s
     52 #13. only adding b[i] to a[i] if b[i] defined (was doing it if
     53 #    either a[i] or b[i] was defined, but if b[i] was undefined
     54 #    it just added 0)                                           1.48s
     55 #14. Stopped converting "." entries to undef and then back      1.16s
     56 #15. Using foreach $i (x..y) instead of for ($i = 0...) in
     57 #    add_array_a_to_b()                                         1.11s
     58 #
     59 # Auto-annotating primes:
     60 #16. Finding count lengths by int((length-1)/3), not by
     61 #    commifying (halves the number of commify calls)            1.68s --> 1.47s
     62 
     63 use strict;
     64 
     65 #----------------------------------------------------------------------------
     66 # Overview: the running example in the comments is for:
     67 #   - events = A,B,C,D
     68 #   - --show=C,A,D
     69 #   - --sort=D,C
     70 #----------------------------------------------------------------------------
     71 
     72 #----------------------------------------------------------------------------
     73 # Global variables, main data structures
     74 #----------------------------------------------------------------------------
     75 # CCs are arrays, the counts corresponding to @events, with 'undef'
     76 # representing '.'.  This makes things fast (faster than using hashes for CCs)
     77 # but we have to use @sort_order and @show_order below to handle the --sort and
     78 # --show options, which is a bit tricky.
     79 #----------------------------------------------------------------------------
     80 
     81 # Total counts for summary (an array reference).
     82 my $summary_CC;
     83 my $totals_CC;
     84 my $summary_calculated = 0;
     85 
     86 # Totals for each function, for overall summary.
     87 # hash(filename:fn_name => CC array)
     88 my %fn_totals;
     89 
     90 # Individual CCs, organised by filename and line_num for easy annotation.
     91 # hash(filename => hash(line_num => CC array))
     92 my %all_ind_CCs;
     93 
     94 # Files chosen for annotation on the command line.  
     95 # key = basename (trimmed of any directory), value = full filename
     96 my %user_ann_files;
     97 
     98 # Generic description string.
     99 my $desc = "";
    100 
    101 # Command line of profiled program.
    102 my $cmd = "";
    103 
    104 # Info on the profiled process.
    105 my $creator = "";
    106 my $pid = "";
    107 my $part = "";
    108 my $thread = "";
    109 
    110 # Positions used for cost lines; default: line numbers
    111 my $has_line = 1;
    112 my $has_addr = 0;
    113 
    114 # Events in input file, eg. (A,B,C,D)
    115 my @events;
    116 my $events;
    117 
    118 # Events to show, from command line, eg. (C,A,D)
    119 my @show_events;
    120 
    121 # Map from @show_events indices to @events indices, eg. (2,0,3).  Gives the
    122 # order in which we must traverse @events in order to show the @show_events, 
    123 # eg. (@events[$show_order[1]], @events[$show_order[2]]...) = @show_events.
    124 # (Might help to think of it like a hash (0 => 2, 1 => 0, 2 => 3).)
    125 my @show_order;
    126 
    127 # Print out the function totals sorted by these events, eg. (D,C).
    128 my @sort_events;
    129 
    130 # Map from @sort_events indices to @events indices, eg. (3,2).  Same idea as
    131 # for @show_order.
    132 my @sort_order;
    133 
    134 # Thresholds, one for each sort event (or default to 1 if no sort events
    135 # specified).  We print out functions and do auto-annotations until we've
    136 # handled this proportion of all the events thresholded.
    137 my @thresholds;
    138 
    139 my $default_threshold = 99;
    140 
    141 my $single_threshold  = $default_threshold;
    142 
    143 # If on, automatically annotates all files that are involved in getting over
    144 # all the threshold counts.
    145 my $auto_annotate = 0;
    146 
    147 # Number of lines to show around each annotated line.
    148 my $context = 8;
    149 
    150 # Directories in which to look for annotation files.
    151 my @include_dirs = ("");
    152 
    153 # Verbose mode
    154 my $verbose = "1";
    155 
    156 # Inclusive statistics (with subroutine events)
    157 my $inclusive = 0;
    158 
    159 # Inclusive totals for each function, for overall summary.
    160 # hash(filename:fn_name => CC array)
    161 my %cfn_totals;
    162 
    163 # hash( file:func => [ called file:func ])
    164 my $called_funcs;
    165 
    166 # hash( file:func => [ calling file:func ])
    167 my $calling_funcs;
    168 
    169 # hash( file:func,line => [called file:func ])
    170 my $called_from_line;
    171 
    172 # hash( file:func,line => file:func
    173 my %func_of_line;
    174 
    175 # hash (file:func => object name)
    176 my %obj_name;
    177 
    178 # Print out the callers of a function
    179 my $tree_caller = 0;
    180 
    181 # Print out the called functions
    182 my $tree_calling = 0;
    183 
    184 # hash( file:func,cfile:cfunc => call CC[])
    185 my %call_CCs;
    186 
    187 # hash( file:func,cfile:cfunc => call counter)
    188 my %call_counter;
    189 
    190 # hash(context, index) => realname for compressed traces
    191 my %compressed;
    192 
    193 # Input file name, will be set in process_cmd_line
    194 my $input_file = "";
    195 
    196 # Version number
    197 my $version = "@VERSION@";
    198 
    199 # Usage message.
    200 my $usage = <<END
    201 usage: callgrind_annotate [options] [callgrind-out-file [source-files...]]
    202 
    203   options for the user, with defaults in [ ], are:
    204     -h --help             show this message
    205     --version             show version
    206     --show=A,B,C          only show figures for events A,B,C [all]
    207     --sort=A,B,C          sort columns by events A,B,C [event column order]
    208     --threshold=<0--100>  percentage of counts (of primary sort event) we
    209                           are interested in [$default_threshold%]
    210     --auto=yes|no         annotate all source files containing functions
    211                           that helped reach the event count threshold [no]
    212     --context=N           print N lines of context before and after
    213                           annotated lines [8]
    214     --inclusive=yes|no    add subroutine costs to functions calls [no]
    215     --tree=none|caller|   print for each function their callers,
    216            calling|both   the called functions or both [none]
    217     -I --include=<dir>    add <dir> to list of directories to search for 
    218                           source files
    219 
    220 END
    221 ;
    222 
    223 # Used in various places of output.
    224 my $fancy = '-' x 80 . "\n";
    225 
    226 #-----------------------------------------------------------------------------
    227 # Argument and option handling
    228 #-----------------------------------------------------------------------------
    229 sub process_cmd_line() 
    230 {
    231     for my $arg (@ARGV) { 
    232 
    233         # Option handling
    234         if ($arg =~ /^-/) {
    235 
    236             # --version
    237             if ($arg =~ /^--version$/) {
    238                 die("callgrind_annotate-$version\n");
    239 
    240             # --show=A,B,C
    241             } elsif ($arg =~ /^--show=(.*)$/) {
    242                 @show_events = split(/,/, $1);
    243 
    244             # --sort=A,B,C
    245             } elsif ($arg =~ /^--sort=(.*)$/) {
    246                 @sort_events = split(/,/, $1);
    247                 my $th_specified = 0;
    248                 foreach my $i (0 .. scalar @sort_events - 1) {
    249                     if ($sort_events[$i] =~ /.*:([\d\.]+)%?$/) {
    250                         my $th = $1;
    251                         ($th >= 0 && $th <= 100) or die($usage);
    252                         $sort_events[$i] =~ s/:.*//;
    253                         $thresholds[$i] = $th;
    254                         $th_specified = 1;
    255                     } else {
    256                         $thresholds[$i] = 0;
    257                     }
    258                 }
    259                 if (not $th_specified) {
    260                     @thresholds = ();
    261                 }
    262 
    263             # --threshold=X (tolerates a trailing '%')
    264             } elsif ($arg =~ /^--threshold=([\d\.]+)%?$/) {
    265                 $single_threshold = $1;
    266                 ($1 >= 0 && $1 <= 100) or die($usage);
    267 
    268             # --auto=yes|no
    269             } elsif ($arg =~ /^--auto=(yes|no)$/) {
    270                 $auto_annotate = 1 if ($1 eq "yes");
    271                 $auto_annotate = 0 if ($1 eq "no");
    272 
    273             # --context=N
    274             } elsif ($arg =~ /^--context=([\d\.]+)$/) {
    275                 $context = $1;
    276                 if ($context < 0) {
    277                     die($usage);
    278                 }
    279 
    280             # --inclusive=yes|no
    281             } elsif ($arg =~ /^--inclusive=(yes|no)$/) {
    282                 $inclusive = 1 if ($1 eq "yes");
    283                 $inclusive = 0 if ($1 eq "no");
    284 
    285             # --tree=none|caller|calling|both
    286             } elsif ($arg =~ /^--tree=(none|caller|calling|both)$/) {
    287                 $tree_caller  = 1 if ($1 eq "caller" || $1 eq "both");
    288                 $tree_calling = 1 if ($1 eq "calling" || $1 eq "both");
    289 
    290             # --include=A,B,C
    291             } elsif ($arg =~ /^(-I|--include)=(.*)$/) {
    292                 my $inc = $2;
    293                 $inc =~ s|/$||;         # trim trailing '/'
    294                 push(@include_dirs, "$inc/");
    295 
    296             } else {            # -h and --help fall under this case
    297                 die($usage);
    298             }
    299 
    300         # Argument handling -- annotation file checking and selection.
    301         # Stick filenames into a hash for quick 'n easy lookup throughout
    302         } else {
    303 	  if ($input_file eq "") {
    304 	    $input_file = $arg;
    305 	  }
    306 	  else {
    307             my $readable = 0;
    308             foreach my $include_dir (@include_dirs) {
    309                 if (-r $include_dir . $arg) {
    310                     $readable = 1;
    311                 }
    312             }
    313             $readable or die("File $arg not found in any of: @include_dirs\n");
    314             $user_ann_files{$arg} = 1;
    315         } 
    316     }
    317     }
    318 
    319     if ($input_file eq "") {
    320       $input_file = (<callgrind.out*>)[0];
    321       if (!defined $input_file) {
    322 	  $input_file = (<cachegrind.out*>)[0];
    323       }
    324 
    325       (defined $input_file) or die($usage);
    326       print "Reading data from '$input_file'...\n";
    327     }
    328 }
    329 
    330 #-----------------------------------------------------------------------------
    331 # Reading of input file
    332 #-----------------------------------------------------------------------------
    333 sub max ($$) 
    334 {
    335     my ($x, $y) = @_;
    336     return ($x > $y ? $x : $y);
    337 }
    338 
    339 # Add the two arrays;  any '.' entries are ignored.  Two tricky things:
    340 # 1. If $a2->[$i] is undefined, it defaults to 0 which is what we want; we turn
    341 #    off warnings to allow this.  This makes things about 10% faster than
    342 #    checking for definedness ourselves.
    343 # 2. We don't add an undefined count or a ".", even though it's value is 0,
    344 #    because we don't want to make an $a2->[$i] that is undef become 0
    345 #    unnecessarily.
    346 sub add_array_a_to_b ($$) 
    347 {
    348     my ($a1, $a2) = @_;
    349 
    350     my $n = max(scalar @$a1, scalar @$a2);
    351     $^W = 0;
    352     foreach my $i (0 .. $n-1) {
    353         $a2->[$i] += $a1->[$i] if (defined $a1->[$i] && "." ne $a1->[$i]);
    354     }
    355     $^W = 1;
    356 }
    357 
    358 # Is this a line with all events zero?
    359 sub is_zero ($)
    360 {
    361     my ($CC) = @_;
    362     my $isZero = 1;
    363     foreach my $i (0 .. (scalar @$CC)-1) {
    364 	$isZero = 0 if ($CC->[$i] >0);
    365     }
    366     return $isZero;
    367 }
    368 
    369 # Add each event count to the CC array.  '.' counts become undef, as do
    370 # missing entries (implicitly).
    371 sub line_to_CC ($)
    372 {
    373     my @CC = (split /\s+/, $_[0]);
    374     (@CC <= @events) or die("Line $.: too many event counts\n");
    375     return \@CC;
    376 }
    377 
    378 sub uncompressed_name($$)
    379 {
    380    my ($context, $name) = @_;
    381 
    382    if ($name =~ /^\((\d+)\)\s*(.*)$/) {
    383      my $index = $1;
    384      my $realname = $2;
    385 
    386      if ($realname eq "") {
    387        $realname = $compressed{$context,$index};
    388      }
    389      else {
    390        $compressed{$context,$index} = $realname;
    391      }
    392      return $realname;
    393    }
    394    return $name;
    395 }
    396 
    397 sub read_input_file() 
    398 {
    399     open(INPUTFILE, "< $input_file") || die "File $input_file not opened\n";
    400 
    401     my $line;
    402 
    403     # Read header
    404     while(<INPUTFILE>) {
    405 
    406       # remove comments
    407       s/#.*$//;
    408 
    409       if (/^$/) { ; }
    410 
    411       elsif (/^version:\s*(\d+)/) {
    412 	# Can't read format with major version > 1
    413 	($1<2) or die("Can't read format with major version $1.\n");
    414       }
    415 
    416       elsif (/^pid:\s+(.*)$/) { $pid = $1;  }
    417       elsif (/^thread:\s+(.*)$/) { $thread = $1;  }
    418       elsif (/^part:\s+(.*)$/) { $part = $1;  }
    419       elsif (/^desc:\s+(.*)$/) {
    420 	my $dline = $1;
    421 	# suppress profile options in description output
    422 	if ($dline =~ /^Option:/) {;}
    423 	else { $desc .= "$dline\n"; }
    424       }
    425       elsif (/^cmd:\s+(.*)$/)  { $cmd = $1; }
    426       elsif (/^creator:\s+(.*)$/)  { $creator = $1; }
    427       elsif (/^positions:\s+(.*)$/) {
    428 	my $positions = $1;
    429 	$has_line = ($positions =~ /line/);
    430 	$has_addr = ($positions =~ /(addr|instr)/);
    431       }
    432       elsif (/^event:\s+.*$/) { 
    433         # ignore lines giving a long name to an event
    434       }
    435       elsif (/^events:\s+(.*)$/) {
    436 	$events = $1;
    437 	
    438 	# events line is last in header
    439 	last;
    440       }
    441       else {
    442 	warn("WARNING: header line $. malformed, ignoring\n");
    443 	if ($verbose) { chomp; warn("    line: '$_'\n"); }
    444       }
    445     }
    446 
    447     # Read "events:" line.  We make a temporary hash in which the Nth event's
    448     # value is N, which is useful for handling --show/--sort options below.
    449     ($events ne "") or die("Line $.: missing events line\n");
    450     @events = split(/\s+/, $events);
    451     my %events;
    452     my $n = 0;
    453     foreach my $event (@events) {
    454         $events{$event} = $n;
    455         $n++
    456     }
    457 
    458     # If no --show arg give, default to showing all events in the file.
    459     # If --show option is used, check all specified events appeared in the
    460     # "events:" line.  Then initialise @show_order.
    461     if (@show_events) {
    462         foreach my $show_event (@show_events) {
    463             (defined $events{$show_event}) or 
    464                 die("--show event `$show_event' did not appear in input\n");
    465         }
    466     } else {
    467         @show_events = @events;
    468     }
    469     foreach my $show_event (@show_events) {
    470         push(@show_order, $events{$show_event});
    471     }
    472 
    473     # Do as for --show, but if no --sort arg given, default to sorting by
    474     # column order (ie. first column event is primary sort key, 2nd column is
    475     # 2ndary key, etc).
    476     if (@sort_events) {
    477         foreach my $sort_event (@sort_events) {
    478             (defined $events{$sort_event}) or 
    479                 die("--sort event `$sort_event' did not appear in input\n");
    480         }
    481     } else {
    482         @sort_events = @events;
    483     }
    484     foreach my $sort_event (@sort_events) {
    485         push(@sort_order, $events{$sort_event});
    486     }
    487 
    488     # If multiple threshold args weren't given via --sort, stick in the single
    489     # threshold (either from --threshold if used, or the default otherwise) for
    490     # the primary sort event, and 0% for the rest.
    491     if (not @thresholds) {
    492         foreach my $e (@sort_order) {
    493             push(@thresholds, 0);
    494         }
    495         $thresholds[0] = $single_threshold;
    496     }
    497 
    498     # Current directory, used to strip from file names if absolute
    499     my $pwd = `pwd`;
    500     chomp $pwd;
    501     $pwd .= '/';
    502 
    503     my $curr_obj = "";
    504     my $curr_file;
    505     my $curr_fn;
    506     my $curr_name;
    507     my $curr_line_num = 0;
    508     my $prev_line_num = 0;
    509 
    510     my $curr_cobj = "";
    511     my $curr_cfile = "";
    512     my $curr_cfunc = "";
    513     my $curr_cname;
    514     my $curr_call_counter = 0;
    515     my $curr_cfn_CC = [];
    516 
    517     my $curr_fn_CC = [];
    518     my $curr_file_ind_CCs = {};     # hash(line_num => CC)
    519 
    520     # Read body of input file.
    521     while (<INPUTFILE>) {
    522 	$prev_line_num = $curr_line_num;
    523 
    524         s/#.*$//;   # remove comments
    525         s/^\+(\d+)/$prev_line_num+$1/e;
    526         s/^\-(\d+)/$prev_line_num-$1/e;
    527         s/^\*/$prev_line_num/e;
    528         if (s/^(-?\d+|0x\w+)\s+//) {
    529             $curr_line_num = $1;
    530 	    if ($has_addr) {
    531 	      if ($has_line) {
    532                 s/^\+(\d+)/$prev_line_num+$1/e;
    533 	        s/^\-(\d+)/$prev_line_num-$1/e;
    534                 s/^\*/$prev_line_num/e;
    535 
    536 	        if (s/^(\d+)\s+//) { $curr_line_num = $1; }
    537 	      }
    538 	      else { $curr_line_num = 0; }
    539 	    }
    540             my $CC = line_to_CC($_);
    541 
    542 	    if ($curr_call_counter>0) {
    543 #	      print "Read ($curr_name => $curr_cname) $curr_call_counter\n";
    544 
    545 	      if (!defined $call_CCs{$curr_name,$curr_cname}) {
    546 		$call_CCs{$curr_name,$curr_cname} = [];
    547 		$call_counter{$curr_name,$curr_cname} = 0;
    548 	      }
    549 	      add_array_a_to_b($CC, $call_CCs{$curr_name,$curr_cname});
    550 	      $call_counter{$curr_name,$curr_cname} += $curr_call_counter;
    551 
    552 	      my $tmp = $called_from_line->{$curr_file,$curr_line_num};
    553 	      if (!defined $tmp) {
    554 		$func_of_line{$curr_file,$curr_line_num} = $curr_name;
    555 	      }
    556 	      $tmp = {} unless defined $tmp;
    557 	      $$tmp{$curr_cname} = 1;
    558 	      $called_from_line->{$curr_file,$curr_line_num} = $tmp;
    559 	      if (!defined $call_CCs{$curr_name,$curr_cname,$curr_line_num}) {
    560 		$call_CCs{$curr_name,$curr_cname,$curr_line_num} = [];
    561 		$call_counter{$curr_name,$curr_cname,$curr_line_num} = 0;
    562 	      }
    563 	      add_array_a_to_b($CC, $call_CCs{$curr_name,$curr_cname,$curr_line_num});
    564 	      $call_counter{$curr_name,$curr_cname,$curr_line_num} += $curr_call_counter;
    565 
    566 	      $curr_call_counter = 0;
    567 
    568 	      # inclusive costs
    569 	      $curr_cfn_CC = $cfn_totals{$curr_cname};
    570 	      $curr_cfn_CC = [] unless (defined $curr_cfn_CC);
    571 	      add_array_a_to_b($CC, $curr_cfn_CC);
    572 	      $cfn_totals{$curr_cname} = $curr_cfn_CC;
    573 
    574 	      if ($inclusive) {
    575 		add_array_a_to_b($CC, $curr_fn_CC);
    576 	      }
    577 	      next;
    578 	    }
    579 
    580             add_array_a_to_b($CC, $curr_fn_CC);
    581 
    582             # If curr_file is selected, add CC to curr_file list.  We look for
    583             # full filename matches;  or, if auto-annotating, we have to
    584             # remember everything -- we won't know until the end what's needed.
    585             if ($auto_annotate || defined $user_ann_files{$curr_file}) {
    586                 my $tmp = $curr_file_ind_CCs->{$curr_line_num};
    587                 $tmp = [] unless defined $tmp;
    588                 add_array_a_to_b($CC, $tmp);
    589                 $curr_file_ind_CCs->{$curr_line_num} = $tmp;
    590             }
    591 
    592         } elsif (s/^fn=(.*)$//) {
    593             # Commit result from previous function
    594             $fn_totals{$curr_name} = $curr_fn_CC if (defined $curr_name);
    595 
    596             # Setup new one
    597             $curr_fn = uncompressed_name("fn",$1);
    598             $curr_name = "$curr_file:$curr_fn";
    599 	    $obj_name{$curr_name} = $curr_obj;
    600             $curr_fn_CC = $fn_totals{$curr_name};
    601             $curr_fn_CC = [] unless (defined $curr_fn_CC);
    602 
    603         } elsif (s/^ob=(.*)$//) {
    604             $curr_obj = uncompressed_name("ob",$1);
    605 
    606         } elsif (s/^fl=(.*)$//) {
    607             $all_ind_CCs{$curr_file} = $curr_file_ind_CCs 
    608                 if (defined $curr_file);
    609 
    610             $curr_file = uncompressed_name("fl",$1);
    611             $curr_file =~ s/^\Q$pwd\E//;
    612             $curr_file_ind_CCs = $all_ind_CCs{$curr_file};
    613             $curr_file_ind_CCs = {} unless (defined $curr_file_ind_CCs);
    614 
    615         } elsif (s/^(fi|fe)=(.*)$//) {
    616             (defined $curr_name) or die("Line $.: Unexpected fi/fe line\n");
    617             $fn_totals{$curr_name} = $curr_fn_CC;
    618             $all_ind_CCs{$curr_file} = $curr_file_ind_CCs;
    619 
    620             $curr_file = uncompressed_name("fl",$2);
    621             $curr_file =~ s/^\Q$pwd\E//;
    622             $curr_name = "$curr_file:$curr_fn";
    623             $curr_file_ind_CCs = $all_ind_CCs{$curr_file};
    624             $curr_file_ind_CCs = {} unless (defined $curr_file_ind_CCs);
    625             $curr_fn_CC = $fn_totals{$curr_name};
    626             $curr_fn_CC = [] unless (defined $curr_fn_CC);
    627 
    628         } elsif (s/^\s*$//) {
    629             # blank, do nothing
    630 
    631         } elsif (s/^cob=(.*)$//) {
    632 	  $curr_cobj = uncompressed_name("ob",$1);
    633 
    634 	} elsif (s/^cf[il]=(.*)$//) {
    635 	  $curr_cfile = uncompressed_name("fl",$1);
    636 
    637 	} elsif (s/^cfn=(.*)$//) {
    638 	  $curr_cfunc = uncompressed_name("fn",$1);
    639 	  if ($curr_cfile eq "") {
    640 	    $curr_cname = "$curr_file:$curr_cfunc";
    641 	  }
    642 	  else {
    643 	    $curr_cname = "$curr_cfile:$curr_cfunc";
    644 	    $curr_cfile = "";
    645 	  }
    646 
    647 	  my $tmp = $calling_funcs->{$curr_cname};
    648 	  $tmp = {} unless defined $tmp;
    649 	  $$tmp{$curr_name} = 1;
    650 	  $calling_funcs->{$curr_cname} = $tmp;
    651 		
    652 	  my $tmp2 = $called_funcs->{$curr_name};
    653 	  $tmp2 = {} unless defined $tmp2;
    654 	  $$tmp2{$curr_cname} = 1;
    655 	  $called_funcs->{$curr_name} = $tmp2;
    656 
    657 	} elsif (s/^calls=(\d+)//) {
    658 	  $curr_call_counter = $1;
    659 
    660         } elsif (s/^(jump|jcnd)=//) {
    661 	  #ignore jump information
    662 
    663         } elsif (s/^jfi=(.*)$//) {
    664           # side effect needed: possibly add compression mapping 
    665           uncompressed_name("fl",$1);
    666           # ignore jump information	
    667 
    668         } elsif (s/^jfn=(.*)$//) {
    669           # side effect needed: possibly add compression mapping
    670           uncompressed_name("fn",$1);
    671           # ignore jump information
    672 
    673         } elsif (s/^totals:\s+//) {
    674 	    $totals_CC = line_to_CC($_);
    675 
    676         } elsif (s/^summary:\s+//) {
    677             $summary_CC = line_to_CC($_);
    678 
    679         } else {
    680             warn("WARNING: line $. malformed, ignoring\n");
    681 	    if ($verbose) { chomp; warn("    line: '$_'\n"); }
    682         }
    683     }
    684 
    685     # Finish up handling final filename/fn_name counts
    686     $fn_totals{"$curr_file:$curr_fn"} = $curr_fn_CC
    687 	if (defined $curr_file && defined $curr_fn);
    688     $all_ind_CCs{$curr_file} =
    689 	$curr_file_ind_CCs if (defined $curr_file);
    690 
    691     # Correct inclusive totals
    692     if ($inclusive) {
    693       foreach my $name (keys %cfn_totals) {
    694 	$fn_totals{$name} = $cfn_totals{$name};
    695       }
    696     }
    697 
    698     close(INPUTFILE);
    699 
    700     if ((not defined $summary_CC) || is_zero($summary_CC)) {
    701 	$summary_CC = $totals_CC;
    702 
    703 	# if neither 'summary:' nor 'totals:' line is given,
    704 	# calculate summary from fn_totals hash
    705 	if ((not defined $summary_CC) || is_zero($summary_CC)) {
    706 	    $summary_calculated = 1;
    707 	    $summary_CC = [];
    708 	    foreach my $name (keys %fn_totals) {
    709 		add_array_a_to_b($fn_totals{$name}, $summary_CC);
    710 	    }
    711 	}
    712     }
    713 }
    714 
    715 #-----------------------------------------------------------------------------
    716 # Print options used
    717 #-----------------------------------------------------------------------------
    718 sub print_options ()
    719 {
    720     print($fancy);
    721     print "Profile data file '$input_file'";
    722     if ($creator ne "") { print " (creator: $creator)"; }
    723     print "\n";
    724 
    725     print($fancy);
    726     print($desc);
    727     my $target = $cmd;
    728     if ($target eq "") { $target = "(unknown)"; }
    729     if ($pid ne "") {
    730       $target .= " (PID $pid";
    731       if ($part ne "") { $target .= ", part $part"; }
    732       if ($thread ne "") { $target .= ", thread $thread"; }
    733       $target .= ")";
    734     }
    735     print("Profiled target:  $target\n");
    736     print("Events recorded:  @events\n");
    737     print("Events shown:     @show_events\n");
    738     print("Event sort order: @sort_events\n");
    739     print("Thresholds:       @thresholds\n");
    740 
    741     my @include_dirs2 = @include_dirs;  # copy @include_dirs
    742     shift(@include_dirs2);       # remove "" entry, which is always the first
    743     unshift(@include_dirs2, "") if (0 == @include_dirs2); 
    744     my $include_dir = shift(@include_dirs2);
    745     print("Include dirs:     $include_dir\n");
    746     foreach my $include_dir (@include_dirs2) {
    747         print("                  $include_dir\n");
    748     }
    749 
    750     my @user_ann_files = keys %user_ann_files;
    751     unshift(@user_ann_files, "") if (0 == @user_ann_files); 
    752     my $user_ann_file = shift(@user_ann_files);
    753     print("User annotated:   $user_ann_file\n");
    754     foreach $user_ann_file (@user_ann_files) {
    755         print("                  $user_ann_file\n");
    756     }
    757 
    758     my $is_on = ($auto_annotate ? "on" : "off");
    759     print("Auto-annotation:  $is_on\n");
    760     print("\n");
    761 }
    762 
    763 #-----------------------------------------------------------------------------
    764 # Print summary and sorted function totals
    765 #-----------------------------------------------------------------------------
    766 sub mycmp ($$) 
    767 {
    768     my ($c, $d) = @_;
    769 
    770     # Iterate through sort events (eg. 3,2); return result if two are different
    771     foreach my $i (@sort_order) {
    772         my ($x, $y);
    773         $x = $c->[$i];
    774         $y = $d->[$i];
    775         $x = -1 unless defined $x;
    776         $y = -1 unless defined $y;
    777 
    778         my $cmp = $y <=> $x;        # reverse sort
    779         if (0 != $cmp) {
    780             return $cmp;
    781         }
    782     }
    783     # Exhausted events, equal
    784     return 0;
    785 }
    786 
    787 sub commify ($) {
    788     my ($val) = @_;
    789     1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/);
    790     return $val;
    791 }
    792 
    793 # Because the counts can get very big, and we don't want to waste screen space
    794 # and make lines too long, we compute exactly how wide each column needs to be
    795 # by finding the widest entry for each one.
    796 sub compute_CC_col_widths (@) 
    797 {
    798     my @CCs = @_;
    799     my $CC_col_widths = [];
    800 
    801     # Initialise with minimum widths (from event names)
    802     foreach my $event (@events) {
    803         push(@$CC_col_widths, length($event));
    804     }
    805     
    806     # Find maximum width count for each column.  @CC_col_width positions
    807     # correspond to @CC positions.
    808     foreach my $CC (@CCs) {
    809         foreach my $i (0 .. scalar(@$CC)-1) {
    810             if (defined $CC->[$i]) {
    811                 # Find length, accounting for commas that will be added
    812                 my $length = length $CC->[$i];
    813                 my $clength = $length + int(($length - 1) / 3);
    814                 $CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength); 
    815             }
    816         }
    817     }
    818     return $CC_col_widths;
    819 }
    820 
    821 # Print the CC with each column's size dictated by $CC_col_widths.
    822 sub print_CC ($$) 
    823 {
    824     my ($CC, $CC_col_widths) = @_;
    825 
    826     foreach my $i (@show_order) {
    827         my $count = (defined $CC->[$i] ? commify($CC->[$i]) : ".");
    828         my $space = ' ' x ($CC_col_widths->[$i] - length($count));
    829         print("$space$count ");
    830     }
    831 }
    832 
    833 sub print_events ($)
    834 {
    835     my ($CC_col_widths) = @_;
    836 
    837     foreach my $i (@show_order) { 
    838         my $event       = $events[$i];
    839         my $event_width = length($event);
    840         my $col_width   = $CC_col_widths->[$i];
    841         my $space       = ' ' x ($col_width - $event_width);
    842         print("$space$event ");
    843     }
    844 }
    845 
    846 # Prints summary and function totals (with separate column widths, so that
    847 # function names aren't pushed over unnecessarily by huge summary figures).
    848 # Also returns a hash containing all the files that are involved in getting the
    849 # events count above the thresholds (ie. all the interesting ones).
    850 sub print_summary_and_fn_totals ()
    851 {
    852     my @fn_fullnames = keys   %fn_totals;
    853 
    854     # Work out the size of each column for printing (summary and functions
    855     # separately).
    856     my $summary_CC_col_widths = compute_CC_col_widths($summary_CC);
    857     my      $fn_CC_col_widths = compute_CC_col_widths(values %fn_totals);
    858 
    859     # Header and counts for summary
    860     print($fancy);
    861     print_events($summary_CC_col_widths);
    862     print("\n");
    863     print($fancy);
    864     print_CC($summary_CC, $summary_CC_col_widths);
    865     print(" PROGRAM TOTALS");
    866     if ($summary_calculated) {
    867 	print(" (calculated)");
    868     }
    869     print("\n\n");
    870 
    871     # Header for functions
    872     print($fancy);
    873     print_events($fn_CC_col_widths);
    874     print(" file:function\n");
    875     print($fancy);
    876 
    877     # Sort function names into order dictated by --sort option.
    878     @fn_fullnames = sort {
    879         mycmp($fn_totals{$a}, $fn_totals{$b})
    880     } @fn_fullnames;
    881 
    882 
    883     # Assertion
    884     (scalar @sort_order == scalar @thresholds) or 
    885         die("sort_order length != thresholds length:\n",
    886             "  @sort_order\n  @thresholds\n");
    887 
    888     my $threshold_files       = {};
    889     # @curr_totals has the same shape as @sort_order and @thresholds
    890     my @curr_totals = ();
    891     foreach my $e (@thresholds) {
    892         push(@curr_totals, 0);
    893     }
    894 
    895     # Print functions, stopping when the threshold has been reached.
    896     foreach my $fn_name (@fn_fullnames) {
    897 
    898         # Stop when we've reached all the thresholds
    899         my $reached_all_thresholds = 1;
    900         foreach my $i (0 .. scalar @thresholds - 1) {
    901             my $prop = $curr_totals[$i] * 100;
    902 	    if ($summary_CC->[$sort_order[$i]] >0) {
    903 	      $prop = $prop / $summary_CC->[$sort_order[$i]];
    904 	    }
    905             $reached_all_thresholds &&= ($prop >= $thresholds[$i]);
    906         }
    907         last if $reached_all_thresholds;
    908 
    909 	if ($tree_caller || $tree_calling) { print "\n"; }
    910 
    911 	if ($tree_caller && ($fn_name ne "???:???")) {
    912 	  # Print function callers
    913 	  my $tmp1 = $calling_funcs->{$fn_name};
    914 	  if (defined $tmp1) {
    915 	    foreach my $calling (keys %$tmp1) {
    916 	      if (defined $call_counter{$calling,$fn_name}) {
    917 		print_CC($call_CCs{$calling,$fn_name}, $fn_CC_col_widths);
    918 		print" < $calling (";
    919 		print $call_counter{$calling,$fn_name} . "x)";
    920 		if (defined $obj_name{$calling}) {
    921 		  print " [$obj_name{$calling}]";
    922 		}
    923 		print "\n";
    924 	      }
    925 	    }
    926 	  }
    927 	}
    928 
    929         # Print function results
    930         my $fn_CC = $fn_totals{$fn_name};
    931         print_CC($fn_CC, $fn_CC_col_widths);
    932 	if ($tree_caller || $tree_calling) { print " * "; }
    933         print(" $fn_name");
    934 	if ((defined $obj_name{$fn_name}) &&
    935 	    ($obj_name{$fn_name} ne "")) {
    936 	  print " [$obj_name{$fn_name}]";
    937 	}
    938 	print "\n";
    939 
    940 	if ($tree_calling && ($fn_name ne "???:???")) {
    941 	  # Print called functions
    942 	  my $tmp2 = $called_funcs->{$fn_name};
    943 	  if (defined $tmp2) {
    944 	    foreach my $called (keys %$tmp2) {
    945 	      if (defined $call_counter{$fn_name,$called}) {
    946 		print_CC($call_CCs{$fn_name,$called}, $fn_CC_col_widths);
    947 		print" >   $called (";
    948 		print $call_counter{$fn_name,$called} . "x)";
    949 		if (defined $obj_name{$called}) {
    950 		  print " [$obj_name{$called}]";
    951 		}
    952 		print "\n";
    953 	      }
    954 	    }
    955 	  }
    956 	}
    957 
    958         # Update the threshold counts
    959         my $filename = $fn_name;
    960         $filename =~ s/:.+$//;    # remove function name
    961         $threshold_files->{$filename} = 1;
    962         foreach my $i (0 .. scalar @sort_order - 1) {
    963 	  if ($inclusive) {
    964 	    $curr_totals[$i] = $summary_CC->[$sort_order[$i]] -
    965                                $fn_CC->[$sort_order[$i]]
    966 	      if (defined $fn_CC->[$sort_order[$i]]);
    967 	  } else {
    968             $curr_totals[$i] += $fn_CC->[$sort_order[$i]] 
    969                 if (defined $fn_CC->[$sort_order[$i]]);
    970         }
    971     }
    972     }
    973     print("\n");
    974 
    975     return $threshold_files;
    976 }
    977 
    978 #-----------------------------------------------------------------------------
    979 # Annotate selected files
    980 #-----------------------------------------------------------------------------
    981 
    982 # Issue a warning that the source file is more recent than the input file. 
    983 sub warning_on_src_more_recent_than_inputfile ($)
    984 {
    985     my $src_file = $_[0];
    986 
    987     my $warning = <<END
    988 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    989 @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@
    990 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    991 @ Source file '$src_file' is more recent than input file '$input_file'.
    992 @ Annotations may not be correct.
    993 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    994 
    995 END
    996 ;
    997     print($warning);
    998 }
    999 
   1000 # If there is information about lines not in the file, issue a warning
   1001 # explaining possible causes.
   1002 sub warning_on_nonexistent_lines ($$$)
   1003 {
   1004     my ($src_more_recent_than_inputfile, $src_file, $excess_line_nums) = @_;
   1005     my $cause_and_solution;
   1006 
   1007     if ($src_more_recent_than_inputfile) {
   1008         $cause_and_solution = <<END
   1009 @@ cause:    '$src_file' has changed since information was gathered.
   1010 @@           If so, a warning will have already been issued about this.
   1011 @@ solution: Recompile program and rerun under "valgrind --cachesim=yes" to 
   1012 @@           gather new information.
   1013 END
   1014     # We suppress warnings about .h files
   1015     } elsif ($src_file =~ /\.h$/) {
   1016         $cause_and_solution = <<END
   1017 @@ cause:    bug in the Valgrind's debug info reader that screws up with .h
   1018 @@           files sometimes
   1019 @@ solution: none, sorry
   1020 END
   1021     } else {
   1022         $cause_and_solution = <<END
   1023 @@ cause:    not sure, sorry
   1024 END
   1025     }
   1026 
   1027     my $warning = <<END
   1028 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   1029 @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@
   1030 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   1031 @@
   1032 @@ Information recorded about lines past the end of '$src_file'.
   1033 @@
   1034 @@ Probable cause and solution:
   1035 $cause_and_solution@@
   1036 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   1037 END
   1038 ;
   1039     print($warning);
   1040 }
   1041 
   1042 sub annotate_ann_files($)
   1043 {
   1044     my ($threshold_files) = @_; 
   1045 
   1046     my %all_ann_files;
   1047     my @unfound_auto_annotate_files;
   1048     my $printed_totals_CC = [];
   1049 
   1050     # If auto-annotating, add interesting files (but not "???")
   1051     if ($auto_annotate) {
   1052         delete $threshold_files->{"???"};
   1053         %all_ann_files = (%user_ann_files, %$threshold_files) 
   1054     } else {
   1055         %all_ann_files = %user_ann_files;
   1056     }
   1057 
   1058     # Track if we did any annotations.
   1059     my $did_annotations = 0;
   1060 
   1061     LOOP:
   1062     foreach my $src_file (keys %all_ann_files) {
   1063 
   1064         my $opened_file = "";
   1065         my $full_file_name = "";
   1066         foreach my $include_dir (@include_dirs) {
   1067             my $try_name = $include_dir . $src_file;
   1068             if (open(INPUTFILE, "< $try_name")) {
   1069                 $opened_file    = $try_name;
   1070                 $full_file_name = ($include_dir eq "" 
   1071                                   ? $src_file 
   1072                                   : "$include_dir + $src_file"); 
   1073                 last;
   1074             }
   1075         }
   1076         
   1077         if (not $opened_file) {
   1078             # Failed to open the file.  If chosen on the command line, die.
   1079             # If arose from auto-annotation, print a little message.
   1080             if (defined $user_ann_files{$src_file}) {
   1081                 die("File $src_file not opened in any of: @include_dirs\n");
   1082 
   1083             } else {
   1084                 push(@unfound_auto_annotate_files, $src_file);
   1085             }
   1086 
   1087         } else {
   1088             # File header (distinguish between user- and auto-selected files).
   1089             print("$fancy");
   1090             my $ann_type = 
   1091                 (defined $user_ann_files{$src_file} ? "User" : "Auto");
   1092             print("-- $ann_type-annotated source: $full_file_name\n");
   1093             print("$fancy");
   1094 
   1095             # Get file's CCs
   1096             my $src_file_CCs = $all_ind_CCs{$src_file};
   1097             if (!defined $src_file_CCs) {
   1098                 print("  No information has been collected for $src_file\n\n");
   1099                 next LOOP;
   1100             }
   1101         
   1102             $did_annotations = 1;
   1103             
   1104             # Numeric, not lexicographic sort!
   1105             my @line_nums = sort {$a <=> $b} keys %$src_file_CCs;  
   1106 
   1107             # If $src_file more recent than cachegrind.out, issue warning
   1108             my $src_more_recent_than_inputfile = 0;
   1109             if ((stat $opened_file)[9] > (stat $input_file)[9]) {
   1110                 $src_more_recent_than_inputfile = 1;
   1111                 warning_on_src_more_recent_than_inputfile($src_file);
   1112             }
   1113 
   1114             # Work out the size of each column for printing
   1115             my $CC_col_widths = compute_CC_col_widths(values %$src_file_CCs);
   1116 
   1117             # Events header
   1118             print_events($CC_col_widths);
   1119             print("\n\n");
   1120 
   1121             # Shift out 0 if it's in the line numbers (from unknown entries,
   1122             # likely due to bugs in Valgrind's stabs debug info reader)
   1123             shift(@line_nums) if (0 == $line_nums[0]);
   1124 
   1125             # Finds interesting line ranges -- all lines with a CC, and all
   1126             # lines within $context lines of a line with a CC.
   1127             my $n = @line_nums;
   1128             my @pairs;
   1129             for (my $i = 0; $i < $n; $i++) {
   1130                 push(@pairs, $line_nums[$i] - $context);   # lower marker
   1131                 while ($i < $n-1 && 
   1132                        $line_nums[$i] + 2*$context >= $line_nums[$i+1]) {
   1133                     $i++;
   1134                 }
   1135                 push(@pairs, $line_nums[$i] + $context);   # upper marker
   1136             }
   1137 
   1138             # Annotate chosen lines, tracking total counts of lines printed
   1139             $pairs[0] = 1 if ($pairs[0] < 1);
   1140             while (@pairs) {
   1141                 my $low  = shift @pairs;
   1142                 my $high = shift @pairs;
   1143                 while ($. < $low-1) {
   1144                     my $tmp = <INPUTFILE>;
   1145                     last unless (defined $tmp);     # hack to detect EOF
   1146                 }
   1147                 my $src_line;
   1148                 # Print line number, unless start of file
   1149                 print("-- line $low " . '-' x 40 . "\n") if ($low != 1);
   1150                 while (($. < $high) && ($src_line = <INPUTFILE>)) {
   1151                     if (defined $line_nums[0] && $. == $line_nums[0]) {
   1152                         print_CC($src_file_CCs->{$.}, $CC_col_widths);
   1153                         add_array_a_to_b($src_file_CCs->{$.}, 
   1154                                          $printed_totals_CC);
   1155                         shift(@line_nums);
   1156 
   1157                     } else {
   1158                         print_CC( [], $CC_col_widths);
   1159                     }
   1160 
   1161                     print(" $src_line");
   1162 
   1163 		    my $tmp  = $called_from_line->{$src_file,$.};
   1164 		    my $func = $func_of_line{$src_file,$.};
   1165 		    if (defined $tmp) {
   1166 		      foreach my $called (keys %$tmp) {
   1167 			if (defined $call_CCs{$func,$called,$.}) {
   1168 			  print_CC($call_CCs{$func,$called,$.}, $CC_col_widths);
   1169 			  print " => $called (";
   1170 			  print $call_counter{$func,$called,$.} . "x)\n";
   1171 			}
   1172 		      }
   1173 		    }
   1174                 }
   1175                 # Print line number, unless EOF
   1176                 if ($src_line) {
   1177                     print("-- line $high " . '-' x 40 . "\n");
   1178                 } else {
   1179                     last;
   1180                 }
   1181             }
   1182 
   1183             # If there was info on lines past the end of the file...
   1184             if (@line_nums) {
   1185                 foreach my $line_num (@line_nums) {
   1186                     print_CC($src_file_CCs->{$line_num}, $CC_col_widths);
   1187                     print(" <bogus line $line_num>\n");
   1188                 }
   1189                 print("\n");
   1190                 warning_on_nonexistent_lines($src_more_recent_than_inputfile,
   1191                                              $src_file, \@line_nums);
   1192             }
   1193             print("\n");
   1194 
   1195             # Print summary of counts attributed to file but not to any
   1196             # particular line (due to incomplete debug info).
   1197             if ($src_file_CCs->{0}) {
   1198                 print_CC($src_file_CCs->{0}, $CC_col_widths);
   1199                 print(" <counts for unidentified lines in $src_file>\n\n");
   1200             }
   1201             
   1202             close(INPUTFILE);
   1203         }
   1204     }
   1205 
   1206     # Print list of unfound auto-annotate selected files.
   1207     if (@unfound_auto_annotate_files) {
   1208         print("$fancy");
   1209         print("The following files chosen for auto-annotation could not be found:\n");
   1210         print($fancy);
   1211         foreach my $f (@unfound_auto_annotate_files) {
   1212             print("  $f\n");
   1213         }
   1214         print("\n");
   1215     }
   1216 
   1217     # If we did any annotating, print what proportion of events were covered by
   1218     # annotated lines above.
   1219     if ($did_annotations) {
   1220         my $percent_printed_CC;
   1221         foreach (my $i = 0; $i < @$summary_CC; $i++) {
   1222             # Some files (in particular the files produced by --xtree-memory)
   1223             # have non additive self costs, so have a special case for these
   1224             # to print all functions and also to avoid a division by 0.
   1225             if ($summary_CC->[$i] == 0
   1226                 || $printed_totals_CC->[$i] > $summary_CC->[$i]) {
   1227                 $percent_printed_CC->[$i] = "100";
   1228             } else {
   1229                 $percent_printed_CC->[$i] = 
   1230                     sprintf("%.0f", 
   1231                             $printed_totals_CC->[$i] / $summary_CC->[$i] * 100);
   1232             }
   1233         }
   1234         my $pp_CC_col_widths = compute_CC_col_widths($percent_printed_CC);
   1235         print($fancy);
   1236         print_events($pp_CC_col_widths);
   1237         print("\n");
   1238         print($fancy);
   1239         print_CC($percent_printed_CC, $pp_CC_col_widths);
   1240         print(" percentage of events annotated\n\n");
   1241     }
   1242 }
   1243 
   1244 #----------------------------------------------------------------------------
   1245 # "main()"
   1246 #----------------------------------------------------------------------------
   1247 process_cmd_line();
   1248 read_input_file();
   1249 print_options();
   1250 my $threshold_files = print_summary_and_fn_totals();
   1251 annotate_ann_files($threshold_files);
   1252 
   1253 ##--------------------------------------------------------------------##
   1254 ##--- end                                           vg_annotate.in ---##
   1255 ##--------------------------------------------------------------------##
   1256 
   1257 
   1258