1 /****************************************************************************** 2 * 3 * Copyright International Business Machines Corp., 2006-2008 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * NAME 20 * gtod-latency.c 21 * 22 * DESCRIPTION 23 * Simple program to measure the time between several pairs of calls to 24 * gettimeofday(). If the average delta is greater than just a few 25 * microseconds on an unloaded system, then something is probably wrong. 26 * 27 * It is quite similar to the programs in the directory, but provides the 28 * additional capability to produce graphical output as a histogram or a 29 * scatter graph.* 30 * 31 * USAGE: 32 * Use run_auto.sh script in current directory to build and run test. 33 * 34 * AUTHOR 35 * Darren Hart <dvhltc (at) us.ibm.com> 36 * 37 * HISTORY 38 * 2006-Aug-17: Initial version by Darren Hart <dvhltc (at) us.ibm.com> 39 * 2006-Aug-23: Minor changes by John Kacur <jekacur (at) ca.ibm.com> 40 * 2006-Nov-20: Augmented to use libstats 41 * 2007-Jul-12: Latency tracing added by Josh Triplett <josh (at) kernel.org> 42 * 2007-Jul-13: Quantiles added by Josh Triplett <josh (at) kernel.org> 43 * 44 * This line has to be added to avoid a stupid CVS problem 45 *****************************************************************************/ 46 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <unistd.h> 50 #include <sys/time.h> 51 #include <sys/types.h> 52 #include <sched.h> 53 #include <errno.h> 54 #include <limits.h> 55 #include <libstats.h> 56 #include <librttest.h> 57 #include <sys/mman.h> 58 59 #define ITERATIONS 10000000 60 #define MIN_ITERATION 10000 61 #define HIST_BUCKETS 20 62 63 #define SCATTER_FILENAME 0 64 #define HIST_FILENAME 1 65 66 #define SCATTER_TITLE 0 67 #define HIST_TITLE 1 68 69 #define SCATTER_LABELX 0 70 #define SCATTER_LABELY 1 71 #define HIST_LABELX 2 72 #define HIST_LABELY 3 73 74 char *titles[] = { "scatter plot", 75 "histogram" 76 }; 77 78 char *filenames[] = { "scatter", 79 "hist" 80 }; 81 82 char *labels[] = { "scatter plot x-axis", 83 "scatter plot y-axis", 84 "histogram x-axis", 85 "histogram y-axis" 86 }; 87 88 static unsigned long long latency_threshold = 0; 89 static unsigned int iterations = ITERATIONS; 90 91 void stats_cmdline_help(void) 92 { 93 printf("Usage: ./gtod_latency {-[so|scatter-output] -[ho|hist-output]" 94 " -[st|scatter-title] -[ht|hist-title] -[sxl|scatter-xlabel]" 95 " -[syl|scatter-ylabel] -[hxl|hist-xlabel] -[hyl|hist-ylabel]" 96 " -[lt|latency-trace] -[i|iterations]}" " -[help] \n"); 97 printf 98 ("**command-line options are not supported yet for this testcase\n"); 99 } 100 101 int stats_cmdline(int argc, char *argv[]) 102 { 103 int i; 104 char *flag; 105 106 if (argc == 1) 107 return 0; 108 109 for (i = 1; i < argc; i++) { 110 if (*argv[i] != '-') { 111 printf("missing flag indicator\n"); 112 return -1; 113 } 114 115 flag = ++argv[i]; 116 117 if (!strcmp(flag, "help") || !strcmp(flag, "h")) { 118 stats_cmdline_help(); 119 exit(0); 120 } 121 122 if (!strcmp(flag, "so") || !strcmp(flag, "scatter-output")) { 123 if (i + 1 == argc) { 124 printf("flag has missing argument\n"); 125 return -1; 126 } 127 filenames[SCATTER_FILENAME] = argv[++i]; 128 continue; 129 } 130 131 if (!strcmp(flag, "ho") || !strcmp(flag, "hist-output")) { 132 if (i + 1 == argc) { 133 printf("flag has missing argument\n"); 134 return -1; 135 } 136 filenames[HIST_FILENAME] = argv[++i]; 137 continue; 138 } 139 140 if (!strcmp(flag, "st") || !strcmp(flag, "scatter-title")) { 141 if (i + 1 == argc) { 142 printf("flag has missing argument\n"); 143 return -1; 144 } 145 titles[SCATTER_TITLE] = argv[++i]; 146 continue; 147 } 148 149 if (!strcmp(flag, "ht") || !strcmp(flag, "hist-title")) { 150 if (i + 1 == argc) { 151 printf("flag has missing argument\n"); 152 return -1; 153 } 154 titles[HIST_TITLE] = argv[++i]; 155 continue; 156 } 157 158 if (!strcmp(flag, "sxl") || !strcmp(flag, "scatter-xlabel")) { 159 if (i + 1 == argc) { 160 printf("flag has missing argument\n"); 161 return -1; 162 } 163 labels[SCATTER_LABELX] = argv[++i]; 164 continue; 165 } 166 167 if (!strcmp(flag, "syl") || !strcmp(flag, "scatter-ylabel")) { 168 if (i + 1 == argc) { 169 printf("flag has missing argument\n"); 170 return -1; 171 } 172 labels[SCATTER_LABELY] = argv[++i]; 173 continue; 174 } 175 176 if (!strcmp(flag, "hxl") || !strcmp(flag, "hist-xlabel")) { 177 if (i + 1 == argc) { 178 printf("flag has missing argument\n"); 179 return -1; 180 } 181 labels[HIST_LABELX] = argv[++i]; 182 continue; 183 } 184 185 if (!strcmp(flag, "hyl") || !strcmp(flag, "hist-ylabel")) { 186 if (i + 1 == argc) { 187 printf("flag has missing argument\n"); 188 return -1; 189 } 190 labels[HIST_LABELY] = argv[++i]; 191 continue; 192 } 193 194 if (!strcmp(flag, "lt") || !strcmp(flag, "latency-trace")) { 195 if (i + 1 == argc) { 196 printf("flag has missing argument\n"); 197 return -1; 198 } 199 latency_threshold = strtoull(argv[++i], NULL, 0); 200 continue; 201 } 202 203 if (!strcmp(flag, "i") || !strcmp(flag, "iterations")) { 204 if (i + 1 == argc) { 205 printf("flag has missing argument\n"); 206 return -1; 207 } 208 iterations = strtoull(argv[++i], NULL, 0); 209 continue; 210 } 211 212 printf("unknown flag given\n"); 213 return -1; 214 } 215 216 return 0; 217 } 218 219 long long timespec_subtract(struct timespec *a, struct timespec *b) 220 { 221 long long ns; 222 ns = (b->tv_sec - a->tv_sec) * 1000000000LL; 223 ns += (b->tv_nsec - a->tv_nsec); 224 return ns; 225 } 226 227 int main(int argc, char *argv[]) 228 { 229 int i, j, k, err; 230 unsigned long long delta; 231 unsigned long long max, min; 232 struct sched_param param; 233 stats_container_t dat; 234 stats_container_t hist; 235 stats_quantiles_t quantiles; 236 stats_record_t rec; 237 struct timespec *start_data; 238 struct timespec *stop_data; 239 240 if (stats_cmdline(argc, argv) < 0) { 241 printf("usage: %s help\n", argv[0]); 242 exit(1); 243 } 244 245 if (iterations < MIN_ITERATION) { 246 iterations = MIN_ITERATION; 247 printf("user \"iterations\" value is too small (use: %d)\n", 248 iterations); 249 } 250 251 stats_container_init(&dat, iterations); 252 stats_container_init(&hist, HIST_BUCKETS); 253 stats_quantiles_init(&quantiles, (int)log10(iterations)); 254 setup(); 255 256 mlockall(MCL_CURRENT | MCL_FUTURE); 257 258 start_data = calloc(iterations, sizeof(struct timespec)); 259 if (start_data == NULL) { 260 printf("Memory allocation Failed (too many Iteration: %d)\n", 261 iterations); 262 exit(1); 263 } 264 stop_data = calloc(iterations, sizeof(struct timespec)); 265 if (stop_data == NULL) { 266 printf("Memory allocation Failed (too many Iteration: %d)\n", 267 iterations); 268 free(start_data); 269 exit(1); 270 } 271 272 /* switch to SCHED_FIFO 99 */ 273 param.sched_priority = sched_get_priority_max(SCHED_FIFO); 274 err = sched_setscheduler(0, SCHED_FIFO, ¶m); 275 276 /* Check that the user has the appropriate privileges */ 277 if (err) { 278 if (errno == EPERM) { 279 fprintf(stderr, 280 "This program runs with a scheduling policy of SCHED_FIFO at priority %d\n", 281 param.sched_priority); 282 fprintf(stderr, 283 "You don't have the necessary privileges to create such a real-time process.\n"); 284 } else { 285 fprintf(stderr, "Failed to set scheduler, errno %d\n", 286 errno); 287 } 288 exit(1); 289 } 290 291 printf("\n----------------------\n"); 292 printf("Gettimeofday() Latency\n"); 293 printf("----------------------\n"); 294 printf("Iterations: %d\n\n", iterations); 295 296 /* collect iterations pairs of gtod calls */ 297 max = min = 0; 298 if (latency_threshold) { 299 latency_trace_enable(); 300 latency_trace_start(); 301 } 302 /* This loop runs for a long time, hence can cause soft lockups. 303 Calling sleep periodically avoids this. */ 304 for (i = 0; i < (iterations / 10000); i++) { 305 for (j = 0; j < 10000; j++) { 306 k = (i * 10000) + j; 307 clock_gettime(CLOCK_MONOTONIC, &start_data[k]); 308 clock_gettime(CLOCK_MONOTONIC, &stop_data[k]); 309 } 310 usleep(1000); 311 } 312 for (i = 0; i < iterations; i++) { 313 delta = timespec_subtract(&start_data[i], &stop_data[i]); 314 rec.x = i; 315 rec.y = delta; 316 stats_container_append(&dat, rec); 317 if (i == 0 || delta < min) 318 min = delta; 319 if (delta > max) 320 max = delta; 321 if (latency_threshold && delta > latency_threshold) 322 break; 323 } 324 if (latency_threshold) { 325 latency_trace_stop(); 326 if (i != iterations) { 327 printf 328 ("Latency threshold (%lluus) exceeded at iteration %d\n", 329 latency_threshold, i); 330 latency_trace_print(); 331 stats_container_resize(&dat, i + 1); 332 } 333 } 334 335 stats_hist(&hist, &dat); 336 stats_container_save(filenames[SCATTER_FILENAME], titles[SCATTER_TITLE], 337 labels[SCATTER_LABELX], labels[SCATTER_LABELY], 338 &dat, "points"); 339 stats_container_save(filenames[HIST_FILENAME], titles[HIST_TITLE], 340 labels[HIST_LABELX], labels[HIST_LABELY], &hist, 341 "steps"); 342 343 /* report on deltas */ 344 printf("Min: %llu ns\n", min); 345 printf("Max: %llu ns\n", max); 346 printf("Avg: %.4f ns\n", stats_avg(&dat)); 347 printf("StdDev: %.4f ns\n", stats_stddev(&dat)); 348 printf("Quantiles:\n"); 349 stats_quantiles_calc(&dat, &quantiles); 350 stats_quantiles_print(&quantiles); 351 352 stats_container_free(&dat); 353 stats_container_free(&hist); 354 stats_quantiles_free(&quantiles); 355 356 return 0; 357 } 358