Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

instrumentation.h

Go to the documentation of this file.
00001 /*
00002  * instrumentation.h
00003  *
00004  * Copyright (c) 2003, 2004 The University of Utah and the Flux Group.
00005  * All rights reserved.
00006  *
00007  * This file is licensed under the terms of the GNU Public License.  
00008  * See the file "license.terms" for restrictions on redistribution 
00009  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00010  */
00011 
00012 #ifndef _misc_instrumentation_h
00013 #define _misc_instrumentation_h
00014 
00015 /**
00016  * @file instrumentation.h
00017  *
00018  * Header file for instrumentation tools.  Instrumenting blocks of code or
00019  * expressions is done by adding a call to CB_INSTR_POINT in the configure.in
00020  * file and surrounding the code with a macro call.  For example, to do high
00021  * resolution timings of the following code:
00022  *
00023  * @code
00024  *   for( lpc = 0; lpc < 100; lpc++ )
00025  *   {
00026  *       ...
00027  *   }
00028  * @endcode
00029  *
00030  * You would modify the configure.in to include:
00031  *
00032  * @code
00033  *   CB_INSTR_POINT([my_point],
00034  *                  [HRTIME_INSTR],
00035  *                  [My instrumentation point])
00036  * @endcode
00037  *
00038  * Where 'my_point' is the name of the instrumentation point, #HRTIME_INSTR is
00039  * the C macro that will record the timings, and 'My instrumentation point' is
00040  * the description of the point.  The result of this call will be two new
00041  * definitions in the config.h: 'INSTR_my_point' and 'INSTR_my_point_data'.
00042  * The first C macro is used to surround the code block and the second contains
00043  * the data used to initialize the instrumentation point structure (iPoint).
00044  * With these new definitions, we can modify our original code to add
00045  * instrumentation:
00046  *
00047  * @code
00048  *   #if defined(INSTR_my_point_data)
00049  *   static struct iPoint IP_my_point = {
00050  *       INSTR_my_point_data
00051  *   };
00052  *   #endif
00053  * ...
00054  *   INSTR_my_point(&IP_my_point, ({
00055  *       for( lpc = 0; lpc < 100; lpc++ )
00056  *       {
00057  *           ...
00058  *       }
00059  *   }));
00060  * @endcode
00061  *
00062  * (Notice the parentheses around the code block, these are required for things
00063  * to parse correctly.)
00064  *
00065  * Finally, to enable this instrumentation point, you either need to use
00066  * --enable-instrumentation to enable all of the points, or
00067  * --enable-instrumentation="my_point ..." to enable this point and any others
00068  * named.
00069  *
00070  * Any instrumentation data collected during a run is usually appended to a
00071  * file #iPrintPointsAtExit function.
00072  *
00073  * @see configure.in
00074  * @see instrumentation_test.c
00075  */
00076 
00077 #ifdef __cplusplus
00078 extern "C" {
00079 #endif
00080 
00081 #include <stdio.h>
00082 #include <stdlib.h>
00083 #include <sys/time.h>
00084 #include <sys/resource.h>
00085 
00086 /**
00087  * Container for low-resolution timer values.
00088  */
00089 typedef unsigned long long lrtime_t;
00090 
00091 /**
00092  * @return A low-resolution time stamp.  The current resolution is on the order
00093  * of microseconds.
00094  */
00095 static inline lrtime_t lrtime(void);
00096 
00097 static inline lrtime_t lrtime(void)
00098 {
00099     struct timeval tv;
00100     lrtime_t retval;
00101     
00102     if( gettimeofday(&tv, NULL) == -1 )
00103     {
00104         abort();
00105     }
00106     retval = (lrtime_t)tv.tv_usec / (lrtime_t)(1);
00107     retval += ((lrtime_t)tv.tv_sec * (lrtime_t)(1000 * 1000));
00108     
00109     return( retval );
00110 }
00111 
00112 /**
00113  * @return The low-resolution CPU usage for the current PID.  The current
00114  * resolution is on the order of microseconds, when available.
00115  */
00116 static inline lrtime_t lrcputime(void);
00117 
00118 static inline lrtime_t lrcputime(void)
00119 {
00120     struct rusage ru;
00121     lrtime_t retval;
00122 
00123     if( getrusage(RUSAGE_SELF, &ru) == -1 )
00124     {
00125         abort();
00126     }
00127     retval = (lrtime_t)ru.ru_utime.tv_usec / (lrtime_t)(1);
00128     retval += ((lrtime_t)ru.ru_utime.tv_sec * (lrtime_t)(1000 * 1000));
00129     retval += (lrtime_t)ru.ru_stime.tv_usec / (lrtime_t)(1);
00130     retval += ((lrtime_t)ru.ru_stime.tv_sec * (lrtime_t)(1000 * 1000));
00131 
00132     return( retval );
00133 }
00134 
00135 /**
00136  * Container for high-resolution timer values.
00137  */
00138 typedef unsigned long long hrtime_t;
00139 
00140 /**
00141  * @return A high-resolution time stamp.
00142  */
00143 static inline hrtime_t hrtime(void);
00144 
00145 #if defined(i386)
00146 static inline hrtime_t hrtime(void)
00147 {
00148     unsigned long long retval;
00149     
00150     __asm__ __volatile__ ("rdtsc" : "=A" (retval) );
00151     return( retval );
00152 }
00153 #else
00154 #warning "CPU timestamp counter not available, using gettimeofday"
00155 static inline hrtime_t hrtime(void)
00156 {
00157     struct timeval tv;
00158     hrtime_t retval;
00159     
00160     gettimeofday(&tv, 0);
00161     retval = (hrtime_t)tv.tv_usec;
00162     retval += ((hrtime_t)tv.tv_sec * (hrtime_t)(1000 * 1000));
00163     return( retval );
00164 }
00165 #endif
00166 
00167 enum {
00168     IPB_PRINT_HISTORY,
00169     IPB_DROP_HISTORY_START,
00170 };
00171 
00172 /**
00173  * Flags for the iPoint structure.
00174  */
00175 enum {
00176     /** Print the entire history in addition to the summary data. */
00177     IPF_PRINT_HISTORY = (1L << IPB_PRINT_HISTORY),
00178     /** Drop data values at the start of the history instead of the tail. */
00179     IPF_DROP_HISTORY_START = (1L << IPB_DROP_HISTORY_START),
00180 
00181     /** Bitmask covering all of the meaningful flag bits. */
00182     IPF_MASK = (IPF_PRINT_HISTORY |
00183                 IPF_DROP_HISTORY_START)
00184 };
00185 
00186 /*
00187  * ip_Name - The name of this instrumentation point.
00188  * ip_Description - A description of this instrumentation point, can be NULL.
00189  * ip_Flags - Holder for any IPF_ flags.
00190  * ip_History.data - An array that can contain atleast ip_History.length
00191  *   elements that should be used to record data values as they are posted.  If
00192  *   left NULL, the values won't be recorded and only statistics will be
00193  *   available.
00194  * ip_History.length - The maximum number of elements that can be placed in
00195  *   the 'data' array.
00196  * ip_History.start - The index where the oldest recorded value is in the
00197  *   'data' array.  Normally, this will be zero, unless the
00198  *   IPF_DROP_HISTORY_START flag is set and the 'data' array overflowed.
00199  * ip_History.lost - The number of data values that were not added to the
00200  *   'data' array because it overflowed.
00201  * ip_Format - The format to use when printing values.  If this is NULL, the
00202  *   default format, '%10.2f  ', is used.
00203  * ip_Count - The number of data values posted to this instrumentation point.
00204  * ip_Total - The sum of the data values posted.
00205  * ip_Minimum - The minimum observed data value that has been posted.
00206  * ip_Maximum - The maximum observed data value that has been posted.
00207  * ip_Succ - Link to the next instrumentation point in the global linked list.
00208  *   Note, the instrumentation point is not added to the list until the first
00209  *   data value is posted.
00210  */
00211 struct iPoint {
00212     const char *ip_Name;
00213     const char *ip_Description;
00214     unsigned long ip_Flags;
00215     struct {
00216         double *data;
00217         size_t length;
00218         unsigned int start;
00219         unsigned int lost;
00220     } ip_History;
00221     const char *ip_Format;
00222     unsigned long long ip_Count;
00223     double ip_Total;
00224     double ip_Minimum;
00225     double ip_Maximum;
00226     struct iPoint *ip_Succ;
00227 };
00228 
00229 /**
00230  * Macro used to statically initialize the ip_History field of the iPoint
00231  * structure.
00232  *
00233  * @param history A statically allocated array of doubles.
00234  */
00235 #define IPOINT_INIT_HISTORY(history) { \
00236     history, \
00237     sizeof(history) / sizeof(double) \
00238 }
00239 
00240 /**
00241  * Macro used to perform high-resolution timings of a block of code.
00242  *
00243  * @param point The iPoint to use when recording data.
00244  * @param block The code block to instrument.
00245  *
00246  * @see NOINSTR
00247  */
00248 #define HRTIME_INSTR(point, block) { \
00249     hrtime_t _start, _end; \
00250 \
00251     _start = hrtime(); \
00252     block; \
00253     _end = hrtime(); \
00254     iPostFloatData(point, (_end - _start)); \
00255 }
00256 
00257 /**
00258  * Macro used to perform low-resolution timings of a block of code.
00259  *
00260  * @param point The iPoint to use when recording data.
00261  * @param block The code block to instrument.
00262  *
00263  * @see NOINSTR
00264  */
00265 #define LRTIME_INSTR(point, block) { \
00266     lrtime_t _start, _end; \
00267 \
00268     _start = lrtime(); \
00269     block; \
00270     _end = lrtime(); \
00271     iPostFloatData(point, (_end - _start)); \
00272 }
00273 
00274 /**
00275  * Macro used to perform low-resolution CPU timings of a block of code.
00276  *
00277  * @param point The iPoint to use when recording data.
00278  * @param block The code block to instrument.
00279  *
00280  * @see NOINSTR
00281  */
00282 #define LRCPUTIME_INSTR(point, block) { \
00283     lrtime_t _start, _end; \
00284 \
00285     _start = lrcputime(); \
00286     block; \
00287     _end = lrcputime(); \
00288     iPostFloatData(point, (_end - _start)); \
00289 }
00290 
00291 /**
00292  * Macro used to accumulate the value of a numeral expression.
00293  *
00294  * @param point The iPoint to use when recording data.
00295  * @param expr An expression that results in a number value.
00296  *
00297  * @see NOINSTR
00298  */
00299 #define ACCUM_INSTR(point, expr) { \
00300     iPostFloatData(point, (double)(expr)); \
00301 }
00302 
00303 /**
00304  * Macro used to avoid instrumenting a block of code.
00305  *
00306  * @param point Not used.  This is only here so it can mimic the other INSTR
00307  * macros.
00308  * @param block A block of code to execute.
00309  */
00310 #define NOINSTR(point, block) (void)(block)
00311 
00312 /**
00313  * The environment variable name to check for the instrumentation output file
00314  * name.
00315  */
00316 #define INSTR_FILE_NAME_ENV "CB_INSTR_FILE_NAME"
00317 
00318 /*
00319  * The structure of global data needed by the instrumentation infrastructure.
00320  *
00321  * iid_OutputFileName - The name of the file where results should be written.
00322  * If NULL, a file be created with a name having the form:
00323  * '<PACKAGE>-instrumentation-<pid>.txt'.  If it is a '-', the results will be
00324  * written to standard out.
00325  * iid_FirstPoint - The first instrumentation point in the list.
00326  * iid_NullPoint - A special instrumentation point value used to detect whether
00327  * an iPoint object is in the global list or not.
00328  */
00329 struct iInstrumentationData {
00330     const char *iid_OutputFileName;
00331     struct iPoint *iid_FirstPoint;
00332     struct iPoint iid_NullPoint;
00333 };
00334 
00335 /**
00336  * The global data needed by the instrumentation infrastructure.
00337  */
00338 extern struct iInstrumentationData instrumentation_data;
00339 
00340 /**
00341  * Post a floating point data value to an instrumentation point.
00342  *
00343  * @param ip The instrumentation point to add the given value to.
00344  * @param value The value to add.
00345  */
00346 void iPostFloatData(struct iPoint *ip, double value);
00347 
00348 /**
00349  * Print the collected values for an instrumentation point.  The summary data
00350  * printed depends on whether or not a history array has been provided:  no
00351  * history means the summary covers all of the posted data; otherwise the
00352  * summary covers only the data held in the history.
00353  *
00354  * @param file The file to print the data to.
00355  * @param ip The instrumentation point to print out.
00356  */
00357 void iPrintPoint(FILE *file, struct iPoint *ip);
00358 
00359 /**
00360  * Print all of the instrumentation points that have had data posted to them
00361  * using iPrintPoint.
00362  *
00363  * @param file The file to print the data to.
00364  */
00365 void iPrintPoints(FILE *file);
00366 
00367 /**
00368  * An atexit(3) function that calls iPrintPoints with a file from one of the
00369  * following sources (listed in order of precedence):
00370  *
00371  * @li The value of the CB_INSTR_FILE_NAME environment variable, if it is
00372  * defined.  Note: A value of "-" represents stdout.
00373  * @li The instrumentation_data.iid_OutputFileName variable, if it is non-NULL.
00374  * Note: A value of "-" represents stdout.
00375  * @li A formatted file name derived from PACKAGE and the current process ID.
00376  *
00377  * All of the files are opened in append mode and the current time is printed
00378  * out before calling #iPrintPoints.  When the function finishes it will clear
00379  * instrumentation_data.iid_FirstPoint so that any future calls to this
00380  * function will not result in data being printed more than once.
00381  */
00382 void iPrintPointsAtExit(void);
00383 
00384 #ifdef __cplusplus
00385 }
00386 #endif
00387 
00388 #endif

Generated on Fri Oct 22 07:50:24 2004 for CPU Broker by  doxygen 1.3.9.1