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