Home | History | Annotate | Download | only in gtod_latency
      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, &param);
    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