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 #define NANOS_PER_MICRO (1000) |
| 28 #define MICROS_PER_MILLI (1000) |
| 29 #define NANOS_PER_MILLI (NANOS_PER_MICRO * MICROS_PER_MILLI) |
| 30 #define MICROS_PER_UNIT (1000 * 1000) |
| 31 #define NANOS_PER_UNIT (NANOS_PER_MICRO * MICROS_PER_UNIT) |
| 32 |
| 33 /* |
| 34 * On an unloaded i7, 1ms with a 1.25 fuzziness factor and a 100,000 |
| 35 * ns constant syscall overhead works fine. On bots, we have to be |
| 36 * much more generous. (This is especially true for qemu-based |
| 37 * testing.) |
| 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 static int g_cputime = 1; |
| 48 static double g_fuzzy_factor = DEFAULT_NANOSLEEP_EXTRA_FACTOR; |
| 49 static uint64_t g_syscall_overhead = DEFAULT_NANOSLEEP_EXTRA_OVERHEAD; |
| 50 static 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 static int ClockMonotonicAccuracyTest(uint64_t sleep_nanos) { |
| 60 int num_failures = 0; |
| 61 |
| 62 struct timespec t_start; |
| 63 struct timespec t_sleep; |
| 64 struct timespec t_end; |
| 65 |
| 66 uint64_t elapsed_nanos; |
| 67 uint64_t elapsed_lower_bound; |
| 68 uint64_t elapsed_upper_bound; |
| 69 |
| 70 t_sleep.tv_sec = sleep_nanos / NANOS_PER_UNIT; |
| 71 t_sleep.tv_nsec = sleep_nanos % NANOS_PER_UNIT; |
| 72 |
| 73 printf("\nCLOCK_MONOTONIC accuracy test:\n"); |
| 74 |
| 75 if (0 != clock_gettime(CLOCK_MONOTONIC, &t_start)) { |
| 76 fprintf(stderr, "clock_test: clock_gettime (start) failed, error %d\n", |
| 77 errno); |
| 78 ++num_failures; |
| 79 goto done; |
| 80 } |
| 81 for (;;) { |
| 82 if (0 == nanosleep(&t_sleep, &t_sleep)) { |
| 83 break; |
| 84 } |
| 85 if (EINTR == errno) { |
| 86 /* interrupted syscall: sleep some more */ |
| 87 continue; |
| 88 } |
| 89 fprintf(stderr, "clock_test: nanosleep failed, error %d\n", |
| 90 errno); |
| 91 num_failures++; |
| 92 goto done; |
| 93 } |
| 94 if (0 != clock_gettime(CLOCK_MONOTONIC, &t_end)) { |
| 95 fprintf(stderr, "clock_test: clock_gettime (end) failed, error %d\n", |
| 96 errno); |
| 97 return 1; |
| 98 } |
| 99 |
| 100 elapsed_nanos = (t_end.tv_sec - t_start.tv_sec) * NANOS_PER_UNIT + |
| 101 (t_end.tv_nsec - t_start.tv_nsec) + g_slop_ms * NANOS_PER_MILLI; |
| 102 |
| 103 elapsed_lower_bound = sleep_nanos; |
| 104 elapsed_upper_bound = (uint64_t) (sleep_nanos * g_fuzzy_factor + |
| 105 g_syscall_overhead); |
| 106 |
| 107 printf("requested sleep: %20"PRIu64" nS\n", sleep_nanos); |
| 108 printf("elapsed sleep: %20"PRIu64" nS\n", elapsed_nanos); |
| 109 printf("sleep lower bound: %20"PRIu64" nS\n", elapsed_lower_bound); |
| 110 printf("sleep upper bound: %20"PRIu64" nS\n", elapsed_upper_bound); |
| 111 |
| 112 if (elapsed_nanos < elapsed_lower_bound || |
| 113 elapsed_upper_bound < elapsed_nanos) { |
| 114 printf("discrepancy too large\n"); |
| 115 num_failures++; |
| 116 } |
| 117 done: |
| 118 printf((0 == num_failures) ? "PASSED\n" : "FAILED\n"); |
| 119 return num_failures; |
| 120 } |
| 121 |
| 122 /* |
| 123 * ClockRealtimeAccuracyTest compares the time returned by |
| 124 * CLOCK_REALTIME against that returned by gettimeofday. |
| 125 */ |
| 126 static int ClockRealtimeAccuracyTest(void) { |
| 127 int num_failures = 0; |
| 128 |
| 129 struct timespec t_now_ts; |
| 130 struct timeval t_now_tv; |
| 131 |
| 132 uint64_t t_now_ts_nanos; |
| 133 uint64_t t_now_tv_nanos; |
| 134 int64_t t_now_diff_nanos; |
| 135 |
| 136 printf("\nCLOCK_REALTIME accuracy test:\n"); |
| 137 |
| 138 if (0 != clock_gettime(CLOCK_REALTIME, &t_now_ts)) { |
| 139 fprintf(stderr, "clock_test: clock_gettime (now) failed, error %d\n", |
| 140 errno); |
| 141 num_failures++; |
| 142 goto done; |
| 143 } |
| 144 if (0 != gettimeofday(&t_now_tv, NULL)) { |
| 145 fprintf(stderr, "clock_test: gettimeofday (now) failed, error %d\n", |
| 146 errno); |
| 147 num_failures++; |
| 148 goto done; |
| 149 } |
| 150 |
| 151 t_now_ts_nanos = t_now_ts.tv_sec * NANOS_PER_UNIT + t_now_ts.tv_nsec; |
| 152 t_now_tv_nanos = t_now_tv.tv_sec * NANOS_PER_UNIT + |
| 153 t_now_tv.tv_usec * NANOS_PER_MICRO; |
| 154 |
| 155 printf("clock_gettime: %20"PRIu64" nS\n", t_now_ts_nanos); |
| 156 printf("gettimeofday: %20"PRIu64" nS\n", t_now_tv_nanos); |
| 157 |
| 158 t_now_diff_nanos = t_now_ts_nanos - t_now_tv_nanos; |
| 159 if (t_now_diff_nanos < 0) { |
| 160 t_now_diff_nanos = -t_now_diff_nanos; |
| 161 } |
| 162 printf("time difference: %20"PRId64" nS\n", t_now_diff_nanos); |
| 163 |
| 164 if (t_now_ts_nanos < g_syscall_overhead) { |
| 165 printf("discrepancy too large\n"); |
| 166 num_failures++; |
| 167 } |
| 168 done: |
| 169 printf((0 == num_failures) ? "PASSED\n" : "FAILED\n"); |
| 170 return num_failures; |
| 171 } |
| 172 |
| 173 struct ThreadInfo { |
| 174 size_t cycles; |
| 175 struct timespec thread_time; |
| 176 struct timespec process_time; |
| 177 int num_failures; |
| 178 }; |
| 179 |
| 180 void *ThreadFunction(void *ptr) { |
| 181 size_t i; |
| 182 struct ThreadInfo *info = (struct ThreadInfo *) ptr; |
| 183 |
| 184 for (i = 1; i < info->cycles; i++) { |
| 185 /* |
| 186 * Use a barrier to ensure that the compiler does not optimize the |
| 187 * empty loop out as we really only want to burn the thread time. |
| 188 */ |
| 189 __sync_synchronize(); |
| 190 } |
| 191 |
| 192 if (0 != clock_gettime(CLOCK_THREAD_CPUTIME_ID, &info->thread_time)) { |
| 193 fprintf(stderr, "clock_test: clock_gettime (now) failed, error %d\n", |
| 194 errno); |
| 195 info->num_failures++; |
| 196 return NULL; |
| 197 } |
| 198 |
| 199 if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &info->process_time)) { |
| 200 fprintf(stderr, "clock_test: clock_gettime (now) failed, error %d\n", |
| 201 errno); |
| 202 info->num_failures++; |
| 203 return NULL; |
| 204 } |
| 205 |
| 206 return NULL; |
| 207 } |
| 208 |
| 209 static int ClockCpuTimeAccuracyTest(void) { |
| 210 int num_failures = 0; |
| 211 |
| 212 int err; |
| 213 struct timespec t_process_start; |
| 214 struct timespec t_process_end; |
| 215 struct timespec t_thread_start; |
| 216 struct timespec t_thread_end; |
| 217 |
| 218 uint64_t thread_elapsed = 0; |
| 219 uint64_t process_elapsed = 0; |
| 220 uint64_t child_thread_elapsed = 0; |
| 221 uint64_t elapsed_lower_bound; |
| 222 uint64_t elapsed_upper_bound; |
| 223 |
| 224 size_t i; |
| 225 struct ThreadInfo info[10]; |
| 226 pthread_t thread[10]; |
| 227 |
| 228 printf("\nCLOCK_PROCESS/THREAD_CPUTIME_ID accuracy test:\n"); |
| 229 |
| 230 if (0 != clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t_thread_start)) { |
| 231 fprintf(stderr, "clock_test: clock_gettime (now) failed, error %d\n", |
| 232 errno); |
| 233 num_failures++; |
| 234 goto done; |
| 235 } |
| 236 |
| 237 if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t_process_start)) { |
| 238 fprintf(stderr, "clock_test: clock_gettime (now) failed, error %d\n", |
| 239 errno); |
| 240 num_failures++; |
| 241 goto done; |
| 242 } |
| 243 |
| 244 for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) { |
| 245 memset(&info[i], 0, sizeof info[i]); |
| 246 info[i].cycles = i * 10000000; |
| 247 if (0 != (err = pthread_create(&thread[i], NULL, |
| 248 ThreadFunction, &info[i]))) { |
| 249 fprintf(stderr, "clock_test: pthread_create failed, error %d\n", |
| 250 err); |
| 251 num_failures++; |
| 252 goto done; |
| 253 } |
| 254 } |
| 255 |
| 256 for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) { |
| 257 if (0 != (err = pthread_join(thread[i], NULL))) { |
| 258 fprintf(stderr, "clock_test: pthread_join failed, error %d\n", |
| 259 err); |
| 260 num_failures++; |
| 261 goto done; |
| 262 } |
| 263 } |
| 264 |
| 265 if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t_process_end)) { |
| 266 fprintf(stderr, "clock_test: clock_gettime (now) failed, error %d\n", |
| 267 errno); |
| 268 num_failures++; |
| 269 goto done; |
| 270 } |
| 271 |
| 272 if (0 != clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t_thread_end)) { |
| 273 fprintf(stderr, "clock_test: clock_gettime (now) failed, error %d\n", |
| 274 errno); |
| 275 num_failures++; |
| 276 goto done; |
| 277 } |
| 278 |
| 279 thread_elapsed = |
| 280 (t_thread_end.tv_sec - t_thread_start.tv_sec) * NANOS_PER_UNIT + |
| 281 (t_thread_end.tv_nsec - t_thread_start.tv_nsec); |
| 282 |
| 283 process_elapsed = |
| 284 (t_process_end.tv_sec - t_process_start.tv_sec) * NANOS_PER_UNIT + |
| 285 (t_process_end.tv_nsec - t_process_start.tv_nsec); |
| 286 |
| 287 for (i = 0; i < NACL_ARRAY_SIZE(thread); i++) { |
| 288 uint64_t thread_elapsed_nanos; |
| 289 uint64_t process_elapsed_nanos; |
| 290 |
| 291 if (info[i].num_failures > 0) { |
| 292 num_failures += info[i].num_failures; |
| 293 goto done; |
| 294 } |
| 295 |
| 296 thread_elapsed_nanos = info[i].thread_time.tv_sec * NANOS_PER_UNIT + |
| 297 info[i].thread_time.tv_nsec; |
| 298 process_elapsed_nanos = info[i].process_time.tv_sec * NANOS_PER_UNIT + |
| 299 info[i].process_time.tv_nsec; |
| 300 printf("%zd: thread=%20"PRIu64" nS, process=%20"PRIu64" nS\n", |
| 301 i, thread_elapsed_nanos, process_elapsed_nanos); |
| 302 child_thread_elapsed += thread_elapsed_nanos; |
| 303 } |
| 304 |
| 305 elapsed_lower_bound = thread_elapsed + child_thread_elapsed; |
| 306 elapsed_upper_bound = (uint64_t) (thread_elapsed + |
| 307 child_thread_elapsed * g_fuzzy_factor + g_syscall_overhead); |
| 308 |
| 309 printf("thread time: %20"PRIu64" nS\n", thread_elapsed); |
| 310 printf("process time: %20"PRIu64" nS\n", process_elapsed); |
| 311 printf("child thread time: %20"PRIu64" nS\n", child_thread_elapsed); |
| 312 printf("elapsed lower bound: %20"PRIu64" nS\n", elapsed_lower_bound); |
| 313 printf("elapsed upper bound: %20"PRIu64" nS\n", elapsed_upper_bound); |
| 314 |
| 315 if (process_elapsed < elapsed_lower_bound || |
| 316 elapsed_upper_bound < process_elapsed) { |
| 317 printf("discrepancy too large\n"); |
| 318 num_failures++; |
| 319 } |
| 320 done: |
| 321 printf((0 == num_failures) ? "PASSED\n" : "FAILED\n"); |
| 322 return num_failures; |
| 323 } |
| 324 |
| 325 int main(int ac, char **av) { |
| 326 uint64_t sleep_nanos = DEFAULT_NANOSLEEP_TIME; |
| 327 int opt; |
| 328 uint32_t num_failures = 0; |
| 329 |
| 330 while (-1 != (opt = getopt(ac, av, "cf:o:s:S:"))) { |
| 331 switch (opt) { |
| 332 case 'c': |
| 333 g_cputime = 0; |
| 334 break; |
| 335 case 'f': |
| 336 g_fuzzy_factor = strtod(optarg, (char **) NULL); |
| 337 break; |
| 338 case 'o': |
| 339 g_syscall_overhead = strtoul(optarg, (char **) NULL, 0); |
| 340 break; |
| 341 case 's': |
| 342 g_slop_ms = strtoul(optarg, (char **) NULL, 0); |
| 343 break; |
| 344 case 'S': |
| 345 sleep_nanos = strtoul(optarg, (char **) NULL, 0); |
| 346 break; |
| 347 default: |
| 348 fprintf(stderr, "clock_test: unrecognized option `%c'.\n", |
| 349 opt); |
| 350 fprintf(stderr, |
| 351 "Usage: clock_test [-f fuzz_factor] [-s sleep_nanos]\n" |
| 352 " [-o syscall_overhead_nanos]\n"); |
| 353 return -1; |
| 354 } |
| 355 } |
| 356 |
| 357 num_failures += ClockMonotonicAccuracyTest(sleep_nanos); |
| 358 num_failures += ClockRealtimeAccuracyTest(); |
| 359 if (g_cputime) { |
| 360 num_failures += ClockCpuTimeAccuracyTest(); |
| 361 } |
| 362 |
| 363 return num_failures; |
| 364 } |
OLD | NEW |