diff mbox

[RFC,19/22] Deterministic replay core functions

Message ID 008701cf9520$113aec00$33b0c400$@Dovgaluk@ispras.ru
State New
Headers show

Commit Message

Pavel Dovgalyuk July 1, 2014, 11:31 a.m. UTC
Functions for writing and reading replay log. These functions are used to 
record the following events:
 * Output of the time() function
 * Sound in and sound out events in winaudio
 * Hardware interrupts and exceptions
 * Special replay debug assert
 * Target time information used for reviewing the events log
 * Start and finish of saving VM
 * Synchronous data from some module
 * Execution of the group of the instructions
 * Hardware clock read (real ticks, host clock, virtual clock)
 * Asynchronous events:
   - Setting event notifier
   - Network packet
   - Worker thread invocation
   - BH execution
   - USB packets
   - Serial port input
   - Mouse and keyboard input
 * Checkpoint in the code (used to synchronize the events)
 * System shutdown

This module also sets timer for periodical snapshotting of the system state,
when it is required by user.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
---

+void replay_change(struct Monitor *mon);
+#endif

Comments

Kirill Batuzov July 2, 2014, 10:50 a.m. UTC | #1
On Tue, 1 Jul 2014, Pavel Dovgaluk wrote:

> Functions for writing and reading replay log. These functions are used to 
> record the following events:
>  * Output of the time() function
>  * Sound in and sound out events in winaudio
>  * Hardware interrupts and exceptions
>  * Special replay debug assert
>  * Target time information used for reviewing the events log
>  * Start and finish of saving VM
>  * Synchronous data from some module
>  * Execution of the group of the instructions
>  * Hardware clock read (real ticks, host clock, virtual clock)
>  * Asynchronous events:
>    - Setting event notifier
>    - Network packet
>    - Worker thread invocation
>    - BH execution
>    - USB packets
>    - Serial port input
>    - Mouse and keyboard input
>  * Checkpoint in the code (used to synchronize the events)
>  * System shutdown
> 
> This module also sets timer for periodical snapshotting of the system state,
> when it is required by user.
> 
> Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
> ---

This change is way to large for one patch (more than 4000 lines of
code). You need to split it into smaller ones before it can be reviewed.
diff mbox

Patch

diff --git a/replay/Makefile.objs b/replay/Makefile.objs
new file mode 100644
index 0000000..926d50e
--- /dev/null
+++ b/replay/Makefile.objs
@@ -0,0 +1 @@ 
+obj-y += replay.o replay-events.o replay-internal.o replay-usb.o replay-icount.o replay-audio.o
replay-char.o replay-debug.o replay-input.o replay-net.o
diff --git a/replay/replay-audio.c b/replay/replay-audio.c
new file mode 100644
index 0000000..9814a25
--- /dev/null
+++ b/replay/replay-audio.c
@@ -0,0 +1,238 @@ 
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "replay.h"
+#include "replay-internal.h"
+#include "qemu/log.h"
+
+/* Sound card state */
+typedef struct
+{
+    void *instance;
+    const int event_id;
+    //void (const *callback)(void *, WAVEHDR *);
+#ifdef _WIN32
+    WAVEHDR *queue;
+#endif
+    /*! Maximum size of the queue */
+    int size;
+    /*! Current size of the queue */
+    sig_atomic_t cur_size;
+    unsigned int head, tail;
+} SoundQueue;
+
+
+static SoundQueue sound_in = {
+        .event_id = EVENT_SOUND_IN
+    },
+    sound_out = {
+        .event_id = EVENT_SOUND_OUT,
+        //.callback = winwave_callback_out_impl
+    };
+
+#ifdef _WIN32
+/*! Spinlock for sound events processing. */
+static spinlock_t sound_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/*****************************************************************************
+ *  Sound queue functions                                                    *
+ *****************************************************************************/
+
+/* callback functions */
+#ifdef _WIN32
+void winwave_callback_out_impl(void *dwInstance, WAVEHDR *h);
+void winwave_callback_in_impl(void *dwInstance, WAVEHDR *h);
+
+void replay_init_sound_in(void *instance, WAVEHDR *hdrs, int sz)
+{
+    sound_in.instance = instance;
+    sound_in.queue = hdrs;
+    sound_in.size = sz;
+    sound_in.head = 0;
+    sound_in.tail = 0;
+    sound_in.cur_size = 0;
+}
+
+void replay_init_sound_out(void *instance, WAVEHDR *hdrs, int sz)
+{
+    sound_out.instance = instance;
+    sound_out.queue = hdrs;
+    sound_out.size = sz;
+    sound_out.head = 0;
+    sound_out.tail = 0;
+    sound_out.cur_size = 0;
+}
+
+static int sound_queue_add(SoundQueue *q, WAVEHDR *hdr)
+{
+    if (q->queue + q->tail != hdr)
+    {
+        // state was loaded and we need to reset the queue
+        if (q->cur_size == 0)
+        {
+            q->head = q->tail = hdr - q->queue;
+        }
+        else
+        {
+            fprintf(stderr, "Replay: Sound queue error\n");
+            exit(1);
+        }
+    }
+
+    if (q->cur_size == q->size)
+    {
+        if (replay_mode == REPLAY_PLAY)
+        {
+            return 1;
+        }
+
+        fprintf(stderr, "Replay: Sound queue overflow\n");
+        exit(1);
+    }
+
+    q->tail = (q->tail + 1) % q->size;
+    ++q->cur_size;
+
+    return 0;
+}
+
+void replay_save_sound_out(void)
+{
+    spin_lock(&sound_lock);
+    while (sound_out.cur_size != 0)
+    {
+        //printf("Saving from output queue %x\n", &sound_out.queue[sound_out.head]);
+        // put the message ID
+        replay_put_event(sound_out.event_id);
+        // save the buffer size
+        replay_put_dword(sound_out.queue[sound_out.head].dwBytesRecorded);
+        // perform winwave-specific actions
+        winwave_callback_out_impl(sound_out.instance, &sound_out.queue[sound_out.head]);
+        // goto the next buffer
+        sound_out.head = (sound_out.head + 1) % sound_out.size;
+        /* TODO!!! Maybe need some kind of lock here in SAVE mode ?? */
+        --sound_out.cur_size;
+    }
+    spin_unlock(&sound_lock);
+}
+
+
+void replay_save_sound_in(void)
+{
+    spin_lock(&sound_lock);
+    while (sound_in.cur_size != 0)
+    {
+        //printf("Saving from input queue %x\n", &sound_in.queue[sound_in.head]);
+
+        // put the message ID
+        replay_put_event(sound_in.event_id);
+        // save the buffer
+        replay_put_array(sound_in.queue[sound_in.head].lpData,
+            sound_in.queue[sound_in.head].dwBytesRecorded);
+        // perform winwave-specific actions
+        winwave_callback_in_impl(sound_in.instance, &sound_in.queue[sound_in.head]);
+        // goto the next buffer
+        sound_in.head = (sound_in.head + 1) % sound_in.size;
+        /* TODO!!! Maybe need some kind of lock here in SAVE mode ?? */
+        --sound_in.cur_size;
+    }
+    spin_unlock(&sound_lock);
+}
+
+
+void replay_read_sound_out(void)
+{
+    if (sound_out.cur_size == 0)
+    {
+        fprintf(stderr, "Replay: Sound queue underflow\n");
+        exit(1);
+    }
+
+    // get the buffer size
+    sound_out.queue[sound_out.head].dwBytesRecorded = replay_get_dword();
+
+    replay_check_error();
+    replay_has_unread_data = 0;
+
+    // perform winwave-specific actions
+    winwave_callback_out_impl(sound_out.instance, &sound_out.queue[sound_out.head]);
+    sound_out.head = (sound_out.head + 1) % sound_out.size;
+    --sound_out.cur_size;
+}
+
+void replay_read_sound_in(void)
+{
+    if (sound_in.cur_size == 0)
+    {
+        fprintf(stderr, "Replay: Sound queue underflow\n");
+        exit(1);
+    }
+
+    // get the buffer size
+    size_t size;
+    replay_get_array(sound_in.queue[sound_in.head].lpData, &size);
+    sound_in.queue[sound_in.head].dwBytesRecorded = (unsigned int)size;
+
+    replay_check_error();
+    replay_has_unread_data = 0;
+
+    // perform winwave-specific actions
+    winwave_callback_in_impl(sound_in.instance, &sound_in.queue[sound_in.head]);
+    sound_in.head = (sound_in.head + 1) % sound_in.size;
+    --sound_in.cur_size;
+}
+
+
+void replay_sound_in_event(WAVEHDR *hdr)
+{
+    spin_lock(&sound_lock);
+    if (sound_queue_add(&sound_in, hdr))
+    {
+        fprintf(stderr, "Replay: Input sound buffer overflow\n");
+        exit(1);
+    }
+    spin_unlock(&sound_lock);
+}
+
+
+
+int replay_sound_out_event(WAVEHDR *hdr)
+{
+    spin_lock(&sound_lock);
+    int result = sound_queue_add(&sound_out, hdr);
+    spin_unlock(&sound_lock);
+
+    return result;
+}
+#endif
+
+bool replay_has_sound_events(void)
+{
+    return sound_in.cur_size || sound_out.cur_size;
+}
+
+void replay_sound_flush_queue(void)
+{
+#ifdef _WIN32
+    spin_lock(&sound_lock);
+    while (sound_out.cur_size != 0)
+    {
+        // perform winwave-specific actions
+        winwave_callback_out_impl(sound_out.instance, &sound_out.queue[sound_out.head]);
+        // goto the next buffer
+        sound_out.head = (sound_out.head + 1) % sound_out.size;
+        /* TODO!!! Maybe need some kind of lock here in SAVE mode ?? */
+        --sound_out.cur_size;
+    }
+    while (sound_in.cur_size != 0)
+    {
+        // perform winwave-specific actions
+        winwave_callback_in_impl(sound_in.instance, &sound_in.queue[sound_in.head]);
+        // goto the next buffer
+        sound_in.head = (sound_in.head + 1) % sound_in.size;
+        --sound_in.cur_size;
+    }
+    spin_unlock(&sound_lock);
+#endif  
+}
+
diff --git a/replay/replay-char.c b/replay/replay-char.c
new file mode 100644
index 0000000..99c4934
--- /dev/null
+++ b/replay/replay-char.c
@@ -0,0 +1,119 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "replay.h"
+#include "replay-internal.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/char.h"
+
+#include "qemu/log.h"
+
+#define MAX_CHAR_DRIVERS MAX_SERIAL_PORTS
+/* Char drivers that generate qemu_chr_be_write events
+   that should be saved into the log. */
+static CharDriverState *char_drivers[MAX_CHAR_DRIVERS];
+
+/* Char event attributes. */
+typedef struct CharEvent
+{
+    int id;
+    uint8_t *buf;
+    size_t len;
+} CharEvent;
+
+static int find_char_driver(CharDriverState *chr)
+{
+    int i = 0;
+    while (i < MAX_CHAR_DRIVERS && char_drivers[i] != chr)
+        ++i;
+
+    return i >= MAX_CHAR_DRIVERS ? -1 : i;
+}
+
+
+void replay_register_char_driver(CharDriverState *chr)
+{
+    chr->replay = true;
+    int i = find_char_driver(NULL);
+
+    if (i < 0)
+    {
+        fprintf(stderr, "Replay: cannot register char driver\n");
+        exit(1);
+    }
+    else
+    {
+        
+        char_drivers[i] = chr;
+    }
+}
+
+void replay_delayed_register_char_driver(void)
+{
+    int i;
+    for (i = 0; i < MAX_SERIAL_PORTS; i++)
+        if (serial_hds[i] != NULL)
+        {
+            serial_hds[i]->replay = true;
+            replay_register_char_driver(serial_hds[i]);
+        }
+}
+
+void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+{
+    CharEvent *event = g_malloc0(sizeof(CharEvent));
+    
+    event->id = find_char_driver(s);
+    if (event->id < 0)
+    {
+        fprintf(stderr, "Replay: cannot find char driver\n");
+        exit(1);
+    }
+    event->buf = g_malloc(len);
+    memcpy(event->buf, buf, len);
+    event->len = len;
+    
+    replay_add_event(REPLAY_ASYNC_EVENT_CHAR, event);
+}
+
+void replay_event_char_run(void *opaque)
+{
+    CharEvent *event = (CharEvent*)opaque;
+
+    qemu_chr_be_write_impl(char_drivers[event->id], event->buf, (int)event->len);
+    
+    g_free(event->buf);
+    g_free(event);
+}
+
+void replay_event_char_save(void *opaque)
+{
+    CharEvent *event = (CharEvent*)opaque;
+    
+    replay_put_byte(event->id);
+    replay_put_array(event->buf, event->len);
+}
+
+void *replay_event_char_read(void)
+{
+    CharEvent *event = g_malloc0(sizeof(CharEvent));
+    
+    event->id = replay_get_byte();
+    replay_get_array_alloc(&event->buf, &event->len);
+    
+    return event;
+}
+
+void replay_event_char_skip(void)
+{
+    size_t size;
+    
+    fseek(replay_file, sizeof(uint8_t), SEEK_CUR);
+    if (fread(&size, sizeof(size), 1, replay_file) != 1)
+    {
+        fprintf(stderr, "Internal error while skipping char event\n");
+        exit(1);
+    }
+    fseek(replay_file, size, SEEK_CUR);
+}
diff --git a/replay/replay-debug.c b/replay/replay-debug.c
new file mode 100644
index 0000000..5d1cc95
--- /dev/null
+++ b/replay/replay-debug.c
@@ -0,0 +1,152 @@ 
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "exec/cpu-defs.h"
+
+#include "replay.h"
+#include "replay-internal.h"
+
+/* Reverse debugging data */
+
+/* Saved handler of the debug exception */
+static CPUDebugExcpHandler *prev_debug_excp_handler;
+/* Step of the last breakpoint hit. Used for seeking in reverse continue mode. */
+static uint64_t last_breakpoint_step;
+/* Start step, where reverse continue begins,
+   or target step for reverse stepping.*/
+static uint64_t last_reverse_step;
+/* Start step, where reverse continue begins.*/
+static uint64_t start_reverse_step;
+/* Previously loaded step for reverse continue */
+static SavedStateInfo *reverse_state;
+
+/*! Breakpoint handler for pass2 of reverse continue.
+    Stops the execution at previously saved breakpoint step. */
+static void reverse_continue_pass2_breakpoint_handler(CPUArchState *env)
+{
+    if (replay_get_current_step() == last_breakpoint_step)
+    {
+        CPUState *cpu = ENV_GET_CPU(env);
+        CPUDebugExcpHandler *handler = prev_debug_excp_handler;
+        prev_debug_excp_handler = NULL;
+
+        play_submode = REPLAY_PLAY_NORMAL;
+
+		cpu->exception_index = EXCP_DEBUG;
+        // invoke the breakpoint
+        cpu_set_debug_excp_handler(handler);
+        handler(env);
+        cpu_exit(cpu);
+    }
+}
+
+/*! Breakpoint handler for pass1 of reverse continue.
+    Saves last breakpoint hit and switches to pass2 when starting point is reached. */
+static void reverse_continue_pass1_breakpoint_handler(CPUArchState *env)
+{
+    if (replay_get_current_step() == last_reverse_step)
+    {
+        CPUState *cpu = ENV_GET_CPU(env);
+        // repeat first pass if breakpoint was not found
+        // on current iteration
+        if (last_breakpoint_step == reverse_state->step - 1
+            && reverse_state != saved_states)
+        {
+            last_reverse_step = reverse_state->step;
+            // load previous state
+            --reverse_state;
+            last_breakpoint_step = reverse_state->step - 1;
+            replay_seek_step(reverse_state->step);
+            // set break should be after seek, because seek resets break
+            replay_set_break(last_reverse_step);
+			cpu_loop_exit(cpu);
+        }
+        else
+        {
+            // this condition is needed, when no breakpoints were found
+            if (last_breakpoint_step == reverse_state->step - 1) 
+            {
+                ++last_breakpoint_step;
+            }
+            cpu_set_debug_excp_handler(reverse_continue_pass2_breakpoint_handler);
+			
+            reverse_continue_pass2_breakpoint_handler(env);
+            replay_seek_step(last_breakpoint_step);
+            cpu_loop_exit(cpu);
+        }
+    }
+    else
+    {
+        // watchpoint branch
+        last_breakpoint_step = replay_get_current_step();
+    }
+}
+
+
+void replay_reverse_breakpoint(void)
+{
+    // we started reverse execution from a breakpoint
+    if (replay_get_current_step() != start_reverse_step)
+    {
+        last_breakpoint_step = replay_get_current_step();
+    }
+}
+
+
+void replay_reverse_continue(void)
+{
+    if (replay_mode == REPLAY_PLAY && play_submode == REPLAY_PLAY_NORMAL)
+    {
+        tb_flush_all();
+        play_submode = REPLAY_PLAY_REVERSE;
+
+        last_reverse_step = replay_get_current_step();
+        start_reverse_step = replay_get_current_step();
+        // load initial state
+        reverse_state = find_nearest_state(replay_get_current_step());
+        replay_seek_step(reverse_state->step);
+        // run to current step
+        replay_set_break(last_reverse_step);
+        // decrement to allow breaking at the first step
+        last_breakpoint_step = reverse_state->step - 1;
+        prev_debug_excp_handler =
+            cpu_set_debug_excp_handler(reverse_continue_pass1_breakpoint_handler);
+    }
+}
+
+
+/*! Breakpoint handler for reverse stepping.
+    Stops at the desired step and skips other breakpoints. */
+static void reverse_step_breakpoint_handler(CPUArchState *env)
+{
+    if (replay_get_current_step() == last_reverse_step)
+    {
+        CPUState *cpu = ENV_GET_CPU(env);
+        CPUDebugExcpHandler *handler = prev_debug_excp_handler;
+        prev_debug_excp_handler = NULL;
+
+        play_submode = REPLAY_PLAY_NORMAL;
+
+        cpu->exception_index = EXCP_DEBUG;
+        // invoke the breakpoint
+        cpu_set_debug_excp_handler(handler);
+        handler(env);
+        cpu_exit(cpu);
+    }
+}
+
+
+void replay_reverse_step(void)
+{
+    if (replay_mode == REPLAY_PLAY && play_submode == REPLAY_PLAY_NORMAL
+        && replay_get_current_step() > 0)
+    {
+        tb_flush_all();
+        play_submode = REPLAY_PLAY_REVERSE;
+
+        last_reverse_step = replay_get_current_step() - 1;
+        replay_seek_step(last_reverse_step);
+
+        prev_debug_excp_handler =
+            cpu_set_debug_excp_handler(reverse_step_breakpoint_handler);
+    }
+}
diff --git a/replay/replay-events.c b/replay/replay-events.c
new file mode 100644
index 0000000..bd2d53f
--- /dev/null
+++ b/replay/replay-events.c
@@ -0,0 +1,463 @@ 
+#include <stdio.h>
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "qemu/queue.h"
+#include "replay.h"
+#include "replay-internal.h"
+#include "qemu/log.h"
+#include "qemu/event_notifier.h"
+#include "monitor/monitor.h"
+#include "block/thread-pool.h"
+#include "block/aio.h"
+#include "ui/input.h"
+
+typedef struct Event
+{
+    unsigned int event_id;
+    void *opaque;
+    void *opaque2;
+    uint64_t id;
+
+    QTAILQ_ENTRY(Event) events;
+} Event;
+
+static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
+
+static QemuMutex lock;
+static unsigned int read_event_id = -1;
+static uint64_t read_bh_id = -1;
+static int read_opt = -1;
+
+// Externals
+bool replay_events_enabled = false;
+
+// Functions
+
+static void replay_run_event(Event *event)
+{
+    switch (event->event_id)
+    {
+    case REPLAY_ASYNC_EVENT_NOTIFIER:
+        event_notifier_set((EventNotifier*)event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_NETWORK:
+        replay_net_send_packet(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_THREAD:
+        thread_pool_work((struct ThreadPool *)event->opaque, (struct ThreadPoolElement
*)event->opaque2);
+        break;
+    case REPLAY_ASYNC_EVENT_BH:
+        aio_bh_call(event->opaque);
+        break;
+#ifdef CONFIG_USB_LIBUSB
+    case REPLAY_ASYNC_EVENT_USB_CTRL:
+        replay_event_usb_ctrl(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_USB_DATA:
+        replay_event_usb_data(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_USB_ISO:
+        replay_event_usb_iso(event->opaque);
+        break;
+#endif
+    case REPLAY_ASYNC_EVENT_CHAR:
+        replay_event_char_run(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT:
+        qemu_input_event_send_impl(NULL, (InputEvent*)event->opaque);
+        // Using local variables, when replaying. Do not free them.
+        if (replay_mode == REPLAY_SAVE)
+        {
+            qapi_free_InputEvent((InputEvent*)event->opaque);
+        }
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
+        qemu_input_event_sync_impl();
+        break;
+    default:
+        fprintf(stderr, "Replay: invalid async event ID (%d) in the queue\n", event->event_id);
+        exit(1);
+        break;
+    }
+}
+
+void replay_enable_events(void)
+{
+    replay_events_enabled = true;
+}
+
+void replay_flush_events(void)
+{
+    qemu_mutex_lock(&lock);
+    while (!QTAILQ_EMPTY(&events_list))
+    {
+        Event *event = QTAILQ_FIRST(&events_list);
+
+        // perform an action
+        replay_run_event(event);
+
+        // go to next event
+        QTAILQ_REMOVE(&events_list, event, events);
+
+        g_free(event);
+    }
+    qemu_mutex_unlock(&lock);
+}
+
+void replay_disable_events(void)
+{
+    replay_events_enabled = false;
+    // Flush events queue before waiting of completion
+    replay_flush_events();
+}
+
+void replay_clear_events(void)
+{
+    qemu_mutex_lock(&lock);
+    while (!QTAILQ_EMPTY(&events_list))
+    {
+        Event *event = QTAILQ_FIRST(&events_list);
+        // go to next event
+        QTAILQ_REMOVE(&events_list, event, events);
+
+        g_free(event);
+    }
+    qemu_mutex_unlock(&lock);
+}
+
+static void replay_add_event_internal(unsigned int event_id, void *opaque, void *opaque2, uint64_t
id)
+{
+    if (event_id >= REPLAY_ASYNC_COUNT)
+    {
+        fprintf(stderr, "Replay: invalid async event ID (%d)\n", event_id);
+        exit(1);
+    }
+    if (!replay_file || replay_mode == REPLAY_NONE
+        || !replay_events_enabled
+        || replay_get_play_submode() == REPLAY_PLAY_CHANGED)
+    {
+        Event e;
+        e.event_id = event_id;
+        e.opaque = opaque;
+        e.opaque2 = opaque2;
+        e.id = id;
+        replay_run_event(&e);
+        return;
+    }
+
+    Event *event = g_malloc0(sizeof(Event));
+    event->event_id = event_id;
+    event->opaque = opaque;
+    event->opaque2 = opaque2;
+    event->id = id;
+
+    qemu_mutex_lock(&lock);
+    QTAILQ_INSERT_TAIL(&events_list, event, events);
+    qemu_mutex_unlock(&lock);
+}
+
+void replay_add_event(unsigned int event_id, void *opaque)
+{
+    replay_add_event_internal(event_id, opaque, NULL, 0);
+}
+
+void replay_add_usb_event(unsigned int event_id, uint64_t id, void *opaque)
+{
+    replay_add_event_internal(event_id, opaque, NULL, id);
+}
+
+void replay_add_bh_event(void *bh, uint64_t id)
+{
+    replay_add_event_internal(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
+}
+
+void replay_add_event2(unsigned int event_id, void *opaque, void *opaque2)
+{
+    replay_add_event_internal(event_id, opaque, opaque2, 0);
+}
+
+void replay_add_thread_event(void *opaque, void *opaque2, uint64_t id)
+{
+    replay_add_event_internal(REPLAY_ASYNC_EVENT_THREAD, opaque, opaque2, id);
+}
+
+void replay_save_events(int opt)
+{
+    qemu_mutex_lock(&lock);
+    while (!QTAILQ_EMPTY(&events_list))
+    {
+        Event *event = QTAILQ_FIRST(&events_list);
+        if (replay_mode != REPLAY_PLAY)
+        {
+            // put the event into the file
+            if (opt == -1)
+            {
+                replay_put_event(EVENT_ASYNC);
+            }
+            else
+            {
+                replay_put_event(EVENT_ASYNC_OPT);
+                replay_put_byte(opt);
+            }
+            replay_put_dword(event->event_id);
+
+            // save event-specific data
+            switch (event->event_id)
+            {
+            case REPLAY_ASYNC_EVENT_NETWORK:
+                replay_net_save_packet(event->opaque);
+                break;
+#ifdef CONFIG_USB_LIBUSB
+            case REPLAY_ASYNC_EVENT_USB_CTRL:
+            case REPLAY_ASYNC_EVENT_USB_DATA:
+                replay_put_qword(event->id);
+                replay_event_save_usb_xfer(event->opaque);
+                break;
+            case REPLAY_ASYNC_EVENT_USB_ISO:
+                replay_put_qword(event->id);
+                replay_event_save_usb_iso_xfer(event->opaque);
+                break;
+#endif
+            case REPLAY_ASYNC_EVENT_THREAD:
+            case REPLAY_ASYNC_EVENT_BH:
+                replay_put_qword(event->id);
+                break;
+            case REPLAY_ASYNC_EVENT_CHAR:
+                replay_event_char_save(event->opaque);
+                break;
+            case REPLAY_ASYNC_EVENT_INPUT:
+                replay_save_input_event(event->opaque);
+                break;
+            }
+        }
+
+        // perform an action
+        replay_run_event(event);
+
+        // go to next event
+        QTAILQ_REMOVE(&events_list, event, events);
+
+        g_free(event);
+    }
+    qemu_mutex_unlock(&lock);
+}
+
+bool replay_has_events(void)
+{
+    return !QTAILQ_EMPTY(&events_list);
+}
+
+
+void replay_read_events(int opt)
+{
+    replay_fetch_data_kind();
+    while ((opt == -1 && replay_data_kind == EVENT_ASYNC)
+        || (opt != -1 && replay_data_kind == EVENT_ASYNC_OPT))
+    {
+        if (read_event_id == -1)
+        {
+            if (opt != -1)
+            {
+                read_opt = replay_get_byte();
+            }
+            read_event_id = replay_get_dword();
+            read_bh_id = -1;
+            replay_check_error();
+        }
+        
+        if (opt != read_opt)
+            break;
+        // Execute some events without searching them in the queue
+        Event e;
+        switch (read_event_id)
+        {
+        case REPLAY_ASYNC_EVENT_NOTIFIER:
+            // Nothing to read 
+            break;
+        case REPLAY_ASYNC_EVENT_NETWORK:
+            e.opaque = replay_net_read_packet();
+            e.event_id = read_event_id;
+            replay_run_event(&e);
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+            // continue with the next event
+            continue;
+        case REPLAY_ASYNC_EVENT_THREAD:
+        case REPLAY_ASYNC_EVENT_BH:
+            if (read_bh_id == -1)
+            {
+                read_bh_id = replay_get_qword();
+            }
+            break;
+#ifdef CONFIG_USB_LIBUSB
+        case REPLAY_ASYNC_EVENT_USB_CTRL:
+        case REPLAY_ASYNC_EVENT_USB_DATA:
+        case REPLAY_ASYNC_EVENT_USB_ISO:
+            if (read_bh_id == -1)
+            {
+                read_bh_id = replay_get_qword();
+            }
+            // read after finding event in the list
+            break;
+#endif
+        case REPLAY_ASYNC_EVENT_CHAR:
+            e.event_id = read_event_id;
+            e.opaque = replay_event_char_read();
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+
+            replay_run_event(&e);
+            // continue with the next event
+            continue;
+        case REPLAY_ASYNC_EVENT_INPUT:
+            e.event_id = read_event_id;
+            e.opaque = replay_read_input_event();
+
+            replay_run_event(&e);
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+            // continue with the next event
+            continue;
+        case REPLAY_ASYNC_EVENT_INPUT_SYNC:
+            e.event_id = read_event_id;
+            e.opaque = 0;
+            replay_run_event(&e);
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+            // continue with the next event
+            continue;
+        default:
+            fprintf(stderr, "Unknown ID %d of replay event\n", read_event_id);
+            exit(1);
+            break;
+        }
+
+        qemu_mutex_lock(&lock);
+
+        Event *event = NULL;
+        Event *curr = NULL;
+        QTAILQ_FOREACH(curr, &events_list, events)
+        {
+            if (curr->event_id == read_event_id
+                && (read_bh_id == -1 || read_bh_id == curr->id))
+            {
+                event = curr;
+                break;
+            }
+        }
+
+        if (event)
+        {
+            // continue reading data
+#ifdef CONFIG_USB_LIBUSB
+            switch (read_event_id)
+            {
+            case REPLAY_ASYNC_EVENT_USB_CTRL:
+            case REPLAY_ASYNC_EVENT_USB_DATA:
+                replay_event_read_usb_xfer(event->opaque);
+                break;
+            case REPLAY_ASYNC_EVENT_USB_ISO:
+                replay_event_read_usb_iso_xfer(event->opaque);
+                break;
+            }
+#endif
+
+            QTAILQ_REMOVE(&events_list, event, events);
+
+            qemu_mutex_unlock(&lock);
+
+            // reset unread data and other parameters to allow
+            // reading other data from the log while
+            // running the event
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_bh_id = -1;
+            read_opt = -1;
+
+            replay_run_event(event);
+            g_free(event);
+
+            replay_fetch_data_kind();
+        }
+        else
+        {
+            qemu_mutex_unlock(&lock);
+            // No such event found in the queue
+            break;
+        }
+    }
+}
+
+
+void replay_init_events(void)
+{
+    read_event_id = -1;
+    qemu_mutex_init(&lock);
+}
+
+
+void replay_finish_events(void)
+{
+    replay_events_enabled = false;
+    replay_clear_events();
+    qemu_mutex_destroy(&lock);
+}
+
+
+void replay_skip_async_event(struct Monitor *mon, uint64_t step, bool opt)
+{
+    if (opt)
+    {
+        replay_get_byte();
+    }
+
+    int id = replay_get_dword();
+    switch (id)
+    {
+    case REPLAY_ASYNC_EVENT_NOTIFIER:
+        break;
+    case REPLAY_ASYNC_EVENT_NETWORK:
+        replay_net_skip_packet(mon, step);
+        break;
+    case REPLAY_ASYNC_EVENT_THREAD:
+    case REPLAY_ASYNC_EVENT_BH:
+        fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+        break;
+#ifdef CONFIG_USB_LIBUSB
+    case REPLAY_ASYNC_EVENT_USB_CTRL:
+    case REPLAY_ASYNC_EVENT_USB_DATA:
+        replay_get_qword();
+        replay_event_skip_usb_xfer();
+        break;
+    case REPLAY_ASYNC_EVENT_USB_ISO:
+        replay_get_qword();
+        replay_event_skip_usb_iso_xfer();
+        break;
+#endif
+    case REPLAY_ASYNC_EVENT_CHAR:
+        replay_event_char_skip();
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT:
+        replay_skip_input_event(mon, step);
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
+        break;
+    default:
+        fprintf(stderr, "Unknown ID %d of replay event found while skipping\n", read_event_id);
+        exit(1);
+        break;
+    }
+}
+
diff --git a/replay/replay-icount.c b/replay/replay-icount.c
new file mode 100644
index 0000000..c216a94
--- /dev/null
+++ b/replay/replay-icount.c
@@ -0,0 +1,112 @@ 
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-common.h"
+#include "sysemu/cpus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+#include "migration/vmstate.h"
+#include "replay.h"
+#include "replay-internal.h"
+
+int replay_icount;
+
+typedef struct {
+    /* Compensate for varying guest execution speed.  */
+    int64_t bias;
+    /* Timer for advancing VM clock, when all CPUs are sleeping */
+    QEMUTimer *icount_warp_timer;
+    int64_t vm_clock_warp_start;
+} ReplayIcount;
+static ReplayIcount icount_data;
+
+
+/* Return the virtual CPU time, based on the instruction counter.  */
+int64_t replay_get_icount(void)
+{
+    int64_t icount = replay_get_current_step();
+    return icount_data.bias + (icount << replay_icount);
+}
+
+static void replay_icount_warp_rt(void *opaque)
+{
+    if (icount_data.vm_clock_warp_start == -1) {
+        return;
+    }
+
+    if (runstate_is_running()) {
+        int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+        int64_t warp_delta = clock - icount_data.vm_clock_warp_start;
+        icount_data.bias += warp_delta;
+        if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
+            qemu_notify_event();
+        }
+    }
+    icount_data.vm_clock_warp_start = -1;
+}
+
+void replay_clock_warp(void)
+{
+    int64_t deadline;
+    if (!replay_checkpoint(9))
+        return;
+    /*
+     * If the CPUs have been sleeping, advance the vm_clock timer now.  This
+     * ensures that the deadline for the timer is computed correctly below.
+     * This also makes sure that the insn counter is synchronized before the
+     * CPU starts running, in case the CPU is woken by an event other than
+     * the earliest vm_clock timer.
+     */
+    if (icount_data.vm_clock_warp_start != -1) {
+        replay_icount_warp_rt(NULL);
+    }
+    if (!all_cpu_threads_idle() || !qemu_clock_has_timers(QEMU_CLOCK_VIRTUAL)) {
+        timer_del(icount_data.icount_warp_timer);
+        return;
+    }
+
+    icount_data.vm_clock_warp_start = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+    deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
+    if (deadline > 0) {
+        /*
+         * Ensure the vm_clock proceeds even when the virtual CPU goes to
+         * sleep.  Otherwise, the CPU might be waiting for a future timer
+         * interrupt to wake it up, but the interrupt never comes because
+         * the vCPU isn't running any insns and thus doesn't advance the
+         * vm_clock.
+         *
+         * An extreme solution for this problem would be to never let VCPUs
+         * sleep in icount mode if there is a pending vm_clock timer; rather
+         * time could just advance to the next vm_clock event.  Instead, we
+         * do stop VCPUs and only advance vm_clock after some "real" time,
+         * (related to the time left until the next event) has passed.  This
+         * rt_clock timer will do this.  This avoids that the warps are too
+         * visible externally---for example, you will not be sending network
+         * packets continuously instead of every 100ms.
+         */
+        timer_mod_ns(icount_data.icount_warp_timer, icount_data.vm_clock_warp_start + deadline);
+    } else {
+        qemu_notify_event();
+    }
+}
+
+static const VMStateDescription vmstate_icount = {
+    .name = "icount",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT64(bias, ReplayIcount),
+        VMSTATE_TIMER(icount_warp_timer, ReplayIcount),
+        VMSTATE_INT64(vm_clock_warp_start, ReplayIcount),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void replay_configure_icount(void)
+{
+    vmstate_register(NULL, 0, &vmstate_icount, &icount_data);
+    icount_data.icount_warp_timer = timer_new_ns(QEMU_CLOCK_HOST, replay_icount_warp_rt, NULL);
+}
diff --git a/replay/replay-input.c b/replay/replay-input.c
new file mode 100644
index 0000000..c4dc7c5
--- /dev/null
+++ b/replay/replay-input.c
@@ -0,0 +1,152 @@ 
+#include "replay.h"
+#include "replay-internal.h"
+#include "qapi-types.h"
+#include "sysemu/sysemu.h"
+#include "monitor/monitor.h"
+#include "ui/input.h"
+#include "qemu/log.h"
+
+void replay_save_input_event(InputEvent *evt)
+{
+    replay_put_dword(evt->kind);
+
+    switch (evt->kind)
+    {
+    case INPUT_EVENT_KIND_KEY:
+        replay_put_dword(evt->key->key->kind);
+
+        switch (evt->key->key->kind)
+        {
+        case KEY_VALUE_KIND_NUMBER:
+            replay_put_qword(evt->key->key->number);
+            replay_put_byte(evt->key->down);
+            break;
+        case KEY_VALUE_KIND_QCODE:
+            replay_put_dword(evt->key->key->qcode);
+            replay_put_byte(evt->key->down);
+            break;
+        case KEY_VALUE_KIND_MAX:
+            /* keep gcc happy */
+            break;
+        }
+        break;
+    case INPUT_EVENT_KIND_BTN:
+        replay_put_dword(evt->btn->button);
+        replay_put_byte(evt->btn->down);
+        break;
+    case INPUT_EVENT_KIND_REL:
+        replay_put_dword(evt->rel->axis);
+        replay_put_qword(evt->rel->value);
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        replay_put_dword(evt->abs->axis);
+        replay_put_qword(evt->abs->value);
+        break;
+    case INPUT_EVENT_KIND_MAX:
+        /* keep gcc happy */
+        break;
+    }
+}
+
+InputEvent *replay_read_input_event(void)
+{
+    static InputEvent evt;
+    static KeyValue keyValue;
+    static InputKeyEvent key;
+    key.key = &keyValue;
+    static InputBtnEvent btn;
+    static InputMoveEvent rel;
+    static InputMoveEvent abs;
+
+    evt.kind = replay_get_dword();
+    switch (evt.kind)
+    {
+    case INPUT_EVENT_KIND_KEY:
+        evt.key = &key;
+        evt.key->key->kind = replay_get_dword();
+
+        switch (evt.key->key->kind)
+        {
+        case KEY_VALUE_KIND_NUMBER:
+            evt.key->key->number = replay_get_qword();
+            evt.key->down = replay_get_byte();
+            break;
+        case KEY_VALUE_KIND_QCODE:
+            evt.key->key->qcode = (QKeyCode)replay_get_dword();
+            evt.key->down = replay_get_byte();
+            break;
+        case KEY_VALUE_KIND_MAX:
+            /* keep gcc happy */
+            break;
+        }
+        break;
+    case INPUT_EVENT_KIND_BTN:
+        evt.btn = &btn;
+        evt.btn->button = (InputButton)replay_get_dword();
+        evt.btn->down = replay_get_byte();
+        break;
+    case INPUT_EVENT_KIND_REL:
+        evt.rel = &rel;
+        evt.rel->axis = (InputAxis)replay_get_dword();
+        evt.rel->value = replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        evt.abs = &abs;
+        evt.abs->axis = (InputAxis)replay_get_dword();
+        evt.abs->value = replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_MAX:
+        /* keep gcc happy */
+        break;
+    }
+
+    return &evt;
+}
+
+void replay_skip_input_event(struct Monitor *mon, uint64_t step)
+{
+    switch (replay_get_dword())
+    {
+    case INPUT_EVENT_KIND_KEY:
+        switch (replay_get_dword())
+        {
+        case KEY_VALUE_KIND_NUMBER:
+            {
+                int64_t number = replay_get_qword();
+                bool down = replay_get_byte();
+                monitor_printf(mon, "%" PRId64 ": Key %"PRIx64" %s\n", step, number, down ?
"pressed" : "released");
+            }
+            break;
+        case KEY_VALUE_KIND_QCODE:
+            {
+                int qcode = replay_get_dword();
+                bool down = replay_get_byte();
+                monitor_printf(mon, "%" PRId64 ": Key %d %s\n", step, qcode, down ? "pressed" :
"released");
+            }
+            break;
+        case KEY_VALUE_KIND_MAX:
+            /* keep gcc happy */
+            break;
+        }
+        break;
+    case INPUT_EVENT_KIND_BTN:
+        {
+            int btn = replay_get_dword();
+            bool down = replay_get_byte();
+            monitor_printf(mon, "%" PRId64 ": Mouse buttons %d %s\n", step, btn, down ? "pressed" :
"released");
+        }
+        break;
+    case INPUT_EVENT_KIND_REL:
+        replay_get_dword();
+        replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        replay_get_dword();
+        replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_MAX:
+        /* keep gcc happy */
+        break;
+    }
+}
+
diff --git a/replay/replay-internal.c b/replay/replay-internal.c
new file mode 100644
index 0000000..96dda4d
--- /dev/null
+++ b/replay/replay-internal.c
@@ -0,0 +1,176 @@ 
+//#include "buffered_file.h"
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "qemu/queue.h"
+#include "replay-internal.h"
+
+volatile unsigned int replay_data_kind = -1;
+volatile unsigned int replay_has_unread_data;
+
+/* File for replay writing */
+FILE *replay_file;
+
+void replay_put_byte(unsigned char byte)
+{
+    if (replay_file)
+    {
+        fwrite(&byte, sizeof(byte), 1, replay_file);
+    }
+}
+
+void replay_put_event(unsigned char event)
+{
+    replay_put_byte(event);
+}
+
+
+void replay_put_word(uint16_t word)
+{
+    if (replay_file)
+        fwrite(&word, sizeof(word), 1, replay_file);
+}
+
+void replay_put_dword(unsigned int dword)
+{
+    if (replay_file)
+        fwrite(&dword, sizeof(dword), 1, replay_file);
+}
+
+void replay_put_qword(int64_t qword)
+{
+    if (replay_file)
+        fwrite(&qword, sizeof(qword), 1, replay_file);
+}
+
+
+void replay_put_array(const uint8_t *buf, size_t size)
+{
+    if (replay_file)
+    {
+        fwrite(&size, sizeof(size), 1, replay_file);
+        fwrite(buf, 1, size, replay_file);
+    }
+}
+
+
+
+unsigned char replay_get_byte(void)
+{
+    unsigned char byte;
+    if (replay_file)
+    {
+        fread(&byte, sizeof(byte), 1, replay_file);
+    }
+    return byte;
+}
+
+uint16_t replay_get_word(void)
+{
+    uint16_t word;
+    if (replay_file)
+        fread(&word, sizeof(word), 1, replay_file);
+
+    return word;
+}
+
+unsigned int replay_get_dword(void)
+{
+    unsigned int dword;
+    if (replay_file)
+        fread(&dword, sizeof(dword), 1, replay_file);
+
+    return dword;
+}
+
+int64_t replay_get_qword(void)
+{
+    int64_t qword;
+    if (replay_file)
+        fread(&qword, sizeof(qword), 1, replay_file);
+
+    return qword;
+}
+
+
+void replay_get_array(uint8_t *buf, size_t *size)
+{
+    if (replay_file)
+    {
+        fread(size, sizeof(*size), 1, replay_file);
+        fread(buf, 1, *size, replay_file);
+    }
+}
+
+void replay_get_array_alloc(uint8_t **buf, size_t *size)
+{
+    if (replay_file)
+    {
+        fread(size, sizeof(*size), 1, replay_file);
+        *buf = g_malloc(*size);
+        fread(*buf, 1, *size, replay_file);
+    }
+}
+
+void replay_check_error(void)
+{
+    if (replay_file)
+    {
+        if (feof(replay_file))
+        {
+            fprintf(stderr, "replay file is over\n");
+            exit(1);
+        }
+        else if (ferror(replay_file))
+        {
+            fprintf(stderr, "replay file is over or something goes wrong\n");
+            exit(1);
+        }
+    }
+}
+
+
+
+void replay_fetch_data_kind(void)
+{
+    if (replay_file)
+    {
+        if (!replay_has_unread_data)
+        {
+            replay_data_kind = replay_get_byte();
+            replay_check_error();
+            replay_has_unread_data = 1;
+        }
+    }
+}
+
+//! Saves cached instructions.
+void replay_save_instructions(void)
+{
+    if (replay_file && replay_mode == REPLAY_SAVE)
+    {
+        if (first_cpu != NULL && first_cpu->instructions_count > 0)
+        {
+            replay_put_event(EVENT_INSTRUCTION);
+            replay_put_dword(first_cpu->instructions_count);
+            replay_state.current_step += first_cpu->instructions_count;
+            first_cpu->instructions_count = 0;
+        }
+#ifdef _WIN32
+        replay_save_sound_in();
+        replay_save_sound_out();
+#endif
+    }
+}
+
+
+void validate_data_kind(int kind)
+{
+    replay_fetch_data_kind();
+    if (replay_data_kind != kind)
+    {
+        fprintf(stderr, "%" PRId64 ": read data kind %d instead of expected %d\n",
+            replay_get_current_step(), replay_data_kind, kind);
+        exit(1);
+    }
+}
+
diff --git a/replay/replay-internal.h b/replay/replay-internal.h
new file mode 100644
index 0000000..10d5734
--- /dev/null
+++ b/replay/replay-internal.h
@@ -0,0 +1,192 @@ 
+#ifndef REPLAY_INTERNAL_H
+#define REPLAY_INTERNAL_H
+
+#include <stdio.h>
+
+// internal data for savevm
+#define EVENT_END_STARTUP           0
+// for time_t event
+#define EVENT_TIME_T	            1
+// for tm event
+#define EVENT_TM                    2
+// for outgoing sound event
+#define EVENT_SOUND_OUT             7
+// for incoming sound event
+#define EVENT_SOUND_IN              8
+// mouse input events
+#define EVENT_INPUT                 10
+// for software interrupt
+#define EVENT_INTERRUPT             15
+// for debug information
+#define EVENT_ASSERT                17
+// for timing information
+#define EVENT_TARGET_TIME           19
+// for shutdown request
+#define EVENT_SHUTDOWN              20
+// for save VM event
+#define EVENT_SAVE_VM_BEGIN         21
+// for save VM event
+#define EVENT_SAVE_VM_END           22
+// for emulated exceptions
+#define EVENT_EXCEPTION             23
+// for async events
+#define EVENT_ASYNC                 24
+#define EVENT_ASYNC_OPT             25
+// for int data
+#define EVENT_DATA_INT              26
+// for data buffer
+#define EVENT_DATA_BUFFER           27
+// for checkoint event
+#define EVENT_INSTRUCTION           32
+// for clock read/writes
+#define EVENT_CLOCK                 64
+// some of grteater codes are reserved for clocks
+
+// for checkpoint event
+#define EVENT_CHECKPOINT            96
+// end of log event
+#define EVENT_END                   127
+
+// limited by Ethernet frame size
+#define MAX_NET_PACKET_SIZE             1560
+
+/*! Information about saved VM state */
+struct SavedStateInfo
+{
+    // Offset in the replay log file where state is saved.
+    uint64_t file_offset;
+    // Step number, corresponding to the saved state.
+    uint64_t step;
+};
+
+struct Monitor;
+
+typedef struct ReplayState
+{
+    //! Cached clock values.
+    int64_t cached_clock[REPLAY_CLOCK_COUNT];
+    //! Nonzero, when next instruction is repeated one and was already
+    //! processed.
+    int skipping_instruction;
+    /*! Current step - number of processed instructions and timer events. */
+    uint64_t current_step;
+
+    /*! Temporary data for saving/loading replay file position. */
+    uint64_t file_offset;
+} ReplayState;
+extern ReplayState replay_state;
+
+/*! Reference to the saved state */
+typedef struct SavedStateInfo SavedStateInfo;
+
+extern volatile unsigned int replay_data_kind;
+extern volatile unsigned int replay_has_unread_data;
+
+extern int play_submode;
+extern SavedStateInfo *saved_states;
+
+/* File for replay writing */
+extern FILE *replay_file;
+
+void replay_put_byte(unsigned char byte);
+void replay_put_event(unsigned char event);
+void replay_put_word(uint16_t word);
+void replay_put_dword(unsigned int dword);
+void replay_put_qword(int64_t qword);
+void replay_put_array(const uint8_t *buf, size_t size);
+
+unsigned char replay_get_byte(void);
+uint16_t replay_get_word(void);
+unsigned int replay_get_dword(void);
+int64_t replay_get_qword(void);
+void replay_get_array(uint8_t *buf, size_t *size);
+void replay_get_array_alloc(uint8_t **buf, size_t *size);
+
+//! Checks error status of the file.
+void replay_check_error(void);
+
+//! Reads data type from the file and stores it in the 
+//! replay_data_kind variable.
+void replay_fetch_data_kind(void);
+
+//! Saves queued events (like instructions and sound).
+void replay_save_instructions(void);
+//! Checks that the next data is corresponding to the desired kind.
+//! Terminates the program in case of error.
+void validate_data_kind(int kind);
+
+
+/*! Saves events from queue into the file */
+void replay_save_events(int opt);
+/*! Returns true if there are any unsaved events in the queue */
+bool replay_has_events(void);
+/*! Read events from the file into the input queue */
+void replay_read_events(int opt);
+/*! Initializes events' processing internals */
+void replay_init_events(void);
+/*! Clears internal data structures for events handling */
+void replay_finish_events(void);
+/*! Skips async event in the log file.
+    Called by the replay_events function.
+    If the event is network packet it should be printed to monitor. */
+void replay_skip_async_event(struct Monitor *mon, uint64_t step, bool opt);
+/*! Clears events list before loading new VM state */
+void replay_clear_events(void);
+
+/*! Finds saved state info which is nearest before the specified step. */
+SavedStateInfo *find_nearest_state(uint64_t step);
+
+
+// USB events
+void replay_event_usb_ctrl(void *opaque);
+void replay_event_usb_data(void *opaque);
+void replay_event_usb_iso(void *opaque);
+void replay_event_save_usb_xfer(void *opaque);
+void replay_event_save_usb_iso_xfer(void *opaque);
+void replay_event_read_usb_xfer(void *opaque);
+void replay_event_read_usb_iso_xfer(void *opaque);
+void replay_event_skip_usb_xfer(void);
+void replay_event_skip_usb_iso_xfer(void);
+bool replay_usb_has_xfers(void);
+void replay_add_usb_event(unsigned int event_id, uint64_t id, void *opaque);
+
+/*! Called to run char device event. */
+void replay_event_char_run(void *opaque);
+/*! Writes char event to the file. */
+void replay_event_char_save(void *opaque);
+/*! Reads char event from the file. */
+void *replay_event_char_read(void);
+/*! Skips char event. */
+void replay_event_char_skip(void);
+/*! Flushes events queue */
+void replay_flush_events(void);
+
+// icount
+void replay_configure_icount(void);
+
+// input events
+void replay_save_input_event(InputEvent *evt);
+InputEvent *replay_read_input_event(void);
+void replay_skip_input_event(struct Monitor *mon, uint64_t step);
+
+// sound events
+bool replay_has_sound_events(void);
+void replay_save_sound_out(void);
+void replay_save_sound_in(void);
+void replay_read_sound_out(void);
+void replay_read_sound_in(void);
+void replay_sound_flush_queue(void);
+
+// Network events
+void replay_net_init(void);
+void replay_net_free(void);
+void replay_net_read_packets_data(void);
+void replay_net_write_packets_data(void);
+void replay_net_save_packet(void *opaque);
+void *replay_net_read_packet(void);
+void replay_net_skip_packet(struct Monitor *mon, uint64_t step);
+/*! Called to send packet that was read or received from external input
+    to the net queue. */
+void replay_net_send_packet(void *opaque);
+
+#endif // REPLAY_INTERNAL_H
diff --git a/replay/replay-net.c b/replay/replay-net.c
new file mode 100644
index 0000000..6c39713
--- /dev/null
+++ b/replay/replay-net.c
@@ -0,0 +1,367 @@ 
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "qemu/log.h"
+#include "net/net.h"
+#include "slirp/slirp.h"
+#include "slirp/libslirp.h"
+#include "monitor/monitor.h"
+#include "replay.h"
+#include "replay-internal.h"
+
+/* Network data */
+NetClientState **vlan_states = NULL;
+size_t vlan_states_count = 0;
+size_t vlan_states_capacity = 0;
+
+/* Structure for storing information about the network packet */
+typedef struct
+{
+    /* Offset in the replay log file where packet is saved. */
+    uint64_t file_offset;
+    /* Number of step when packet came. */
+    uint64_t step;
+} QEMU_PACKED NetPacketInfo;
+
+typedef struct NetPacketQueue
+{
+    /* ID of the packet */
+    uint64_t id;
+    /* ID of the network client */
+    int32_t nc_id;
+    size_t size;
+    uint8_t buf[MAX_NET_PACKET_SIZE];
+    uint64_t offset;
+} NetPacketQueue;
+
+/* Network packets count. */
+static uint64_t net_packets_count;
+/* Capacity of the array for packets parameters. */
+static uint64_t net_packets_capacity;
+/* Array for storing network packets parameters. */
+static NetPacketInfo *net_packets;
+
+/* Data for network fuzzing - modified versions of packets */
+
+/* Packet data */
+typedef struct ModifiedNetPacketInfo
+{
+    /* ID of the packet */
+    int64_t id;
+    /* Size of the packet */
+    size_t size;
+    /* Contents of the packet */
+    uint8_t *buf;
+
+    QLIST_ENTRY(ModifiedNetPacketInfo) next;
+} ModifiedNetPacketInfo;
+
+static QLIST_HEAD(, ModifiedNetPacketInfo) modified_net_packets =
+    QLIST_HEAD_INITIALIZER(modified_net_packets);
+
+
+void replay_net_init(void)
+{
+    net_packets_count = 0;
+}
+
+void replay_net_read_packets_data(void)
+{
+    // read network packets info
+    net_packets_count = replay_get_qword();
+    net_packets_capacity = net_packets_count;
+    if (net_packets_count)
+    {
+        net_packets = (NetPacketInfo*)g_malloc(sizeof(NetPacketInfo) * net_packets_count);
+        if (fread(net_packets, sizeof(NetPacketInfo), net_packets_count, replay_file) !=
net_packets_count)
+        {
+            fprintf(stderr, "Internal error in replay_net_read_packets_data\n");
+            exit(1);
+        }
+    }
+}
+
+void replay_net_write_packets_data(void)
+{
+    // write network packets info
+    replay_put_qword(net_packets_count);
+    if (net_packets && net_packets_count)
+    {
+        // TODO ??? Save distinct fields in the loop
+        fwrite(net_packets, sizeof(NetPacketInfo), net_packets_count, replay_file);
+    }
+}
+
+void replay_add_network_client(NetClientState *nc)
+{
+    if (vlan_states_count == 0)
+    {
+        vlan_states = (NetClientState **)g_malloc(sizeof(*vlan_states));
+        vlan_states_count = 0;
+        vlan_states_capacity = 1;
+    }
+    else if (vlan_states_count == vlan_states_capacity)
+    {
+        vlan_states_capacity *= 2;
+        vlan_states = (NetClientState **)g_realloc(vlan_states, sizeof(*vlan_states) *
vlan_states_capacity);
+    }
+
+    vlan_states[vlan_states_count++] = nc;
+}
+
+void replay_net_free(void)
+{
+    // free modified packets
+    ModifiedNetPacketInfo *packet, *nextPacket;
+    QLIST_FOREACH_SAFE(packet, &modified_net_packets, next, nextPacket)
+    {
+        QLIST_REMOVE(packet, next);
+        g_free(packet->buf);
+        g_free(packet);
+    }
+
+    if (vlan_states)
+    {
+        g_free(vlan_states);
+        vlan_states = NULL;
+    }
+}
+
+
+//! Saves network packet into the log.
+void replay_save_net_packet(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    if (replay_file)
+    {
+        // save packet info into the array
+        if (net_packets_capacity == net_packets_count)
+        {
+            if (net_packets_capacity == 0)
+                net_packets_capacity = 1;
+            else
+                net_packets_capacity *= 2;
+            net_packets = (NetPacketInfo*)g_realloc(net_packets, net_packets_capacity *
sizeof(NetPacketInfo));
+        }
+
+        // add packet processing event to the queue
+        NetPacketQueue *p = (NetPacketQueue*)g_malloc0(sizeof(NetPacketQueue));
+        p->id = net_packets_count;
+        p->size = size;
+        if (net_hub_id_for_client(nc, &p->nc_id) < 0)
+        {
+            fprintf(stderr, "Replay: Cannot determine net client id\n");
+            exit(1);
+        }
+        memcpy(p->buf, buf, size);
+        replay_add_event(REPLAY_ASYNC_EVENT_NETWORK, p);
+
+        ++net_packets_count;
+    }
+}
+
+
+static ModifiedNetPacketInfo *find_modified_packet(int64_t id)
+{
+    ModifiedNetPacketInfo *packet = NULL;
+    QLIST_FOREACH(packet, &modified_net_packets, next)
+    {
+        if (packet->id == id)
+            return packet;
+    }
+
+    return NULL;
+}
+
+static NetClientState *replay_net_find_vlan(NetPacketQueue *packet)
+{
+    int i;
+    for (i = 0 ; i < vlan_states_count ; ++i)
+    {
+        int id = 0;
+        if (net_hub_id_for_client(vlan_states[i], &id) < 0)
+        {
+            fprintf(stderr, "Replay: Cannot determine net client id\n");
+            exit(1);
+        }
+        if (id == packet->nc_id)
+            return vlan_states[i];
+    }
+
+    fprintf(stderr, "Replay: please specify -net replay command-line option\n");
+    exit(1);
+
+    return NULL;
+}
+
+void replay_net_send_packet(void *opaque)
+{
+    NetPacketQueue *packet = (NetPacketQueue *)opaque;
+    NetClientState *vlan_state = replay_net_find_vlan(packet);
+
+    if (replay_mode == REPLAY_SAVE)
+    {
+        net_packets[packet->id].file_offset = packet->offset;
+        net_packets[packet->id].step = replay_get_current_step();
+
+        qemu_send_packet(vlan_state, packet->buf, packet->size);
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        // fuzzing or not
+        ModifiedNetPacketInfo *mod_packet = find_modified_packet(packet->id);
+        if (mod_packet)
+        {
+            replay_set_play_changed();
+            qemu_send_packet(vlan_state, mod_packet->buf, mod_packet->size);
+        }
+        else
+        {
+            qemu_send_packet(vlan_state, packet->buf, packet->size);
+        }
+    }
+
+    g_free(packet);
+}
+
+int replay_get_packet(struct Monitor *mon, int64_t id)
+{
+    if (replay_mode == REPLAY_PLAY && replay_file)
+    {
+        if (id < 0 || id >= net_packets_count)
+            return 0;
+
+        uint64_t offset = ftello64(replay_file);
+
+        size_t size;
+        // find the packet
+        fseeko64(replay_file, net_packets[id].file_offset, SEEK_SET);
+        // skip the id and nc_id
+        fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+        fseek(replay_file, sizeof(uint32_t), SEEK_CUR);
+
+        monitor_printf(mon, "Packet: ");
+        // read the packet
+        if (fread(&size, sizeof(size), 1, replay_file) != 1)
+        {
+            fprintf(stderr, "Internal error in replay_get_packet\n");
+            exit(1);
+        }
+        while (size--)
+        {
+            uint8_t byte;
+            if (fread(&byte, sizeof(byte), 1, replay_file) != 1)
+            {
+                fprintf(stderr, "Internal error in replay_get_packet\n");
+                exit(1);
+            }
+            //monitor_printf(mon, "%02x", byte);
+            monitor_print_bytes(mon, &byte, sizeof(byte));
+        }
+        monitor_printf(mon, "#\n\n");
+
+        // restore original position
+        fseeko64(replay_file, offset, SEEK_SET);
+
+        //**************************** printing modified packet
+        ModifiedNetPacketInfo *packet = find_modified_packet(id);
+        if (packet != NULL)
+        {
+            monitor_printf(mon, "Modified packet: ");
+            size_t i = 0;
+            for ( ; i < packet->size ; ++i)
+                monitor_print_bytes(mon, &packet->buf[i], sizeof(uint8_t));
+
+            monitor_printf(mon, "#\n\n");
+        }
+        //****************************
+
+        return 1;
+    }
+
+    return 0;
+}
+
+
+int replay_set_packet(struct Monitor *mon, int64_t id, const char *bytes)
+{
+    if (replay_mode == REPLAY_PLAY && replay_file)
+    {
+        if (bytes == NULL)
+        {
+            // remove packet
+            ModifiedNetPacketInfo *packet = find_modified_packet(id);
+            if (packet == NULL)
+                return 0;
+
+            QLIST_REMOVE(packet, next);
+            g_free(packet->buf);
+            g_free(packet);
+
+            return 1;
+        }
+        else
+        {
+            ModifiedNetPacketInfo *packet = find_modified_packet(id);
+            if (!packet)
+            {
+                // add packet
+                packet = (ModifiedNetPacketInfo *)g_malloc(sizeof(*packet));
+                QLIST_INSERT_HEAD(&modified_net_packets, packet, next);
+                packet->id = id;
+            }
+            else
+            {
+                g_free(packet->buf);
+            }
+
+            packet->size = strlen(bytes) / 2;
+            packet->buf = g_malloc(packet->size);
+            size_t i = 0;
+            for ( ; i < packet->size ; ++i)
+            {
+                packet->buf[i] = replay_xtoi(bytes[2 * i]) * 16 + replay_xtoi(bytes[2 * i + 1]);
+            }
+
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void replay_net_save_packet(void *opaque)
+{
+    NetPacketQueue *p = (NetPacketQueue*)opaque;
+    p->offset = ftello64(replay_file);
+    replay_put_qword(p->id);
+    replay_put_dword(p->nc_id);
+    replay_put_array(p->buf, p->size);
+    replay_network_packets += p->size;
+}
+
+void *replay_net_read_packet(void)
+{
+    NetPacketQueue *p = g_malloc0(sizeof(NetPacketQueue));;
+    p->id = replay_get_qword();
+    p->nc_id = replay_get_dword();
+    replay_get_array(p->buf, &p->size);
+    replay_check_error();
+
+    return p;
+}
+
+void replay_net_skip_packet(struct Monitor *mon, uint64_t step)
+{
+    uint64_t id;
+    int nc_id;
+    size_t size;
+    if (fread(&id, sizeof(id), 1, replay_file) != 1
+        || fread(&nc_id, sizeof(nc_id), 1, replay_file) != 1
+        || fread(&size, sizeof(size), 1, replay_file) != 1)
+    {
+        fprintf(stderr, "Internal error while skipping network packet\n");
+        exit(1);
+    }
+    fseek(replay_file, size, SEEK_CUR);
+    monitor_printf(mon, "%" PRId64 ": Net packet net=%d id=%" PRId64 "\n", step, nc_id, id);
+}
+
diff --git a/replay/replay-usb.c b/replay/replay-usb.c
new file mode 100644
index 0000000..9ee96e8
--- /dev/null
+++ b/replay/replay-usb.c
@@ -0,0 +1,241 @@ 
+#include "qemu-common.h"
+#include "replay.h"
+#include "replay-internal.h"
+#include "hw/usb.h"
+
+#ifdef CONFIG_USB_LIBUSB
+#include "hw/host-libusb.h"
+
+static uint64_t replay_get_xfer_id(struct libusb_transfer *xfer)
+{
+    USBHostRequest *r = xfer->user_data;
+    USBHostDevice *host = r->host;
+
+    return ((uint64_t)host->match.vendor_id << 32)
+           | host->match.product_id;
+}
+
+static uint64_t replay_get_iso_xfer_id(struct libusb_transfer *xfer)
+{
+    USBHostIsoXfer *r = xfer->user_data;
+    USBHostDevice  *host = r->ring->host;
+
+    return ((uint64_t)host->match.vendor_id << 32)
+           | host->match.product_id;
+}
+
+void replay_req_complete_ctrl(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_CTRL,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+void replay_req_register_ctrl(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_CTRL,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+
+void replay_event_usb_ctrl(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+
+    usb_host_req_complete_ctrl(xfer);
+}
+
+void replay_event_save_usb_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostRequest *r = xfer->user_data;
+    if (replay_mode == REPLAY_SAVE)
+    {
+        // save data
+        replay_put_dword(xfer->status);
+        replay_put_dword(xfer->actual_length);
+        replay_put_array(xfer->buffer, r->in ? xfer->length : 0);
+    }
+}
+
+void replay_event_save_usb_iso_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostIsoXfer *iso = xfer->user_data;
+    int i;
+    if (replay_mode == REPLAY_SAVE)
+    {
+        bool in = iso->ring->ep->pid == USB_TOKEN_IN;
+        // save data
+        replay_put_dword(xfer->status);
+        replay_put_dword(xfer->num_iso_packets);
+        for (i = 0 ; i < xfer->num_iso_packets ; ++i)
+        {
+            // all other fields of the packet are not used
+            unsigned int len = xfer->iso_packet_desc[i].actual_length;
+            //replay_put_dword(len);
+            if (/*len && */in)
+            {
+                replay_put_array(usb_host_get_iso_packet_buffer(iso, i), len);
+            }
+        }
+    }
+}
+
+void replay_event_read_usb_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostRequest *r = xfer->user_data;
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        // read data here
+        xfer->status = replay_get_dword();
+        xfer->actual_length = replay_get_dword();
+        size_t sz;
+        replay_get_array(xfer->buffer, &sz);
+        if (r->in && xfer->length != (int)sz)
+        {
+            fprintf(stderr, "Replay: trying to read USB control/data buffer with unexpected
size\n");
+            exit(1);
+        }
+    }
+}
+
+void replay_event_read_usb_iso_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostIsoXfer *iso = xfer->user_data;
+    int i;
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        bool in = iso->ring->ep->pid == USB_TOKEN_IN;
+        // read data here
+        xfer->status = replay_get_dword();
+        xfer->num_iso_packets = replay_get_dword();
+        for (i = 0 ; i < xfer->num_iso_packets ; ++i)
+        {
+            // all other fields of the packet are not used
+            //unsigned int len = replay_get_dword();
+            if (in)
+            {
+                size_t sz;
+                replay_get_array(usb_host_get_iso_packet_buffer(iso, i), &sz);
+                xfer->iso_packet_desc[i].actual_length = (unsigned int)sz;
+            }
+        }
+    }
+}
+
+void replay_event_skip_usb_xfer(void)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        size_t sz;
+        // data/ctrl
+        replay_get_dword();
+        replay_get_dword();
+        if (fread(&sz, sizeof(sz), 1, replay_file) != 1)
+        {
+            fprintf(stderr, "Internal error while skipping usb xfer\n");
+            exit(1);
+        }
+        fseek(replay_file, sz, SEEK_CUR);
+    }
+}
+
+void replay_event_skip_usb_iso_xfer(void)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        // status
+        replay_get_dword();
+        // count
+        int num = replay_get_dword();
+        // packets
+        while (num--)
+        {
+            size_t sz;
+            if (fread(&sz, sizeof(sz), 1, replay_file) != 1)
+            {
+                fprintf(stderr, "Internal error while skipping usb iso xfer\n");
+                exit(1);
+            }
+            fseek(replay_file, sz, SEEK_CUR);
+        }
+    }
+}
+
+
+void replay_req_complete_data(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_DATA,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+void replay_req_register_data(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_DATA,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+
+void replay_event_usb_data(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+
+    usb_host_req_complete_data(xfer);
+}
+
+
+void replay_req_complete_iso(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_ISO,
+                              replay_get_iso_xfer_id(xfer), xfer);
+    }
+}
+
+void replay_req_register_iso(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+    USBHostIsoXfer *r = xfer->user_data;
+    USBHostDevice  *s = r->ring->host;
+
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_ISO,
+                              replay_get_iso_xfer_id(xfer), xfer);
+    }
+}
+
+
+void replay_event_usb_iso(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+
+    usb_host_req_complete_iso(xfer);
+}
+
+#endif
+
+bool replay_usb_has_xfers(void)
+{
+#ifdef CONFIG_USB_LIBUSB
+    return usb_host_has_xfers();
+#else
+    return false;
+#endif
+}
diff --git a/replay/replay.c b/replay/replay.c
new file mode 100644
index 0000000..6897c88
--- /dev/null
+++ b/replay/replay.c
@@ -0,0 +1,1604 @@ 
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+
+#include "replay.h"
+#include "replay-internal.h"
+
+#include "exec/cpu-defs.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "ui/console.h"
+#include "monitor/monitor.h"
+#include "ui/keymaps.h"
+#include "exec/exec-all.h"
+#include "qapi/qmp/qlist.h"
+#include "qemu/thread.h"
+#include "sysemu/char.h"
+#include "cpu.h"
+#include "hw/ide/internal.h"
+#include "hw/ide/pci.h"
+#include "block/block_int.h"
+#include "sysemu/blockdev.h"
+
+// Current version of the replay mechanism.
+// Increase it when file format changes.
+#define REPLAY_VERSION              0xe02001
+// Size of replay log header
+#define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
+
+int replay_mode = REPLAY_NONE;
+/*! Stores current submode for PLAY mode */
+int play_submode = REPLAY_PLAY_UNKNOWN;
+
+uint64_t replay_network_packets;
+
+ReplayState replay_state;
+
+/*! Step for stopping execution at. */
+static uint64_t replay_break_step = -1;
+/*! Non-zero, when debug events should be written */
+static int debug_mode = 0;
+
+/* Offsets of clocks used for switching from replay to execution */
+int64_t realtime_clock_replay_offset;
+int64_t real_ticks_replay_offset;
+
+/* Name of replay file  */
+static char *replay_filename;
+/* Suffix for the disk images filenames */
+char *replay_image_suffix;
+
+int not_compatible_replay_param = 0;
+
+/* 
+    Auto-saving for VM states data 
+*/
+
+/* Minimum capacity of saved states information array */
+#define SAVED_STATES_MIN_CAPACITY   128
+/* Format of the name for the saved state */
+#define SAVED_STATE_NAME_FORMAT     "replay_%" PRId64
+
+/* Timer for auto-save VM states */
+static QEMUTimer *save_timer;
+/* Save state period in seconds */
+static uint64_t save_state_period;
+/* List of the saved states information */
+SavedStateInfo *saved_states;
+/* Number of saved states */
+static size_t saved_states_count;
+/* Capacity of the buffer for saved states */
+static size_t saved_states_capacity;
+/* Number of last loaded/saved state */
+static uint64_t current_saved_state;
+
+//* Target time */
+
+/* Timer for auto-save target RTC */
+static QEMUTimer *target_timer;
+/* Target timer saving period */
+static uint64_t target_timer_period;
+/* Additional parameter for function which provides time */
+static void *replay_target_time_dev;
+/* Pointer to function which provides time */
+static replay_get_date_func replay_get_target_time;
+
+/*****************************************************************************
+ *  Replay functions
+ *****************************************************************************/
+
+int replay_get_play_submode(void)
+{
+    return play_submode;
+}
+
+
+void replay_set_play_changed(void)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        replay_change(NULL);
+    }
+}
+
+
+static void replay_pre_save(void *opaque)
+{
+    ReplayState *state = opaque;
+    state->file_offset = ftello64(replay_file);
+}
+
+
+static int replay_post_load(void *opaque, int version_id)
+{
+    first_cpu->instructions_count = 0;
+
+    // seek the replay file
+    ReplayState *state = opaque;
+    fseeko64(replay_file, state->file_offset, SEEK_SET);
+    replay_has_unread_data = 0;
+
+    return 0;
+}
+
+
+static const VMStateDescription vmstate_replay = {
+    .name = "replay",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = replay_pre_save,
+    .post_load = replay_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT64_ARRAY(cached_clock, ReplayState, REPLAY_CLOCK_COUNT),
+        VMSTATE_INT32(skipping_instruction, ReplayState),
+        VMSTATE_UINT64(current_step, ReplayState),
+        VMSTATE_UINT64(file_offset, ReplayState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static void replay_savevm(void *opaque)
+{
+    char name[128];
+    uint64_t offset;
+
+#ifdef CONFIG_USB_LIBUSB
+    if (replay_usb_has_xfers())
+    {
+        // Retry of saving VM state, when USB host controller is not ready.
+        // We cannot save or interrupt non-finished transfers, so
+        // just wait for finishing them later.
+        timer_mod(save_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
+        return;
+    }
+#endif
+
+    offset = ftello64(replay_file);
+
+    replay_save_instructions();
+
+    replay_put_event(EVENT_SAVE_VM_BEGIN);
+
+    vm_stop(RUN_STATE_SAVE_VM);
+
+    // save VM state 
+    sprintf(name, SAVED_STATE_NAME_FORMAT, current_saved_state);
+    if (save_vmstate(default_mon, name) > 0)
+    {
+        // if period is 0, save only once
+        if (save_state_period != 0)
+        {
+            timer_mod(save_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + save_state_period);
+        }
+
+        // add more memory to buffer
+        if (saved_states_count >= saved_states_capacity)
+        {
+            saved_states_capacity += SAVED_STATES_MIN_CAPACITY;
+            saved_states = g_realloc(saved_states, saved_states_capacity * sizeof(SavedStateInfo));
+            if (!saved_states)
+            {
+                saved_states_count = 0;
+                fprintf(stderr, "Replay: Saved states memory reallocation failed.\n");
+                exit(1);
+            }
+        }
+        // save state ID into the buffer
+        saved_states[saved_states_count].file_offset = offset;
+        saved_states[saved_states_count].step = replay_get_current_step();
+        ++saved_states_count;
+        ++current_saved_state;
+    }
+    else
+    {
+        fprintf(stderr, "Cannot save simulator states for replay.\n");
+    }
+
+    replay_put_event(EVENT_SAVE_VM_END);
+
+    tb_flush_all();
+
+    vm_start();
+}
+
+
+static void replay_save_target_timer(void *opaque)
+{
+    struct tm datetime;
+
+    if (replay_mode != REPLAY_SAVE
+        || !replay_get_target_time)
+    {
+        return;
+    }
+
+    // get time
+    replay_get_target_time(replay_target_time_dev, &datetime);
+
+    replay_put_event(EVENT_TARGET_TIME);
+    replay_put_dword(datetime.tm_sec);
+    replay_put_dword(datetime.tm_min);
+    replay_put_dword(datetime.tm_hour);
+    replay_put_dword(datetime.tm_mday);
+    replay_put_dword(datetime.tm_mon);
+    replay_put_dword(datetime.tm_year);
+
+    time_t ticks = time(NULL);
+    struct tm *host = localtime(&ticks);
+    replay_put_dword(host->tm_sec);
+    replay_put_dword(host->tm_min);
+    replay_put_dword(host->tm_hour);
+    replay_put_dword(host->tm_mday);
+    replay_put_dword(host->tm_mon + 1);
+    replay_put_dword(host->tm_year + 1900);
+
+    timer_mod(target_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + target_timer_period);
+}
+
+unsigned long long prev_time = 0;
+
+static void replay_enable(const char *fname, int mode)
+{
+    const char *fmode = NULL;
+    if (replay_file)
+    {
+        fprintf(stderr, "some replay operation is already started\n");
+        return;
+    }
+
+    if (use_icount == 2) {
+        fprintf(stderr, "replay: icount auto mode is not tested yet\n");
+        exit(1);
+    }
+
+    switch (mode)
+    {
+    case REPLAY_SAVE:
+        fmode = "wb";
+        break;
+    case REPLAY_PLAY:
+        fmode = "rb";
+        play_submode = REPLAY_PLAY_NORMAL;
+        break;
+    default:
+        fprintf(stderr, "internal error: invalid replay mode\n");
+        exit(1);
+    }
+
+    atexit(replay_finish);
+
+    replay_file = fopen(fname, fmode);
+    if (replay_file == NULL)
+    {
+        fprintf(stderr, "open %s: %s\n", fname, strerror(errno));
+        exit(1);
+    }
+
+    replay_filename = g_strdup(fname);
+
+    // init variables
+    replay_mode = mode;
+    replay_has_unread_data = 0;
+    replay_data_kind = -1;
+    replay_state.skipping_instruction = 0;
+    replay_state.current_step = 0;
+    replay_break_step = -1;
+    current_saved_state = 0;
+
+    replay_net_init();
+
+    // skip file header for SAVE and check it for PLAY
+    if (replay_mode == REPLAY_SAVE)
+    {
+        fseek(replay_file, HEADER_SIZE, SEEK_SET);
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        unsigned int version = replay_get_dword();
+        uint64_t offset = replay_get_qword();
+        if (version != REPLAY_VERSION)
+        {
+            fprintf(stderr, "Replay: invalid input log file version\n");
+            exit(1);
+        }
+        // read states table
+        fseeko64(replay_file, offset, SEEK_SET);
+        saved_states_count = replay_get_qword();
+        saved_states_capacity = saved_states_count;
+        if (saved_states_count)
+        {
+            saved_states = g_malloc(sizeof(SavedStateInfo) * saved_states_count);
+            fread(saved_states, sizeof(SavedStateInfo), saved_states_count, replay_file);
+        }
+        replay_net_read_packets_data();
+
+        // go to the beginning
+        fseek(replay_file, 12, SEEK_SET);
+    }
+
+    replay_init_events();
+
+    vmstate_register(NULL, 0, &vmstate_replay, &replay_state);
+
+    prev_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+}
+
+
+void replay_configure(QemuOpts *opts, int mode)
+{
+    const char *fname;
+    
+    fname = qemu_opt_get(opts, "fname");
+    if (!fname)
+    {
+        fprintf(stderr, "File name not specified for replay\n");
+        exit(1);
+    }
+
+    const char *suffix = qemu_opt_get(opts, "suffix");
+    if (suffix)
+    {
+        replay_image_suffix = g_strdup(suffix);
+    }
+    else
+    {
+        replay_image_suffix = g_strdup("replay_qcow");
+    }
+    
+    save_state_period = 1000LL * qemu_opt_get_number(opts, "period", 0);
+    debug_mode = qemu_opt_get_bool(opts, "debug", 0);
+
+    replay_icount = (int)qemu_opt_get_number(opts, "icount", 0);
+    if (replay_icount)
+    {
+        replay_configure_icount();
+    }
+    
+    if (mode == REPLAY_SAVE)
+    {
+        target_timer_period = 1000LL * qemu_opt_get_number(opts, "rtc", 0);
+    }
+
+    replay_enable(fname, mode);
+}
+
+
+void replay_init_timer(void)
+{
+    replay_enable_events();
+
+    /* create timer for states auto-saving */
+    if (replay_mode == REPLAY_SAVE)
+    {
+        saved_states_count = 0;
+        if (!saved_states)
+        {
+            saved_states = g_malloc(sizeof(SavedStateInfo) * SAVED_STATES_MIN_CAPACITY);
+            saved_states_capacity = SAVED_STATES_MIN_CAPACITY;
+        }
+        if (save_state_period)
+        {
+            save_timer = timer_new_ms(QEMU_CLOCK_REALTIME, replay_savevm, NULL);
+            timer_mod(save_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
+        }
+        replay_put_event(EVENT_END_STARTUP);
+        /* Save it right now without waiting for timer */
+        replay_savevm(NULL);
+
+        // periodic saving of target time into the log
+        if (target_timer_period)
+        {
+            target_timer = timer_new_ms(QEMU_CLOCK_REALTIME, replay_save_target_timer, NULL);
+            timer_mod(target_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
+        }
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        // load VM state by seeking to step 0
+        replay_state.current_step = -1;
+        replay_seek_step(0);
+        replay_set_break(-1);
+    }
+}
+
+
+void replay_change(struct Monitor *mon)
+{
+    if (play_submode == REPLAY_PLAY_NORMAL)
+    {
+        // cannot use breakpoints in this mode
+        replay_break_step = -1;
+        // init clock offsets to make them smooth
+        realtime_clock_replay_offset = get_clock_realtime() - get_clock_realtime_impl();
+        real_ticks_replay_offset = cpu_get_real_ticks() - cpu_get_real_ticks_impl();
+        // switch play mode
+        tb_flush_all();
+        play_submode = REPLAY_PLAY_CHANGED;
+        replay_flush_events();
+        replay_sound_flush_queue();
+    }
+    else if (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        if (mon)
+            monitor_printf(mon, "Execute replay_seek to the specified state before starting
replay_change.\n");
+        return;
+    }
+    else
+    {
+        fprintf(stderr, "Cannot start replay_change\n");
+        exit(1);
+    }
+}
+
+
+
+//! Closes replay file.
+void replay_finish(void)
+{
+    if (replay_mode == REPLAY_NONE)
+        return;
+    replay_save_instructions();
+
+    // finalize the file
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_SAVE)
+        {
+            uint64_t offset;
+            // write end event
+            replay_put_event(EVENT_END);
+
+            // write states table
+            offset = ftello64(replay_file);
+            replay_put_qword(saved_states_count);
+            if (saved_states && saved_states_count)
+            {
+                // TODO ??? Save distinct fields in the loop
+                fwrite(saved_states, sizeof(SavedStateInfo), saved_states_count, replay_file);
+            }
+            replay_net_write_packets_data();
+
+            // write header
+            fseek(replay_file, 0, SEEK_SET);
+            replay_put_dword(REPLAY_VERSION);
+            replay_put_qword(offset);
+        }
+
+        // close file
+        fclose(replay_file);
+        replay_file = NULL;
+    }
+
+    replay_net_free();
+
+    // deallocate everything
+    if (save_timer)
+    {
+        timer_del(save_timer);
+        timer_free(save_timer);
+        save_timer = NULL;
+    }
+    if (target_timer)
+    {
+        timer_del(target_timer);
+        timer_free(target_timer);
+        target_timer = NULL;
+    }
+    if (saved_states)
+    {
+        g_free(saved_states);
+        saved_states = NULL;
+    }
+
+    if (replay_filename)
+    {
+        g_free(replay_filename);
+        replay_filename = NULL;
+    }
+
+    if (replay_image_suffix)
+    {
+        g_free(replay_image_suffix);
+        replay_image_suffix = NULL;
+    }
+
+    replay_finish_events();
+}
+
+
+
+//! Saves the value of the desired clock into the output file.
+void replay_save_clock(unsigned int kind, int64_t clock)
+{
+    replay_save_instructions();
+
+    if (kind >= REPLAY_CLOCK_COUNT)
+    {
+        fprintf(stderr, "invalid clock ID %d for replay\n", kind);
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        replay_put_event(EVENT_CLOCK + kind);
+        replay_put_qword(clock);
+    }
+}
+
+
+//! Reads next clock value from the file.
+//! If clock kind read from the file is different from the parameter,
+//! the value is not used.
+//! If the parameter is -1, the clock value is read to the cache anyway.
+static void replay_read_next_clock(unsigned int kind)
+{
+    replay_fetch_data_kind();
+    if (replay_file)
+    {
+        unsigned int read_kind = replay_data_kind - EVENT_CLOCK;
+
+        if (kind != -1 && read_kind != kind)
+        {
+            return;
+        }
+        if (read_kind >= REPLAY_CLOCK_COUNT)
+        {
+            fprintf(stderr, "invalid clock ID %d was read from replay\n", read_kind);
+            exit(1);
+        }
+
+        int64_t clock = replay_get_qword();
+
+        replay_check_error();
+        replay_has_unread_data = 0;
+
+        replay_state.cached_clock[read_kind] = clock;
+    }
+}
+
+
+//! Checks SAVEVM event while reading event log. 
+static void check_savevm(void)
+{
+    replay_fetch_data_kind();
+    if (replay_data_kind != EVENT_SAVE_VM_BEGIN && replay_data_kind != EVENT_SAVE_VM_END)
+    {
+        fprintf(stderr, "read wrong data kind %d within savevm\n", replay_data_kind);
+        exit(1);
+    }
+    replay_has_unread_data = 0;
+}
+
+//! Skips clock events saved to file while saving the VM state.
+static void replay_skip_savevm(void)
+{
+    char name[128];
+
+    replay_has_unread_data = 0;
+
+    if (runstate_is_running())
+    {
+        vm_stop(RUN_STATE_RESTORE_VM);
+    }
+    else
+    {
+        // Read saved timers from event log
+        cpu_disable_ticks();
+        // Read checkpoints
+        while (replay_checkpoint(8)) {
+            /* Nothing */
+        }
+    }
+
+    ++current_saved_state;
+    sprintf(name, SAVED_STATE_NAME_FORMAT, current_saved_state);
+    load_vmstate(name);
+
+    // check the closing event
+    check_savevm();
+
+    tb_flush_all();
+
+    // Read saved timers from event log
+    cpu_enable_ticks();
+
+    vm_start();
+}
+
+//! Skips async events until some sync event will be found.
+static int skip_async_events(int stop_event)
+{
+    /* nothing to skip - not all instructions used 
+       or timer is not processed yet*/
+    if (first_cpu != NULL && first_cpu->instructions_count != 0 && replay_has_unread_data)
+    {
+        return 0;
+    }
+
+    int skipping = 1;
+    while (skipping)
+    {
+        replay_fetch_data_kind();
+        if (stop_event == replay_data_kind)
+            skipping = 0;
+        switch (replay_data_kind)
+        {
+        case EVENT_END:
+            fprintf(stderr, "Replay log file was successfully played\n");
+            exit(1);
+            break;
+        case EVENT_SOUND_OUT:
+#ifdef _WIN32
+            replay_read_sound_out();
+#endif
+            break;
+        case EVENT_SOUND_IN:
+#ifdef _WIN32
+            replay_read_sound_in();
+#endif
+            break;
+        case EVENT_SAVE_VM_BEGIN:
+            // cannot correctly load VM while in CPU thread
+            if (qemu_in_vcpu_thread())
+            {
+                skipping = 0;
+                break;
+            }
+            replay_skip_savevm();
+            break;
+        case EVENT_SHUTDOWN:
+            replay_has_unread_data = 0;
+            qemu_system_shutdown_request_impl();
+            break;
+        case EVENT_TARGET_TIME:
+            replay_has_unread_data = 0;
+            // target time
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            // host time
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            break;
+        case EVENT_INSTRUCTION:
+            first_cpu->instructions_count = replay_get_dword();
+            skipping = 0;
+            break;
+        default:
+            // clock, time_t, checkpoint and other events
+            skipping = 0;
+            break;
+        }
+    }
+    
+    return 0;
+}
+
+
+
+//! Skips async events invocations from the input,
+//! until required data kind is found.
+static int skip_async_events_until(unsigned int kind)
+{
+    while (true)
+    {
+        if (skip_async_events(kind))
+        {
+            return 1;
+        }
+
+        if (replay_data_kind != kind)
+        {
+            // TODO: fix this
+            if (replay_data_kind == EVENT_ASYNC && kind == EVENT_INSTRUCTION)
+            {
+                return 0;
+            }
+
+            fprintf(stderr, "%" PRId64 ": read data kind %d instead of expected %d\n", 
+                replay_get_current_step(), replay_data_kind, kind);
+            exit(1);
+        }
+        break;
+    }
+
+    return 0;
+}
+
+
+void replay_assert(uint64_t value, const char *message)
+{
+    if (!replay_file || !debug_mode)
+        return;
+
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+
+        replay_put_event(EVENT_ASSERT);
+        replay_put_qword(value);
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+    
+        if (skip_async_events_until(EVENT_ASSERT))
+        {
+            // No assert was written here
+            // TODO: do not exit?
+            fprintf(stderr, "Cannot read assert\n");
+            exit(1);
+        }
+
+        int64_t val = replay_get_qword();
+
+        replay_check_error();
+        replay_has_unread_data = 0;
+
+        if (value != val)
+        {
+            fprintf(stderr, "%" PRId64 ": invalid assert: read %" PRIx64 " instead of expected %"
PRIx64 " (%s)\n", 
+                replay_get_current_step(), val, value, message ? message : "---");
+            exit(1);
+        }
+    }
+}
+
+
+
+//! Reads next clock event from the input.
+int64_t replay_read_clock(unsigned int kind)
+{
+    if (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        fprintf(stderr, "Cannot read log in replay run mode\n");
+        exit(1);
+    }
+
+    if (kind >= REPLAY_CLOCK_COUNT)
+    {
+        fprintf(stderr, "invalid clock ID %d for replay\n", kind);
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        if (!skip_async_events(EVENT_CLOCK + kind))
+        {
+            if (replay_data_kind == EVENT_CLOCK + kind)
+            {
+                replay_read_next_clock(kind);
+            }
+        }
+        int64_t ret = replay_state.cached_clock[kind];
+        
+        return ret;
+    }
+
+    fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__);
+    exit(1);
+}
+
+int replay_has_timer_request(void)
+{
+    if (replay_state.skipping_instruction)
+    {
+        return 0;
+    }
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+            return 1;
+
+        skip_async_events(EVENT_ASYNC);
+        if (replay_data_kind == EVENT_ASYNC)
+            return 1;
+
+        {
+            if (replay_get_current_step() == replay_break_step)
+                return 1;
+
+            return 0;
+        }
+    }
+    else if (replay_mode == REPLAY_SAVE)
+    {
+        if (replay_has_events())
+        {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void replay_write_interrupt_request(void)
+{
+    replay_save_instructions();
+    replay_put_event(EVENT_INTERRUPT);
+}
+
+
+int replay_read_interrupt_request(void)
+{
+    // try to read event
+    if (replay_mode == REPLAY_PLAY)
+    {
+        skip_async_events(EVENT_INTERRUPT);
+        if (replay_data_kind == EVENT_INTERRUPT)
+        {
+        }
+    }
+    int ret = replay_data_kind == EVENT_INTERRUPT;
+    
+    return ret;
+}
+
+void replay_reset_interrupt_request(void)
+{
+    if (replay_data_kind == EVENT_INTERRUPT)
+    {
+        replay_has_unread_data = 0;
+    }
+}
+
+int replay_interrupt_request(void)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_write_interrupt_request();
+        return 1;
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+        
+        if (replay_read_interrupt_request())
+        {
+            replay_reset_interrupt_request();
+            return 1;
+        }
+        else
+        {
+            // no action
+            // wait here in outer loop
+        }
+
+        return 0;
+    }
+
+    return 1;
+}
+
+
+bool replay_exception(void)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+        replay_put_event(EVENT_EXCEPTION);
+        return true;
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+            return true;
+    
+        skip_async_events(EVENT_EXCEPTION);
+        if (replay_data_kind == EVENT_EXCEPTION)
+        {
+            replay_has_unread_data = 0;
+            return true;
+        }
+        return false;
+    }
+
+    return true;
+}
+
+
+
+void replay_shutdown_request(void)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_put_event(EVENT_SHUTDOWN);
+    }
+}
+
+bool replay_has_code(void)
+{
+    if (play_submode == REPLAY_PLAY_CHANGED)
+        return true;
+
+    if (replay_has_instruction())
+        return true;
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        skip_async_events(EVENT_INTERRUPT);
+        if (replay_data_kind == EVENT_INTERRUPT)
+            return true;
+        skip_async_events(EVENT_EXCEPTION);
+        if (replay_data_kind == EVENT_EXCEPTION)
+            return true;
+        return false;
+    }
+
+    return true;
+}
+
+int replay_has_instruction(void)
+{
+    int res = 1;
+    if (replay_state.skipping_instruction)
+    {
+        return 1;
+    }
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        skip_async_events(EVENT_INSTRUCTION);
+        if (replay_data_kind != EVENT_INSTRUCTION
+            && replay_data_kind != EVENT_ASYNC)
+        {
+            res = 0;
+        }
+    }
+    return res;
+}
+
+
+void replay_instruction(int process_timer)
+{
+    if (replay_state.skipping_instruction) {
+        replay_state.skipping_instruction = 0;
+
+        return;
+    }
+
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_SAVE)
+        {
+            if (replay_has_sound_events())
+            {
+                replay_save_instructions();
+            }
+
+            if (process_timer && replay_has_events())
+            {
+                replay_save_instructions();
+                // events will be after the last instruction
+                replay_save_events(-1);
+            }
+            else
+            {
+                // instruction - increase the step counter
+                ++first_cpu->instructions_count;
+            }
+        }
+        else if (replay_mode == REPLAY_PLAY)
+        {
+            if (play_submode == REPLAY_PLAY_CHANGED)
+            {
+                replay_state.current_step += first_cpu->instructions_count;
+                first_cpu->instructions_count = 0;
+                return;
+            }
+        
+            if (skip_async_events_until(EVENT_INSTRUCTION))
+            {
+                // execution should be stopped in changed replay mode
+                return;
+            }
+            // No timer is expected right now
+            if (first_cpu->instructions_count >= 1)
+            {
+                // stop only at instruction
+                if (replay_get_current_step() == replay_break_step)
+                {
+                    replay_break_step = -1;
+
+                    // for stopping VM
+                    if (play_submode == REPLAY_PLAY_NORMAL)
+                    {
+                        first_cpu->exception_index = EXCP_DEBUG;
+                        monitor_printf(default_mon, "Execution has stopped.\n");
+                        vm_stop(EXCP_DEBUG);
+                    }
+                    else if (play_submode == REPLAY_PLAY_GOTO)
+                    {
+                        play_submode = REPLAY_PLAY_NORMAL;
+                        cpu_handle_debug_exception(first_cpu->env_ptr);
+                    }
+                    else if (play_submode == REPLAY_PLAY_REVERSE)
+                    {
+						cpu_handle_debug_exception(first_cpu->env_ptr);
+                        return;
+                    }
+                    // for breaking execution loop
+                    cpu_exit(first_cpu);
+                    return;
+                }
+
+                ++replay_state.current_step;
+                --first_cpu->instructions_count;
+                if (first_cpu->instructions_count == 0)
+                    replay_has_unread_data = 0;
+            }
+            else
+            {
+                replay_read_events(-1);
+            }
+        }
+    }
+}
+
+
+bool replay_has_checkpoint(unsigned int checkpoint)
+{
+    if (replay_file && replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+            return true;
+    
+        if (skip_async_events(EVENT_CHECKPOINT + checkpoint))
+        {
+            return false;
+        }
+        if (replay_data_kind == EVENT_CHECKPOINT + checkpoint)
+        {
+            return true;
+        }
+        // not checking the checkpoint id
+        // assume that this function is called at the right place
+        if (replay_data_kind == EVENT_ASYNC_OPT)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+//! Should be called at optional check points in the execution.
+//! These check points are skipped, if they were not meet.
+//! Saves checkpoint in the SAVE mode and validates (skipping
+//! the interrupts) in the PLAY mode.
+//! Returns 0 in PLAY mode if checkpoint was not found.
+//! Returns 1 in all other cases.
+//! Used checkpoints: 0 1 2 3 5 6 7 8 9
+int replay_checkpoint(unsigned int checkpoint)
+{
+    replay_save_instructions();
+
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_PLAY)
+        {
+            if (play_submode == REPLAY_PLAY_CHANGED)
+                return 1;
+            if (skip_async_events(EVENT_CHECKPOINT + checkpoint))
+            {
+                return 0;
+            }
+            if (replay_data_kind != EVENT_CHECKPOINT + checkpoint)
+            {
+                if (replay_data_kind == EVENT_ASYNC_OPT)
+                {
+                    replay_read_events(checkpoint);
+                    replay_fetch_data_kind();
+                    return replay_data_kind != EVENT_ASYNC_OPT;
+                }
+                return 0;
+            }
+            replay_has_unread_data = 0;
+            replay_read_events(checkpoint);
+            replay_fetch_data_kind();
+            return replay_data_kind != EVENT_ASYNC_OPT;
+        }
+        else if (replay_mode == REPLAY_SAVE)
+        {
+            replay_put_event(EVENT_CHECKPOINT + checkpoint);
+            replay_save_events(checkpoint);
+        }
+    }
+
+    return 1;
+}
+
+
+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);
+        }
+    }
+}
+
+time_t replay_read_time_t(void)
+{
+    if (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        fprintf(stderr, "Cannot read log in replay run mode\n");
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        time_t tm;
+
+        if (skip_async_events_until(EVENT_TIME_T))
+        {
+            // TODO: return cached value
+            fprintf(stderr, "Cannot read time_t when stopped\n");
+            exit(1);
+        }
+
+        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 (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        fprintf(stderr, "Cannot read log in replay run mode\n");
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        if (skip_async_events_until(EVENT_TM))
+        {
+            return;
+        }
+
+        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);
+}
+
+
+
+void replay_undo_last_instruction(void)
+{
+    if (replay_mode == REPLAY_SAVE
+        || play_submode == REPLAY_PLAY_CHANGED)
+    {
+        first_cpu->instructions_count--;
+    }
+    else
+    {
+        replay_state.skipping_instruction = 1;
+    }
+}
+
+
+uint64_t replay_get_current_step(void)
+{
+    if (first_cpu == NULL)
+        return 0;
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_SAVE)
+        {
+            return replay_state.current_step + first_cpu->instructions_count;
+        }
+    }
+    return replay_state.current_step;
+}
+
+
+void replay_set_break(uint64_t step)
+{
+    replay_break_step = step;
+}
+
+
+SavedStateInfo *find_nearest_state(uint64_t step)
+{
+    SavedStateInfo *first = saved_states;
+    SavedStateInfo *last = saved_states + saved_states_count;
+    while (first < last - 1)
+    {
+        SavedStateInfo *next = first + (last - first) / 2;
+        if (next->step > step)
+            last = next;
+        else
+            first = next;
+    }
+
+    return first;
+}
+
+
+int replay_seek_step(uint64_t step)
+{
+    if (replay_mode != REPLAY_PLAY)
+        return 0;
+
+    // load VM state, if possible
+    if (saved_states_count > 0)
+    {
+        // find VM state to load
+        SavedStateInfo *first = find_nearest_state(step);
+
+        if (first->step <= step 
+            && (replay_get_current_step() > step 
+                || replay_get_current_step() < first->step
+                // always load the state, if something was changed
+                || play_submode == REPLAY_PLAY_CHANGED))
+        {
+            char name[128];
+            bool running = runstate_is_running();
+            if (running && !qemu_in_vcpu_thread())
+            {
+                vm_stop(RUN_STATE_RESTORE_VM);
+            }
+            else
+            {
+                cpu_disable_ticks();
+            }
+            
+            replay_clear_events();
+            replay_sound_flush_queue();
+
+            // reset the mode before loading to allow correct timers operation
+            if (/*play_submode == REPLAY_PLAY_STOPPED
+                || */play_submode == REPLAY_PLAY_CHANGED)
+            {
+                play_submode = REPLAY_PLAY_NORMAL;
+            }
+            
+            current_saved_state = first - saved_states;
+            sprintf(name, SAVED_STATE_NAME_FORMAT, current_saved_state);
+            if (load_vmstate(name) < 0)
+            {
+                fprintf(stderr, "Replay: cannot load VM state while seeking\n");
+                exit(1);
+            }
+            // check end event
+            check_savevm();
+
+            tb_flush_all();
+
+            cpu_enable_ticks();
+            if (running && !qemu_in_vcpu_thread()) 
+            {
+                vm_start();
+            }
+
+            replay_fetch_data_kind();
+            while (replay_data_kind >= EVENT_CLOCK && replay_data_kind < EVENT_CLOCK +
REPLAY_CLOCK_COUNT)
+            {
+                replay_read_next_clock(-1);
+                replay_fetch_data_kind();
+            }
+        }
+    }
+
+    // setup the breakpoint
+    if (step >= replay_get_current_step())
+    {
+        replay_set_break(step);
+        return 1;
+    }
+
+    return 0;
+}
+
+
+
+void replay_goto_step(uint64_t step)
+{
+    if (replay_seek_step(step))
+    {
+        play_submode = REPLAY_PLAY_GOTO;
+    }
+}
+
+
+void replay_events(struct Monitor *mon)
+{
+    if (replay_mode == REPLAY_PLAY && replay_file) 
+    {
+        int ok = 1;
+        // becomes 1 when grey key is pressed or released
+        uint64_t offset = ftello64(replay_file);
+        uint64_t step = 0;
+        fseek(replay_file, HEADER_SIZE, SEEK_SET);
+
+        // read whole file and print the events
+        while (ok && !feof(replay_file)) 
+        {
+            uint8_t data;
+            size_t size;
+            fread(&data, sizeof(data), 1, replay_file);
+            switch (data) {
+            case EVENT_END_STARTUP:
+                break;
+            case EVENT_TIME_T:
+                fseek(replay_file, sizeof(time_t), SEEK_CUR);
+                break;
+            case EVENT_TM:
+                fseek(replay_file, sizeof(uint32_t) * 9, SEEK_CUR);
+                break;
+            case EVENT_SOUND_OUT:
+                fseek(replay_file, sizeof(uint32_t), SEEK_CUR);
+                break;
+            case EVENT_SOUND_IN:
+                fread(&size, sizeof(size), 1, replay_file);
+                fseek(replay_file, size, SEEK_CUR);
+                break;
+            case EVENT_SAVE_VM_BEGIN:
+                monitor_printf(mon, "%" PRId64 ": VM state\n", step);
+                break;
+            case EVENT_SAVE_VM_END:
+                break;
+            case EVENT_INTERRUPT:
+            case EVENT_EXCEPTION:
+                break;
+            case EVENT_SHUTDOWN:
+                monitor_printf(mon, "%" PRId64 ": Request for closing simulator window\n", step);
+                break;
+            case EVENT_ASSERT:
+                fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+                break;
+            case EVENT_TARGET_TIME:
+                {
+                    int32_t sec, min, hour, day, month, year,
+                        hsec, hmin, hhour, hday, hmonth, hyear;
+                    sec = replay_get_dword();
+                    min = replay_get_dword();
+                    hour = replay_get_dword();
+                    day = replay_get_dword();
+                    month = replay_get_dword();
+                    year = replay_get_dword();
+                    hsec = replay_get_dword();
+                    hmin = replay_get_dword();
+                    hhour = replay_get_dword();
+                    hday = replay_get_dword();
+                    hmonth = replay_get_dword();
+                    hyear = replay_get_dword();
+                    monitor_printf(mon, "%" PRId64 ": Target time %d.%02d.%02d-%02d:%02d:%02d  Host
time %d.%02d.%02d-%02d:%02d:%02d\n", 
+                        step, year, month, day, hour, min, sec, hyear, hmonth, hday, hhour, hmin,
hsec);
+                }
+                break;
+            case EVENT_INSTRUCTION:
+                {
+                    uint32_t count;
+                    fread(&count, sizeof(count), 1, replay_file);
+                    step += count;
+                }
+                break;
+            case EVENT_DATA_INT:
+                fseek(replay_file, sizeof(uint32_t), SEEK_CUR);
+                break;
+            case EVENT_DATA_BUFFER:
+                fread(&size, sizeof(size), 1, replay_file);
+                fseek(replay_file, size, SEEK_CUR);
+                break;
+            case EVENT_END:
+                ok = 0;
+                break;
+            case EVENT_ASYNC:
+            case EVENT_ASYNC_OPT:
+                replay_skip_async_event(mon, step, data == EVENT_ASYNC_OPT);
+                break;
+            default:
+                if (data >= EVENT_CLOCK && data < EVENT_CHECKPOINT)
+                {
+                    fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+                }
+                else if (data >= EVENT_CHECKPOINT && data < EVENT_END)
+                {
+                    // skip
+                }
+                else
+                {
+                    ok = 0;
+                    monitor_printf(mon, "%" PRId64 ": Found unknown event %d\n", step, data);
+                }
+                break;
+            }
+        }
+        fseeko64(replay_file, offset, SEEK_SET);
+    }
+}
+
+
+int replay_xtoi(char c)
+{
+    switch (c)
+    {
+    case 'a':
+    case 'A':
+        return 10;
+    case 'b':
+    case 'B':
+        return 11;
+    case 'c':
+    case 'C':
+        return 12;
+    case 'd':
+    case 'D':
+        return 13;
+    case 'e':
+    case 'E':
+        return 14;
+    case 'f':
+    case 'F':
+        return 15;
+    default:
+        if (isdigit(c))
+            return c - '0';
+
+        return 0;
+    }
+
+    return 0;
+}
+
+int64_t replay_get_file_offset(void)
+{
+    return (int64_t) ftello64(replay_file);
+}
+
+void replay_register_get_time(replay_get_date_func f, void *opaque)
+{
+    if (replay_mode != REPLAY_NONE)
+    {
+        replay_get_target_time = f;
+        replay_target_time_dev = opaque;
+    }
+}
+
+void replay_data_int(int *data)
+{
+    if (replay_file && replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+        skip_async_events(EVENT_DATA_INT);
+        validate_data_kind(EVENT_DATA_INT);
+        *data = replay_get_dword();
+        replay_check_error();
+        replay_has_unread_data = 0;
+    }
+    else if (replay_file && replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+        replay_put_event(EVENT_DATA_INT);
+        replay_put_dword(*data);
+    }
+}
+
+void replay_data_buffer(unsigned char *data, size_t size)
+{
+    if (replay_file && replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+        size_t read_size = 0;
+        skip_async_events(EVENT_DATA_BUFFER);
+        validate_data_kind(EVENT_DATA_BUFFER);
+        replay_get_array(data, &read_size);
+        replay_check_error();
+        if (read_size != size)
+        {
+            fprintf(stderr, "Replay: read non-matching size of the buffer\n");
+            exit(1);
+        }
+        replay_has_unread_data = 0;
+    }
+    else if (replay_file && replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+        replay_put_event(EVENT_DATA_BUFFER);
+        replay_put_array(data, size);
+    }
+}
+
diff --git a/replay/replay.h b/replay/replay.h
new file mode 100644
index 0000000..fc72a92
--- /dev/null
+++ b/replay/replay.h
@@ -0,0 +1,249 @@ 
+#ifndef REPLAY_H
+#define REPLAY_H
+
+#include <time.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+#include "qemu/option_int.h"
+#include "qemu/typedefs.h"
+
+struct QemuOpts;
+struct VncDisplay;
+struct Monitor;
+struct ISADevice;
+typedef struct CharDriverState CharDriverState;
+struct libusb_transfer;
+
+/* replay modes */
+#define REPLAY_NONE 0
+#define REPLAY_SAVE 1
+#define REPLAY_PLAY 2
+/* replay submodes */
+#define REPLAY_PLAY_UNKNOWN 0
+/* Normal log replaying */
+#define REPLAY_PLAY_NORMAL  1
+/* Replaying for reverse execution */
+#define REPLAY_PLAY_REVERSE 2
+/* Running from the some starting point in the replay mode
+   or after changing registers, memory, or network packet.  */
+#define REPLAY_PLAY_CHANGED 3
+/* Replaying for seeking to the specified step of the trace */
+#define REPLAY_PLAY_GOTO    4
+
+/* replay clock kinds */
+/* rdtsc */
+#define REPLAY_CLOCK_REAL_TICKS 0
+/* host_clock */
+#define REPLAY_CLOCK_REALTIME   1
+/* vm_clock */
+#define REPLAY_CLOCK_VIRTUAL    2
+
+#define REPLAY_CLOCK_COUNT      3
+
+/* replay async event kinds */
+#define REPLAY_ASYNC_EVENT_NOTIFIER    1
+#define REPLAY_ASYNC_EVENT_NETWORK     2
+#define REPLAY_ASYNC_EVENT_THREAD      3
+#define REPLAY_ASYNC_EVENT_BH          4
+#define REPLAY_ASYNC_EVENT_USB_CTRL    5
+#define REPLAY_ASYNC_EVENT_USB_DATA    6
+#define REPLAY_ASYNC_EVENT_USB_ISO     7
+#define REPLAY_ASYNC_EVENT_CHAR        8
+#define REPLAY_ASYNC_EVENT_INPUT       9
+#define REPLAY_ASYNC_EVENT_INPUT_SYNC  10
+
+#define REPLAY_ASYNC_COUNT             11
+
+extern int replay_mode;
+extern char *replay_image_suffix;
+/*! Shift value for icount based on replay or zero, if it is disabled. */
+extern int replay_icount;
+extern int not_compatible_replay_param;
+extern uint64_t replay_network_packets;
+
+/*! Returns replay play submode */
+int replay_get_play_submode(void);
+/*! Enter "something changed" replay submode */
+void replay_set_play_changed(void);
+void replay_init_overlay(void);
+/*! Enables recording or saving event log with specified parameters */
+void replay_configure(struct QemuOpts *opts, int mode);
+//void replay_enable(const char *fname, int mode);
+void replay_init_timer(void);
+void replay_add_network_client(struct NetClientState *nc);
+/*! Retrieves name of the file, which is used for recording
+    or replaying execution log. */
+const char *replay_get_filename(void);
+int64_t replay_get_file_offset(void);
+
+//! Puts debug information into the log in save mode
+//! and checks this information in play mode.
+void replay_assert(uint64_t value, const char *message);
+
+void replay_save_clock(unsigned int kind, int64_t clock);
+int64_t replay_read_clock(unsigned int kind);
+
+//! Called to write software interrupt event into the log.
+//! This event enables breaking the translation block execution.
+void replay_write_interrupt_request(void);
+//! Tries to read interrupt event from the file 
+//! and sets interrupt pending flag if the event was read.
+//! Returns non-zero, when interrupt request is pending.
+int replay_read_interrupt_request(void);
+//! Resets interrupt request flag.
+void replay_reset_interrupt_request(void);
+//! Called by interrupt handlers to write or read
+//! interrupt processing events.
+//! \return non-zero if interrupt should be processed
+int replay_interrupt_request(void);
+
+//! Called by exception handler to write or read
+//! exception processing events.
+bool replay_exception(void);
+
+int replay_has_timer_request(void);
+
+//! Called when qemu shutdown is requested.
+void replay_shutdown_request(void);
+
+//! Closes replay file.
+void replay_finish(void);
+
+//! Returns non-zero if next event is instruction.
+int replay_has_instruction(void);
+//! Returns true if next event can be processed by cpu-exec.
+bool replay_has_code(void);
+//! Should be called at check points in the execution.
+//! Saves instructions count in the SAVE mode and validates (skipping
+//! the interrupts) in the PLAY mode.
+//! \param process_timer Contains non-zero value, when timer requests should be processed.
+//!                      This parameter is not taken into account in PLAY mode.
+void replay_instruction(int process_timer);
+bool replay_has_checkpoint(unsigned int checkpoint);
+int replay_checkpoint(unsigned int checkpoint);
+
+void replay_save_time_t(time_t tm);
+time_t replay_read_time_t(void);
+
+void replay_save_tm(struct tm *tm);
+void replay_read_tm(struct tm *tm);
+
+void replay_save_net_packet(NetClientState *nc, const uint8_t *buf, size_t size);
+
+/* Sound functions may be called from another thread */
+#ifdef _WIN32
+void replay_sound_in_event(WAVEHDR *hdr);
+/*! Adds header to the queue.
+    In SAVE mode this header is queued for saving into log.
+    In PLAY mode this header is queued for reading from log.
+    Returns 1 in PLAY mode when queue is full.
+    Otherwise returns 0.
+*/
+int replay_sound_out_event(WAVEHDR *hdr);
+//! Initializes queue for sound input.
+void replay_init_sound_in(void *instance, WAVEHDR *hdrs, int sz);
+//! Initializes queue for sound output.
+void replay_init_sound_out(void *instance, WAVEHDR *hdrs, int sz);
+#endif
+
+void replay_undo_last_instruction(void);
+
+/* Functions to retrieve information about replay process.
+   Used by monitor module. */
+
+/*! Returns number of passed instructions and timer events. */
+uint64_t replay_get_current_step(void);
+/*! Sets step where execution should be stopped. */
+void replay_set_break(uint64_t step);
+/*! Seeks to the specified step. 
+    Loads VM state, if possible, and sets break to specified step.
+    Returns nonzero, when seeking was successful.
+    */
+int replay_seek_step(uint64_t step);
+/*! Calls replay_seek_step and switches to REPLAY_PLAY_GOTO mode. */
+void replay_goto_step(uint64_t step);
+
+/*! Prints events from the log. */
+void replay_events(struct Monitor *mon);
+/*! Prints desired network packet contents. 
+    Returns non-zero, if packet was successfully printed.
+ */
+int replay_get_packet(struct Monitor *mon, int64_t id);
+/*! Adds desired packet to the list of the "modified" packets. 
+    When replaying, packet with the specified id read from the file
+    will be replaced with packet, added with this command.
+    If bytes is null, modified packet with desired id will be 
+    removed from the list.
+    Returns non-zero, if packet was successfully added or removed.
+ */
+int replay_set_packet(struct Monitor *mon, int64_t id, const char *bytes);
+/* utility function */
+int replay_xtoi(char);
+
+/*! Initializes reverse continue execution.
+    Called by debugger module. */
+void replay_reverse_continue(void);
+/*! Initializes reverse step execution.
+    Called by debugger module. */
+void replay_reverse_step(void);
+/* Called instead of invoking breakpoint in reverse continue mode. */
+void replay_reverse_breakpoint(void);
+
+/* Save and load USB events */
+/*! Called in save mode when xfer for ctrl usb data is completed */
+void replay_req_complete_ctrl(struct libusb_transfer *xfer);
+/*! Called in play mode to register xfer to be completed by reading it from the log */
+void replay_req_register_ctrl(struct libusb_transfer *xfer);
+/*! Called in save mode when xfer for usb data request is completed */
+void replay_req_complete_data(struct libusb_transfer *xfer);
+/*! Called in play mode to register xfer to be completed by reading it from the log */
+void replay_req_register_data(struct libusb_transfer *xfer);
+/*! Called in save mode when iso xfer for usb data request is completed */
+void replay_req_complete_iso(struct libusb_transfer *xfer);
+/*! Called in play mode to register iso xfer to be completed by reading it from the log */
+void replay_req_register_iso(struct libusb_transfer *xfer);
+
+#define REPLAY_ASSERT(val) replay_assert(val, NULL)
+#define REPLAY_ASSERT_M(val, msg) replay_assert(val, msg)
+
+typedef void (*replay_get_date_func)(struct ISADevice*, struct tm *);
+
+/*! Registers function which gets time from target OS. */
+void replay_register_get_time(replay_get_date_func f, void *opaque);
+
+/* Async events functions */
+/*! Adds specified async event to the queue */
+void replay_add_event(unsigned int event_id, void *opaque);
+/*! Adds specified async event with 2 parameters to the queue */
+void replay_add_event2(unsigned int event_id, void *opaque, void *opaque2);
+/*! Adds BH event to the queue */
+void replay_add_bh_event(void *bh, uint64_t id);
+/*! Adds thread event to the queue */
+void replay_add_thread_event(void *opaque, void *opaque2, uint64_t id);
+
+void replay_enable_events(void);
+void replay_disable_events(void);
+
+/*! Registers char driver to save it's events */
+void replay_register_char_driver(CharDriverState *chr);
+
+/*! Writes or reads integer value to/from replay log. */
+void replay_data_int(int *data);
+/*! Macro for using replay_data_int function */
+#define REPLAY_DATA_INT(var, value) ((replay_mode == REPLAY_NONE ? var = (value) : replay_mode ==
REPLAY_SAVE ? (var = (value), replay_data_int(&var), var) : /* PLAY */ (replay_data_int(&var),
var)))
+/*! Writes or reads buffer to/from replay log. */
+void replay_data_buffer(unsigned char *data, size_t size);
+/*! Macro for using replay_data_buffer function */
+#define REPLAY_DATA_VAR(var) replay_data_buffer((unsigned char*)&(var), sizeof(var))
+/*! Saves write to char device event to the log */
+void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
+
+/* Returns the virtual CPU time, based on the instruction counter.  */
+int64_t replay_get_icount(void);
+void replay_clock_warp(void);
+
+/*! Starts normal execution from the current replay step. */