Skip to content

Commit

Permalink
Use the same global clock for all processes spawned by faketime -f '.…
Browse files Browse the repository at this point in the history
….. iN'

In case the clock is set to advance with each faked time(), etc. call, the
counter uses the same clock counter stored in shared memory and protected
by a semaphore.
  • Loading branch information
rbalint committed Aug 19, 2013
1 parent bfd4b9f commit d5cd706
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 14 deletions.
3 changes: 2 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ FAKETIME="+1y i2,0"

will make the clock step two seconds per each time(), etc. call, running
completely independently from the system clock. It helps running programs
with some determinism.
with some determinism. In this single case all spawned processes will use
the same global clock without restaring it at the start of each process.

For testing, your should run a command like

Expand Down
4 changes: 3 additions & 1 deletion man/faketime.1
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ 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 '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'
In this single case all spawned processes will use the same global clock without restaring 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)
.fi
.SH ADVANCED TIMESTAMP FORMAT
Expand Down
3 changes: 2 additions & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ PREFIX ?= /usr/local

CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -DFAKE_INTERNAL_CALLS -fPIC -DPOSIX_REALTIME -DLIMITEDFAKING -DSPAWNSUPPORT -DPREFIX='"'$(PREFIX)'"'
LIB_LDFLAGS += -shared
LDFLAGS += -lrt
LDADD += -ldl -lm -lpthread

SRC = libfaketime.c
Expand All @@ -71,7 +72,7 @@ ${LIBS_OBJ}: libfaketime.c
${CC} -o $@ -c ${CFLAGS} ${EXTRA_FLAGS} $<

%.so.${SONAME}: %.o
${CC} -o $@ -Wl,-soname,$@ ${LIB_LDFLAGS} $< ${LDADD}
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ${LDADD}

clean:
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS}
Expand Down
110 changes: 103 additions & 7 deletions src/faketime.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <semaphore.h>

const char version[] = "0.8";

Expand All @@ -35,6 +40,12 @@ static const char *date_cmd = "gdate";
static const char *date_cmd = "date";
#endif

#define PATH_BUFSIZE 4096

/* semaphore and shared memory names */
char sem_name[PATH_BUFSIZE] = {0}, shm_name[PATH_BUFSIZE] = {0};


void usage(const char *name) {
printf("\n");
printf("Usage: %s [switches] <timestamp> <program with arguments>\n", name);
Expand All @@ -53,14 +64,27 @@ void usage(const char *name) {
printf("%s '2008-12-24 08:15:42' /bin/date\n", name);
printf("%s -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n", name);
printf("%s -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n", name);
printf("%s -f '+2,5y i2,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n", name);
printf("%s -f '+2,5y i2,0' /bin/bash -c 'date; while true; do date; sleep 1 ; done'\n", name);
printf("In this single case all spawned processes will use the same global clock\n");
printf("without restaring it at the start of each process.\n\n");
printf("(Please note that it depends on your locale settings whether . or , has to be used for fractions)\n");
printf("\n");

}

/** Clean up shared objects */
static void cleanup_shobjs () {
if (-1 == sem_unlink(sem_name)) {
perror("sem_unlink");
}
if (-1 == shm_unlink(shm_name)) {
perror("shm_unlink");
}
}

int main (int argc, char **argv)
{
pid_t child_pid;
int curr_opt = 1;
bool use_mt = false, use_direct = false;
int pfds[2];
Expand Down Expand Up @@ -101,10 +125,9 @@ int main (int argc, char **argv)
if (!use_direct) {
// TODO get seconds
pipe(pfds);
pid_t pid;
int ret = EXIT_SUCCESS;

if (0 == (pid = fork())) {
if (0 == (child_pid = fork())) {
close(1); /* close normal stdout */
dup(pfds[1]); /* make stdout same as pfds[1] */
close(pfds[0]); /* we don't need this */
Expand All @@ -116,7 +139,7 @@ int main (int argc, char **argv)
char buf[256] = {0}; /* e will have way less than 256 digits */
close(pfds[1]); /* we won't write to this */
read(pfds[0], buf, 256);
waitpid(pid, &ret, 0);
waitpid(child_pid, &ret, 0);
if (ret != EXIT_SUCCESS) {
printf("Error: Timestamp to fake not recognized, please re-try with a "
"different timestamp.\n");
Expand All @@ -134,6 +157,70 @@ int main (int argc, char **argv)
/* we just consumed the timestamp option */
curr_opt++;

{
/* create semaphores and shared memory */
int shm_fd;
sem_t *sem;
uint64_t *ticks;
char shared_objs[PATH_BUFSIZE];

snprintf(sem_name, PATH_BUFSIZE -1 ,"/faketime_sem_%d", getpid());
snprintf(shm_name, PATH_BUFSIZE -1 ,"/faketime_shm_%d", getpid());

if (SEM_FAILED == (sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1))) {
perror("sem_open");
exit(EXIT_FAILURE);
}

/* create shm */
if (-1 == (shm_fd = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR))) {
perror("shm_open");
if (-1 == sem_unlink(argv[2])) {
perror("sem_unlink");
}
exit(EXIT_FAILURE);
}

/* set shm size */
if (-1 == ftruncate(shm_fd, sizeof(uint64_t))) {
perror("ftruncate");
cleanup_shobjs();
exit(EXIT_FAILURE);
}

/* map shm */
if (MAP_FAILED == (ticks = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE,
MAP_SHARED, shm_fd, 0))) {
perror("mmap");
cleanup_shobjs();
exit(EXIT_FAILURE);
}

if (sem_wait(sem) == -1) {
perror("sem_wait");
cleanup_shobjs();
exit(EXIT_FAILURE);
}

/* init elapsed time ticks to zero */
*ticks = 0;
if (-1 == munmap(ticks, (sizeof(uint64_t)))) {
perror("munmap");
cleanup_shobjs();
exit(EXIT_FAILURE);
}

if (sem_post(sem) == -1) {
perror("semop");
cleanup_shobjs();
exit(EXIT_FAILURE);
}

snprintf(shared_objs, PATH_BUFSIZE, "%s %s", sem_name, shm_name);
setenv("FAKETIME_SHARED", shared_objs, true);

}

{
char *ftpl_path;
#ifdef __APPLE__
Expand All @@ -158,8 +245,17 @@ int main (int argc, char **argv)
}
#endif
}
if (EXIT_SUCCESS != execvp(argv[curr_opt], &argv[curr_opt])) {
perror("Running specified command failed");
exit(EXIT_FAILURE);

/* run command and clean up shared objects */
if (0 == (child_pid = fork())) {
if (EXIT_SUCCESS != execvp(argv[curr_opt], &argv[curr_opt])) {
perror("Running specified command failed");
exit(EXIT_FAILURE);
}
} else {
int ret;
waitpid(child_pid, &ret, 0);
cleanup_shobjs();
exit(ret);
}
}
76 changes: 72 additions & 4 deletions src/libfaketime.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>

/* pthread-handling contributed by David North, TDI in version 0.7 */
#ifdef PTHREAD
Expand Down Expand Up @@ -99,8 +104,71 @@ static int fake_stat_disabled = 0;

/**
* When advancing time linearly with each time(), etc. call, the calls are
* counted here */
static long int ticks = 0;
* counted in shared memory pointed at by ticks and protected by ticks_sem
* semaphore */
static sem_t *ticks_sem = NULL;
static uint64_t *ticks = NULL;


void ft_init (void) __attribute__ ((constructor));
void ft_cleanup (void) __attribute__ ((destructor));

void ft_init (void)
{
int ticks_shm_fd;
char sem_name[256], shm_name[256], *ft_shared = getenv("FAKETIME_SHARED");
if (ft_shared != NULL) {
if (sscanf(ft_shared, "%255s %255s", sem_name, shm_name) < 2 ) {
printf("Error parsing semaphor name and shared memory id from string: %s", ft_shared);
exit(1);
}

if (SEM_FAILED == (ticks_sem = sem_open(sem_name, 0))) {
perror("sem_open");
exit(1);
}

if (-1 == (ticks_shm_fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR))) {
perror("shm_open");
exit(1);
}
if (MAP_FAILED == (ticks = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE,
MAP_SHARED, ticks_shm_fd, 0))) {
perror("mmap");
exit(1);
}
}
}

void ft_cleanup (void)
{
/* detach from shared memory */
munmap(ticks, sizeof(uint64_t));
sem_close(ticks_sem);
}

static time_t next_time(double ticklen)
{
time_t ret = 0;

if (ticks_sem != NULL) {
/* lock */
if (sem_wait(ticks_sem) == -1) {
perror("sem_wait");
exit(1);
}

/* calculate and update elapsed time */
ret = ticklen * (*ticks)++;
/* unlock */
if (sem_post(ticks_sem) == -1) {
perror("sem_post");
exit(1);
}
}
return ret;

}

/* Contributed by Philipp Hachtmann in version 0.6 */
int __xstat (int ver, const char *path, struct stat *buf) {
Expand Down Expand Up @@ -860,7 +928,7 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER;
*time_tptr += (long) timeadj;
} else if (NULL != (tmp_time_fmt = strchr(user_faked_time, 'i'))) {
/* increment time with every time() call*/
*time_tptr = atof(tmp_time_fmt + 1) * ticks++;
*time_tptr += next_time(atof(tmp_time_fmt + 1));
}

*time_tptr += (long) frac_user_offset;
Expand All @@ -884,7 +952,7 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER;
*time_tptr += (long) timeadj;
} else if (NULL != (tmp_time_fmt = strchr(user_faked_time, 'i'))) {
/* increment time with every time() call*/
*time_tptr = atof(tmp_time_fmt + 1) * ticks++;
*time_tptr += next_time(atof(tmp_time_fmt + 1));
}

*time_tptr += user_offset;
Expand Down

0 comments on commit d5cd706

Please sign in to comment.