Message ID | 20150122085238.5276.4455.stgit@PASHA-ISP.def.inno |
---|---|
State | New |
Headers | show |
On 22/01/2015 09:52, Pavel Dovgalyuk wrote: > 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.dovgaluk@ispras.ru> > --- > hw/timer/mc146818rtc.c | 3 + > hw/timer/pl031.c | 3 + > include/qemu-common.h | 1 > replay/replay-internal.h | 4 + > replay/replay-time.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++ > replay/replay.h | 8 +++ > vl.c | 17 +++++- > 7 files changed, 163 insertions(+), 5 deletions(-) > > diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c > index f18d128..92295fb 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" > @@ -703,7 +704,7 @@ static void rtc_set_date_from_host(ISADevice *dev) > RTCState *s = MC146818_RTC(dev); > struct tm tm; > > - qemu_get_timedate(&tm, 0); What about just making qemu_get_timedate use qemu_clock_get_ns(QEMU_CLOCK_HOST) instead of time()? This would just work using the infrastructure of the previous patch. Paolo > + replay_get_timedate(&tm); > > 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 34d9b44..b02d288 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,7 @@ static int pl031_init(SysBusDevice *dev) > sysbus_init_mmio(dev, &s->iomem); > > sysbus_init_irq(dev, &s->irq); > - qemu_get_timedate(&tm, 0); > + replay_get_timedate(&tm); > 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 644b46d..8c9957e 100644 > --- a/include/qemu-common.h > +++ b/include/qemu-common.h > @@ -129,6 +129,7 @@ void dump_drift_info(FILE *f, fprintf_function cpu_fprintf); > 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 e906ec3..be71e6f 100755 > --- a/replay/replay-internal.h > +++ b/replay/replay-internal.h > @@ -23,6 +23,10 @@ enum ReplayEvents { > EVENT_EXCEPTION, > /* for async events */ > EVENT_ASYNC, > + /* for time_t event */ > + EVENT_TIME_T, > + /* for tm event */ > + EVENT_TM, > /* for clock read/writes */ > /* some of grteater codes are reserved for clocks */ > EVENT_CLOCK > diff --git a/replay/replay-time.c b/replay/replay-time.c > index 5f5bc6a..282d00e 100755 > --- a/replay/replay-time.c > +++ b/replay/replay-time.c > @@ -82,3 +82,135 @@ int64_t replay_read_clock(ReplayClockKind 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_mutex_lock(); > + 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); > + } > + replay_mutex_unlock(); > + } > +} > + > +/*! Reads time_t value from the log. Stops execution in case of error */ > +static time_t replay_read_time_t(void) > +{ > + replay_exec_instructions(); > + > + if (replay_file) { > + time_t tm; > + > + replay_mutex_lock(); > + 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; > + replay_mutex_unlock(); > + > + return tm; > + } > + > + fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__); > + exit(1); > +} > + > +static void replay_save_tm(struct tm *tm) > +{ > + replay_save_instructions(); > + > + if (replay_file) { > + replay_mutex_lock(); > + 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); > + replay_mutex_unlock(); > + } > +} > + > +static void replay_read_tm(struct tm *tm) > +{ > + replay_exec_instructions(); > + > + if (replay_file) { > + replay_mutex_lock(); > + 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; > + replay_mutex_unlock(); > + > + return; > + } > + > + fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__); > + exit(1); > +} > + > +void replay_get_timedate(struct tm *tm) > +{ > + 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); > + } > +} > + > +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 8a8e7e0..2f1402c 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 */ > @@ -67,6 +68,13 @@ int64_t replay_read_clock(ReplayClockKind kind); > : replay_mode == REPLAY_MODE_RECORD \ > ? replay_save_clock((clock), (value)) \ > : (value)) > +/*! 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); > +/*! This function is used for obtaining current timedate. > + In record mode it also saves in into the replay log. > + In replay mode it just read from the log. */ > +void replay_get_timedate(struct tm *tm); > > /* Asynchronous events queue */ > > diff --git a/vl.c b/vl.c > index fbf4240..baff3b5 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 > > @@ -715,7 +716,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; > > @@ -732,6 +733,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; > @@ -747,7 +758,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) > @@ -785,7 +796,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; > } > } > >
> From: Paolo Bonzini [mailto:pbonzini@redhat.com] > On 22/01/2015 09:52, Pavel Dovgalyuk wrote: > > 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.dovgaluk@ispras.ru> > > --- > > hw/timer/mc146818rtc.c | 3 + > > hw/timer/pl031.c | 3 + > > include/qemu-common.h | 1 > > replay/replay-internal.h | 4 + > > replay/replay-time.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++ > > replay/replay.h | 8 +++ > > vl.c | 17 +++++- > > 7 files changed, 163 insertions(+), 5 deletions(-) > > > > diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c > > index f18d128..92295fb 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" > > @@ -703,7 +704,7 @@ static void rtc_set_date_from_host(ISADevice *dev) > > RTCState *s = MC146818_RTC(dev); > > struct tm tm; > > > > - qemu_get_timedate(&tm, 0); > > What about just making qemu_get_timedate use > qemu_clock_get_ns(QEMU_CLOCK_HOST) instead of time()? This would just > work using the infrastructure of the previous patch. I can get rid of these calls, but localtime() function used in qemu_get_timedate() will not work deterministically. Should we save its' result then? Pavel Dovgalyuk
On 03/02/2015 15:05, Pavel Dovgaluk wrote: >> > >> > What about just making qemu_get_timedate use >> > qemu_clock_get_ns(QEMU_CLOCK_HOST) instead of time()? This would just >> > work using the infrastructure of the previous patch. > I can get rid of these calls, but localtime() function used > in qemu_get_timedate() will not work deterministically. > Should we save its' result then? It is deterministic if the user's timezone remains the same. You could just outlaw "-rtc base=localtime". Also, if you choose to record the output of qemu_get_timedate(), you have to do the same for qemu_timedate_diff(). Switching qemu_timedate_diff to use QEMU_CLOCK_HOST would also work for qemu_timedate_diff(). Paolo
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index f18d128..92295fb 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" @@ -703,7 +704,7 @@ static void rtc_set_date_from_host(ISADevice *dev) RTCState *s = MC146818_RTC(dev); struct tm tm; - qemu_get_timedate(&tm, 0); + replay_get_timedate(&tm); 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 34d9b44..b02d288 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,7 @@ static int pl031_init(SysBusDevice *dev) sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); - qemu_get_timedate(&tm, 0); + replay_get_timedate(&tm); 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 644b46d..8c9957e 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -129,6 +129,7 @@ void dump_drift_info(FILE *f, fprintf_function cpu_fprintf); 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 e906ec3..be71e6f 100755 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -23,6 +23,10 @@ enum ReplayEvents { EVENT_EXCEPTION, /* for async events */ EVENT_ASYNC, + /* for time_t event */ + EVENT_TIME_T, + /* for tm event */ + EVENT_TM, /* for clock read/writes */ /* some of grteater codes are reserved for clocks */ EVENT_CLOCK diff --git a/replay/replay-time.c b/replay/replay-time.c index 5f5bc6a..282d00e 100755 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -82,3 +82,135 @@ int64_t replay_read_clock(ReplayClockKind 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_mutex_lock(); + 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); + } + replay_mutex_unlock(); + } +} + +/*! Reads time_t value from the log. Stops execution in case of error */ +static time_t replay_read_time_t(void) +{ + replay_exec_instructions(); + + if (replay_file) { + time_t tm; + + replay_mutex_lock(); + 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; + replay_mutex_unlock(); + + return tm; + } + + fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__); + exit(1); +} + +static void replay_save_tm(struct tm *tm) +{ + replay_save_instructions(); + + if (replay_file) { + replay_mutex_lock(); + 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); + replay_mutex_unlock(); + } +} + +static void replay_read_tm(struct tm *tm) +{ + replay_exec_instructions(); + + if (replay_file) { + replay_mutex_lock(); + 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; + replay_mutex_unlock(); + + return; + } + + fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__); + exit(1); +} + +void replay_get_timedate(struct tm *tm) +{ + 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); + } +} + +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 8a8e7e0..2f1402c 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 */ @@ -67,6 +68,13 @@ int64_t replay_read_clock(ReplayClockKind kind); : replay_mode == REPLAY_MODE_RECORD \ ? replay_save_clock((clock), (value)) \ : (value)) +/*! 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); +/*! This function is used for obtaining current timedate. + In record mode it also saves in into the replay log. + In replay mode it just read from the log. */ +void replay_get_timedate(struct tm *tm); /* Asynchronous events queue */ diff --git a/vl.c b/vl.c index fbf4240..baff3b5 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 @@ -715,7 +716,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; @@ -732,6 +733,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; @@ -747,7 +758,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) @@ -785,7 +796,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; } }
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.dovgaluk@ispras.ru> --- hw/timer/mc146818rtc.c | 3 + hw/timer/pl031.c | 3 + include/qemu-common.h | 1 replay/replay-internal.h | 4 + replay/replay-time.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++ replay/replay.h | 8 +++ vl.c | 17 +++++- 7 files changed, 163 insertions(+), 5 deletions(-)