mirror of
https://github.com/wolfcw/libfaketime.git
synced 2026-05-18 17:26:09 +03:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ba77bd61b | ||
|
|
e36ec5726e | ||
|
|
37c9430117 | ||
|
|
4aa0077bfc | ||
|
|
097ce79771 | ||
|
|
994b7c75ca | ||
|
|
483a7a703c | ||
|
|
e8a1c1fd3b | ||
|
|
75d91ea726 | ||
|
|
6c7aa3966c | ||
|
|
3062fb2004 | ||
|
|
b687b165b2 | ||
|
|
e21bf3017a | ||
|
|
13d47210d5 | ||
|
|
712733e5f0 | ||
|
|
1e931cb4cd | ||
|
|
026a2627af | ||
|
|
7295f20288 | ||
|
|
92bf909d95 | ||
|
|
ebe76e26b0 | ||
|
|
c0aa6189f7 | ||
|
|
7ba95f4cb0 | ||
|
|
aabe141783 | ||
|
|
dbe865dfdb | ||
|
|
1231a002e0 | ||
|
|
3109728f45 | ||
|
|
949b36e6a2 | ||
|
|
0c76f27777 | ||
|
|
7bfe6566b3 | ||
|
|
c2a9bc1878 | ||
|
|
4d0f0b7426 | ||
|
|
9d63a80062 | ||
|
|
cccce3bf23 | ||
|
|
f33dda8022 | ||
|
|
4fc06b90df | ||
|
|
75e130c4f1 | ||
|
|
066f38baac | ||
|
|
4de86c2145 | ||
|
|
ffdb51bc30 | ||
|
|
3aa2028174 | ||
|
|
6566162e7e | ||
|
|
53ba71e547 | ||
|
|
6404d81f63 | ||
|
|
44c578c6d6 | ||
|
|
2649cdb156 | ||
|
|
71b31e908d | ||
|
|
523584abd4 | ||
|
|
a2e406c669 |
15
README
15
README
@@ -256,10 +256,10 @@ the difference:
|
||||
|
||||
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
|
||||
FAKETIME_DONT_RESET=1 \
|
||||
/bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
|
||||
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
|
||||
/bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
|
||||
In the second example, the "date" command will always print the same time,
|
||||
while in the first example, with FAKETIME_DONT_RESET set, time will increment
|
||||
@@ -354,7 +354,7 @@ like "+0 x2", i.e., use an explicit zero offset as a prefix in your FAKETIME.
|
||||
For testing, your should run a command like
|
||||
|
||||
LD_PRELOAD=./libfaketime.so.1 FAKETIME="+1,5y x10,0" \
|
||||
/bin/bash -c 'while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
bash -c 'while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
|
||||
For each second that the endless loop sleeps, the executed bash shell will
|
||||
think that 10 seconds have passed ($SECONDS is a bash-internal variable
|
||||
@@ -420,6 +420,15 @@ LD_PRELOAD=/path/to/libfaketime.so.1 \
|
||||
# (in a different terminal window or whatever)
|
||||
touch -t 2002290123.45 /tmp/my-demo-file.tmp
|
||||
|
||||
Setting the environment variable FAKETIME_FOLLOW_ABSOLUTE=1 enables a submode
|
||||
of FAKETIME_FOLLOW_FILE behavior where fake time ONLY advances when the follow
|
||||
file's timestamp advances. In this mode an application that is not subject to
|
||||
libfaketime LD_PRELOAD intercept can absolutely control time for applications
|
||||
that are hooked by libfaketime. For example, a host application can control the
|
||||
timestamp of a follow file mapped into a container to implement (relatively)
|
||||
clean pause/resume behavior for fake time applications running within the
|
||||
container.
|
||||
|
||||
|
||||
Changing the 'x' modifier during run-time
|
||||
-----------------------------------------
|
||||
|
||||
@@ -40,11 +40,11 @@ Use a specific GNU-date compatible implementation of the helper used to transfor
|
||||
|
||||
.SH EXAMPLES
|
||||
.nf
|
||||
faketime 'last Friday 5 pm' /bin/date
|
||||
faketime '2008-12-24 08:15:42' /bin/date
|
||||
faketime -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y i2,0' /bin/bash -c 'while true; do date ; sleep 1 ; done'
|
||||
faketime 'last Friday 5 pm' date
|
||||
faketime '2008-12-24 08:15:42' date
|
||||
faketime -f '+2,5y x10,0' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y x0,50' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y i2,0' bash -c 'while true; do date ; sleep 1 ; done'
|
||||
In this single case all spawned processes will use the same global clock without restarting it at the start of each process.
|
||||
|
||||
(Please note that it depends on your locale settings whether . or , has to be used for fractional offsets)
|
||||
|
||||
47
src/Makefile
47
src/Makefile
@@ -49,13 +49,24 @@
|
||||
#
|
||||
# INTERCEPT_SYSCALL
|
||||
# - (On GNU/Linux only) intercept glibc's syscall() for known relevant syscalls.
|
||||
# If enabled, this currently only works to divert the getrandom syscall.
|
||||
# If enabled, this currently only works a few types of syscalls,
|
||||
# including FUTEX (see below), clock_gettime, etc.
|
||||
#
|
||||
# - note that on unusual architectures, if INTERCEPT_SYSCALL is set, you may
|
||||
# need to explicitly define variadic_promotion_t (e.g. by putting
|
||||
# -Dvariadic_promotion_t=int into CFLAGS). See src/faketime_common.h for
|
||||
# more info.
|
||||
#
|
||||
# INTERCEPT_FUTEX
|
||||
# - (On GNU/Linux only) intercept glibc's syscall() for relevant FUTEX syscalls.
|
||||
# If enabled, FUTEX syscalls will be intercepted and translated correspondingly.
|
||||
# - when FUTEX_WAIT_BITSET (with absolute deadline) is set, the deadline will
|
||||
# be adjusted based on the faketime.
|
||||
# - when FUTEX_WAKE (with relative deadline) is set, the deadline will be
|
||||
# adjusted based on the time rate alone.
|
||||
# - for other FUTEX operations, no adjustment is made for now.
|
||||
#
|
||||
#
|
||||
# FAKE_STATELESS
|
||||
# - Remove support for any functionality that requires sharing state across
|
||||
# threads of a process, or different processes. This decreases the risk of
|
||||
@@ -115,6 +126,14 @@ PREFIX ?= /usr/local
|
||||
LIBDIRNAME ?= /lib/faketime
|
||||
PLATFORM ?=$(shell uname)
|
||||
|
||||
ifneq ($(filter android%,$(subst -, ,$(CC))),)
|
||||
IS_ANDROID := true
|
||||
endif
|
||||
|
||||
ifeq ($(shell $(CC) -dM -E - < /dev/null | grep -c "__ANDROID__"),1)
|
||||
IS_ANDROID := true
|
||||
endif
|
||||
|
||||
ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1)
|
||||
COMPILER := clang
|
||||
else
|
||||
@@ -136,15 +155,26 @@ ifeq ($(PLATFORM),SunOS)
|
||||
CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=600
|
||||
endif
|
||||
|
||||
ifdef IS_ANDROID
|
||||
CFLAGS += -D__ANDROID__
|
||||
endif
|
||||
|
||||
LIB_LDFLAGS += -shared
|
||||
|
||||
LDFLAGS += $(FAKETIME_LINK_FLAGS)
|
||||
ifneq ($(PLATFORM),SunOS)
|
||||
ifndef IS_ANDROID
|
||||
LDFLAGS += -Wl,--version-script=libfaketime.map
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef IS_ANDROID
|
||||
LDADD += -ldl -lm
|
||||
BIN_LDFLAGS +=
|
||||
else
|
||||
LDADD += -ldl -lm -lrt -lpthread
|
||||
BIN_LDFLAGS += -lrt -lpthread
|
||||
endif
|
||||
|
||||
SRC = libfaketime.c
|
||||
LIBS_OBJ = libfaketime.o libfaketimeMT.o
|
||||
@@ -157,17 +187,20 @@ all: ${LIBS} ${BINS}
|
||||
|
||||
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME
|
||||
|
||||
${LIBS_OBJ}: libfaketime.c
|
||||
ft_sem.o: ft_sem.c ft_sem.h
|
||||
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} $<
|
||||
|
||||
${LIBS_OBJ}: libfaketime.c ft_sem.h
|
||||
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $<
|
||||
|
||||
%.so.${SONAME}: %.o libfaketime.map
|
||||
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ${LDADD}
|
||||
%.so.${SONAME}: %.o ft_sem.o libfaketime.map
|
||||
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ft_sem.o ${LDADD}
|
||||
|
||||
${BINS}: faketime.c
|
||||
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ${LDFLAGS} ${BIN_LDFLAGS}
|
||||
${BINS}: faketime.c ft_sem.o ft_sem.h
|
||||
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ft_sem.o ${LDFLAGS} ${BIN_LDFLAGS}
|
||||
|
||||
clean:
|
||||
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS}
|
||||
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS} ft_sem.o
|
||||
|
||||
distclean: clean
|
||||
@echo
|
||||
|
||||
@@ -71,25 +71,32 @@ LIB_LDFLAGS += -dynamiclib -current_version 0.9.12 -compatibility_version 0.7
|
||||
|
||||
ARCH := $(shell uname -m)
|
||||
|
||||
ifeq ($(ARCH),arm64)
|
||||
CFLAGS += -arch arm64e -arch arm64
|
||||
CFLAGS += -fptrauth-calls -fptrauth-returns
|
||||
endif
|
||||
|
||||
SONAME = 1
|
||||
LIBS = libfaketime.${SONAME}.dylib
|
||||
BINS = faketime
|
||||
|
||||
all: ${LIBS} ${BINS}
|
||||
|
||||
ifeq ($(ARCH),arm64)
|
||||
libfaketime.${SONAME}.dylib: libfaketime.c
|
||||
${CC} -o libfaketime.arm64e.dylib ${CFLAGS} -arch arm64e -fptrauth-calls -fptrauth-returns ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
|
||||
${CC} -o libfaketime.arm64.dylib ${CFLAGS} -arch arm64 ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
|
||||
lipo -create -output $@ libfaketime.arm64e.dylib libfaketime.arm64.dylib
|
||||
rm libfaketime.arm64e.dylib libfaketime.arm64.dylib
|
||||
|
||||
faketime: faketime.c
|
||||
${CC} -o $@ ${CFLAGS} -arch arm64e -arch arm64 ${LDFLAGS} $<
|
||||
|
||||
else
|
||||
libfaketime.${SONAME}.dylib: libfaketime.c
|
||||
${CC} -o $@ ${CFLAGS} ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
|
||||
|
||||
faketime: faketime.c
|
||||
${CC} -o $@ ${CFLAGS} ${LDFLAGS} $<
|
||||
endif
|
||||
|
||||
clean:
|
||||
@rm -f ${OBJ} ${LIBS} ${BINS}
|
||||
@rm -f ${OBJ} ${LIBS} ${BINS} libfaketime.arm64e.dylib libfaketime.arm64.dylib
|
||||
|
||||
distclean: clean
|
||||
@echo
|
||||
|
||||
51
src/android_compat.h
Normal file
51
src/android_compat.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef ANDROID_COMPAT_H
|
||||
#define ANDROID_COMPAT_H
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static inline const char *__android_shm_tmpdir(void)
|
||||
{
|
||||
const char *dir = getenv("FAKETIME_SHM_DIR");
|
||||
if (dir == NULL)
|
||||
dir = getenv("TMPDIR");
|
||||
if (dir == NULL)
|
||||
dir = "/data/local/tmp";
|
||||
return dir;
|
||||
}
|
||||
|
||||
static inline int __android_shm_open(const char *name, int oflag, mode_t mode)
|
||||
{
|
||||
char path[512];
|
||||
const char *dir = __android_shm_tmpdir();
|
||||
while (*name == '/') name++;
|
||||
snprintf(path, sizeof(path), "%s/faketime_shm_%s", dir, name);
|
||||
int fd = open(path, oflag, mode);
|
||||
if (fd == -1 && (oflag & O_CREAT))
|
||||
{
|
||||
mkdir(dir, 0777);
|
||||
fd = open(path, oflag, mode);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static inline int __android_shm_unlink(const char *name)
|
||||
{
|
||||
char path[512];
|
||||
const char *dir = __android_shm_tmpdir();
|
||||
while (*name == '/') name++;
|
||||
snprintf(path, sizeof(path), "%s/faketime_shm_%s", dir, name);
|
||||
return unlink(path);
|
||||
}
|
||||
|
||||
#define shm_open __android_shm_open
|
||||
#define shm_unlink __android_shm_unlink
|
||||
|
||||
#endif /* __ANDROID__ */
|
||||
#endif /* ANDROID_COMPAT_H */
|
||||
@@ -44,8 +44,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <semaphore.h>
|
||||
#include "ft_sem.h"
|
||||
|
||||
#include "android_compat.h"
|
||||
#include "faketime_common.h"
|
||||
|
||||
const char version[] = "0.9.12";
|
||||
@@ -60,6 +61,7 @@ static const char *date_cmd = "date";
|
||||
|
||||
/* semaphore and shared memory names */
|
||||
char sem_name[PATH_BUFSIZE] = {0}, shm_name[PATH_BUFSIZE] = {0};
|
||||
static ft_sem_t wrapper_sem;
|
||||
|
||||
void usage(const char *name)
|
||||
{
|
||||
@@ -84,11 +86,11 @@ void usage(const char *name)
|
||||
" --date-prog PROG : Use specified GNU-compatible implementation of 'date' program\n"
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
"%s 'last friday 5 pm' /bin/date\n"
|
||||
"%s '2008-12-24 08:15:42' /bin/date\n"
|
||||
"%s -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y i2,0' /bin/bash -c 'date; while true; do date; sleep 1 ; done'\n"
|
||||
"%s 'last friday 5 pm' date\n"
|
||||
"%s '2008-12-24 08:15:42' date\n"
|
||||
"%s -f '+2,5y x10,0' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y x0,50' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y i2,0' bash -c 'date; while true; do date; sleep 1 ; done'\n"
|
||||
"In this single case all spawned processes will use the same global clock\n"
|
||||
"without restarting it at the start of each process.\n\n"
|
||||
"(Please note that it depends on your locale settings whether . or , has to be used for fractions)\n"
|
||||
@@ -98,9 +100,9 @@ void usage(const char *name)
|
||||
/** Clean up shared objects */
|
||||
static void cleanup_shobjs()
|
||||
{
|
||||
if (-1 == sem_unlink(sem_name))
|
||||
if (-1 == ft_sem_unlink(&wrapper_sem))
|
||||
{
|
||||
perror("faketime: sem_unlink");
|
||||
perror("faketime: ft_sem_unlink");
|
||||
}
|
||||
if (-1 == shm_unlink(shm_name))
|
||||
{
|
||||
@@ -255,9 +257,8 @@ int main (int argc, char **argv)
|
||||
curr_opt++;
|
||||
|
||||
{
|
||||
/* create semaphores and shared memory */
|
||||
/* create lock and shared memory */
|
||||
int shm_fd;
|
||||
sem_t *sem;
|
||||
struct ft_shared_s *ft_shared;
|
||||
char shared_objs[PATH_BUFSIZE * 2 + 1];
|
||||
|
||||
@@ -271,10 +272,10 @@ int main (int argc, char **argv)
|
||||
snprintf(sem_name, PATH_BUFSIZE -1 ,"/faketime_sem_%ld", (long)getpid());
|
||||
snprintf(shm_name, PATH_BUFSIZE -1 ,"/faketime_shm_%ld", (long)getpid());
|
||||
|
||||
if (SEM_FAILED == (sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
|
||||
if (-1 == ft_sem_create(sem_name, &wrapper_sem))
|
||||
{
|
||||
perror("faketime: sem_open");
|
||||
fprintf(stderr, "The faketime wrapper only works on platforms that support the sem_open()\nsystem call. However, you may LD_PRELOAD libfaketime without using this wrapper.\n");
|
||||
perror("faketime: ft_sem_create");
|
||||
fprintf(stderr, "The faketime wrapper failed to create its lock.\nHowever, you may LD_PRELOAD libfaketime without using this wrapper.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -282,15 +283,12 @@ int main (int argc, char **argv)
|
||||
if (-1 == (shm_fd = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR)))
|
||||
{
|
||||
perror("faketime: shm_open");
|
||||
if (-1 == sem_unlink(argv[2]))
|
||||
{
|
||||
perror("faketime: sem_unlink");
|
||||
}
|
||||
ft_sem_unlink(&wrapper_sem);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* set shm size */
|
||||
if (-1 == ftruncate(shm_fd, sizeof(uint64_t)))
|
||||
if (-1 == ftruncate(shm_fd, sizeof(struct ft_shared_s)))
|
||||
{
|
||||
perror("faketime: ftruncate");
|
||||
cleanup_shobjs();
|
||||
@@ -306,9 +304,9 @@ int main (int argc, char **argv)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (sem_wait(sem) == -1)
|
||||
if (ft_sem_lock(&wrapper_sem) == -1)
|
||||
{
|
||||
perror("faketime: sem_wait");
|
||||
perror("faketime: ft_sem_lock");
|
||||
cleanup_shobjs();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -316,12 +314,16 @@ int main (int argc, char **argv)
|
||||
/* init elapsed time ticks to zero */
|
||||
ft_shared->ticks = 0;
|
||||
ft_shared->file_idx = 0;
|
||||
ft_shared->start_time.real.tv_sec = 0;
|
||||
ft_shared->start_time.real.tv_nsec = -1;
|
||||
ft_shared->start_time.mon.tv_sec = 0;
|
||||
ft_shared->start_time.mon.tv_nsec = -1;
|
||||
ft_shared->start_time.mon_raw.tv_sec = 0;
|
||||
ft_shared->start_time.mon_raw.tv_nsec = -1;
|
||||
ft_shared->start_time_real.sec = 0;
|
||||
ft_shared->start_time_real.nsec = -1;
|
||||
ft_shared->start_time_mon.sec = 0;
|
||||
ft_shared->start_time_mon.nsec = -1;
|
||||
ft_shared->start_time_mon_raw.sec = 0;
|
||||
ft_shared->start_time_mon_raw.nsec = -1;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
ft_shared->start_time_boot.sec = 0;
|
||||
ft_shared->start_time_boot.nsec = -1;
|
||||
#endif
|
||||
|
||||
if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s))))
|
||||
{
|
||||
@@ -330,16 +332,16 @@ int main (int argc, char **argv)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (sem_post(sem) == -1)
|
||||
if (ft_sem_unlock(&wrapper_sem) == -1)
|
||||
{
|
||||
perror("faketime: semop");
|
||||
perror("faketime: ft_sem_unlock");
|
||||
cleanup_shobjs();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
snprintf(shared_objs, sizeof(shared_objs), "%s %s", sem_name, shm_name);
|
||||
setenv("FAKETIME_SHARED", shared_objs, true);
|
||||
sem_close(sem);
|
||||
ft_sem_close(&wrapper_sem);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -38,6 +38,13 @@ struct system_time_s
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Fixed-width time representation for shared memory (arch-independent) */
|
||||
struct ft_shared_time_s
|
||||
{
|
||||
int64_t sec;
|
||||
int64_t nsec;
|
||||
};
|
||||
|
||||
/* Data shared among faketime-spawned processes */
|
||||
struct ft_shared_s
|
||||
{
|
||||
@@ -47,8 +54,13 @@ struct ft_shared_s
|
||||
uint64_t ticks;
|
||||
/* Index of timestamp to be loaded from file */
|
||||
uint64_t file_idx;
|
||||
/* System time Faketime started at */
|
||||
struct system_time_s start_time;
|
||||
/* System time Faketime started at (fixed-width for cross-arch safety) */
|
||||
struct ft_shared_time_s start_time_real;
|
||||
struct ft_shared_time_s start_time_mon;
|
||||
struct ft_shared_time_s start_time_mon_raw;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
struct ft_shared_time_s start_time_boot;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* These are all needed in order to properly build on OSX */
|
||||
|
||||
227
src/ft_sem.c
Normal file
227
src/ft_sem.c
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* This file is part of libfaketime, version 0.9.12
|
||||
*
|
||||
* libfaketime is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License v2 along
|
||||
* with the libfaketime; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "ft_sem.h"
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
#include <sys/sem.h>
|
||||
#endif
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
#include <sys/file.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* =======================================================================
|
||||
* Semaphore related functions === SEM
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
int ft_sem_name2key(char *name)
|
||||
{
|
||||
key_t key;
|
||||
char fullname[256];
|
||||
snprintf(fullname, sizeof(fullname), "/tmp%s", name);
|
||||
fullname[sizeof(fullname) - 1] = '\0';
|
||||
int fd = open(fullname, O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror("libfaketime: open");
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
if (-1 == (key = ftok(fullname, 'F')))
|
||||
{
|
||||
perror("libfaketime: ftok");
|
||||
return -1;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
static int ft_sem_name_to_path(const char *name, char *path, size_t pathlen)
|
||||
{
|
||||
const char *prefix = "/faketime_sem_";
|
||||
const char *p = strstr(name, prefix);
|
||||
if (p == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
const char *pid_str = p + strlen(prefix);
|
||||
const char *tmpdir;
|
||||
#ifdef __ANDROID__
|
||||
tmpdir = getenv("TMPDIR");
|
||||
if (tmpdir == NULL) tmpdir = "/data/local/tmp";
|
||||
#else
|
||||
tmpdir = "/dev/shm";
|
||||
#endif
|
||||
snprintf(path, pathlen, "%s/faketime_lock_%s", tmpdir, pid_str);
|
||||
path[pathlen - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ft_sem_create(char *name, ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
if (SEM_FAILED == (ft_sem->sem = sem_open(name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
key_t key = ft_sem_name2key(name);
|
||||
int nsems = 1;
|
||||
|
||||
ft_sem->semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | S_IWUSR | S_IRUSR);
|
||||
if (ft_sem->semid >= 0) { /* we got here first */
|
||||
struct sembuf sb = {
|
||||
.sem_num = 0,
|
||||
.sem_op = 1, /* number of resources the semaphore has (when decremented down to 0 the semaphore will block) */
|
||||
.sem_flg = 0,
|
||||
};
|
||||
/* the number of semaphore operation structures (struct sembuf) passed to semop */
|
||||
int nsops = 1;
|
||||
|
||||
/* do a semop() to "unlock" the semaphore */
|
||||
if (-1 == semop(ft_sem->semid, &sb, nsops)) {
|
||||
return -1;
|
||||
}
|
||||
} else if (errno == EEXIST) { /* someone else got here before us */
|
||||
return -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
char path[256];
|
||||
if (ft_sem_name_to_path(name, path, sizeof(path)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ft_sem->fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR);
|
||||
if (ft_sem->fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
strncpy(ft_sem->name, name, sizeof ft_sem->name - 1);
|
||||
ft_sem->name[sizeof ft_sem->name - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft_sem_open(char *name, ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
if (SEM_FAILED == (ft_sem->sem = sem_open(name, 0)))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
key_t key = ft_sem_name2key(name);
|
||||
ft_sem->semid = semget(key, 1, S_IWUSR | S_IRUSR);
|
||||
if (ft_sem->semid < 0) {
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
char path[256];
|
||||
if (ft_sem_name_to_path(name, path, sizeof(path)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ft_sem->fd = open(path, O_RDWR);
|
||||
if (ft_sem->fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
strncpy(ft_sem->name, name, sizeof ft_sem->name - 1);
|
||||
ft_sem->name[sizeof ft_sem->name - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft_sem_lock(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_wait(ft_sem->sem);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
struct sembuf sb = {
|
||||
.sem_num = 0,
|
||||
.sem_op = -1, /* allocate resource (lock) */
|
||||
.sem_flg = 0,
|
||||
};
|
||||
return semop(ft_sem->semid, &sb, 1);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
return flock(ft_sem->fd, LOCK_EX);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ft_sem_unlock(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_post(ft_sem->sem);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
struct sembuf sb = {
|
||||
.sem_num = 0,
|
||||
.sem_op = 1, /* free resource (unlock) */
|
||||
.sem_flg = 0,
|
||||
};
|
||||
return semop(ft_sem->semid, &sb, 1);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
return flock(ft_sem->fd, LOCK_UN);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ft_sem_close(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_close(ft_sem->sem);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
/* NOP -- there's no "close" equivalent */
|
||||
(void)ft_sem;
|
||||
return 0;
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
int ret = close(ft_sem->fd);
|
||||
ft_sem->fd = -1;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ft_sem_unlink(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_unlink(ft_sem->name);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
return semctl(ft_sem->semid, 0, IPC_RMID);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
char path[256];
|
||||
if (ft_sem_name_to_path(ft_sem->name, path, sizeof(path)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return unlink(path);
|
||||
#endif
|
||||
}
|
||||
62
src/ft_sem.h
Normal file
62
src/ft_sem.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of libfaketime, version 0.9.12
|
||||
*
|
||||
* libfaketime is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License v2 along
|
||||
* with the libfaketime; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef FT_SEM_H
|
||||
#define FT_SEM_H
|
||||
|
||||
/* semaphore backend options */
|
||||
#define FT_POSIX 1
|
||||
#define FT_SYSV 2
|
||||
#define FT_FLOCK 3
|
||||
|
||||
/* set default backend */
|
||||
#ifndef FT_SEMAPHORE_BACKEND
|
||||
#define FT_SEMAPHORE_BACKEND FT_FLOCK
|
||||
#endif
|
||||
|
||||
/* validate selected backend */
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
#else
|
||||
#error "Unknown FT_SEMAPHORE_BACKEND; select between FT_POSIX, FT_SYSV, and FT_FLOCK"
|
||||
#endif
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[256];
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
sem_t *sem;
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
int semid;
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
int fd;
|
||||
#endif
|
||||
} ft_sem_t;
|
||||
|
||||
int ft_sem_create(char *name, ft_sem_t *ft_sem);
|
||||
int ft_sem_open(char *name, ft_sem_t *ft_sem);
|
||||
int ft_sem_lock(ft_sem_t *ft_sem);
|
||||
int ft_sem_unlock(ft_sem_t *ft_sem);
|
||||
int ft_sem_close(ft_sem_t *ft_sem);
|
||||
int ft_sem_unlink(ft_sem_t *ft_sem);
|
||||
|
||||
#endif /* FT_SEM_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
## Copyright (c) 2013, adrelanos at riseup dot net
|
||||
## All rights reserved.
|
||||
|
||||
@@ -9,7 +9,7 @@ OBJ = ${SRC:.c=.o}
|
||||
TEST_SNIPPETS = $(notdir $(basename $(wildcard snippets/*.c)))
|
||||
EXPECTATIONS = $(notdir $(basename $(wildcard snippets/*.variable)))
|
||||
|
||||
ALL_TESTS = timetest test
|
||||
ALL_TESTS = timetest test shm_layout_test
|
||||
|
||||
ifneq ($(filter -DINTERCEPT_SYSCALL,${CFLAGS}),)
|
||||
ALL_TESTS += confirm_variadic_promotion
|
||||
@@ -26,6 +26,9 @@ all: $(ALL_TESTS)
|
||||
timetest: ${OBJ}
|
||||
${CC} -o $@ ${OBJ} ${LDFLAGS}
|
||||
|
||||
shm_layout_test: shm_layout_test.c ../src/faketime_common.h
|
||||
${CC} -o $@ ${CFLAGS} $<
|
||||
|
||||
test: timetest functest libmallocintercept.so
|
||||
@echo
|
||||
@./test.sh
|
||||
@@ -76,7 +79,7 @@ use_lib_%: _use_lib_test.c snippets/%.c lib%.so
|
||||
## cleanup and metainformation
|
||||
|
||||
clean:
|
||||
@rm -f ${OBJ} timetest getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so
|
||||
@rm -f ${OBJ} timetest shm_layout_test getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so
|
||||
|
||||
distclean: clean
|
||||
@echo
|
||||
|
||||
@@ -66,7 +66,7 @@ get_monotonic_time()
|
||||
clock_id=$1; shift;
|
||||
FAKETIME_DONT_FAKE_MONOTONIC=${dont_fake_mono} \
|
||||
fakecmd "2014-07-21 09:00:00" \
|
||||
/bin/bash -c "for i in 1 2; do \
|
||||
bash -c "for i in 1 2; do \
|
||||
perl -w -MTime::HiRes=clock_gettime,${clock_id} -E \
|
||||
'say clock_gettime(${clock_id})'; \
|
||||
sleep 1; \
|
||||
|
||||
79
test/functests/test_follow_absolute.sh
Normal file
79
test/functests/test_follow_absolute.sh
Normal file
@@ -0,0 +1,79 @@
|
||||
# Tests for FAKETIME_FOLLOW_ABSOLUTE feature.
|
||||
#
|
||||
# When FAKETIME_FOLLOW_ABSOLUTE=1 is set alongside FAKETIME="%" and
|
||||
# FAKETIME_FOLLOW_FILE, time freezes at the follow file's mtime
|
||||
# and only advances when the file's mtime changes.
|
||||
|
||||
FOLLOW_FILE=".follow_absolute_test_file"
|
||||
|
||||
init()
|
||||
{
|
||||
typeset testsuite="$1"
|
||||
PLATFORM=$(platform)
|
||||
if [ -z "$PLATFORM" ]; then
|
||||
echo "$testsuite: unknown platform! quitting"
|
||||
return 1
|
||||
fi
|
||||
echo "# PLATFORM=$PLATFORM"
|
||||
return 0
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
init
|
||||
|
||||
run_testcase follow_absolute_basic
|
||||
run_testcase follow_absolute_freeze
|
||||
run_testcase follow_absolute_tracks_mtime
|
||||
|
||||
rm -f "$FOLLOW_FILE"
|
||||
}
|
||||
|
||||
# Helper to run a command with follow-absolute configuration
|
||||
follow_absolute_cmd()
|
||||
{
|
||||
FAKETIME_FOLLOW_FILE="$FOLLOW_FILE" \
|
||||
FAKETIME_FOLLOW_ABSOLUTE=1 \
|
||||
fakecmd "%" "$@"
|
||||
}
|
||||
|
||||
# Test that time matches the follow file's mtime
|
||||
follow_absolute_basic()
|
||||
{
|
||||
touch -d "2020-03-15 10:30:00 UTC" "$FOLLOW_FILE"
|
||||
typeset actual
|
||||
actual=$(follow_absolute_cmd date -u +"%Y-%m-%d %H:%M:%S")
|
||||
asserteq "$actual" "2020-03-15 10:30:00" \
|
||||
"time should match follow file mtime"
|
||||
}
|
||||
|
||||
# Test that time stays frozen (does not advance with real time)
|
||||
follow_absolute_freeze()
|
||||
{
|
||||
touch -d "2020-03-15 10:30:00 UTC" "$FOLLOW_FILE"
|
||||
typeset timestamps
|
||||
timestamps=$(follow_absolute_cmd \
|
||||
perl -e 'print time(), "\n"; sleep(2); print time(), "\n"')
|
||||
typeset first second
|
||||
first=$(echo "$timestamps" | head -1)
|
||||
second=$(echo "$timestamps" | tail -1)
|
||||
asserteq "$first" "$second" \
|
||||
"time should stay frozen within a single process"
|
||||
}
|
||||
|
||||
# Test that time tracks file mtime changes at millisecond precision
|
||||
follow_absolute_tracks_mtime()
|
||||
{
|
||||
touch -d "2020-03-15 10:30:00.000 UTC" "$FOLLOW_FILE"
|
||||
typeset first
|
||||
first=$(follow_absolute_cmd \
|
||||
perl -MTime::HiRes=time -e 'printf "%.3f\n", time()')
|
||||
|
||||
touch -d "2020-03-15 10:30:00.005 UTC" "$FOLLOW_FILE"
|
||||
typeset second
|
||||
second=$(follow_absolute_cmd \
|
||||
perl -MTime::HiRes=time -e 'printf "%.3f\n", time()')
|
||||
|
||||
assertneq "$first" "$second" \
|
||||
"time should advance with file mtime (ms precision)"
|
||||
}
|
||||
31
test/functests/test_shm_across_processes.sh
Normal file
31
test/functests/test_shm_across_processes.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
# Verify that shared memory works end-to-end across processes.
|
||||
# The faketime wrapper creates SHM with a known FAKETIME value,
|
||||
# and the child process (via LD_PRELOAD) reads it and reports
|
||||
# the faked time.
|
||||
|
||||
init()
|
||||
{
|
||||
typeset testsuite="$1"
|
||||
PLATFORM=$(platform)
|
||||
if [ -z "$PLATFORM" ]; then
|
||||
echo "$testsuite: unknown platform! quitting"
|
||||
return 1
|
||||
fi
|
||||
echo "# PLATFORM=$PLATFORM"
|
||||
return 0
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
init
|
||||
|
||||
run_testcase shm_year_check
|
||||
}
|
||||
|
||||
shm_year_check()
|
||||
{
|
||||
typeset expected="2020"
|
||||
typeset actual
|
||||
actual=$(fakecmd "2020-06-15 12:00:00" date -u +%Y)
|
||||
asserteq "$actual" "$expected" "child process should see faked year via SHM"
|
||||
}
|
||||
@@ -76,7 +76,7 @@ void *malloc(size_t size) {
|
||||
}
|
||||
|
||||
void free(void *ptr) {
|
||||
long int ptr2 = (long int) ptr; ptr2 -= (long int) ptr;
|
||||
(void) ptr; /* unused */
|
||||
print_msg("Called free() on from libmallocintercept...");
|
||||
poke_faketime();
|
||||
print_msg("successfully\n");
|
||||
|
||||
65
test/shm_layout_test.c
Normal file
65
test/shm_layout_test.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../src/faketime_common.h"
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check_offset(const char *field, size_t actual, size_t expected)
|
||||
{
|
||||
if (actual != expected)
|
||||
{
|
||||
fprintf(stderr, "FAIL: offsetof(ft_shared_s, %s) = %zu, expected %zu\n",
|
||||
field, actual, expected);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OK: offsetof(ft_shared_s, %s) = %zu\n", field, actual);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_size(const char *name, size_t actual, size_t expected)
|
||||
{
|
||||
if (actual != expected)
|
||||
{
|
||||
fprintf(stderr, "FAIL: sizeof(%s) = %zu, expected %zu\n",
|
||||
name, actual, expected);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OK: sizeof(%s) = %zu\n", name, actual);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
/* ft_shared_time_s must be exactly 16 bytes: two int64_t */
|
||||
check_size("ft_shared_time_s", sizeof(struct ft_shared_time_s), 16);
|
||||
|
||||
/* Field offsets in ft_shared_s */
|
||||
check_offset("ticks", offsetof(struct ft_shared_s, ticks), 0);
|
||||
check_offset("file_idx", offsetof(struct ft_shared_s, file_idx), 8);
|
||||
check_offset("start_time_real", offsetof(struct ft_shared_s, start_time_real), 16);
|
||||
check_offset("start_time_mon", offsetof(struct ft_shared_s, start_time_mon), 32);
|
||||
check_offset("start_time_mon_raw", offsetof(struct ft_shared_s, start_time_mon_raw), 48);
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
check_offset("start_time_boot", offsetof(struct ft_shared_s, start_time_boot), 64);
|
||||
check_size("ft_shared_s", sizeof(struct ft_shared_s), 80);
|
||||
#else
|
||||
check_size("ft_shared_s", sizeof(struct ft_shared_s), 64);
|
||||
#endif
|
||||
|
||||
if (failures > 0)
|
||||
{
|
||||
fprintf(stderr, "%d layout check(s) failed\n", failures);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("All layout checks passed\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
32
test/snippets/syscall_clock_nanosleep.c
Normal file
32
test/snippets/syscall_clock_nanosleep.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Test raw syscall(__NR_clock_nanosleep): relative and TIMER_ABSTIME */
|
||||
#ifdef __NR_clock_nanosleep
|
||||
struct timespec start_fake, end_fake_rel, end_fake_abs;
|
||||
struct timespec req_rel = {0, 200 * 1000 * 1000}; /* 200ms fake sleep */
|
||||
struct timespec req_abs;
|
||||
long ret;
|
||||
|
||||
/* Capture starting time (fake view) */
|
||||
clock_gettime(CLOCK_REALTIME, &start_fake);
|
||||
|
||||
/* Relative sleep via syscall */
|
||||
ret = syscall(__NR_clock_nanosleep, CLOCK_REALTIME, 0, &req_rel, NULL);
|
||||
clock_gettime(CLOCK_REALTIME, &end_fake_rel);
|
||||
|
||||
/* Absolute sleep target: 300ms after start_fake *./test_variable_data.sh */
|
||||
req_abs.tv_sec = start_fake.tv_sec;
|
||||
req_abs.tv_nsec = start_fake.tv_nsec + 300 * 1000 * 1000;
|
||||
if (req_abs.tv_nsec >= 1000000000) { req_abs.tv_sec += 1; req_abs.tv_nsec -= 1000000000; }
|
||||
|
||||
/* Absolute sleep via syscall */
|
||||
ret = syscall(__NR_clock_nanosleep, CLOCK_REALTIME, TIMER_ABSTIME, &req_abs, NULL);
|
||||
clock_gettime(CLOCK_REALTIME, &end_fake_abs);
|
||||
|
||||
/* Report durations (fake view). */
|
||||
long rel_ns = (end_fake_rel.tv_sec - start_fake.tv_sec) * 1000000000L + (end_fake_rel.tv_nsec - start_fake.tv_nsec);
|
||||
long abs_ns = (end_fake_abs.tv_sec - start_fake.tv_sec) * 1000000000L + (end_fake_abs.tv_nsec - start_fake.tv_nsec);
|
||||
printf("[%s] syscall(clock_nanosleep) relative 200ms -> ~%ld ns, absolute +300ms -> ~%ld ns\n", where, rel_ns, abs_ns);
|
||||
return ret;
|
||||
#else
|
||||
printf("[%s] __NR_clock_nanosleep not defined on this platform\n", where);
|
||||
#endif
|
||||
|
||||
1
test/snippets/syscall_clock_nanosleep.variable
Normal file
1
test/snippets/syscall_clock_nanosleep.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME 2020-01-01 00:00:00
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! /bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# testframe.sh DIR
|
||||
# bare-bones testing framework.
|
||||
# run the test suites in the given DIR;
|
||||
|
||||
209
test/timetest.c
209
test/timetest.c
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
@@ -39,11 +40,131 @@
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <utime.h>
|
||||
|
||||
#define MONO_FIX_TIMEOUT_SECONDS 1
|
||||
#define MONO_FIX_TOLERANCE_SECONDS 0.25 // Increased tolerance slightly for CI environments
|
||||
#define MONO_FIX_LOWER_BOUND (MONO_FIX_TIMEOUT_SECONDS - MONO_FIX_TOLERANCE_SECONDS)
|
||||
#define MONO_FIX_UPPER_BOUND (MONO_FIX_TIMEOUT_SECONDS + MONO_FIX_TOLERANCE_SECONDS)
|
||||
|
||||
#define VERBOSE 0
|
||||
|
||||
#define SIG SIGUSR1
|
||||
|
||||
#ifdef __ARM_ARCH
|
||||
static int fake_monotonic_clock = 0;
|
||||
#else
|
||||
static int fake_monotonic_clock = 1;
|
||||
#endif
|
||||
|
||||
static void test_utime_now(void)
|
||||
{
|
||||
char path[] = "/tmp/libfaketime-utime-XXXXXX";
|
||||
int fd;
|
||||
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror("mkstemp");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (utime(path, NULL) == -1)
|
||||
{
|
||||
perror("utime(NULL)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (utimes(path, NULL) == -1)
|
||||
{
|
||||
perror("utimes(NULL)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (close(fd) == -1)
|
||||
{
|
||||
perror("close");
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unlink(path) == -1)
|
||||
{
|
||||
perror("unlink");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("utime()/utimes(): NOW handling passed\n");
|
||||
}
|
||||
|
||||
static void test_utimens_now(void)
|
||||
{
|
||||
char path[] = "/tmp/libfaketime-utimensat-XXXXXX";
|
||||
struct timespec now_times[2];
|
||||
int fd;
|
||||
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror("mkstemp");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (utimensat(AT_FDCWD, path, NULL, 0) == -1)
|
||||
{
|
||||
perror("utimensat(NULL)");
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (futimens(fd, NULL) == -1)
|
||||
{
|
||||
perror("futimens(NULL)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
now_times[0].tv_sec = now_times[1].tv_sec = 0;
|
||||
now_times[0].tv_nsec = now_times[1].tv_nsec = UTIME_NOW;
|
||||
|
||||
if (utimensat(AT_FDCWD, path, now_times, 0) == -1)
|
||||
{
|
||||
perror("utimensat(UTIME_NOW)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (futimens(fd, now_times) == -1)
|
||||
{
|
||||
perror("futimens(UTIME_NOW)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (close(fd) == -1)
|
||||
{
|
||||
perror("close");
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unlink(path) == -1)
|
||||
{
|
||||
perror("unlink");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("utimensat()/futimens(): NOW handling passed\n");
|
||||
}
|
||||
|
||||
static void
|
||||
handler(int sig, siginfo_t *si, void *uc)
|
||||
{
|
||||
@@ -57,36 +178,53 @@ handler(int sig, siginfo_t *si, void *uc)
|
||||
}
|
||||
}
|
||||
|
||||
static void get_fake_monotonic_setting(int* current_value)
|
||||
{
|
||||
char *tmp_env;
|
||||
if ((tmp_env = getenv("FAKETIME_DONT_FAKE_MONOTONIC")) != NULL
|
||||
|| (tmp_env = getenv("DONT_FAKE_MONOTONIC")) != NULL)
|
||||
{
|
||||
if (0 == strcmp(tmp_env, "1"))
|
||||
{
|
||||
(*current_value) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*current_value) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* pthread_test(void* args)
|
||||
{
|
||||
pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t fake_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t fake_cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
pthread_cond_t monotonic_cond;
|
||||
pthread_condattr_t attr;
|
||||
|
||||
struct timespec timeToWait, now;
|
||||
struct timespec time_to_wait, now;
|
||||
int rt;
|
||||
|
||||
args = args; // silence compiler warning about unused argument
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
timeToWait.tv_sec = now.tv_sec+1;
|
||||
timeToWait.tv_nsec = now.tv_nsec;
|
||||
time_to_wait.tv_sec = now.tv_sec+1;
|
||||
time_to_wait.tv_nsec = now.tv_nsec;
|
||||
|
||||
printf("pthread_cond_timedwait: CLOCK_REALTIME test\n");
|
||||
printf("(Intentionally sleeping 1 second...)\n");
|
||||
fflush(stdout);
|
||||
|
||||
pthread_mutex_lock(&fakeMutex);
|
||||
rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
|
||||
pthread_mutex_lock(&fake_mtx);
|
||||
rt = pthread_cond_timedwait(&fake_cond, &fake_mtx, &time_to_wait);
|
||||
if (rt != ETIMEDOUT)
|
||||
{
|
||||
printf("pthread_cond_timedwait failed\n");
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
|
||||
|
||||
pthread_condattr_init(&attr);
|
||||
@@ -94,24 +232,63 @@ void* pthread_test(void* args)
|
||||
pthread_cond_init(&monotonic_cond, &attr);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
timeToWait.tv_sec = now.tv_sec+1;
|
||||
timeToWait.tv_nsec = now.tv_nsec;
|
||||
time_to_wait.tv_sec = now.tv_sec+1;
|
||||
time_to_wait.tv_nsec = now.tv_nsec;
|
||||
|
||||
printf("pthread_cond_timedwait: CLOCK_MONOTONIC test\n");
|
||||
printf("(Intentionally sleeping 1 second...)\n");
|
||||
printf("(If this test hangs for more than a few seconds, please report\n your glibc version and system details as FORCE_MONOTONIC_FIX\n issue at https://github.com/wolfcw/libfaketime)\n");
|
||||
fflush(stdout);
|
||||
|
||||
pthread_mutex_lock(&fakeMutex);
|
||||
rt = pthread_cond_timedwait(&monotonic_cond, &fakeMutex, &timeToWait);
|
||||
pthread_mutex_lock(&fake_mtx);
|
||||
rt = pthread_cond_timedwait(&monotonic_cond, &fake_mtx, &time_to_wait);
|
||||
if (rt != ETIMEDOUT)
|
||||
{
|
||||
printf("pthread_cond_timedwait failed\n");
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
|
||||
get_fake_monotonic_setting(&fake_monotonic_clock);
|
||||
if (!fake_monotonic_clock)
|
||||
{
|
||||
printf("pthread_cond_timedwait: using real CLOCK_MONOTONIC test\n");
|
||||
struct timespec mono_after_wait;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
time_to_wait.tv_sec = now.tv_sec + MONO_FIX_TIMEOUT_SECONDS;
|
||||
time_to_wait.tv_nsec = now.tv_nsec;
|
||||
|
||||
struct timespec current_real_time;
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_real_time);
|
||||
|
||||
struct timespec new_real_time = current_real_time;
|
||||
new_real_time.tv_sec += 3600; // Advance by one hour
|
||||
|
||||
clock_settime(CLOCK_REALTIME, &new_real_time);
|
||||
|
||||
pthread_mutex_lock(&fake_mtx);
|
||||
|
||||
rt = pthread_cond_timedwait(&monotonic_cond, &fake_mtx, &time_to_wait);
|
||||
clock_gettime(CLOCK_MONOTONIC, &mono_after_wait);
|
||||
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
|
||||
double elapsed_wall_time = (mono_after_wait.tv_sec - now.tv_sec) +
|
||||
(mono_after_wait.tv_nsec - now.tv_nsec) / 1000000000.0;
|
||||
|
||||
if (rt == ETIMEDOUT && (elapsed_wall_time >= MONO_FIX_LOWER_BOUND && elapsed_wall_time <= MONO_FIX_UPPER_BOUND))
|
||||
{
|
||||
printf("pthread_cond_timedwait with real CLOCK_MONOTONIC passed: elapsed time = %.2f seconds\n", elapsed_wall_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("pthread_cond_timedwait with real CLOCK_MONOTONIC FAILED: elapsed time = %.2f seconds, return code = %d\n", elapsed_wall_time, rt);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
pthread_cond_destroy(&monotonic_cond);
|
||||
|
||||
return NULL;
|
||||
@@ -237,6 +414,8 @@ printf("%s", 0 == 1 ? argv[0] : "");
|
||||
printf("gettimeofday() : Current date and time: %s", ctime(&tv.tv_sec));
|
||||
|
||||
#ifndef __APPLE__
|
||||
test_utime_now();
|
||||
test_utimens_now();
|
||||
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
|
||||
{
|
||||
perror("sigprocmask");
|
||||
|
||||
Reference in New Issue
Block a user