1 : /*
2 : * Copyright (c) 2012 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 <stdio.h>
8 : #include <string.h>
9 :
10 : #include "native_client/src/shared/platform/nacl_clock.h"
11 :
12 : #include "native_client/src/shared/platform/platform_init.h"
13 :
14 : #include "native_client/src/include/nacl_macros.h"
15 : #include "native_client/src/include/portability.h"
16 : #include "native_client/src/shared/platform/nacl_time.h"
17 : #include "native_client/src/shared/platform/nacl_threads.h"
18 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
19 :
20 : /*
21 : * Very basic sanity check. With clock functionality, tests are a
22 : * pain without a set of globally consistent dependency injection for
23 : * syscalls, since faking out time-related syscalls in the test
24 : * without faking out the same syscalls used by other modules is
25 : * difficult. Furthermore, this test is trying to verify basic
26 : * functionality -- and testing against a mock interface that works
27 : * according to our expectations of what the syscalls will do isn't
28 : * the same: our assumptions might be wrong, and we ought to have a
29 : * test that verifies end-to-end functionality. Here, we just compare
30 : * clock_gettime of the realtime clock against gettimeofday, and do
31 : * two monotonic clock samples between a nanosleep to verify that the
32 : * monotonic clock *approximately* measured the sleep duration -- with
33 : * great fuzziness.
34 : *
35 : * On an unloaded i7, 1ms with a 1.25 fuzziness factor and a 100,000
36 : * ns constant syscall overhead works fine. On bots, we have to be
37 : * much more generous. (This is especially true for qemu-based
38 : * testing.)
39 : */
40 : #define DEFAULT_NANOSLEEP_EXTRA_OVERHEAD (10 * NACL_NANOS_PER_MILLI)
41 : #define DEFAULT_NANOSLEEP_EXTRA_FACTOR (100.0)
42 : #define DEFAULT_NANOSLEEP_TIME (10 * NACL_NANOS_PER_MILLI)
43 :
44 : #define THREAD_CYCLES 100000000
45 :
46 : /*
47 : * Global testing parameters -- fuzziness coefficients in determining
48 : * what is considered accurate.
49 : */
50 : int g_cputime = 0;
51 : double g_fuzzy_factor = DEFAULT_NANOSLEEP_EXTRA_FACTOR;
52 : uint64_t g_syscall_overhead = DEFAULT_NANOSLEEP_EXTRA_OVERHEAD;
53 : uint64_t g_slop_ms = 0;
54 :
55 : /*
56 : * ClockMonotonicAccuracyTest samples the NACL_ABI_CLOCK_MONOTONIC
57 : * clock before and after invoking NaClNanosleep and computes the time
58 : * delta. The test is considered to pass if the time delta is close
59 : * to the requested value. "Close" is a per-host-OS attribute, thus
60 : * the above testing parameters.
61 : */
62 1 : static int ClockMonotonicAccuracyTest(uint64_t sleep_nanos) {
63 1 : int num_failures = 0;
64 :
65 : int err;
66 : struct nacl_abi_timespec t_start;
67 : struct nacl_abi_timespec t_sleep;
68 : struct nacl_abi_timespec t_end;
69 :
70 : uint64_t elapsed_nanos;
71 : uint64_t elapsed_lower_bound;
72 : uint64_t elapsed_upper_bound;
73 :
74 1 : t_sleep.tv_sec = sleep_nanos / NACL_NANOS_PER_UNIT;
75 1 : t_sleep.tv_nsec = sleep_nanos % NACL_NANOS_PER_UNIT;
76 :
77 1 : printf("\nCLOCK_MONOTONIC accuracy test:\n");
78 :
79 1 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_MONOTONIC, &t_start))) {
80 : fprintf(stderr,
81 : "nacl_clock_test: NaClClockGetTime (start) failed, error %d\n",
82 0 : err);
83 0 : ++num_failures;
84 0 : goto done;
85 : }
86 : for (;;) {
87 1 : err = NaClNanosleep(&t_sleep, &t_sleep);
88 1 : if (0 == err) {
89 1 : break;
90 : }
91 0 : if (-NACL_ABI_EINTR == err) {
92 : /* interrupted syscall: sleep some more */
93 0 : continue;
94 : }
95 : fprintf(stderr,
96 0 : "nacl_clock_test: NaClNanoSleep failed, error %d\n", err);
97 0 : num_failures++;
98 0 : goto done;
99 : }
100 1 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_MONOTONIC, &t_end))) {
101 : fprintf(stderr,
102 : "nacl_clock_test: NaClClockGetTime (end) failed, error %d\n",
103 0 : err);
104 0 : return 1;
105 : }
106 :
107 : elapsed_nanos = (t_end.tv_sec - t_start.tv_sec) * NACL_NANOS_PER_UNIT +
108 1 : (t_end.tv_nsec - t_start.tv_nsec) + g_slop_ms * NACL_NANOS_PER_MILLI;
109 :
110 1 : elapsed_lower_bound = sleep_nanos;
111 : elapsed_upper_bound = (uint64_t) (sleep_nanos * g_fuzzy_factor +
112 1 : g_syscall_overhead);
113 :
114 1 : printf("requested sleep: %20"NACL_PRIu64" nS\n", sleep_nanos);
115 1 : printf("actual elapsed sleep: %20"NACL_PRIu64" nS\n", elapsed_nanos);
116 1 : printf("sleep lower bound: %20"NACL_PRIu64" nS\n", elapsed_lower_bound);
117 1 : printf("sleep upper bound: %20"NACL_PRIu64" nS\n", elapsed_upper_bound);
118 :
119 : if (elapsed_nanos < elapsed_lower_bound ||
120 1 : elapsed_upper_bound < elapsed_nanos) {
121 0 : printf("discrepancy too large\n");
122 0 : num_failures++;
123 : }
124 : done:
125 1 : printf((0 == num_failures) ? "PASSED\n" : "FAILED\n");
126 1 : return num_failures;
127 1 : }
128 :
129 : /*
130 : * ClockRealtimeAccuracyTest compares the time returned by
131 : * NACL_ABI_CLOCK_REALTIME against that returned by NaClGetTimeOfDay.
132 : */
133 1 : static int ClockRealtimeAccuracyTest(void) {
134 1 : int num_failures = 0;
135 :
136 : int err;
137 : struct nacl_abi_timespec t_now_ts;
138 : struct nacl_abi_timeval t_now_tv;
139 :
140 : uint64_t t_now_ts_nanos;
141 : uint64_t t_now_tv_nanos;
142 : int64_t t_now_diff_nanos;
143 :
144 1 : printf("\nCLOCK_REALTIME accuracy test:\n");
145 :
146 1 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_REALTIME, &t_now_ts))) {
147 : fprintf(stderr,
148 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
149 0 : err);
150 0 : num_failures++;
151 0 : goto done;
152 : }
153 1 : if (0 != (err = NaClGetTimeOfDay(&t_now_tv))) {
154 : fprintf(stderr,
155 : "nacl_clock_test: NaClGetTimeOfDay (now) failed, error %d\n",
156 0 : err);
157 0 : num_failures++;
158 0 : goto done;
159 : }
160 :
161 1 : t_now_ts_nanos = t_now_ts.tv_sec * NACL_NANOS_PER_UNIT + t_now_ts.tv_nsec;
162 : t_now_tv_nanos = t_now_tv.nacl_abi_tv_sec * NACL_NANOS_PER_UNIT +
163 1 : t_now_tv.nacl_abi_tv_usec * NACL_NANOS_PER_MICRO;
164 :
165 1 : printf("clock_gettime: %20"NACL_PRIu64" nS\n", t_now_ts_nanos);
166 1 : printf("gettimeofday: %20"NACL_PRIu64" nS\n", t_now_tv_nanos);
167 :
168 1 : t_now_diff_nanos = t_now_ts_nanos - t_now_tv_nanos;
169 1 : if (t_now_diff_nanos < 0) {
170 0 : t_now_diff_nanos = -t_now_diff_nanos;
171 : }
172 1 : printf("time difference: %20"NACL_PRId64" nS\n", t_now_diff_nanos);
173 :
174 1 : if (t_now_ts_nanos < g_syscall_overhead) {
175 0 : printf("discrepancy too large\n");
176 0 : num_failures++;
177 : }
178 : done:
179 1 : printf((0 == num_failures) ? "PASSED\n" : "FAILED\n");
180 1 : return num_failures;
181 1 : }
182 :
183 : #ifndef NACL_NO_CPUTIME_TEST
184 : struct ThreadInfo {
185 : size_t cycles;
186 : struct nacl_abi_timespec thread_time;
187 : struct nacl_abi_timespec process_time;
188 : int num_failures;
189 : };
190 :
191 0 : void WINAPI ThreadFunction(void *ptr) {
192 : int err;
193 :
194 : size_t i;
195 0 : struct ThreadInfo *info = (struct ThreadInfo *) ptr;
196 :
197 0 : for (i = 1; i < info->cycles; i++) {
198 : #if defined(__GNUC__)
199 : __asm__ volatile("" ::: "memory");
200 : #elif NACL_WINDOWS
201 : _ReadWriteBarrier();
202 : #else
203 : # error Unsupported platform
204 : #endif
205 0 : }
206 :
207 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_THREAD_CPUTIME_ID,
208 0 : &info->thread_time))) {
209 : fprintf(stderr,
210 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
211 0 : err);
212 0 : info->num_failures++;
213 0 : return;
214 : }
215 :
216 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_PROCESS_CPUTIME_ID,
217 0 : &info->process_time))) {
218 : fprintf(stderr,
219 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
220 0 : err);
221 0 : info->num_failures++;
222 : return;
223 : }
224 0 : }
225 :
226 0 : static int ClockCpuTimeAccuracyTest(void) {
227 0 : int num_failures = 0;
228 :
229 : int err;
230 : struct nacl_abi_timespec t_process_start;
231 : struct nacl_abi_timespec t_process_end;
232 : struct nacl_abi_timespec t_thread_start;
233 : struct nacl_abi_timespec t_thread_end;
234 :
235 0 : uint64_t thread_elapsed = 0;
236 0 : uint64_t process_elapsed = 0;
237 0 : uint64_t child_thread_elapsed = 0;
238 : uint64_t elapsed_lower_bound;
239 : uint64_t elapsed_upper_bound;
240 :
241 : size_t i;
242 : struct ThreadInfo info[10];
243 : struct NaClThread thread[10];
244 :
245 0 : printf("\nCLOCK_PROCESS/THREAD_CPUTIME_ID accuracy test:\n");
246 :
247 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_THREAD_CPUTIME_ID,
248 0 : &t_thread_start))) {
249 : fprintf(stderr,
250 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
251 0 : err);
252 0 : num_failures++;
253 0 : goto done;
254 : }
255 :
256 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_PROCESS_CPUTIME_ID,
257 0 : &t_process_start))) {
258 : fprintf(stderr,
259 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
260 0 : err);
261 0 : num_failures++;
262 0 : goto done;
263 : }
264 :
265 0 : for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) {
266 0 : memset(&info[i], 0, sizeof info[i]);
267 0 : info[i].cycles = i * THREAD_CYCLES;
268 : if (!NaClThreadCreateJoinable(&thread[i], ThreadFunction, &info[i],
269 0 : 65536)) {
270 : fprintf(stderr,
271 0 : "nacl_clock_test: NaClThreadCreateJoinable failed\n");
272 0 : num_failures++;
273 0 : goto done;
274 : }
275 0 : }
276 :
277 0 : for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) {
278 0 : NaClThreadJoin(&thread[i]);
279 0 : }
280 :
281 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_PROCESS_CPUTIME_ID,
282 0 : &t_process_end))) {
283 : fprintf(stderr,
284 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
285 0 : err);
286 0 : num_failures++;
287 0 : goto done;
288 : }
289 :
290 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_THREAD_CPUTIME_ID,
291 0 : &t_thread_end))) {
292 : fprintf(stderr,
293 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
294 0 : err);
295 0 : num_failures++;
296 0 : goto done;
297 : }
298 :
299 : thread_elapsed =
300 : (t_thread_end.tv_sec - t_thread_start.tv_sec) * NACL_NANOS_PER_UNIT +
301 0 : (t_thread_end.tv_nsec - t_thread_start.tv_nsec);
302 :
303 : process_elapsed =
304 : (t_process_end.tv_sec - t_process_start.tv_sec) * NACL_NANOS_PER_UNIT +
305 0 : (t_process_end.tv_nsec - t_process_start.tv_nsec);
306 :
307 0 : for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) {
308 : uint64_t thread_elapsed_nanos;
309 : uint64_t process_elapsed_nanos;
310 0 : if (info[i].num_failures > 0) {
311 0 : num_failures += info[i].num_failures;
312 0 : goto done;
313 : }
314 : thread_elapsed_nanos = info[i].thread_time.tv_sec * NACL_NANOS_PER_UNIT +
315 0 : info[i].thread_time.tv_nsec;
316 : process_elapsed_nanos = info[i].process_time.tv_sec * NACL_NANOS_PER_UNIT +
317 0 : info[i].process_time.tv_nsec;
318 : printf("%"NACL_PRIdS": thread=%20"NACL_PRIu64" nS, process=%20"
319 : NACL_PRIu64" nS\n",
320 0 : i, thread_elapsed_nanos, process_elapsed_nanos);
321 0 : child_thread_elapsed += thread_elapsed_nanos;
322 0 : }
323 :
324 0 : elapsed_lower_bound = thread_elapsed + child_thread_elapsed;
325 : elapsed_upper_bound = (uint64_t) (thread_elapsed +
326 0 : child_thread_elapsed * g_fuzzy_factor + g_syscall_overhead);
327 :
328 0 : printf("thread time: %20"NACL_PRIu64" nS\n", thread_elapsed);
329 0 : printf("process time: %20"NACL_PRIu64" nS\n", process_elapsed);
330 0 : printf("child thread time: %20"NACL_PRIu64" nS\n", child_thread_elapsed);
331 0 : printf("elapsed lower bound: %20"NACL_PRIu64" nS\n", elapsed_lower_bound);
332 0 : printf("elapsed upper bound: %20"NACL_PRIu64" nS\n", elapsed_upper_bound);
333 :
334 : if (process_elapsed < elapsed_lower_bound ||
335 0 : elapsed_upper_bound < process_elapsed) {
336 0 : printf("discrepancy too large\n");
337 0 : num_failures++;
338 : }
339 : done:
340 0 : printf((0 == num_failures) ? "PASSED\n" : "FAILED\n");
341 0 : return num_failures;
342 0 : }
343 : #endif
344 :
345 1 : int main(int ac, char **av) {
346 1 : uint64_t sleep_nanos = DEFAULT_NANOSLEEP_TIME;
347 :
348 : int opt;
349 1 : uint32_t num_failures = 0;
350 :
351 1 : puts("This is a basic functionality test being repurposed as an");
352 1 : puts(" unit/regression test. The test parameters default to values that");
353 1 : puts("are more appropriate for heavily loaded continuous-testing robots.");
354 : printf("\nThe default values are:\n -S %"NACL_PRIu64
355 : " -f %f -o %"NACL_PRIu64" -s %"NACL_PRIu64"\n\n",
356 1 : sleep_nanos, g_fuzzy_factor, g_syscall_overhead, g_slop_ms);
357 1 : puts("For testing functionality, this test should be run with a different");
358 1 : puts("set of parameters. On an unloaded i7, a sleep duration (-S) of");
359 1 : puts("1000000 ns (one millisecond), with a fuzziness factor (-f) of 1.25,");
360 1 : puts("a constant test overhead of 100000 ns (100 us), and a");
361 1 : puts("sleep duration \"slop\" (-s) of 0 is fine. The CPU time tests has to");
362 1 : puts("be explicitly enabled (-c) its run time is significant.");
363 :
364 1 : while (-1 != (opt = getopt(ac, av, "cf:o:s:S:"))) {
365 1 : switch (opt) {
366 : case 'c':
367 0 : g_cputime = 1;
368 0 : break;
369 : case 'f':
370 0 : g_fuzzy_factor = strtod(optarg, (char **) NULL);
371 0 : break;
372 : case 'o':
373 0 : g_syscall_overhead = strtoul(optarg, (char **) NULL, 0);
374 0 : break;
375 : case 's':
376 1 : g_slop_ms = strtoul(optarg, (char **) NULL, 0);
377 1 : break;
378 : case 'S':
379 0 : sleep_nanos = strtoul(optarg, (char **) NULL, 0);
380 0 : break;
381 : default:
382 : fprintf(stderr, "nacl_clock_test: unrecognized option `%c'.\n",
383 0 : opt);
384 : fprintf(stderr,
385 : "Usage: nacl_clock_test [-c] [-f fuzz_factor]\n"
386 0 : " [-s sleep_nanos] [-o syscall_overhead_nanos]\n");
387 0 : return -1;
388 : }
389 1 : }
390 :
391 1 : NaClPlatformInit();
392 :
393 1 : if (g_cputime) {
394 0 : num_failures += ClockCpuTimeAccuracyTest();
395 0 : } else {
396 1 : num_failures += ClockMonotonicAccuracyTest(sleep_nanos);
397 1 : num_failures += ClockRealtimeAccuracyTest();
398 : }
399 :
400 1 : NaClPlatformFini();
401 :
402 1 : return num_failures;
403 1 : }
|