Merge branch 'monotonic_v2_squashed'

This commit is contained in:
Nick Mathewson 2016-07-19 11:42:26 +02:00
commit 558f7d3701
16 changed files with 856 additions and 170 deletions

6
changes/monotonic Normal file
View File

@ -0,0 +1,6 @@
o Minor features (backend):
- Tor now uses the operating system's monotonic timers (where available)
for internal fine-grained timing. Previously we would look at the
system clock, and then attempt to compensate for the clock running
backwards. Closes ticket 18908.

View File

@ -33,6 +33,12 @@
#ifdef HAVE_SYS_STAT_H #ifdef HAVE_SYS_STAT_H
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -89,12 +95,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#include "tor_readpassphrase.h" #include "tor_readpassphrase.h"
#endif #endif
#ifndef HAVE_GETTIMEOFDAY
#ifdef HAVE_FTIME
#include <sys/timeb.h>
#endif
#endif
/* Includes for the process attaching prevention */ /* Includes for the process attaching prevention */
#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) #if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
/* Only use the linux prctl; the IRIX prctl is totally different */ /* Only use the linux prctl; the IRIX prctl is totally different */
@ -116,12 +116,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#ifdef HAVE_SIGNAL_H #ifdef HAVE_SIGNAL_H
#include <signal.h> #include <signal.h>
#endif #endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
#ifdef HAVE_SYS_MMAN_H #ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
@ -131,12 +125,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#ifdef HAVE_SYS_FILE_H #ifdef HAVE_SYS_FILE_H
#include <sys/file.h> #include <sys/file.h>
#endif #endif
#ifdef TOR_UNIT_TESTS
#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H)
/* as fallback implementation for tor_sleep_msec */
#include <sys/select.h>
#endif
#endif
#include "torlog.h" #include "torlog.h"
#include "util.h" #include "util.h"
@ -2836,53 +2824,6 @@ compute_num_cpus(void)
return num_cpus; return num_cpus;
} }
/** Set *timeval to the current time of day. On error, log and terminate.
* (Same as gettimeofday(timeval,NULL), but never returns -1.)
*/
void
tor_gettimeofday(struct timeval *timeval)
{
#ifdef _WIN32
/* Epoch bias copied from perl: number of units between windows epoch and
* Unix epoch. */
#define EPOCH_BIAS U64_LITERAL(116444736000000000)
#define UNITS_PER_SEC U64_LITERAL(10000000)
#define USEC_PER_SEC U64_LITERAL(1000000)
#define UNITS_PER_USEC U64_LITERAL(10)
union {
uint64_t ft_64;
FILETIME ft_ft;
} ft;
/* number of 100-nsec units since Jan 1, 1601 */
GetSystemTimeAsFileTime(&ft.ft_ft);
if (ft.ft_64 < EPOCH_BIAS) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"System time is before 1970; failing.");
exit(1);
/* LCOV_EXCL_STOP */
}
ft.ft_64 -= EPOCH_BIAS;
timeval->tv_sec = (unsigned) (ft.ft_64 / UNITS_PER_SEC);
timeval->tv_usec = (unsigned) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC);
#elif defined(HAVE_GETTIMEOFDAY)
if (gettimeofday(timeval, NULL)) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"gettimeofday failed.");
/* If gettimeofday dies, we have either given a bad timezone (we didn't),
or segfaulted.*/
exit(1);
/* LCOV_EXCL_STOP */
}
#elif defined(HAVE_FTIME)
struct timeb tb;
ftime(&tb);
timeval->tv_sec = tb.time;
timeval->tv_usec = tb.millitm * 1000;
#else
#error "No way to get time."
#endif
return;
}
#if !defined(_WIN32) #if !defined(_WIN32)
/** Defined iff we need to add locks when defining fake versions of reentrant /** Defined iff we need to add locks when defining fake versions of reentrant
@ -3441,26 +3382,6 @@ get_total_system_memory(size_t *mem_out)
return 0; return 0;
} }
#ifdef TOR_UNIT_TESTS
/** Delay for <b>msec</b> milliseconds. Only used in tests. */
void
tor_sleep_msec(int msec)
{
#ifdef _WIN32
Sleep(msec);
#elif defined(HAVE_USLEEP)
sleep(msec / 1000);
/* Some usleep()s hate sleeping more than 1 sec */
usleep((msec % 1000) * 1000);
#elif defined(HAVE_SYS_SELECT_H)
struct timeval tv = { msec / 1000, (msec % 1000) * 1000};
select(0, NULL, NULL, NULL, &tv);
#else
sleep(CEIL_DIV(msec, 1000));
#endif
}
#endif
/** Emit the password prompt <b>prompt</b>, then read up to <b>buflen</b> /** Emit the password prompt <b>prompt</b>, then read up to <b>buflen</b>
* bytes of passphrase into <b>output</b>. Return the number of bytes in * bytes of passphrase into <b>output</b>. Return the number of bytes in
* the passphrase, excluding terminating NUL. * the passphrase, excluding terminating NUL.

View File

@ -42,6 +42,8 @@
#include <netinet6/in6.h> #include <netinet6/in6.h>
#endif #endif
#include "compat_time.h"
#if defined(__has_feature) #if defined(__has_feature)
# if __has_feature(address_sanitizer) # if __has_feature(address_sanitizer)
/* Some of the fancy glibc strcmp() macros include references to memory that /* Some of the fancy glibc strcmp() macros include references to memory that
@ -379,15 +381,6 @@ const char *tor_fix_source_file(const char *fname);
#endif #endif
/* ===== Time compatibility */ /* ===== Time compatibility */
#if !defined(HAVE_GETTIMEOFDAY) && !defined(HAVE_STRUCT_TIMEVAL_TV_SEC)
/** Implementation of timeval for platforms that don't have it. */
struct timeval {
time_t tv_sec;
unsigned int tv_usec;
};
#endif
void tor_gettimeofday(struct timeval *timeval);
struct tm *tor_localtime_r(const time_t *timep, struct tm *result); struct tm *tor_localtime_r(const time_t *timep, struct tm *result);
struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
@ -737,10 +730,6 @@ char *format_win32_error(DWORD err);
#endif #endif
#ifdef TOR_UNIT_TESTS
void tor_sleep_msec(int msec);
#endif
#ifdef COMPAT_PRIVATE #ifdef COMPAT_PRIVATE
#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS) #if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
#define NEED_ERSATZ_SOCKETPAIR #define NEED_ERSATZ_SOCKETPAIR

View File

@ -388,33 +388,3 @@ tor_gettimeofday_cache_set(const struct timeval *tv)
#endif #endif
#endif #endif
/**
* As tor_gettimeofday_cached, but can never move backwards in time.
*
* The returned value may diverge from wall-clock time, since wall-clock time
* can trivially be adjusted backwards, and this can't. Don't mix wall-clock
* time with these values in the same calculation.
*
* Depending on implementation, this function may or may not "smooth out" huge
* jumps forward in wall-clock time. It may or may not keep its results
* advancing forward (as opposed to stalling) if the wall-clock time goes
* backwards. The current implementation does neither of of these.
*
* This function is not thread-safe; do not call it outside the main thread.
*
* In future versions of Tor, this may return a time does not have its
* origin at the Unix epoch.
*/
void
tor_gettimeofday_cached_monotonic(struct timeval *tv)
{
struct timeval last_tv = { 0, 0 };
tor_gettimeofday_cached(tv);
if (timercmp(tv, &last_tv, OP_LT)) {
memcpy(tv, &last_tv, sizeof(struct timeval));
} else {
memcpy(&last_tv, tv, sizeof(struct timeval));
}
}

View File

@ -70,7 +70,6 @@ void tor_gettimeofday_cache_clear(void);
#ifdef TOR_UNIT_TESTS #ifdef TOR_UNIT_TESTS
void tor_gettimeofday_cache_set(const struct timeval *tv); void tor_gettimeofday_cache_set(const struct timeval *tv);
#endif #endif
void tor_gettimeofday_cached_monotonic(struct timeval *tv);
#ifdef COMPAT_LIBEVENT_PRIVATE #ifdef COMPAT_LIBEVENT_PRIVATE

513
src/common/compat_time.c Normal file
View File

@ -0,0 +1,513 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file compat_time.c
* \brief Portable wrappers for finding out the current time, running
* timers, etc.
**/
#define COMPAT_TIME_PRIVATE
#include "compat.h"
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef TOR_UNIT_TESTS
#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H)
/* as fallback implementation for tor_sleep_msec */
#include <sys/select.h>
#endif
#endif
#ifdef __APPLE__
#include <mach/mach_time.h>
#endif
#include "torlog.h"
#include "util.h"
#include "container.h"
#ifndef HAVE_GETTIMEOFDAY
#ifdef HAVE_FTIME
#include <sys/timeb.h>
#endif
#endif
#ifdef TOR_UNIT_TESTS
/** Delay for <b>msec</b> milliseconds. Only used in tests. */
void
tor_sleep_msec(int msec)
{
#ifdef _WIN32
Sleep(msec);
#elif defined(HAVE_USLEEP)
sleep(msec / 1000);
/* Some usleep()s hate sleeping more than 1 sec */
usleep((msec % 1000) * 1000);
#elif defined(HAVE_SYS_SELECT_H)
struct timeval tv = { msec / 1000, (msec % 1000) * 1000};
select(0, NULL, NULL, NULL, &tv);
#else
sleep(CEIL_DIV(msec, 1000));
#endif
}
#endif
/** Set *timeval to the current time of day. On error, log and terminate.
* (Same as gettimeofday(timeval,NULL), but never returns -1.)
*/
void
tor_gettimeofday(struct timeval *timeval)
{
#ifdef _WIN32
/* Epoch bias copied from perl: number of units between windows epoch and
* Unix epoch. */
#define EPOCH_BIAS U64_LITERAL(116444736000000000)
#define UNITS_PER_SEC U64_LITERAL(10000000)
#define USEC_PER_SEC U64_LITERAL(1000000)
#define UNITS_PER_USEC U64_LITERAL(10)
union {
uint64_t ft_64;
FILETIME ft_ft;
} ft;
/* number of 100-nsec units since Jan 1, 1601 */
GetSystemTimeAsFileTime(&ft.ft_ft);
if (ft.ft_64 < EPOCH_BIAS) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"System time is before 1970; failing.");
exit(1);
/* LCOV_EXCL_STOP */
}
ft.ft_64 -= EPOCH_BIAS;
timeval->tv_sec = (unsigned) (ft.ft_64 / UNITS_PER_SEC);
timeval->tv_usec = (unsigned) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC);
#elif defined(HAVE_GETTIMEOFDAY)
if (gettimeofday(timeval, NULL)) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"gettimeofday failed.");
/* If gettimeofday dies, we have either given a bad timezone (we didn't),
or segfaulted.*/
exit(1);
/* LCOV_EXCL_STOP */
}
#elif defined(HAVE_FTIME)
struct timeb tb;
ftime(&tb);
timeval->tv_sec = tb.time;
timeval->tv_usec = tb.millitm * 1000;
#else
#error "No way to get time."
#endif
return;
}
#define ONE_MILLION ((int64_t) (1000 * 1000))
#define ONE_BILLION ((int64_t) (1000 * 1000 * 1000))
/** True iff monotime_init has been called. */
static int monotime_initialized = 0;
/* "ratchet" functions for monotonic time. */
#if defined(_WIN32) || defined(TOR_UNIT_TESTS)
/** Protected by lock: last value returned by monotime_get(). */
static int64_t last_pctr = 0;
/** Protected by lock: offset we must add to monotonic time values. */
static int64_t pctr_offset = 0;
/* If we are using GetTickCount(), how many times has it rolled over? */
static uint32_t rollover_count = 0;
/* If we are using GetTickCount(), what's the last value it returned? */
static int64_t last_tick_count = 0;
/** Helper for windows: Called with a sequence of times that are supposed
* to be monotonic; increments them as appropriate so that they actually
* _are_ monotonic.
*
* Caller must hold lock. */
STATIC int64_t
ratchet_performance_counter(int64_t count_raw)
{
/* must hold lock */
const int64_t count_adjusted = count_raw + pctr_offset;
if (PREDICT_UNLIKELY(count_adjusted < last_pctr)) {
/* Monotonicity failed! Pretend no time elapsed. */
pctr_offset = last_pctr - count_raw;
return last_pctr;
} else {
last_pctr = count_adjusted;
return count_adjusted;
}
}
STATIC int64_t
ratchet_coarse_performance_counter(const int64_t count_raw)
{
int64_t count = count_raw + (((int64_t)rollover_count) << 32);
while (PREDICT_UNLIKELY(count < last_tick_count)) {
++rollover_count;
count = count_raw + (((int64_t)rollover_count) << 32);
}
last_tick_count = count;
return count;
}
#endif
#if defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS)
static struct timeval last_timeofday = { 0, 0 };
static struct timeval timeofday_offset = { 0, 0 };
/** Helper for gettimeofday(): Called with a sequence of times that are
* supposed to be monotonic; increments them as appropriate so that they
* actually _are_ monotonic.
*
* Caller must hold lock. */
STATIC void
ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out)
{
/* must hold lock */
timeradd(timeval_raw, &timeofday_offset, out);
if (PREDICT_UNLIKELY(timercmp(out, &last_timeofday, <))) {
/* time ran backwards. Instead, declare that no time occurred. */
timersub(&last_timeofday, timeval_raw, &timeofday_offset);
memcpy(out, &last_timeofday, sizeof(struct timeval));
} else {
memcpy(&last_timeofday, out, sizeof(struct timeval));
}
}
#endif
#ifdef TOR_UNIT_TESTS
/** For testing: reset all the ratchets */
void
monotime_reset_ratchets_for_testing(void)
{
last_pctr = pctr_offset = last_tick_count = 0;
rollover_count = 0;
memset(&last_timeofday, 0, sizeof(struct timeval));
memset(&timeofday_offset, 0, sizeof(struct timeval));
}
#endif
#ifdef __APPLE__
/** Initialized on startup: tells is how to convert from ticks to
* nanoseconds.
*/
static struct mach_timebase_info mach_time_info;
static void
monotime_init_internal(void)
{
tor_assert(!monotime_initialized);
int r = mach_timebase_info(&mach_time_info);
tor_assert(r == 0);
tor_assert(mach_time_info.denom != 0);
}
/**
* Set "out" to the most recent monotonic time value
*/
void
monotime_get(monotime_t *out)
{
out->abstime_ = mach_absolute_time();
}
/**
* Return the number of nanoseconds between <b>start</b> and <b>end</b>.
*/
int64_t
monotime_diff_nsec(const monotime_t *start,
const monotime_t *end)
{
if (BUG(mach_time_info.denom == 0)) {
monotime_init();
}
const int64_t diff_ticks = end->abstime_ - start->abstime_;
const int64_t diff_nsec =
(diff_ticks * mach_time_info.numer) / mach_time_info.denom;
return diff_nsec;
}
/* end of "__APPLE__" */
#elif defined(HAVE_CLOCK_GETTIME)
static void
monotime_init_internal(void)
{
/* no action needed. */
}
void
monotime_get(monotime_t *out)
{
int r = clock_gettime(CLOCK_MONOTONIC, &out->ts_);
tor_assert(r == 0);
}
#ifdef CLOCK_MONOTONIC_COARSE
void
monotime_coarse_get(monotime_coarse_t *out)
{
int r = clock_gettime(CLOCK_MONOTONIC_COARSE, &out->ts_);
tor_assert(r == 0);
}
#endif
int64_t
monotime_diff_nsec(const monotime_t *start,
const monotime_t *end)
{
const int64_t diff_sec = end->ts_.tv_sec - start->ts_.tv_sec;
const int64_t diff_nsec = diff_sec * ONE_BILLION +
(end->ts_.tv_nsec - start->ts_.tv_nsec);
return diff_nsec;
}
/* end of "HAVE_CLOCK_GETTIME" */
#elif defined (_WIN32)
/** Result of QueryPerformanceFrequency, as an int64_t. */
static int64_t ticks_per_second = 0;
/** Lock to protect last_pctr and pctr_offset */
static CRITICAL_SECTION monotime_lock;
/** Lock to protect rollover_count and last_tick_count */
static CRITICAL_SECTION monotime_coarse_lock;
typedef ULONGLONG (WINAPI *GetTickCount64_fn_t)(void);
static GetTickCount64_fn_t GetTickCount64_fn = NULL;
static void
monotime_init_internal(void)
{
tor_assert(!monotime_initialized);
BOOL ok = InitializeCriticalSectionAndSpinCount(&monotime_lock, 200);
tor_assert(ok);
ok = InitializeCriticalSectionAndSpinCount(&monotime_coarse_lock, 200);
tor_assert(ok);
LARGE_INTEGER li;
ok = QueryPerformanceFrequency(&li);
tor_assert(ok);
tor_assert(li.QuadPart);
ticks_per_second = li.QuadPart;
last_pctr = 0;
pctr_offset = 0;
HANDLE h = load_windows_system_library(TEXT("kernel32.dll"));
if (h) {
GetTickCount64_fn = (GetTickCount64_fn_t)
GetProcAddress(h, "GetTickCount64");
}
// FreeLibrary(h) ?
}
void
monotime_get(monotime_t *out)
{
if (BUG(monotime_initialized == 0)) {
monotime_init();
}
/* Alas, QueryPerformanceCounter is not always monotonic: see bug list at
https://www.python.org/dev/peps/pep-0418/#windows-queryperformancecounter
*/
EnterCriticalSection(&monotime_lock);
LARGE_INTEGER res;
BOOL ok = QueryPerformanceCounter(&res);
tor_assert(ok);
const int64_t count_raw = res.QuadPart;
out->pcount_ = ratchet_performance_counter(count_raw);
LeaveCriticalSection(&monotime_lock);
}
void
monotime_coarse_get(monotime_coarse_t *out)
{
if (GetTickCount64_fn) {
out->tick_count_ = (int64_t)GetTickCount64_fn();
} else {
EnterCriticalSection(&monotime_coarse_lock);
DWORD tick = GetTickCount();
out->tick_count_ = ratchet_coarse_performance_counter(tick);
LeaveCriticalSection(&monotime_coarse_lock);
}
}
int64_t
monotime_diff_nsec(const monotime_t *start,
const monotime_t *end)
{
if (BUG(monotime_initialized == 0)) {
monotime_init();
}
const int64_t diff_ticks = end->pcount_ - start->pcount_;
return (diff_ticks * ONE_BILLION) / ticks_per_second;
}
int64_t
monotime_coarse_diff_msec(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
const int64_t diff_ticks = end->tick_count_ - start->tick_count_;
return diff_ticks;
}
int64_t
monotime_coarse_diff_usec(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
return monotime_coarse_diff_msec(start, end) * 1000;
}
int64_t
monotime_coarse_diff_nsec(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
return monotime_coarse_diff_msec(start, end) * ONE_MILLION;
}
/* end of "_WIN32" */
#elif defined(MONOTIME_USING_GETTIMEOFDAY)
static tor_mutex_t monotime_lock;
/** Initialize the monotonic timer subsystem. */
static void
monotime_init_internal(void)
{
tor_assert(!monotime_initialized);
tor_mutex_init(&monotime_lock);
}
void
monotime_get(monotime_t *out)
{
if (BUG(monotime_initialized == 0)) {
monotime_init();
}
tor_mutex_acquire(&monotime_lock);
struct timeval timeval_raw;
tor_gettimeofday(&timeval_raw);
ratchet_timeval(&timeval_raw, &out->tv_);
tor_mutex_release(&monotime_lock);
}
int64_t
monotime_diff_nsec(const monotime_t *start,
const monotime_t *end)
{
struct timeval diff;
timersub(&end->tv_, &start->tv_, &diff);
return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000);
}
/* end of "MONOTIME_USING_GETTIMEOFDAY" */
#else
#error "No way to implement monotonic timers."
#endif
static monotime_t initialized_at;
#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
static monotime_coarse_t initialized_at_coarse;
#endif
/**
* Initialize the monotonic timer subsystem. Must be called before any
* monotonic timer functions. This function is idempotent.
*/
void
monotime_init(void)
{
if (!monotime_initialized) {
monotime_init_internal();
monotime_get(&initialized_at);
#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
monotime_coarse_get(&initialized_at_coarse);
#endif
monotime_initialized = 1;
}
}
int64_t
monotime_diff_usec(const monotime_t *start,
const monotime_t *end)
{
const int64_t nsec = monotime_diff_nsec(start, end);
return CEIL_DIV(nsec, 1000);
}
int64_t
monotime_diff_msec(const monotime_t *start,
const monotime_t *end)
{
const int64_t nsec = monotime_diff_nsec(start, end);
return CEIL_DIV(nsec, ONE_MILLION);
}
uint64_t
monotime_absolute_nsec(void)
{
monotime_t now;
if (BUG(monotime_initialized == 0)) {
monotime_init();
}
monotime_get(&now);
return monotime_diff_nsec(&initialized_at, &now);
}
uint64_t
monotime_absolute_usec(void)
{
return monotime_absolute_nsec() / 1000;
}
uint64_t
monotime_absolute_msec(void)
{
return monotime_absolute_nsec() / ONE_MILLION;
}
#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
uint64_t
monotime_coarse_absolute_nsec(void)
{
if (BUG(monotime_initialized == 0)) {
monotime_init();
}
monotime_coarse_t now;
monotime_coarse_get(&now);
return monotime_coarse_diff_nsec(&initialized_at_coarse, &now);
}
uint64_t
monotime_coarse_absolute_usec(void)
{
return monotime_coarse_absolute_nsec() / 1000;
}
uint64_t
monotime_coarse_absolute_msec(void)
{
return monotime_coarse_absolute_nsec() / ONE_MILLION;
}
#endif

150
src/common/compat_time.h Normal file
View File

@ -0,0 +1,150 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file compat_time.h
*
* \brief Functions and types for monotonic times.
*
* monotime_* functions try to provide a high-resolution monotonic timer with
* something the best resolution the system provides. monotime_coarse_*
* functions run faster (if the operating system gives us a way to do that)
* but produce a less accurate timer: accuracy will probably be on the order
* of tens of milliseconds.
*/
#ifndef TOR_COMPAT_TIME_H
#define TOR_COMPAT_TIME_H
#include "orconfig.h"
#if defined(HAVE_CLOCK_GETTIME)
/* to ensure definition of CLOCK_MONOTONIC_COARSE if it's there */
#include <time.h>
#endif
#if !defined(HAVE_GETTIMEOFDAY) && !defined(HAVE_STRUCT_TIMEVAL_TV_SEC)
/** Implementation of timeval for platforms that don't have it. */
struct timeval {
time_t tv_sec;
unsigned int tv_usec;
};
#endif
/** Represents a monotonic timer in a platform-dependent way. */
typedef struct monotime_t {
#ifdef __APPLE__
/* On apple, there is a 64-bit counter whose precision we must look up. */
uint64_t abstime_;
#elif defined(HAVE_CLOCK_GETTIME)
/* It sure would be nice to use clock_gettime(). Posix is a nice thing. */
struct timespec ts_;
#elif defined (_WIN32)
/* On Windows, there is a 64-bit counter whose precision we must look up. */
int64_t pcount_;
#else
#define MONOTIME_USING_GETTIMEOFDAY
/* Otherwise, we will be stuck using gettimeofday. */
struct timeval tv_;
#endif
} monotime_t;
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC_COARSE)
#define MONOTIME_COARSE_FN_IS_DIFFERENT
#define monotime_coarse_t monotime_t
#elif defined(_WIN32)
#define MONOTIME_COARSE_FN_IS_DIFFERENT
#define MONOTIME_COARSE_TYPE_IS_DIFFERENT
/** Represents a coarse monotonic time in a platform-independent way. */
typedef struct monotime_coarse_t {
uint64_t tick_count_;
} monotime_coarse_t;
#else
#define monotime_coarse_t monotime_t
#endif
/**
* Initialize the timing subsystem. This function is idempotent.
*/
void monotime_init(void);
/**
* Set <b>out</b> to the current time.
*/
void monotime_get(monotime_t *out);
/**
* Return the number of nanoseconds between <b>start</b> and <b>end</b>.
*/
int64_t monotime_diff_nsec(const monotime_t *start, const monotime_t *end);
/**
* Return the number of microseconds between <b>start</b> and <b>end</b>.
*/
int64_t monotime_diff_usec(const monotime_t *start, const monotime_t *end);
/**
* Return the number of milliseconds between <b>start</b> and <b>end</b>.
*/
int64_t monotime_diff_msec(const monotime_t *start, const monotime_t *end);
/**
* Return the number of nanoseconds since the timer system was initialized.
*/
uint64_t monotime_absolute_nsec(void);
/**
* Return the number of microseconds since the timer system was initialized.
*/
uint64_t monotime_absolute_usec(void);
/**
* Return the number of milliseconds since the timer system was initialized.
*/
uint64_t monotime_absolute_msec(void);
#if defined(MONOTIME_COARSE_FN_IS_DIFFERENT)
/**
* Set <b>out</b> to the current coarse time.
*/
void monotime_coarse_get(monotime_coarse_t *out);
uint64_t monotime_coarse_absolute_nsec(void);
uint64_t monotime_coarse_absolute_usec(void);
uint64_t monotime_coarse_absolute_msec(void);
#else
#define monotime_coarse_get monotime_get
#define monotime_coarse_absolute_nsec monotime_absolute_nsec
#define monotime_coarse_absolute_usec monotime_absolute_usec
#define monotime_coarse_absolute_msec monotime_absolute_msec
#endif
#if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)
int64_t monotime_coarse_diff_nsec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
int64_t monotime_coarse_diff_msec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
#else
#define monotime_coarse_diff_nsec monotime_diff_nsec
#define monotime_coarse_diff_usec monotime_diff_usec
#define monotime_coarse_diff_msec monotime_diff_msec
#endif
void tor_gettimeofday(struct timeval *timeval);
#ifdef TOR_UNIT_TESTS
void tor_sleep_msec(int msec);
#endif
#ifdef COMPAT_TIME_PRIVATE
#if defined(_WIN32) || defined(TOR_UNIT_TESTS)
STATIC int64_t ratchet_performance_counter(int64_t count_raw);
STATIC int64_t ratchet_coarse_performance_counter(int64_t count_raw);
#endif
#if defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS)
STATIC void ratchet_timeval(const struct timeval *timeval_raw,
struct timeval *out);
#endif
#ifdef TOR_UNIT_TESTS
void monotime_reset_ratchets_for_testing(void);
#endif
#endif
#endif

View File

@ -83,6 +83,7 @@ LIBOR_A_SRC = \
src/common/backtrace.c \ src/common/backtrace.c \
src/common/compat.c \ src/common/compat.c \
src/common/compat_threads.c \ src/common/compat_threads.c \
src/common/compat_time.c \
src/common/container.c \ src/common/container.c \
src/common/log.c \ src/common/log.c \
src/common/memarea.c \ src/common/memarea.c \

View File

@ -18,9 +18,6 @@
*/ */
/* Notes: /* Notes:
*
* The use of tor_gettimeofday_cached_monotonic() is kind of ugly. It would
* be neat to fix it.
* *
* Having a way to free all timers on shutdown would free people from the * Having a way to free all timers on shutdown would free people from the
* need to track them. Not sure if that's clever though. * need to track them. Not sure if that's clever though.
@ -72,6 +69,8 @@ struct timeout_cb {
static struct timeouts *global_timeouts = NULL; static struct timeouts *global_timeouts = NULL;
static struct event *global_timer_event = NULL; static struct event *global_timer_event = NULL;
static monotime_t start_of_time;
/** We need to choose this value carefully. Because we're using timer wheels, /** We need to choose this value carefully. Because we're using timer wheels,
* it actually costs us to have extra resolution we don't use. So for now, * it actually costs us to have extra resolution we don't use. So for now,
* I'm going to define our resolution as .1 msec, and hope that's good enough. * I'm going to define our resolution as .1 msec, and hope that's good enough.
@ -95,9 +94,8 @@ static struct event *global_timer_event = NULL;
/** /**
* Convert the timeval in <b>tv</b> to a timeout_t, and return it. * Convert the timeval in <b>tv</b> to a timeout_t, and return it.
* *
* The output resolution is set by USEC_PER_TICK, and the time corresponding * The output resolution is set by USEC_PER_TICK. Only use this to convert
* to 0 is the same as the time corresponding to 0 from * delays to number of ticks; the time represented by 0 is undefined.
* tor_gettimeofday_cached_monotonic().
*/ */
static timeout_t static timeout_t
tv_to_timeout(const struct timeval *tv) tv_to_timeout(const struct timeval *tv)
@ -108,7 +106,8 @@ tv_to_timeout(const struct timeval *tv)
} }
/** /**
* Convert the timeout in <b>t</b> to a timeval in <b>tv_out</b> * Convert the timeout in <b>t</b> to a timeval in <b>tv_out</b>. Only
* use this for delays, not absolute times.
*/ */
static void static void
timeout_to_tv(timeout_t t, struct timeval *tv_out) timeout_to_tv(timeout_t t, struct timeval *tv_out)
@ -122,12 +121,10 @@ timeout_to_tv(timeout_t t, struct timeval *tv_out)
* Update the timer <b>tv</b> to the current time in <b>tv</b>. * Update the timer <b>tv</b> to the current time in <b>tv</b>.
*/ */
static void static void
timer_advance_to_cur_time(const struct timeval *tv) timer_advance_to_cur_time(const monotime_t *now)
{ {
timeout_t cur_tick = tv_to_timeout(tv); timeout_t cur_tick = CEIL_DIV(monotime_diff_usec(&start_of_time, now),
if (BUG(cur_tick < timeouts_get_curtime(global_timeouts))) { USEC_PER_TICK);
cur_tick = timeouts_get_curtime(global_timeouts); // LCOV_EXCL_LINE
}
timeouts_update(global_timeouts, cur_tick); timeouts_update(global_timeouts, cur_tick);
} }
@ -138,11 +135,12 @@ timer_advance_to_cur_time(const struct timeval *tv)
static void static void
libevent_timer_reschedule(void) libevent_timer_reschedule(void)
{ {
struct timeval now; monotime_t now;
tor_gettimeofday_cached_monotonic(&now); monotime_get(&now);
timer_advance_to_cur_time(&now); timer_advance_to_cur_time(&now);
timeout_t delay = timeouts_timeout(global_timeouts); timeout_t delay = timeouts_timeout(global_timeouts);
struct timeval d; struct timeval d;
if (delay > MIN_CHECK_TICKS) if (delay > MIN_CHECK_TICKS)
delay = MIN_CHECK_TICKS; delay = MIN_CHECK_TICKS;
@ -161,9 +159,8 @@ libevent_timer_callback(evutil_socket_t fd, short what, void *arg)
(void)what; (void)what;
(void)arg; (void)arg;
struct timeval now; monotime_t now;
tor_gettimeofday_cache_clear(); monotime_get(&now);
tor_gettimeofday_cached_monotonic(&now);
timer_advance_to_cur_time(&now); timer_advance_to_cur_time(&now);
tor_timer_t *t; tor_timer_t *t;
@ -171,7 +168,6 @@ libevent_timer_callback(evutil_socket_t fd, short what, void *arg)
t->callback.cb(t, t->callback.arg, &now); t->callback.cb(t, t->callback.arg, &now);
} }
tor_gettimeofday_cache_clear();
libevent_timer_reschedule(); libevent_timer_reschedule();
} }
@ -194,6 +190,9 @@ timers_initialize(void)
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
monotime_init();
monotime_get(&start_of_time);
struct event *timer_event; struct event *timer_event;
timer_event = tor_event_new(tor_libevent_get_base(), timer_event = tor_event_new(tor_libevent_get_base(),
-1, 0, libevent_timer_callback, NULL); -1, 0, libevent_timer_callback, NULL);
@ -256,24 +255,25 @@ timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg)
} }
/** /**
* Schedule the timer t to fire at the current time plus a delay of <b>tv</b>. * Schedule the timer t to fire at the current time plus a delay of
* All times are relative to tor_gettimeofday_cached_monotonic. * <b>delay</b> microseconds. All times are relative to monotime_get().
*/ */
void void
timer_schedule(tor_timer_t *t, const struct timeval *tv) timer_schedule(tor_timer_t *t, const struct timeval *tv)
{ {
const timeout_t when = tv_to_timeout(tv); const timeout_t delay = tv_to_timeout(tv);
struct timeval now;
tor_gettimeofday_cached_monotonic(&now); monotime_t now;
monotime_get(&now);
timer_advance_to_cur_time(&now); timer_advance_to_cur_time(&now);
/* Take the old timeout value. */ /* Take the old timeout value. */
timeout_t to = timeouts_timeout(global_timeouts); timeout_t to = timeouts_timeout(global_timeouts);
timeouts_add(global_timeouts, t, when); timeouts_add(global_timeouts, t, delay);
/* Should we update the libevent timer? */ /* Should we update the libevent timer? */
if (to <= when) { if (to <= delay) {
return; /* we're already going to fire before this timer would trigger. */ return; /* we're already going to fire before this timer would trigger. */
} }
libevent_timer_reschedule(); libevent_timer_reschedule();

View File

@ -7,8 +7,9 @@
#include "orconfig.h" #include "orconfig.h"
#include "testsupport.h" #include "testsupport.h"
struct monotime_t;
typedef struct timeout tor_timer_t; typedef struct timeout tor_timer_t;
typedef void (*timer_cb_fn_t)(tor_timer_t *, void *, const struct timeval *); typedef void (*timer_cb_fn_t)(tor_timer_t *, void *, const struct monotime_t *);
tor_timer_t *timer_new(timer_cb_fn_t cb, void *arg); tor_timer_t *timer_new(timer_cb_fn_t cb, void *arg);
void timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg); void timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg);
void timer_schedule(tor_timer_t *t, const struct timeval *delay); void timer_schedule(tor_timer_t *t, const struct timeval *delay);

View File

@ -405,7 +405,7 @@ static chunk_t *
buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
{ {
chunk_t *chunk; chunk_t *chunk;
struct timeval now;
if (CHUNK_ALLOC_SIZE(capacity) < buf->default_chunk_size) { if (CHUNK_ALLOC_SIZE(capacity) < buf->default_chunk_size) {
chunk = chunk_new_with_alloc_size(buf->default_chunk_size); chunk = chunk_new_with_alloc_size(buf->default_chunk_size);
} else if (capped && CHUNK_ALLOC_SIZE(capacity) > MAX_CHUNK_ALLOC) { } else if (capped && CHUNK_ALLOC_SIZE(capacity) > MAX_CHUNK_ALLOC) {
@ -414,8 +414,7 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
chunk = chunk_new_with_alloc_size(preferred_chunk_size(capacity)); chunk = chunk_new_with_alloc_size(preferred_chunk_size(capacity));
} }
tor_gettimeofday_cached_monotonic(&now); chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec();
chunk->inserted_time = (uint32_t)tv_to_msec(&now);
if (buf->tail) { if (buf->tail) {
tor_assert(buf->head); tor_assert(buf->head);
@ -430,8 +429,8 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
} }
/** Return the age of the oldest chunk in the buffer <b>buf</b>, in /** Return the age of the oldest chunk in the buffer <b>buf</b>, in
* milliseconds. Requires the current time, in truncated milliseconds since * milliseconds. Requires the current monotonic time, in truncated msec,
* the epoch, as its input <b>now</b>. * as its input <b>now</b>.
*/ */
uint32_t uint32_t
buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now) buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now)

View File

@ -2015,7 +2015,7 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
/** Return the age in milliseconds of the oldest buffer chunk on <b>conn</b>, /** Return the age in milliseconds of the oldest buffer chunk on <b>conn</b>,
* where age is taken in milliseconds before the time <b>now</b> (in truncated * where age is taken in milliseconds before the time <b>now</b> (in truncated
* milliseconds since the epoch). If the connection has no data, treat * absolute monotonic msec). If the connection has no data, treat
* it as having age zero. * it as having age zero.
**/ **/
static uint32_t static uint32_t
@ -2138,7 +2138,6 @@ circuits_handle_oom(size_t current_allocation)
size_t mem_recovered=0; size_t mem_recovered=0;
int n_circuits_killed=0; int n_circuits_killed=0;
int n_dirconns_killed=0; int n_dirconns_killed=0;
struct timeval now;
uint32_t now_ms; uint32_t now_ms;
log_notice(LD_GENERAL, "We're low on memory. Killing circuits with " log_notice(LD_GENERAL, "We're low on memory. Killing circuits with "
"over-long queues. (This behavior is controlled by " "over-long queues. (This behavior is controlled by "
@ -2152,8 +2151,7 @@ circuits_handle_oom(size_t current_allocation)
mem_to_recover = current_allocation - mem_target; mem_to_recover = current_allocation - mem_target;
} }
tor_gettimeofday_cached_monotonic(&now); now_ms = (uint32_t)monotime_coarse_absolute_msec();
now_ms = (uint32_t)tv_to_msec(&now);
circlist = circuit_get_global_list(); circlist = circuit_get_global_list();
SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) { SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) {

View File

@ -3646,6 +3646,8 @@ tor_main(int argc, char *argv[])
#endif #endif
} }
monotime_init();
switch (get_options()->command) { switch (get_options()->command) {
case CMD_RUN_TOR: case CMD_RUN_TOR:
#ifdef NT_SERVICE #ifdef NT_SERVICE

View File

@ -2320,14 +2320,12 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
int exitward, const cell_t *cell, int exitward, const cell_t *cell,
int wide_circ_ids, int use_stats) int wide_circ_ids, int use_stats)
{ {
struct timeval now;
packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids); packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids);
(void)circ; (void)circ;
(void)exitward; (void)exitward;
(void)use_stats; (void)use_stats;
tor_gettimeofday_cached_monotonic(&now);
copy->inserted_time = (uint32_t)tv_to_msec(&now); copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec();
cell_queue_append(queue, copy); cell_queue_append(queue, copy);
} }

View File

@ -28,15 +28,26 @@ static tor_timer_t *timers[N_TIMERS] = {NULL};
static int n_active_timers = 0; static int n_active_timers = 0;
static int n_fired = 0; static int n_fired = 0;
static monotime_t started_at;
static int64_t delay_usec[N_TIMERS];
static int64_t diffs_mono_usec[N_TIMERS];
static void static void
timer_cb(tor_timer_t *t, void *arg, const struct timeval *now) timer_cb(tor_timer_t *t, void *arg, const monotime_t *now_mono)
{ {
struct timeval now;
tor_gettimeofday(&now);
tor_timer_t **t_ptr = arg; tor_timer_t **t_ptr = arg;
tor_assert(*t_ptr == t); tor_assert(*t_ptr == t);
int idx = (int) (t_ptr - timers); int idx = (int) (t_ptr - timers);
++fired[idx]; ++fired[idx];
timersub(now, &fire_at[idx], &difference[idx]); timersub(&now, &fire_at[idx], &difference[idx]);
diffs_mono_usec[idx] =
monotime_diff_usec(&started_at, now_mono) -
delay_usec[idx];
++n_fired; ++n_fired;
// printf("%d / %d\n",n_fired, N_TIMERS); // printf("%d / %d\n",n_fired, N_TIMERS);
if (n_fired == n_active_timers) { if (n_fired == n_active_timers) {
event_base_loopbreak(tor_libevent_get_base()); event_base_loopbreak(tor_libevent_get_base());
@ -57,10 +68,12 @@ main(int argc, char **argv)
int ret; int ret;
struct timeval now; struct timeval now;
tor_gettimeofday(&now); tor_gettimeofday(&now);
monotime_get(&started_at);
for (i = 0; i < N_TIMERS; ++i) { for (i = 0; i < N_TIMERS; ++i) {
struct timeval delay; struct timeval delay;
delay.tv_sec = crypto_rand_int_range(0,MAX_DURATION); delay.tv_sec = crypto_rand_int_range(0,MAX_DURATION);
delay.tv_usec = crypto_rand_int_range(0,1000000); delay.tv_usec = crypto_rand_int_range(0,1000000);
delay_usec[i] = delay.tv_sec * 1000000 + delay.tv_usec;
timeradd(&now, &delay, &fire_at[i]); timeradd(&now, &delay, &fire_at[i]);
timers[i] = timer_new(timer_cb, &timers[i]); timers[i] = timer_new(timer_cb, &timers[i]);
timer_schedule(timers[i], &delay); timer_schedule(timers[i], &delay);
@ -88,7 +101,8 @@ main(int argc, char **argv)
continue; continue;
} }
tor_assert(fired[i] == 1); tor_assert(fired[i] == 1);
int64_t diff = difference[i].tv_usec + difference[i].tv_sec * 1000000; //int64_t diff = difference[i].tv_usec + difference[i].tv_sec * 1000000;
int64_t diff = diffs_mono_usec[i];
total_difference += diff; total_difference += diff;
total_square_difference += diff*diff; total_square_difference += diff*diff;
} }

View File

@ -5,6 +5,7 @@
#include "orconfig.h" #include "orconfig.h"
#define COMPAT_PRIVATE #define COMPAT_PRIVATE
#define COMPAT_TIME_PRIVATE
#define CONTROL_PRIVATE #define CONTROL_PRIVATE
#define UTIL_PRIVATE #define UTIL_PRIVATE
#include "or.h" #include "or.h"
@ -5285,6 +5286,128 @@ test_util_calloc_check(void *arg)
; ;
} }
static void
test_util_monotonic_time(void *arg)
{
(void)arg;
monotime_t mt1, mt2;
monotime_coarse_t mtc1, mtc2;
uint64_t nsec1, nsec2, usec1, msec1;
uint64_t nsecc1, nsecc2, usecc1, msecc1;
monotime_get(&mt1);
monotime_coarse_get(&mtc1);
nsec1 = monotime_absolute_nsec();
usec1 = monotime_absolute_usec();
msec1 = monotime_absolute_msec();
nsecc1 = monotime_coarse_absolute_nsec();
usecc1 = monotime_coarse_absolute_usec();
msecc1 = monotime_coarse_absolute_msec();
tor_sleep_msec(200);
monotime_get(&mt2);
monotime_coarse_get(&mtc2);
nsec2 = monotime_absolute_nsec();
nsecc2 = monotime_coarse_absolute_nsec();
/* We need to be a little careful here since we don't know the system load.
*/
tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_GE, 175);
tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_LT, 1000);
tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_GE, 125);
tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_LT, 1000);
tt_u64_op(nsec2-nsec1, OP_GE, 175000000);
tt_u64_op(nsec2-nsec1, OP_LT, 1000000000);
tt_u64_op(nsecc2-nsecc1, OP_GE, 125000000);
tt_u64_op(nsecc2-nsecc1, OP_LT, 1000000000);
tt_u64_op(msec1, OP_GE, nsec1 / 1000000);
tt_u64_op(usec1, OP_GE, nsec1 / 1000);
tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000);
tt_u64_op(usecc1, OP_GE, nsecc1 / 1000);
tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1);
tt_u64_op(usec1, OP_LE, nsec1 / 1000 +10);
tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1);
tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 10);
done:
;
}
static void
test_util_monotonic_time_ratchet(void *arg)
{
(void)arg;
monotime_reset_ratchets_for_testing();
/* win32, performance counter ratchet. */
tt_i64_op(100, OP_EQ, ratchet_performance_counter(100));
tt_i64_op(101, OP_EQ, ratchet_performance_counter(101));
tt_i64_op(2000, OP_EQ, ratchet_performance_counter(2000));
tt_i64_op(2000, OP_EQ, ratchet_performance_counter(100));
tt_i64_op(2005, OP_EQ, ratchet_performance_counter(105));
tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1105));
tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1000));
tt_i64_op(3010, OP_EQ, ratchet_performance_counter(1005));
/* win32, GetTickCounts32 ratchet-and-rollover-detector. */
const int64_t R = ((int64_t)1) << 32;
tt_i64_op(5, OP_EQ, ratchet_coarse_performance_counter(5));
tt_i64_op(1000, OP_EQ, ratchet_coarse_performance_counter(1000));
tt_i64_op(5+R, OP_EQ, ratchet_coarse_performance_counter(5));
tt_i64_op(10+R, OP_EQ, ratchet_coarse_performance_counter(10));
tt_i64_op(4+R*2, OP_EQ, ratchet_coarse_performance_counter(4));
/* gettimeofday regular ratchet. */
struct timeval tv_in = {0,0}, tv_out;
tv_in.tv_usec = 9000;
ratchet_timeval(&tv_in, &tv_out);
tt_int_op(tv_out.tv_usec, OP_EQ, 9000);
tt_i64_op(tv_out.tv_sec, OP_EQ, 0);
tv_in.tv_sec = 1337;
tv_in.tv_usec = 0;
ratchet_timeval(&tv_in, &tv_out);
tt_int_op(tv_out.tv_usec, OP_EQ, 0);
tt_i64_op(tv_out.tv_sec, OP_EQ, 1337);
tv_in.tv_sec = 1336;
tv_in.tv_usec = 500000;
ratchet_timeval(&tv_in, &tv_out);
tt_int_op(tv_out.tv_usec, OP_EQ, 0);
tt_i64_op(tv_out.tv_sec, OP_EQ, 1337);
tv_in.tv_sec = 1337;
tv_in.tv_usec = 0;
ratchet_timeval(&tv_in, &tv_out);
tt_int_op(tv_out.tv_usec, OP_EQ, 500000);
tt_i64_op(tv_out.tv_sec, OP_EQ, 1337);
tv_in.tv_sec = 1337;
tv_in.tv_usec = 600000;
ratchet_timeval(&tv_in, &tv_out);
tt_int_op(tv_out.tv_usec, OP_EQ, 100000);
tt_i64_op(tv_out.tv_sec, OP_EQ, 1338);
tv_in.tv_sec = 1000;
tv_in.tv_usec = 1000;
ratchet_timeval(&tv_in, &tv_out);
tt_int_op(tv_out.tv_usec, OP_EQ, 100000);
tt_i64_op(tv_out.tv_sec, OP_EQ, 1338);
tv_in.tv_sec = 2000;
tv_in.tv_usec = 2000;
ratchet_timeval(&tv_in, &tv_out);
tt_int_op(tv_out.tv_usec, OP_EQ, 101000);
tt_i64_op(tv_out.tv_sec, OP_EQ, 2338);
done:
;
}
#define UTIL_LEGACY(name) \ #define UTIL_LEGACY(name) \
{ #name, test_util_ ## name , 0, NULL, NULL } { #name, test_util_ ## name , 0, NULL, NULL }
@ -5373,6 +5496,8 @@ struct testcase_t util_tests[] = {
UTIL_TEST(touch_file, 0), UTIL_TEST(touch_file, 0),
UTIL_TEST_NO_WIN(pwdb, TT_FORK), UTIL_TEST_NO_WIN(pwdb, TT_FORK),
UTIL_TEST(calloc_check, 0), UTIL_TEST(calloc_check, 0),
UTIL_TEST(monotonic_time, 0),
UTIL_TEST(monotonic_time_ratchet, TT_FORK),
END_OF_TESTCASES END_OF_TESTCASES
}; };