00001 /* 00002 * instrumentation.h 00003 * 00004 * Copyright (c) 2003 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 only this point. 00068 * 00069 * @see configure.in 00070 * @see instrumentation_test.c 00071 */ 00072 00073 #ifdef __cplusplus 00074 extern "C" { 00075 #endif 00076 00077 #include <stdio.h> 00078 #include <sys/time.h> 00079 00080 /** 00081 * Container for low-resolution timer values. 00082 */ 00083 typedef unsigned long long lrtime_t; 00084 00085 /** 00086 * @return A low-resolution time stamp. The current resolution is on the order 00087 * of milliseconds. 00088 */ 00089 static inline lrtime_t lrtime(void); 00090 00091 static inline lrtime_t lrtime(void) 00092 { 00093 struct timeval tv; 00094 lrtime_t retval; 00095 00096 gettimeofday(&tv, 0); 00097 retval = (lrtime_t)tv.tv_usec / (lrtime_t)(1000); 00098 retval += ((lrtime_t)tv.tv_sec * (lrtime_t)(1000)); 00099 return( retval ); 00100 } 00101 00102 /** 00103 * Container for high-resolution timer values. 00104 */ 00105 typedef unsigned long long hrtime_t; 00106 00107 /** 00108 * @return A high-resolution time stamp. 00109 */ 00110 static inline hrtime_t hrtime(void); 00111 00112 #if defined(i386) 00113 static inline hrtime_t hrtime(void) 00114 { 00115 unsigned long long retval; 00116 00117 __asm__ __volatile__ ("rdtsc" : "=A" (retval) ); 00118 return( retval ); 00119 } 00120 #else 00121 #warning "CPU timestamp counter not available, using gettimeofday" 00122 static inline hrtime_t hrtime(void) 00123 { 00124 struct timeval tv; 00125 hrtime_t retval; 00126 00127 gettimeofday(&tv, 0); 00128 retval = (hrtime_t)tv.tv_usec; 00129 retval += ((hrtime_t)tv.tv_sec * (hrtime_t)(1000 * 1000)); 00130 return( retval ); 00131 } 00132 #endif 00133 00134 enum { 00135 IPB_DROP_HISTORY_START, 00136 }; 00137 00138 /* 00139 * IPF_DROP_HISTORY_START - Drop data values at the start of the history 00140 * instead of the tail. 00141 */ 00142 enum { 00143 IPF_DROP_HISTORY_START, 00144 }; 00145 00146 /* 00147 * ip_Name - The name of this instrumentation point. 00148 * ip_Description - A description of this instrumentation point, can be NULL. 00149 * ip_Flags - XXX 00150 * ip_History.data - An array that can contain atleast ip_History.length 00151 * elements that should be used to record data values as they are posted. If 00152 * left NULL, the values won't be recorded and only statistics will be 00153 * available. 00154 * ip_History.length - The maximum number of elements that can be placed in 00155 * the 'data' array. 00156 * ip_History.start - The index where the oldest recorded value is in the 00157 * 'data' array. Normally, this will be zero, unless the 00158 * IPF_DROP_HISTORY_START flag is set and the 'data' array overflowed. 00159 * ip_History.lost - The number of data values that were not added to the 00160 * 'data' array because it overflowed. 00161 * ip_Format - The format to use when printing values. If this is NULL, the 00162 * default format, '%10.2f ', is used. 00163 * ip_Count - The number of data values posted to this instrumentation point. 00164 * ip_Total - The sum of the data values posted. 00165 * ip_Minimum - The minimum observed data value that has been posted. 00166 * ip_Maximum - The maximum observed data value that has been posted. 00167 * ip_Succ - Link to the next instrumentation point in the global linked list. 00168 * Note, the instrumentation point is not added to the list until the first 00169 * data value is posted. 00170 */ 00171 struct iPoint { 00172 const char *ip_Name; 00173 const char *ip_Description; 00174 unsigned long ip_Flags; 00175 struct { 00176 double *data; 00177 size_t length; 00178 unsigned int start; 00179 unsigned int lost; 00180 } ip_History; 00181 const char *ip_Format; 00182 unsigned long long ip_Count; 00183 double ip_Total; 00184 double ip_Minimum; 00185 double ip_Maximum; 00186 struct iPoint *ip_Succ; 00187 }; 00188 00189 /** 00190 * Macro used to statically initialize the ip_History field of the iPoint 00191 * structure. 00192 * 00193 * @param history A statically allocated array of doubles. 00194 */ 00195 #define IPOINT_INIT_HISTORY(history) { \ 00196 history, \ 00197 sizeof(history) / sizeof(double) \ 00198 } 00199 00200 /** 00201 * Macro used to perform high-resolution timings of a block of code. 00202 * 00203 * @param point The iPoint to use when recording data. 00204 * @param block The code block to instrument. 00205 * 00206 * @see NOINSTR 00207 */ 00208 #define HRTIME_INSTR(point, block) { \ 00209 hrtime_t _start, _end; \ 00210 \ 00211 _start = hrtime(); \ 00212 block; \ 00213 _end = hrtime(); \ 00214 iPostFloatData(point, (_end - _start)); \ 00215 } 00216 00217 /** 00218 * Macro used to perform low-resolution timings of a block of code. 00219 * 00220 * @param point The iPoint to use when recording data. 00221 * @param block The code block to instrument. 00222 * 00223 * @see NOINSTR 00224 */ 00225 #define LRTIME_INSTR(point, block) { \ 00226 lrtime_t _start, _end; \ 00227 \ 00228 _start = lrtime(); \ 00229 block; \ 00230 _end = lrtime(); \ 00231 iPostFloatData(point, (_end - _start)); \ 00232 } 00233 00234 /** 00235 * Macro used to record the value of an numeral expression. 00236 * 00237 * @param point The iPoint to use when recording data. 00238 * @param expr An expression that results in a number value. 00239 * 00240 * @see NOINSTR 00241 */ 00242 #define ACCUM_INSTR(point, expr) { \ 00243 iPostFloatData(point, (double)(expr)); \ 00244 } 00245 00246 /** 00247 * Macro used to avoid instrumenting a block of code. 00248 * 00249 * @param point Not used. This is only here so it can mimic the other INSTR 00250 * macros. 00251 * @param block A block of code to execute. 00252 * 00253 * @see NOINSTR 00254 */ 00255 #define NOINSTR(point, block) block 00256 00257 /* 00258 * The structure of global data needed by the instrumentation infrastructure. 00259 * 00260 * iid_OutputFileName - The name of the file where results should be written. 00261 * If NULL, a file be created with a name having the form: 00262 * '<PACKAGE>-instrumentation-<pid>.txt'. If it is a '-', the results will be 00263 * written to standard out. 00264 * iid_FirstPoint - The first instrumentation point in the list. 00265 * iid_NullPoint - A special instrumentation point value used to detect whether 00266 * an iPoint object is in the global list or not. 00267 */ 00268 struct iInstrumentationData { 00269 const char *iid_OutputFileName; 00270 struct iPoint *iid_FirstPoint; 00271 struct iPoint iid_NullPoint; 00272 }; 00273 00274 /** 00275 * The global data needed by the instrumentation infrastructure. 00276 */ 00277 extern struct iInstrumentationData instrumentation_data; 00278 00279 /** 00280 * Post a floating point data value to an instrumentation point. 00281 * 00282 * @param ip The instrumentation point to add the given value to. 00283 * @param value The value to add. 00284 */ 00285 void iPostFloatData(struct iPoint *ip, double value); 00286 00287 /** 00288 * Print the collected values for an instrumentation point. 00289 * 00290 * @param file The file to print the data to. 00291 * @param ip The instrumentation point to print out. 00292 */ 00293 void iPrintPoint(FILE *file, struct iPoint *ip); 00294 00295 /** 00296 * Print all of the instrumentation points that have had data posted to them. 00297 * 00298 * @param file The file to print the data to. 00299 */ 00300 void iPrintPoints(FILE *file); 00301 00302 /** 00303 * An atexit(3) function that calls iPrintPoints with a file whose name is 00304 * taken from instrumentation_data.iid_OutputFileName or derived from the 00305 * package name and process ID. 00306 */ 00307 void iPrintPointsAtExit(void); 00308 00309 #ifdef __cplusplus 00310 } 00311 #endif 00312 00313 #endif