• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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