Skip to content

Commit

Permalink
timer: Introduce osmo_clock_gettime to override clock_gettime
Browse files Browse the repository at this point in the history
Change-Id: I5bebc6e01fc9d238065bc2517058f0ba85620349
  • Loading branch information
pespin authored and laf0rge committed Mar 1, 2018
1 parent 721aa6d commit 87fade8
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 4 deletions.
6 changes: 6 additions & 0 deletions include/osmocom/core/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#pragma once

#include <sys/time.h>
#include <time.h>
#include <stdbool.h>

#include <osmocom/core/linuxlist.h>
Expand Down Expand Up @@ -87,6 +88,7 @@ int osmo_timers_update(void);
int osmo_timers_check(void);

int osmo_gettimeofday(struct timeval *tv, struct timezone *tz);
int osmo_clock_gettime(clockid_t clk_id, struct timespec *tp);

/*
* timer override
Expand All @@ -96,4 +98,8 @@ extern bool osmo_gettimeofday_override;
extern struct timeval osmo_gettimeofday_override_time;
void osmo_gettimeofday_override_add(time_t secs, suseconds_t usecs);

void osmo_clock_override_enable(clockid_t clk_id, bool enable);
void osmo_clock_override_add(clockid_t clk_id, time_t secs, long nsecs);
struct timespec *osmo_clock_override_gettimespec(clockid_t clk_id);

This comment has been minimized.

Copy link
@Mic92

Mic92 Dec 8, 2018

@pespin this broke the macOS build: https://github.com/NixOS/nixpkgs/pull/51742/checks?check_run_id=39071151

A fix would be:

#if defined(__APPLE__)
typedef int clockid_t;
#endif

This comment has been minimized.

Copy link
@pespin

pespin Dec 9, 2018

Author Contributor

Hi, thanks for reporting the issue. I created a ticket in https://osmocom.org/issues/3722 to track the issue. Please register and fill it there next time if you can, otherwise information may be lost. I submitted a patch to gerrit with a possible fix, please give it a try and provide feedback whether it solves the issue. Could you provide more information on the system your are building with? which version of MacOS? which compiler? Thanks!

This comment has been minimized.

Copy link
@Mic92

Mic92 Dec 9, 2018

I beefed up the issue a little bit.


/*! @} */
3 changes: 2 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ endif
lib_LTLIBRARIES = libosmocore.la

libosmocore_la_LIBADD = $(BACKTRACE_LIB) $(TALLOC_LIBS)
libosmocore_la_SOURCES = timer.c timer_gettimeofday.c select.c signal.c msgb.c bits.c \
libosmocore_la_SOURCES = timer.c timer_gettimeofday.c timer_clockgettime.c \
select.c signal.c msgb.c bits.c \
bitvec.c bitcomp.c counter.c fsm.c \
write_queue.c utils.c socket.c \
logging.c logging_syslog.c logging_gsmtap.c rate_ctr.c \
Expand Down
138 changes: 138 additions & 0 deletions src/timer_clockgettime.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Authors: Pau Espin Pedrol <pespin@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/

/*! \addtogroup timer
* @{
* \file timer_clockgettime.c
* Overriding Time: osmo_clock_gettime()
* - Useful to write and reproduce tests that depend on specific time
* factors. This API allows to fake the timespec provided by `clock_gettime()`
* by using a small shim osmo_clock_gettime().
* - Choose the clock you want to override, for instance CLOCK_MONOTONIC.
* - If the clock override is disabled (default) for a given clock,
* osmo_clock_gettime() will do the same as regular `clock_gettime()`.
* - If you want osmo_clock_gettime() to provide a specific time, you must
* enable time override with osmo_clock_override_enable(),
* then set a pointer to the timespec storing the fake time for that
* specific clock (`struct timespec *ts =
* osmo_clock_override_gettimespec()`) and set it as
* desired. Next time osmo_clock_gettime() is called, it will return the
* values previously set through the ts pointer.
* - A helper osmo_clock_override_add() is provided to increment a given
* overriden clock with a specific amount of time.
*/

/*! \file timer_clockgettime.c
*/

#include <stdlib.h>
#include <stdbool.h>
#include <sys/time.h>
#include <time.h>

#include <osmocom/core/timer_compat.h>

/*! An internal structure to handle overriden time for each clock type. */
struct fakeclock {
bool override;
struct timespec time;
};

static struct fakeclock realtime;
static struct fakeclock realtime_coarse;
static struct fakeclock mono;
static struct fakeclock mono_coarse;
static struct fakeclock mono_raw;
static struct fakeclock boottime;
static struct fakeclock boottime;
static struct fakeclock proc_cputime_id;
static struct fakeclock th_cputime_id;

static struct fakeclock* clkid_to_fakeclock(clockid_t clk_id)
{
switch(clk_id) {
case CLOCK_REALTIME:
return &realtime;
case CLOCK_REALTIME_COARSE:
return &realtime_coarse;
case CLOCK_MONOTONIC:
return &mono;
case CLOCK_MONOTONIC_COARSE:
return &mono_coarse;
case CLOCK_MONOTONIC_RAW:
return &mono_raw;
case CLOCK_BOOTTIME:
return &boottime;
case CLOCK_PROCESS_CPUTIME_ID:
return &proc_cputime_id;
case CLOCK_THREAD_CPUTIME_ID:
return &th_cputime_id;
default:
return NULL;
}
}

/*! Shim around clock_gettime to be able to set the time manually.
*
* To override, use osmo_clock_override_enable and set the desired
* current time with osmo_clock_gettimespec. */
int osmo_clock_gettime(clockid_t clk_id, struct timespec *tp)
{
struct fakeclock* c = clkid_to_fakeclock(clk_id);
if (!c || !c->override)
return clock_gettime(clk_id, tp);

*tp = c->time;
return 0;
}

/*! Convenience function to enable or disable a specific clock fake time.
*/
void osmo_clock_override_enable(clockid_t clk_id, bool enable)
{
struct fakeclock* c = clkid_to_fakeclock(clk_id);
if (c)
c->override = enable;
}

/*! Convenience function to return a pointer to the timespec handling the
* fake time for clock clk_id. */
struct timespec *osmo_clock_override_gettimespec(clockid_t clk_id)
{
struct fakeclock* c = clkid_to_fakeclock(clk_id);
if (c)
return &c->time;
return NULL;
}

/*! Convenience function to advance the fake time.
*
* Adds the given values to the clock time. */
void osmo_clock_override_add(clockid_t clk_id, time_t secs, long nsecs)
{
struct timespec val = { secs, nsecs };
struct fakeclock* c = clkid_to_fakeclock(clk_id);
if (c)
timespecadd(&c->time, &val, &c->time);
}

/*! @} */
2 changes: 1 addition & 1 deletion src/timer_gettimeofday.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct timeval osmo_gettimeofday_override_time = { 23, 424242 };
* N. B: gettimeofday() is affected by discontinuous jumps in the system time
* (e.g., if the system administrator manually changes the system time).
* Hence this should NEVER be used for elapsed time computation.
* Instead, clock_gettime(CLOCK_MONOTONIC, ..) should be used for that (with similar shim if necessary).
* Instead, osmo_clock_gettime() with CLOCK_MONOTONIC should be used for that.
*/
int osmo_gettimeofday(struct timeval *tv, struct timezone *tz)
{
Expand Down
7 changes: 5 additions & 2 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
coding/coding_test conv/conv_gsm0503_test \
abis/abis_test endian/endian_test sercomm/sercomm_test \
prbs/prbs_test gsm23003/gsm23003_test \
codec/codec_ecu_fr_test
codec/codec_ecu_fr_test timer/clk_override_test

if ENABLE_MSGFILE
check_PROGRAMS += msgfile/msgfile_test
Expand Down Expand Up @@ -122,6 +122,8 @@ sms_sms_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la

timer_timer_test_SOURCES = timer/timer_test.c

timer_clk_override_test_SOURCES = timer/clk_override_test.c

ussd_ussd_test_SOURCES = ussd/ussd_test.c
ussd_ussd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la

Expand Down Expand Up @@ -250,7 +252,8 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
osmo-auc-gen/osmo-auc-gen_test.err \
conv/conv_gsm0503_test.ok endian/endian_test.ok \
sercomm/sercomm_test.ok prbs/prbs_test.ok \
gsm23003/gsm23003_test.ok
gsm23003/gsm23003_test.ok \
timer/clk_override_test.ok

DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c
BUILT_SOURCES = conv/gsm0503_test_vectors.c
Expand Down
6 changes: 6 additions & 0 deletions tests/testsuite.at
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ cat $abs_srcdir/timer/timer_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/timer/timer_test], [0], [expout], [ignore])
AT_CLEANUP

AT_SETUP([clk_override])
AT_KEYWORDS([clk_override])
cat $abs_srcdir/timer/clk_override_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/timer/clk_override_test], [0], [expout], [ignore])
AT_CLEANUP

AT_SETUP([tlv])
AT_KEYWORDS([tlv])
cat $abs_srcdir/tlv/tlv_test.ok > expout
Expand Down
89 changes: 89 additions & 0 deletions tests/timer/clk_override_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* Authors: Holger Hans Peter Freyther <zecke@selfish.org>
* Pablo Neira Ayuso <pablo@gnumonks.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer_compat.h>

int main(int argc, char *argv[])
{

struct timespec ts1 = { 123, 456 }, ts2 = {1, 200};
struct timespec read1, read2, res;
struct timespec *mono;

osmo_clock_gettime(CLOCK_BOOTTIME, &read1);
usleep(500);
osmo_clock_gettime(CLOCK_BOOTTIME, &read2);
if (!timespeccmp(&read2, &read1, >))
return EXIT_FAILURE;
printf("Non implemented clocks work fine\n");

osmo_clock_gettime(CLOCK_MONOTONIC, &read1);
usleep(500);
osmo_clock_gettime(CLOCK_MONOTONIC, &read2);
if (!timespeccmp(&read2, &read1, >))
return EXIT_FAILURE;
printf("Monotonic clock is working fine by default\n");

osmo_clock_override_enable(CLOCK_MONOTONIC, true);
printf("Monotonic clock override enabled\n");

mono = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);
if (timespecisset(mono))
return EXIT_FAILURE;
printf("Monotonic override is cleared by default\n");

memcpy(mono, &ts1, sizeof(struct timespec));
osmo_clock_gettime(CLOCK_MONOTONIC, &read1);
if (!timespeccmp(&ts1, &read1, ==))
return EXIT_FAILURE;
printf("Monotonic clock can be overriden\n");

osmo_clock_override_add(CLOCK_MONOTONIC, ts2.tv_sec, ts2.tv_nsec);
osmo_clock_gettime(CLOCK_MONOTONIC, &read1);
timespecadd(&ts2, &ts1, &res);
if (!timespeccmp(&res, &read1, ==))
return EXIT_FAILURE;
printf("osmo_clock_override_add works fine.\n");

osmo_clock_override_enable(CLOCK_MONOTONIC, false);
printf("Monotonic clock override disabled\n");

osmo_clock_gettime(CLOCK_MONOTONIC, &read1);
usleep(500);
osmo_clock_gettime(CLOCK_MONOTONIC, &read2);
if (!timespeccmp(&read2, &read1, >))
return EXIT_FAILURE;
printf("Monotonic clock is working fine after enable+disable.\n");

return 0;
}
8 changes: 8 additions & 0 deletions tests/timer/clk_override_test.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Non implemented clocks work fine
Monotonic clock is working fine by default
Monotonic clock override enabled
Monotonic override is cleared by default
Monotonic clock can be overriden
osmo_clock_override_add works fine.
Monotonic clock override disabled
Monotonic clock is working fine after enable+disable.

0 comments on commit 87fade8

Please sign in to comment.