blob: 4a526d97ef2da5191d73788d31b4adb792ccc97e [file] [log] [blame]
weidendoa17f2a32006-03-20 10:27:30 +00001#! /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#
weidendob934b192011-07-11 14:46:44 +000010# Copyright (C) 2003-2011 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
weidendoa17f2a32006-03-20 10:27:30 +000011#
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 = ();
weidendob934b192011-07-11 14:46:44 +000030 open LIST, "vgdb -l|";
31 while(<LIST>) {
weidendoe5704c92011-07-21 18:58:37 +000032 if (/^use --pid=(\d+) for \S*?valgrind\s+(.*?)\s*$/) {
weidendob934b192011-07-11 14:46:44 +000033 $pid = $1;
34 $cmd = $2;
35 if (!($cmd =~ /--tool=callgrind/)) { next; }
36 while($cmd =~ s/^-+\S+\s+//) {}
weidendoe5704c92011-07-21 18:58:37 +000037 $cmdline{$pid} = $cmd;
38 $cmd =~ s/^(\S*).*/$1/;
weidendob934b192011-07-11 14:46:44 +000039 $cmd{$pid} = $cmd;
weidendoe5704c92011-07-21 18:58:37 +000040 #print "Found PID $pid, cmd '$cmd{$pid}', cmdline '$cmdline{$pid}'.\n";
weidendob934b192011-07-11 14:46:44 +000041 push(@pids, $pid);
42 }
weidendoa17f2a32006-03-20 10:27:30 +000043 }
weidendob934b192011-07-11 14:46:44 +000044 close LIST;
weidendoa17f2a32006-03-20 10:27:30 +000045}
46
47sub printHeader {
48 if ($headerPrinted) { return; }
49 $headerPrinted = 1;
weidendoa17f2a32006-03-20 10:27:30 +000050
51 print "Observe the status and control currently active callgrind runs.\n";
weidendob934b192011-07-11 14:46:44 +000052 print "(C) 2003-2011, Josef Weidendorfer (Josef.Weidendorfer\@gmx.de)\n\n";
weidendoa17f2a32006-03-20 10:27:30 +000053}
54
55sub printVersion {
56 print "callgrind_control-@VERSION@\n";
57 exit;
58}
59
weidendo3e1f0492009-08-07 20:20:41 +000060sub shortHelp {
61 print "See '$0 -h' for help.\n";
62 exit;
63}
64
weidendoa17f2a32006-03-20 10:27:30 +000065sub printHelp {
66 printHeader;
67
njnab773b02009-08-09 23:27:00 +000068 print "Usage: callgrind_control [options] [pid|program-name...]\n\n";
weidendo9ff99f42009-08-07 23:19:09 +000069 print "If no pids/names are given, an action is applied to all currently\n";
weidendoa17f2a32006-03-20 10:27:30 +000070 print "active Callgrind runs. Default action is printing short information.\n\n";
71 print "Options:\n";
weidendob934b192011-07-11 14:46:44 +000072 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";
weidendoa17f2a32006-03-20 10:27:30 +000081 print "\n";
82 exit;
83}
84
85
86#
weidendob934b192011-07-11 14:46:44 +000087# Parts more or less copied from cg_annotate (author: Nicholas Nethercote)
weidendoa17f2a32006-03-20 10:27:30 +000088#
89
90sub prepareEvents {
91
92 @events = split(/\s+/, $events);
93 %events = ();
94 $n = 0;
95 foreach $event (@events) {
96 $events{$event} = $n;
97 $n++;
98 }
99 if (@show_events) {
100 foreach my $show_event (@show_events) {
101 (defined $events{$show_event}) or
102 print "Warning: Event `$show_event' is not being collected\n";
103 }
104 } else {
105 @show_events = @events;
106 }
107 @show_order = ();
108 foreach my $show_event (@show_events) {
109 push(@show_order, $events{$show_event});
110 }
111}
112
113sub max ($$)
114{
115 my ($x, $y) = @_;
116 return ($x > $y ? $x : $y);
117}
118
119sub line_to_CC ($)
120{
121 my @CC = (split /\s+/, $_[0]);
122 (@CC <= @events) or die("Line $.: too many event counts\n");
123 return \@CC;
124}
125
126sub commify ($) {
127 my ($val) = @_;
128 1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/);
129 return $val;
130}
131
132sub compute_CC_col_widths (@)
133{
134 my @CCs = @_;
135 my $CC_col_widths = [];
136
137 # Initialise with minimum widths (from event names)
138 foreach my $event (@events) {
139 push(@$CC_col_widths, length($event));
140 }
141
142 # Find maximum width count for each column. @CC_col_width positions
143 # correspond to @CC positions.
144 foreach my $CC (@CCs) {
145 foreach my $i (0 .. scalar(@$CC)-1) {
146 if (defined $CC->[$i]) {
147 # Find length, accounting for commas that will be added
148 my $length = length $CC->[$i];
149 my $clength = $length + int(($length - 1) / 3);
150 $CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength);
151 }
152 }
153 }
154 return $CC_col_widths;
155}
156
157# Print the CC with each column's size dictated by $CC_col_widths.
158sub print_CC ($$)
159{
160 my ($CC, $CC_col_widths) = @_;
161
162 foreach my $i (@show_order) {
163 my $count = (defined $CC->[$i] ? commify($CC->[$i]) : ".");
164 my $space = ' ' x ($CC_col_widths->[$i] - length($count));
165 print("$space$count ");
166 }
167}
168
169sub print_events ($)
170{
171 my ($CC_col_widths) = @_;
172
173 foreach my $i (@show_order) {
174 my $event = $events[$i];
175 my $event_width = length($event);
176 my $col_width = $CC_col_widths->[$i];
177 my $space = ' ' x ($col_width - $event_width);
178 print("$space$event ");
179 }
180}
181
182
183
184#
185# Main
186#
187
188getCallgrindPids;
189
190$requestEvents = 0;
191$requestDump = 0;
192$switchInstr = 0;
193$headerPrinted = 0;
weidendoa17f2a32006-03-20 10:27:30 +0000194$dumpHint = "";
weidendoce6489f2011-09-06 19:08:35 +0000195$verbose = 0;
weidendoa17f2a32006-03-20 10:27:30 +0000196
197%spids = ();
198foreach $arg (@ARGV) {
199 if ($arg =~ /^-/) {
200 if ($requestDump == 1) { $requestDump = 2; }
201 if ($requestEvents == 1) { $requestEvents = 2; }
weidendoa17f2a32006-03-20 10:27:30 +0000202
weidendo9ff99f42009-08-07 23:19:09 +0000203 if ($arg =~ /^(-h|--help)$/) {
204 printHelp;
205 }
206 elsif ($arg =~ /^--version$/) {
207 printVersion;
208 }
weidendoce6489f2011-09-06 19:08:35 +0000209 elsif ($arg =~ /^-v$/) {
210 $verbose++;
211 next;
212 }
weidendo9ff99f42009-08-07 23:19:09 +0000213 elsif ($arg =~ /^(-s|--stat)$/) {
214 $printStatus = 1;
215 next;
216 }
217 elsif ($arg =~ /^(-b|--back)$/) {
218 $printBacktrace = 1;
219 next;
220 }
221 elsif ($arg =~ /^-e$/) {
222 $requestEvents = 1;
223 next;
224 }
225 elsif ($arg =~ /^(-d|--dump)(|=.*)$/) {
226 if ($2 ne "") {
227 $requestDump = 2;
228 $dumpHint = substr($2,1);
229 }
230 else {
231 # take next argument as dump hint
232 $requestDump = 1;
233 }
234 next;
235 }
236 elsif ($arg =~ /^(-z|--zero)$/) {
237 $requestZero = 1;
238 next;
239 }
240 elsif ($arg =~ /^(-k|--kill)$/) {
241 $requestKill = 1;
242 next;
243 }
244 elsif ($arg =~ /^(-i|--instr)(|=on|=off)$/) {
245 $switchInstr = 2;
246 if ($2 eq "=on") {
weidendob934b192011-07-11 14:46:44 +0000247 $switchInstrMode = "on";
weidendo9ff99f42009-08-07 23:19:09 +0000248 }
249 elsif ($2 eq "=off") {
weidendob934b192011-07-11 14:46:44 +0000250 $switchInstrMode = "off";
weidendo9ff99f42009-08-07 23:19:09 +0000251 }
252 else {
253 # check next argument for "on" or "off"
254 $switchInstr = 1;
255 }
256 next;
257 }
weidendo9ff99f42009-08-07 23:19:09 +0000258 else {
259 print "Error: unknown command line option '$arg'.\n";
260 shortHelp;
261 }
weidendoa17f2a32006-03-20 10:27:30 +0000262 }
263
264 if ($arg =~ /^[A-Za-z_]/) {
265 # arguments of -d/-e/-i are non-numeric
266 if ($requestDump == 1) {
267 $requestDump = 2;
268 $dumpHint = $arg;
269 next;
270 }
271
272 if ($requestEvents == 1) {
273 $requestEvents = 2;
274 @show_events = split(/,/, $arg);
275 next;
276 }
277
278 if ($switchInstr == 1) {
279 $switchInstr = 2;
weidendo9ff99f42009-08-07 23:19:09 +0000280 if ($arg eq "on") {
weidendob934b192011-07-11 14:46:44 +0000281 $switchInstrMode = "on";
weidendo9ff99f42009-08-07 23:19:09 +0000282 }
283 elsif ($arg eq "off") {
weidendob934b192011-07-11 14:46:44 +0000284 $switchInstrMode = "off";
weidendo9ff99f42009-08-07 23:19:09 +0000285 }
286 else {
287 print "Error: need to specify 'on' or 'off' after '-i'.\n";
288 shortHelp;
weidendoa17f2a32006-03-20 10:27:30 +0000289 }
290 next;
291 }
292 }
293
weidendoa17f2a32006-03-20 10:27:30 +0000294 if (defined $cmd{$arg}) { $spids{$arg} = 1; next; }
295 $nameFound = 0;
296 foreach $p (@pids) {
weidendoe5704c92011-07-21 18:58:37 +0000297 if ($cmd{$p} =~ /$arg$/) {
weidendoa17f2a32006-03-20 10:27:30 +0000298 $nameFound = 1;
299 $spids{$p} = 1;
300 }
301 }
302 if ($nameFound) { next; }
303
weidendo3e1f0492009-08-07 20:20:41 +0000304 print "Error: Callgrind task with PID/name '$arg' not detected.\n";
305 shortHelp;
306}
307
weidendo9ff99f42009-08-07 23:19:09 +0000308
309if ($switchInstr == 1) {
310 print "Error: need to specify 'on' or 'off' after '-i'.\n";
weidendo3e1f0492009-08-07 20:20:41 +0000311 shortHelp;
weidendoa17f2a32006-03-20 10:27:30 +0000312}
313
weidendoa17f2a32006-03-20 10:27:30 +0000314if (scalar @pids == 0) {
315 print "No active callgrind runs detected.\n";
weidendoa17f2a32006-03-20 10:27:30 +0000316 exit;
317}
318
319@spids = keys %spids;
320if (scalar @spids >0) { @pids = @spids; }
321
weidendob934b192011-07-11 14:46:44 +0000322$vgdbCommand = "";
weidendoa17f2a32006-03-20 10:27:30 +0000323$waitForAnswer = 0;
324if ($requestDump) {
weidendob934b192011-07-11 14:46:44 +0000325 $vgdbCommand = "dump";
326 if ($dumpHint ne "") { $vgdbCommand .= " ".$dumpHint; }
weidendoa17f2a32006-03-20 10:27:30 +0000327}
weidendob934b192011-07-11 14:46:44 +0000328if ($requestZero) { $vgdbCommand = "zero"; }
329if ($requestKill) { $vgdbCommand = "v.kill"; }
330if ($switchInstr) { $vgdbCommand = "instrumentation ".$switchInstrMode; }
weidendoa17f2a32006-03-20 10:27:30 +0000331if ($printStatus || $printBacktrace || $requestEvents) {
weidendoce6489f2011-09-06 19:08:35 +0000332 $vgdbCommand = "status internal";
weidendoa17f2a32006-03-20 10:27:30 +0000333 $waitForAnswer = 1;
334}
335
336foreach $pid (@pids) {
337 $pidstr = "PID $pid: ";
weidendoe5704c92011-07-21 18:58:37 +0000338 if ($pid >0) { print $pidstr.$cmdline{$pid}; }
weidendoa17f2a32006-03-20 10:27:30 +0000339
weidendob934b192011-07-11 14:46:44 +0000340 if ($vgdbCommand eq "") {
weidendoa17f2a32006-03-20 10:27:30 +0000341 print "\n";
weidendoa17f2a32006-03-20 10:27:30 +0000342 next;
weidendoa17f2a32006-03-20 10:27:30 +0000343 }
weidendoce6489f2011-09-06 19:08:35 +0000344 if ($verbose>0) {
345 print " [requesting '$vgdbCommand']\n";
346 } else {
347 print "\n";
348 }
weidendob934b192011-07-11 14:46:44 +0000349 open RESULT, "vgdb --pid=$pid $vgdbCommand|";
weidendoa17f2a32006-03-20 10:27:30 +0000350
351 @tids = ();
352 $ctid = 0;
353 %fcount = ();
354 %func = ();
355 %calls = ();
356 %events = ();
357 @events = ();
weidendo6faa99e2007-02-16 21:55:25 +0000358 @threads = ();
weidendoa17f2a32006-03-20 10:27:30 +0000359 %totals = ();
360
361 $exec_bbs = 0;
362 $dist_bbs = 0;
363 $exec_calls = 0;
364 $dist_calls = 0;
365 $dist_ctxs = 0;
366 $dist_funcs = 0;
weidendo6faa99e2007-02-16 21:55:25 +0000367 $threads = "";
weidendoa17f2a32006-03-20 10:27:30 +0000368 $events = "";
369
370 while(<RESULT>) {
371 if (/function-(\d+)-(\d+): (.+)$/) {
372 if ($ctid != $1) {
373 $ctid = $1;
374 push(@tids, $ctid);
375 $fcount{$ctid} = 0;
376 }
377 $fcount{$ctid}++;
378 $func{$ctid,$fcount{$ctid}} = $3;
379 }
380 elsif (/calls-(\d+)-(\d+): (.+)$/) {
381 if ($ctid != $1) { next; }
382 $calls{$ctid,$fcount{$ctid}} = $3;
383 }
384 elsif (/events-(\d+)-(\d+): (.+)$/) {
385 if ($ctid != $1) { next; }
386 $events{$ctid,$fcount{$ctid}} = line_to_CC($3);
387 }
388 elsif (/events-(\d+): (.+)$/) {
389 if (scalar @events == 0) { next; }
390 $totals{$1} = line_to_CC($2);
391 }
392 elsif (/executed-bbs: (\d+)/) { $exec_bbs = $1; }
393 elsif (/distinct-bbs: (\d+)/) { $dist_bbs = $1; }
394 elsif (/executed-calls: (\d+)/) { $exec_calls = $1; }
395 elsif (/distinct-calls: (\d+)/) { $dist_calls = $1; }
396 elsif (/distinct-functions: (\d+)/) { $dist_funcs = $1; }
397 elsif (/distinct-contexts: (\d+)/) { $dist_ctxs = $1; }
398 elsif (/events: (.+)$/) { $events = $1; prepareEvents; }
weidendo6faa99e2007-02-16 21:55:25 +0000399 elsif (/threads: (.+)$/) { $threads = $1; @threads = split " ", $threads; }
weidendoa17f2a32006-03-20 10:27:30 +0000400 elsif (/instrumentation: (\w+)$/) { $instrumentation = $1; }
401 }
402
weidendob934b192011-07-11 14:46:44 +0000403 #if ($? ne "0") { print " Got Error $?\n"; }
404 if (!$waitForAnswer) { print " OK.\n"; next; }
weidendoa17f2a32006-03-20 10:27:30 +0000405
406 if ($instrumentation eq "off") {
407 print " No information available as instrumentation is switched off.\n\n";
408 exit;
409 }
410
411 if ($printStatus) {
412 if ($requestEvents <1) {
weidendo6faa99e2007-02-16 21:55:25 +0000413 print " Number of running threads: " .($#threads+1). ", thread IDs: $threads\n";
weidendoa17f2a32006-03-20 10:27:30 +0000414 print " Events collected: $events\n";
415 }
416
417 print " Functions: ".commify($dist_funcs);
418 print " (executed ".commify($exec_calls);
419 print ", contexts ".commify($dist_ctxs).")\n";
420
421 print " Basic blocks: ".commify($dist_bbs);
422 print " (executed ".commify($exec_bbs);
423 print ", call sites ".commify($dist_calls).")\n";
424 }
425
426 if ($requestEvents >0) {
427 $totals_width = compute_CC_col_widths(values %totals);
428 print "\n Totals:";
429 print_events($totals_width);
430 print("\n");
431 foreach $tid (@tids) {
432 print " Th".substr(" ".$tid,-2)." ";
433 print_CC($totals{$tid}, $totals_width);
434 print("\n");
435 }
436 }
437
438 if ($printBacktrace) {
439
440 if ($requestEvents >0) {
441 $totals_width = compute_CC_col_widths(values %events);
442 }
443
444 foreach $tid (@tids) {
445 print "\n Frame: ";
446 if ($requestEvents >0) {
447 print_events($totals_width);
448 }
449 print "Backtrace for Thread $tid\n";
450
451 $i = $fcount{$tid};
452 $c = 0;
453 while($i>0 && $c<100) {
454 $fc = substr(" $c",-2);
455 print " [$fc] ";
456 if ($requestEvents >0) {
457 print_CC($events{$tid,$i-1}, $totals_width);
458 }
459 print $func{$tid,$i};
460 if ($i > 1) {
461 print " (".$calls{$tid,$i-1}." x)";
462 }
463 print "\n";
464 $i--;
465 $c++;
466 }
467 print "\n";
468 }
469 }
470 print "\n";
471}
472