/* Copyright 2016-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" #include #include #include #include "lib/evloop/compat_libevent.h" #include "lib/evloop/timers.h" #include "lib/crypt_ops/crypto_init.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/log/util_bug.h" #include "lib/time/compat_time.h" #include "lib/wallclock/timeval.h" #include "lib/subsys/subsys.h" #include "lib/evloop/evloop_sys.h" #define N_TIMERS 1000 #define MAX_DURATION 30 #define N_DISABLE 5 static struct timeval fire_at[N_TIMERS] = { {0,0} }; static int is_disabled[N_TIMERS] = {0}; static int fired[N_TIMERS] = {0}; static struct timeval difference[N_TIMERS] = { {0,0} }; static tor_timer_t *timers[N_TIMERS] = {NULL}; static int n_active_timers = 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 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_assert(*t_ptr == t); int idx = (int) (t_ptr - timers); ++fired[idx]; timersub(&now, &fire_at[idx], &difference[idx]); diffs_mono_usec[idx] = monotime_diff_usec(&started_at, now_mono) - delay_usec[idx]; ++n_fired; // printf("%d / %d\n",n_fired, N_TIMERS); if (n_fired == n_active_timers) { tor_libevent_exit_loop_after_callback(tor_libevent_get_base()); } } int main(int argc, char **argv) { (void)argc; (void)argv; if (sys_evloop.initialize()) { printf("Couldn't initialize evloop subsystem; exiting.\n"); return 1; } tor_libevent_cfg cfg; memset(&cfg, 0, sizeof(cfg)); tor_libevent_initialize(&cfg, 0); timers_initialize(); init_logging(1); if (crypto_global_init(0, NULL, NULL) < 0) return 1; int i; int ret; struct timeval now; tor_gettimeofday(&now); monotime_get(&started_at); for (i = 0; i < N_TIMERS; ++i) { struct timeval delay; delay.tv_sec = crypto_rand_int_range(0,MAX_DURATION); 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]); timers[i] = timer_new(timer_cb, &timers[i]); timer_schedule(timers[i], &delay); ++n_active_timers; } /* Disable some; we'll make sure they don't trigger. */ for (i = 0; i < N_DISABLE; ++i) { int idx = crypto_rand_int_range(0, N_TIMERS); if (is_disabled[idx]) continue; is_disabled[idx] = 1; timer_disable(timers[idx]); --n_active_timers; } tor_libevent_run_event_loop(tor_libevent_get_base(), 0); int64_t total_difference = 0; uint64_t total_square_difference = 0; tor_assert(n_fired == n_active_timers); for (i = 0; i < N_TIMERS; ++i) { if (is_disabled[i]) { tor_assert(fired[i] == 0); continue; } tor_assert(fired[i] == 1); //int64_t diff = difference[i].tv_usec + difference[i].tv_sec * 1000000; int64_t diff = diffs_mono_usec[i]; total_difference += diff; total_square_difference += diff*diff; } const int64_t mean_diff = total_difference / n_active_timers; printf("mean difference: %"PRId64" usec\n", (mean_diff)); const double mean_sq = ((double)total_square_difference)/ n_active_timers; const double sq_mean = mean_diff * mean_diff; const double stddev = sqrt(mean_sq - sq_mean); printf("standard deviation: %lf usec\n", stddev); #define MAX_DIFF_USEC (500*1000) #define MAX_STDDEV_USEC (500*1000) #define ODD_DIFF_USEC (2000) #define ODD_STDDEV_USEC (2000) if (mean_diff < 0 || mean_diff > MAX_DIFF_USEC || stddev > MAX_STDDEV_USEC) { printf("Either your system is under ridiculous load, or the " "timer backend is broken.\n"); ret = 1; } else if (mean_diff > ODD_DIFF_USEC || stddev > ODD_STDDEV_USEC) { printf("Either your system is a bit slow or the " "timer backend is odd.\n"); ret = 0; } else { printf("Looks good enough.\n"); ret = 0; } timer_free_(NULL); for (i = 0; i < N_TIMERS; ++i) { timer_free(timers[i]); } timers_shutdown(); return ret; }