• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@us.ibm.com>
36  *
37  * HISTORY
38  *      2006-Aug-17: Initial version by Darren Hart <dvhltc@us.ibm.com>
39  *      2006-Aug-23: Minor changes by John Kacur <jekacur@ca.ibm.com>
40  *      2006-Nov-20: Augmented to use libstats
41  *      2007-Jul-12: Latency tracing added by Josh Triplett <josh@kernel.org>
42  *      2007-Jul-13: Quantiles added by Josh Triplett <josh@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 
stats_cmdline_help(void)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 
stats_cmdline(int argc,char * argv[])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 
timespec_subtract(struct timespec * a,struct timespec * b)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 
main(int argc,char * argv[])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