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