next up previous contents
Next: Auxillary routines Up: Source Code Previous: select_alternatives.cc

Profiling Implementation

qprofiling.h

extern int qp_initialize(char *base_filename, int counters, int verbose);
extern int qp_dump(int verbose);

#ifdef __GNUC__
extern unsigned int *qp_timestamps_hi;
extern unsigned int *qp_timestamps_lo;
extern unsigned int *qp_counters;
extern unsigned long long int *qp_totals;
extern unsigned int qp_counters_count;
extern unsigned int qp_tmp_hi;
extern unsigned int qp_tmp_lo;
#define qp_RDTSC ".hword 0x310F" /* Assembly form: db 0Fh 31h */
#endif

/********************************************************************/

/* See qprofiling.c for info about how gcc handles the inline keyword. */

#ifdef __GNUC__
inline extern void qp_start(int counter, int careful)
{
#if 0
  if (careful) /* Prevents accessing memory past malloc'ed amount */
    counter = counter % qp_counters_count;
#endif
  qp_counters[counter]++;
  if (qp_counters[counter] == 1)
    {
      {
	register unsigned int eax asm ("%eax");
	register unsigned int edx asm ("%edx");

	asm volatile (qp_RDTSC);
	qp_tmp_hi = edx; qp_tmp_lo = eax;
      }
      qp_timestamps_hi[counter] = qp_tmp_hi;
      qp_timestamps_lo[counter] = qp_tmp_lo;
    }
}
#else
extern void qp_start(int counter, int careful);
#endif

/********************************************************************/

#ifdef __GNUC__
inline extern void qp_stop(int counter, int careful)
{
#if 0
  if (careful) /* Prevents accessing memory past malloc'ed amount */
    counter = counter % qp_counters_count;
#endif
  if (qp_counters[counter] == 1)
    {
      /* Diff timestamps[counter] with the current time, and
	 add to qp_totals[counter] */
      register unsigned int eax asm ("%eax");
      register unsigned int edx asm ("%edx");

      asm volatile (qp_RDTSC);
      qp_tmp_hi = edx; qp_tmp_lo = eax;

/*
      printf("Start (EDX:EAX): %x:%x.\n",
	     qp_timestamps_hi[counter], qp_timestamps_lo[counter]);
      printf("Finish (EDX:EAX): %x:%x.\n",
	     qp_tmp_hi, qp_tmp_lo);
*/

      if (qp_tmp_hi == qp_timestamps_hi[counter])
	/* Common case */
	qp_totals[counter] += (qp_tmp_lo - qp_timestamps_lo[counter]);
      else
	{
	  if (qp_tmp_lo < qp_timestamps_lo[counter])
	    /* Need to borrow to perform subtraction... */
	    qp_totals[counter] +=
	      (((unsigned long long int)1<<32) - qp_timestamps_lo[counter])
	      + qp_tmp_lo
	      + (((unsigned long long int)1<<32)
		 * ((--qp_tmp_hi) - qp_timestamps_hi[counter]));
	  else
	    qp_totals[counter] +=
	      (qp_tmp_lo - qp_timestamps_lo[counter])
	      + (((unsigned long long int)1<<32)
		 * (qp_tmp_hi - qp_timestamps_hi[counter]));
	}
      qp_counters[counter]--;
      return;
    }
  else if (qp_counters[counter] > 1)
    {
      qp_counters[counter]--;
      return;
    }
  else /* qp_counters[counter] < 0 */
    qp_counters[counter] = 0;
}
#else
extern void qp_stop(int counter, int careful);
#endif

/********************************************************************/

qprofiling.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/********************************************************************/

unsigned int *qp_timestamps_hi = NULL;
unsigned int *qp_timestamps_lo = NULL;
unsigned int *qp_counters = NULL;
unsigned long long int *qp_totals = NULL;
unsigned int qp_counters_count;
unsigned int qp_tmp_hi, qp_tmp_lo;
char *qp_basefilename = NULL;
char *qp_filename = NULL;
const qp_SUCCESS = 1;
const qp_FAILURE = 0;
#define qp_RDTSC ".hword 0x310F" /* Assembly form: db 0Fh 31h */

/********************************************************************/

int qp_initialize(char *base_filename, int counters, int verbose)
{
  FILE *file;

  qp_counters_count = counters;
  if (((qp_timestamps_hi = (unsigned int *)
	calloc(counters,sizeof(unsigned int))) == NULL)
      || ((qp_timestamps_lo = (unsigned int *)
	   calloc(counters,sizeof(unsigned int))) == NULL)
      || ((qp_counters = (unsigned int *)
	   calloc(counters,sizeof(unsigned int))) == NULL)
      || ((qp_totals = (unsigned long long int *)
	   calloc(counters,sizeof(unsigned long long int))) == NULL))
    {
      if (verbose)
	fprintf(stderr, __FILE__ ": failed allocating profiling buffers.\n");
      return qp_FAILURE;
    }

  qp_basefilename = strdup(base_filename);
  if ((qp_filename = (char *) malloc(1024)) == NULL)
    return qp_FAILURE;
  sprintf(qp_filename, "%s.qprofiling_data", qp_basefilename);
  qp_filename = (char *) realloc(qp_filename, strlen(qp_filename)+1);

  file = fopen(qp_filename,"a+");
  if (file == NULL)
    {
      if (verbose)
	fprintf(stderr, __FILE__ ": failed opening output file '%s'.\n",
		qp_filename);
      return qp_FAILURE;
    }
  else
    fclose(file);
  return qp_SUCCESS;
}

/********************************************************************/

/* Adapted from itoa(), K&R second edition, page 64 */
void lltoa(unsigned long long int n, char s[])
{
  int i = 0;
  do {
    s[i++] = (n % 10) + '0';
  } while ((n /= 10) > 0);
  s[i] = '\0';
  {
    int c,i,j;
    for (i = 0, j = strlen(s)-1; i<j; i++, j--)
      {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
      }
  }
}

/****************/

int qp_dump(int verbose)
{
  FILE *file;
  unsigned int int_buffer;
  long int new_file_size;
  int output_problem = 0;
  int bytes_written = 0;
  int runs = 0;

  /* Diagnostic output */
  if (verbose)
    {
      int some_output_flag = 0;
      int i;
      for (i=0; i<qp_counters_count; i++)
	{
	  if (qp_totals[i] != 0)
	    {
	      static char buffer[1024];
	      lltoa(qp_totals[i], buffer);
	      fprintf(stderr,"Slot %u: %s. ", i, buffer);
	      some_output_flag++;
	    }
	}
      if (some_output_flag)
	{
	  fprintf(stderr,"\n");
	}
      else
	{
	  fprintf(stderr,"All profiling slots had zero values.\n");
	}
    }

  /* WARNING: at this time, this code does not do *ANYTHING* about
     file locking.  If two copies of a profiled program start up and
     are running at the same time, eit. */

  file = fopen(qp_filename,"a+");
  if (file == NULL)
    {
      if (verbose)
	fprintf(stderr, __FILE__ ": failed opening output file '%s'.\n",
		qp_filename);
      return qp_FAILURE;
    }

#define qp_OUTPUT(addr,obj_size,obj_count) \
  if (fwrite(addr,obj_size,obj_count,file) < obj_count) \
    output_problem++; \
  else \
    bytes_written += obj_size * obj_count;

  int_buffer = 0xEFBEADDE; /* Magic cookie */
  qp_OUTPUT(&int_buffer, sizeof(unsigned int), 1);

  int_buffer = 0x00000001; /* File format version identifier */
  qp_OUTPUT(&int_buffer, sizeof(unsigned int), 1);

  int_buffer = qp_counters_count;
  qp_OUTPUT(&int_buffer, sizeof(unsigned int), 1);

  qp_OUTPUT(qp_totals, sizeof(unsigned long long int), qp_counters_count);

  fflush(file);
  new_file_size = ftell(file);
  if (fclose(file) != 0) output_problem++;

  runs = atoi(getenv("RUNS"));
  if (runs == 0)
    runs = 4;

  if (new_file_size >= (runs * bytes_written))
    {
      char buffer[1024];
      sprintf(buffer,"select_alternatives %s -p %s %s %s.s_a.temp",
	      (verbose == 0) ? "" : "-d",
	      qp_filename, qp_basefilename, qp_basefilename);
      if (verbose)
	fprintf(stderr, "Accumulated enough results, "
		"invoking select_alternatives....\n");
      system(buffer);
    }

  return (output_problem == 0)? qp_SUCCESS : qp_FAILURE;
}

/**********************************************************************

Library documentation (draft)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The "qp_" prefix is an attempt to reduce the chance of name collision.

 ==== int qp_initialize(char *base_filename, int counters, int verbose)

Performs any set-up necessary.  Filename argument is used as the base
name for the data file to store profiling output; qp_initialize may
check immediately if such a file is writeable.  Counters argument
indicate how many different "stopwatch" counters will be allocated.
Function returns qp_SUCCESS if it is successful, qp_FAILURE if there
is a problem.  In the latter case, the function may write diagnostic
information out to stderr if the verbose argument is nonzero, and
further calls to qp_* functions will result in undefined program
behavior.  [Alternatively, we could have the other qp_* functions
defined to do nothing if qp_initialize fails; that would make for
nicer semantics -- the program will still work, even if saving
profiling data is going to fail -- but at the price of greater
run-time overhead, since the implementation which comes immediately to
mind is to set a flag which is checked every time qp_start and qp_stop
is called.  Indirecting calls to qp_start and qp_stop through global
variables which qp_initialize could set to different functions strikes
me as roughly the same magnitude of overhead (admittedly, small).]

 ==== int qp_dump(int verbose)

Writes out profiling data to file designated by filename argument to
qp_initialize.  This function should only be called once; if it is
called multiple times, each time it will overwrite any data previously
written-out during this program execution.  Function returns
qp_SUCCESS if it is successful, qp_FAILURE if there was a problem.  In
the latter case, the function may write diagnostic information to
stderr if the verbose argument is nonzero.

 ==== void qp_start(int counter, int careful)
 ==== void qp_stop(int counter, int careful)

For any given value for the counter argument, the time elapsed between
the first call to qp_start and the matching call (for example, think
matching parentheses) to qp_stop is recorded.  The meaning of elapsed
time is purposely left undefined here; in particular, it may mean wall
clock time *OR* the equivalent of UNIX user+system time, or something
completely different.  Calls to these functions with different values
for the counter argument may be freely interleaved.  If the careful
argument is non-zero, some sanity checking will be performed: the
value for the counter argument will be bound-checked against the value
of the counters argument to qp_initialize, and an error message will
be issued to stderr if the number of calls to qp_stop for a given
counter value exceeds the number of calls to qp_start.

**********************************************************************/

// [....]



Reinventing Computing, MIT AI Lab. Author: pshuang@ai.mit.edu (Ping Huang)