vtwrapper.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 2010, 2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* $Id: vtwrapper.c,v 1.4 2010/08/12 09:31:50 fdupont Exp $ */
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <math.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef SYS_select
#include <sys/select.h>
#endif
#ifdef SYS_poll
#include <poll.h>
#endif
#ifdef SYS_kevent
#include <sys/event.h>
#endif
#ifdef SYS_epoll_wait
#include <sys/epoll.h>
#endif
#ifdef SYS_gettimeofday
#define VIRTUAL_TIME
#ifdef VIRTUAL_TIME
static struct timeval epoch = { 0, 0 };
static int _init_called = 0;
void
_init(void) {
(void)syscall(SYS_gettimeofday, &epoch, NULL);
_init_called = 1;
}
static void
absolute_inflate(struct timeval *vt, struct timeval *rt)
{
double d;
rt->tv_sec = vt->tv_sec;
rt->tv_usec = vt->tv_usec;
if ((epoch.tv_sec > vt->tv_sec) ||
((epoch.tv_sec == vt->tv_sec) && (epoch.tv_usec > vt->tv_usec)))
return;
rt->tv_sec -= epoch.tv_sec;
rt->tv_usec -= epoch.tv_usec;
while (rt->tv_usec < 0) {
rt->tv_sec -= 1;
rt->tv_usec += 1000000;
}
if (rt->tv_sec == 0)
goto done;
d = (double) (rt->tv_sec - 1);
d += (double) rt->tv_usec / 1000000.;
d = exp(d);
rt->tv_sec = (time_t) d;
d -= (double) rt->tv_sec;
rt->tv_usec = (suseconds_t) (d * 1000000.);
done:
rt->tv_sec += epoch.tv_sec;
rt->tv_usec += epoch.tv_usec;
while (rt->tv_usec >= 1000000) {
rt->tv_sec += 1;
rt->tv_usec -= 1000000;
}
return;
}
static void
absolute_deflate(struct timeval *rt, struct timeval *vt) {
double d;
vt->tv_sec = rt->tv_sec;
vt->tv_usec = rt->tv_usec;
if ((epoch.tv_sec > rt->tv_sec) ||
((epoch.tv_sec == rt->tv_sec) && (epoch.tv_usec > rt->tv_usec)))
return;
vt->tv_sec -= epoch.tv_sec;
vt->tv_usec -= epoch.tv_usec;
while (vt->tv_usec < 0) {
vt->tv_sec -= 1;
vt->tv_usec += 1000000;
}
if (vt->tv_sec == 0)
goto done;
d = (double) vt->tv_sec;
d += (double) vt->tv_usec / 1000000.;
d = log(d);
vt->tv_sec = (time_t) d;
d -= (double) vt->tv_sec;
vt->tv_sec += 1;
vt->tv_usec = (suseconds_t) (d * 1000000.);
done:
vt->tv_sec += epoch.tv_sec;
vt->tv_usec += epoch.tv_usec;
while (vt->tv_usec >= 1000000) {
vt->tv_sec += 1;
vt->tv_usec -= 1000000;
}
return;
}
static void
interval_inflate(struct timeval *vt, struct timeval *rt) {
struct timeval now, tv;
(void) gettimeofday(&now, NULL);
absolute_deflate(&now, &tv);
tv.tv_sec += vt->tv_sec;
tv.tv_usec += vt->tv_usec;
while (tv.tv_usec >= 1000000) {
tv.tv_sec += 1;
tv.tv_usec -= 1000000;
}
absolute_inflate(&tv, rt);
rt->tv_sec -= now.tv_sec;
rt->tv_usec -= now.tv_usec;
if (rt->tv_usec < 0) {
rt->tv_sec -= 1;
rt->tv_usec += 1000000;
}
return;
}
static void
interval_deflate(struct timeval *rt, struct timeval *vt) {
struct timeval now, tv;
vt->tv_sec = rt->tv_sec;
vt->tv_usec = rt->tv_usec;
if ((vt->tv_sec == 0) && (vt->tv_usec <= 10000))
return;
(void) gettimeofday(&now, NULL);
tv.tv_sec = now.tv_sec + rt->tv_sec;
tv.tv_usec = now.tv_usec + rt->tv_usec;
while (tv.tv_usec >= 1000000) {
tv.tv_sec += 1;
tv.tv_usec -= 1000000;
}
absolute_deflate(&now, &now);
absolute_deflate(&tv, vt);
vt->tv_sec -= now.tv_sec;
vt->tv_usec -= now.tv_usec;
while (vt->tv_usec < 0) {
vt->tv_sec -= 1;
vt->tv_usec += 1000000;
}
if ((vt->tv_sec == 0) && (vt->tv_usec < 10000))
vt->tv_usec = 10000;
return;
}
#endif
int
gettimeofday(struct timeval *tv, struct timezone *tz) {
#ifdef VIRTUAL_TIME
struct timeval now;
int ret;
if (!_init_called) _init();
if (epoch.tv_sec == 0)
return syscall(SYS_gettimeofday, tv, tz);
ret = syscall(SYS_gettimeofday, &now, tz);
if (ret == 0)
absolute_inflate(&now, tv);
return ret;
#else
return syscall(SYS_gettimeofday, tv, tz);
#endif
}
#ifdef SYS_select
int
select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
struct timeval *timeout)
{
#ifdef VIRTUAL_TIME
struct timeval tv;
if (!_init_called) _init();
if (epoch.tv_sec == 0 || timeout == NULL ||
(timeout->tv_sec == 0 && timeout->tv_usec == 0))
return syscall(SYS_select, nfds, rfds, wfds, xfds, timeout);
interval_deflate(timeout, &tv);
return syscall(SYS_select, nfds, rfds, wfds, xfds, &tv);
#else
return syscall(SYS_select, nfds, rfds, wfds, xfds, timeout);
#endif
}
#endif
#ifdef SYS_poll
int
poll(struct pollfd fds[], nfds_t nfds, int timeout) {
#ifdef VIRTUAL_TIME
struct timeval in, out;
if (!_init_called) _init();
if (timeout <= 0 || epoch.tv_sec == 0)
return syscall(SYS_poll, fds, nfds, timeout);
in.tv_sec = timeout / 1000;
in.tv_usec = (timeout % 1000) * 1000;
interval_deflate(&in, &out);
timeout = out.tv_sec * 1000 + out.tv_usec / 1000;
return syscall(SYS_poll, fds, nfds, timeout);
#else
return syscall(SYS_poll, fds, nfds, timeout);
#endif
}
#endif
#ifdef SYS_kevent
int
kevent(int kq, struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents, const struct timespec *timeout)
{
#ifdef VIRTUAL_TIME
struct timeval in, out;
struct timespec ts;
if (!_init_called) _init();
if (epoch.tv_sec == 0 || timeout == NULL ||
(timeout->tv_sec == 0 && timeout->tv_nsec == 0))
return syscall(SYS_kevent, kq, changelist, nchanges,
eventlist, nevents, timeout);
in.tv_sec = timeout->tv_sec;
in.tv_usec = timeout->tv_nsec / 1000;
interval_deflate(&in, &out);
ts.tv_sec = out.tv_sec;
ts.tv_nsec = out.tv_usec * 1000;
return syscall(SYS_kevent, kq, changelist, nchanges, eventlist,
nevents, &ts);
#else
return syscall(SYS_kevent, kq, changelist, nchanges, eventlist,
nevents, timeout);
#endif
}
#endif
#ifdef SYS_epoll_wait
int
epoll_wait(int fd, struct epoll_event *events, int maxevents, int timeout) {
#ifdef VIRTUAL_TIME
struct timeval in, out;
if (!_init_called) _init();
if (timeout == 0 || timeout == -1 || epoch.tv_sec == 0)
return syscall(SYS_epoll_wait, fd, events, maxevents, timeout);
in.tv_sec = timeout / 1000;
in.tv_usec = (timeout % 1000) * 1000;
interval_deflate(&in, &out);
timeout = out.tv_sec * 1000 + out.tv_usec / 1000;
return syscall(SYS_poll, fd, events, maxevents, timeout);
#else
return syscall(SYS_poll, fd, events, maxevents, timeout);
#endif
}
#endif
#endif