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