Merge pull request #138 from mliertzer/fake_pthread

Implement fix for pthread_cond_timedwait
This commit is contained in:
Wolfgang Hommel
2018-01-02 12:23:57 +01:00
committed by GitHub
8 changed files with 1503 additions and 28 deletions

View File

@@ -9,12 +9,9 @@
# NO_ATFILE
# - Disables support for the fstatat() group of functions
#
# PTHREAD
# - Define this to enable multithreading support.
#
# PTHREAD_SINGLETHREADED_TIME
# - Define this if you want to single-thread time() ... there ARE
# possibile caching side-effects in a multithreaded environment
# possible caching side-effects in a multithreaded environment
# without this, but the performance impact may require you to
# try it unsynchronized.
#
@@ -24,17 +21,20 @@
# that make use of low-level system calls, such as Java Virtual
# Machines.
#
# FAKE_SLEEP
# - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
# FAKE_SLEEP
# - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
#
# FAKE_TIMERS
# - Also intercept timer_settime() and timer_gettime()
# FAKE_TIMERS
# - Also intercept timer_settime() and timer_gettime()
#
# MULTI_ARCH
# - If MULTI_ARCH is set, the faketime wrapper program will put a literal
# $LIB into the LD_PRELOAD environment variable it creates, which makes
# ld automatically choose the correct library version to use for the
# target binary. Use for Linux platforms with Multi-Arch support only!
# FAKE_PTHREAD
# - Intercept pthread_cond_timedwait
#
# MULTI_ARCH
# - If MULTI_ARCH is set, the faketime wrapper program will put a literal
# $LIB into the LD_PRELOAD environment variable it creates, which makes
# ld automatically choose the correct library version to use for the
# target binary. Use for Linux platforms with Multi-Arch support only!
#
# * Compilation addition: second libMT target added for building the pthread-
# enabled library as a separate library
@@ -62,7 +62,7 @@ PREFIX ?= /usr/local
LIBDIRNAME ?= /lib/faketime
PLATFORM ?=$(shell uname)
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_STAT -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"'
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"'
ifeq ($(PLATFORM),SunOS)
CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=600
endif
@@ -86,7 +86,7 @@ LIBS = libfaketime.so.${SONAME} libfaketimeMT.so.${SONAME}
all: ${LIBS} ${BINS}
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD -DPTHREAD_SINGLETHREADED_TIME
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME
${LIBS_OBJ}: libfaketime.c
${CC} -o $@ -c ${CFLAGS} ${EXTRA_FLAGS} $<

View File

@@ -41,11 +41,14 @@
#include <netinet/in.h>
#include <limits.h>
#include "uthash.h"
#include "time_ops.h"
#include "faketime_common.h"
/* pthread-handling contributed by David North, TDI in version 0.7 */
#ifdef PTHREAD
#if defined PTHREAD_SINGLETHREADED_TIME || defined FAKE_PTHREAD
#include <pthread.h>
#endif
@@ -140,6 +143,13 @@ static int (*real___ftime) (struct timeb *);
static int (*real___gettimeofday) (struct timeval *, void *);
static int (*real___clock_gettime) (clockid_t clk_id, struct timespec *tp);
#endif
#ifdef FAKE_PTHREAD
static int (*real_pthread_cond_timedwait_225) (pthread_cond_t *, pthread_mutex_t*, struct timespec *);
static int (*real_pthread_cond_timedwait_232) (pthread_cond_t *, pthread_mutex_t*, struct timespec *);
static int (*real_pthread_cond_init_232) (pthread_cond_t *restrict, const pthread_condattr_t *restrict);
static int (*real_pthread_cond_destroy_232) (pthread_cond_t *);
#endif
#ifndef __APPLEOSX__
#ifdef FAKE_TIMERS
static int (*real_timer_settime_22) (int timerid, int flags, const struct itimerspec *new_value,
@@ -255,8 +265,8 @@ enum ft_mode_t {FT_FREEZE, FT_START_AT, FT_NOOP} ft_mode = FT_FREEZE;
/* Time to fake is not provided through FAKETIME env. var. */
static bool parse_config_file = true;
void ft_cleanup (void) __attribute__ ((destructor));
void ftpl_init (void) __attribute__ ((constructor));
static void ft_cleanup (void) __attribute__ ((destructor));
static void ftpl_init (void) __attribute__ ((constructor));
/*
@@ -299,7 +309,7 @@ static void ft_shm_init (void)
}
}
void ft_cleanup (void)
static void ft_cleanup (void)
{
/* detach from shared memory */
if (ft_shared != NULL)
@@ -1128,7 +1138,7 @@ typedef union {
/*
* Faketime's function implementation's compatibility mode
*/
typedef enum {FT_COMPAT_GLIBC_2_2, FT_COMPAT_GLIBC_2_3_3} ft_lib_compat;
typedef enum {FT_COMPAT_GLIBC_2_2, FT_COMPAT_GLIBC_2_3_3} ft_lib_compat_timer;
/*
@@ -1138,7 +1148,7 @@ typedef enum {FT_COMPAT_GLIBC_2_2, FT_COMPAT_GLIBC_2_3_3} ft_lib_compat;
static int
timer_settime_common(timer_t_or_int timerid, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value, ft_lib_compat compat)
struct itimerspec *old_value, ft_lib_compat_timer compat)
{
int result;
struct itimerspec new_real;
@@ -1293,7 +1303,7 @@ int timer_settime_233(timer_t timerid, int flags,
* Faked timer_gettime()
* Does not affect timer speed when stepping clock with each time() call.
*/
int timer_gettime_common(timer_t_or_int timerid, struct itimerspec *curr_value, ft_lib_compat compat)
int timer_gettime_common(timer_t_or_int timerid, struct itimerspec *curr_value, ft_lib_compat_timer compat)
{
int result;
@@ -1521,7 +1531,14 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
if (result == -1) return result; /* original function failed */
/* pass the real current time to our faking version, overwriting it */
if (fake_monotonic_clock || clk_id != CLOCK_MONOTONIC)
if (fake_monotonic_clock || (clk_id != CLOCK_MONOTONIC && clk_id != CLOCK_MONOTONIC_RAW
#ifdef CLOCK_MONOTONIC_COARSE
&& clk_id != CLOCK_MONOTONIC_COARSE
#endif
#ifdef CLOCK_BOOTTIME
&& clk_id != CLOCK_BOOTTIME
#endif
))
{
result = fake_clock_gettime(clk_id, tp);
}
@@ -1636,7 +1653,7 @@ parse_modifiers:
* =======================================================================
*/
void ftpl_init(void)
static void ftpl_init(void)
{
char *tmp_env;
bool dont_fake_final;
@@ -1674,6 +1691,26 @@ void ftpl_init(void)
real___gettimeofday = dlsym(RTLD_NEXT, "__gettimeofday");
real___clock_gettime = dlsym(RTLD_NEXT, "__clock_gettime");
#endif
#ifdef FAKE_PTHREAD
real_pthread_cond_timedwait_225 = dlvsym(RTLD_NEXT, "pthread_cond_timedwait", "GLIBC_2.2.5");
real_pthread_cond_timedwait_232 = dlvsym(RTLD_NEXT, "pthread_cond_timedwait", "GLIBC_2.3.2");
real_pthread_cond_init_232 = dlvsym(RTLD_NEXT, "pthread_cond_init", "GLIBC_2.3.2");
real_pthread_cond_destroy_232 = dlvsym(RTLD_NEXT, "pthread_cond_destroy", "GLIBC_2.3.2");
if (NULL == real_pthread_cond_timedwait_232)
{
real_pthread_cond_timedwait_232 = dlsym(RTLD_NEXT, "pthread_cond_timedwait");
}
if (NULL == real_pthread_cond_init_232)
{
real_pthread_cond_init_232 = dlsym(RTLD_NEXT, "pthread_cond_init");
}
if (NULL == real_pthread_cond_destroy_232)
{
real_pthread_cond_destroy_232 = dlsym(RTLD_NEXT, "pthread_cond_destroy");
}
#endif
#ifdef __APPLEOSX__
real_clock_get_time = dlsym(RTLD_NEXT, "clock_get_time");
real_clock_gettime = apple_clock_gettime;
@@ -2327,7 +2364,15 @@ int __clock_gettime(clockid_t clk_id, struct timespec *tp)
if (result == -1) return result; /* original function failed */
/* pass the real current time to our faking version, overwriting it */
if (fake_monotonic_clock || clk_id != CLOCK_MONOTONIC)
if (fake_monotonic_clock || (clk_id != CLOCK_MONOTONIC && clk_id != CLOCK_MONOTONIC_RAW
#ifdef CLOCK_MONOTONIC_COARSE
&& clk_id != CLOCK_MONOTONIC_COARSE
#endif
#ifdef CLOCK_BOOTTIME
&& clk_id != CLOCK_BOOTTIME
#endif
))
{
result = fake_clock_gettime(clk_id, tp);
}
@@ -2394,6 +2439,150 @@ int __ftime(struct timeb *tb)
#endif
/*
* =======================================================================
* Faked pthread_cond_timedwait === FAKE(pthread)
* =======================================================================
*/
/* pthread_cond_timedwait
The specified absolute time in pthread_cond_timedwait is directly
passed to the kernel via the futex syscall. The kernel, however,
does not know about the fake time. In 99.9% of cases, the time
until this function should wait is calculated by an application
relatively to the current time, which has been faked in the
application. Hence, we should convert the waiting time back to real
time.
pthread_cond_timedwait in GLIBC_2_2_5 only supports
CLOCK_REALTIME. Since the init and destroy functions are not
redefined for GLIBC_2_2_5, a corresponding cond will never be
added to monotonic_conds and hence the correct branch will
always be taken.
*/
#ifdef FAKE_PTHREAD
typedef enum {FT_COMPAT_GLIBC_2_2_5, FT_COMPAT_GLIBC_2_3_2} ft_lib_compat_pthread;
struct pthread_cond_monotonic {
pthread_cond_t *ptr;
UT_hash_handle hh;
};
static struct pthread_cond_monotonic *monotonic_conds = NULL;
int pthread_cond_init_232(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr)
{
clockid_t clock_id;
int result;
result = real_pthread_cond_init_232(cond, attr);
if (result != 0 || attr == NULL)
return result;
pthread_condattr_getclock(attr, &clock_id);
if (clock_id == CLOCK_MONOTONIC) {
struct pthread_cond_monotonic *e = (struct pthread_cond_monotonic*)malloc(sizeof(struct pthread_cond_monotonic));
e->ptr = cond;
HASH_ADD_PTR(monotonic_conds, ptr, e);
}
return result;
}
int pthread_cond_destroy_232(pthread_cond_t *cond)
{
struct pthread_cond_monotonic* e;
HASH_FIND_PTR(monotonic_conds, &cond, e);
if (e) {
HASH_DEL(monotonic_conds, e);
free(e);
}
return real_pthread_cond_destroy_232(cond);
}
int pthread_cond_timedwait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime, ft_lib_compat_pthread compat)
{
struct timespec tp, tdiff_actual, realtime, faketime;
struct timespec *tf = NULL;
struct pthread_cond_monotonic* e;
clockid_t clk_id;
int result;
if (abstime != NULL)
{
HASH_FIND_PTR(monotonic_conds, &cond, e);
if (e != NULL)
clk_id = CLOCK_MONOTONIC;
else
clk_id = CLOCK_REALTIME;
DONT_FAKE_TIME(result = (*real_clock_gettime)(clk_id, &realtime));
if (result == -1)
{
return EINVAL;
}
faketime = realtime;
(void)fake_clock_gettime(clk_id, &faketime);
timespecsub(abstime, &faketime, &tp);
if (user_rate_set)
{
timespecmul(&tp, 1.0 / user_rate, &tdiff_actual);
}
else
{
tdiff_actual = tp;
}
/* For CLOCK_MONOTONIC, pthread_cond_timedwait uses clock_gettime
internally to calculate the appropriate duration for the
waiting time. This already uses the faked functions, hence, the
fake time needs to be passed to pthread_cond_timedwait for
CLOCK_MONOTONIC. */
if(clk_id == CLOCK_MONOTONIC)
timespecadd(&faketime, &tdiff_actual, &tp);
else
timespecadd(&realtime, &tdiff_actual, &tp);
tf = &tp;
}
switch (compat) {
case FT_COMPAT_GLIBC_2_3_2:
result = real_pthread_cond_timedwait_232(cond, mutex, tf);
break;
case FT_COMPAT_GLIBC_2_2_5:
result = real_pthread_cond_timedwait_225(cond, mutex, tf);
break;
}
return result;
}
int pthread_cond_timedwait_225(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
{
return pthread_cond_timedwait_common(cond, mutex, abstime, FT_COMPAT_GLIBC_2_2_5);
}
int pthread_cond_timedwait_232(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
{
return pthread_cond_timedwait_common(cond, mutex, abstime, FT_COMPAT_GLIBC_2_3_2);
}
__asm__(".symver pthread_cond_timedwait_225, pthread_cond_timedwait@GLIBC_2.2.5");
__asm__(".symver pthread_cond_timedwait_232, pthread_cond_timedwait@@GLIBC_2.3.2");
__asm__(".symver pthread_cond_init_232, pthread_cond_init@@GLIBC_2.3.2");
__asm__(".symver pthread_cond_destroy_232, pthread_cond_destroy@@GLIBC_2.3.2");
#endif
/*
* Editor modelines
*

View File

@@ -4,7 +4,18 @@ GLIBC_2.2 {
timer_gettime; timer_settime;
local: timer_settime_*; timer_gettime_*;
};
GLIBC_2.3.3 {
# Changed timer_t.
timer_gettime; timer_settime;
} GLIBC_2.2;
GLIBC_2.2.5 {
global: pthread_cond_timedwait;
local: pthread_cond_timedwait_*; pthread_cond_init_*; pthread_cond_destroy*;
};
GLIBC_2.3.2 {
pthread_cond_timedwait; pthread_cond_init; pthread_cond_destroy;
} GLIBC_2.2.5;

1208
src/uthash.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
CC = gcc
CFLAGS = -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra
LDFLAGS = -lrt
LDFLAGS = -lrt -lpthread
SRC = timetest.c
OBJ = ${SRC:.c=.o}

View File

@@ -47,7 +47,7 @@ echo
echo "============================================================================="
echo
echo "Running the test program with 10 days postive offset specified, and sped up 2 times"
echo "Running the test program with 10 days positive offset specified, and sped up 2 times"
echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"+10d x2\" ./timetest"
LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="+10d x2" NO_FAKE_STAT=1 ./timetest
echo

View File

@@ -33,7 +33,7 @@ echo "FAKETIME=\"-10d\" NO_FAKE_STAT=1 ./timetest"
FAKETIME="-10d" NO_FAKE_STAT=1 ./timetest
echo
echo "Running the test program with 10 days postive offset specified, and sped up 2 times"
echo "Running the test program with 10 days positive offset specified, and sped up 2 times"
echo "FAKETIME=\"+10d x2\" ./timetest"
FAKETIME="+10d x2" NO_FAKE_STAT=1 ./timetest
echo

View File

@@ -18,6 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
@@ -31,6 +32,8 @@
#endif
#ifndef __APPLE__
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#define VERBOSE 0
@@ -49,6 +52,64 @@ handler(int sig, siginfo_t *si, void *uc)
printf("Caught signal %d\n", sig);
}
}
void* pthread_test(void* args)
{
pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
pthread_cond_t monotonic_cond;
pthread_condattr_t attr;
struct timespec timeToWait, 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;
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);
if (rt != ETIMEDOUT)
{
printf("pthread_cond_timedwait failed\n");
exit(EXIT_FAILURE);
}
pthread_mutex_unlock(&fakeMutex);
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&monotonic_cond, &attr);
clock_gettime(CLOCK_MONOTONIC, &now);
timeToWait.tv_sec = now.tv_sec+1;
timeToWait.tv_nsec = now.tv_nsec;
printf("pthread_cond_timedwait: CLOCK_MONOTONIC test\n");
printf("(Intentionally sleeping 1 second...)\n");
fflush(stdout);
pthread_mutex_lock(&fakeMutex);
rt = pthread_cond_timedwait(&monotonic_cond, &fakeMutex, &timeToWait);
if (rt != ETIMEDOUT)
{
printf("pthread_cond_timedwait failed\n");
exit(EXIT_FAILURE);
}
pthread_mutex_unlock(&fakeMutex);
pthread_cond_destroy(&monotonic_cond);
return NULL;
}
#endif
int main (int argc, char **argv)
@@ -69,6 +130,12 @@ int main (int argc, char **argv)
#endif
#ifndef __APPLE__
pthread_t thread;
void *ret;
pthread_create(&thread, NULL, pthread_test, NULL);
pthread_join(thread, &ret);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);