qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v5 1/7] replay: character devices


From: Paolo Bonzini
Subject: Re: [Qemu-devel] [PATCH v5 1/7] replay: character devices
Date: Mon, 14 Mar 2016 15:11:45 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.6.0


On 14/03/2016 08:44, Pavel Dovgalyuk wrote:
> This patch implements record and replay of character devices.
> It records chardevs communication in replay mode. Recorded information
> include data read from backend and counter of bytes written
> from frontend to backend to preserve frontend internal state.
> If character device was configured through the command line in record mode,
> then in replay mode it should be also added to command line. Backend of
> the character device could be changed in replay mode.
> Replaying of devices that perform ioctl and get_msgfd operations is not
> supported.
> gdbstub which also acts as a backend is not recorded to allow controlling
> the replaying through gdb. Monitor backends are also not recorded.
> 
> Signed-off-by: Pavel Dovgalyuk <address@hidden>

Thanks, this looks good.

Paolo

> ---
>  gdbstub.c                |    2 -
>  include/sysemu/char.h    |   26 +++++++
>  include/sysemu/replay.h  |   17 +++++
>  qemu-char.c              |  138 +++++++++++++++++++++++++++++++-------
>  replay/Makefile.objs     |    1 
>  replay/replay-char.c     |  167 
> ++++++++++++++++++++++++++++++++++++++++++++++
>  replay/replay-events.c   |   17 ++++-
>  replay/replay-internal.h |   18 +++++
>  replay/replay.c          |    2 -
>  9 files changed, 358 insertions(+), 30 deletions(-)
>  create mode 100755 replay/replay-char.c
> 
> diff --git a/gdbstub.c b/gdbstub.c
> index 61c12b1..fdcb0ee 100644
> --- a/gdbstub.c
> +++ b/gdbstub.c
> @@ -1752,7 +1752,7 @@ int gdbserver_start(const char *device)
>              sigaction(SIGINT, &act, NULL);
>          }
>  #endif
> -        chr = qemu_chr_new("gdb", device, NULL);
> +        chr = qemu_chr_new_noreplay("gdb", device, NULL);
>          if (!chr)
>              return -1;
>  
> diff --git a/include/sysemu/char.h b/include/sysemu/char.h
> index e46884f..4c2f777 100644
> --- a/include/sysemu/char.h
> +++ b/include/sysemu/char.h
> @@ -86,6 +86,7 @@ struct CharDriverState {
>      int is_mux;
>      guint fd_in_tag;
>      QemuOpts *opts;
> +    bool replay;
>      QTAILQ_ENTRY(CharDriverState) next;
>  };
>  
> @@ -139,6 +140,22 @@ CharDriverState *qemu_chr_new(const char *label, const 
> char *filename,
>                                void (*init)(struct CharDriverState *s));
>  
>  /**
> + * @qemu_chr_new_noreplay:
> + *
> + * Create a new character backend from a URI.
> + * Character device communications are not written
> + * into the replay log.
> + *
> + * @label the name of the backend
> + * @filename the URI
> + * @init not sure..
> + *
> + * Returns: a new character backend
> + */
> +CharDriverState *qemu_chr_new_noreplay(const char *label, const char 
> *filename,
> +                                       void (*init)(struct CharDriverState 
> *s));
> +
> +/**
>   * @qemu_chr_delete:
>   *
>   * Destroy a character backend and remove it from the list of
> @@ -341,6 +358,15 @@ int qemu_chr_be_can_write(CharDriverState *s);
>   */
>  void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
>  
> +/**
> + * @qemu_chr_be_write_impl:
> + *
> + * Implementation of back end writing. Used by replay module.
> + *
> + * @buf a buffer to receive data from the front end
> + * @len the number of bytes to receive from the front end
> + */
> +void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
>  
>  /**
>   * @qemu_chr_be_event:
> diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
> index e4108e8..d24d502 100644
> --- a/include/sysemu/replay.h
> +++ b/include/sysemu/replay.h
> @@ -114,4 +114,21 @@ void replay_input_event(QemuConsole *src, InputEvent 
> *evt);
>  /*! Adds input sync event to the queue */
>  void replay_input_sync_event(void);
>  
> +/* Character device */
> +
> +/*! Registers char driver to save it's events */
> +void replay_register_char_driver(struct CharDriverState *chr);
> +/*! Saves write to char device event to the log */
> +void replay_chr_be_write(struct CharDriverState *s, uint8_t *buf, int len);
> +/*! Writes char write return value to the replay log. */
> +void replay_char_write_event_save(int res, int offset);
> +/*! Reads char write return value from the replay log. */
> +void replay_char_write_event_load(int *res, int *offset);
> +/*! Reads information about read_all character event. */
> +int replay_char_read_all_load(uint8_t *buf);
> +/*! Writes character read_all error code into the replay log. */
> +void replay_char_read_all_save_error(int res);
> +/*! Writes character read_all execution result into the replay log. */
> +void replay_char_read_all_save_buf(uint8_t *buf, int offset);
> +
>  #endif
> diff --git a/qemu-char.c b/qemu-char.c
> index e0147f3..b418307 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -37,6 +37,7 @@
>  #include "io/channel-socket.h"
>  #include "io/channel-file.h"
>  #include "io/channel-tls.h"
> +#include "sysemu/replay.h"
>  
>  #include <zlib.h>
>  
> @@ -234,10 +235,46 @@ static void qemu_chr_fe_write_log(CharDriverState *s,
>      }
>  }
>  
> +static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, 
> int len, int *offset)
> +{
> +    int res = 0;
> +    *offset = 0;
> +
> +    qemu_mutex_lock(&s->chr_write_lock);
> +    while (*offset < len) {
> +        do {
> +            res = s->chr_write(s, buf + *offset, len - *offset);
> +            if (res == -1 && errno == EAGAIN) {
> +                g_usleep(100);
> +            }
> +        } while (res == -1 && errno == EAGAIN);
> +
> +        if (res <= 0) {
> +            break;
> +        }
> +
> +        *offset += res;
> +    }
> +    if (*offset > 0) {
> +        qemu_chr_fe_write_log(s, buf, *offset);
> +    }
> +    qemu_mutex_unlock(&s->chr_write_lock);
> +
> +    return res;
> +}
> +
>  int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
>  {
>      int ret;
>  
> +    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
> +        int offset;
> +        replay_char_write_event_load(&ret, &offset);
> +        assert(offset <= len);
> +        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
> +        return ret;
> +    }
> +
>      qemu_mutex_lock(&s->chr_write_lock);
>      ret = s->chr_write(s, buf, len);
>  
> @@ -246,34 +283,31 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t 
> *buf, int len)
>      }
>  
>      qemu_mutex_unlock(&s->chr_write_lock);
> +    
> +    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
> +        replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
> +    }
> +    
>      return ret;
>  }
>  
>  int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
>  {
> -    int offset = 0;
> -    int res = 0;
> +    int offset;
> +    int res;
>  
> -    qemu_mutex_lock(&s->chr_write_lock);
> -    while (offset < len) {
> -        do {
> -            res = s->chr_write(s, buf + offset, len - offset);
> -            if (res == -1 && errno == EAGAIN) {
> -                g_usleep(100);
> -            }
> -        } while (res == -1 && errno == EAGAIN);
> +    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
> +        replay_char_write_event_load(&res, &offset);
> +        assert(offset <= len);
> +        qemu_chr_fe_write_buffer(s, buf, offset, &offset);
> +        return res;
> +    }
>  
> -        if (res <= 0) {
> -            break;
> -        }
> +    res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
>  
> -        offset += res;
> +    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
> +        replay_char_write_event_save(res, offset);
>      }
> -    if (offset > 0) {
> -        qemu_chr_fe_write_log(s, buf, offset);
> -    }
> -
> -    qemu_mutex_unlock(&s->chr_write_lock);
>  
>      if (res < 0) {
>          return res;
> @@ -289,6 +323,10 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t 
> *buf, int len)
>      if (!s->chr_sync_read) {
>          return 0;
>      }
> +    
> +    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
> +        return replay_char_read_all_load(buf);
> +    }
>  
>      while (offset < len) {
>          do {
> @@ -303,6 +341,9 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t 
> *buf, int len)
>          }
>  
>          if (res < 0) {
> +            if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
> +                replay_char_read_all_save_error(res);
> +            }
>              return res;
>          }
>  
> @@ -313,14 +354,22 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t 
> *buf, int len)
>          }
>      }
>  
> +    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
> +        replay_char_read_all_save_buf(buf, offset);
> +    }
>      return offset;
>  }
>  
>  int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
>  {
> -    if (!s->chr_ioctl)
> -        return -ENOTSUP;
> -    return s->chr_ioctl(s, cmd, arg);
> +    int res;
> +    if (!s->chr_ioctl || s->replay) {
> +        res = -ENOTSUP;
> +    } else {
> +        res = s->chr_ioctl(s, cmd, arg);
> +    }
> +
> +    return res;
>  }
>  
>  int qemu_chr_be_can_write(CharDriverState *s)
> @@ -330,17 +379,35 @@ int qemu_chr_be_can_write(CharDriverState *s)
>      return s->chr_can_read(s->handler_opaque);
>  }
>  
> -void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
> +void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
>  {
>      if (s->chr_read) {
>          s->chr_read(s->handler_opaque, buf, len);
>      }
>  }
>  
> +void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
> +{
> +    if (s->replay) {
> +        if (replay_mode == REPLAY_MODE_PLAY) {
> +            return;
> +        }
> +        replay_chr_be_write(s, buf, len);
> +    } else {
> +        qemu_chr_be_write_impl(s, buf, len);
> +    }
> +}
> +
>  int qemu_chr_fe_get_msgfd(CharDriverState *s)
>  {
>      int fd;
> -    return (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
> +    int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
> +    if (s->replay) {
> +        fprintf(stderr,
> +                "Replay: get msgfd is not supported for serial devices 
> yet\n");
> +        exit(1);
> +    }
> +    return res;
>  }
>  
>  int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
> @@ -3855,7 +3922,8 @@ err:
>      return NULL;
>  }
>  
> -CharDriverState *qemu_chr_new(const char *label, const char *filename, void 
> (*init)(struct CharDriverState *s))
> +CharDriverState *qemu_chr_new_noreplay(const char *label, const char 
> *filename,
> +                                       void (*init)(struct CharDriverState 
> *s))
>  {
>      const char *p;
>      CharDriverState *chr;
> @@ -3881,6 +3949,21 @@ CharDriverState *qemu_chr_new(const char *label, const 
> char *filename, void (*in
>      return chr;
>  }
>  
> +CharDriverState *qemu_chr_new(const char *label, const char *filename, void 
> (*init)(struct CharDriverState *s))
> +{
> +    CharDriverState *chr;
> +    chr = qemu_chr_new_noreplay(label, filename, init);
> +    if (chr) {
> +        chr->replay = replay_mode != REPLAY_MODE_NONE;
> +        if (chr->replay && chr->chr_ioctl) {
> +            fprintf(stderr,
> +                    "Replay: ioctl is not supported for serial devices 
> yet\n");
> +        }
> +        replay_register_char_driver(chr);
> +    }
> +    return chr;
> +}
> +
>  void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
>  {
>      if (chr->chr_set_echo) {
> @@ -4475,6 +4558,11 @@ void qmp_chardev_remove(const char *id, Error **errp)
>          error_setg(errp, "Chardev '%s' is busy", id);
>          return;
>      }
> +    if (chr->replay) {
> +        error_setg(errp,
> +            "Chardev '%s' cannot be unplugged in record/replay mode", id);
> +        return;
> +    }
>      qemu_chr_delete(chr);
>  }
>  
> diff --git a/replay/Makefile.objs b/replay/Makefile.objs
> index 232193a..70e5572 100644
> --- a/replay/Makefile.objs
> +++ b/replay/Makefile.objs
> @@ -3,3 +3,4 @@ common-obj-y += replay-internal.o
>  common-obj-y += replay-events.o
>  common-obj-y += replay-time.o
>  common-obj-y += replay-input.o
> +common-obj-y += replay-char.o
> diff --git a/replay/replay-char.c b/replay/replay-char.c
> new file mode 100755
> index 0000000..bfbaf2e
> --- /dev/null
> +++ b/replay/replay-char.c
> @@ -0,0 +1,167 @@
> +/*
> + * replay-char.c
> + *
> + * Copyright (c) 2010-2016 Institute for System Programming
> + *                         of the Russian Academy of Sciences.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "qemu/osdep.h"
> +#include "sysemu/replay.h"
> +#include "replay-internal.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/char.h"
> +
> +/* Char drivers that generate qemu_chr_be_write events
> +   that should be saved into the log. */
> +static CharDriverState **char_drivers;
> +static int drivers_count;
> +
> +/* 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;
> +    for ( ; i < drivers_count ; ++i) {
> +        if (char_drivers[i] == chr) {
> +            return i;
> +        }
> +    }
> +    return -1;
> +}
> +
> +void replay_register_char_driver(CharDriverState *chr)
> +{
> +    if (replay_mode == REPLAY_MODE_NONE) {
> +        return;
> +    }
> +    char_drivers = g_realloc(char_drivers,
> +                             sizeof(*char_drivers) * (drivers_count + 1));
> +    char_drivers[drivers_count++] = chr;
> +}
> +
> +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_READ, event, NULL, 0);
> +}
> +
> +void replay_event_char_read_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_read_save(void *opaque)
> +{
> +    CharEvent *event = (CharEvent *)opaque;
> +
> +    replay_put_byte(event->id);
> +    replay_put_array(event->buf, event->len);
> +}
> +
> +void *replay_event_char_read_load(void)
> +{
> +    CharEvent *event = g_malloc0(sizeof(CharEvent));
> +
> +    event->id = replay_get_byte();
> +    replay_get_array_alloc(&event->buf, &event->len);
> +
> +    return event;
> +}
> +
> +void replay_char_write_event_save(int res, int offset)
> +{
> +    replay_save_instructions();
> +    replay_mutex_lock();
> +    replay_put_event(EVENT_CHAR_WRITE);
> +    replay_put_dword(res);
> +    replay_put_dword(offset);
> +    replay_mutex_unlock();
> +}
> +
> +void replay_char_write_event_load(int *res, int *offset)
> +{
> +    replay_account_executed_instructions();
> +    replay_mutex_lock();
> +    if (replay_next_event_is(EVENT_CHAR_WRITE)) {
> +        *res = replay_get_dword();
> +        *offset = replay_get_dword();
> +        replay_finish_event();
> +        replay_mutex_unlock();
> +    } else {
> +        replay_mutex_unlock();
> +        error_report("Missing character write event in the replay log");
> +        exit(1);
> +    }
> +}
> +
> +int replay_char_read_all_load(uint8_t *buf)
> +{
> +    replay_mutex_lock();
> +    if (replay_next_event_is(EVENT_CHAR_READ_ALL)) {
> +        size_t size;
> +        int res;
> +        replay_get_array(buf, &size);
> +        replay_finish_event();
> +        replay_mutex_unlock();
> +        res = (int)size;
> +        assert(res >= 0);
> +        return res;
> +    } else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) {
> +        int res = replay_get_dword();
> +        replay_finish_event();
> +        replay_mutex_unlock();
> +        return res;
> +    } else {
> +        replay_mutex_unlock();
> +        error_report("Missing character read all event in the replay log");
> +        exit(1);
> +    }
> +}
> +
> +void replay_char_read_all_save_error(int res)
> +{
> +    assert(res < 0);
> +    replay_save_instructions();
> +    replay_mutex_lock();
> +    replay_put_event(EVENT_CHAR_READ_ALL_ERROR);
> +    replay_put_dword(res);
> +    replay_mutex_unlock();
> +}
> +
> +void replay_char_read_all_save_buf(uint8_t *buf, int offset)
> +{
> +    replay_save_instructions();
> +    replay_mutex_lock();
> +    replay_put_event(EVENT_CHAR_READ_ALL);
> +    replay_put_array(buf, offset);
> +    replay_mutex_unlock();
> +}
> diff --git a/replay/replay-events.c b/replay/replay-events.c
> index 2628109..ca940f7 100644
> --- a/replay/replay-events.c
> +++ b/replay/replay-events.c
> @@ -48,6 +48,9 @@ static void replay_run_event(Event *event)
>      case REPLAY_ASYNC_EVENT_INPUT_SYNC:
>          qemu_input_event_sync_impl();
>          break;
> +    case REPLAY_ASYNC_EVENT_CHAR_READ:
> +        replay_event_char_read_run(event->opaque);
> +        break;
>      default:
>          error_report("Replay: invalid async event ID (%d) in the queue",
>                      event->event_kind);
> @@ -102,9 +105,9 @@ void replay_clear_events(void)
>  }
>  
>  /*! Adds specified async event to the queue */
> -static void replay_add_event(ReplayAsyncEventKind event_kind,
> -                             void *opaque,
> -                             void *opaque2, uint64_t id)
> +void replay_add_event(ReplayAsyncEventKind event_kind,
> +                      void *opaque,
> +                      void *opaque2, uint64_t id)
>  {
>      assert(event_kind < REPLAY_ASYNC_COUNT);
>  
> @@ -168,6 +171,9 @@ static void replay_save_event(Event *event, int 
> checkpoint)
>              break;
>          case REPLAY_ASYNC_EVENT_INPUT_SYNC:
>              break;
> +        case REPLAY_ASYNC_EVENT_CHAR_READ:
> +            replay_event_char_read_save(event->opaque);
> +            break;
>          default:
>              error_report("Unknown ID %d of replay event", read_event_kind);
>              exit(1);
> @@ -221,6 +227,11 @@ static Event *replay_read_event(int checkpoint)
>          event->event_kind = read_event_kind;
>          event->opaque = 0;
>          return event;
> +    case REPLAY_ASYNC_EVENT_CHAR_READ:
> +        event = g_malloc0(sizeof(Event));
> +        event->event_kind = read_event_kind;
> +        event->opaque = replay_event_char_read_load();
> +        return event;
>      default:
>          error_report("Unknown ID %d of replay event", read_event_kind);
>          exit(1);
> diff --git a/replay/replay-internal.h b/replay/replay-internal.h
> index 5438ebd..11f9a85 100644
> --- a/replay/replay-internal.h
> +++ b/replay/replay-internal.h
> @@ -24,6 +24,11 @@ enum ReplayEvents {
>      EVENT_ASYNC,
>      /* for shutdown request */
>      EVENT_SHUTDOWN,
> +    /* for character device write event */
> +    EVENT_CHAR_WRITE,
> +    /* for character device read all event */
> +    EVENT_CHAR_READ_ALL,
> +    EVENT_CHAR_READ_ALL_ERROR,
>      /* for clock read/writes */
>      /* some of greater codes are reserved for clocks */
>      EVENT_CLOCK,
> @@ -43,6 +48,7 @@ enum ReplayAsyncEventKind {
>      REPLAY_ASYNC_EVENT_BH,
>      REPLAY_ASYNC_EVENT_INPUT,
>      REPLAY_ASYNC_EVENT_INPUT_SYNC,
> +    REPLAY_ASYNC_EVENT_CHAR_READ,
>      REPLAY_ASYNC_COUNT
>  };
>  
> @@ -124,6 +130,9 @@ bool replay_has_events(void);
>  void replay_save_events(int checkpoint);
>  /*! Read events from the file into the input queue */
>  void replay_read_events(int checkpoint);
> +/*! Adds specified async event to the queue */
> +void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque,
> +                      void *opaque2, uint64_t id);
>  
>  /* Input events */
>  
> @@ -136,4 +145,13 @@ void replay_add_input_event(struct InputEvent *event);
>  /*! Adds input sync event to the queue */
>  void replay_add_input_sync_event(void);
>  
> +/* Character devices */
> +
> +/*! Called to run char device read event. */
> +void replay_event_char_read_run(void *opaque);
> +/*! Writes char read event to the file. */
> +void replay_event_char_read_save(void *opaque);
> +/*! Reads char event read from the file. */
> +void *replay_event_char_read_load(void);
> +
>  #endif
> diff --git a/replay/replay.c b/replay/replay.c
> index f8739c2..fcfde4f 100644
> --- a/replay/replay.c
> +++ b/replay/replay.c
> @@ -20,7 +20,7 @@
>  
>  /* Current version of the replay mechanism.
>     Increase it when file format changes. */
> -#define REPLAY_VERSION              0xe02002
> +#define REPLAY_VERSION              0xe02003
>  /* Size of replay log header */
>  #define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
>  
> 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]