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
/********************************************************************/
#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.
**********************************************************************/
// [....]