diff mbox series

[RFC,v5,024/126] error: auto propagated local_err

Message ID 20191011160552.22907-25-vsementsov@virtuozzo.com (mailing list archive)
State New, archived
Headers show
Series error: auto propagated local_err | expand

Commit Message

Vladimir Sementsov-Ogievskiy Oct. 11, 2019, 4:04 p.m. UTC
Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
functions with errp OUT parameter.

It has three goals:

1. Fix issue with error_fatal & error_prepend/error_append_hint: user
can't see this additional information, because exit() happens in
error_setg earlier than information is added. [Reported by Greg Kurz]

2. Fix issue with error_abort & error_propagate: when we wrap
error_abort by local_err+error_propagate, resulting coredump will
refer to error_propagate and not to the place where error happened.
(the macro itself doesn't fix the issue, but it allows to [3.] drop all
local_err+error_propagate pattern, which will definitely fix the issue)
[Reported by Kevin Wolf]

3. Drop local_err+error_propagate pattern, which is used to workaround
void functions with errp parameter, when caller wants to know resulting
status. (Note: actually these functions could be merely updated to
return int error code).

To achieve these goals, we need to add invocation of the macro at start
of functions, which needs error_prepend/error_append_hint (1.); add
invocation of the macro at start of functions which do
local_err+error_propagate scenario the check errors, drop local errors
from them and just use *errp instead (2., 3.).

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---

CC: Gerd Hoffmann <kraxel@redhat.com>
CC: "Gonglei (Arei)" <arei.gonglei@huawei.com>
CC: Eduardo Habkost <ehabkost@redhat.com>
CC: Igor Mammedov <imammedo@redhat.com>
CC: Laurent Vivier <lvivier@redhat.com>
CC: Amit Shah <amit@kernel.org>
CC: Kevin Wolf <kwolf@redhat.com>
CC: Max Reitz <mreitz@redhat.com>
CC: John Snow <jsnow@redhat.com>
CC: Ari Sundholm <ari@tuxera.com>
CC: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
CC: Paolo Bonzini <pbonzini@redhat.com>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Fam Zheng <fam@euphon.net>
CC: Stefan Weil <sw@weilnetz.de>
CC: Ronnie Sahlberg <ronniesahlberg@gmail.com>
CC: Peter Lieven <pl@kamp.de>
CC: Eric Blake <eblake@redhat.com>
CC: "Denis V. Lunev" <den@openvz.org>
CC: Markus Armbruster <armbru@redhat.com>
CC: Alberto Garcia <berto@igalia.com>
CC: Jason Dillaman <dillaman@redhat.com>
CC: Wen Congyang <wencongyang2@huawei.com>
CC: Xie Changlong <xiechanglong.d@gmail.com>
CC: Liu Yuan <namei.unix@gmail.com>
CC: "Richard W.M. Jones" <rjones@redhat.com>
CC: Jeff Cody <codyprime@gmail.com>
CC: "Marc-André Lureau" <marcandre.lureau@redhat.com>
CC: "Daniel P. Berrangé" <berrange@redhat.com>
CC: Richard Henderson <rth@twiddle.net>
CC: Greg Kurz <groug@kaod.org>
CC: "Michael S. Tsirkin" <mst@redhat.com>
CC: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
CC: Beniamino Galvani <b.galvani@gmail.com>
CC: Peter Maydell <peter.maydell@linaro.org>
CC: "Cédric Le Goater" <clg@kaod.org>
CC: Andrew Jeffery <andrew@aj.id.au>
CC: Joel Stanley <joel@jms.id.au>
CC: Andrew Baumann <Andrew.Baumann@microsoft.com>
CC: "Philippe Mathieu-Daudé" <philmd@redhat.com>
CC: Antony Pavlov <antonynpavlov@gmail.com>
CC: Jean-Christophe Dubois <jcd@tribudubois.net>
CC: Peter Chubb <peter.chubb@nicta.com.au>
CC: Subbaraya Sundeep <sundeep.lkml@gmail.com>
CC: Eric Auger <eric.auger@redhat.com>
CC: Alistair Francis <alistair@alistair23.me>
CC: "Edgar E. Iglesias" <edgar.iglesias@gmail.com>
CC: Stefano Stabellini <sstabellini@kernel.org>
CC: Anthony Perard <anthony.perard@citrix.com>
CC: Paul Durrant <paul@xen.org>
CC: Paul Burton <pburton@wavecomp.com>
CC: Aleksandar Rikalo <arikalo@wavecomp.com>
CC: Chris Wulff <crwulff@gmail.com>
CC: Marek Vasut <marex@denx.de>
CC: David Gibson <david@gibson.dropbear.id.au>
CC: Cornelia Huck <cohuck@redhat.com>
CC: Halil Pasic <pasic@linux.ibm.com>
CC: Christian Borntraeger <borntraeger@de.ibm.com>
CC: "Hervé Poussineau" <hpoussin@reactos.org>
CC: Xiao Guangrong <xiaoguangrong.eric@gmail.com>
CC: Aurelien Jarno <aurelien@aurel32.net>
CC: Aleksandar Markovic <amarkovic@wavecomp.com>
CC: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
CC: Jason Wang <jasowang@redhat.com>
CC: Laszlo Ersek <lersek@redhat.com>
CC: Yuval Shaia <yuval.shaia@oracle.com>
CC: Palmer Dabbelt <palmer@sifive.com>
CC: Sagar Karandikar <sagark@eecs.berkeley.edu>
CC: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
CC: David Hildenbrand <david@redhat.com>
CC: Thomas Huth <thuth@redhat.com>
CC: Eric Farman <farman@linux.ibm.com>
CC: Matthew Rosato <mjrosato@linux.ibm.com>
CC: Hannes Reinecke <hare@suse.com>
CC: Michael Walle <michael@walle.cc>
CC: Artyom Tarasenko <atar4qemu@gmail.com>
CC: Stefan Berger <stefanb@linux.ibm.com>
CC: Samuel Thibault <samuel.thibault@ens-lyon.org>
CC: Alex Williamson <alex.williamson@redhat.com>
CC: Tony Krowiak <akrowiak@linux.ibm.com>
CC: Pierre Morel <pmorel@linux.ibm.com>
CC: Michael Roth <mdroth@linux.vnet.ibm.com>
CC: Hailiang Zhang <zhang.zhanghailiang@huawei.com>
CC: Juan Quintela <quintela@redhat.com>
CC: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
CC: Luigi Rizzo <rizzo@iet.unipi.it>
CC: Giuseppe Lettieri <g.lettieri@iet.unipi.it>
CC: Vincenzo Maffione <v.maffione@gmail.com>
CC: Jan Kiszka <jan.kiszka@siemens.com>
CC: Anthony Green <green@moxielogic.com>
CC: Stafford Horne <shorne@gmail.com>
CC: Guan Xuetao <gxt@mprc.pku.edu.cn>
CC: Max Filippov <jcmvbkbc@gmail.com>
CC: qemu-block@nongnu.org
CC: integration@gluster.org
CC: sheepdog@lists.wpkg.org
CC: qemu-arm@nongnu.org
CC: xen-devel@lists.xenproject.org
CC: qemu-ppc@nongnu.org
CC: qemu-s390x@nongnu.org
CC: qemu-riscv@nongnu.org

 include/qapi/error.h | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

Comments

Marc-André Lureau Nov. 8, 2019, 9:10 p.m. UTC | #1
On Fri, Oct 11, 2019 at 10:11 PM Vladimir Sementsov-Ogievskiy
<vsementsov@virtuozzo.com> wrote:
>
> Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
> functions with errp OUT parameter.
>
> It has three goals:
>
> 1. Fix issue with error_fatal & error_prepend/error_append_hint: user
> can't see this additional information, because exit() happens in
> error_setg earlier than information is added. [Reported by Greg Kurz]
>
> 2. Fix issue with error_abort & error_propagate: when we wrap
> error_abort by local_err+error_propagate, resulting coredump will
> refer to error_propagate and not to the place where error happened.
> (the macro itself doesn't fix the issue, but it allows to [3.] drop all
> local_err+error_propagate pattern, which will definitely fix the issue)
> [Reported by Kevin Wolf]
>
> 3. Drop local_err+error_propagate pattern, which is used to workaround
> void functions with errp parameter, when caller wants to know resulting
> status. (Note: actually these functions could be merely updated to
> return int error code).
>
> To achieve these goals, we need to add invocation of the macro at start
> of functions, which needs error_prepend/error_append_hint (1.); add
> invocation of the macro at start of functions which do
> local_err+error_propagate scenario the check errors, drop local errors
> from them and just use *errp instead (2., 3.).
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---
>
> CC: Gerd Hoffmann <kraxel@redhat.com>
> CC: "Gonglei (Arei)" <arei.gonglei@huawei.com>
> CC: Eduardo Habkost <ehabkost@redhat.com>
> CC: Igor Mammedov <imammedo@redhat.com>
> CC: Laurent Vivier <lvivier@redhat.com>
> CC: Amit Shah <amit@kernel.org>
> CC: Kevin Wolf <kwolf@redhat.com>
> CC: Max Reitz <mreitz@redhat.com>
> CC: John Snow <jsnow@redhat.com>
> CC: Ari Sundholm <ari@tuxera.com>
> CC: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
> CC: Paolo Bonzini <pbonzini@redhat.com>
> CC: Stefan Hajnoczi <stefanha@redhat.com>
> CC: Fam Zheng <fam@euphon.net>
> CC: Stefan Weil <sw@weilnetz.de>
> CC: Ronnie Sahlberg <ronniesahlberg@gmail.com>
> CC: Peter Lieven <pl@kamp.de>
> CC: Eric Blake <eblake@redhat.com>
> CC: "Denis V. Lunev" <den@openvz.org>
> CC: Markus Armbruster <armbru@redhat.com>
> CC: Alberto Garcia <berto@igalia.com>
> CC: Jason Dillaman <dillaman@redhat.com>
> CC: Wen Congyang <wencongyang2@huawei.com>
> CC: Xie Changlong <xiechanglong.d@gmail.com>
> CC: Liu Yuan <namei.unix@gmail.com>
> CC: "Richard W.M. Jones" <rjones@redhat.com>
> CC: Jeff Cody <codyprime@gmail.com>
> CC: "Marc-André Lureau" <marcandre.lureau@redhat.com>
> CC: "Daniel P. Berrangé" <berrange@redhat.com>
> CC: Richard Henderson <rth@twiddle.net>
> CC: Greg Kurz <groug@kaod.org>
> CC: "Michael S. Tsirkin" <mst@redhat.com>
> CC: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
> CC: Beniamino Galvani <b.galvani@gmail.com>
> CC: Peter Maydell <peter.maydell@linaro.org>
> CC: "Cédric Le Goater" <clg@kaod.org>
> CC: Andrew Jeffery <andrew@aj.id.au>
> CC: Joel Stanley <joel@jms.id.au>
> CC: Andrew Baumann <Andrew.Baumann@microsoft.com>
> CC: "Philippe Mathieu-Daudé" <philmd@redhat.com>
> CC: Antony Pavlov <antonynpavlov@gmail.com>
> CC: Jean-Christophe Dubois <jcd@tribudubois.net>
> CC: Peter Chubb <peter.chubb@nicta.com.au>
> CC: Subbaraya Sundeep <sundeep.lkml@gmail.com>
> CC: Eric Auger <eric.auger@redhat.com>
> CC: Alistair Francis <alistair@alistair23.me>
> CC: "Edgar E. Iglesias" <edgar.iglesias@gmail.com>
> CC: Stefano Stabellini <sstabellini@kernel.org>
> CC: Anthony Perard <anthony.perard@citrix.com>
> CC: Paul Durrant <paul@xen.org>
> CC: Paul Burton <pburton@wavecomp.com>
> CC: Aleksandar Rikalo <arikalo@wavecomp.com>
> CC: Chris Wulff <crwulff@gmail.com>
> CC: Marek Vasut <marex@denx.de>
> CC: David Gibson <david@gibson.dropbear.id.au>
> CC: Cornelia Huck <cohuck@redhat.com>
> CC: Halil Pasic <pasic@linux.ibm.com>
> CC: Christian Borntraeger <borntraeger@de.ibm.com>
> CC: "Hervé Poussineau" <hpoussin@reactos.org>
> CC: Xiao Guangrong <xiaoguangrong.eric@gmail.com>
> CC: Aurelien Jarno <aurelien@aurel32.net>
> CC: Aleksandar Markovic <amarkovic@wavecomp.com>
> CC: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
> CC: Jason Wang <jasowang@redhat.com>
> CC: Laszlo Ersek <lersek@redhat.com>
> CC: Yuval Shaia <yuval.shaia@oracle.com>
> CC: Palmer Dabbelt <palmer@sifive.com>
> CC: Sagar Karandikar <sagark@eecs.berkeley.edu>
> CC: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
> CC: David Hildenbrand <david@redhat.com>
> CC: Thomas Huth <thuth@redhat.com>
> CC: Eric Farman <farman@linux.ibm.com>
> CC: Matthew Rosato <mjrosato@linux.ibm.com>
> CC: Hannes Reinecke <hare@suse.com>
> CC: Michael Walle <michael@walle.cc>
> CC: Artyom Tarasenko <atar4qemu@gmail.com>
> CC: Stefan Berger <stefanb@linux.ibm.com>
> CC: Samuel Thibault <samuel.thibault@ens-lyon.org>
> CC: Alex Williamson <alex.williamson@redhat.com>
> CC: Tony Krowiak <akrowiak@linux.ibm.com>
> CC: Pierre Morel <pmorel@linux.ibm.com>
> CC: Michael Roth <mdroth@linux.vnet.ibm.com>
> CC: Hailiang Zhang <zhang.zhanghailiang@huawei.com>
> CC: Juan Quintela <quintela@redhat.com>
> CC: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
> CC: Luigi Rizzo <rizzo@iet.unipi.it>
> CC: Giuseppe Lettieri <g.lettieri@iet.unipi.it>
> CC: Vincenzo Maffione <v.maffione@gmail.com>
> CC: Jan Kiszka <jan.kiszka@siemens.com>
> CC: Anthony Green <green@moxielogic.com>
> CC: Stafford Horne <shorne@gmail.com>
> CC: Guan Xuetao <gxt@mprc.pku.edu.cn>
> CC: Max Filippov <jcmvbkbc@gmail.com>
> CC: qemu-block@nongnu.org
> CC: integration@gluster.org
> CC: sheepdog@lists.wpkg.org
> CC: qemu-arm@nongnu.org
> CC: xen-devel@lists.xenproject.org
> CC: qemu-ppc@nongnu.org
> CC: qemu-s390x@nongnu.org
> CC: qemu-riscv@nongnu.org
>
>  include/qapi/error.h | 38 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
>
> diff --git a/include/qapi/error.h b/include/qapi/error.h
> index d6898d833b..47238d9065 100644
> --- a/include/qapi/error.h
> +++ b/include/qapi/error.h
> @@ -345,6 +345,44 @@ void error_set_internal(Error **errp,
>                          ErrorClass err_class, const char *fmt, ...)
>      GCC_FMT_ATTR(6, 7);
>
> +typedef struct ErrorPropagator {
> +    Error *local_err;
> +    Error **errp;
> +} ErrorPropagator;
> +
> +static inline void error_propagator_cleanup(ErrorPropagator *prop)
> +{
> +    error_propagate(prop->errp, prop->local_err);
> +}
> +
> +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup);
> +
> +/*
> + * ERRP_AUTO_PROPAGATE
> + *
> + * This macro is created to be the first line of a function with Error **errp
> + * OUT parameter. It's needed only in cases where we want to use error_prepend,
> + * error_append_hint or dereference *errp. It's still safe (but useless) in
> + * other cases.
> + *
> + * If errp is NULL or points to error_fatal, it is rewritten to point to a
> + * local Error object, which will be automatically propagated to the original
> + * errp on function exit (see error_propagator_cleanup).
> + *
> + * After invocation of this macro it is always safe to dereference errp
> + * (as it's not NULL anymore) and to add information (by error_prepend or
> + * error_append_hint)
> + * (as, if it was error_fatal, we swapped it with a local_error to be
> + * propagated on cleanup).

Nice improvements. Minor drawback, the abort()/exit() will now take
place when going out of scope and running the cleanup instead of error
location. Not a big problem I guess.

> + *
> + * Note: we don't wrap the error_abort case, as we want resulting coredump
> + * to point to the place where the error happened, not to error_propagate.
> + */
> +#define ERRP_AUTO_PROPAGATE()                                  \
> +    g_auto(ErrorPropagator) _auto_errp_prop = {.errp = errp};  \
> +    errp = ((errp == NULL || *errp == error_fatal)             \
> +            ? &_auto_errp_prop.local_err : errp)
> +
>  /*
>   * Special error destination to abort on error.
>   * See error_setg() and error_propagate() for details.
> --
> 2.21.0
>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Eric Blake Nov. 8, 2019, 10:45 p.m. UTC | #2
On 11/8/19 3:10 PM, Marc-André Lureau wrote:

>> +/*
>> + * ERRP_AUTO_PROPAGATE
>> + *
>> + * This macro is created to be the first line of a function with Error **errp
>> + * OUT parameter. It's needed only in cases where we want to use error_prepend,
>> + * error_append_hint or dereference *errp. It's still safe (but useless) in
>> + * other cases.
>> + *
>> + * If errp is NULL or points to error_fatal, it is rewritten to point to a
>> + * local Error object, which will be automatically propagated to the original
>> + * errp on function exit (see error_propagator_cleanup).
>> + *
>> + * After invocation of this macro it is always safe to dereference errp
>> + * (as it's not NULL anymore) and to add information (by error_prepend or
>> + * error_append_hint)
>> + * (as, if it was error_fatal, we swapped it with a local_error to be
>> + * propagated on cleanup).
> 
> Nice improvements. Minor drawback, the abort()/exit() will now take
> place when going out of scope and running the cleanup instead of error
> location. Not a big problem I guess.

Your assessment is not quite right:

Any abort() will happen at the leaf node (because we are no longer 
wrapping thing into a local err and skipping error_propagate altogether 
for &error_abort).

You are correct that any exit() will now happen during cleanup, but that 
is an undetectable change (there is no stack trace present for 
&error_fatal, so calling error_propagate at a later point in time does 
not affect the observable end behavior).
Markus Armbruster Dec. 4, 2019, 2:59 p.m. UTC | #3
Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:

> Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
> functions with errp OUT parameter.
>
> It has three goals:
>
> 1. Fix issue with error_fatal & error_prepend/error_append_hint: user
> can't see this additional information, because exit() happens in
> error_setg earlier than information is added. [Reported by Greg Kurz]
>
> 2. Fix issue with error_abort & error_propagate: when we wrap
> error_abort by local_err+error_propagate, resulting coredump will
> refer to error_propagate and not to the place where error happened.

I get what you mean, but I have plenty of context.

> (the macro itself doesn't fix the issue, but it allows to [3.] drop all
> local_err+error_propagate pattern, which will definitely fix the issue)

The parenthesis is not part of the goal.

> [Reported by Kevin Wolf]
>
> 3. Drop local_err+error_propagate pattern, which is used to workaround
> void functions with errp parameter, when caller wants to know resulting
> status. (Note: actually these functions could be merely updated to
> return int error code).
>
> To achieve these goals, we need to add invocation of the macro at start
> of functions, which needs error_prepend/error_append_hint (1.); add
> invocation of the macro at start of functions which do
> local_err+error_propagate scenario the check errors, drop local errors
> from them and just use *errp instead (2., 3.).

The paragraph talks about two cases: 1. and 2.+3.  Makes me think we
want two paragraphs, each illustrated with an example.

What about you provide the examples, and then I try to polish the prose?

> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---
>
> CC: Gerd Hoffmann <kraxel@redhat.com>
> CC: "Gonglei (Arei)" <arei.gonglei@huawei.com>
> CC: Eduardo Habkost <ehabkost@redhat.com>
> CC: Igor Mammedov <imammedo@redhat.com>
> CC: Laurent Vivier <lvivier@redhat.com>
> CC: Amit Shah <amit@kernel.org>
> CC: Kevin Wolf <kwolf@redhat.com>
> CC: Max Reitz <mreitz@redhat.com>
> CC: John Snow <jsnow@redhat.com>
> CC: Ari Sundholm <ari@tuxera.com>
> CC: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
> CC: Paolo Bonzini <pbonzini@redhat.com>
> CC: Stefan Hajnoczi <stefanha@redhat.com>
> CC: Fam Zheng <fam@euphon.net>
> CC: Stefan Weil <sw@weilnetz.de>
> CC: Ronnie Sahlberg <ronniesahlberg@gmail.com>
> CC: Peter Lieven <pl@kamp.de>
> CC: Eric Blake <eblake@redhat.com>
> CC: "Denis V. Lunev" <den@openvz.org>
> CC: Markus Armbruster <armbru@redhat.com>
> CC: Alberto Garcia <berto@igalia.com>
> CC: Jason Dillaman <dillaman@redhat.com>
> CC: Wen Congyang <wencongyang2@huawei.com>
> CC: Xie Changlong <xiechanglong.d@gmail.com>
> CC: Liu Yuan <namei.unix@gmail.com>
> CC: "Richard W.M. Jones" <rjones@redhat.com>
> CC: Jeff Cody <codyprime@gmail.com>
> CC: "Marc-André Lureau" <marcandre.lureau@redhat.com>
> CC: "Daniel P. Berrangé" <berrange@redhat.com>
> CC: Richard Henderson <rth@twiddle.net>
> CC: Greg Kurz <groug@kaod.org>
> CC: "Michael S. Tsirkin" <mst@redhat.com>
> CC: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
> CC: Beniamino Galvani <b.galvani@gmail.com>
> CC: Peter Maydell <peter.maydell@linaro.org>
> CC: "Cédric Le Goater" <clg@kaod.org>
> CC: Andrew Jeffery <andrew@aj.id.au>
> CC: Joel Stanley <joel@jms.id.au>
> CC: Andrew Baumann <Andrew.Baumann@microsoft.com>
> CC: "Philippe Mathieu-Daudé" <philmd@redhat.com>
> CC: Antony Pavlov <antonynpavlov@gmail.com>
> CC: Jean-Christophe Dubois <jcd@tribudubois.net>
> CC: Peter Chubb <peter.chubb@nicta.com.au>
> CC: Subbaraya Sundeep <sundeep.lkml@gmail.com>
> CC: Eric Auger <eric.auger@redhat.com>
> CC: Alistair Francis <alistair@alistair23.me>
> CC: "Edgar E. Iglesias" <edgar.iglesias@gmail.com>
> CC: Stefano Stabellini <sstabellini@kernel.org>
> CC: Anthony Perard <anthony.perard@citrix.com>
> CC: Paul Durrant <paul@xen.org>
> CC: Paul Burton <pburton@wavecomp.com>
> CC: Aleksandar Rikalo <arikalo@wavecomp.com>
> CC: Chris Wulff <crwulff@gmail.com>
> CC: Marek Vasut <marex@denx.de>
> CC: David Gibson <david@gibson.dropbear.id.au>
> CC: Cornelia Huck <cohuck@redhat.com>
> CC: Halil Pasic <pasic@linux.ibm.com>
> CC: Christian Borntraeger <borntraeger@de.ibm.com>
> CC: "Hervé Poussineau" <hpoussin@reactos.org>
> CC: Xiao Guangrong <xiaoguangrong.eric@gmail.com>
> CC: Aurelien Jarno <aurelien@aurel32.net>
> CC: Aleksandar Markovic <amarkovic@wavecomp.com>
> CC: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
> CC: Jason Wang <jasowang@redhat.com>
> CC: Laszlo Ersek <lersek@redhat.com>
> CC: Yuval Shaia <yuval.shaia@oracle.com>
> CC: Palmer Dabbelt <palmer@sifive.com>
> CC: Sagar Karandikar <sagark@eecs.berkeley.edu>
> CC: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
> CC: David Hildenbrand <david@redhat.com>
> CC: Thomas Huth <thuth@redhat.com>
> CC: Eric Farman <farman@linux.ibm.com>
> CC: Matthew Rosato <mjrosato@linux.ibm.com>
> CC: Hannes Reinecke <hare@suse.com>
> CC: Michael Walle <michael@walle.cc>
> CC: Artyom Tarasenko <atar4qemu@gmail.com>
> CC: Stefan Berger <stefanb@linux.ibm.com>
> CC: Samuel Thibault <samuel.thibault@ens-lyon.org>
> CC: Alex Williamson <alex.williamson@redhat.com>
> CC: Tony Krowiak <akrowiak@linux.ibm.com>
> CC: Pierre Morel <pmorel@linux.ibm.com>
> CC: Michael Roth <mdroth@linux.vnet.ibm.com>
> CC: Hailiang Zhang <zhang.zhanghailiang@huawei.com>
> CC: Juan Quintela <quintela@redhat.com>
> CC: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
> CC: Luigi Rizzo <rizzo@iet.unipi.it>
> CC: Giuseppe Lettieri <g.lettieri@iet.unipi.it>
> CC: Vincenzo Maffione <v.maffione@gmail.com>
> CC: Jan Kiszka <jan.kiszka@siemens.com>
> CC: Anthony Green <green@moxielogic.com>
> CC: Stafford Horne <shorne@gmail.com>
> CC: Guan Xuetao <gxt@mprc.pku.edu.cn>
> CC: Max Filippov <jcmvbkbc@gmail.com>
> CC: qemu-block@nongnu.org
> CC: integration@gluster.org
> CC: sheepdog@lists.wpkg.org
> CC: qemu-arm@nongnu.org
> CC: xen-devel@lists.xenproject.org
> CC: qemu-ppc@nongnu.org
> CC: qemu-s390x@nongnu.org
> CC: qemu-riscv@nongnu.org
>
>  include/qapi/error.h | 38 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
>
> diff --git a/include/qapi/error.h b/include/qapi/error.h
> index d6898d833b..47238d9065 100644
> --- a/include/qapi/error.h
> +++ b/include/qapi/error.h
> @@ -345,6 +345,44 @@ void error_set_internal(Error **errp,
>                          ErrorClass err_class, const char *fmt, ...)
>      GCC_FMT_ATTR(6, 7);
>  
> +typedef struct ErrorPropagator {
> +    Error *local_err;
> +    Error **errp;
> +} ErrorPropagator;
> +
> +static inline void error_propagator_cleanup(ErrorPropagator *prop)
> +{
> +    error_propagate(prop->errp, prop->local_err);
> +}
> +
> +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup);
> +
> +/*
> + * ERRP_AUTO_PROPAGATE
> + *
> + * This macro is created to be the first line of a function with Error **errp
> + * OUT parameter. It's needed only in cases where we want to use error_prepend,
> + * error_append_hint or dereference *errp. It's still safe (but useless) in
> + * other cases.
> + *
> + * If errp is NULL or points to error_fatal, it is rewritten to point to a
> + * local Error object, which will be automatically propagated to the original
> + * errp on function exit (see error_propagator_cleanup).
> + *
> + * After invocation of this macro it is always safe to dereference errp
> + * (as it's not NULL anymore) and to add information (by error_prepend or
> + * error_append_hint)
> + * (as, if it was error_fatal, we swapped it with a local_error to be
> + * propagated on cleanup).
> + *
> + * Note: we don't wrap the error_abort case, as we want resulting coredump
> + * to point to the place where the error happened, not to error_propagate.
> + */
> +#define ERRP_AUTO_PROPAGATE()                                  \
> +    g_auto(ErrorPropagator) _auto_errp_prop = {.errp = errp};  \
> +    errp = ((errp == NULL || *errp == error_fatal)             \
> +            ? &_auto_errp_prop.local_err : errp)
> +
>  /*
>   * Special error destination to abort on error.
>   * See error_setg() and error_propagate() for details.

Missing: update of the big comment starting with "Error reporting system
loosely patterned after Glib's GError."
Vladimir Sementsov-Ogievskiy Dec. 5, 2019, 9:38 a.m. UTC | #4
04.12.2019 17:59, Markus Armbruster wrote:
> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
> 
>> Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
>> functions with errp OUT parameter.
>>
>> It has three goals:
>>
>> 1. Fix issue with error_fatal & error_prepend/error_append_hint: user
>> can't see this additional information, because exit() happens in
>> error_setg earlier than information is added. [Reported by Greg Kurz]
>>
>> 2. Fix issue with error_abort & error_propagate: when we wrap
>> error_abort by local_err+error_propagate, resulting coredump will
>> refer to error_propagate and not to the place where error happened.
> 
> I get what you mean, but I have plenty of context.
> 
>> (the macro itself doesn't fix the issue, but it allows to [3.] drop all
>> local_err+error_propagate pattern, which will definitely fix the issue)
> 
> The parenthesis is not part of the goal.
> 
>> [Reported by Kevin Wolf]
>>
>> 3. Drop local_err+error_propagate pattern, which is used to workaround
>> void functions with errp parameter, when caller wants to know resulting
>> status. (Note: actually these functions could be merely updated to
>> return int error code).
>>
>> To achieve these goals, we need to add invocation of the macro at start
>> of functions, which needs error_prepend/error_append_hint (1.); add
>> invocation of the macro at start of functions which do
>> local_err+error_propagate scenario the check errors, drop local errors
>> from them and just use *errp instead (2., 3.).
> 
> The paragraph talks about two cases: 1. and 2.+3. 

Hmm, I don't think so.. 1. and 2. are issues. 3. is a refactoring.. We just
fix achieve 2 and 3 by one action.

> Makes me think we
> want two paragraphs, each illustrated with an example.
> 
> What about you provide the examples, and then I try to polish the prose?

1: error_fatal problem

Assume the following code flow:

int f1(errp) {
    ...
    ret = f2(errp);
    if (ret < 0) {
       error_append_hint(errp, "very useful hint");
       return ret;
    }
    ...
}

Now, if we call f1 with &error_fatal argument and f2 fails, the program
will exit immediately inside f2, when setting the errp. User will not
see the hint.

So, in this case we should use local_err.

2: error_abort problem

Now, consider functions without return value. We normally use local_err
variable to catch failures:

void f1(errp) {
    Error *local_err = NULL;
    ...
    f2(&local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }
    ...
}

Now, if we call f2 with &error_abort and f2 fails, the stack in resulting
crash dump will point to error_propagate, not to the failure point in f2,
which complicates debugging.

So, we should never wrap error_abort by local_err.

===

Our solution:

- Fixes [1.], adding invocation of new macro into functions with error_appen_hint/error_prepend,
   New macro will wrap error_fatal.
- Fixes [2.], by switching from hand-written local_err to smart macro, which never
   wraps error_abort.
- Handles [3.], by switching to macro, which is less code
- Additionally, macro doesn't wrap normal non-zero errp, to avoid extra propagations
   (in fact, error_propagate is called, but returns immediately on first if (!local_err))

> 
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> Reviewed-by: Eric Blake <eblake@redhat.com>
>> ---
>>
>> CC: Gerd Hoffmann <kraxel@redhat.com>
>> CC: "Gonglei (Arei)" <arei.gonglei@huawei.com>
>> CC: Eduardo Habkost <ehabkost@redhat.com>
>> CC: Igor Mammedov <imammedo@redhat.com>
>> CC: Laurent Vivier <lvivier@redhat.com>
>> CC: Amit Shah <amit@kernel.org>
>> CC: Kevin Wolf <kwolf@redhat.com>
>> CC: Max Reitz <mreitz@redhat.com>
>> CC: John Snow <jsnow@redhat.com>
>> CC: Ari Sundholm <ari@tuxera.com>
>> CC: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
>> CC: Paolo Bonzini <pbonzini@redhat.com>
>> CC: Stefan Hajnoczi <stefanha@redhat.com>
>> CC: Fam Zheng <fam@euphon.net>
>> CC: Stefan Weil <sw@weilnetz.de>
>> CC: Ronnie Sahlberg <ronniesahlberg@gmail.com>
>> CC: Peter Lieven <pl@kamp.de>
>> CC: Eric Blake <eblake@redhat.com>
>> CC: "Denis V. Lunev" <den@openvz.org>
>> CC: Markus Armbruster <armbru@redhat.com>
>> CC: Alberto Garcia <berto@igalia.com>
>> CC: Jason Dillaman <dillaman@redhat.com>
>> CC: Wen Congyang <wencongyang2@huawei.com>
>> CC: Xie Changlong <xiechanglong.d@gmail.com>
>> CC: Liu Yuan <namei.unix@gmail.com>
>> CC: "Richard W.M. Jones" <rjones@redhat.com>
>> CC: Jeff Cody <codyprime@gmail.com>
>> CC: "Marc-André Lureau" <marcandre.lureau@redhat.com>
>> CC: "Daniel P. Berrangé" <berrange@redhat.com>
>> CC: Richard Henderson <rth@twiddle.net>
>> CC: Greg Kurz <groug@kaod.org>
>> CC: "Michael S. Tsirkin" <mst@redhat.com>
>> CC: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
>> CC: Beniamino Galvani <b.galvani@gmail.com>
>> CC: Peter Maydell <peter.maydell@linaro.org>
>> CC: "Cédric Le Goater" <clg@kaod.org>
>> CC: Andrew Jeffery <andrew@aj.id.au>
>> CC: Joel Stanley <joel@jms.id.au>
>> CC: Andrew Baumann <Andrew.Baumann@microsoft.com>
>> CC: "Philippe Mathieu-Daudé" <philmd@redhat.com>
>> CC: Antony Pavlov <antonynpavlov@gmail.com>
>> CC: Jean-Christophe Dubois <jcd@tribudubois.net>
>> CC: Peter Chubb <peter.chubb@nicta.com.au>
>> CC: Subbaraya Sundeep <sundeep.lkml@gmail.com>
>> CC: Eric Auger <eric.auger@redhat.com>
>> CC: Alistair Francis <alistair@alistair23.me>
>> CC: "Edgar E. Iglesias" <edgar.iglesias@gmail.com>
>> CC: Stefano Stabellini <sstabellini@kernel.org>
>> CC: Anthony Perard <anthony.perard@citrix.com>
>> CC: Paul Durrant <paul@xen.org>
>> CC: Paul Burton <pburton@wavecomp.com>
>> CC: Aleksandar Rikalo <arikalo@wavecomp.com>
>> CC: Chris Wulff <crwulff@gmail.com>
>> CC: Marek Vasut <marex@denx.de>
>> CC: David Gibson <david@gibson.dropbear.id.au>
>> CC: Cornelia Huck <cohuck@redhat.com>
>> CC: Halil Pasic <pasic@linux.ibm.com>
>> CC: Christian Borntraeger <borntraeger@de.ibm.com>
>> CC: "Hervé Poussineau" <hpoussin@reactos.org>
>> CC: Xiao Guangrong <xiaoguangrong.eric@gmail.com>
>> CC: Aurelien Jarno <aurelien@aurel32.net>
>> CC: Aleksandar Markovic <amarkovic@wavecomp.com>
>> CC: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
>> CC: Jason Wang <jasowang@redhat.com>
>> CC: Laszlo Ersek <lersek@redhat.com>
>> CC: Yuval Shaia <yuval.shaia@oracle.com>
>> CC: Palmer Dabbelt <palmer@sifive.com>
>> CC: Sagar Karandikar <sagark@eecs.berkeley.edu>
>> CC: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
>> CC: David Hildenbrand <david@redhat.com>
>> CC: Thomas Huth <thuth@redhat.com>
>> CC: Eric Farman <farman@linux.ibm.com>
>> CC: Matthew Rosato <mjrosato@linux.ibm.com>
>> CC: Hannes Reinecke <hare@suse.com>
>> CC: Michael Walle <michael@walle.cc>
>> CC: Artyom Tarasenko <atar4qemu@gmail.com>
>> CC: Stefan Berger <stefanb@linux.ibm.com>
>> CC: Samuel Thibault <samuel.thibault@ens-lyon.org>
>> CC: Alex Williamson <alex.williamson@redhat.com>
>> CC: Tony Krowiak <akrowiak@linux.ibm.com>
>> CC: Pierre Morel <pmorel@linux.ibm.com>
>> CC: Michael Roth <mdroth@linux.vnet.ibm.com>
>> CC: Hailiang Zhang <zhang.zhanghailiang@huawei.com>
>> CC: Juan Quintela <quintela@redhat.com>
>> CC: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
>> CC: Luigi Rizzo <rizzo@iet.unipi.it>
>> CC: Giuseppe Lettieri <g.lettieri@iet.unipi.it>
>> CC: Vincenzo Maffione <v.maffione@gmail.com>
>> CC: Jan Kiszka <jan.kiszka@siemens.com>
>> CC: Anthony Green <green@moxielogic.com>
>> CC: Stafford Horne <shorne@gmail.com>
>> CC: Guan Xuetao <gxt@mprc.pku.edu.cn>
>> CC: Max Filippov <jcmvbkbc@gmail.com>
>> CC: qemu-block@nongnu.org
>> CC: integration@gluster.org
>> CC: sheepdog@lists.wpkg.org
>> CC: qemu-arm@nongnu.org
>> CC: xen-devel@lists.xenproject.org
>> CC: qemu-ppc@nongnu.org
>> CC: qemu-s390x@nongnu.org
>> CC: qemu-riscv@nongnu.org
>>
>>   include/qapi/error.h | 38 ++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 38 insertions(+)
>>
>> diff --git a/include/qapi/error.h b/include/qapi/error.h
>> index d6898d833b..47238d9065 100644
>> --- a/include/qapi/error.h
>> +++ b/include/qapi/error.h
>> @@ -345,6 +345,44 @@ void error_set_internal(Error **errp,
>>                           ErrorClass err_class, const char *fmt, ...)
>>       GCC_FMT_ATTR(6, 7);
>>   
>> +typedef struct ErrorPropagator {
>> +    Error *local_err;
>> +    Error **errp;
>> +} ErrorPropagator;
>> +
>> +static inline void error_propagator_cleanup(ErrorPropagator *prop)
>> +{
>> +    error_propagate(prop->errp, prop->local_err);
>> +}
>> +
>> +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup);
>> +
>> +/*
>> + * ERRP_AUTO_PROPAGATE
>> + *
>> + * This macro is created to be the first line of a function with Error **errp
>> + * OUT parameter. It's needed only in cases where we want to use error_prepend,
>> + * error_append_hint or dereference *errp. It's still safe (but useless) in
>> + * other cases.
>> + *
>> + * If errp is NULL or points to error_fatal, it is rewritten to point to a
>> + * local Error object, which will be automatically propagated to the original
>> + * errp on function exit (see error_propagator_cleanup).
>> + *
>> + * After invocation of this macro it is always safe to dereference errp
>> + * (as it's not NULL anymore) and to add information (by error_prepend or
>> + * error_append_hint)
>> + * (as, if it was error_fatal, we swapped it with a local_error to be
>> + * propagated on cleanup).
>> + *
>> + * Note: we don't wrap the error_abort case, as we want resulting coredump
>> + * to point to the place where the error happened, not to error_propagate.
>> + */
>> +#define ERRP_AUTO_PROPAGATE()                                  \
>> +    g_auto(ErrorPropagator) _auto_errp_prop = {.errp = errp};  \
>> +    errp = ((errp == NULL || *errp == error_fatal)             \
>> +            ? &_auto_errp_prop.local_err : errp)
>> +
>>   /*
>>    * Special error destination to abort on error.
>>    * See error_setg() and error_propagate() for details.
> 
> Missing: update of the big comment starting with "Error reporting system
> loosely patterned after Glib's GError."
>
Markus Armbruster Dec. 5, 2019, 12:36 p.m. UTC | #5
Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:

> 04.12.2019 17:59, Markus Armbruster wrote:
>> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
>> 
>>> Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
>>> functions with errp OUT parameter.
>>>
>>> It has three goals:
>>>
>>> 1. Fix issue with error_fatal & error_prepend/error_append_hint: user
>>> can't see this additional information, because exit() happens in
>>> error_setg earlier than information is added. [Reported by Greg Kurz]
>>>
>>> 2. Fix issue with error_abort & error_propagate: when we wrap
>>> error_abort by local_err+error_propagate, resulting coredump will
>>> refer to error_propagate and not to the place where error happened.
>> 
>> I get what you mean, but I have plenty of context.
>> 
>>> (the macro itself doesn't fix the issue, but it allows to [3.] drop all
>>> local_err+error_propagate pattern, which will definitely fix the issue)
>> 
>> The parenthesis is not part of the goal.
>> 
>>> [Reported by Kevin Wolf]
>>>
>>> 3. Drop local_err+error_propagate pattern, which is used to workaround
>>> void functions with errp parameter, when caller wants to know resulting
>>> status. (Note: actually these functions could be merely updated to
>>> return int error code).
>>>
>>> To achieve these goals, we need to add invocation of the macro at start
>>> of functions, which needs error_prepend/error_append_hint (1.); add
>>> invocation of the macro at start of functions which do
>>> local_err+error_propagate scenario the check errors, drop local errors
>>> from them and just use *errp instead (2., 3.).
>> 
>> The paragraph talks about two cases: 1. and 2.+3. 
>
> Hmm, I don't think so.. 1. and 2. are issues. 3. is a refactoring.. We just
> fix achieve 2 and 3 by one action.
>
>> Makes me think we
>> want two paragraphs, each illustrated with an example.
>> 
>> What about you provide the examples, and then I try to polish the prose?
>
> 1: error_fatal problem
>
> Assume the following code flow:
>
> int f1(errp) {
>     ...
>     ret = f2(errp);
>     if (ret < 0) {
>        error_append_hint(errp, "very useful hint");
>        return ret;
>     }
>     ...
> }
>
> Now, if we call f1 with &error_fatal argument and f2 fails, the program
> will exit immediately inside f2, when setting the errp. User will not
> see the hint.
>
> So, in this case we should use local_err.

How does this example look after the transformation?

> 2: error_abort problem
>
> Now, consider functions without return value. We normally use local_err
> variable to catch failures:
>
> void f1(errp) {
>     Error *local_err = NULL;
>     ...
>     f2(&local_err);
>     if (local_err) {
>         error_propagate(errp, local_err);
>         return;
>     }
>     ...
> }
>
> Now, if we call f2 with &error_abort and f2 fails, the stack in resulting
> crash dump will point to error_propagate, not to the failure point in f2,
> which complicates debugging.
>
> So, we should never wrap error_abort by local_err.

Likewise.

>
> ===
>
> Our solution:
>
> - Fixes [1.], adding invocation of new macro into functions with error_appen_hint/error_prepend,
>    New macro will wrap error_fatal.
> - Fixes [2.], by switching from hand-written local_err to smart macro, which never
>    wraps error_abort.
> - Handles [3.], by switching to macro, which is less code
> - Additionally, macro doesn't wrap normal non-zero errp, to avoid extra propagations
>    (in fact, error_propagate is called, but returns immediately on first if (!local_err))
Vladimir Sementsov-Ogievskiy Dec. 5, 2019, 2:58 p.m. UTC | #6
05.12.2019 15:36, Markus Armbruster wrote:
> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
> 
>> 04.12.2019 17:59, Markus Armbruster wrote:
>>> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
>>>
>>>> Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
>>>> functions with errp OUT parameter.
>>>>
>>>> It has three goals:
>>>>
>>>> 1. Fix issue with error_fatal & error_prepend/error_append_hint: user
>>>> can't see this additional information, because exit() happens in
>>>> error_setg earlier than information is added. [Reported by Greg Kurz]
>>>>
>>>> 2. Fix issue with error_abort & error_propagate: when we wrap
>>>> error_abort by local_err+error_propagate, resulting coredump will
>>>> refer to error_propagate and not to the place where error happened.
>>>
>>> I get what you mean, but I have plenty of context.
>>>
>>>> (the macro itself doesn't fix the issue, but it allows to [3.] drop all
>>>> local_err+error_propagate pattern, which will definitely fix the issue)
>>>
>>> The parenthesis is not part of the goal.
>>>
>>>> [Reported by Kevin Wolf]
>>>>
>>>> 3. Drop local_err+error_propagate pattern, which is used to workaround
>>>> void functions with errp parameter, when caller wants to know resulting
>>>> status. (Note: actually these functions could be merely updated to
>>>> return int error code).
>>>>
>>>> To achieve these goals, we need to add invocation of the macro at start
>>>> of functions, which needs error_prepend/error_append_hint (1.); add
>>>> invocation of the macro at start of functions which do
>>>> local_err+error_propagate scenario the check errors, drop local errors
>>>> from them and just use *errp instead (2., 3.).
>>>
>>> The paragraph talks about two cases: 1. and 2.+3.
>>
>> Hmm, I don't think so.. 1. and 2. are issues. 3. is a refactoring.. We just
>> fix achieve 2 and 3 by one action.
>>
>>> Makes me think we
>>> want two paragraphs, each illustrated with an example.
>>>
>>> What about you provide the examples, and then I try to polish the prose?
>>
>> 1: error_fatal problem
>>
>> Assume the following code flow:
>>
>> int f1(errp) {
>>      ...
>>      ret = f2(errp);
>>      if (ret < 0) {
>>         error_append_hint(errp, "very useful hint");
>>         return ret;
>>      }
>>      ...
>> }
>>
>> Now, if we call f1 with &error_fatal argument and f2 fails, the program
>> will exit immediately inside f2, when setting the errp. User will not
>> see the hint.
>>
>> So, in this case we should use local_err.
> 
> How does this example look after the transformation?

Good point.

int f1(errp) {
    ERRP_AUTO_PROPAGATE();
    ...
    ret = f2(errp);
    if (ret < 0) {
       error_append_hint(errp, "very useful hint");
       return ret;
    }
    ...
}

- nothing changed, only add macro at start. But now errp is safe, if it was
error_fatal it is wrapped by local error, and will only call exit on automatic
propagation on f1 finish.

> 
>> 2: error_abort problem
>>
>> Now, consider functions without return value. We normally use local_err
>> variable to catch failures:
>>
>> void f1(errp) {
>>      Error *local_err = NULL;
>>      ...
>>      f2(&local_err);
>>      if (local_err) {
>>          error_propagate(errp, local_err);
>>          return;
>>      }
>>      ...
>> }
>>
>> Now, if we call f2 with &error_abort and f2 fails, the stack in resulting
>> crash dump will point to error_propagate, not to the failure point in f2,
>> which complicates debugging.
>>
>> So, we should never wrap error_abort by local_err.
> 
> Likewise.

And here:

void f1(errp) {
     ERRP_AUTO_PROPAGATE();
     ...
     f2(errp);
     if (*errp) {
         return;
     }
     ...

- if errp was NULL, it is wrapped, so dereferencing errp is safe. On return,
   local error is automatically propagated to original one.

> 
>>
>> ===
>>
>> Our solution:
>>
>> - Fixes [1.], adding invocation of new macro into functions with error_appen_hint/error_prepend,
>>     New macro will wrap error_fatal.
>> - Fixes [2.], by switching from hand-written local_err to smart macro, which never
>>     wraps error_abort.
>> - Handles [3.], by switching to macro, which is less code
>> - Additionally, macro doesn't wrap normal non-zero errp, to avoid extra propagations
>>     (in fact, error_propagate is called, but returns immediately on first if (!local_err))
>
Vladimir Sementsov-Ogievskiy Dec. 5, 2019, 4:36 p.m. UTC | #7
05.12.2019 17:58, Vladimir Sementsov-Ogievskiy wrote:
> 05.12.2019 15:36, Markus Armbruster wrote:
>> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
>>
>>> 04.12.2019 17:59, Markus Armbruster wrote:
>>>> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
>>>>
>>>>> Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
>>>>> functions with errp OUT parameter.
>>>>>
>>>>> It has three goals:
>>>>>
>>>>> 1. Fix issue with error_fatal & error_prepend/error_append_hint: user
>>>>> can't see this additional information, because exit() happens in
>>>>> error_setg earlier than information is added. [Reported by Greg Kurz]
>>>>>
>>>>> 2. Fix issue with error_abort & error_propagate: when we wrap
>>>>> error_abort by local_err+error_propagate, resulting coredump will
>>>>> refer to error_propagate and not to the place where error happened.
>>>>
>>>> I get what you mean, but I have plenty of context.
>>>>
>>>>> (the macro itself doesn't fix the issue, but it allows to [3.] drop all
>>>>> local_err+error_propagate pattern, which will definitely fix the issue)
>>>>
>>>> The parenthesis is not part of the goal.
>>>>
>>>>> [Reported by Kevin Wolf]
>>>>>
>>>>> 3. Drop local_err+error_propagate pattern, which is used to workaround
>>>>> void functions with errp parameter, when caller wants to know resulting
>>>>> status. (Note: actually these functions could be merely updated to
>>>>> return int error code).
>>>>>
>>>>> To achieve these goals, we need to add invocation of the macro at start
>>>>> of functions, which needs error_prepend/error_append_hint (1.); add
>>>>> invocation of the macro at start of functions which do
>>>>> local_err+error_propagate scenario the check errors, drop local errors
>>>>> from them and just use *errp instead (2., 3.).
>>>>
>>>> The paragraph talks about two cases: 1. and 2.+3.
>>>
>>> Hmm, I don't think so.. 1. and 2. are issues. 3. is a refactoring.. We just
>>> fix achieve 2 and 3 by one action.
>>>
>>>> Makes me think we
>>>> want two paragraphs, each illustrated with an example.
>>>>
>>>> What about you provide the examples, and then I try to polish the prose?
>>>
>>> 1: error_fatal problem
>>>
>>> Assume the following code flow:
>>>
>>> int f1(errp) {
>>>       ...
>>>       ret = f2(errp);
>>>       if (ret < 0) {
>>>          error_append_hint(errp, "very useful hint");
>>>          return ret;
>>>       }
>>>       ...
>>> }
>>>
>>> Now, if we call f1 with &error_fatal argument and f2 fails, the program
>>> will exit immediately inside f2, when setting the errp. User will not
>>> see the hint.
>>>
>>> So, in this case we should use local_err.
>>
>> How does this example look after the transformation?
> 
> Good point.
> 
> int f1(errp) {
>      ERRP_AUTO_PROPAGATE();
>      ...
>      ret = f2(errp);
>      if (ret < 0) {
>         error_append_hint(errp, "very useful hint");
>         return ret;
>      }
>      ...
> }
> 
> - nothing changed, only add macro at start. But now errp is safe, if it was
> error_fatal it is wrapped by local error, and will only call exit on automatic
> propagation on f1 finish.
> 
>>
>>> 2: error_abort problem
>>>
>>> Now, consider functions without return value. We normally use local_err
>>> variable to catch failures:
>>>
>>> void f1(errp) {
>>>       Error *local_err = NULL;
>>>       ...
>>>       f2(&local_err);
>>>       if (local_err) {
>>>           error_propagate(errp, local_err);
>>>           return;
>>>       }
>>>       ...
>>> }
>>>
>>> Now, if we call f2 with &error_abort and f2 fails, the stack in resulting
>>> crash dump will point to error_propagate, not to the failure point in f2,
>>> which complicates debugging.
>>>
>>> So, we should never wrap error_abort by local_err.
>>
>> Likewise.
> 
> And here:
> 
> void f1(errp) {
>       ERRP_AUTO_PROPAGATE();
>       ...
>       f2(errp);
>       if (*errp) {
>           return;
>       }
>       ...
> 
> - if errp was NULL, it is wrapped, so dereferencing errp is safe. On return,
>     local error is automatically propagated to original one.

and if it was error_abort, it is not wrapped, so will crash where failed.

> 
>>
>>>
>>> ===
>>>
>>> Our solution:
>>>
>>> - Fixes [1.], adding invocation of new macro into functions with error_appen_hint/error_prepend,
>>>      New macro will wrap error_fatal.
>>> - Fixes [2.], by switching from hand-written local_err to smart macro, which never
>>>      wraps error_abort.
>>> - Handles [3.], by switching to macro, which is less code
>>> - Additionally, macro doesn't wrap normal non-zero errp, to avoid extra propagations
>>>      (in fact, error_propagate is called, but returns immediately on first if (!local_err))
>>
> 
>
Eric Blake Dec. 5, 2019, 5:32 p.m. UTC | #8
On 12/5/19 8:58 AM, Vladimir Sementsov-Ogievskiy wrote:

>>>> What about you provide the examples, and then I try to polish the prose?
>>>
>>> 1: error_fatal problem
>>>
>>> Assume the following code flow:
>>>
>>> int f1(errp) {
>>>       ...
>>>       ret = f2(errp);
>>>       if (ret < 0) {
>>>          error_append_hint(errp, "very useful hint");
>>>          return ret;
>>>       }
>>>       ...
>>> }
>>>
>>> Now, if we call f1 with &error_fatal argument and f2 fails, the program
>>> will exit immediately inside f2, when setting the errp. User will not
>>> see the hint.
>>>
>>> So, in this case we should use local_err.
>>
>> How does this example look after the transformation?

Without ERRP_AUTO_PROPAGATE(), the transformation is a lot of boilerplate:

int f1(errp) {
     Error *err = NULL;
     ret = f2(&err);
     if (ret < 0) {
         error_append_hint(&err, "very useful hint");
         error_propagate(errp, err);
         return ret;
     }
}

what's worse, that boilerplate to solve problem 1 turns out to be...

> 
> Good point.
> 
> int f1(errp) {
>      ERRP_AUTO_PROPAGATE();
>      ...
>      ret = f2(errp);
>      if (ret < 0) {
>         error_append_hint(errp, "very useful hint");
>         return ret;
>      }
>      ...
> }
> 
> - nothing changed, only add macro at start. But now errp is safe, if it was
> error_fatal it is wrapped by local error, and will only call exit on automatic
> propagation on f1 finish.
> 
>>
>>> 2: error_abort problem
>>>
>>> Now, consider functions without return value. We normally use local_err
>>> variable to catch failures:
>>>
>>> void f1(errp) {
>>>       Error *local_err = NULL;
>>>       ...
>>>       f2(&local_err);
>>>       if (local_err) {
>>>           error_propagate(errp, local_err);
>>>           return;
>>>       }
>>>       ...
>>> }

the very same code as the cause of problem 2.

>>>
>>> Now, if we call f2 with &error_abort and f2 fails, the stack in resulting
>>> crash dump will point to error_propagate, not to the failure point in f2,
>>> which complicates debugging.
>>>
>>> So, we should never wrap error_abort by local_err.
>>
>> Likewise.
> 
> And here:
> 
> void f1(errp) {
>       ERRP_AUTO_PROPAGATE();
>       ...
>       f2(errp);
>       if (*errp) {
>           return;
>       }
>       ...
> 
> - if errp was NULL, it is wrapped, so dereferencing errp is safe. On return,
>     local error is automatically propagated to original one.

So, the use of ERRP_AUTO_PROPAGATE() solves BOTH problems 1 and 2 - we 
avoid the boilerplate that trades one problem for another, by 
consolidating ALL of the boilerplate into a single-line macro, such that 
error_propagate() no longer needs to be called anywhere except inside 
the ERRP_AUTO_PROPAGATE macro.

> 
>>
>>>
>>> ===
>>>
>>> Our solution:
>>>
>>> - Fixes [1.], adding invocation of new macro into functions with error_appen_hint/error_prepend,
>>>      New macro will wrap error_fatal.
>>> - Fixes [2.], by switching from hand-written local_err to smart macro, which never
>>>      wraps error_abort.
>>> - Handles [3.], by switching to macro, which is less code
>>> - Additionally, macro doesn't wrap normal non-zero errp, to avoid extra propagations
>>>      (in fact, error_propagate is called, but returns immediately on first if (!local_err))
>>
> 
>
Markus Armbruster Dec. 6, 2019, 8:13 a.m. UTC | #9
Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:

> 05.12.2019 17:58, Vladimir Sementsov-Ogievskiy wrote:
>> 05.12.2019 15:36, Markus Armbruster wrote:
>>> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
>>>
>>>> 04.12.2019 17:59, Markus Armbruster wrote:
>>>>> Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> writes:
>>>>>
>>>>>> Here is introduced ERRP_AUTO_PROPAGATE macro, to be used at start of
>>>>>> functions with errp OUT parameter.
>>>>>>
>>>>>> It has three goals:
>>>>>>
>>>>>> 1. Fix issue with error_fatal & error_prepend/error_append_hint: user
>>>>>> can't see this additional information, because exit() happens in
>>>>>> error_setg earlier than information is added. [Reported by Greg Kurz]
>>>>>>
>>>>>> 2. Fix issue with error_abort & error_propagate: when we wrap
>>>>>> error_abort by local_err+error_propagate, resulting coredump will
>>>>>> refer to error_propagate and not to the place where error happened.
>>>>>
>>>>> I get what you mean, but I have plenty of context.
>>>>>
>>>>>> (the macro itself doesn't fix the issue, but it allows to [3.] drop all
>>>>>> local_err+error_propagate pattern, which will definitely fix the issue)
>>>>>
>>>>> The parenthesis is not part of the goal.
>>>>>
>>>>>> [Reported by Kevin Wolf]
>>>>>>
>>>>>> 3. Drop local_err+error_propagate pattern, which is used to workaround
>>>>>> void functions with errp parameter, when caller wants to know resulting
>>>>>> status. (Note: actually these functions could be merely updated to
>>>>>> return int error code).
>>>>>>
>>>>>> To achieve these goals, we need to add invocation of the macro at start
>>>>>> of functions, which needs error_prepend/error_append_hint (1.); add
>>>>>> invocation of the macro at start of functions which do
>>>>>> local_err+error_propagate scenario the check errors, drop local errors
>>>>>> from them and just use *errp instead (2., 3.).
>>>>>
>>>>> The paragraph talks about two cases: 1. and 2.+3.
>>>>
>>>> Hmm, I don't think so.. 1. and 2. are issues. 3. is a refactoring.. We just
>>>> fix achieve 2 and 3 by one action.
>>>>
>>>>> Makes me think we
>>>>> want two paragraphs, each illustrated with an example.
>>>>>
>>>>> What about you provide the examples, and then I try to polish the prose?
>>>>
>>>> 1: error_fatal problem
>>>>
>>>> Assume the following code flow:
>>>>
>>>> int f1(errp) {
>>>>       ...
>>>>       ret = f2(errp);
>>>>       if (ret < 0) {
>>>>          error_append_hint(errp, "very useful hint");
>>>>          return ret;
>>>>       }
>>>>       ...
>>>> }
>>>>
>>>> Now, if we call f1 with &error_fatal argument and f2 fails, the program
>>>> will exit immediately inside f2, when setting the errp. User will not
>>>> see the hint.
>>>>
>>>> So, in this case we should use local_err.
>>>
>>> How does this example look after the transformation?
>> 
>> Good point.
>> 
>> int f1(errp) {
>>      ERRP_AUTO_PROPAGATE();
>>      ...
>>      ret = f2(errp);
>>      if (ret < 0) {
>>         error_append_hint(errp, "very useful hint");
>>         return ret;
>>      }
>>      ...
>> }
>> 
>> - nothing changed, only add macro at start. But now errp is safe, if it was
>> error_fatal it is wrapped by local error, and will only call exit on automatic
>> propagation on f1 finish.
>> 
>>>
>>>> 2: error_abort problem
>>>>
>>>> Now, consider functions without return value. We normally use local_err
>>>> variable to catch failures:
>>>>
>>>> void f1(errp) {
>>>>       Error *local_err = NULL;
>>>>       ...
>>>>       f2(&local_err);
>>>>       if (local_err) {
>>>>           error_propagate(errp, local_err);
>>>>           return;
>>>>       }
>>>>       ...
>>>> }
>>>>
>>>> Now, if we call f2 with &error_abort and f2 fails, the stack in resulting
>>>> crash dump will point to error_propagate, not to the failure point in f2,
>>>> which complicates debugging.
>>>>
>>>> So, we should never wrap error_abort by local_err.
>>>
>>> Likewise.
>> 
>> And here:
>> 
>> void f1(errp) {
>>       ERRP_AUTO_PROPAGATE();
>>       ...
>>       f2(errp);
>>       if (*errp) {
>>           return;
>>       }
>>       ...
>> 
>> - if errp was NULL, it is wrapped, so dereferencing errp is safe. On return,
>>     local error is automatically propagated to original one.
>
> and if it was error_abort, it is not wrapped, so will crash where failed.

We still need to avoid passing on unwrapped @errp when we want to ignore
some errors, as in fit_load_fdt(), but that should be quite rare.
Doesn't invalidate your approach.

[...]
diff mbox series

Patch

diff --git a/include/qapi/error.h b/include/qapi/error.h
index d6898d833b..47238d9065 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -345,6 +345,44 @@  void error_set_internal(Error **errp,
                         ErrorClass err_class, const char *fmt, ...)
     GCC_FMT_ATTR(6, 7);
 
+typedef struct ErrorPropagator {
+    Error *local_err;
+    Error **errp;
+} ErrorPropagator;
+
+static inline void error_propagator_cleanup(ErrorPropagator *prop)
+{
+    error_propagate(prop->errp, prop->local_err);
+}
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup);
+
+/*
+ * ERRP_AUTO_PROPAGATE
+ *
+ * This macro is created to be the first line of a function with Error **errp
+ * OUT parameter. It's needed only in cases where we want to use error_prepend,
+ * error_append_hint or dereference *errp. It's still safe (but useless) in
+ * other cases.
+ *
+ * If errp is NULL or points to error_fatal, it is rewritten to point to a
+ * local Error object, which will be automatically propagated to the original
+ * errp on function exit (see error_propagator_cleanup).
+ *
+ * After invocation of this macro it is always safe to dereference errp
+ * (as it's not NULL anymore) and to add information (by error_prepend or
+ * error_append_hint)
+ * (as, if it was error_fatal, we swapped it with a local_error to be
+ * propagated on cleanup).
+ *
+ * Note: we don't wrap the error_abort case, as we want resulting coredump
+ * to point to the place where the error happened, not to error_propagate.
+ */
+#define ERRP_AUTO_PROPAGATE()                                  \
+    g_auto(ErrorPropagator) _auto_errp_prop = {.errp = errp};  \
+    errp = ((errp == NULL || *errp == error_fatal)             \
+            ? &_auto_errp_prop.local_err : errp)
+
 /*
  * Special error destination to abort on error.
  * See error_setg() and error_propagate() for details.