diff options
author | Pau Espin Pedrol <pespin@sysmocom.de> | 2018-02-26 19:42:22 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2018-03-01 12:33:02 +0000 |
commit | 87fade88bd8471d0459a306255403e854122120e (patch) | |
tree | 1f090b3419983d8d08c72bfd67895f7fec679dd3 | |
parent | 721aa6ded9c736e3cc5b20824dd58b1af4f4a907 (diff) |
timer: Introduce osmo_clock_gettime to override clock_gettime
Change-Id: I5bebc6e01fc9d238065bc2517058f0ba85620349
-rw-r--r-- | include/osmocom/core/timer.h | 6 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/timer_clockgettime.c | 138 | ||||
-rw-r--r-- | src/timer_gettimeofday.c | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 7 | ||||
-rw-r--r-- | tests/testsuite.at | 6 | ||||
-rw-r--r-- | tests/timer/clk_override_test.c | 89 | ||||
-rw-r--r-- | tests/timer/clk_override_test.ok | 8 |
8 files changed, 255 insertions, 4 deletions
diff --git a/include/osmocom/core/timer.h b/include/osmocom/core/timer.h index 4958efb3..caf4c678 100644 --- a/include/osmocom/core/timer.h +++ b/include/osmocom/core/timer.h @@ -40,6 +40,7 @@ #pragma once #include <sys/time.h> +#include <time.h> #include <stdbool.h> #include <osmocom/core/linuxlist.h> @@ -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 @@ -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); + /*! @} */ diff --git a/src/Makefile.am b/src/Makefile.am index 3d6e6f79..2641a975 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/timer_clockgettime.c b/src/timer_clockgettime.c new file mode 100644 index 00000000..8d9760c5 --- /dev/null +++ b/src/timer_clockgettime.c @@ -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); +} + +/*! @} */ diff --git a/src/timer_gettimeofday.c b/src/timer_gettimeofday.c index 15b5e31d..5273a3b8 100644 --- a/src/timer_gettimeofday.c +++ b/src/timer_gettimeofday.c @@ -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) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 5dd8e22b..cca128dd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 @@ -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 @@ -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 diff --git a/tests/testsuite.at b/tests/testsuite.at index 0ec852cc..15a89b6d 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -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 diff --git a/tests/timer/clk_override_test.c b/tests/timer/clk_override_test.c new file mode 100644 index 00000000..308e8212 --- /dev/null +++ b/tests/timer/clk_override_test.c @@ -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; +} diff --git a/tests/timer/clk_override_test.ok b/tests/timer/clk_override_test.ok new file mode 100644 index 00000000..1fb519c9 --- /dev/null +++ b/tests/timer/clk_override_test.ok @@ -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. |