int NaClClockGetTime(nacl_clockid_t clk_id, struct nacl_abi_timespec *tp) { int rv = -NACL_ABI_EINVAL; struct nacl_abi_timeval tv; uint64_t t_mono_prev_us; uint64_t t_mono_cur_us; if (!g_NaClClock_is_initialized) { NaClLog(LOG_FATAL, "NaClClockGetTime invoked without successful NaClClockInit\n"); } switch (clk_id) { case NACL_CLOCK_REALTIME: rv = NaClGetTimeOfDay(&tv); if (0 == rv) { tp->tv_sec = tv.nacl_abi_tv_sec; tp->tv_nsec = tv.nacl_abi_tv_usec * 1000; } break; case NACL_CLOCK_MONOTONIC: /* * Get real time, compare with last monotonic time. If later * than last monotonic time, set last monotonic time to real * time timestamp; otherwise we leave last monotonoic time * alone. In either case, return last monotonic time. * * The interpretation used here is that "monotonic" means * monotonic non-decreasing, as opposed to monotonic increasing. * We don't assume that GetTimeOfDay only yields high-order bits * so we can replace low-order bits of the time value with a * counter to fake monotonicity. We are dangerously close to * the resolution limit of 1ns imposed by the timespec structure * already -- it's only a few Moore's Law generations away where * we may have to return the same time stamp for repeated calls * to clock_gettime (if CPU frequency clock is continued to be * used to drive performance counters; RTDSC is moving to a * fixed rate [constant_tsc], fortunately). */ rv = NaClGetTimeOfDay(&tv); if (0 == rv) { NaClXMutexLock(&g_nacl_clock_mu); t_mono_prev_us = g_nacl_clock_tv.nacl_abi_tv_sec * 1000000 + g_nacl_clock_tv.nacl_abi_tv_usec; t_mono_cur_us = tv.nacl_abi_tv_sec * 1000000 + tv.nacl_abi_tv_usec; if (t_mono_cur_us >= t_mono_cur_us) { g_nacl_clock_tv = tv; } tp->tv_sec = g_nacl_clock_tv.nacl_abi_tv_sec + MAGIC_OFFSET; tp->tv_nsec = g_nacl_clock_tv.nacl_abi_tv_usec * 1000; NaClXMutexUnlock(&g_nacl_clock_mu); rv = 0; } break; case NACL_CLOCK_PROCESS_CPUTIME_ID: case NACL_CLOCK_THREAD_CPUTIME_ID: break; } return rv; }
void TestAbsWait(void *arg) { uint64_t sleep_usec; struct nacl_abi_timeval now; struct nacl_abi_timespec t; sleep_usec = ((struct TestFunctorArg *) arg)->sleep_usec; (void) NaClGetTimeOfDay(&now); t.tv_sec = (nacl_abi_time_t) (now.nacl_abi_tv_sec + sleep_usec / kMicroXinX); t.tv_nsec = (long int) (kNanoXinMicroX * (now.nacl_abi_tv_usec + (sleep_usec % kMicroXinX))); while (t.tv_nsec > kNanoXinX) { t.tv_nsec -= kNanoXinX; ++t.tv_sec; } if (gVerbosity > 1) { printf("TestAbsWait: locking\n"); } NaClXMutexLock(&gMu); if (gVerbosity > 1) { printf("TestAbsWait: waiting\n"); } NaClXCondVarTimedWaitAbsolute(&gCv, &gMu, &t); if (gVerbosity > 1) { printf("TestAbsWait: unlocking\n"); } NaClXMutexUnlock(&gMu); }
int NaClClockInit(void) { if (0 != NaClGetTimeOfDay(&g_nacl_clock_tv)) { return 0; } g_NaClClock_is_initialized = NaClMutexCtor(&g_nacl_clock_mu); return g_NaClClock_is_initialized; }
/* * Records the time in sv and returns its index in sv * Note that the first time this routine is called on a sv that was just * constructed via NaClPerfCounterCtor(), it will return 1, but that * is actually the SECOND sample. */ int NaClPerfCounterMark(struct NaClPerfCounter *sv, const char *ev_name) { if((NULL == sv) || (NULL == ev_name)) { ZLOG(LOG_ERROR, "NaClPerfCounterMark received null args"); return -1; } if(sv->samples >= NACL_MAX_PERF_COUNTER_SAMPLES) { ZLOG(LOG_ERROR, "NaClPerfCounterMark going beyond buffer size"); return -1; } /* busy loop until we succeed, damn it */ while(0 != NaClGetTimeOfDay(&(sv->sample_list[sv->samples]))) ; /* * This relies upon memset() inside NaClPerfCounterCtor() for * correctness */ NACL_ASSERT_IS_ARRAY(sv->sample_names[sv->samples]); strncpy(sv->sample_names[sv->samples], ev_name, LAST_IDX(sv->sample_names[sv->samples])); /* Being explicit about string termination */ sv->sample_names[sv->samples][LAST_IDX(sv->sample_names[sv->samples])] = '\0'; return (sv->samples)++; }
void NaClPerfCounterCtor(struct NaClPerfCounter *sv, const char *app_name) { if(NULL == sv) { ZLOG(LOG_ERROR, "NaClPerfCounterStart received null pointer"); return; }; memset(sv, 0, sizeof(struct NaClPerfCounter)); if(NULL == app_name) { app_name = "__unknown_app__"; } NACL_ASSERT_IS_ARRAY(sv->app_name); strncpy(sv->app_name, app_name, LAST_IDX(sv->app_name)); /* Being explicit about string termination */ sv->app_name[LAST_IDX(sv->app_name)] = '\0'; strncpy(sv->sample_names[0], "__start__", LAST_IDX(sv->sample_names[0])); while(0 != NaClGetTimeOfDay(&sv->sample_list[sv->samples])) { /* repeat until we get a sample */ } sv->samples++; }
int32_t NaClSysGetTimeOfDay(struct NaClAppThread *natp, struct nacl_abi_timeval *tv, struct nacl_abi_timezone *tz) { int32_t retval; uintptr_t sysaddr; UNREFERENCED_PARAMETER(tz); NaClSysCommonThreadSyscallEnter(natp); sysaddr = NaClUserToSysAddrRange(natp->nap, (uintptr_t) tv, sizeof tv); /* * tz is not supported in linux, nor is it supported by glibc, since * tzset(3) and the zoneinfo file should be used instead. * * TODO(bsy) Do we make the zoneinfo directory available to * applications? */ if (kNaClBadAddress == sysaddr) { retval = -NACL_ABI_EFAULT; goto cleanup; } retval = NaClGetTimeOfDay((struct nacl_abi_timeval *) sysaddr); cleanup: NaClSysCommonThreadSyscallLeave(natp); return retval; }
/* * ClockRealtimeAccuracyTest compares the time returned by * NACL_ABI_CLOCK_REALTIME against that returned by NaClGetTimeOfDay. */ int ClockRealtimeAccuracyTest(void) { int num_failures = 0; int err; struct nacl_abi_timespec t_now_ts; struct nacl_abi_timeval t_now_tv; uint64_t t_now_ts_nanos; uint64_t t_now_tv_nanos; int64_t t_now_diff_nanos; printf("\nCLOCK_REALTIME accuracy test:\n"); if (0 != (err = NaClClockGetTime(NACL_ABI_CLOCK_REALTIME, &t_now_ts))) { fprintf(stderr, "nacl_clock_test: NaClClockGetTime (now) failed, error %d\n", err); num_failures++; goto done; } if (0 != (err = NaClGetTimeOfDay(&t_now_tv))) { fprintf(stderr, "nacl_clock_test: NaClGetTimeOfDay (now) failed, error %d\n", err); num_failures++; goto done; } t_now_ts_nanos = t_now_ts.tv_sec * NANOS_PER_UNIT + t_now_ts.tv_nsec; t_now_tv_nanos = t_now_tv.nacl_abi_tv_sec * NANOS_PER_UNIT + t_now_tv.nacl_abi_tv_usec * NANOS_PER_MICRO; printf("clock_gettime: %20"NACL_PRIu64" nS\n", t_now_ts_nanos); printf("gettimeofday: %20"NACL_PRIu64" nS\n", t_now_tv_nanos); t_now_diff_nanos = t_now_ts_nanos - t_now_tv_nanos; if (t_now_diff_nanos < 0) { t_now_diff_nanos = -t_now_diff_nanos; } printf("time difference: %20"NACL_PRId64" nS\n", t_now_diff_nanos); if (t_now_ts_nanos < g_syscall_overhead) { printf("discrepancy too large\n"); num_failures++; } done: printf((0 == num_failures) ? "PASSED\n" : "FAILED\n"); return num_failures; }
int32_t NaClSysGetTimeOfDay(struct NaClAppThread *natp, struct nacl_abi_timeval *tv, struct nacl_abi_timezone *tz) { uintptr_t sysaddr; int retval; struct nacl_abi_timeval now; UNREFERENCED_PARAMETER(tz); NaClLog(3, ("Entered NaClSysGetTimeOfDay(%08"NACL_PRIxPTR ", 0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxPTR")\n"), (uintptr_t) natp, (uintptr_t) tv, (uintptr_t) tz); NaClSysCommonThreadSyscallEnter(natp); sysaddr = NaClUserToSysAddrRange(natp->nap, (uintptr_t) tv, sizeof tv); /* * tz is not supported in linux, nor is it supported by glibc, since * tzset(3) and the zoneinfo file should be used instead. * * TODO(bsy) Do we make the zoneinfo directory available to * applications? */ if (kNaClBadAddress == sysaddr) { retval = -NACL_ABI_EFAULT; goto cleanup; } retval = NaClGetTimeOfDay(&now); if (0 == retval) { /* * To make it harder to distinguish Linux platforms from Windows, * coarsen the time to the same level we get on Windows - * milliseconds, unless in "debug" mode. */ if (!NaClHighResolutionTimerEnabled()) { now.nacl_abi_tv_usec = (now.nacl_abi_tv_usec / 1000) * 1000; } CHECK(now.nacl_abi_tv_usec >= 0); CHECK(now.nacl_abi_tv_usec < NACL_MICROS_PER_UNIT); *(struct nacl_abi_timeval *) sysaddr = now; } cleanup: NaClSysCommonThreadSyscallLeave(natp); return retval; }
void TimerCheckElapsed(struct Timer *self, uint64_t cond_timeout_usec, uint64_t min_elapsed_usec, uint64_t max_elapsed_usec) { struct nacl_abi_timeval now; uint64_t elapsed_usec; int failed; (void) NaClGetTimeOfDay(&now); elapsed_usec = kMicroXinX * (now.nacl_abi_tv_sec - self->base.nacl_abi_tv_sec) + now.nacl_abi_tv_usec - self->base.nacl_abi_tv_usec; /* * NB: De Morgan's thm on ASSERT args so that there will be some output * on failure. */ failed = (min_elapsed_usec > elapsed_usec || elapsed_usec > max_elapsed_usec); if (gVerbosity || failed) { printf("condvar timeout was %"NACL_PRId64".%06"NACL_PRId64" sec\n", cond_timeout_usec / kMicroXinX, cond_timeout_usec % kMicroXinX); printf("min elapsed %"NACL_PRId64".%06"NACL_PRId64" sec\n", min_elapsed_usec / kMicroXinX, min_elapsed_usec % kMicroXinX); printf("max elapsed %"NACL_PRId64".%06"NACL_PRId64" sec\n", max_elapsed_usec / kMicroXinX, max_elapsed_usec % kMicroXinX); printf("actual elapsed %"NACL_PRId64".%06"NACL_PRId64" sec\n", elapsed_usec / kMicroXinX, elapsed_usec % kMicroXinX); if (failed) { PrintFailureSuggestions(); } } ASSERT(min_elapsed_usec <= elapsed_usec); ASSERT(elapsed_usec <= max_elapsed_usec); }
void TimerStart(struct Timer *self) { (void) NaClGetTimeOfDay(&self->base); }