This patch introduces functions for recording and replaying realtime sources, that do not use qemu-clock interface. These include return value of time() function in time_t and struct tm forms. Patch also adds warning to get_timedate function to prevent its usage in recording mode, because it may lead to non-determinism.
Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@ispras.ru> --- hw/timer/mc146818rtc.c | 3 + hw/timer/pl031.c | 10 ++++ include/qemu-common.h | 1 replay/replay-internal.h | 4 ++ replay/replay-time.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++ replay/replay.h | 8 +++ vl.c | 17 ++++++- 7 files changed, 146 insertions(+), 5 deletions(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 0b78d88..b901851 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -28,6 +28,7 @@ #include "qapi/visitor.h" #include "qapi-event.h" #include "qmp-commands.h" +#include "replay/replay.h" #ifdef TARGET_I386 #include "hw/i386/apic.h" @@ -710,7 +711,7 @@ static void rtc_set_date_from_host(ISADevice *dev) RTCState *s = MC146818_RTC(dev); struct tm tm; - qemu_get_timedate(&tm, 0); + qemu_get_timedate_no_warning(&tm, 0); s->base_rtc = mktimegm(&tm); s->last_update = qemu_clock_get_ns(rtc_clock); diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c index f8e5abc..40e1700 100644 --- a/hw/timer/pl031.c +++ b/hw/timer/pl031.c @@ -14,6 +14,7 @@ #include "hw/sysbus.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "replay/replay.h" //#define DEBUG_PL031 @@ -200,7 +201,14 @@ static int pl031_init(SysBusDevice *dev) sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); - qemu_get_timedate(&tm, 0); + if (replay_mode == REPLAY_MODE_RECORD) { + qemu_get_timedate_no_warning(&tm, 0); + replay_save_tm(&tm); + } else if (replay_mode == REPLAY_MODE_PLAY) { + replay_read_tm(&tm); + } else { + qemu_get_timedate_no_warning(&tm, 0); + } s->tick_offset = mktimegm(&tm) - qemu_clock_get_ns(rtc_clock) / get_ticks_per_sec(); diff --git a/include/qemu-common.h b/include/qemu-common.h index 6ef8282..5b9d358 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -121,6 +121,7 @@ extern int use_icount; int qemu_main(int argc, char **argv, char **envp); #endif +void qemu_get_timedate_no_warning(struct tm *tm, int offset); void qemu_get_timedate(struct tm *tm, int offset); int qemu_timedate_diff(struct tm *tm); diff --git a/replay/replay-internal.h b/replay/replay-internal.h index 14cb9d5..b21f76b 100755 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -15,6 +15,10 @@ #include <stdio.h> #include "sysemu/sysemu.h" +/* for time_t event */ +#define EVENT_TIME_T 1 +/* for tm event */ +#define EVENT_TM 2 /* for software interrupt */ #define EVENT_INTERRUPT 15 /* for emulated exceptions */ diff --git a/replay/replay-time.c b/replay/replay-time.c index 07c1599..e906481 100755 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -73,3 +73,111 @@ int64_t replay_read_clock(unsigned int kind) fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__); exit(1); } + +/*! Saves time_t value to the log */ +static void replay_save_time_t(time_t tm) +{ + replay_save_instructions(); + + if (replay_file) { + replay_put_event(EVENT_TIME_T); + if (sizeof(tm) == 4) { + replay_put_dword(tm); + } else if (sizeof(tm) == 8) { + replay_put_qword(tm); + } else { + fprintf(stderr, "invalid time_t sizeof: %u\n", + (unsigned)sizeof(tm)); + exit(1); + } + } +} + +/*! Reads time_t value from the log. Stops execution in case of error */ +static time_t replay_read_time_t(void) +{ + if (replay_file) { + time_t tm; + + skip_async_events_until(EVENT_TIME_T); + + if (sizeof(tm) == 4) { + tm = replay_get_dword(); + } else if (sizeof(tm) == 8) { + tm = replay_get_qword(); + } else { + fprintf(stderr, "invalid time_t sizeof: %u\n", + (unsigned)sizeof(tm)); + exit(1); + } + + replay_check_error(); + + replay_has_unread_data = 0; + + return tm; + } + + fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__); + exit(1); +} + +void replay_save_tm(struct tm *tm) +{ + replay_save_instructions(); + + if (replay_file) { + replay_put_event(EVENT_TM); + + replay_put_dword(tm->tm_sec); + replay_put_dword(tm->tm_min); + replay_put_dword(tm->tm_hour); + replay_put_dword(tm->tm_mday); + replay_put_dword(tm->tm_mon); + replay_put_dword(tm->tm_year); + replay_put_dword(tm->tm_wday); + replay_put_dword(tm->tm_yday); + replay_put_dword(tm->tm_isdst); + } +} + +void replay_read_tm(struct tm *tm) +{ + if (replay_file) { + skip_async_events_until(EVENT_TM); + + tm->tm_sec = replay_get_dword(); + tm->tm_min = replay_get_dword(); + tm->tm_hour = replay_get_dword(); + tm->tm_mday = replay_get_dword(); + tm->tm_mon = replay_get_dword(); + tm->tm_year = replay_get_dword(); + tm->tm_wday = replay_get_dword(); + tm->tm_yday = replay_get_dword(); + tm->tm_isdst = replay_get_dword(); + + replay_check_error(); + replay_has_unread_data = 0; + + return; + } + + fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__); + exit(1); +} + +time_t replay_time(void) +{ + time_t systime; + + if (replay_mode == REPLAY_MODE_RECORD) { + systime = time(NULL); + replay_save_time_t(systime); + } else if (replay_mode == REPLAY_MODE_PLAY) { + systime = replay_read_time_t(); + } else { + systime = time(NULL); + } + + return systime; +} diff --git a/replay/replay.h b/replay/replay.h index 938d395..e19ae08 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -14,6 +14,7 @@ #include <stdbool.h> #include <stdint.h> +#include <time.h> #include "qapi-types.h" /* replay clock kinds */ @@ -64,6 +65,13 @@ bool replay_has_interrupt(void); void replay_save_clock(unsigned int kind, int64_t clock); /*! Read the specified clock from the log or return cached data */ int64_t replay_read_clock(unsigned int kind); +/*! Returns result of time() function execution in normal and record modes. + In play mode returns value read from the log. */ +time_t replay_time(void); +/*! Saves struct tm value to the log */ +void replay_save_tm(struct tm *tm); +/*! Reads struct tm value from the log. Stops execution in case of error */ +void replay_read_tm(struct tm *tm); /* Asynchronous events queue */ diff --git a/vl.c b/vl.c index fe451aa..4932fa8 100644 --- a/vl.c +++ b/vl.c @@ -118,6 +118,7 @@ int main(int argc, char **argv) #include "qapi/opts-visitor.h" #include "qom/object_interfaces.h" #include "qapi-event.h" +#include "replay/replay.h" #define DEFAULT_RAM_SIZE 128 @@ -757,7 +758,7 @@ void vm_start(void) /***********************************************************/ /* host time/date access */ -void qemu_get_timedate(struct tm *tm, int offset) +void qemu_get_timedate_no_warning(struct tm *tm, int offset) { time_t ti; @@ -774,6 +775,16 @@ void qemu_get_timedate(struct tm *tm, int offset) } } +/* host time/date access with replay warning */ +void qemu_get_timedate(struct tm *tm, int offset) +{ + if (replay_mode == REPLAY_MODE_RECORD) { + fprintf(stderr, "REPLAY WARNING! qemu_get_timedate " + "function may lead to non-determinism\n"); + } + qemu_get_timedate_no_warning(tm, offset); +} + int qemu_timedate_diff(struct tm *tm) { time_t seconds; @@ -789,7 +800,7 @@ int qemu_timedate_diff(struct tm *tm) else seconds = mktimegm(tm) + rtc_date_offset; - return seconds - time(NULL); + return seconds - replay_time(); } static void configure_rtc_date_offset(const char *startdate, int legacy) @@ -827,7 +838,7 @@ static void configure_rtc_date_offset(const char *startdate, int legacy) "'2006-06-17T16:01:21' or '2006-06-17'\n"); exit(1); } - rtc_date_offset = time(NULL) - rtc_start_date; + rtc_date_offset = replay_time() - rtc_start_date; } }