/*
*-----------------------------------------------------------------------------
*
- * Hostinfo_RawSystemTimerNS --
+ * HostinfoGetTimeOfDayMonotonic --
*
- * Return the raw time.
- * - Do this as fast as is practical; no locks are used
- * - These timers may go backwards or make no forward progress
+ * Return the system time as indicated by Hostinfo_GetTimeOfDay(), with
+ * locking to ensure monotonicity.
*
- * Hostinfo_SystemTimerNS --
+ * Uses OS native locks as lib/lock is not available in lib/misc. This
+ * is safe because nothing occurs while locked that can be reentrant.
*
- * Return the time.
- * - These timers are documented to never go backwards.
- * - These timers may take locks
+ * Results:
+ * The time in microseconds is returned. Zero upon error.
*
- * NOTES:
- * These are the routines to use when performing timing measurements.
+ * Side effects:
+ * None.
*
- * The value returned is valid (finish-time - start-time) only within a
- * single process. Don't send a time measurement obtained with these
- * routines to another process and expect a relative time measurement
- * to be correct.
+ *-----------------------------------------------------------------------------
+ */
+
+static VmTimeType
+HostinfoGetTimeOfDayMonotonic(void)
+{
+ VmTimeType newTime;
+ VmTimeType curTime;
+
+ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+ static VmTimeType lastTimeBase;
+ static VmTimeType lastTimeRead;
+ static VmTimeType lastTimeReset;
+
+ pthread_mutex_lock(&mutex); // Use native mechanism, just like Windows
+
+ Hostinfo_GetTimeOfDay(&curTime);
+
+ if (curTime == 0) {
+ newTime = 0;
+ goto exit;
+ }
+
+ /*
+ * Don't let time be negative or go backward. We do this by tracking a
+ * base and moving forward from there.
+ */
+
+ newTime = lastTimeBase + (curTime - lastTimeReset);
+
+ if (newTime < lastTimeRead) {
+ lastTimeReset = curTime;
+ lastTimeBase = lastTimeRead + 1;
+ newTime = lastTimeBase + (curTime - lastTimeReset);
+ }
+
+ lastTimeRead = newTime;
+
+exit:
+ pthread_mutex_unlock(&mutex);
+
+ return newTime;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
*
- * The actual resolution of these "clocks" are undefined - it varies
- * depending on hardware, OSen and OS versions.
+ * HostinfoSystemTimerVmkernel --
+ * HostinfoSystemTimerMach --
+ * HostinfoSystemTimerPosix --
+ *
+ * Returns system time based on a monotonic, nanosecond-resolution,
+ * fast timer provided by the (relevant) operating system.
+ *
+ * Caller should check return value, as some variants may not be known
+ * to be absent until runtime. Where possible, these functions collapse
+ * into constants at compile-time.
*
* Results:
- * The time in nanoseconds is returned. Zero upon error.
+ * TRUE if timer is available; FALSE to indicate unavailability.
+ * If available, the current time is returned via 'result'.
*
* Side effects:
* None.
*-----------------------------------------------------------------------------
*/
-#if defined(__APPLE__)
-VmTimeType
-Hostinfo_RawSystemTimerNS(void)
+static Bool
+HostinfoSystemTimerVmkernel(VmTimeType *result) // OUT
{
+ if (HostType_OSIsPureVMK()) {
+ uint64 uptime = 0;
+ /*
+ * The uptime is ensured to never go backwards and is valid across
+ * processes.
+ *
+ * TODO: get a vmkernel uptime that reports in ns (for real?)
+ */
+#ifdef VMX86_SERVER
+ if (UNLIKELY(VMKernel_GetUptimeUS(&uptime) != VMK_OK)) {
+ Log("%s: failure!\n", __FUNCTION__);
+
+ uptime = 0; // A timer read failure - this is really bad!
+ }
+#endif
+
+ *result = 1000 * uptime; // Convert to nanoseconds
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static Bool
+HostinfoSystemTimerMach(VmTimeType *result) // OUT
+{
+#if __APPLE__
+# define vmx86_apple 1
VmTimeType raw;
mach_timebase_info_data_t *ptr;
static Atomic_Ptr atomic; /* Implicitly initialized to NULL. --mbellon */
if ((ptr->numer == 1) && (ptr->denom == 1)) {
/* The scaling values are unity, save some time/arithmetic */
- return raw;
+ *result = raw;
} else {
/* The scaling values are not unity. Prevent overflow when scaling */
- return ((double) raw) * (((double) ptr->numer) / ((double) ptr->denom));
+ *result = ((double) raw) * (((double) ptr->numer) / ((double) ptr->denom));
}
+ return TRUE;
+#else
+# define vmx86_apple 0
+ return FALSE;
+#endif
}
-
-VmTimeType
-Hostinfo_SystemTimerNS(void)
-{
- return Hostinfo_RawSystemTimerNS();
-}
-#elif defined(VMX86_SERVER)
-VmTimeType
-Hostinfo_RawSystemTimerNS(void)
+static Bool
+HostinfoSystemTimerPosix(VmTimeType *result) // OUT
{
- VmTimeType uptime;
+#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
+# define vmx86_posix 1
+ /* Weak declaration to avoid librt.so dependency */
+ extern int clock_gettime(clockid_t clk_id, struct timespec *tp) __attribute__ ((weak));
- /*
- * The uptime is ensured to never go backwards and is valid across
- * processes.
- *
- * TODO: get a vmkernel uptime that reports in ns (for real?)
- */
+ /* Assignment is idempotent (expected to always be same answer). */
+ static volatile enum { UNKNOWN, PRESENT, FAILED } hasGetTime = UNKNOWN;
- if (UNLIKELY(VMKernel_GetUptimeUS(&uptime) != VMK_OK)) {
- Log("%s: failure!\n", __FUNCTION__);
+ struct timespec ts;
+ int ret;
- uptime = 0; // A timer read failure - this is really bad!
- }
+ switch (hasGetTime) {
+ case FAILED:
+ break;
+ case UNKNOWN:
+ if (clock_gettime == NULL) {
+ /* librt.so is not present. No clock_gettime() */
+ hasGetTime = FAILED;
+ break;
+ }
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ret != 0) {
+ hasGetTime = FAILED;
+ /*
+ * Well-understood error codes:
+ * ENOSYS, OS does not implement syscall
+ * EINVAL, OS implements syscall but not CLOCK_MONOTONIC
+ */
+ if (errno != ENOSYS && errno != EINVAL) {
+ Log("%s: failure, err %d!\n", __FUNCTION__, errno);
+ }
+ break;
+ }
+ hasGetTime = PRESENT;
+ /* Fall through to 'case PRESENT' */
- return 1000 * uptime; // Convert to nanoseconds
+ case PRESENT:
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ ASSERT(ret == 0);
+ *result = (VmTimeType)ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ return TRUE;
+ }
+ return FALSE;
+#else
+# define vmx86_posix 0
+ /* No Posix support for clock_gettime() */
+ return FALSE;
+#endif
}
-VmTimeType
-Hostinfo_SystemTimerNS(void)
-{
- return Hostinfo_RawSystemTimerNS();
-}
-#else
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Hostinfo_RawSystemTimerNS --
+ *
+ * Return the raw time.
+ * - Do this as fast as is practical; no locks are used
+ * - These timers may go backwards or make no forward progress
+ *
+ * Hostinfo_SystemTimerNS --
+ *
+ * Return the time.
+ * - These timers are documented to never go backwards.
+ * - These timers may take locks
+ *
+ * NOTES:
+ * These are the routines to use when performing timing measurements.
+ *
+ * The value returned is valid (finish-time - start-time) only within a
+ * single process. Don't send a time measurement obtained with these
+ * routines to another process and expect a relative time measurement
+ * to be correct.
+ *
+ * The actual resolution of these "clocks" are undefined - it varies
+ * depending on hardware, OSen and OS versions.
+ *
+ * Results:
+ * The time in nanoseconds is returned. Zero upon error.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
VmTimeType
Hostinfo_RawSystemTimerNS(void)
{
VmTimeType result;
- struct timeval tval;
- /* Read the time from the operating system - may go backwards. */
- if (LIKELY(gettimeofday(&tval, NULL) == 0)) {
- /* Convert into nanoseconds. */
- result = 1000ULL * (((VmTimeType)tval.tv_sec) * 1000000 + tval.tv_usec);
+ if ((vmx86_server && HostinfoSystemTimerVmkernel(&result)) ||
+ (vmx86_apple && HostinfoSystemTimerMach(&result)) ||
+ (vmx86_posix && HostinfoSystemTimerPosix(&result))) {
+ /* Host provides monotonic clock source. */
} else {
- Log("%s: failure!\n", __FUNCTION__);
-
- result = 0; // A timer read failure - this is really bad!
+ Hostinfo_GetTimeOfDay(&result);
+ result *= 1000; /* GetTimeOfDay is microseconds. */
}
-
return result;
}
-
VmTimeType
Hostinfo_SystemTimerNS(void)
{
- VmTimeType newTime;
- VmTimeType curTime;
-
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
- static VmTimeType lastTimeBase;
- static VmTimeType lastTimeRead;
- static VmTimeType lastTimeReset;
-
- /*
- * TODO: research using -lrt and clock_gettime.
- *
- * These routines live in lib/misc and cannot use routines from other
- * libraries, including lib/lock.
- */
-
- pthread_mutex_lock(&mutex); // Use native mechanism, just like Windows
-
- curTime = Hostinfo_RawSystemTimerNS();
-
- if (curTime == 0) {
- newTime = 0;
- goto exit;
- }
-
- /*
- * Don't let time be negative or go backward. We do this by tracking a
- * base and moving forward from there.
- */
-
- newTime = lastTimeBase + (curTime - lastTimeReset);
+ VmTimeType result;
- if (newTime < lastTimeRead) {
- lastTimeReset = curTime;
- lastTimeBase = lastTimeRead + 1;
- newTime = lastTimeBase + (curTime - lastTimeReset);
+ if ((vmx86_server && HostinfoSystemTimerVmkernel(&result)) ||
+ (vmx86_apple && HostinfoSystemTimerMach(&result)) ||
+ (vmx86_posix && HostinfoSystemTimerPosix(&result))) {
+ /* Host provides monotonic clock source. */
+ return result;
+ } else {
+ /* GetTimeOfDay is microseconds. */
+ return HostinfoGetTimeOfDayMonotonic() * 1000;
}
-
- lastTimeRead = newTime;
-
-exit:
- pthread_mutex_unlock(&mutex);
-
- return newTime;
}
-#endif
+#undef vmx86_apple
+#undef vmx86_posix
/*