/****************************************************************************** * * Copyright © International Business Machines Corp., 2006-2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * NAME * gtod-latency.c * * DESCRIPTION * Simple program to measure the time between several pairs of calls to * gettimeofday(). If the average delta is greater than just a few * microseconds on an unloaded system, then something is probably wrong. * * It is quite similar to the programs in the directory, but provides the * additional capability to produce graphical output as a histogram or a * scatter graph.* * * USAGE: * Use run_auto.sh script in current directory to build and run test. * * AUTHOR * Darren Hart <dvhltc@us.ibm.com> * * HISTORY * 2006-Aug-17: Initial version by Darren Hart <dvhltc@us.ibm.com> * 2006-Aug-23: Minor changes by John Kacur <jekacur@ca.ibm.com> * 2006-Nov-20: Augmented to use libstats * 2007-Jul-12: Latency tracing added by Josh Triplett <josh@kernel.org> * 2007-Jul-13: Quantiles added by Josh Triplett <josh@kernel.org> * * This line has to be added to avoid a stupid CVS problem *****************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <sched.h> #include <errno.h> #include <limits.h> #include <libstats.h> #include <librttest.h> #include <sys/mman.h> #define ITERATIONS 10000000 #define MIN_ITERATION 10000 #define HIST_BUCKETS 20 #define SCATTER_FILENAME 0 #define HIST_FILENAME 1 #define SCATTER_TITLE 0 #define HIST_TITLE 1 #define SCATTER_LABELX 0 #define SCATTER_LABELY 1 #define HIST_LABELX 2 #define HIST_LABELY 3 char *titles[] = { "scatter plot", "histogram" }; char *filenames[] = { "scatter", "hist" }; char *labels[] = { "scatter plot x-axis", "scatter plot y-axis", "histogram x-axis", "histogram y-axis" }; static unsigned long long latency_threshold = 0; static unsigned int iterations = ITERATIONS; void stats_cmdline_help(void) { printf("Usage: ./gtod_latency {-[so|scatter-output] -[ho|hist-output]" " -[st|scatter-title] -[ht|hist-title] -[sxl|scatter-xlabel]" " -[syl|scatter-ylabel] -[hxl|hist-xlabel] -[hyl|hist-ylabel]" " -[lt|latency-trace] -[i|iterations]}" " -[help] \n"); printf ("**command-line options are not supported yet for this testcase\n"); } int stats_cmdline(int argc, char *argv[]) { int i; char *flag; if (argc == 1) return 0; for (i = 1; i < argc; i++) { if (*argv[i] != '-') { printf("missing flag indicator\n"); return -1; } flag = ++argv[i]; if (!strcmp(flag, "help") || !strcmp(flag, "h")) { stats_cmdline_help(); exit(0); } if (!strcmp(flag, "so") || !strcmp(flag, "scatter-output")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } filenames[SCATTER_FILENAME] = argv[++i]; continue; } if (!strcmp(flag, "ho") || !strcmp(flag, "hist-output")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } filenames[HIST_FILENAME] = argv[++i]; continue; } if (!strcmp(flag, "st") || !strcmp(flag, "scatter-title")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } titles[SCATTER_TITLE] = argv[++i]; continue; } if (!strcmp(flag, "ht") || !strcmp(flag, "hist-title")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } titles[HIST_TITLE] = argv[++i]; continue; } if (!strcmp(flag, "sxl") || !strcmp(flag, "scatter-xlabel")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } labels[SCATTER_LABELX] = argv[++i]; continue; } if (!strcmp(flag, "syl") || !strcmp(flag, "scatter-ylabel")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } labels[SCATTER_LABELY] = argv[++i]; continue; } if (!strcmp(flag, "hxl") || !strcmp(flag, "hist-xlabel")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } labels[HIST_LABELX] = argv[++i]; continue; } if (!strcmp(flag, "hyl") || !strcmp(flag, "hist-ylabel")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } labels[HIST_LABELY] = argv[++i]; continue; } if (!strcmp(flag, "lt") || !strcmp(flag, "latency-trace")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } latency_threshold = strtoull(argv[++i], NULL, 0); continue; } if (!strcmp(flag, "i") || !strcmp(flag, "iterations")) { if (i + 1 == argc) { printf("flag has missing argument\n"); return -1; } iterations = strtoull(argv[++i], NULL, 0); continue; } printf("unknown flag given\n"); return -1; } return 0; } long long timespec_subtract(struct timespec *a, struct timespec *b) { long long ns; ns = (b->tv_sec - a->tv_sec) * 1000000000LL; ns += (b->tv_nsec - a->tv_nsec); return ns; } int main(int argc, char *argv[]) { int i, j, k, err; unsigned long long delta; unsigned long long max, min; struct sched_param param; stats_container_t dat; stats_container_t hist; stats_quantiles_t quantiles; stats_record_t rec; struct timespec *start_data; struct timespec *stop_data; if (stats_cmdline(argc, argv) < 0) { printf("usage: %s help\n", argv[0]); exit(1); } if (iterations < MIN_ITERATION) { iterations = MIN_ITERATION; printf("user \"iterations\" value is too small (use: %d)\n", iterations); } stats_container_init(&dat, iterations); stats_container_init(&hist, HIST_BUCKETS); stats_quantiles_init(&quantiles, (int)log10(iterations)); setup(); mlockall(MCL_CURRENT | MCL_FUTURE); start_data = calloc(iterations, sizeof(struct timespec)); if (start_data == NULL) { printf("Memory allocation Failed (too many Iteration: %d)\n", iterations); exit(1); } stop_data = calloc(iterations, sizeof(struct timespec)); if (stop_data == NULL) { printf("Memory allocation Failed (too many Iteration: %d)\n", iterations); free(start_data); exit(1); } /* switch to SCHED_FIFO 99 */ param.sched_priority = sched_get_priority_max(SCHED_FIFO); err = sched_setscheduler(0, SCHED_FIFO, ¶m); /* Check that the user has the appropriate privileges */ if (err) { if (errno == EPERM) { fprintf(stderr, "This program runs with a scheduling policy of SCHED_FIFO at priority %d\n", param.sched_priority); fprintf(stderr, "You don't have the necessary privileges to create such a real-time process.\n"); } else { fprintf(stderr, "Failed to set scheduler, errno %d\n", errno); } exit(1); } printf("\n----------------------\n"); printf("Gettimeofday() Latency\n"); printf("----------------------\n"); printf("Iterations: %d\n\n", iterations); /* collect iterations pairs of gtod calls */ max = min = 0; if (latency_threshold) { latency_trace_enable(); latency_trace_start(); } /* This loop runs for a long time, hence can cause soft lockups. Calling sleep periodically avoids this. */ for (i = 0; i < (iterations / 10000); i++) { for (j = 0; j < 10000; j++) { k = (i * 10000) + j; clock_gettime(CLOCK_MONOTONIC, &start_data[k]); clock_gettime(CLOCK_MONOTONIC, &stop_data[k]); } usleep(1000); } for (i = 0; i < iterations; i++) { delta = timespec_subtract(&start_data[i], &stop_data[i]); rec.x = i; rec.y = delta; stats_container_append(&dat, rec); if (i == 0 || delta < min) min = delta; if (delta > max) max = delta; if (latency_threshold && delta > latency_threshold) break; } if (latency_threshold) { latency_trace_stop(); if (i != iterations) { printf ("Latency threshold (%lluus) exceeded at iteration %d\n", latency_threshold, i); latency_trace_print(); stats_container_resize(&dat, i + 1); } } stats_hist(&hist, &dat); stats_container_save(filenames[SCATTER_FILENAME], titles[SCATTER_TITLE], labels[SCATTER_LABELX], labels[SCATTER_LABELY], &dat, "points"); stats_container_save(filenames[HIST_FILENAME], titles[HIST_TITLE], labels[HIST_LABELX], labels[HIST_LABELY], &hist, "steps"); /* report on deltas */ printf("Min: %llu ns\n", min); printf("Max: %llu ns\n", max); printf("Avg: %.4f ns\n", stats_avg(&dat)); printf("StdDev: %.4f ns\n", stats_stddev(&dat)); printf("Quantiles:\n"); stats_quantiles_calc(&dat, &quantiles); stats_quantiles_print(&quantiles); stats_container_free(&dat); stats_container_free(&hist); stats_quantiles_free(&quantiles); return 0; }