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