blob: 23c14d9025372dec61ee4fdc527217506d957b37 [file] [log] [blame]
weidendoa17f2a32006-03-20 10:27:30 +00001/*
2 This file is part of Callgrind, a Valgrind skin for call graph
3 profiling programs.
4
5 Copyright (C) 2002-2004, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
6
7 This skin is derived from and contains lot of code from Cachegrind
8 Copyright (C) 2002 Nicholas Nethercote (njn25@cam.ac.uk)
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2 of the
13 License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 02111-1307, USA.
24
25 The GNU General Public License is contained in the file COPYING.
26*/
27
28/*
29 * Functions related to interactive commands via "callgrind.cmd"
30 */
31
32#include "config.h"
33#include "global.h"
34
35#include <pub_tool_threadstate.h> // VG_N_THREADS
36
37static Char outbuf[FILENAME_LEN + FN_NAME_LEN + OBJ_NAME_LEN];
38
39static Char* command_file = 0;
40static Char* command_file2 = 0;
41static Char* result_file = 0;
42static Char* info_file = 0;
43static Char* dump_base = 0;
44
45static Bool command_inited = False;
46
47void CLG_(init_command)(Char* dir, Char* dumps)
48{
49 Int fd, size;
50 SysRes res;
51
52 dump_base = dumps;
53
54 size = VG_(strlen)(dir) + VG_(strlen)(DEFAULT_COMMANDNAME) +10;
55 command_file = (char*) CLG_MALLOC(size);
56 CLG_ASSERT(command_file != 0);
57 VG_(sprintf)(command_file, "%s/%s.%d",
58 dir, DEFAULT_COMMANDNAME, VG_(getpid)());
59
60 /* This is for compatibility with the "Force Now" Button of current
61 * KCachegrind releases, as it doesn't use ".pid" to distinguish
62 * different callgrind instances from same base directory.
63 * Should be removed sometimes in the future (29.10.03)
64 */
65 command_file2 = (char*) CLG_MALLOC(size);
66 CLG_ASSERT(command_file2 != 0);
67 VG_(sprintf)(command_file2, "%s/%s",
68 dir, DEFAULT_COMMANDNAME);
69
70 size = VG_(strlen)(dir) + VG_(strlen)(DEFAULT_RESULTNAME) +10;
71 result_file = (char*) CLG_MALLOC(size);
72 CLG_ASSERT(result_file != 0);
73 VG_(sprintf)(result_file, "%s/%s.%d",
74 dir, DEFAULT_RESULTNAME, VG_(getpid)());
75
76 info_file = (char*) CLG_MALLOC(VG_(strlen)(DEFAULT_INFONAME) + 10);
77 CLG_ASSERT(info_file != 0);
78 VG_(sprintf)(info_file, "%s.%d", DEFAULT_INFONAME, VG_(getpid)());
79
80 CLG_DEBUG(1, " dump file base: '%s'\n", dump_base);
81 CLG_DEBUG(1, " command file: '%s'\n", command_file);
82 CLG_DEBUG(1, " result file: '%s'\n", result_file);
83 CLG_DEBUG(1, " info file: '%s'\n", info_file);
84
85 /* create info file to indicate that we are running */
86 res = VG_(open)(info_file, VKI_O_WRONLY|VKI_O_TRUNC, 0);
87 if (res.isError) {
88 res = VG_(open)(info_file, VKI_O_CREAT|VKI_O_WRONLY,
89 VKI_S_IRUSR|VKI_S_IWUSR);
90 if (res.isError) {
91 VG_(message)(Vg_DebugMsg,
92 "warning: can't write info file '%s'", info_file);
93 info_file = 0;
94 fd = -1;
95 }
96 }
97 if (!res.isError)
98 fd = (Int) res.val;
99 if (fd>=0) {
100 Char buf[512];
101 Int i;
102
103 VG_(sprintf)(buf,
104 "# This file is generated by Callgrind-" VERSION ".\n"
105 "# It is used to enable controlling the supervision of\n"
106 "# '%s'\n"
107 "# by external tools.\n\n",
108#if VG_CORE_INTERFACE_VERSION < 9
109 VG_(client_argv[0])
110#else
111 VG_(args_the_exename)
112#endif
113 );
114 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
115
116 VG_(sprintf)(buf, "version: " VERSION "\n");
117 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
118
119 VG_(sprintf)(buf, "base: %s\n", dir);
120 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
121
122 VG_(sprintf)(buf, "dumps: %s\n", dump_base);
123 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
124
125 VG_(sprintf)(buf, "control: %s\n", command_file);
126 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
127
128 VG_(sprintf)(buf, "result: %s\n", result_file);
129 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
130
131 VG_(strcpy)(buf, "cmd:");
132 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
133#if VG_CORE_INTERFACE_VERSION < 9
134 for (i = 0; i < VG_(client_argc); i++) {
135 if (!VG_(client_argv[i])) continue;
136 VG_(sprintf)(buf, " %s", VG_(client_argv[i]));
137 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
138 }
139#else
140 VG_(sprintf)(buf, " %s", VG_(args_the_exename));
141 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
142 for (i = 0; i < VG_(args_for_client).used; i++) {
143 if (!VG_(args_for_client).strs[i]) continue;
144 VG_(sprintf)(buf, " %s", VG_(args_for_client).strs[i]);
145 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
146 }
147#endif
148 VG_(write)(fd, "\n", 1);
149 VG_(close)(fd);
150 }
151
152 command_inited = True;
153}
154
155void CLG_(finish_command)()
156{
157 /* unlink info file */
158 if (info_file) VG_(unlink)(info_file);
159}
160
161
162static Int createRes(Int fd)
163{
164 SysRes res;
165
166 if (fd > -2) return fd;
167
168 /* fd == -2: No error, but we need to create the file */
169 res = VG_(open)(result_file,
170 VKI_O_CREAT|VKI_O_WRONLY|VKI_O_TRUNC,
171 VKI_S_IRUSR|VKI_S_IWUSR);
172
173 /* VG_(open) can return any negative number on error. Remap errors to -1,
174 * to not confuse it with our special value -2
175 */
176 if (res.isError) fd = -1;
177 else fd = (Int) res.val;
178
179 return fd;
180}
181
182/* Run Info: Fixed information for a callgrind run */
183static Int dump_info(Int fd)
184{
185 Char* buf = outbuf;
186 int i;
187
188 if ( (fd = createRes(fd)) <0) return fd;
189
190 /* version */
191 VG_(sprintf)(buf, "version: " VERSION "\n");
192 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
193
194 /* "pid:" line */
195 VG_(sprintf)(buf, "pid: %d\n", VG_(getpid)());
196 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
197
198 /* "base:" line */
199 VG_(sprintf)(buf, "base: %s\n", dump_base);
200 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
201
202 /* "cmd:" line */
203 VG_(strcpy)(buf, "cmd:");
204 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
205#if VG_CORE_INTERFACE_VERSION < 9
206 for (i = 0; i < VG_(client_argc); i++) {
207 if (!VG_(client_argv[i])) continue;
208 VG_(sprintf)(buf, " %s", VG_(client_argv[i]));
209 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
210 }
211#else
212 VG_(sprintf)(buf, " %s", VG_(args_the_exename));
213 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
214 for (i = 0; i < VG_(args_for_client).used; i++) {
215 if (!VG_(args_for_client).strs[i]) continue;
216 VG_(sprintf)(buf, " %s", VG_(args_for_client).strs[i]);
217 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
218 }
219#endif
220
221 return fd;
222}
223
224
225/* Helper for dump_state */
226
227Int dump_fd;
228
229void static dump_state_of_thread(thread_info* ti)
230{
231 Char* buf = outbuf;
232 int t = CLG_(current_tid);
233 Int p, i;
234 static FullCost sum = 0, tmp = 0;
235 BBCC *from, *to;
236 call_entry* ce;
237
238 p = VG_(sprintf)(buf, "events-%d: ", t);
239 CLG_(init_cost_lz)( CLG_(sets).full, &sum );
240 CLG_(copy_cost_lz)( CLG_(sets).full, &tmp, ti->lastdump_cost );
241 CLG_(add_diff_cost)( CLG_(sets).full, sum,
242 ti->lastdump_cost,
243 ti->states.entry[0]->cost);
244 CLG_(copy_cost)( CLG_(sets).full, ti->lastdump_cost, tmp );
245 p += CLG_(sprint_mappingcost)(buf + p, CLG_(dumpmap), sum);
246 p += VG_(sprintf)(buf+p, "\n");
247 VG_(write)(dump_fd, (void*)buf, p);
248
249 p = VG_(sprintf)(buf, "frames-%d: %d\n", t,
250 CLG_(current_call_stack).sp);
251 VG_(write)(dump_fd, (void*)buf, p);
252 ce = 0;
253 for(i = 0; i < CLG_(current_call_stack).sp; i++) {
254 ce = CLG_(get_call_entry)(i);
255 /* if this frame is skipped, we don't have counters */
256 if (!ce->jcc) continue;
257
258 from = ce->jcc->from;
259 p = VG_(sprintf)(buf, "function-%d-%d: %s\n",t, i,
260 from->cxt->fn[0]->name);
261 VG_(write)(dump_fd, (void*)buf, p);
262
263 p = VG_(sprintf)(buf, "calls-%d-%d: ",t, i);
264 p+= VG_(sprintf)(buf+p, "%llu\n", ce->jcc->call_counter);
265 VG_(write)(dump_fd, (void*)buf, p);
266
267 /* FIXME: EventSets! */
268 CLG_(copy_cost)( CLG_(sets).full, sum, ce->jcc->cost );
269 CLG_(copy_cost)( CLG_(sets).full, tmp, ce->enter_cost );
270 CLG_(add_diff_cost)( CLG_(sets).full, sum,
271 ce->enter_cost, CLG_(current_state).cost );
272 CLG_(copy_cost)( CLG_(sets).full, ce->enter_cost, tmp );
273
274 p = VG_(sprintf)(buf, "events-%d-%d: ",t, i);
275 p += CLG_(sprint_mappingcost)(buf + p, CLG_(dumpmap), sum );
276 p += VG_(sprintf)(buf+p, "\n");
277 VG_(write)(dump_fd, (void*)buf, p);
278 }
279 if (ce && ce->jcc) {
280 to = ce->jcc->to;
281 p = VG_(sprintf)(buf, "function-%d-%d: %s\n",t, i,
282 to->cxt->fn[0]->name );
283 VG_(write)(dump_fd, (void*)buf, p);
284 }
285}
286
287/* Dump info on current callgrind state */
288static Int dump_state(Int fd)
289{
290 Char* buf = outbuf;
291 thread_info** th;
292 int t, p;
293 Int orig_tid = CLG_(current_tid);
294
295 if ( (fd = createRes(fd)) <0) return fd;
296
297 VG_(sprintf)(buf, "instrumentation: %s\n",
298 CLG_(instrument_state) ? "on":"off");
299 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
300
301 if (!CLG_(instrument_state)) return fd;
302
303 VG_(sprintf)(buf, "executed-bbs: %llu\n", CLG_(stat).bb_executions);
304 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
305
306 VG_(sprintf)(buf, "executed-calls: %llu\n", CLG_(stat).call_counter);
307 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
308
309 VG_(sprintf)(buf, "distinct-bbs: %d\n", CLG_(stat).distinct_bbs);
310 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
311
312 VG_(sprintf)(buf, "distinct-calls: %d\n", CLG_(stat).distinct_jccs);
313 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
314
315 VG_(sprintf)(buf, "distinct-functions: %d\n", CLG_(stat).distinct_fns);
316 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
317
318 VG_(sprintf)(buf, "distinct-contexts: %d\n", CLG_(stat).distinct_contexts);
319 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
320
321 /* "events:" line. Given here because it will be dynamic in the future */
322 p = VG_(sprintf)(buf, "events: ");
323 CLG_(sprint_eventmapping)(buf+p, CLG_(dumpmap));
324 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
325 VG_(write)(fd, "\n", 1);
326
327 /* "part:" line (number of last part. Is 0 at start */
328 VG_(sprintf)(buf, "\npart: %d\n", CLG_(get_dump_counter)());
329 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
330
331 /* threads */
332 th = CLG_(get_threads)();
333 p = VG_(sprintf)(buf, "threads:");
334 for(t=1;t<VG_N_THREADS;t++) {
335 if (!th[t]) continue;
336 p += VG_(sprintf)(buf+p, " %d", t);
337 }
338 p += VG_(sprintf)(buf+p, "\n");
339 VG_(write)(fd, (void*)buf, p);
340
341 VG_(sprintf)(buf, "current-tid: %d\n", orig_tid);
342 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
343
344 /* current event counters */
345 dump_fd = fd;
346 CLG_(forall_threads)(dump_state_of_thread);
347
348 return fd;
349}
350
351void CLG_(check_command)()
352{
353 /* check for dumps needed */
354 static Char buf[512];
355 static Char cmdBuffer[512];
356 Char *cmdPos = 0, *cmdNextLine = 0;
357 Int fd, bytesRead = 0, do_kill = 0;
358 static Char* cfile = 0;
359 SysRes res;
360
361 if (!command_inited) return;
362
363 /* toggle between 2 command files, with/without ".pid" postfix */
364 cfile = ((cfile == command_file) || (cfile == 0)) ?
365 command_file2 : command_file;
366
367
368 res = VG_(open)(cfile, VKI_O_RDONLY,0);
369 if (!res.isError) {
370 fd = (Int) res.val;
371 bytesRead = VG_(read)(fd,cmdBuffer,500);
372 cmdBuffer[500] = 0; /* no command overrun please */
373 VG_(close)(fd);
374 /* don't delete command file on read error (e.g. EAGAIN) */
375 if (bytesRead>0) {
376 cmdPos = cmdBuffer;
377 }
378 }
379
380 /* force creation of result file if needed */
381 fd = -2;
382
383 while((bytesRead>0) && *cmdPos) {
384
385 /* Calculate pointer for next line */
386 cmdNextLine = cmdPos+1;
387 while((bytesRead>0) && *cmdNextLine && (*cmdNextLine != '\n')) {
388 cmdNextLine++;
389 bytesRead--;
390 }
391 if ((bytesRead>0) && (*cmdNextLine == '\n')) {
392 *cmdNextLine = 0;
393 cmdNextLine++;
394 bytesRead--;
395 }
396
397 /* Command with integer option */
398 if ((*cmdPos >= '0') && (*cmdPos <='9')) {
399 int value = *cmdPos-'0';
400 cmdPos++;
401 while((*cmdPos >= '0') && (*cmdPos <='9')) {
402 value = 10*value + (*cmdPos-'0');
403 cmdPos++;
404 }
405 while((*cmdPos == ' ') || (*cmdPos == '\t')) cmdPos++;
406
407 switch(*cmdPos) {
408#if CLG_ENABLE_DEBUG
409 /* verbosity */
410 case 'V':
411 case 'v':
412 CLG_(clo).verbose = value;
413 break;
414#endif
415 default:
416 break;
417 }
418
419 cmdPos = cmdNextLine;
420 continue;
421 }
422
423 /* Command with boolean/switch option */
424 if ((*cmdPos=='+') ||
425 (*cmdPos=='-')) {
426 int value = (cmdPos[0] == '+');
427 cmdPos++;
428 while((*cmdPos == ' ') || (*cmdPos == '\t')) cmdPos++;
429
430 switch(*cmdPos) {
431 case 'I':
432 case 'i':
433 CLG_(set_instrument_state)("Command", value);
434 break;
435
436 default:
437 break;
438 }
439
440 cmdPos = cmdNextLine;
441 continue;
442 }
443
444 /* regular command */
445 switch(*cmdPos) {
446 case 'D':
447 case 'd':
448 /* DUMP */
449
450 /* skip command */
451 while(*cmdPos && (*cmdPos != ' ')) cmdPos++;
452 if (*cmdPos)
453 VG_(sprintf)(buf, "Dump Command:%s", cmdPos);
454 else
455 VG_(sprintf)(buf, "Dump Command");
456 CLG_(dump_profile)(buf, False);
457 break;
458
459 case 'Z':
460 case 'z':
461 CLG_(zero_all_cost)(False);
462 break;
463
464 case 'K':
465 case 'k':
466 /* Kill: Delay to be able to remove command file before. */
467 do_kill = 1;
468 break;
469
470 case 'I':
471 case 'i':
472 fd = dump_info(fd);
473 break;
474
475 case 's':
476 case 'S':
477 fd = dump_state(fd);
478 break;
479
480 case 'O':
481 case 'o':
482 /* Options Info */
483 if ( (fd = createRes(fd)) <0) break;
484
485 VG_(sprintf)(buf, "\ndesc: Option: --skip-plt=%s\n",
486 CLG_(clo).skip_plt ? "yes" : "no");
487 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
488 VG_(sprintf)(buf, "desc: Option: --collect-jumps=%s\n",
489 CLG_(clo).collect_jumps ? "yes" : "no");
490 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
491 VG_(sprintf)(buf, "desc: Option: --separate-recs=%d\n",
492 CLG_(clo).separate_recursions);
493 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
494 VG_(sprintf)(buf, "desc: Option: --separate-callers=%d\n",
495 CLG_(clo).separate_callers);
496 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
497
498 break;
499
500 default:
501 break;
502 }
503
504 cmdPos = cmdNextLine;
505 }
506
507 /* If command executed, delete command file */
508 if (cmdPos) VG_(unlink)(cfile);
509 if (fd>=0) VG_(close)(fd);
510
511 if (do_kill) {
512 VG_(message)(Vg_UserMsg,
513 "Killed because of command from %s", cfile);
514 CLG_(fini)(0);
515 VG_(exit)(1);
516 }
517}