2 ##--------------------------------------------------------------------##
3 ##--- Control supervision of applications run with callgrind ---##
4 ##--- callgrind_control ---##
5 ##--------------------------------------------------------------------##
7 # This file is part of Callgrind, a cache-simulator and call graph
8 # tracer built on Valgrind.
10 # Copyright (C) 2003-2017 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
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.
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.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, see <http://www.gnu.org/licenses/>.
26 # vgdb_exe will be set to a vgdb found 'near' the callgrind_control file
29 sub getCallgrindPids
{
32 open LIST
, $vgdb_exe . " $vgdbPrefixOption -l|";
34 if (/^use --pid=(\d+) for \S*?valgrind\s+(.*?)\s*$/) {
37 if (!($cmd =~ /--tool=callgrind/)) { next; }
38 while($cmd =~ s/^-+\S+\s+//) {}
39 $cmdline{$pid} = $cmd;
40 $cmd =~ s/^(\S*).*/$1/;
42 #print "Found PID $pid, cmd '$cmd{$pid}', cmdline '$cmdline{$pid}'.\n";
50 if ($headerPrinted) { return; }
53 print "Observe the status and control currently active callgrind runs.\n";
54 print "(C) 2003-2011, Josef Weidendorfer (Josef.Weidendorfer\@gmx.de)\n\n";
58 print "callgrind_control-@VERSION@\n";
63 print "See '$0 -h' for help.\n";
70 print "Usage: callgrind_control [options] [pid|program-name...]\n\n";
71 print "If no pids/names are given, an action is applied to all currently\n";
72 print "active Callgrind runs. Default action is printing short information.\n\n";
74 print " -h --help Show this help text\n";
75 print " --version Show version\n";
76 print " -s --stat Show statistics\n";
77 print " -b --back Show stack/back trace\n";
78 print " -e [<A>,...] Show event counters for <A>,... (default: all)\n";
79 print " --dump[=<s>] Request a dump optionally using <s> as description\n";
80 print " -z --zero Zero all event counters\n";
81 print " -k --kill Kill\n";
82 print " -i --instr=on|off Switch instrumentation state on/off\n";
83 print "Uncommon options:\n";
84 print " --vgdb-prefix=<prefix> Only provide this if the same was given to Valgrind\n";
91 # Parts more or less copied from cg_annotate (author: Nicholas Nethercote)
96 @events = split(/\s+/, $events);
99 foreach $event (@events) {
100 $events{$event} = $n;
104 foreach my $show_event (@show_events) {
105 (defined $events{$show_event}) or
106 print "Warning: Event `$show_event' is not being collected\n";
109 @show_events = @events;
112 foreach my $show_event (@show_events) {
113 push(@show_order, $events{$show_event});
120 return ($x > $y ?
$x : $y);
125 my @CC = (split /\s+/, $_[0]);
126 (@CC <= @events) or die("Line $.: too many event counts\n");
132 1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/);
136 sub compute_CC_col_widths
(@
)
139 my $CC_col_widths = [];
141 # Initialise with minimum widths (from event names)
142 foreach my $event (@events) {
143 push(@
$CC_col_widths, length($event));
146 # Find maximum width count for each column. @CC_col_width positions
147 # correspond to @CC positions.
148 foreach my $CC (@CCs) {
149 foreach my $i (0 .. scalar(@
$CC)-1) {
150 if (defined $CC->[$i]) {
151 # Find length, accounting for commas that will be added
152 my $length = length $CC->[$i];
153 my $clength = $length + int(($length - 1) / 3);
154 $CC_col_widths->[$i] = max
($CC_col_widths->[$i], $clength);
158 return $CC_col_widths;
161 # Print the CC with each column's size dictated by $CC_col_widths.
164 my ($CC, $CC_col_widths) = @_;
166 foreach my $i (@show_order) {
167 my $count = (defined $CC->[$i] ? commify
($CC->[$i]) : ".");
168 my $space = ' ' x
($CC_col_widths->[$i] - length($count));
169 print("$space$count ");
175 my ($CC_col_widths) = @_;
177 foreach my $i (@show_order) {
178 my $event = $events[$i];
179 my $event_width = length($event);
180 my $col_width = $CC_col_widths->[$i];
181 my $space = ' ' x
($col_width - $event_width);
182 print("$space$event ");
192 # Search the appropriate vgdb executable
193 my $controldir = dirname
(__FILE__
);
194 if (-x
$controldir . "/vgdb") {
195 # classical case: callgrind_control and vgdb from the install bin dir
196 $vgdb_exe = $controldir . "/vgdb";
197 } elsif (-x
$controldir . "/../coregrind/vgdb") {
198 # callgrind_control called from the callgrind tool source/build dir
199 $vgdb_exe = $controldir . "/../coregrind/vgdb";
201 # no idea. Use whatever vgdb found in PATH
204 # print "will use vgdb at [" . $vgdb_exe . "]\n";
206 # To find the list of active pids, we need to have
207 # the --vgdb-prefix option if given.
208 $vgdbPrefixOption = "";
209 foreach $arg (@ARGV) {
210 if ($arg =~ /^--vgdb-prefix=.*$/) {
211 $vgdbPrefixOption=$arg;
227 foreach $arg (@ARGV) {
229 if ($requestDump == 1) { $requestDump = 2; }
230 if ($requestEvents == 1) { $requestEvents = 2; }
232 if ($arg =~ /^(-h|--help)$/) {
235 elsif ($arg =~ /^--version$/) {
238 elsif ($arg =~ /^--vgdb-prefix=.*$/) {
239 # handled during the initial parsing.
242 elsif ($arg =~ /^-v$/) {
246 elsif ($arg =~ /^(-s|--stat)$/) {
250 elsif ($arg =~ /^(-b|--back)$/) {
254 elsif ($arg =~ /^-e$/) {
258 elsif ($arg =~ /^(-d|--dump)(|=.*)$/) {
261 $dumpHint = substr($2,1);
264 # take next argument as dump hint
269 elsif ($arg =~ /^(-z|--zero)$/) {
273 elsif ($arg =~ /^(-k|--kill)$/) {
277 elsif ($arg =~ /^(-i|--instr)(|=on|=off)$/) {
280 $switchInstrMode = "on";
282 elsif ($2 eq "=off") {
283 $switchInstrMode = "off";
286 # check next argument for "on" or "off"
292 print "Error: unknown command line option '$arg'.\n";
297 if ($arg =~ /^[A-Za-z_]/) {
298 # arguments of -d/-e/-i are non-numeric
299 if ($requestDump == 1) {
305 if ($requestEvents == 1) {
307 @show_events = split(/,/, $arg);
311 if ($switchInstr == 1) {
314 $switchInstrMode = "on";
316 elsif ($arg eq "off") {
317 $switchInstrMode = "off";
320 print "Error: need to specify 'on' or 'off' after '-i'.\n";
327 if (defined $cmd{$arg}) { $spids{$arg} = 1; next; }
330 if ($cmd{$p} =~ /$arg$/) {
335 if ($nameFound) { next; }
337 print "Error: Callgrind task with PID/name '$arg' not detected.\n";
342 if ($switchInstr == 1) {
343 print "Error: need to specify 'on' or 'off' after '-i'.\n";
347 if (scalar @pids == 0) {
348 print "No active callgrind runs detected.\n";
352 @spids = keys %spids;
353 if (scalar @spids >0) { @pids = @spids; }
358 $vgdbCommand = "dump";
359 if ($dumpHint ne "") { $vgdbCommand .= " ".$dumpHint; }
361 if ($requestZero) { $vgdbCommand = "zero"; }
362 if ($requestKill) { $vgdbCommand = "v.kill"; }
363 if ($switchInstr) { $vgdbCommand = "instrumentation ".$switchInstrMode; }
364 if ($printStatus || $printBacktrace || $requestEvents) {
365 $vgdbCommand = "status internal";
369 foreach $pid (@pids) {
370 $pidstr = "PID $pid: ";
371 if ($pid >0) { print $pidstr.$cmdline{$pid}; }
373 if ($vgdbCommand eq "") {
378 print " [requesting '$vgdbCommand']\n";
382 open RESULT
, $vgdb_exe . " $vgdbPrefixOption --pid=$pid $vgdbCommand|";
404 if (/function-(\d+)-(\d+): (.+)$/) {
411 $func{$ctid,$fcount{$ctid}} = $3;
413 elsif (/calls-(\d+)-(\d+): (.+)$/) {
414 if ($ctid != $1) { next; }
415 $calls{$ctid,$fcount{$ctid}} = $3;
417 elsif (/events-(\d+)-(\d+): (.+)$/) {
418 if ($ctid != $1) { next; }
419 $events{$ctid,$fcount{$ctid}} = line_to_CC
($3);
421 elsif (/events-(\d+): (.+)$/) {
422 if (scalar @events == 0) { next; }
423 $totals{$1} = line_to_CC
($2);
425 elsif (/executed-bbs: (\d+)/) { $exec_bbs = $1; }
426 elsif (/distinct-bbs: (\d+)/) { $dist_bbs = $1; }
427 elsif (/executed-calls: (\d+)/) { $exec_calls = $1; }
428 elsif (/distinct-calls: (\d+)/) { $dist_calls = $1; }
429 elsif (/distinct-functions: (\d+)/) { $dist_funcs = $1; }
430 elsif (/distinct-contexts: (\d+)/) { $dist_ctxs = $1; }
431 elsif (/events: (.+)$/) { $events = $1; prepareEvents
; }
432 elsif (/threads: (.+)$/) { $threads = $1; @threads = split " ", $threads; }
433 elsif (/instrumentation: (\w+)$/) { $instrumentation = $1; }
436 #if ($? ne "0") { print " Got Error $?\n"; }
437 if (!$waitForAnswer) { print " OK.\n"; next; }
439 if ($instrumentation eq "off") {
440 print " No information available as instrumentation is switched off.\n\n";
445 if ($requestEvents <1) {
446 print " Number of running threads: " .($#threads+1). ", thread IDs: $threads\n";
447 print " Events collected: $events\n";
450 print " Functions: ".commify
($dist_funcs);
451 print " (executed ".commify
($exec_calls);
452 print ", contexts ".commify
($dist_ctxs).")\n";
454 print " Basic blocks: ".commify
($dist_bbs);
455 print " (executed ".commify
($exec_bbs);
456 print ", call sites ".commify
($dist_calls).")\n";
459 if ($requestEvents >0) {
460 $totals_width = compute_CC_col_widths
(values %totals);
462 print_events
($totals_width);
464 foreach $tid (@tids) {
465 print " Th".substr(" ".$tid,-2)." ";
466 print_CC
($totals{$tid}, $totals_width);
471 if ($printBacktrace) {
473 if ($requestEvents >0) {
474 $totals_width = compute_CC_col_widths
(values %events);
477 foreach $tid (@tids) {
479 if ($requestEvents >0) {
480 print_events
($totals_width);
482 print "Backtrace for Thread $tid\n";
486 while($i>0 && $c<100) {
487 $fc = substr(" $c",-2);
489 if ($requestEvents >0) {
490 print_CC
($events{$tid,$i-1}, $totals_width);
492 print $func{$tid,$i};
494 print " (".$calls{$tid,$i-1}." x)";