Logo Search packages:      
Sourcecode: callgrind version File versions

command.c

/*
   This file is part of Callgrind, a Valgrind skin for call graph
   profiling programs.

   Copyright (C) 2002-2004, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)

   This skin is derived from and contains lot of code from Cachegrind
   Copyright (C) 2002 Nicholas Nethercote (njn25@cam.ac.uk)

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

   The GNU General Public License is contained in the file COPYING.
*/

/*
 * Functions related to interactive commands via "callgrind.cmd"
 */

#include "global.h"

static Char outbuf[FILENAME_LEN + FN_NAME_LEN + OBJ_NAME_LEN];

static Char* command_file = 0;
static Char* command_file2 = 0;
static Char* result_file = 0;
static Char* info_file = 0;
static Char* dump_base = 0;

static Bool command_inited = False;

void SK_(init_command)(Char* dir, Char* dumps)
{
  Int fd, size;

  dump_base = dumps;

  size = VG_(strlen)(dir) + VG_(strlen)(DEFAULT_COMMANDNAME) +10;
  command_file = VG_(malloc)(size);
  CT_ASSERT(command_file != 0);
  VG_(sprintf)(command_file, "%s/%s.%d",
             dir, DEFAULT_COMMANDNAME, VG_(getpid)());

  /* This is for compatibility with the "Force Now" Button of current
   * KCachegrind releases, as it doesn't use ".pid" to distinguish
   * different callgrind instances from same base directory.
   * Should be removed sometimes in the future (29.10.03)
   */
  command_file2 = VG_(malloc)(size);
  CT_ASSERT(command_file2 != 0);
  VG_(sprintf)(command_file2, "%s/%s",
             dir, DEFAULT_COMMANDNAME);

  size = VG_(strlen)(dir) + VG_(strlen)(DEFAULT_RESULTNAME) +10;
  result_file = VG_(malloc)(size);
  CT_ASSERT(result_file != 0);
  VG_(sprintf)(result_file, "%s/%s.%d",
             dir, DEFAULT_RESULTNAME, VG_(getpid)());

  info_file = VG_(malloc)(VG_(strlen)(DEFAULT_INFONAME) + 10);
  CT_ASSERT(info_file != 0);
  VG_(sprintf)(info_file, "%s.%d", DEFAULT_INFONAME, VG_(getpid)());

  CT_DEBUG(1, "  dump file base: '%s'\n", dump_base);
  CT_DEBUG(1, "  command file:   '%s'\n", command_file);
  CT_DEBUG(1, "  result file:    '%s'\n", result_file);
  CT_DEBUG(1, "  info file:      '%s'\n", info_file);

  /* create info file to indicate that we are running */ 
  fd = VG_(open)(info_file, VKI_O_WRONLY|VKI_O_TRUNC, 0);
  if (fd<0) { 
    fd = VG_(open)(info_file, VKI_O_CREAT|VKI_O_WRONLY,
               VKI_S_IRUSR|VKI_S_IWUSR);
    if (fd <0) {
      VG_(message)(Vg_DebugMsg, 
               "warning: can't write info file '%s'", info_file);
      info_file = 0;
      fd = -1;
    }
  }
  if (fd>=0) {
    Char buf[512];
    Int i;
    
    VG_(sprintf)(buf, 
             "# This file is generated by Callgrind-" VERSION ".\n"
             "# It is used to enable controlling the supervision of\n"
             "#  '%s'\n"
             "# by external tools.\n\n", VG_(client_argv[0]));
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    VG_(sprintf)(buf, "version: " VERSION "\n");
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    VG_(sprintf)(buf, "base: %s\n", dir);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    VG_(sprintf)(buf, "dumps: %s\n", dump_base);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    VG_(sprintf)(buf, "control: %s\n", command_file);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    VG_(sprintf)(buf, "result: %s\n", result_file);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    VG_(strcpy)(buf, "cmd:");
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    for (i = 0; i < VG_(client_argc); i++) {
      VG_(sprintf)(buf, " %s", VG_(client_argv[i]));
      VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    }
    VG_(write)(fd, "\n", 1);
    VG_(close)(fd);
  }

  command_inited = True;
}

void SK_(finish_command)()
{
  /* unlink info file */
  if (info_file) VG_(unlink)(info_file);
}


static Int createRes(Int fd)
{
    if (fd > -2) return fd;

    /* fd == -2: No error, but we need to create the file */
    fd = VG_(open)(result_file,
               VKI_O_CREAT|VKI_O_WRONLY|VKI_O_TRUNC,
               VKI_S_IRUSR|VKI_S_IWUSR);

    /* VG_(open) can return any negative number on error. Remap errors to -1,
     * to not confuse it with our special value -2
     */
    if (fd<0) fd = -1;

    return fd;
}

/* Run Info: Fixed information for a callgrind run */
static Int dump_info(Int fd)
{
    Char* buf = outbuf;
    int i;
    
    if ( (fd = createRes(fd)) <0) return fd;

    /* version */
    VG_(sprintf)(buf, "version: " VERSION "\n");
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    /* "pid:" line */
    VG_(sprintf)(buf, "pid: %d\n", VG_(getpid)());
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    /* "base:" line */
    VG_(sprintf)(buf, "base: %s\n", dump_base);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    
    /* "cmd:" line */
    VG_(strcpy)(buf, "cmd:");
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    for (i = 0; i < VG_(client_argc); i++) {
      VG_(sprintf)(buf, " %s", VG_(client_argv[i]));
      VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    }

    return fd;
}


/* Helper for dump_state */

Int dump_fd;

void static dump_state_of_thread(thread_info* ti)
{
    Char* buf = outbuf;
    int t = SK_(current_tid);
    Int p, i;
    static FullCost sum = 0, tmp = 0;
    BBCC *from, *to;
    call_entry* ce;

    p = VG_(sprintf)(buf, "events-%d: ", t);
    SK_(init_cost_lz)( SK_(sets).full, &sum );
    SK_(copy_cost_lz)( SK_(sets).full, &tmp, ti->lastdump_cost );
    SK_(add_diff_cost)( SK_(sets).full, sum,
                  ti->lastdump_cost,
                  ti->states.entry[0]->cost);
    SK_(copy_cost)( SK_(sets).full, ti->lastdump_cost, tmp );
    p += SK_(sprint_mappingcost)(buf + p, SK_(dumpmap), sum);
    p += VG_(sprintf)(buf+p, "\n");
    VG_(write)(dump_fd, (void*)buf, p);

    p = VG_(sprintf)(buf, "frames-%d: %d\n", t,
                 SK_(current_call_stack).sp);
    VG_(write)(dump_fd, (void*)buf, p);
    ce = 0;
    for(i = 0; i < SK_(current_call_stack).sp; i++) {
      ce = SK_(get_call_entry)(i);
      /* if this frame is skipped, we don't have counters */
      if (!ce->jcc) continue;
      
      from = ce->jcc->from;
      p = VG_(sprintf)(buf, "function-%d-%d: %s\n",t, i, 
                   from->cxt->fn[0]->name);         
      VG_(write)(dump_fd, (void*)buf, p);
      
      p = VG_(sprintf)(buf, "calls-%d-%d: ",t, i);
      p+= VG_(sprintf)(buf+p, "%llu\n", ce->jcc->call_counter);
      VG_(write)(dump_fd, (void*)buf, p);
      
      /* FIXME: EventSets! */
      SK_(copy_cost)( SK_(sets).full, sum, ce->jcc->cost );
      SK_(copy_cost)( SK_(sets).full, tmp, ce->enter_cost );
      SK_(add_diff_cost)( SK_(sets).full, sum,
                    ce->enter_cost, SK_(current_state).cost );
      SK_(copy_cost)( SK_(sets).full, ce->enter_cost, tmp );
      
      p = VG_(sprintf)(buf, "events-%d-%d: ",t, i);
      p += SK_(sprint_mappingcost)(buf + p, SK_(dumpmap), sum );
      p += VG_(sprintf)(buf+p, "\n");
      VG_(write)(dump_fd, (void*)buf, p);
    }
    if (ce && ce->jcc) {
      to = ce->jcc->to;
      p = VG_(sprintf)(buf, "function-%d-%d: %s\n",t, i, 
                   to->cxt->fn[0]->name );          
      VG_(write)(dump_fd, (void*)buf, p);
    }
}

/* Dump info on current callgrind state */
static Int dump_state(Int fd)
{
    Char* buf = outbuf;
    thread_info** th;
    int t, p;
    Int orig_tid = SK_(current_tid);

    if ( (fd = createRes(fd)) <0) return fd;

    VG_(sprintf)(buf, "instrumentation: %s\n",
             SK_(instrument_state) ? "on":"off");
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    if (!SK_(instrument_state)) return fd;

    VG_(sprintf)(buf, "executed-bbs: %llu\n", SK_(stat).bb_executions);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    VG_(sprintf)(buf, "executed-calls: %llu\n", SK_(stat).call_counter);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    VG_(sprintf)(buf, "distinct-bbs: %d\n", SK_(stat).distinct_bbs);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    VG_(sprintf)(buf, "distinct-calls: %d\n", SK_(stat).distinct_jccs);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    VG_(sprintf)(buf, "distinct-functions: %d\n", SK_(stat).distinct_fns);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    VG_(sprintf)(buf, "distinct-contexts: %d\n", SK_(stat).distinct_contexts);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    /* "events:" line. Given here because it will be dynamic in the future */
    p = VG_(sprintf)(buf, "events: ");
    SK_(sprint_eventmapping)(buf+p, SK_(dumpmap));
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
    VG_(write)(fd, "\n", 1);
            
    /* "part:" line (number of last part. Is 0 at start */
    VG_(sprintf)(buf, "\npart: %d\n", SK_(get_dump_counter)());
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
            
    /* threads */
    th = SK_(get_threads)();
    p = VG_(sprintf)(buf, "threads:");
    for(t=1;t<VG_N_THREADS;t++) {
      if (!th[t]) continue;
      p += VG_(sprintf)(buf+p, " %d", t);
    }
    p += VG_(sprintf)(buf+p, "\n");
    VG_(write)(fd, (void*)buf, p);

    VG_(sprintf)(buf, "current-tid: %d\n", orig_tid);
    VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

    /* current event counters */
    dump_fd = fd;
    SK_(forall_threads)(dump_state_of_thread);

    return fd;
}

void SK_(check_command)()
{
    /* check for dumps needed */
    static Char buf[512];
    static Char cmdBuffer[512];
    Char *cmdPos = 0, *cmdNextLine = 0;
    Int fd, bytesRead = 0, do_kill = 0;
    static Char* cfile = 0;

    if (!command_inited) return;

    /* toggle between 2 command files, with/without ".pid" postfix */
    cfile = ((cfile == command_file) || (cfile == 0)) ? 
      command_file2 : command_file;

    fd = VG_(open)(cfile, VKI_O_RDONLY,0);
    if (fd>=0) {
      bytesRead = VG_(read)(fd,cmdBuffer,500);
      cmdBuffer[500] = 0; /* no command overrun please */
      VG_(close)(fd);
      /* don't delete command file on read error (e.g. EAGAIN) */
      if (bytesRead>0) {
          cmdPos = cmdBuffer;
      }
    }

    /* force creation of result file if needed */
    fd = -2;

    while((bytesRead>0) && *cmdPos) {
      
      /* Calculate pointer for next line */
      cmdNextLine = cmdPos+1;
      while((bytesRead>0) && *cmdNextLine && (*cmdNextLine != '\n')) {
        cmdNextLine++;
        bytesRead--;
      }
      if ((bytesRead>0) && (*cmdNextLine == '\n')) {
        *cmdNextLine = 0;
        cmdNextLine++;
        bytesRead--;
      } 

      /* Command with integer option */
      if ((*cmdPos >= '0') && (*cmdPos <='9')) {
        int value = *cmdPos-'0';
        cmdPos++;
        while((*cmdPos >= '0') && (*cmdPos <='9')) {
          value = 10*value + (*cmdPos-'0');
          cmdPos++;
        }
        while((*cmdPos == ' ') || (*cmdPos == '\t')) cmdPos++;
        
        switch(*cmdPos) {
#if CT_ENABLE_DEBUG
          /* verbosity */
        case 'V':
        case 'v':
          SK_(clo).verbose = value;
          break;
#endif
        default:
          break;        
        }

        cmdPos = cmdNextLine;
        continue;
      }  

      /* Command with boolean/switch option */
      if ((*cmdPos=='+') || 
          (*cmdPos=='-')) {
        int value = (cmdPos[0] == '+');
        cmdPos++;
        while((*cmdPos == ' ') || (*cmdPos == '\t')) cmdPos++;
        
        switch(*cmdPos) {
        case 'I':
        case 'i':
          SK_(set_instrument_state)("Command", value);
          break;

        default:
          break;
        }

        cmdPos = cmdNextLine;
        continue;
      }

      /* regular command */
      switch(*cmdPos) {
      case 'D':
      case 'd':
        /* DUMP */

        /* skip command */
        while(*cmdPos && (*cmdPos != ' ')) cmdPos++;
        if (*cmdPos)
          VG_(sprintf)(buf, "Dump Command:%s", cmdPos);
        else
          VG_(sprintf)(buf, "Dump Command");
        SK_(dump_profile)(buf, False);
        break;
          
      case 'Z':
      case 'z':
          SK_(zero_all_cost)(False);
          break;

      case 'K':
      case 'k':
          /* Kill: Delay to be able to remove command file before. */
          do_kill = 1;
          break;

      case 'I':
      case 'i':
          fd = dump_info(fd);
          break;

      case 's':
      case 'S':
          fd = dump_state(fd);
          break;

      case 'O':
      case 'o':
          /* Options Info */
          if ( (fd = createRes(fd)) <0) break;

          VG_(sprintf)(buf, "\ndesc: Option: --skip-plt=%s\n",
                   SK_(clo).skip_plt ? "yes" : "no");
          VG_(write)(fd, (void*)buf, VG_(strlen)(buf));         
          VG_(sprintf)(buf, "desc: Option: --collect-jumps=%s\n",
                   SK_(clo).collect_jumps ? "yes" : "no");
          VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
          VG_(sprintf)(buf, "desc: Option: --separate-recs=%d\n",
                   SK_(clo).separate_recursions);
          VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
          VG_(sprintf)(buf, "desc: Option: --separate-callers=%d\n",
                   SK_(clo).separate_callers);
          VG_(write)(fd, (void*)buf, VG_(strlen)(buf));

          break;

      default:
        break;
      }

      cmdPos = cmdNextLine;
    }

    /* If command executed, delete command file */
    if (cmdPos) VG_(unlink)(cfile);
    if (fd>=0) VG_(close)(fd);          

    if (do_kill) {
      VG_(message)(Vg_UserMsg,
               "Killed because of command from %s", cfile);
      SK_(fini)(0);
      VG_(exit)(1);
    }
}

Generated by  Doxygen 1.6.0   Back to index