1#! /usr/bin/perl -w 2##--------------------------------------------------------------------## 3##--- Control supervision of applications run with callgrind ---## 4##--- callgrind_control ---## 5##--------------------------------------------------------------------## 6 7# This file is part of Callgrind, a cache-simulator and call graph 8# tracer built on Valgrind. 9# 10# Copyright (C) 2003-2013 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 11# 12# This program is free software; you can redistribute it and/or 13# modify it under the terms of the GNU General Public License as 14# published by the Free Software Foundation; either version 2 of the 15# License, or (at your option) any later version. 16# 17# This program is distributed in the hope that it will be useful, but 18# WITHOUT ANY WARRANTY; without even the implied warranty of 19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20# General Public License for more details. 21# 22# You should have received a copy of the GNU General Public License 23# along with this program; if not, write to the Free Software 24# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 25# 02111-1307, USA. 26 27sub getCallgrindPids { 28 29 @pids = (); 30 open LIST, "vgdb $vgdbPrefixOption -l|"; 31 while(<LIST>) { 32 if (/^use --pid=(\d+) for \S*?valgrind\s+(.*?)\s*$/) { 33 $pid = $1; 34 $cmd = $2; 35 if (!($cmd =~ /--tool=callgrind/)) { next; } 36 while($cmd =~ s/^-+\S+\s+//) {} 37 $cmdline{$pid} = $cmd; 38 $cmd =~ s/^(\S*).*/$1/; 39 $cmd{$pid} = $cmd; 40 #print "Found PID $pid, cmd '$cmd{$pid}', cmdline '$cmdline{$pid}'.\n"; 41 push(@pids, $pid); 42 } 43 } 44 close LIST; 45} 46 47sub printHeader { 48 if ($headerPrinted) { return; } 49 $headerPrinted = 1; 50 51 print "Observe the status and control currently active callgrind runs.\n"; 52 print "(C) 2003-2011, Josef Weidendorfer (Josef.Weidendorfer\@gmx.de)\n\n"; 53} 54 55sub printVersion { 56 print "callgrind_control-@VERSION@\n"; 57 exit; 58} 59 60sub shortHelp { 61 print "See '$0 -h' for help.\n"; 62 exit; 63} 64 65sub printHelp { 66 printHeader; 67 68 print "Usage: callgrind_control [options] [pid|program-name...]\n\n"; 69 print "If no pids/names are given, an action is applied to all currently\n"; 70 print "active Callgrind runs. Default action is printing short information.\n\n"; 71 print "Options:\n"; 72 print " -h --help Show this help text\n"; 73 print " --version Show version\n"; 74 print " -s --stat Show statistics\n"; 75 print " -b --back Show stack/back trace\n"; 76 print " -e [<A>,...] Show event counters for <A>,... (default: all)\n"; 77 print " --dump[=<s>] Request a dump optionally using <s> as description\n"; 78 print " -z --zero Zero all event counters\n"; 79 print " -k --kill Kill\n"; 80 print " -i --instr=on|off Switch instrumentation state on/off\n"; 81 print "Uncommon options:\n"; 82 print " --vgdb-prefix=<prefix> Only provide this if the same was given to Valgrind\n"; 83 print "\n"; 84 exit; 85} 86 87 88# 89# Parts more or less copied from cg_annotate (author: Nicholas Nethercote) 90# 91 92sub prepareEvents { 93 94 @events = split(/\s+/, $events); 95 %events = (); 96 $n = 0; 97 foreach $event (@events) { 98 $events{$event} = $n; 99 $n++; 100 } 101 if (@show_events) { 102 foreach my $show_event (@show_events) { 103 (defined $events{$show_event}) or 104 print "Warning: Event `$show_event' is not being collected\n"; 105 } 106 } else { 107 @show_events = @events; 108 } 109 @show_order = (); 110 foreach my $show_event (@show_events) { 111 push(@show_order, $events{$show_event}); 112 } 113} 114 115sub max ($$) 116{ 117 my ($x, $y) = @_; 118 return ($x > $y ? $x : $y); 119} 120 121sub line_to_CC ($) 122{ 123 my @CC = (split /\s+/, $_[0]); 124 (@CC <= @events) or die("Line $.: too many event counts\n"); 125 return \@CC; 126} 127 128sub commify ($) { 129 my ($val) = @_; 130 1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/); 131 return $val; 132} 133 134sub compute_CC_col_widths (@) 135{ 136 my @CCs = @_; 137 my $CC_col_widths = []; 138 139 # Initialise with minimum widths (from event names) 140 foreach my $event (@events) { 141 push(@$CC_col_widths, length($event)); 142 } 143 144 # Find maximum width count for each column. @CC_col_width positions 145 # correspond to @CC positions. 146 foreach my $CC (@CCs) { 147 foreach my $i (0 .. scalar(@$CC)-1) { 148 if (defined $CC->[$i]) { 149 # Find length, accounting for commas that will be added 150 my $length = length $CC->[$i]; 151 my $clength = $length + int(($length - 1) / 3); 152 $CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength); 153 } 154 } 155 } 156 return $CC_col_widths; 157} 158 159# Print the CC with each column's size dictated by $CC_col_widths. 160sub print_CC ($$) 161{ 162 my ($CC, $CC_col_widths) = @_; 163 164 foreach my $i (@show_order) { 165 my $count = (defined $CC->[$i] ? commify($CC->[$i]) : "."); 166 my $space = ' ' x ($CC_col_widths->[$i] - length($count)); 167 print("$space$count "); 168 } 169} 170 171sub print_events ($) 172{ 173 my ($CC_col_widths) = @_; 174 175 foreach my $i (@show_order) { 176 my $event = $events[$i]; 177 my $event_width = length($event); 178 my $col_width = $CC_col_widths->[$i]; 179 my $space = ' ' x ($col_width - $event_width); 180 print("$space$event "); 181 } 182} 183 184 185 186# 187# Main 188# 189 190# To find the list of active pids, we need to have 191# the --vgdb-prefix option if given. 192$vgdbPrefixOption = ""; 193foreach $arg (@ARGV) { 194 if ($arg =~ /^--vgdb-prefix=.*$/) { 195 $vgdbPrefixOption=$arg; 196 } 197 next; 198} 199 200getCallgrindPids; 201 202$requestEvents = 0; 203$requestDump = 0; 204$switchInstr = 0; 205$headerPrinted = 0; 206$dumpHint = ""; 207 208$verbose = 0; 209 210%spids = (); 211foreach $arg (@ARGV) { 212 if ($arg =~ /^-/) { 213 if ($requestDump == 1) { $requestDump = 2; } 214 if ($requestEvents == 1) { $requestEvents = 2; } 215 216 if ($arg =~ /^(-h|--help)$/) { 217 printHelp; 218 } 219 elsif ($arg =~ /^--version$/) { 220 printVersion; 221 } 222 elsif ($arg =~ /^--vgdb-prefix=.*$/) { 223 # handled during the initial parsing. 224 next; 225 } 226 elsif ($arg =~ /^-v$/) { 227 $verbose++; 228 next; 229 } 230 elsif ($arg =~ /^(-s|--stat)$/) { 231 $printStatus = 1; 232 next; 233 } 234 elsif ($arg =~ /^(-b|--back)$/) { 235 $printBacktrace = 1; 236 next; 237 } 238 elsif ($arg =~ /^-e$/) { 239 $requestEvents = 1; 240 next; 241 } 242 elsif ($arg =~ /^(-d|--dump)(|=.*)$/) { 243 if ($2 ne "") { 244 $requestDump = 2; 245 $dumpHint = substr($2,1); 246 } 247 else { 248 # take next argument as dump hint 249 $requestDump = 1; 250 } 251 next; 252 } 253 elsif ($arg =~ /^(-z|--zero)$/) { 254 $requestZero = 1; 255 next; 256 } 257 elsif ($arg =~ /^(-k|--kill)$/) { 258 $requestKill = 1; 259 next; 260 } 261 elsif ($arg =~ /^(-i|--instr)(|=on|=off)$/) { 262 $switchInstr = 2; 263 if ($2 eq "=on") { 264 $switchInstrMode = "on"; 265 } 266 elsif ($2 eq "=off") { 267 $switchInstrMode = "off"; 268 } 269 else { 270 # check next argument for "on" or "off" 271 $switchInstr = 1; 272 } 273 next; 274 } 275 else { 276 print "Error: unknown command line option '$arg'.\n"; 277 shortHelp; 278 } 279 } 280 281 if ($arg =~ /^[A-Za-z_]/) { 282 # arguments of -d/-e/-i are non-numeric 283 if ($requestDump == 1) { 284 $requestDump = 2; 285 $dumpHint = $arg; 286 next; 287 } 288 289 if ($requestEvents == 1) { 290 $requestEvents = 2; 291 @show_events = split(/,/, $arg); 292 next; 293 } 294 295 if ($switchInstr == 1) { 296 $switchInstr = 2; 297 if ($arg eq "on") { 298 $switchInstrMode = "on"; 299 } 300 elsif ($arg eq "off") { 301 $switchInstrMode = "off"; 302 } 303 else { 304 print "Error: need to specify 'on' or 'off' after '-i'.\n"; 305 shortHelp; 306 } 307 next; 308 } 309 } 310 311 if (defined $cmd{$arg}) { $spids{$arg} = 1; next; } 312 $nameFound = 0; 313 foreach $p (@pids) { 314 if ($cmd{$p} =~ /$arg$/) { 315 $nameFound = 1; 316 $spids{$p} = 1; 317 } 318 } 319 if ($nameFound) { next; } 320 321 print "Error: Callgrind task with PID/name '$arg' not detected.\n"; 322 shortHelp; 323} 324 325 326if ($switchInstr == 1) { 327 print "Error: need to specify 'on' or 'off' after '-i'.\n"; 328 shortHelp; 329} 330 331if (scalar @pids == 0) { 332 print "No active callgrind runs detected.\n"; 333 exit; 334} 335 336@spids = keys %spids; 337if (scalar @spids >0) { @pids = @spids; } 338 339$vgdbCommand = ""; 340$waitForAnswer = 0; 341if ($requestDump) { 342 $vgdbCommand = "dump"; 343 if ($dumpHint ne "") { $vgdbCommand .= " ".$dumpHint; } 344} 345if ($requestZero) { $vgdbCommand = "zero"; } 346if ($requestKill) { $vgdbCommand = "v.kill"; } 347if ($switchInstr) { $vgdbCommand = "instrumentation ".$switchInstrMode; } 348if ($printStatus || $printBacktrace || $requestEvents) { 349 $vgdbCommand = "status internal"; 350 $waitForAnswer = 1; 351} 352 353foreach $pid (@pids) { 354 $pidstr = "PID $pid: "; 355 if ($pid >0) { print $pidstr.$cmdline{$pid}; } 356 357 if ($vgdbCommand eq "") { 358 print "\n"; 359 next; 360 } 361 if ($verbose>0) { 362 print " [requesting '$vgdbCommand']\n"; 363 } else { 364 print "\n"; 365 } 366 open RESULT, "vgdb $vgdbPrefixOption --pid=$pid $vgdbCommand|"; 367 368 @tids = (); 369 $ctid = 0; 370 %fcount = (); 371 %func = (); 372 %calls = (); 373 %events = (); 374 @events = (); 375 @threads = (); 376 %totals = (); 377 378 $exec_bbs = 0; 379 $dist_bbs = 0; 380 $exec_calls = 0; 381 $dist_calls = 0; 382 $dist_ctxs = 0; 383 $dist_funcs = 0; 384 $threads = ""; 385 $events = ""; 386 387 while(<RESULT>) { 388 if (/function-(\d+)-(\d+): (.+)$/) { 389 if ($ctid != $1) { 390 $ctid = $1; 391 push(@tids, $ctid); 392 $fcount{$ctid} = 0; 393 } 394 $fcount{$ctid}++; 395 $func{$ctid,$fcount{$ctid}} = $3; 396 } 397 elsif (/calls-(\d+)-(\d+): (.+)$/) { 398 if ($ctid != $1) { next; } 399 $calls{$ctid,$fcount{$ctid}} = $3; 400 } 401 elsif (/events-(\d+)-(\d+): (.+)$/) { 402 if ($ctid != $1) { next; } 403 $events{$ctid,$fcount{$ctid}} = line_to_CC($3); 404 } 405 elsif (/events-(\d+): (.+)$/) { 406 if (scalar @events == 0) { next; } 407 $totals{$1} = line_to_CC($2); 408 } 409 elsif (/executed-bbs: (\d+)/) { $exec_bbs = $1; } 410 elsif (/distinct-bbs: (\d+)/) { $dist_bbs = $1; } 411 elsif (/executed-calls: (\d+)/) { $exec_calls = $1; } 412 elsif (/distinct-calls: (\d+)/) { $dist_calls = $1; } 413 elsif (/distinct-functions: (\d+)/) { $dist_funcs = $1; } 414 elsif (/distinct-contexts: (\d+)/) { $dist_ctxs = $1; } 415 elsif (/events: (.+)$/) { $events = $1; prepareEvents; } 416 elsif (/threads: (.+)$/) { $threads = $1; @threads = split " ", $threads; } 417 elsif (/instrumentation: (\w+)$/) { $instrumentation = $1; } 418 } 419 420 #if ($? ne "0") { print " Got Error $?\n"; } 421 if (!$waitForAnswer) { print " OK.\n"; next; } 422 423 if ($instrumentation eq "off") { 424 print " No information available as instrumentation is switched off.\n\n"; 425 exit; 426 } 427 428 if ($printStatus) { 429 if ($requestEvents <1) { 430 print " Number of running threads: " .($#threads+1). ", thread IDs: $threads\n"; 431 print " Events collected: $events\n"; 432 } 433 434 print " Functions: ".commify($dist_funcs); 435 print " (executed ".commify($exec_calls); 436 print ", contexts ".commify($dist_ctxs).")\n"; 437 438 print " Basic blocks: ".commify($dist_bbs); 439 print " (executed ".commify($exec_bbs); 440 print ", call sites ".commify($dist_calls).")\n"; 441 } 442 443 if ($requestEvents >0) { 444 $totals_width = compute_CC_col_widths(values %totals); 445 print "\n Totals:"; 446 print_events($totals_width); 447 print("\n"); 448 foreach $tid (@tids) { 449 print " Th".substr(" ".$tid,-2)." "; 450 print_CC($totals{$tid}, $totals_width); 451 print("\n"); 452 } 453 } 454 455 if ($printBacktrace) { 456 457 if ($requestEvents >0) { 458 $totals_width = compute_CC_col_widths(values %events); 459 } 460 461 foreach $tid (@tids) { 462 print "\n Frame: "; 463 if ($requestEvents >0) { 464 print_events($totals_width); 465 } 466 print "Backtrace for Thread $tid\n"; 467 468 $i = $fcount{$tid}; 469 $c = 0; 470 while($i>0 && $c<100) { 471 $fc = substr(" $c",-2); 472 print " [$fc] "; 473 if ($requestEvents >0) { 474 print_CC($events{$tid,$i-1}, $totals_width); 475 } 476 print $func{$tid,$i}; 477 if ($i > 1) { 478 print " (".$calls{$tid,$i-1}." x)"; 479 } 480 print "\n"; 481 $i--; 482 $c++; 483 } 484 print "\n"; 485 } 486 } 487 print "\n"; 488} 489 490