Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Add support for tapered, precise, absolute faketime start time #456

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 197 additions & 2 deletions src/libfaketime.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ static int outfile = -1;

static bool limited_faking = false;
static long callcounter = 0;

static bool ft_taper = 0;
static long long ft_taper_begin_nsec_since_epoch = -1;
static long long ft_taper_end_nsec_since_epoch = -1;

static long ft_start_after_secs = -1;
static long ft_stop_after_secs = -1;
static long ft_start_after_ncalls = -1;
Expand Down Expand Up @@ -1156,6 +1161,149 @@ int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct
}
#endif

static long long faketime_offset_ns()
{
return ((long long)user_offset.tv_sec) * 1000000000 + (long long)user_offset.tv_nsec;
}

// figure out what combination of ifdefs is appropriate, or use
// this unconditionally
#if 1

static long long mult_div_avoid_overflow(long long a, long long b, long long d)
{
__int128_t acc = a;
acc *= b;
acc /= d;
return (long long)acc;
}

#else

// (a * b + c) / d, but do something to avoid multiplication overflow
// (only "mostly" prevents overflow, not if the values are already
// over 2^62)
//
// precondition: a < d, r < d
static long long mad_div_avoid_overflow_inner(long long a, long long b, long long c, long long d)
{
if (a <= 1 || b <= 1) {
return (a * b + c) / d;
}
// how many multiples of [a] we need to make a full [d]
long long k = (d + a - 1) / a; // 2 <= k <= d/2+1
long long rem_per_f = a * k - d;
// how many occurrences of [a*k] (and therefore [d]) we can
// easily take from [a*b]
long long f = b / k;
// remaining: rem_per_f * f + (b % k) * a + c
long long rem = (b % k) * a + c;
long long res = f + rem / d;
rem = rem % d;
return res + mad_div_avoid_overflow_inner(rem_per_f, f, rem, d);
}

static long long mult_div_avoid_overflow(long long a, long long b, long long d)
{
long long res = a / d;
return res + mad_div_avoid_overflow_inner(a%d, b, 0, d);
}

#endif

static long long faketime_do_tapered_transform_gen(long long t, long long taper_begin, long long taper_end, long long offset)
{
if (t <= taper_begin)
{
return t;
}
if (t >= taper_end)
{
return t + offset;
}
long long w = taper_end - taper_begin;
long long h = w + offset;
// [h] output time passes over [w] input time
long long dt1 = t - taper_begin;
// so we're looking for dt1 * h / w
long long dt2 = mult_div_avoid_overflow(dt1, h, w);
return taper_begin + dt2;
}

static long long faketime_do_tapered_offset(long long t)
{
if (!ft_taper)
{
return t + faketime_offset_ns();
}
return faketime_do_tapered_transform_gen(t, ft_taper_begin_nsec_since_epoch, ft_taper_end_nsec_since_epoch, faketime_offset_ns());
}

static long long faketime_undo_tapered_offset(long long t)
{
if (!ft_taper)
{
return t - faketime_offset_ns();
}
long long offset = faketime_offset_ns();
return faketime_do_tapered_transform_gen(t, ft_taper_begin_nsec_since_epoch, ft_taper_end_nsec_since_epoch + offset, -offset);
}

static void faketime_div_mod(long long ns, long long d, long long *res, long long *rem)
{
*res = ns / d;
*rem = ns % d;
if(*rem < 0) {
(*rem) += d;
(*res)--;
}
}

static struct timeval faketime_undo_tapered_offset_timeval(struct timeval t)
{
long long nsec = ((long long)t.tv_sec) * 1000000000 + ((long long)t.tv_usec) * 1000;
nsec = faketime_undo_tapered_offset(nsec);
long long sec;
long long nsec_rem;
faketime_div_mod(nsec, 1000000000, &sec, &nsec_rem);
t.tv_sec = sec;
t.tv_usec = nsec_rem / 1000;
return t;
}

static struct timespec faketime_undo_tapered_offset_timespec(struct timespec t)
{
long long nsec = ((long long)t.tv_sec) * 1000000000 + ((long long)t.tv_nsec);
nsec = faketime_undo_tapered_offset(nsec);
long long sec;
long long nsec_rem;
faketime_div_mod(nsec, 1000000000, &sec, &nsec_rem);
t.tv_sec = sec;
t.tv_nsec = nsec_rem;
return t;
}

static struct timespec faketime_do_tapered_offset_timespec(struct timespec t)
{
long long nsec = ((long long)t.tv_sec) * 1000000000 + ((long long)t.tv_nsec);
nsec = faketime_do_tapered_offset(nsec);
long long sec;
long long nsec_rem;
faketime_div_mod(nsec, 1000000000, &sec, &nsec_rem);
t.tv_sec = sec;
t.tv_nsec = nsec_rem;
return t;
}

// the precision here will be really atrocious,
// if tapering compresses time significantly
static int faketime_undo_tapered_offset_sec(int t)
{
long long nsec = ((long long)t) * 1000000000;
nsec = faketime_undo_tapered_offset(nsec);
return nsec / 1000000000;
}

#ifdef FAKE_FILE_TIMESTAMPS
#ifdef MACOS_DYLD_INTERPOSE
int macos_utime(const char *filename, const struct utimbuf *times)
Expand All @@ -1180,6 +1328,13 @@ int utime(const char *filename, const struct utimbuf *times)
{
ntbuf.actime = times->actime - user_offset.tv_sec;
ntbuf.modtime = times->modtime - user_offset.tv_sec;

if (ft_taper)
{
ntbuf.actime = faketime_undo_tapered_offset_sec(times->actime);
ntbuf.modtime = faketime_undo_tapered_offset_sec(times->modtime);
}

times = &ntbuf;
}
#ifdef MACOS_DYLD_INTERPOSE
Expand Down Expand Up @@ -1217,6 +1372,11 @@ int utimes(const char *filename, const struct timeval times[2])
user_offset2.tv_usec = user_offset.tv_nsec / 1000;
timersub(&times[0], &user_offset2, &tn[0]);
timersub(&times[1], &user_offset2, &tn[1]);
if (ft_taper)
{
tn[0] = faketime_undo_tapered_offset_timeval(times[0]);
tn[1] = faketime_undo_tapered_offset_timeval(times[1]);
}
times = tn;
}
#ifdef MACOS_DYLD_INTERPOSE
Expand Down Expand Up @@ -1261,6 +1421,11 @@ static void fake_two_timespec(const struct timespec in_times[2], struct timespec
else
{
timersub2(&in_times[j], &user_offset, &out_times[j], n);
if (ft_taper)
{
out_times[j] = faketime_undo_tapered_offset_timespec(in_times[j]);
}

}
}
}
Expand Down Expand Up @@ -2897,6 +3062,24 @@ static void ftpl_really_init(void)
}
}

if ((tmp_env = getenv("FAKETIME_TAPER_BEGIN_NSEC_SINCE_EPOCH")) != NULL)
{
ft_taper_begin_nsec_since_epoch = atoll(tmp_env);
if(!ft_taper)
{
ft_taper = true;
ft_taper_end_nsec_since_epoch = ft_taper_begin_nsec_since_epoch;
}
}
if ((tmp_env = getenv("FAKETIME_TAPER_END_NSEC_SINCE_EPOCH")) != NULL)
{
ft_taper_end_nsec_since_epoch = atoll(tmp_env);
if(!ft_taper)
{
ft_taper = true;
ft_taper_begin_nsec_since_epoch = ft_taper_end_nsec_since_epoch;
}
}
if ((tmp_env = getenv("FAKETIME_START_AFTER_SECONDS")) != NULL)
{
ft_start_after_secs = atol(tmp_env);
Expand Down Expand Up @@ -3220,7 +3403,6 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
timespecsub(tp, &ftpl_starttime.real, &tmp_ts);
break;
}

if (limited_faking)
{
/* Check whether we actually should be faking the returned timestamp. */
Expand Down Expand Up @@ -3373,7 +3555,20 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
{
timeadj = tdiff;
}
timespecadd(&user_faked_time_timespec, &timeadj, tp);

if(ft_taper)
{
// Tapered offset time transition ignores the speed up/slow
// down setting. Someone should figure out how to do speed
// up / slow down correctly in that case (ideally, a global
// speed-up, as a part of a global time mapping routine, not
// relying on a per-process state).
*tp = faketime_do_tapered_offset_timespec(*tp);
}
else
{
timespecadd(&user_faked_time_timespec, &timeadj, tp);
}
}
break;

Expand Down