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, ¶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