OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2013 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 #include <errno.h> | |
8 #include <inttypes.h> | |
9 #include <pthread.h> | |
10 #include <stdio.h> | |
11 #include <string.h> | |
12 #include <sys/types.h> | |
13 #include <sys/time.h> | |
14 #include <time.h> | |
15 #include <unistd.h> | |
16 | |
17 #include "native_client/src/include/nacl_macros.h" | |
18 | |
19 /* | |
20 * This test resembles the trusted version which can be found in | |
21 * src/shared/platform/nacl_clock_test.c, but uses the stable ABI | |
22 * to test the integration correctness. When making changes, please | |
23 * keep both tests in sync if possible. | |
24 */ | |
25 | |
26 | |
27 /* | |
28 * On an unloaded i7, 1ms with a 1.25 fuzziness factor and a 100,000 | |
29 * ns constant syscall overhead works fine. On bots, we have to be | |
30 * much more generous. (This is especially true for qemu-based | |
31 * testing.) | |
32 */ | |
33 #define NANOS_PER_MICRO (1000) | |
34 #define MICROS_PER_MILLI (1000) | |
35 #define NANOS_PER_MILLI (NANOS_PER_MICRO * MICROS_PER_MILLI) | |
36 #define MICROS_PER_UNIT (1000 * 1000) | |
37 #define NANOS_PER_UNIT (NANOS_PER_MICRO * MICROS_PER_UNIT) | |
38 | |
39 #define DEFAULT_NANOSLEEP_EXTRA_OVERHEAD (10 * NANOS_PER_MILLI) | |
40 #define DEFAULT_NANOSLEEP_EXTRA_FACTOR (100.0) | |
41 #define DEFAULT_NANOSLEEP_TIME (10 * NANOS_PER_MILLI) | |
42 | |
43 /* | |
44 * Global testing parameters -- fuzziness coefficients in determining | |
45 * what is considered accurate. | |
46 */ | |
47 int g_cputime = 1; | |
48 double g_fuzzy_factor = DEFAULT_NANOSLEEP_EXTRA_FACTOR; | |
49 uint64_t g_syscall_overhead = DEFAULT_NANOSLEEP_EXTRA_OVERHEAD; | |
50 uint64_t g_slop_ms = 0; | |
51 | |
52 /* | |
53 * ClockMonotonicAccuracyTest samples the CLOCK_MONOTONIC | |
54 * clock before and after invoking NaClNanosleep and computes the time | |
55 * delta. The test is considered to pass if the time delta is close | |
56 * to the requested value. "Close" is a per-host-OS attribute, thus | |
57 * the above testing parameters. | |
58 */ | |
59 int ClockMonotonicAccuracyTest(uint64_t sleep_nanos) { | |
60 int num_failures = 0; | |
61 | |
62 int err; | |
63 struct timespec t_start; | |
64 struct timespec t_sleep; | |
65 struct timespec t_end; | |
66 | |
67 uint64_t elapsed_nanos; | |
68 uint64_t elapsed_lower_bound; | |
69 uint64_t elapsed_upper_bound; | |
70 | |
71 t_sleep.tv_sec = sleep_nanos / NANOS_PER_UNIT; | |
72 t_sleep.tv_nsec = sleep_nanos % NANOS_PER_UNIT; | |
73 | |
74 printf("\nCLOCK_MONOTONIC accuracy test:\n"); | |
75 | |
76 if (0 != (err = clock_gettime(CLOCK_MONOTONIC, &t_start))) { | |
77 fprintf(stderr, | |
78 "nacl_clock_test: clock_gettime (start) failed, error %d\n", | |
79 err); | |
80 ++num_failures; | |
81 goto done; | |
82 } | |
83 for (;;) { | |
84 err = nanosleep(&t_sleep, &t_sleep); | |
85 if (0 == err) { | |
86 break; | |
87 } | |
88 if (-EINTR == err) { | |
Roland McGrath
2013/08/28 16:53:06
Return value is an errno code, not a negated errno
Petr Hosek
2013/08/28 19:31:11
Oh, I completely forgot about those return values,
| |
89 /* interrupted syscall: sleep some more */ | |
90 continue; | |
91 } | |
92 fprintf(stderr, | |
93 "nacl_clock_test: nanosleep failed, error %d\n", err); | |
94 num_failures++; | |
95 goto done; | |
96 } | |
97 if (0 != (err = clock_gettime(CLOCK_MONOTONIC, &t_end))) { | |
98 fprintf(stderr, | |
99 "nacl_clock_test: clock_gettime (end) failed, error %d\n", | |
100 err); | |
101 return 1; | |
102 } | |
103 | |
104 elapsed_nanos = (t_end.tv_sec - t_start.tv_sec) * NANOS_PER_UNIT | |
105 + (t_end.tv_nsec - t_start.tv_nsec) + g_slop_ms * NANOS_PER_MILLI; | |
106 | |
107 elapsed_lower_bound = sleep_nanos; | |
108 elapsed_upper_bound = (uint64_t) (sleep_nanos * g_fuzzy_factor | |
109 + g_syscall_overhead); | |
110 | |
111 printf("requested sleep: %20"PRIu64" nS\n", sleep_nanos); | |
112 printf("actual elapsed sleep: %20"PRIu64" nS\n", elapsed_nanos); | |
113 printf("sleep lower bound: %20"PRIu64" nS\n", elapsed_lower_bound); | |
114 printf("sleep upper bound: %20"PRIu64" nS\n", elapsed_upper_bound); | |
115 | |
116 if (elapsed_nanos < elapsed_lower_bound | |
117 || elapsed_upper_bound < elapsed_nanos) { | |
118 printf("discrepancy too large\n"); | |
119 num_failures++; | |
120 } | |
121 done: | |
122 printf((0 == num_failures) ? "PASSED\n" : "FAILED\n"); | |
123 return num_failures; | |
124 } | |
125 | |
126 /* | |
127 * ClockRealtimeAccuracyTest compares the time returned by | |
128 * CLOCK_REALTIME against that returned by gettimeofday. | |
129 */ | |
130 int ClockRealtimeAccuracyTest(void) { | |
131 int num_failures = 0; | |
132 | |
133 int err; | |
134 struct timespec t_now_ts; | |
135 struct timeval t_now_tv; | |
136 | |
137 uint64_t t_now_ts_nanos; | |
138 uint64_t t_now_tv_nanos; | |
139 int64_t t_now_diff_nanos; | |
140 | |
141 printf("\nCLOCK_REALTIME accuracy test:\n"); | |
142 | |
143 if (0 != (err = clock_gettime(CLOCK_REALTIME, &t_now_ts))) { | |
144 fprintf(stderr, | |
145 "clock_test: clock_gettime (now) failed, error %d\n", | |
146 err); | |
147 num_failures++; | |
148 goto done; | |
149 } | |
150 if (0 != (err = gettimeofday(&t_now_tv, NULL))) { | |
Roland McGrath
2013/08/28 16:53:06
The return value is -1 and the error is in errno.
Petr Hosek
2013/08/28 19:31:11
ditto
| |
151 fprintf(stderr, | |
152 "clock_test: gettimeofday (now) failed, error %d\n", | |
153 err); | |
154 num_failures++; | |
155 goto done; | |
156 } | |
157 | |
158 t_now_ts_nanos = t_now_ts.tv_sec * NANOS_PER_UNIT + t_now_ts.tv_nsec; | |
159 t_now_tv_nanos = t_now_tv.tv_sec * NANOS_PER_UNIT | |
160 + t_now_tv.tv_usec * NANOS_PER_MICRO; | |
161 | |
162 printf("clock_gettime: %20"PRIu64" nS\n", t_now_ts_nanos); | |
163 printf("gettimeofday: %20"PRIu64" nS\n", t_now_tv_nanos); | |
164 | |
165 t_now_diff_nanos = t_now_ts_nanos - t_now_tv_nanos; | |
166 if (t_now_diff_nanos < 0) { | |
167 t_now_diff_nanos = -t_now_diff_nanos; | |
168 } | |
169 printf("time difference: %20"PRId64" nS\n", t_now_diff_nanos); | |
170 | |
171 if (t_now_ts_nanos < g_syscall_overhead) { | |
172 printf("discrepancy too large\n"); | |
173 num_failures++; | |
174 } | |
175 done: | |
176 printf((0 == num_failures) ? "PASSED\n" : "FAILED\n"); | |
177 return num_failures; | |
178 } | |
179 | |
180 struct ThreadInfo { | |
181 size_t cycles; | |
182 struct timespec thread_time; | |
183 struct timespec process_time; | |
184 int num_failures; | |
185 }; | |
186 | |
187 void *ThreadFunction(void *ptr) { | |
188 int err; | |
189 | |
190 size_t i; | |
191 struct ThreadInfo *info = (struct ThreadInfo *) ptr; | |
192 | |
193 for (i = 1; i < info->cycles; i++) { | |
194 __sync_synchronize(); | |
195 } | |
196 | |
197 if (0 != (err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, | |
198 &info->thread_time))) { | |
199 fprintf(stderr, | |
200 "clock_test: clock_gettime (now) failed, error %d\n", | |
201 err); | |
202 info->num_failures++; | |
203 return NULL; | |
204 } | |
205 | |
206 if (0 != (err = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, | |
207 &info->process_time))) { | |
208 fprintf(stderr, | |
209 "clock_test: clock_gettime (now) failed, error %d\n", | |
210 err); | |
211 info->num_failures++; | |
212 return NULL; | |
213 } | |
214 | |
215 return NULL; | |
216 } | |
217 | |
218 int ClockCpuTimeAccuracyTest(void) { | |
219 int num_failures = 0; | |
220 | |
221 int err; | |
222 struct timespec t_process_start; | |
223 struct timespec t_process_end; | |
224 struct timespec t_thread_start; | |
225 struct timespec t_thread_end; | |
226 | |
227 uint64_t thread_elapsed = 0; | |
228 uint64_t process_elapsed = 0; | |
229 uint64_t child_thread_elapsed = 0; | |
230 uint64_t elapsed_lower_bound; | |
231 uint64_t elapsed_upper_bound; | |
232 | |
233 size_t i; | |
234 struct ThreadInfo info[10]; | |
235 pthread_t thread[10]; | |
236 | |
237 printf("\nCLOCK_PROCESS/THREAD_CPUTIME_ID accuracy test:\n"); | |
238 | |
239 if (0 != (err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, | |
240 &t_thread_start))) { | |
241 fprintf(stderr, | |
242 "clock_test: clock_gettime (now) failed, error %d\n", | |
243 err); | |
244 num_failures++; | |
245 goto done; | |
246 } | |
247 | |
248 if (0 != (err = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, | |
249 &t_process_start))) { | |
250 fprintf(stderr, | |
251 "clock_test: clock_gettime (now) failed, error %d\n", | |
252 err); | |
253 num_failures++; | |
254 goto done; | |
255 } | |
256 | |
257 for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) { | |
258 memset(&info[i], 0, sizeof info[i]); | |
259 info[i].cycles = i * 10000000; | |
260 if (0 != (err = pthread_create(&thread[i], NULL, | |
261 ThreadFunction, &info[i]))) { | |
262 fprintf(stderr, | |
263 "clock_test: pthread_create failed, error %d\n", | |
264 err); | |
265 num_failures++; | |
266 goto done; | |
267 } | |
268 } | |
269 | |
270 for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) { | |
271 pthread_join(thread[i], NULL); | |
272 } | |
273 | |
274 if (0 != (err = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, | |
275 &t_process_end))) { | |
276 fprintf(stderr, | |
277 "clock_test: clock_gettime (now) failed, error %d\n", | |
278 err); | |
279 num_failures++; | |
280 goto done; | |
281 } | |
282 | |
283 if (0 != (err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, | |
284 &t_thread_end))) { | |
285 fprintf(stderr, | |
286 "clock_test: clock_gettime (now) failed, error %d\n", | |
287 err); | |
288 num_failures++; | |
289 goto done; | |
290 } | |
291 | |
292 thread_elapsed = | |
293 (t_thread_end.tv_sec - t_thread_start.tv_sec) * NANOS_PER_UNIT | |
294 + (t_thread_end.tv_nsec - t_thread_start.tv_nsec); | |
295 | |
296 process_elapsed = | |
297 (t_process_end.tv_sec - t_process_start.tv_sec) * NANOS_PER_UNIT | |
298 + (t_process_end.tv_nsec - t_process_start.tv_nsec); | |
299 | |
300 for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) { | |
301 uint64_t thread_elapsed_nanos; | |
302 uint64_t process_elapsed_nanos; | |
303 | |
304 if (info[i].num_failures > 0) { | |
305 num_failures += info[i].num_failures; | |
306 goto done; | |
307 } | |
308 | |
309 thread_elapsed_nanos = info[i].thread_time.tv_sec * NANOS_PER_UNIT | |
310 + info[i].thread_time.tv_nsec; | |
311 process_elapsed_nanos = info[i].process_time.tv_sec * NANOS_PER_UNIT | |
312 + info[i].process_time.tv_nsec; | |
313 printf("%zd: thread=%20"PRIu64" nS, process=%20"PRIu64" nS\n", | |
314 i, thread_elapsed_nanos, process_elapsed_nanos); | |
315 child_thread_elapsed += thread_elapsed_nanos; | |
316 } | |
317 | |
318 elapsed_lower_bound = thread_elapsed + child_thread_elapsed; | |
319 elapsed_upper_bound = (uint64_t) (thread_elapsed | |
320 + child_thread_elapsed * g_fuzzy_factor + g_syscall_overhead); | |
321 | |
322 printf("thread time: %20"PRIu64" nS\n", thread_elapsed); | |
323 printf("process time: %20"PRIu64" nS\n", process_elapsed); | |
324 printf("child thread time: %20"PRIu64" nS\n", child_thread_elapsed); | |
325 printf("elapsed lower bound: %20"PRIu64" nS\n", elapsed_lower_bound); | |
326 printf("elapsed upper bound: %20"PRIu64" nS\n", elapsed_upper_bound); | |
327 | |
328 if (process_elapsed < elapsed_lower_bound | |
329 || elapsed_upper_bound < process_elapsed) { | |
330 printf("discrepancy too large\n"); | |
331 num_failures++; | |
332 } | |
333 done: | |
334 printf((0 == num_failures) ? "PASSED\n" : "FAILED\n"); | |
335 return num_failures; | |
336 } | |
337 | |
338 int main(int ac, char **av) { | |
339 uint64_t sleep_nanos = DEFAULT_NANOSLEEP_TIME; | |
340 int opt; | |
341 uint32_t num_failures = 0; | |
342 | |
343 while (-1 != (opt = getopt(ac, av, "cf:o:s:S:"))) { | |
344 switch (opt) { | |
345 case 'c': | |
346 g_cputime = 0; | |
347 break; | |
348 case 'f': | |
349 g_fuzzy_factor = strtod(optarg, (char **) NULL); | |
350 break; | |
351 case 'o': | |
352 g_syscall_overhead = strtoul(optarg, (char **) NULL, 0); | |
353 break; | |
354 case 's': | |
355 g_slop_ms = strtoul(optarg, (char **) NULL, 0); | |
356 break; | |
357 case 'S': | |
358 sleep_nanos = strtoul(optarg, (char **) NULL, 0); | |
359 break; | |
360 default: | |
361 fprintf(stderr, "clock_test: unrecognized option `%c'.\n", | |
362 opt); | |
363 fprintf(stderr, | |
364 "Usage: clock_test [-f fuzz_factor] [-s sleep_nanos]\n" | |
365 " [-o syscall_overhead_nanos]\n"); | |
366 return -1; | |
367 } | |
368 } | |
369 | |
370 num_failures += ClockMonotonicAccuracyTest(sleep_nanos); | |
371 num_failures += ClockRealtimeAccuracyTest(); | |
372 if (g_cputime) { | |
373 num_failures += ClockCpuTimeAccuracyTest(); | |
374 } | |
375 | |
376 return num_failures; | |
377 } | |
OLD | NEW |