1 /*
2 This file is part of Callgrind, a Valgrind tool for call graph
3 profiling programs.
4
5 Copyright (C) 2002-2011, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
6
7 This tool is derived from and contains lot of code from Cachegrind
8 Copyright (C) 2002-2011 Nicholas Nethercote (njn@valgrind.org)
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
37 // Version for the syntax in command/result files for interactive control
38 #define COMMAND_VERSION "1.0"
39
40 static Char outbuf[FILENAME_LEN + FN_NAME_LEN + OBJ_NAME_LEN];
41
42 static Char* command_file = 0;
43 static Char* command_file2 = 0;
44 static Char* current_command_file = 0;
45 static Char* result_file = 0;
46 static Char* result_file2 = 0;
47 static Char* current_result_file = 0;
48 static Char* info_file = 0;
49 static Char* out_file = 0;
50
51 static Int thisPID = 0;
52
53 /**
54 * Setup for interactive control of a callgrind run
55 */
setup_control(void)56 static void setup_control(void)
57 {
58 Int fd, size;
59 SysRes res;
60 Char* dir;
61 const HChar *tmpdir;
62
63 CLG_ASSERT(thisPID != 0);
64
65 fd = -1;
66 dir = CLG_(get_out_directory)();
67 out_file = CLG_(get_out_file)();
68
69 /* name of command file */
70 size = VG_(strlen)(dir) + VG_(strlen)(DEFAULT_COMMANDNAME) +10;
71 command_file = (char*) CLG_MALLOC("cl.command.sc.1", size);
72 CLG_ASSERT(command_file != 0);
73 VG_(sprintf)(command_file, "%s/%s.%d",
74 dir, DEFAULT_COMMANDNAME, thisPID);
75
76 /* This is for compatibility with the "Force Now" Button of current
77 * KCachegrind releases, as it doesn't use ".pid" to distinguish
78 * different callgrind instances from same base directory.
79 */
80 command_file2 = (char*) CLG_MALLOC("cl.command.sc.2", size);
81 CLG_ASSERT(command_file2 != 0);
82 VG_(sprintf)(command_file2, "%s/%s",
83 dir, DEFAULT_COMMANDNAME);
84
85 size = VG_(strlen)(dir) + VG_(strlen)(DEFAULT_RESULTNAME) +10;
86 result_file = (char*) CLG_MALLOC("cl.command.sc.3", size);
87 CLG_ASSERT(result_file != 0);
88 VG_(sprintf)(result_file, "%s/%s.%d",
89 dir, DEFAULT_RESULTNAME, thisPID);
90
91 /* If we get a command from a command file without .pid, use
92 * a result file without .pid suffix
93 */
94 result_file2 = (char*) CLG_MALLOC("cl.command.sc.4", size);
95 CLG_ASSERT(result_file2 != 0);
96 VG_(sprintf)(result_file2, "%s/%s",
97 dir, DEFAULT_RESULTNAME);
98
99 tmpdir = VG_(tmpdir)();
100 info_file = (char*) CLG_MALLOC("cl.command.sc.5",
101 VG_(strlen)(tmpdir) +
102 VG_(strlen)(DEFAULT_INFONAME) + 10);
103 CLG_ASSERT(info_file != 0);
104 VG_(sprintf)(info_file, "%s/%s.%d", tmpdir, DEFAULT_INFONAME, thisPID);
105
106 CLG_DEBUG(1, "Setup for interactive control (PID: %d):\n", thisPID);
107 CLG_DEBUG(1, " output file: '%s'\n", out_file);
108 CLG_DEBUG(1, " command file: '%s'\n", command_file);
109 CLG_DEBUG(1, " result file: '%s'\n", result_file);
110 CLG_DEBUG(1, " info file: '%s'\n", info_file);
111
112 /* create info file to indicate that we are running */
113 res = VG_(open)(info_file, VKI_O_WRONLY|VKI_O_TRUNC, 0);
114 if (sr_isError(res)) {
115 res = VG_(open)(info_file, VKI_O_CREAT|VKI_O_WRONLY,
116 VKI_S_IRUSR|VKI_S_IWUSR);
117 if (sr_isError(res)) {
118 VG_(message)(Vg_DebugMsg,
119 "warning: can't write info file '%s'\n", info_file);
120 info_file = 0;
121 fd = -1;
122 }
123 }
124 if (!sr_isError(res))
125 fd = (Int) sr_Res(res);
126 if (fd>=0) {
127 Char buf[512];
128 Int i;
129
130 WRITE_STR3(fd,
131 "# This file is generated by Callgrind-" VERSION ".\n"
132 "# It is used to enable controlling the supervision of\n"
133 "# '", VG_(args_the_exename), "'\n"
134 "# by external tools.\n\n");
135
136 VG_(sprintf)(buf, "version: " COMMAND_VERSION "\n");
137 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
138
139 WRITE_STR3(fd, "base: ", dir, "\n");
140 WRITE_STR3(fd, "dumps: ", out_file, "\n");
141 WRITE_STR3(fd, "control: ", command_file, "\n");
142 WRITE_STR3(fd, "result: ", result_file, "\n");
143
144 WRITE_STR2(fd, "cmd: ", VG_(args_the_exename));
145 for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
146 HChar* arg = * (HChar**)VG_(indexXA)( VG_(args_for_client), i );
147 if (!arg) continue;
148 WRITE_STR2(fd, " ", arg);
149 }
150 VG_(write)(fd, "\n", 1);
151 VG_(close)(fd);
152 }
153 }
154
CLG_(init_command)155 void CLG_(init_command)()
156 {
157 thisPID = VG_(getpid)();
158 setup_control();
159 }
160
CLG_(finish_command)161 void CLG_(finish_command)()
162 {
163 /* unlink info file */
164 if (info_file) VG_(unlink)(info_file);
165 }
166
167
createRes(Int fd)168 static Int createRes(Int fd)
169 {
170 SysRes res;
171
172 if (fd > -2) return fd;
173
174 /* fd == -2: No error, but we need to create the file */
175 CLG_ASSERT(current_result_file != 0);
176 res = VG_(open)(current_result_file,
177 VKI_O_CREAT|VKI_O_WRONLY|VKI_O_TRUNC,
178 VKI_S_IRUSR|VKI_S_IWUSR);
179
180 /* VG_(open) can return any negative number on error. Remap errors to -1,
181 * to not confuse it with our special value -2
182 */
183 if (sr_isError(res)) fd = -1;
184 else fd = (Int) sr_Res(res);
185
186 return fd;
187 }
188
189 /* Run Info: Persistant information of the callgrind run */
dump_info(Int fd)190 static Int dump_info(Int fd)
191 {
192 Char* buf = outbuf;
193 int i;
194
195 if ( (fd = createRes(fd)) <0) return fd;
196
197 /* creator */
198 VG_(sprintf)(buf, "creator: callgrind-" VERSION "\n");
199 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
200
201 /* version */
202 VG_(sprintf)(buf, "version: " COMMAND_VERSION "\n");
203 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
204
205 /* "pid:" line */
206 VG_(sprintf)(buf, "pid: %d\n", VG_(getpid)());
207 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
208
209 /* "base:" line */
210 WRITE_STR3(fd, "base: ", out_file, "\n");
211
212 /* "cmd:" line */
213 WRITE_STR2(fd, "cmd: ", VG_(args_the_exename));
214 for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
215 HChar* arg = * (HChar**)VG_(indexXA)( VG_(args_for_client), i );
216 if (!arg) continue;
217 WRITE_STR2(fd, " ", arg);
218 }
219 VG_(write)(fd, "\n", 1);
220
221 return fd;
222 }
223
224
225 /* Helper for dump_state */
226
227 Int dump_fd;
228
dump_state_of_thread(thread_info * ti)229 void 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 */
dump_state(Int fd)288 static 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
CLG_(check_command)351 void 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 SysRes res;
359 Int currentPID;
360 static Int check_counter = 0;
361
362 /* Check for PID change, i.e. whether we run as child after a fork.
363 * If yes, we setup interactive control for the new process
364 */
365 currentPID = VG_(getpid)();
366 if (thisPID != currentPID) {
367 thisPID = currentPID;
368 setup_control();
369 }
370
371 /* Toggle between 2 command files, with/without ".pid" postfix
372 * (needed for compatibility with KCachegrind, which wants to trigger
373 * a dump by writing into a command file without the ".pid" postfix)
374 */
375 check_counter++;
376 if (check_counter % 2) {
377 current_command_file = command_file;
378 current_result_file = result_file;
379 }
380 else {
381 current_command_file = command_file2;
382 current_result_file = result_file2;
383 }
384
385 res = VG_(open)(current_command_file, VKI_O_RDONLY,0);
386 if (!sr_isError(res)) {
387 fd = (Int) sr_Res(res);
388 bytesRead = VG_(read)(fd,cmdBuffer,500);
389 cmdBuffer[500] = 0; /* no command overrun please */
390 VG_(close)(fd);
391 /* don't delete command file on read error (e.g. EAGAIN) */
392 if (bytesRead>0) {
393 cmdPos = cmdBuffer;
394 }
395 }
396
397 /* force creation of result file if needed */
398 fd = -2;
399
400 while((bytesRead>0) && *cmdPos) {
401
402 /* Calculate pointer for next line */
403 cmdNextLine = cmdPos+1;
404 while((bytesRead>0) && *cmdNextLine && (*cmdNextLine != '\n')) {
405 cmdNextLine++;
406 bytesRead--;
407 }
408 if ((bytesRead>0) && (*cmdNextLine == '\n')) {
409 *cmdNextLine = 0;
410 cmdNextLine++;
411 bytesRead--;
412 }
413
414 /* Command with integer option */
415 if ((*cmdPos >= '0') && (*cmdPos <='9')) {
416 int value = *cmdPos-'0';
417 cmdPos++;
418 while((*cmdPos >= '0') && (*cmdPos <='9')) {
419 value = 10*value + (*cmdPos-'0');
420 cmdPos++;
421 }
422 while((*cmdPos == ' ') || (*cmdPos == '\t')) cmdPos++;
423
424 switch(*cmdPos) {
425 #if CLG_ENABLE_DEBUG
426 /* verbosity */
427 case 'V':
428 case 'v':
429 CLG_(clo).verbose = value;
430 break;
431 #endif
432 default:
433 break;
434 }
435
436 cmdPos = cmdNextLine;
437 continue;
438 }
439
440 /* Command with boolean/switch option */
441 if ((*cmdPos=='+') ||
442 (*cmdPos=='-')) {
443 int value = (cmdPos[0] == '+');
444 cmdPos++;
445 while((*cmdPos == ' ') || (*cmdPos == '\t')) cmdPos++;
446
447 switch(*cmdPos) {
448 case 'I':
449 case 'i':
450 CLG_(set_instrument_state)("Command", value);
451 break;
452
453 default:
454 break;
455 }
456
457 cmdPos = cmdNextLine;
458 continue;
459 }
460
461 /* regular command */
462 switch(*cmdPos) {
463 case 'D':
464 case 'd':
465 /* DUMP */
466
467 /* skip command */
468 while(*cmdPos && (*cmdPos != ' ')) cmdPos++;
469 if (*cmdPos)
470 VG_(sprintf)(buf, "Dump Command:%s", cmdPos);
471 else
472 VG_(sprintf)(buf, "Dump Command");
473 CLG_(dump_profile)(buf, False);
474 break;
475
476 case 'Z':
477 case 'z':
478 CLG_(zero_all_cost)(False);
479 break;
480
481 case 'K':
482 case 'k':
483 /* Kill: Delay to be able to remove command file before. */
484 do_kill = 1;
485 break;
486
487 case 'I':
488 case 'i':
489 fd = dump_info(fd);
490 break;
491
492 case 's':
493 case 'S':
494 fd = dump_state(fd);
495 break;
496
497 case 'O':
498 case 'o':
499 /* Options Info */
500 if ( (fd = createRes(fd)) <0) break;
501
502 VG_(sprintf)(buf, "\ndesc: Option: --skip-plt=%s\n",
503 CLG_(clo).skip_plt ? "yes" : "no");
504 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
505 VG_(sprintf)(buf, "desc: Option: --collect-jumps=%s\n",
506 CLG_(clo).collect_jumps ? "yes" : "no");
507 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
508 VG_(sprintf)(buf, "desc: Option: --separate-recs=%d\n",
509 CLG_(clo).separate_recursions);
510 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
511 VG_(sprintf)(buf, "desc: Option: --separate-callers=%d\n",
512 CLG_(clo).separate_callers);
513 VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
514
515 break;
516
517 default:
518 break;
519 }
520
521 cmdPos = cmdNextLine;
522 }
523
524 /* If command executed, delete command file */
525 if (cmdPos) VG_(unlink)(current_command_file);
526 if (fd>=0) VG_(close)(fd);
527
528 if (do_kill) {
529 VG_(message)(Vg_UserMsg,
530 "Killed because of command from %s\n",
531 current_command_file);
532 CLG_(fini)(0);
533 VG_(exit)(1);
534 }
535 }
536