1 /*
2  * Copyright 2008 Google Inc. All Rights Reserved.
3  * Author: md@google.com (Michael Davidson)
4  *
5  * Based on time-warp-test.c, which is:
6  * Copyright (C) 2005, Ingo Molnar
7  */
8 #define _GNU_SOURCE
9 
10 #include <errno.h>
11 #include <pthread.h>
12 #include <getopt.h>
13 #include <sched.h>
14 #include <signal.h>
15 #include <stdarg.h>
16 #include <stdint.h>
17 #include <inttypes.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/time.h>
22 #include <time.h>
23 
24 #include "cpuset.h"
25 #include "spinlock.h"
26 #include "threads.h"
27 #include "logging.h"
28 
29 
30 char	*program	= "";
31 long	duration	= 0;
32 long	threshold	= 0;
33 int	verbose		= 0;
34 
35 const char optstring[] = "c:d:ht:v";
36 
37 struct option options[] = {
38 	{ "cpus",	required_argument,	0, 	'c'	},
39 	{ "duration",	required_argument,	0,	'd'	},
40 	{ "help",	no_argument,		0, 	'h'	},
41 	{ "threshold",	required_argument,	0, 	't'	},
42 	{ "verbose",	no_argument,		0, 	'v'	},
43 	{ 0,	0,	0,	0 }
44 };
45 
46 
usage(void)47 void usage(void)
48 {
49 	printf("usage: %s [-hv] [-c <cpu_set>] [-d duration] [-t threshold] "
50 		"tsc|gtod|clock", program);
51 }
52 
53 
54 const char help_text[] =
55 "check time sources for monotonicity across multiple CPUs\n"
56 "  -c,--cpus        set of cpus to test (default: all)\n"
57 "  -d,--duration    test duration in seconds (default: infinite)\n"
58 "  -t,--threshold   error threshold (default: 0)\n"
59 "  -v,--verbose     verbose output\n"
60 "  tsc              test the TSC\n"
61 "  gtod             test gettimeofday()\n"
62 "  clock            test CLOCK_MONOTONIC\n";
63 
64 
help(void)65 void help(void)
66 {
67 	usage();
68 	printf("%s", help_text);
69 }
70 
71 
72 /*
73  * get the TSC as 64 bit value with CPU clock frequency resolution
74  */
75 #if defined(__x86_64__)
rdtsc(void)76 static inline uint64_t rdtsc(void)
77 {
78 	uint32_t	tsc_lo, tsc_hi;
79 	__asm__ __volatile__("rdtsc" : "=a" (tsc_lo), "=d" (tsc_hi));
80 	return ((uint64_t)tsc_hi << 32) | tsc_lo;
81 }
82 #elif defined(__i386__)
rdtsc(void)83 static inline uint64_t rdtsc(void)
84 {
85 	uint64_t	tsc;
86 	__asm__ __volatile__("rdtsc" : "=A" (tsc));
87 	return tsc;
88 }
89 #else
90 #error "rdtsc() not implemented for this architecture"
91 #endif
92 
93 
rdtsc_mfence(void)94 static inline uint64_t rdtsc_mfence(void)
95 {
96 	__asm__ __volatile__("mfence" ::: "memory");
97 	return rdtsc();
98 }
99 
100 
rdtsc_lfence(void)101 static inline uint64_t rdtsc_lfence(void)
102 {
103 	__asm__ __volatile__("lfence" ::: "memory");
104 	return rdtsc();
105 }
106 
107 
108 /*
109  * get result from gettimeofday() as a 64 bit value
110  * with microsecond resolution
111  */
rdgtod(void)112 static inline uint64_t rdgtod(void)
113 {
114 	struct timeval tv;
115 
116 	gettimeofday(&tv, NULL);
117 	return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
118 }
119 
120 
121 /*
122  * get result from clock_gettime(CLOCK_MONOTONIC) as a 64 bit value
123  * with nanosecond resolution
124  */
rdclock(void)125 static inline uint64_t rdclock(void)
126 {
127 	struct timespec ts;
128 
129 	clock_gettime(CLOCK_MONOTONIC, &ts);
130 	return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
131 }
132 
133 
134 /*
135  * test data
136  */
137 typedef struct test_info {
138 	const char	*name;		/* test name			*/
139 	void		(*func)(struct test_info *);	/* the test	*/
140 	spinlock_t	lock;
141 	uint64_t	last;		/* last time value		*/
142 	long		loops;		/* # of test loop iterations	*/
143 	long		warps;		/* # of backward time jumps	*/
144 	int64_t		worst;		/* worst backward time jump	*/
145 	uint64_t	start;		/* test start time		*/
146 	int		done;		/* flag to stop test		*/
147 } test_info_t;
148 
149 
show_warps(struct test_info * test)150 void show_warps(struct test_info *test)
151 {
152 	INFO("new %s-warp maximum: %9"PRId64, test->name, test->worst);
153 }
154 
155 
156 #define	DEFINE_TEST(_name)				\
157 							\
158 void _name##_test(struct test_info *test)		\
159 {							\
160 	uint64_t t0, t1;				\
161 	int64_t delta;					\
162 							\
163 	spin_lock(&test->lock);				\
164 	t1 = rd##_name();				\
165 	t0 = test->last;				\
166 	test->last = rd##_name();			\
167 	test->loops++;					\
168 	spin_unlock(&test->lock);			\
169 							\
170 	delta = t1 - t0;				\
171 	if (delta < 0 && delta < -threshold) {		\
172 		spin_lock(&test->lock);			\
173 		++test->warps;				\
174 		if (delta < test->worst) {		\
175 			test->worst = delta;		\
176 			show_warps(test);		\
177 		}					\
178 		spin_unlock(&test->lock);		\
179 	}						\
180 	if (!((unsigned long)t0 & 31))			\
181 		asm volatile ("rep; nop");		\
182 }							\
183 							\
184 struct test_info _name##_test_info = {			\
185 	.name = #_name,					\
186 	.func = _name##_test,				\
187 }
188 
189 DEFINE_TEST(tsc);
190 DEFINE_TEST(tsc_lfence);
191 DEFINE_TEST(tsc_mfence);
192 DEFINE_TEST(gtod);
193 DEFINE_TEST(clock);
194 
195 struct test_info *tests[] = {
196 	&tsc_test_info,
197 	&tsc_lfence_test_info,
198 	&tsc_mfence_test_info,
199 	&gtod_test_info,
200 	&clock_test_info,
201 	NULL
202 };
203 
204 
show_progress(struct test_info * test)205 void show_progress(struct test_info *test)
206 {
207 	static int	count;
208 	const char	progress[] = "\\|/-";
209 	uint64_t	elapsed = rdgtod() - test->start;
210 
211         printf(" | %.2f us, %s-warps:%ld %c\r",
212                         (double)elapsed/(double)test->loops,
213 			test->name,
214                         test->warps,
215 			progress[++count & 3]);
216 	fflush(stdout);
217 }
218 
219 
test_loop(void * arg)220 void *test_loop(void *arg)
221 {
222 	struct test_info *test = arg;
223 
224 	while (! test->done)
225 		(*test->func)(test);
226 
227 	return NULL;
228 }
229 
230 
run_test(cpu_set_t * cpus,long duration,struct test_info * test)231 int run_test(cpu_set_t *cpus, long duration, struct test_info *test)
232 {
233 	int		errs;
234 	int		ncpus;
235 	int		nthreads;
236 	struct timespec ts		= { .tv_sec = 0, .tv_nsec = 200000000 };
237 	struct timespec	*timeout	= (verbose || duration) ? &ts : NULL;
238 	sigset_t	signals;
239 
240 	/*
241 	 * Make sure that SIG_INT is blocked so we can
242 	 * wait for it in the main test loop below.
243 	 */
244 	sigemptyset(&signals);
245 	sigaddset(&signals, SIGINT);
246 	sigprocmask(SIG_BLOCK, &signals, NULL);
247 
248 	/*
249 	 * test start time
250 	 */
251 	test->start = rdgtod();
252 
253 	/*
254  	 * create the threads
255  	 */
256 	ncpus = count_cpus(cpus);
257 	nthreads = create_per_cpu_threads(cpus, test_loop, test);
258 	if (nthreads != ncpus) {
259 		ERROR(0, "failed to create threads: expected %d, got %d",
260 			ncpus, nthreads);
261 		if (nthreads) {
262 			test->done = 1;
263 			join_threads();
264 		}
265 		return 1;
266 	}
267 
268 	if (duration) {
269 		INFO("running %s test on %d cpus for %ld seconds",
270 			 test->name, ncpus, duration);
271 	} else {
272 		INFO("running %s test on %d cpus", test->name, ncpus);
273 	}
274 
275 	/*
276  	 * wait for a signal
277  	 */
278 	while (sigtimedwait(&signals, NULL, timeout) < 0) {
279 		if (duration  && rdgtod() > test->start + duration * 1000000)
280 			break;
281 
282 		if (verbose)
283 			show_progress(test);
284 	}
285 
286 	/*
287 	 * tell the test threads that we are done and wait for them to exit
288 	 */
289 	test->done = 1;
290 
291 	join_threads();
292 
293 	errs = (test->warps != 0);
294 
295 	if (!errs)
296 		printf("PASS:\n");
297 	else
298 		printf("FAIL: %s-worst-warp=%"PRId64"\n",
299 			test->name, test->worst);
300 
301 	return errs;
302 }
303 
304 
305 int
main(int argc,char * argv[])306 main(int argc, char *argv[])
307 {
308 	int		c;
309 	cpu_set_t	cpus;
310 	int		errs;
311 	int		i;
312 	test_info_t	*test;
313 	const char	*testname;
314 	extern int	opterr;
315 	extern int	optind;
316 	extern char	*optarg;
317 
318 	if ((program = strrchr(argv[0], '/')) != NULL)
319 		++program;
320 	else
321 		program = argv[0];
322 	set_program_name(program);
323 
324 	/*
325 	 * default to checking all cpus
326 	 */
327 	for (c = 0; c < CPU_SETSIZE; c++) {
328 		CPU_SET(c, &cpus);
329 	}
330 
331 	opterr = 0;
332 	errs = 0;
333 	while ((c = getopt_long(argc, argv, optstring, options, NULL)) != EOF) {
334 		switch (c) {
335 			case 'c':
336 				if (parse_cpu_set(optarg, &cpus) != 0)
337 					++errs;
338 				break;
339 			case 'd':
340 				duration = strtol(optarg, NULL, 0);
341 				break;
342 			case 'h':
343 				help();
344 				exit(0);
345 			case 't':
346 				threshold = strtol(optarg, NULL, 0);
347 				break;
348 			case 'v':
349 				++verbose;
350 				break;
351 			default:
352 				ERROR(0, "unknown option '%c'", c);
353 				++errs;
354 				break;
355 		}
356 	}
357 
358 	if (errs || optind != argc-1) {
359 		usage();
360 		exit(1);
361 	}
362 
363 	testname = argv[optind];
364 	for (i = 0; (test = tests[i]) != NULL; i++) {
365 		if (strcmp(testname, test->name) == 0)
366 			break;
367 	}
368 
369 	if (!test) {
370 		ERROR(0, "unknown test '%s'\n", testname);
371 		usage();
372 		exit(1);
373 	}
374 
375 	/*
376 	 * limit the set of CPUs to the ones that are currently available
377 	 * (Note that on some kernel versions sched_setaffinity() will fail
378 	 * if you specify CPUs that are not currently online so we ignore
379 	 * the return value and hope for the best)
380 	 */
381 	sched_setaffinity(0, sizeof cpus, &cpus);
382 	if (sched_getaffinity(0, sizeof cpus, &cpus) < 0) {
383 		ERROR(errno, "sched_getaffinity() failed");
384 		exit(1);
385 	}
386 
387 	return run_test(&cpus, duration, test);
388 }
389