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 :
9 : #include "native_client/src/shared/platform/nacl_clock.h"
10 :
11 : #include "native_client/src/shared/platform/platform_init.h"
12 :
13 : #include "native_client/src/include/portability.h"
14 : #include "native_client/src/shared/platform/nacl_time.h"
15 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
16 :
17 : /*
18 : * Very basic sanity check. With clock functionality, tests are a
19 : * pain without a set of globally consistent dependency injection for
20 : * syscalls, since faking out time-related syscalls in the test
21 : * without faking out the same syscalls used by other modules is
22 : * difficult. Furthermore, this test is trying to verify basic
23 : * functionality -- and testing against a mock interface that works
24 : * according to our expectations of what the syscalls will do isn't
25 : * the same: our assumptions might be wrong, and we ought to have a
26 : * test that verifies end-to-end functionality. Here, we just compare
27 : * clock_gettime of the realtime clock against gettimeofday, and do
28 : * two monotonic clock samples between a nanosleep to verify that the
29 : * monotonic clock *approximately* measured the sleep duration -- with
30 : * great fuzziness.
31 : *
32 : * On an unloaded i7, 1ms with a 1.25 fuzziness factor and a 100,000
33 : * ns constant syscall overhead works fine. On bots, we have to be
34 : * much more generous. (This is especially true for qemu-based
35 : * testing.)
36 : */
37 : #define NANOS_PER_MICRO (1000)
38 : #define MICROS_PER_MILLI (1000)
39 : #define NANOS_PER_MILLI (NANOS_PER_MICRO * MICROS_PER_MILLI)
40 : #define MICROS_PER_UNIT (1000 * 1000)
41 : #define NANOS_PER_UNIT (NANOS_PER_MICRO * MICROS_PER_UNIT)
42 :
43 : #define DEFAULT_NANOSLEEP_EXTRA_OVERHEAD (10 * NANOS_PER_MILLI)
44 : #define DEFAULT_NANOSLEEP_EXTRA_FACTOR (100.0)
45 : #define DEFAULT_NANOSLEEP_TIME (10 * NANOS_PER_MILLI)
46 :
47 : /*
48 : * Global testing parameters -- fuzziness coefficients in determining
49 : * what is considered accurate.
50 : */
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 : 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 / NANOS_PER_UNIT;
75 1 : t_sleep.tv_nsec = sleep_nanos % 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 0 : fprintf(stderr,
81 : "nacl_clock_test: NaClClockGetTime (start) failed, error %d\n",
82 : 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 0 : fprintf(stderr,
96 : "nacl_clock_test: NaClNanoSleep failed, error %d\n", err);
97 0 : num_failures++;
98 0 : goto done;
99 0 : }
100 1 : if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_MONOTONIC, &t_end))) {
101 0 : fprintf(stderr,
102 : "nacl_clock_test: NaClClockGetTime (end) failed, error %d\n",
103 : err);
104 0 : return 1;
105 : }
106 :
107 1 : elapsed_nanos = (t_end.tv_sec - t_start.tv_sec) * NANOS_PER_UNIT
108 : + (t_end.tv_nsec - t_start.tv_nsec) + g_slop_ms * NANOS_PER_MILLI;
109 :
110 1 : elapsed_lower_bound = sleep_nanos;
111 1 : elapsed_upper_bound = (uint64_t) (sleep_nanos * g_fuzzy_factor
112 : + 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 1 : if (elapsed_nanos < elapsed_lower_bound
120 : || elapsed_upper_bound < elapsed_nanos) {
121 0 : printf("discrepancy too large\n");
122 0 : num_failures++;
123 : }
124 1 : done:
125 1 : printf((0 == num_failures) ? "PASSED\n" : "FAILED\n");
126 1 : return num_failures;
127 : }
128 :
129 : /*
130 : * ClockRealtimeAccuracyTest compares the time returned by
131 : * NACL_ABI_CLOCK_REALTIME against that returned by NaClGetTimeOfDay.
132 : */
133 1 : 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 0 : fprintf(stderr,
148 : "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n",
149 : err);
150 0 : num_failures++;
151 0 : goto done;
152 : }
153 1 : if (0 != (err = NaClGetTimeOfDay(&t_now_tv))) {
154 0 : fprintf(stderr,
155 : "nacl_clock_test: NaClGetTimeOfDay (now) failed, error %d\n",
156 : err);
157 0 : num_failures++;
158 0 : goto done;
159 : }
160 :
161 1 : t_now_ts_nanos = t_now_ts.tv_sec * NANOS_PER_UNIT + t_now_ts.tv_nsec;
162 1 : t_now_tv_nanos = t_now_tv.nacl_abi_tv_sec * NANOS_PER_UNIT
163 : + t_now_tv.nacl_abi_tv_usec * 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 1 : done:
179 1 : printf((0 == num_failures) ? "PASSED\n" : "FAILED\n");
180 1 : return num_failures;
181 : }
182 :
183 1 : int main(int ac, char **av) {
184 1 : uint64_t sleep_nanos = DEFAULT_NANOSLEEP_TIME;
185 :
186 : int opt;
187 1 : uint32_t num_failures = 0;
188 :
189 1 : puts("This is a basic functionality test being repurposed as an");
190 1 : puts(" unit/regression test. The test parameters default to values that");
191 1 : puts("are more appropriate for heavily loaded continuous-testing robots.");
192 1 : printf("\nThe default values are:\n -S %"NACL_PRIu64
193 : " -f %f -o %"NACL_PRIu64" -s %"NACL_PRIu64"\n\n",
194 : sleep_nanos, g_fuzzy_factor, g_syscall_overhead, g_slop_ms);
195 1 : puts("For testing functionality, this test should be run with a different");
196 1 : puts("set of parameters. On an unloaded i7, a sleep duration (-S) of");
197 1 : puts("1000000 ns (one millisecond), with a fuzziness factor (-f) of 1.25,");
198 1 : puts("a constant test overhead of 100000 ns (100 us), and a");
199 1 : puts("sleep duration \"slop\" (-s) of 0 is fine.");
200 :
201 1 : while (-1 != (opt = getopt(ac, av, "f:o:s:S:"))) {
202 1 : switch (opt) {
203 : case 'f':
204 0 : g_fuzzy_factor = strtod(optarg, (char **) NULL);
205 0 : break;
206 : case 'o':
207 0 : g_syscall_overhead = strtoul(optarg, (char **) NULL, 0);
208 0 : break;
209 : case 's':
210 1 : g_slop_ms = strtoul(optarg, (char **) NULL, 0);
211 1 : break;
212 : case 'S':
213 0 : sleep_nanos = strtoul(optarg, (char **) NULL, 0);
214 0 : break;
215 : default:
216 0 : fprintf(stderr, "nacl_clock_test: unrecognized option `%c'.\n",
217 : opt);
218 0 : fprintf(stderr,
219 : "Usage: nacl_clock_test [-f fuzz_factor] [-s sleep_nanos]\n"
220 : " [-o syscall_overhead_nanos]\n");
221 0 : return -1;
222 : }
223 : }
224 :
225 1 : NaClPlatformInit();
226 :
227 1 : num_failures += ClockMonotonicAccuracyTest(sleep_nanos);
228 1 : num_failures += ClockRealtimeAccuracyTest();
229 :
230 1 : NaClPlatformFini();
231 :
232 1 : return num_failures;
233 : }
|