diff mbox

make screendump an async command

Message ID 1371151644-22308-1-git-send-email-alevy@redhat.com
State New
Headers show

Commit Message

Alon Levy June 13, 2013, 7:27 p.m. UTC
This fixes the broken screendump behavior for qxl devices in native mode
since 81fb6f1504fb9ef71f2382f44af34756668296e8.

Note: due to QAPI not generating async commands yet I had to remove the
schema screendump definition.

Related RHBZ: 973374
This patch is not enough to fix said bz, with the linux qxl driver you get garbage still, just not out of date garbage.

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 hmp.c                     |  2 +-
 hw/display/qxl-render.c   |  1 +
 hw/display/vga.c          |  1 +
 include/qapi/qmp/qerror.h |  6 +++++
 include/ui/console.h      | 10 ++++++++
 qapi-schema.json          | 13 -----------
 qmp-commands.hx           |  3 ++-
 ui/console.c              | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
 8 files changed, 78 insertions(+), 16 deletions(-)

Comments

Paolo Bonzini June 13, 2013, 8:08 p.m. UTC | #1
Il 13/06/2013 15:27, Alon Levy ha scritto:
> This fixes the broken screendump behavior for qxl devices in native mode
> since 81fb6f1504fb9ef71f2382f44af34756668296e8.
> 
> Note: due to QAPI not generating async commands yet I had to remove the
> schema screendump definition.
> 
> Related RHBZ: 973374
> This patch is not enough to fix said bz, with the linux qxl driver you get garbage still, just not out of date garbage.
> 
> Signed-off-by: Alon Levy <alevy@redhat.com>
> ---
>  hmp.c                     |  2 +-
>  hw/display/qxl-render.c   |  1 +
>  hw/display/vga.c          |  1 +
>  include/qapi/qmp/qerror.h |  6 +++++
>  include/ui/console.h      | 10 ++++++++
>  qapi-schema.json          | 13 -----------
>  qmp-commands.hx           |  3 ++-
>  ui/console.c              | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
>  8 files changed, 78 insertions(+), 16 deletions(-)
> 
> diff --git a/hmp.c b/hmp.c
> index 494a9aa..2a37975 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1346,7 +1346,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
>      const char *filename = qdict_get_str(qdict, "filename");
>      Error *err = NULL;
>  
> -    qmp_screendump(filename, &err);
> +    hmp_screen_dump_helper(filename, &err);
>      hmp_handle_error(mon, &err);
>  }
>  
> diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
> index f511a62..d719448 100644
> --- a/hw/display/qxl-render.c
> +++ b/hw/display/qxl-render.c
> @@ -139,6 +139,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
>                         qxl->dirty[i].bottom - qxl->dirty[i].top);
>      }
>      qxl->num_dirty_rects = 0;
> +    console_screendump_complete(vga->con);
>  }
>  
>  /*
> diff --git a/hw/display/vga.c b/hw/display/vga.c
> index 21a108d..1fc52eb 100644
> --- a/hw/display/vga.c
> +++ b/hw/display/vga.c
> @@ -1922,6 +1922,7 @@ static void vga_update_display(void *opaque)
>              break;
>          }
>      }
> +    console_screendump_complete(s->con);
>  }
>  
>  /* force a full display refresh */
> diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
> index 6c0a18d..1bd7efa 100644
> --- a/include/qapi/qmp/qerror.h
> +++ b/include/qapi/qmp/qerror.h
> @@ -237,6 +237,12 @@ void assert_no_error(Error *err);
>  #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
>      ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
>  
> +#define QERR_SCREENDUMP_NOT_AVAILABLE \
> +    ERROR_CLASS_GENERIC_ERROR, "There is no QemuConsole I can screendump from"

s/QemuConsole/graphical console/

> +#define QERR_SCREENDUMP_IN_PROGRESS \
> +    ERROR_CLASS_GENERIC_ERROR, "Existing screendump in progress"
> +

Please use error_setg instead.

>  #define QERR_SOCKET_CONNECT_FAILED \
>      ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"
>  
> diff --git a/include/ui/console.h b/include/ui/console.h
> index 4307b5f..643da45 100644
> --- a/include/ui/console.h
> +++ b/include/ui/console.h
> @@ -341,4 +341,14 @@ int index_from_keycode(int code);
>  void early_gtk_display_init(void);
>  void gtk_display_init(DisplayState *ds);
>  
> +/* hw/display/vga.c hw/display/qxl.c */
> +void console_screendump_complete(QemuConsole *con);
> +
> +/* monitor.c */
> +int qmp_screendump(Monitor *mon, const QDict *qdict,
> +                   MonitorCompletion cb, void *opaque);
> +
> +/* hmp.c */
> +void hmp_screen_dump_helper(const char *filename, Error **errp);
> +
>  #endif
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 5ad6894..f5fdc2f 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3112,19 +3112,6 @@
>    'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
>  
>  ##
> -# @screendump:
> -#
> -# Write a PPM of the VGA screen to a file.
> -#
> -# @filename: the path of a new PPM file to store the image
> -#
> -# Returns: Nothing on success
> -#
> -# Since: 0.14.0
> -##
> -{ 'command': 'screendump', 'data': {'filename': 'str'} }

Please just comment out the declaration with an explanation.

Paolo

> -
> -##
>  # @nbd-server-start:
>  #
>  # Start an NBD server listening on the given host and port.  Block
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 8cea5e5..bde8f43 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -146,7 +146,8 @@ EQMP
>      {
>          .name       = "screendump",
>          .args_type  = "filename:F",
> -        .mhandler.cmd_new = qmp_marshal_input_screendump,
> +        .mhandler.cmd_async = qmp_screendump,
> +        .flags      = MONITOR_CMD_ASYNC,
>      },
>  
>  SQMP
> diff --git a/ui/console.c b/ui/console.c
> index 28bba6d..0efd588 100644
> --- a/ui/console.c
> +++ b/ui/console.c
> @@ -116,6 +116,12 @@ typedef enum {
>  struct QemuConsole {
>      Object parent;
>  
> +    struct {
> +        char *filename;
> +        MonitorCompletion *completion_cb;
> +        void *completion_opaque;
> +    } screendump;
> +
>      int index;
>      console_type_t console_type;
>      DisplayState *ds;
> @@ -313,11 +319,61 @@ write_err:
>      goto out;
>  }
>  
> -void qmp_screendump(const char *filename, Error **errp)
> +int qmp_screendump(Monitor *mon, const QDict *qdict,
> +                   MonitorCompletion cb, void *opaque)
>  {
>      QemuConsole *con = qemu_console_lookup_by_index(0);
> +    const char *filename = qdict_get_str(qdict, "filename");
> +
> +    if (con == NULL) {
> +        qerror_report(QERR_SCREENDUMP_NOT_AVAILABLE);
> +        return -1;
> +    }
> +    if (filename == NULL) {
> +        qerror_report(QERR_MISSING_PARAMETER, "filename");
> +        return -1;
> +    }
> +    if (con->screendump.filename != NULL) {
> +        qerror_report(QERR_SCREENDUMP_IN_PROGRESS);
> +        return -1;
> +    }
> +    con->screendump.filename = g_strdup(filename);
> +    con->screendump.completion_cb = cb;
> +    con->screendump.completion_opaque = opaque;
> +
> +    graphic_hw_update(con);
> +    return 0;
> +}
> +
> +void console_screendump_complete(QemuConsole *con)
> +{
> +    Error *errp = NULL;
>      DisplaySurface *surface;
>  
> +    if (!con->screendump.filename) {
> +        return;
> +    }
> +    assert(con->screendump.completion_cb);
> +    surface = qemu_console_surface(con);
> +    ppm_save(con->screendump.filename, surface, &errp);
> +    if (errp) {
> +        /*
> +         * TODO: return data? raise error somehow? read qmp-spec for async
> +         * error reporting.
> +         */
> +    }
> +    con->screendump.completion_cb(con->screendump.completion_opaque, NULL);
> +    g_free(con->screendump.filename);
> +    con->screendump.filename = NULL;
> +    con->screendump.completion_cb = NULL;
> +    con->screendump.completion_opaque = NULL;
> +}
> +
> +void hmp_screen_dump_helper(const char *filename, Error **errp)
> +{
> +    DisplaySurface *surface;
> +    QemuConsole *con = qemu_console_lookup_by_index(0);
> +
>      if (con == NULL) {
>          error_setg(errp, "There is no QemuConsole I can screendump from.");
>          return;
>
Gerd Hoffmann June 14, 2013, 11:18 a.m. UTC | #2
Hi,

> Note: due to QAPI not generating async commands yet I had to remove the
> schema screendump definition.

Hmm, that will break libvirt I suspect.  Guess this one has to wait
until QAPI gained proper async command support.

cheers,
  Gerd
Alon Levy June 14, 2013, 6:02 p.m. UTC | #3
> Hi,
> 
> > Note: due to QAPI not generating async commands yet I had to remove the
> > schema screendump definition.
> 
> Hmm, that will break libvirt I suspect.  Guess this one has to wait
> until QAPI gained proper async command support.

It doesn't break anything. I've tested, and here is the QMP transcript, the only difference is internal to qemu, from libvirt point of view it issues the same command and gets the same response:

$ ./x86_64-softmmu/qemu-system-x86_64 -qmp stdio
{"QMP": {"version": {"qemu": {"micro": 50, "minor": 5, "major": 1}, "package": ""}, "capabilities": []}}
{"execute":"qmp_capabilities"}
{"return": {}}
{"execute":"screendump","arguments":{"filename":"test2.ppm"}}
{"return": {}}


> 
> cheers,
>   Gerd
> 
> 
> 
>
Alon Levy June 14, 2013, 6:07 p.m. UTC | #4
> > Hi,
> > 
> > > Note: due to QAPI not generating async commands yet I had to remove the
> > > schema screendump definition.
> > 
> > Hmm, that will break libvirt I suspect.  Guess this one has to wait
> > until QAPI gained proper async command support.
> 
> It doesn't break anything. I've tested,

To clarify, I tested with "virsh screenshot" as well.

> and here is the QMP transcript, the
> only difference is internal to qemu, from libvirt point of view it issues
> the same command and gets the same response:
> 
> $ ./x86_64-softmmu/qemu-system-x86_64 -qmp stdio
> {"QMP": {"version": {"qemu": {"micro": 50, "minor": 5, "major": 1},
> "package": ""}, "capabilities": []}}
> {"execute":"qmp_capabilities"}
> {"return": {}}
> {"execute":"screendump","arguments":{"filename":"test2.ppm"}}
> {"return": {}}
> 
> 
> > 
> > cheers,
> >   Gerd
> > 
> > 
> > 
> > 
> 
>
Anthony Liguori June 14, 2013, 6:21 p.m. UTC | #5
Alon Levy <alevy@redhat.com> writes:

> This fixes the broken screendump behavior for qxl devices in native mode
> since 81fb6f1504fb9ef71f2382f44af34756668296e8.
>
> Note: due to QAPI not generating async commands yet I had to remove the
> schema screendump definition.
>
> Related RHBZ: 973374
> This patch is not enough to fix said bz, with the linux qxl driver you get garbage still, just not out of date garbage.
>
> Signed-off-by: Alon Levy <alevy@redhat.com>

Async commands are badly broken with respect to error handling.

This needs to be done after we get the new QMP server.

Why is QXL unable to do a synchronous screendump?

Regards,

Anthony Liguori

> ---
>  hmp.c                     |  2 +-
>  hw/display/qxl-render.c   |  1 +
>  hw/display/vga.c          |  1 +
>  include/qapi/qmp/qerror.h |  6 +++++
>  include/ui/console.h      | 10 ++++++++
>  qapi-schema.json          | 13 -----------
>  qmp-commands.hx           |  3 ++-
>  ui/console.c              | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
>  8 files changed, 78 insertions(+), 16 deletions(-)
>
> diff --git a/hmp.c b/hmp.c
> index 494a9aa..2a37975 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1346,7 +1346,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
>      const char *filename = qdict_get_str(qdict, "filename");
>      Error *err = NULL;
>  
> -    qmp_screendump(filename, &err);
> +    hmp_screen_dump_helper(filename, &err);
>      hmp_handle_error(mon, &err);
>  }
>  
> diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
> index f511a62..d719448 100644
> --- a/hw/display/qxl-render.c
> +++ b/hw/display/qxl-render.c
> @@ -139,6 +139,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
>                         qxl->dirty[i].bottom - qxl->dirty[i].top);
>      }
>      qxl->num_dirty_rects = 0;
> +    console_screendump_complete(vga->con);
>  }
>  
>  /*
> diff --git a/hw/display/vga.c b/hw/display/vga.c
> index 21a108d..1fc52eb 100644
> --- a/hw/display/vga.c
> +++ b/hw/display/vga.c
> @@ -1922,6 +1922,7 @@ static void vga_update_display(void *opaque)
>              break;
>          }
>      }
> +    console_screendump_complete(s->con);
>  }
>  
>  /* force a full display refresh */
> diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
> index 6c0a18d..1bd7efa 100644
> --- a/include/qapi/qmp/qerror.h
> +++ b/include/qapi/qmp/qerror.h
> @@ -237,6 +237,12 @@ void assert_no_error(Error *err);
>  #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
>      ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
>  
> +#define QERR_SCREENDUMP_NOT_AVAILABLE \
> +    ERROR_CLASS_GENERIC_ERROR, "There is no QemuConsole I can screendump from"
> +
> +#define QERR_SCREENDUMP_IN_PROGRESS \
> +    ERROR_CLASS_GENERIC_ERROR, "Existing screendump in progress"
> +
>  #define QERR_SOCKET_CONNECT_FAILED \
>      ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"
>  
> diff --git a/include/ui/console.h b/include/ui/console.h
> index 4307b5f..643da45 100644
> --- a/include/ui/console.h
> +++ b/include/ui/console.h
> @@ -341,4 +341,14 @@ int index_from_keycode(int code);
>  void early_gtk_display_init(void);
>  void gtk_display_init(DisplayState *ds);
>  
> +/* hw/display/vga.c hw/display/qxl.c */
> +void console_screendump_complete(QemuConsole *con);
> +
> +/* monitor.c */
> +int qmp_screendump(Monitor *mon, const QDict *qdict,
> +                   MonitorCompletion cb, void *opaque);
> +
> +/* hmp.c */
> +void hmp_screen_dump_helper(const char *filename, Error **errp);
> +
>  #endif
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 5ad6894..f5fdc2f 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3112,19 +3112,6 @@
>    'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
>  
>  ##
> -# @screendump:
> -#
> -# Write a PPM of the VGA screen to a file.
> -#
> -# @filename: the path of a new PPM file to store the image
> -#
> -# Returns: Nothing on success
> -#
> -# Since: 0.14.0
> -##
> -{ 'command': 'screendump', 'data': {'filename': 'str'} }
> -
> -##
>  # @nbd-server-start:
>  #
>  # Start an NBD server listening on the given host and port.  Block
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 8cea5e5..bde8f43 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -146,7 +146,8 @@ EQMP
>      {
>          .name       = "screendump",
>          .args_type  = "filename:F",
> -        .mhandler.cmd_new = qmp_marshal_input_screendump,
> +        .mhandler.cmd_async = qmp_screendump,
> +        .flags      = MONITOR_CMD_ASYNC,
>      },
>  
>  SQMP
> diff --git a/ui/console.c b/ui/console.c
> index 28bba6d..0efd588 100644
> --- a/ui/console.c
> +++ b/ui/console.c
> @@ -116,6 +116,12 @@ typedef enum {
>  struct QemuConsole {
>      Object parent;
>  
> +    struct {
> +        char *filename;
> +        MonitorCompletion *completion_cb;
> +        void *completion_opaque;
> +    } screendump;
> +
>      int index;
>      console_type_t console_type;
>      DisplayState *ds;
> @@ -313,11 +319,61 @@ write_err:
>      goto out;
>  }
>  
> -void qmp_screendump(const char *filename, Error **errp)
> +int qmp_screendump(Monitor *mon, const QDict *qdict,
> +                   MonitorCompletion cb, void *opaque)
>  {
>      QemuConsole *con = qemu_console_lookup_by_index(0);
> +    const char *filename = qdict_get_str(qdict, "filename");
> +
> +    if (con == NULL) {
> +        qerror_report(QERR_SCREENDUMP_NOT_AVAILABLE);
> +        return -1;
> +    }
> +    if (filename == NULL) {
> +        qerror_report(QERR_MISSING_PARAMETER, "filename");
> +        return -1;
> +    }
> +    if (con->screendump.filename != NULL) {
> +        qerror_report(QERR_SCREENDUMP_IN_PROGRESS);
> +        return -1;
> +    }
> +    con->screendump.filename = g_strdup(filename);
> +    con->screendump.completion_cb = cb;
> +    con->screendump.completion_opaque = opaque;
> +
> +    graphic_hw_update(con);
> +    return 0;
> +}
> +
> +void console_screendump_complete(QemuConsole *con)
> +{
> +    Error *errp = NULL;
>      DisplaySurface *surface;
>  
> +    if (!con->screendump.filename) {
> +        return;
> +    }
> +    assert(con->screendump.completion_cb);
> +    surface = qemu_console_surface(con);
> +    ppm_save(con->screendump.filename, surface, &errp);
> +    if (errp) {
> +        /*
> +         * TODO: return data? raise error somehow? read qmp-spec for async
> +         * error reporting.
> +         */
> +    }
> +    con->screendump.completion_cb(con->screendump.completion_opaque, NULL);
> +    g_free(con->screendump.filename);
> +    con->screendump.filename = NULL;
> +    con->screendump.completion_cb = NULL;
> +    con->screendump.completion_opaque = NULL;
> +}
> +
> +void hmp_screen_dump_helper(const char *filename, Error **errp)
> +{
> +    DisplaySurface *surface;
> +    QemuConsole *con = qemu_console_lookup_by_index(0);
> +
>      if (con == NULL) {
>          error_setg(errp, "There is no QemuConsole I can screendump from.");
>          return;
> -- 
> 1.8.2.1
Alon Levy June 14, 2013, 11:55 p.m. UTC | #6
On Fri, 2013-06-14 at 13:21 -0500, Anthony Liguori wrote:
> Alon Levy <alevy@redhat.com> writes:
> 
> > This fixes the broken screendump behavior for qxl devices in native mode
> > since 81fb6f1504fb9ef71f2382f44af34756668296e8.
> >
> > Note: due to QAPI not generating async commands yet I had to remove the
> > schema screendump definition.
> >
> > Related RHBZ: 973374
> > This patch is not enough to fix said bz, with the linux qxl driver you get garbage still, just not out of date garbage.
> >
> > Signed-off-by: Alon Levy <alevy@redhat.com>
> 
> Async commands are badly broken with respect to error handling.

Are there any ideas on how this should work? From the user perspective
nothing changes, so this is an internal qemu design issue afaict.

> 
> This needs to be done after we get the new QMP server.

Is there any ETA on this? I'm not pressuring, I'm just trying to figure
out if it is eminent or else I'll add a temporary patch to the
downstream (which is what I'm trying to avoid by sending this patch in
the first place).

> 
> Why is QXL unable to do a synchronous screendump?

The qxl device needs to flush all the commands that haven't been
rendered. The rendering is done off thread in the worker thread for that
device. The communication between the threads happens via a pipe that
the io thread is listening to. That is the same thread the qmp command
is received on, so there is no option to block unless I start a new
event loop in there, which is ugly. i.e. I need some kind of bottom
half, like the async command provides, which client_migrate_info uses.

> 
> Regards,
> 
> Anthony Liguori
> 
> > ---
> >  hmp.c                     |  2 +-
> >  hw/display/qxl-render.c   |  1 +
> >  hw/display/vga.c          |  1 +
> >  include/qapi/qmp/qerror.h |  6 +++++
> >  include/ui/console.h      | 10 ++++++++
> >  qapi-schema.json          | 13 -----------
> >  qmp-commands.hx           |  3 ++-
> >  ui/console.c              | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
> >  8 files changed, 78 insertions(+), 16 deletions(-)
> >
> > diff --git a/hmp.c b/hmp.c
> > index 494a9aa..2a37975 100644
> > --- a/hmp.c
> > +++ b/hmp.c
> > @@ -1346,7 +1346,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
> >      const char *filename = qdict_get_str(qdict, "filename");
> >      Error *err = NULL;
> >  
> > -    qmp_screendump(filename, &err);
> > +    hmp_screen_dump_helper(filename, &err);
> >      hmp_handle_error(mon, &err);
> >  }
> >  
> > diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
> > index f511a62..d719448 100644
> > --- a/hw/display/qxl-render.c
> > +++ b/hw/display/qxl-render.c
> > @@ -139,6 +139,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
> >                         qxl->dirty[i].bottom - qxl->dirty[i].top);
> >      }
> >      qxl->num_dirty_rects = 0;
> > +    console_screendump_complete(vga->con);
> >  }
> >  
> >  /*
> > diff --git a/hw/display/vga.c b/hw/display/vga.c
> > index 21a108d..1fc52eb 100644
> > --- a/hw/display/vga.c
> > +++ b/hw/display/vga.c
> > @@ -1922,6 +1922,7 @@ static void vga_update_display(void *opaque)
> >              break;
> >          }
> >      }
> > +    console_screendump_complete(s->con);
> >  }
> >  
> >  /* force a full display refresh */
> > diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
> > index 6c0a18d..1bd7efa 100644
> > --- a/include/qapi/qmp/qerror.h
> > +++ b/include/qapi/qmp/qerror.h
> > @@ -237,6 +237,12 @@ void assert_no_error(Error *err);
> >  #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
> >      ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
> >  
> > +#define QERR_SCREENDUMP_NOT_AVAILABLE \
> > +    ERROR_CLASS_GENERIC_ERROR, "There is no QemuConsole I can screendump from"
> > +
> > +#define QERR_SCREENDUMP_IN_PROGRESS \
> > +    ERROR_CLASS_GENERIC_ERROR, "Existing screendump in progress"
> > +
> >  #define QERR_SOCKET_CONNECT_FAILED \
> >      ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"
> >  
> > diff --git a/include/ui/console.h b/include/ui/console.h
> > index 4307b5f..643da45 100644
> > --- a/include/ui/console.h
> > +++ b/include/ui/console.h
> > @@ -341,4 +341,14 @@ int index_from_keycode(int code);
> >  void early_gtk_display_init(void);
> >  void gtk_display_init(DisplayState *ds);
> >  
> > +/* hw/display/vga.c hw/display/qxl.c */
> > +void console_screendump_complete(QemuConsole *con);
> > +
> > +/* monitor.c */
> > +int qmp_screendump(Monitor *mon, const QDict *qdict,
> > +                   MonitorCompletion cb, void *opaque);
> > +
> > +/* hmp.c */
> > +void hmp_screen_dump_helper(const char *filename, Error **errp);
> > +
> >  #endif
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index 5ad6894..f5fdc2f 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -3112,19 +3112,6 @@
> >    'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
> >  
> >  ##
> > -# @screendump:
> > -#
> > -# Write a PPM of the VGA screen to a file.
> > -#
> > -# @filename: the path of a new PPM file to store the image
> > -#
> > -# Returns: Nothing on success
> > -#
> > -# Since: 0.14.0
> > -##
> > -{ 'command': 'screendump', 'data': {'filename': 'str'} }
> > -
> > -##
> >  # @nbd-server-start:
> >  #
> >  # Start an NBD server listening on the given host and port.  Block
> > diff --git a/qmp-commands.hx b/qmp-commands.hx
> > index 8cea5e5..bde8f43 100644
> > --- a/qmp-commands.hx
> > +++ b/qmp-commands.hx
> > @@ -146,7 +146,8 @@ EQMP
> >      {
> >          .name       = "screendump",
> >          .args_type  = "filename:F",
> > -        .mhandler.cmd_new = qmp_marshal_input_screendump,
> > +        .mhandler.cmd_async = qmp_screendump,
> > +        .flags      = MONITOR_CMD_ASYNC,
> >      },
> >  
> >  SQMP
> > diff --git a/ui/console.c b/ui/console.c
> > index 28bba6d..0efd588 100644
> > --- a/ui/console.c
> > +++ b/ui/console.c
> > @@ -116,6 +116,12 @@ typedef enum {
> >  struct QemuConsole {
> >      Object parent;
> >  
> > +    struct {
> > +        char *filename;
> > +        MonitorCompletion *completion_cb;
> > +        void *completion_opaque;
> > +    } screendump;
> > +
> >      int index;
> >      console_type_t console_type;
> >      DisplayState *ds;
> > @@ -313,11 +319,61 @@ write_err:
> >      goto out;
> >  }
> >  
> > -void qmp_screendump(const char *filename, Error **errp)
> > +int qmp_screendump(Monitor *mon, const QDict *qdict,
> > +                   MonitorCompletion cb, void *opaque)
> >  {
> >      QemuConsole *con = qemu_console_lookup_by_index(0);
> > +    const char *filename = qdict_get_str(qdict, "filename");
> > +
> > +    if (con == NULL) {
> > +        qerror_report(QERR_SCREENDUMP_NOT_AVAILABLE);
> > +        return -1;
> > +    }
> > +    if (filename == NULL) {
> > +        qerror_report(QERR_MISSING_PARAMETER, "filename");
> > +        return -1;
> > +    }
> > +    if (con->screendump.filename != NULL) {
> > +        qerror_report(QERR_SCREENDUMP_IN_PROGRESS);
> > +        return -1;
> > +    }
> > +    con->screendump.filename = g_strdup(filename);
> > +    con->screendump.completion_cb = cb;
> > +    con->screendump.completion_opaque = opaque;
> > +
> > +    graphic_hw_update(con);
> > +    return 0;
> > +}
> > +
> > +void console_screendump_complete(QemuConsole *con)
> > +{
> > +    Error *errp = NULL;
> >      DisplaySurface *surface;
> >  
> > +    if (!con->screendump.filename) {
> > +        return;
> > +    }
> > +    assert(con->screendump.completion_cb);
> > +    surface = qemu_console_surface(con);
> > +    ppm_save(con->screendump.filename, surface, &errp);
> > +    if (errp) {
> > +        /*
> > +         * TODO: return data? raise error somehow? read qmp-spec for async
> > +         * error reporting.
> > +         */
> > +    }
> > +    con->screendump.completion_cb(con->screendump.completion_opaque, NULL);
> > +    g_free(con->screendump.filename);
> > +    con->screendump.filename = NULL;
> > +    con->screendump.completion_cb = NULL;
> > +    con->screendump.completion_opaque = NULL;
> > +}
> > +
> > +void hmp_screen_dump_helper(const char *filename, Error **errp)
> > +{
> > +    DisplaySurface *surface;
> > +    QemuConsole *con = qemu_console_lookup_by_index(0);
> > +
> >      if (con == NULL) {
> >          error_setg(errp, "There is no QemuConsole I can screendump from.");
> >          return;
> > -- 
> > 1.8.2.1
> 
>
Gerd Hoffmann June 17, 2013, 6:06 a.m. UTC | #7
Hi,

> Why is QXL unable to do a synchronous screendump?

Lazy rendering.  By default spice-server doesn't render anything, it
just sends over all drawing ops to the client who does the actual
rendering for the user.

qemu can kick spice-server and ask it to render stuff locally if needed.
 Happens when either the guest asks for it or when qemu needs it for its
own purposes.  One reason is screendump, the other is qxl being used
with gtk/sdl/vnc ui instead of spice.

Today we kick the spice server worker thread to do the rendering, but we
don't wait for it completing the rendering before writing out the
screendump.  That isn't perfect of course because you are not guaranteed
to get a up-to-date dump.  But works good enougth for some use cases
like autotest, which does screendumps each second anyway, so the actual
screen dump is at most one second old.


Hmm, while thinking about it:  There is another screendump change in the
pipeline: allow screen dumps from *any* device.  So, I think this is
actually a very good reason to implement a new screendump command as I
think we can kill two birds with one stone then:

First we can add a new parameter to specify the device we want dump
from.  And second we are not bound to the behavior of the existing
screendump command.  So we could simply send out a qmp event as
completion (or error) notification.

Comments?

cheers,
  Gerd

> 
> Regards,
> 
> Anthony Liguori
> 
>> ---
>>  hmp.c                     |  2 +-
>>  hw/display/qxl-render.c   |  1 +
>>  hw/display/vga.c          |  1 +
>>  include/qapi/qmp/qerror.h |  6 +++++
>>  include/ui/console.h      | 10 ++++++++
>>  qapi-schema.json          | 13 -----------
>>  qmp-commands.hx           |  3 ++-
>>  ui/console.c              | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
>>  8 files changed, 78 insertions(+), 16 deletions(-)
>>
>> diff --git a/hmp.c b/hmp.c
>> index 494a9aa..2a37975 100644
>> --- a/hmp.c
>> +++ b/hmp.c
>> @@ -1346,7 +1346,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
>>      const char *filename = qdict_get_str(qdict, "filename");
>>      Error *err = NULL;
>>  
>> -    qmp_screendump(filename, &err);
>> +    hmp_screen_dump_helper(filename, &err);
>>      hmp_handle_error(mon, &err);
>>  }
>>  
>> diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
>> index f511a62..d719448 100644
>> --- a/hw/display/qxl-render.c
>> +++ b/hw/display/qxl-render.c
>> @@ -139,6 +139,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
>>                         qxl->dirty[i].bottom - qxl->dirty[i].top);
>>      }
>>      qxl->num_dirty_rects = 0;
>> +    console_screendump_complete(vga->con);
>>  }
>>  
>>  /*
>> diff --git a/hw/display/vga.c b/hw/display/vga.c
>> index 21a108d..1fc52eb 100644
>> --- a/hw/display/vga.c
>> +++ b/hw/display/vga.c
>> @@ -1922,6 +1922,7 @@ static void vga_update_display(void *opaque)
>>              break;
>>          }
>>      }
>> +    console_screendump_complete(s->con);
>>  }
>>  
>>  /* force a full display refresh */
>> diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
>> index 6c0a18d..1bd7efa 100644
>> --- a/include/qapi/qmp/qerror.h
>> +++ b/include/qapi/qmp/qerror.h
>> @@ -237,6 +237,12 @@ void assert_no_error(Error *err);
>>  #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
>>      ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
>>  
>> +#define QERR_SCREENDUMP_NOT_AVAILABLE \
>> +    ERROR_CLASS_GENERIC_ERROR, "There is no QemuConsole I can screendump from"
>> +
>> +#define QERR_SCREENDUMP_IN_PROGRESS \
>> +    ERROR_CLASS_GENERIC_ERROR, "Existing screendump in progress"
>> +
>>  #define QERR_SOCKET_CONNECT_FAILED \
>>      ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"
>>  
>> diff --git a/include/ui/console.h b/include/ui/console.h
>> index 4307b5f..643da45 100644
>> --- a/include/ui/console.h
>> +++ b/include/ui/console.h
>> @@ -341,4 +341,14 @@ int index_from_keycode(int code);
>>  void early_gtk_display_init(void);
>>  void gtk_display_init(DisplayState *ds);
>>  
>> +/* hw/display/vga.c hw/display/qxl.c */
>> +void console_screendump_complete(QemuConsole *con);
>> +
>> +/* monitor.c */
>> +int qmp_screendump(Monitor *mon, const QDict *qdict,
>> +                   MonitorCompletion cb, void *opaque);
>> +
>> +/* hmp.c */
>> +void hmp_screen_dump_helper(const char *filename, Error **errp);
>> +
>>  #endif
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index 5ad6894..f5fdc2f 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -3112,19 +3112,6 @@
>>    'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
>>  
>>  ##
>> -# @screendump:
>> -#
>> -# Write a PPM of the VGA screen to a file.
>> -#
>> -# @filename: the path of a new PPM file to store the image
>> -#
>> -# Returns: Nothing on success
>> -#
>> -# Since: 0.14.0
>> -##
>> -{ 'command': 'screendump', 'data': {'filename': 'str'} }
>> -
>> -##
>>  # @nbd-server-start:
>>  #
>>  # Start an NBD server listening on the given host and port.  Block
>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>> index 8cea5e5..bde8f43 100644
>> --- a/qmp-commands.hx
>> +++ b/qmp-commands.hx
>> @@ -146,7 +146,8 @@ EQMP
>>      {
>>          .name       = "screendump",
>>          .args_type  = "filename:F",
>> -        .mhandler.cmd_new = qmp_marshal_input_screendump,
>> +        .mhandler.cmd_async = qmp_screendump,
>> +        .flags      = MONITOR_CMD_ASYNC,
>>      },
>>  
>>  SQMP
>> diff --git a/ui/console.c b/ui/console.c
>> index 28bba6d..0efd588 100644
>> --- a/ui/console.c
>> +++ b/ui/console.c
>> @@ -116,6 +116,12 @@ typedef enum {
>>  struct QemuConsole {
>>      Object parent;
>>  
>> +    struct {
>> +        char *filename;
>> +        MonitorCompletion *completion_cb;
>> +        void *completion_opaque;
>> +    } screendump;
>> +
>>      int index;
>>      console_type_t console_type;
>>      DisplayState *ds;
>> @@ -313,11 +319,61 @@ write_err:
>>      goto out;
>>  }
>>  
>> -void qmp_screendump(const char *filename, Error **errp)
>> +int qmp_screendump(Monitor *mon, const QDict *qdict,
>> +                   MonitorCompletion cb, void *opaque)
>>  {
>>      QemuConsole *con = qemu_console_lookup_by_index(0);
>> +    const char *filename = qdict_get_str(qdict, "filename");
>> +
>> +    if (con == NULL) {
>> +        qerror_report(QERR_SCREENDUMP_NOT_AVAILABLE);
>> +        return -1;
>> +    }
>> +    if (filename == NULL) {
>> +        qerror_report(QERR_MISSING_PARAMETER, "filename");
>> +        return -1;
>> +    }
>> +    if (con->screendump.filename != NULL) {
>> +        qerror_report(QERR_SCREENDUMP_IN_PROGRESS);
>> +        return -1;
>> +    }
>> +    con->screendump.filename = g_strdup(filename);
>> +    con->screendump.completion_cb = cb;
>> +    con->screendump.completion_opaque = opaque;
>> +
>> +    graphic_hw_update(con);
>> +    return 0;
>> +}
>> +
>> +void console_screendump_complete(QemuConsole *con)
>> +{
>> +    Error *errp = NULL;
>>      DisplaySurface *surface;
>>  
>> +    if (!con->screendump.filename) {
>> +        return;
>> +    }
>> +    assert(con->screendump.completion_cb);
>> +    surface = qemu_console_surface(con);
>> +    ppm_save(con->screendump.filename, surface, &errp);
>> +    if (errp) {
>> +        /*
>> +         * TODO: return data? raise error somehow? read qmp-spec for async
>> +         * error reporting.
>> +         */
>> +    }
>> +    con->screendump.completion_cb(con->screendump.completion_opaque, NULL);
>> +    g_free(con->screendump.filename);
>> +    con->screendump.filename = NULL;
>> +    con->screendump.completion_cb = NULL;
>> +    con->screendump.completion_opaque = NULL;
>> +}
>> +
>> +void hmp_screen_dump_helper(const char *filename, Error **errp)
>> +{
>> +    DisplaySurface *surface;
>> +    QemuConsole *con = qemu_console_lookup_by_index(0);
>> +
>>      if (con == NULL) {
>>          error_setg(errp, "There is no QemuConsole I can screendump from.");
>>          return;
>> -- 
>> 1.8.2.1
>
Alon Levy June 17, 2013, 9:30 a.m. UTC | #8
On Mon, 2013-06-17 at 08:06 +0200, Gerd Hoffmann wrote:
>   Hi,
> 
> > Why is QXL unable to do a synchronous screendump?
> 
> Lazy rendering.  By default spice-server doesn't render anything, it
> just sends over all drawing ops to the client who does the actual
> rendering for the user.
> 
> qemu can kick spice-server and ask it to render stuff locally if needed.
>  Happens when either the guest asks for it or when qemu needs it for its
> own purposes.  One reason is screendump, the other is qxl being used
> with gtk/sdl/vnc ui instead of spice.
> 
> Today we kick the spice server worker thread to do the rendering, but we
> don't wait for it completing the rendering before writing out the
> screendump.  That isn't perfect of course because you are not guaranteed
> to get a up-to-date dump.  But works good enougth for some use cases
> like autotest, which does screendumps each second anyway, so the actual
> screen dump is at most one second old.
> 
> 
> Hmm, while thinking about it:  There is another screendump change in the
> pipeline: allow screen dumps from *any* device.  So, I think this is
> actually a very good reason to implement a new screendump command as I
> think we can kill two birds with one stone then:
> 
> First we can add a new parameter to specify the device we want dump
> from.  And second we are not bound to the behavior of the existing
> screendump command.  So we could simply send out a qmp event as
> completion (or error) notification.
> 
> Comments?

I personally think having an event is not really required, it
complicates things in qemu by requiring the command to return before it
issues any events, i.e. this would be illegal (and breaks libvirt):

Libvirt: { "execute": "screendump", "arguments": { "filename":
"/tmp/image" } }
Qemu: { "event": "SCREENDUMP_COMPLETE", "data" : { "filename":
"screenshot.ppm"} }
Qemu: { "return": {} }

But on the other hand if this is something that would be acceptable now
and having proper Async error handling is not forthcoming (why btw? is
anyone working on it) . But it would become baggage later on..

> 
> cheers,
>   Gerd
> 
> > 
> > Regards,
> > 
> > Anthony Liguori
> > 
> >> ---
> >>  hmp.c                     |  2 +-
> >>  hw/display/qxl-render.c   |  1 +
> >>  hw/display/vga.c          |  1 +
> >>  include/qapi/qmp/qerror.h |  6 +++++
> >>  include/ui/console.h      | 10 ++++++++
> >>  qapi-schema.json          | 13 -----------
> >>  qmp-commands.hx           |  3 ++-
> >>  ui/console.c              | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
> >>  8 files changed, 78 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/hmp.c b/hmp.c
> >> index 494a9aa..2a37975 100644
> >> --- a/hmp.c
> >> +++ b/hmp.c
> >> @@ -1346,7 +1346,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
> >>      const char *filename = qdict_get_str(qdict, "filename");
> >>      Error *err = NULL;
> >>  
> >> -    qmp_screendump(filename, &err);
> >> +    hmp_screen_dump_helper(filename, &err);
> >>      hmp_handle_error(mon, &err);
> >>  }
> >>  
> >> diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
> >> index f511a62..d719448 100644
> >> --- a/hw/display/qxl-render.c
> >> +++ b/hw/display/qxl-render.c
> >> @@ -139,6 +139,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
> >>                         qxl->dirty[i].bottom - qxl->dirty[i].top);
> >>      }
> >>      qxl->num_dirty_rects = 0;
> >> +    console_screendump_complete(vga->con);
> >>  }
> >>  
> >>  /*
> >> diff --git a/hw/display/vga.c b/hw/display/vga.c
> >> index 21a108d..1fc52eb 100644
> >> --- a/hw/display/vga.c
> >> +++ b/hw/display/vga.c
> >> @@ -1922,6 +1922,7 @@ static void vga_update_display(void *opaque)
> >>              break;
> >>          }
> >>      }
> >> +    console_screendump_complete(s->con);
> >>  }
> >>  
> >>  /* force a full display refresh */
> >> diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
> >> index 6c0a18d..1bd7efa 100644
> >> --- a/include/qapi/qmp/qerror.h
> >> +++ b/include/qapi/qmp/qerror.h
> >> @@ -237,6 +237,12 @@ void assert_no_error(Error *err);
> >>  #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
> >>      ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
> >>  
> >> +#define QERR_SCREENDUMP_NOT_AVAILABLE \
> >> +    ERROR_CLASS_GENERIC_ERROR, "There is no QemuConsole I can screendump from"
> >> +
> >> +#define QERR_SCREENDUMP_IN_PROGRESS \
> >> +    ERROR_CLASS_GENERIC_ERROR, "Existing screendump in progress"
> >> +
> >>  #define QERR_SOCKET_CONNECT_FAILED \
> >>      ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"
> >>  
> >> diff --git a/include/ui/console.h b/include/ui/console.h
> >> index 4307b5f..643da45 100644
> >> --- a/include/ui/console.h
> >> +++ b/include/ui/console.h
> >> @@ -341,4 +341,14 @@ int index_from_keycode(int code);
> >>  void early_gtk_display_init(void);
> >>  void gtk_display_init(DisplayState *ds);
> >>  
> >> +/* hw/display/vga.c hw/display/qxl.c */
> >> +void console_screendump_complete(QemuConsole *con);
> >> +
> >> +/* monitor.c */
> >> +int qmp_screendump(Monitor *mon, const QDict *qdict,
> >> +                   MonitorCompletion cb, void *opaque);
> >> +
> >> +/* hmp.c */
> >> +void hmp_screen_dump_helper(const char *filename, Error **errp);
> >> +
> >>  #endif
> >> diff --git a/qapi-schema.json b/qapi-schema.json
> >> index 5ad6894..f5fdc2f 100644
> >> --- a/qapi-schema.json
> >> +++ b/qapi-schema.json
> >> @@ -3112,19 +3112,6 @@
> >>    'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
> >>  
> >>  ##
> >> -# @screendump:
> >> -#
> >> -# Write a PPM of the VGA screen to a file.
> >> -#
> >> -# @filename: the path of a new PPM file to store the image
> >> -#
> >> -# Returns: Nothing on success
> >> -#
> >> -# Since: 0.14.0
> >> -##
> >> -{ 'command': 'screendump', 'data': {'filename': 'str'} }
> >> -
> >> -##
> >>  # @nbd-server-start:
> >>  #
> >>  # Start an NBD server listening on the given host and port.  Block
> >> diff --git a/qmp-commands.hx b/qmp-commands.hx
> >> index 8cea5e5..bde8f43 100644
> >> --- a/qmp-commands.hx
> >> +++ b/qmp-commands.hx
> >> @@ -146,7 +146,8 @@ EQMP
> >>      {
> >>          .name       = "screendump",
> >>          .args_type  = "filename:F",
> >> -        .mhandler.cmd_new = qmp_marshal_input_screendump,
> >> +        .mhandler.cmd_async = qmp_screendump,
> >> +        .flags      = MONITOR_CMD_ASYNC,
> >>      },
> >>  
> >>  SQMP
> >> diff --git a/ui/console.c b/ui/console.c
> >> index 28bba6d..0efd588 100644
> >> --- a/ui/console.c
> >> +++ b/ui/console.c
> >> @@ -116,6 +116,12 @@ typedef enum {
> >>  struct QemuConsole {
> >>      Object parent;
> >>  
> >> +    struct {
> >> +        char *filename;
> >> +        MonitorCompletion *completion_cb;
> >> +        void *completion_opaque;
> >> +    } screendump;
> >> +
> >>      int index;
> >>      console_type_t console_type;
> >>      DisplayState *ds;
> >> @@ -313,11 +319,61 @@ write_err:
> >>      goto out;
> >>  }
> >>  
> >> -void qmp_screendump(const char *filename, Error **errp)
> >> +int qmp_screendump(Monitor *mon, const QDict *qdict,
> >> +                   MonitorCompletion cb, void *opaque)
> >>  {
> >>      QemuConsole *con = qemu_console_lookup_by_index(0);
> >> +    const char *filename = qdict_get_str(qdict, "filename");
> >> +
> >> +    if (con == NULL) {
> >> +        qerror_report(QERR_SCREENDUMP_NOT_AVAILABLE);
> >> +        return -1;
> >> +    }
> >> +    if (filename == NULL) {
> >> +        qerror_report(QERR_MISSING_PARAMETER, "filename");
> >> +        return -1;
> >> +    }
> >> +    if (con->screendump.filename != NULL) {
> >> +        qerror_report(QERR_SCREENDUMP_IN_PROGRESS);
> >> +        return -1;
> >> +    }
> >> +    con->screendump.filename = g_strdup(filename);
> >> +    con->screendump.completion_cb = cb;
> >> +    con->screendump.completion_opaque = opaque;
> >> +
> >> +    graphic_hw_update(con);
> >> +    return 0;
> >> +}
> >> +
> >> +void console_screendump_complete(QemuConsole *con)
> >> +{
> >> +    Error *errp = NULL;
> >>      DisplaySurface *surface;
> >>  
> >> +    if (!con->screendump.filename) {
> >> +        return;
> >> +    }
> >> +    assert(con->screendump.completion_cb);
> >> +    surface = qemu_console_surface(con);
> >> +    ppm_save(con->screendump.filename, surface, &errp);
> >> +    if (errp) {
> >> +        /*
> >> +         * TODO: return data? raise error somehow? read qmp-spec for async
> >> +         * error reporting.
> >> +         */
> >> +    }
> >> +    con->screendump.completion_cb(con->screendump.completion_opaque, NULL);
> >> +    g_free(con->screendump.filename);
> >> +    con->screendump.filename = NULL;
> >> +    con->screendump.completion_cb = NULL;
> >> +    con->screendump.completion_opaque = NULL;
> >> +}
> >> +
> >> +void hmp_screen_dump_helper(const char *filename, Error **errp)
> >> +{
> >> +    DisplaySurface *surface;
> >> +    QemuConsole *con = qemu_console_lookup_by_index(0);
> >> +
> >>      if (con == NULL) {
> >>          error_setg(errp, "There is no QemuConsole I can screendump from.");
> >>          return;
> >> -- 
> >> 1.8.2.1
> > 
>
Luiz Capitulino June 17, 2013, 1:41 p.m. UTC | #9
On Fri, 14 Jun 2013 19:55:21 -0400
Alon Levy <alevy@redhat.com> wrote:

> On Fri, 2013-06-14 at 13:21 -0500, Anthony Liguori wrote:
> > Alon Levy <alevy@redhat.com> writes:
> > 
> > > This fixes the broken screendump behavior for qxl devices in native mode
> > > since 81fb6f1504fb9ef71f2382f44af34756668296e8.
> > >
> > > Note: due to QAPI not generating async commands yet I had to remove the
> > > schema screendump definition.
> > >
> > > Related RHBZ: 973374
> > > This patch is not enough to fix said bz, with the linux qxl driver you get garbage still, just not out of date garbage.
> > >
> > > Signed-off-by: Alon Levy <alevy@redhat.com>
> > 
> > Async commands are badly broken with respect to error handling.
> 
> Are there any ideas on how this should work? From the user perspective
> nothing changes, so this is an internal qemu design issue afaict.
> 
> > 
> > This needs to be done after we get the new QMP server.
> 
> Is there any ETA on this? I'm not pressuring, I'm just trying to figure
> out if it is eminent or else I'll add a temporary patch to the
> downstream (which is what I'm trying to avoid by sending this patch in
> the first place).

No ETA. We have to finish QAPI conversion work first (which is halted ATM)
_or_ we lazily port the missing conversions by doing error conversion
only _or_ we add some legacy mechanism to the new QMP server.

More importantly though, IIRC we have agreed on not having async commands
at all. All async stuff should be done with command plus event.
Luiz Capitulino June 17, 2013, 1:54 p.m. UTC | #10
On Mon, 17 Jun 2013 08:06:58 +0200
Gerd Hoffmann <kraxel@redhat.com> wrote:

> Hmm, while thinking about it:  There is another screendump change in the
> pipeline: allow screen dumps from *any* device.  So, I think this is
> actually a very good reason to implement a new screendump command as I
> think we can kill two birds with one stone then:
> 
> First we can add a new parameter to specify the device we want dump
> from.  And second we are not bound to the behavior of the existing
> screendump command.  So we could simply send out a qmp event as
> completion (or error) notification.

That's how we agreed on doing async commands, and looks like a good
solution to me.
Gerd Hoffmann June 17, 2013, 2 p.m. UTC | #11
On 06/17/13 11:30, Alon Levy wrote:
> But on the other hand if this is something that would be acceptable now
> and having proper Async error handling is not forthcoming (why btw? is
> anyone working on it) . But it would become baggage later on..

Don't think it is too bad, RfC patches will go out in a moment ...

cheers,
  Gerd
diff mbox

Patch

diff --git a/hmp.c b/hmp.c
index 494a9aa..2a37975 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1346,7 +1346,7 @@  void hmp_screen_dump(Monitor *mon, const QDict *qdict)
     const char *filename = qdict_get_str(qdict, "filename");
     Error *err = NULL;
 
-    qmp_screendump(filename, &err);
+    hmp_screen_dump_helper(filename, &err);
     hmp_handle_error(mon, &err);
 }
 
diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
index f511a62..d719448 100644
--- a/hw/display/qxl-render.c
+++ b/hw/display/qxl-render.c
@@ -139,6 +139,7 @@  static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
                        qxl->dirty[i].bottom - qxl->dirty[i].top);
     }
     qxl->num_dirty_rects = 0;
+    console_screendump_complete(vga->con);
 }
 
 /*
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 21a108d..1fc52eb 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1922,6 +1922,7 @@  static void vga_update_display(void *opaque)
             break;
         }
     }
+    console_screendump_complete(s->con);
 }
 
 /* force a full display refresh */
diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
index 6c0a18d..1bd7efa 100644
--- a/include/qapi/qmp/qerror.h
+++ b/include/qapi/qmp/qerror.h
@@ -237,6 +237,12 @@  void assert_no_error(Error *err);
 #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
     ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
 
+#define QERR_SCREENDUMP_NOT_AVAILABLE \
+    ERROR_CLASS_GENERIC_ERROR, "There is no QemuConsole I can screendump from"
+
+#define QERR_SCREENDUMP_IN_PROGRESS \
+    ERROR_CLASS_GENERIC_ERROR, "Existing screendump in progress"
+
 #define QERR_SOCKET_CONNECT_FAILED \
     ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"
 
diff --git a/include/ui/console.h b/include/ui/console.h
index 4307b5f..643da45 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -341,4 +341,14 @@  int index_from_keycode(int code);
 void early_gtk_display_init(void);
 void gtk_display_init(DisplayState *ds);
 
+/* hw/display/vga.c hw/display/qxl.c */
+void console_screendump_complete(QemuConsole *con);
+
+/* monitor.c */
+int qmp_screendump(Monitor *mon, const QDict *qdict,
+                   MonitorCompletion cb, void *opaque);
+
+/* hmp.c */
+void hmp_screen_dump_helper(const char *filename, Error **errp);
+
 #endif
diff --git a/qapi-schema.json b/qapi-schema.json
index 5ad6894..f5fdc2f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3112,19 +3112,6 @@ 
   'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
 
 ##
-# @screendump:
-#
-# Write a PPM of the VGA screen to a file.
-#
-# @filename: the path of a new PPM file to store the image
-#
-# Returns: Nothing on success
-#
-# Since: 0.14.0
-##
-{ 'command': 'screendump', 'data': {'filename': 'str'} }
-
-##
 # @nbd-server-start:
 #
 # Start an NBD server listening on the given host and port.  Block
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 8cea5e5..bde8f43 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -146,7 +146,8 @@  EQMP
     {
         .name       = "screendump",
         .args_type  = "filename:F",
-        .mhandler.cmd_new = qmp_marshal_input_screendump,
+        .mhandler.cmd_async = qmp_screendump,
+        .flags      = MONITOR_CMD_ASYNC,
     },
 
 SQMP
diff --git a/ui/console.c b/ui/console.c
index 28bba6d..0efd588 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -116,6 +116,12 @@  typedef enum {
 struct QemuConsole {
     Object parent;
 
+    struct {
+        char *filename;
+        MonitorCompletion *completion_cb;
+        void *completion_opaque;
+    } screendump;
+
     int index;
     console_type_t console_type;
     DisplayState *ds;
@@ -313,11 +319,61 @@  write_err:
     goto out;
 }
 
-void qmp_screendump(const char *filename, Error **errp)
+int qmp_screendump(Monitor *mon, const QDict *qdict,
+                   MonitorCompletion cb, void *opaque)
 {
     QemuConsole *con = qemu_console_lookup_by_index(0);
+    const char *filename = qdict_get_str(qdict, "filename");
+
+    if (con == NULL) {
+        qerror_report(QERR_SCREENDUMP_NOT_AVAILABLE);
+        return -1;
+    }
+    if (filename == NULL) {
+        qerror_report(QERR_MISSING_PARAMETER, "filename");
+        return -1;
+    }
+    if (con->screendump.filename != NULL) {
+        qerror_report(QERR_SCREENDUMP_IN_PROGRESS);
+        return -1;
+    }
+    con->screendump.filename = g_strdup(filename);
+    con->screendump.completion_cb = cb;
+    con->screendump.completion_opaque = opaque;
+
+    graphic_hw_update(con);
+    return 0;
+}
+
+void console_screendump_complete(QemuConsole *con)
+{
+    Error *errp = NULL;
     DisplaySurface *surface;
 
+    if (!con->screendump.filename) {
+        return;
+    }
+    assert(con->screendump.completion_cb);
+    surface = qemu_console_surface(con);
+    ppm_save(con->screendump.filename, surface, &errp);
+    if (errp) {
+        /*
+         * TODO: return data? raise error somehow? read qmp-spec for async
+         * error reporting.
+         */
+    }
+    con->screendump.completion_cb(con->screendump.completion_opaque, NULL);
+    g_free(con->screendump.filename);
+    con->screendump.filename = NULL;
+    con->screendump.completion_cb = NULL;
+    con->screendump.completion_opaque = NULL;
+}
+
+void hmp_screen_dump_helper(const char *filename, Error **errp)
+{
+    DisplaySurface *surface;
+    QemuConsole *con = qemu_console_lookup_by_index(0);
+
     if (con == NULL) {
         error_setg(errp, "There is no QemuConsole I can screendump from.");
         return;