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