blob: f32a75dfb8a49f28c0d74ffe9546817c51f4e513 [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#
10# Copyright (C) 2003,2004,2005 Josef Weidendorfer
11# Josef.Weidendorfer@gmx.de
12#
13# This program is free software; you can redistribute it and/or
14# modify it under the terms of the GNU General Public License as
15# published by the Free Software Foundation; either version 2 of the
16# License, or (at your option) any later version.
17#
18# This program is distributed in the hope that it will be useful, but
19# WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21# General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program; if not, write to the Free Software
25# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26# 02111-1307, USA.
27
28sub getCallgrindPids {
29
30 @pids = ();
31 foreach $f (</tmp/callgrind.info.*>) {
32 ($pid) = ($f =~ /info\.(\d+)/);
33 if ($pid eq "") { next; }
34 $mapfile = "/proc/$pid/maps";
35 if (!-e $mapfile) { next; }
36
37 open MAP, "<$mapfile";
38 $found = 0;
39 while(<MAP>) {
weidendo4e28a852006-04-21 00:58:58 +000040 # works both for VG 3.0 and VG 3.1+
weidendoa17f2a32006-03-20 10:27:30 +000041 if (/callgrind/) { $found = 1; }
42 }
43 close MAP;
44 if ($found == 0) { next; }
45
weidendo73903ee2007-09-18 19:12:57 +000046 $res = open INFO, "<$f";
47 if (!$res) { next; }
weidendoa17f2a32006-03-20 10:27:30 +000048 while(<INFO>) {
49 if (/version: (\d+)/) { $mversion{$pid} = $1; }
50 if (/cmd: (.+)$/) { $cmd{$pid} = $1; }
51 if (/control: (.+)$/) { $control{$pid} = $1; }
52 if (/base: (.+)$/) { $base{$pid} = $1; }
53 if (/result: (.+)$/) { $result{$pid} = $1; }
54 }
55 close INFO;
56
57 if ($mversion{$pid} > 1) {
weidendo5b409942006-05-01 01:38:32 +000058 print " PID $pid: Unsupported command interface (version $mversion{$pid}) ?!\n\n";
weidendoa17f2a32006-03-20 10:27:30 +000059 next;
60 }
61
62 push(@pids, $pid);
63 }
64}
65
66sub printHeader {
67 if ($headerPrinted) { return; }
68 $headerPrinted = 1;
69 if ($beQuiet) { return; }
70
71 print "Observe the status and control currently active callgrind runs.\n";
72 print "(C) 2003-2005, Josef Weidendorfer (Josef.Weidendorfer\@gmx.de)\n\n";
73}
74
75sub printVersion {
76 print "callgrind_control-@VERSION@\n";
77 exit;
78}
79
80sub printHelp {
81 printHeader;
82
83 print "Usage: callgrind_control [options] [ <PID>|<Name> ...]\n\n";
84 print "If no PIDs/Names are given, an action is applied to all currently\n";
85 print "active Callgrind runs. Default action is printing short information.\n\n";
86 print "Options:\n";
87 print " -h Print this help text\n";
88 print " -v Print version\n";
89 print " -q Be quiet\n";
90 print " -l Print more information\n";
91 print " -s Print status information\n";
92 print " -b Print backtrace information\n";
93 print " -e [A,..] Print event counters for A,.. [default: all]\n";
94 print " -d [str] Request a profile dump, include <str> as trigger hint\n";
95 print " -z Zero all cost counters\n";
96 print " -k Kill\n";
97 print " -i on/off Switch instrumentation state on/off\n";
98 print " -w <dir> Manually specify the working directory of a callgrind run\n";
99 print "\n";
100 exit;
101}
102
103
104#
105# Parts more or less copied from ct_annotate (author: Nicholas Nethercote)
106#
107
108sub prepareEvents {
109
110 @events = split(/\s+/, $events);
111 %events = ();
112 $n = 0;
113 foreach $event (@events) {
114 $events{$event} = $n;
115 $n++;
116 }
117 if (@show_events) {
118 foreach my $show_event (@show_events) {
119 (defined $events{$show_event}) or
120 print "Warning: Event `$show_event' is not being collected\n";
121 }
122 } else {
123 @show_events = @events;
124 }
125 @show_order = ();
126 foreach my $show_event (@show_events) {
127 push(@show_order, $events{$show_event});
128 }
129}
130
131sub max ($$)
132{
133 my ($x, $y) = @_;
134 return ($x > $y ? $x : $y);
135}
136
137sub line_to_CC ($)
138{
139 my @CC = (split /\s+/, $_[0]);
140 (@CC <= @events) or die("Line $.: too many event counts\n");
141 return \@CC;
142}
143
144sub commify ($) {
145 my ($val) = @_;
146 1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/);
147 return $val;
148}
149
150sub compute_CC_col_widths (@)
151{
152 my @CCs = @_;
153 my $CC_col_widths = [];
154
155 # Initialise with minimum widths (from event names)
156 foreach my $event (@events) {
157 push(@$CC_col_widths, length($event));
158 }
159
160 # Find maximum width count for each column. @CC_col_width positions
161 # correspond to @CC positions.
162 foreach my $CC (@CCs) {
163 foreach my $i (0 .. scalar(@$CC)-1) {
164 if (defined $CC->[$i]) {
165 # Find length, accounting for commas that will be added
166 my $length = length $CC->[$i];
167 my $clength = $length + int(($length - 1) / 3);
168 $CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength);
169 }
170 }
171 }
172 return $CC_col_widths;
173}
174
175# Print the CC with each column's size dictated by $CC_col_widths.
176sub print_CC ($$)
177{
178 my ($CC, $CC_col_widths) = @_;
179
180 foreach my $i (@show_order) {
181 my $count = (defined $CC->[$i] ? commify($CC->[$i]) : ".");
182 my $space = ' ' x ($CC_col_widths->[$i] - length($count));
183 print("$space$count ");
184 }
185}
186
187sub print_events ($)
188{
189 my ($CC_col_widths) = @_;
190
191 foreach my $i (@show_order) {
192 my $event = $events[$i];
193 my $event_width = length($event);
194 my $col_width = $CC_col_widths->[$i];
195 my $space = ' ' x ($col_width - $event_width);
196 print("$space$event ");
197 }
198}
199
200
201
202#
203# Main
204#
205
206getCallgrindPids;
207
208$requestEvents = 0;
209$requestDump = 0;
210$switchInstr = 0;
211$headerPrinted = 0;
212$beQuiet = 0;
213$dumpHint = "";
214$gotW = 0;
215$workingDir = "";
216
217%spids = ();
218foreach $arg (@ARGV) {
219 if ($arg =~ /^-/) {
220 if ($requestDump == 1) { $requestDump = 2; }
221 if ($requestEvents == 1) { $requestEvents = 2; }
222 if ($gotW == 1) { $gotW = 2; }
223
224 if ($arg =~ /^-?-h/) { printHelp; }
225 if ($arg =~ /^-?-v/) { printVersion; }
226 if ($arg =~ /^-q/) { $beQuiet = 1; next; }
227 if ($arg =~ /^-l/) { $printLong = 1; next; }
228 if ($arg =~ /^-s/) { $printStatus = 1; next; }
229 if ($arg =~ /^-b/) { $printBacktrace = 1; next; }
230 if ($arg =~ /^-d/) { $requestDump = 1; next; }
231 if ($arg =~ /^-z/) { $requestZero = 1; next; }
232 if ($arg =~ /^-k/) { $requestKill = 1; next; }
233 if ($arg =~ /^-e/) { $requestEvents = 1; next; }
234 if ($arg =~ /^-i/) { $switchInstr = 1; next; }
235 if ($arg =~ /^-w/) { $gotW = 1; next; }
236
237 printHeader;
238 print "Unknown option '$arg'.\n\n";
239 printHelp;
240 }
241
242 if ($arg =~ /^[A-Za-z_]/) {
243 # arguments of -d/-e/-i are non-numeric
244 if ($requestDump == 1) {
245 $requestDump = 2;
246 $dumpHint = $arg;
247 next;
248 }
249
250 if ($requestEvents == 1) {
251 $requestEvents = 2;
252 @show_events = split(/,/, $arg);
253 next;
254 }
255
256 if ($switchInstr == 1) {
257 $switchInstr = 2;
258 $switchInstrMode = "+";
259 if (($arg eq "off") || ($arg eq "no")) {
260 $switchInstrMode = "-";
261 }
262 next;
263 }
264 }
265
266 if ($gotW == 1) {
267 $gotW = 2;
268 $workingDir = $arg;
269 if (!-d $workingDir) {
270 print "Error: directory '$workingDir' does not exist.\n";
271 printHelp;
272 }
273 next;
274 }
275
276 if (defined $cmd{$arg}) { $spids{$arg} = 1; next; }
277 $nameFound = 0;
278 foreach $p (@pids) {
279 if ($cmd{$p} =~ /^$arg/) {
280 $nameFound = 1;
281 $spids{$p} = 1;
282 }
283 }
284 if ($nameFound) { next; }
285
286 printHeader;
287 print "Non-existent Callgrind task with PID/Name '$arg'.\n\n";
288 printHelp;
289}
290
291if ($workingDir ne "") {
292 # Generate dummy information for dummy pid 0
293 $pid = "0";
weidendo4e28a852006-04-21 00:58:58 +0000294 $mversion{$pid} = "1.0";
weidendoa17f2a32006-03-20 10:27:30 +0000295 $cmd{$pid} = "???";
296 $base{$pid} = $workingDir;
297 $control{$pid} = "$workingDir/callgrind.cmd";
weidendo4e28a852006-04-21 00:58:58 +0000298 $result{$pid} = "$workingDir/callgrind.res";
weidendoa17f2a32006-03-20 10:27:30 +0000299
300 # Only handle this faked callgrind run
301 @pids = ($pid);
302}
303
304if (scalar @pids == 0) {
305 print "No active callgrind runs detected.\n";
306 #print "Detection fails when /proc/*/maps is not readable.\n";
307 print "[Detection can fail on some systems; to work around this,\n";
308 print " specify the working directory of a callgrind run with '-w']\n";
309 exit;
310}
311
312@spids = keys %spids;
313if (scalar @spids >0) { @pids = @spids; }
314
315$command = "";
316$waitForAnswer = 0;
317if ($requestDump) {
318 $command = "Dump";
319 if ($dumpHint ne "") { $command .= " ".$dumpHint; }
320}
321if ($requestZero) { $command = "Zero"; }
322if ($requestKill) { $command = "Kill"; }
323if ($switchInstr) { $command = $switchInstrMode."Instrumentation"; }
324if ($printStatus || $printBacktrace || $requestEvents) {
325 $command = "Status";
326 $waitForAnswer = 1;
327}
328
329foreach $pid (@pids) {
330 $pidstr = "PID $pid: ";
weidendo4e28a852006-04-21 00:58:58 +0000331 if ($pid >0) { print $pidstr.$cmd{$pid}; }
weidendoa17f2a32006-03-20 10:27:30 +0000332
333 if ($command eq "") {
334 if ($printLong) {
335 #print " " x length $pidstr;
336 print " (in $base{$pid})\n";
337 }
338 else {
339 print "\n";
340 }
341 next;
342 }
343 else {
344 if (! (open CONTROL, ">$control{$pid}")) {
345 print " [sending '$command' failed: permission denied]\n";
346 next;
347 }
348 print " [requesting '$command'...]\n";
349 print CONTROL $command;
350 close CONTROL;
351
352 while(-e $control{$pid}) {
353 # sleep for 250 ms
354 select(undef, undef, undef, 0.25);
355 }
356 }
357
weidendo4e28a852006-04-21 00:58:58 +0000358 #print "Reading ".$result{$pid}. "...\n";
weidendoa17f2a32006-03-20 10:27:30 +0000359 if ($result{$pid} eq "") { $waitForAnswer=0; }
360 if (!$waitForAnswer) { print " OK.\n"; next; }
361
362 if (! (open RESULT, "<$result{$pid}")) {
363 print " Warning: Can't open expected result file $result{$pid}.\n";
364 next;
365 }
366
367 @tids = ();
368 $ctid = 0;
369 %fcount = ();
370 %func = ();
371 %calls = ();
372 %events = ();
373 @events = ();
weidendo6faa99e2007-02-16 21:55:25 +0000374 @threads = ();
weidendoa17f2a32006-03-20 10:27:30 +0000375 %totals = ();
376
377 $exec_bbs = 0;
378 $dist_bbs = 0;
379 $exec_calls = 0;
380 $dist_calls = 0;
381 $dist_ctxs = 0;
382 $dist_funcs = 0;
weidendo6faa99e2007-02-16 21:55:25 +0000383 $threads = "";
weidendoa17f2a32006-03-20 10:27:30 +0000384 $events = "";
385
386 while(<RESULT>) {
387 if (/function-(\d+)-(\d+): (.+)$/) {
388 if ($ctid != $1) {
389 $ctid = $1;
390 push(@tids, $ctid);
391 $fcount{$ctid} = 0;
392 }
393 $fcount{$ctid}++;
394 $func{$ctid,$fcount{$ctid}} = $3;
395 }
396 elsif (/calls-(\d+)-(\d+): (.+)$/) {
397 if ($ctid != $1) { next; }
398 $calls{$ctid,$fcount{$ctid}} = $3;
399 }
400 elsif (/events-(\d+)-(\d+): (.+)$/) {
401 if ($ctid != $1) { next; }
402 $events{$ctid,$fcount{$ctid}} = line_to_CC($3);
403 }
404 elsif (/events-(\d+): (.+)$/) {
405 if (scalar @events == 0) { next; }
406 $totals{$1} = line_to_CC($2);
407 }
408 elsif (/executed-bbs: (\d+)/) { $exec_bbs = $1; }
409 elsif (/distinct-bbs: (\d+)/) { $dist_bbs = $1; }
410 elsif (/executed-calls: (\d+)/) { $exec_calls = $1; }
411 elsif (/distinct-calls: (\d+)/) { $dist_calls = $1; }
412 elsif (/distinct-functions: (\d+)/) { $dist_funcs = $1; }
413 elsif (/distinct-contexts: (\d+)/) { $dist_ctxs = $1; }
414 elsif (/events: (.+)$/) { $events = $1; prepareEvents; }
weidendo6faa99e2007-02-16 21:55:25 +0000415 elsif (/threads: (.+)$/) { $threads = $1; @threads = split " ", $threads; }
weidendoa17f2a32006-03-20 10:27:30 +0000416 elsif (/instrumentation: (\w+)$/) { $instrumentation = $1; }
417 }
418
419 unlink $result{$pid};
420
421 if ($instrumentation eq "off") {
422 print " No information available as instrumentation is switched off.\n\n";
423 exit;
424 }
425
426 if ($printStatus) {
427 if ($requestEvents <1) {
weidendo6faa99e2007-02-16 21:55:25 +0000428 print " Number of running threads: " .($#threads+1). ", thread IDs: $threads\n";
weidendoa17f2a32006-03-20 10:27:30 +0000429 print " Events collected: $events\n";
430 }
431
432 print " Functions: ".commify($dist_funcs);
433 print " (executed ".commify($exec_calls);
434 print ", contexts ".commify($dist_ctxs).")\n";
435
436 print " Basic blocks: ".commify($dist_bbs);
437 print " (executed ".commify($exec_bbs);
438 print ", call sites ".commify($dist_calls).")\n";
439 }
440
441 if ($requestEvents >0) {
442 $totals_width = compute_CC_col_widths(values %totals);
443 print "\n Totals:";
444 print_events($totals_width);
445 print("\n");
446 foreach $tid (@tids) {
447 print " Th".substr(" ".$tid,-2)." ";
448 print_CC($totals{$tid}, $totals_width);
449 print("\n");
450 }
451 }
452
453 if ($printBacktrace) {
454
455 if ($requestEvents >0) {
456 $totals_width = compute_CC_col_widths(values %events);
457 }
458
459 foreach $tid (@tids) {
460 print "\n Frame: ";
461 if ($requestEvents >0) {
462 print_events($totals_width);
463 }
464 print "Backtrace for Thread $tid\n";
465
466 $i = $fcount{$tid};
467 $c = 0;
468 while($i>0 && $c<100) {
469 $fc = substr(" $c",-2);
470 print " [$fc] ";
471 if ($requestEvents >0) {
472 print_CC($events{$tid,$i-1}, $totals_width);
473 }
474 print $func{$tid,$i};
475 if ($i > 1) {
476 print " (".$calls{$tid,$i-1}." x)";
477 }
478 print "\n";
479 $i--;
480 $c++;
481 }
482 print "\n";
483 }
484 }
485 print "\n";
486}
487