Home | History | Annotate | Download | only in callgrind
      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 (at) gmx.de)
      6 
      7    This tool is derived from and contains lot of code from Cachegrind
      8    Copyright (C) 2002-2011 Nicholas Nethercote (njn (at) 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  */
     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 
    155 void CLG_(init_command)()
    156 {
    157   thisPID = VG_(getpid)();
    158   setup_control();
    159 }
    160 
    161 void CLG_(finish_command)()
    162 {
    163   /* unlink info file */
    164   if (info_file) VG_(unlink)(info_file);
    165 }
    166 
    167 
    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 */
    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 
    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 */
    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 
    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