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