On 14.03.2020 00:54, Markus Armbruster wrote:
Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> writes:
13.03.2020 18:42, Markus Armbruster wrote:
Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> writes:
12.03.2020 19:36, Markus Armbruster wrote:
I may have a second look tomorrow with fresher eyes, but let's get this
out now as is.
Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> writes:
Script adds ERRP_AUTO_PROPAGATE macro invocation where appropriate and
does corresponding changes in code (look for details in
include/qapi/error.h)
Usage example:
spatch --sp-file scripts/coccinelle/auto-propagated-errp.cocci \
--macro-file scripts/cocci-macro-file.h --in-place --no-show-diff \
--max-width 80 FILES...
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com>
---
Cc: Eric Blake <ebl...@redhat.com>
Cc: Kevin Wolf <kw...@redhat.com>
Cc: Max Reitz <mre...@redhat.com>
Cc: Greg Kurz <gr...@kaod.org>
Cc: Christian Schoenebeck <qemu_...@crudebyte.com>
Cc: Stefano Stabellini <sstabell...@kernel.org>
Cc: Anthony Perard <anthony.per...@citrix.com>
Cc: Paul Durrant <p...@xen.org>
Cc: Stefan Hajnoczi <stefa...@redhat.com>
Cc: "Philippe Mathieu-Daudé" <phi...@redhat.com>
Cc: Laszlo Ersek <ler...@redhat.com>
Cc: Gerd Hoffmann <kra...@redhat.com>
Cc: Stefan Berger <stef...@linux.ibm.com>
Cc: Markus Armbruster <arm...@redhat.com>
Cc: Michael Roth <mdr...@linux.vnet.ibm.com>
Cc: qemu-devel@nongnu.org
Cc: qemu-bl...@nongnu.org
Cc: xen-de...@lists.xenproject.org
scripts/coccinelle/auto-propagated-errp.cocci | 327 ++++++++++++++++++
include/qapi/error.h | 3 +
MAINTAINERS | 1 +
3 files changed, 331 insertions(+)
create mode 100644 scripts/coccinelle/auto-propagated-errp.cocci
diff --git a/scripts/coccinelle/auto-propagated-errp.cocci
b/scripts/coccinelle/auto-propagated-errp.cocci
new file mode 100644
index 0000000000..7dac2dcfa4
--- /dev/null
+++ b/scripts/coccinelle/auto-propagated-errp.cocci
@@ -0,0 +1,327 @@
+// Use ERRP_AUTO_PROPAGATE (see include/qapi/error.h)
+//
+// Copyright (c) 2020 Virtuozzo International GmbH.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see
+// <http://www.gnu.org/licenses/>.
+//
+// Usage example:
+// spatch --sp-file scripts/coccinelle/auto-propagated-errp.cocci \
+// --macro-file scripts/cocci-macro-file.h --in-place \
+// --no-show-diff --max-width 80 FILES...
+//
+// Note: --max-width 80 is needed because coccinelle default is less
+// than 80, and without this parameter coccinelle may reindent some
+// lines which fit into 80 characters but not to coccinelle default,
+// which in turn produces extra patch hunks for no reason.
This is about unwanted reformatting of parameter lists due to the ___
chaining hack. --max-width 80 makes that less likely, but not
impossible.
We can search for unwanted reformatting of parameter lists. I think
grepping diffs for '^\+.*Error \*\*' should do the trick. For the whole
tree, I get one false positive (not a parameter list), and one hit:
@@ -388,8 +388,10 @@ static void object_post_init_with_type(O
}
}
-void object_apply_global_props(Object *obj, const GPtrArray *props,
Error **errp)
+void object_apply_global_props(Object *obj, const GPtrArray *props,
+ Error **errp)
{
+ ERRP_AUTO_PROPAGATE();
int i;
if (!props) {
Reformatting, but not unwanted.
Yes, I saw it. This line is 81 character length, so it's OK to fix it in one
hunk with
ERRP_AUTO_PROPAGATE addition even for non-automatic patch.
Agree.
The --max-width 80 hack is good enough for me.
It does result in slightly long transformed lines, e.g. this one in
replication.c:
@@ -113,7 +113,7 @@ static int replication_open(BlockDriverS
s->mode = REPLICATION_MODE_PRIMARY;
top_id = qemu_opt_get(opts, REPLICATION_TOP_ID);
if (top_id) {
- error_setg(&local_err, "The primary side does not support option
top-id");
+ error_setg(errp, "The primary side does not support option
top-id");
goto fail;
}
} else if (!strcmp(mode, "secondary")) {
v8 did break this line (that's how I found it). However, v9 still
shortens the line, just not below the target. All your + lines look
quite unlikely to lengthen lines. Let's not worry about this.
+// Switch unusual Error ** parameter names to errp
+// (this is necessary to use ERRP_AUTO_PROPAGATE).
+//
+// Disable optional_qualifier to skip functions with
+// "Error *const *errp" parameter.
+//
+// Skip functions with "assert(_errp && *_errp)" statement, because
+// that signals unusual semantics, and the parameter name may well
+// serve a purpose. (like nbd_iter_channel_error()).
+//
+// Skip util/error.c to not touch, for example, error_propagate() and
+// error_propagate_prepend().
+@ depends on !(file in "util/error.c") disable optional_qualifier@
+identifier fn;
+identifier _errp != errp;
+@@
+
+ fn(...,
+- Error **_errp
++ Error **errp
+ ,...)
+ {
+(
+ ... when != assert(_errp && *_errp)
+&
+ <...
+- _errp
++ errp
+ ...>
+)
+ }
+
+// Add invocation of ERRP_AUTO_PROPAGATE to errp-functions where
+// necessary
+//
+// Note, that without "when any" the final "..." does not mach
+// something matched by previous pattern, i.e. the rule will not match
+// double error_prepend in control flow like in
+// vfio_set_irq_signaling().
+//
+// Note, "exists" says that we want apply rule even if it matches not
+// on all possible control flows (otherwise, it will not match
+// standard pattern when error_propagate() call is in if branch).
+@ disable optional_qualifier exists@
+identifier fn, local_err;
+symbol errp;
+@@
+
+ fn(..., Error **errp, ...)
+ {
++ ERRP_AUTO_PROPAGATE();
+ ... when != ERRP_AUTO_PROPAGATE();
+(
+(
+ error_append_hint(errp, ...);
+|
+ error_prepend(errp, ...);
+|
+ error_vprepend(errp, ...);
+)
+ ... when any
+|
+ Error *local_err = NULL;
+ ...
+(
+ error_propagate_prepend(errp, local_err, ...);
+|
+ error_propagate(errp, local_err);
+)
+ ...
+)
+ }
+
+
+// Match functions with propagation of local error to errp.
+// We want to refer these functions in several following rules, but I
+// don't know a proper way to inherit a function, not just its name
+// (to not match another functions with same name in following rules).
+// Not-proper way is as follows: rename errp parameter in functions
+// header and match it in following rules. Rename it back after all
+// transformations.
+//
+// The simplest case of propagation scheme is single definition of
+// local_err with at most one error_propagate_prepend or
+// error_propagate on each control-flow. Still, we want to match more
+// complex schemes too. We'll warn them with help of further rules.
I think what we actually want is to examine instances of this pattern to
figure out whether and how we want to transform them. Perhaps:
// The common case is a single definition of local_err with at most one
// error_propagate_prepend() or error_propagate() on each control-flow
// path. Instances of this case we convert with this script. Functions
For me, sounds a bit like "other things we don't convert".
Actually we convert other things too.
What other patterns of error propagation do we convert?
Something like in xen_block_device_destroy, why not? Otherwise, it's better to
avoid
matching things like xen_block_device_destroy, not just warn them.
But I'd prefer to proceed now as is to fit into 5.0.. Too much time already
spent on this. So, I'm OK with your wording too.
Let's scratch "Instances of this case we convert with this script."
OK
// with multiple definitions or propagates we want to examine
// manually. Later rules emit warnings to guide us to them.
+@rule1 disable optional_qualifier exists@
+identifier fn, local_err;
+symbol errp;
+@@
+
+ fn(..., Error **
+- errp
++ ____
+ , ...)
+ {
+ ...
+ Error *local_err = NULL;
+ ...
+(
+ error_propagate_prepend(errp, local_err, ...);
+|
+ error_propagate(errp, local_err);
+)
+ ...
+ }
+
+
+// Warn several Error * definitions.
+@check1 disable optional_qualifier exists@
+identifier fn = rule1.fn, local_err, local_err2;
Elsewhere, you use just rule.fn instead of fn = rule1.fn. Any
particular reason for the difference?
I didn't find other way to ref check1.fn in next python rule. It just don't
work if I write here just rule1.fn.
With the ___ chaining hack, I doubt we still need "= rule1.fn" or
"rule1.fn". If I replace "fn = rule1.fn" and "rule.fn" by just "fn"
everywhere, then apply the script to the complete tree, I get the same
result.
I think, it's more efficient to reuse names from previous rules. I think it
should
work faster (more information, less extra matching).
Nope. With my hacked up script (patch appended) Coccinelle is actually
*faster* for the .[ch] touched by this series: with your unmodified
script, it takes a bit over 12s on my box, with mine around 7s. Output
is identical.
Never guess performance, always measure it :)
Hmm, whole tree results would be better proof
Two notes on my script:
* Unlike yours, it recognizes double-propagation in my test case.
Discussed below.
* Its "several definitions of" warning includes positions. That turned
out to be useless, but I've been too lazy to take that out again.
+@@
+
+ fn(..., Error ** ____, ...)
+ {
+ ...
+ Error *local_err = NULL;
+ ... when any
+ Error *local_err2 = NULL;
+ ... when any
+ }
This flags functions that have more than one declaration along any
control flow path. It doesn't flag this one:
void gnat(bool b, Error **errp)
{
if (b) {
Error *local_err = NULL;
foo(arg, &local_err);
error_propagate(errp, local_err);
} else {
Error *local_err = NULL;
bar(arg, &local_err);
error_propagate(errp, local_err);
}
}
The Coccinelle script does the right thing for this one regardless.
I'd prefer to have such functions flagged, too. But spending time on
convincing Coccinelle to do it for me is not worthwhile; I can simply
search the diff produced by Coccinelle for deletions of declarations
that are not indented exactly four spaces.
But if we keep this rule, we should adjust its comment
// Warn several Error * definitions.
because it sure suggests it also catches functions like the one I gave
above.
Hmm, yes.. We can write "Warn several Error * definitions in _one_
control flow (it's not so trivial to match _any_ case with several
definitions with coccinelle)" or something like this.
+
+@ script:python @
+fn << check1.fn;
+@@
+
+print('Warning: function {} has several definitions of '
+ 'Error * local variable'.format(fn))
+
+// Warn several propagations in control flow.
+@check2 disable optional_qualifier exists@
+identifier fn = rule1.fn;
+symbol errp;
+position p1, p2;
+@@
+
+ fn(..., Error ** ____, ...)
+ {
+ ...
+(
+ error_propagate_prepend(errp, ...);@p1
+|
+ error_propagate(errp, ...);@p1
+)
+ ...
+(
+ error_propagate_prepend(errp, ...);@p2
+|
+ error_propagate(errp, ...);@p2
+)
+ ... when any
+ }
+
Hmm, we don't catch the example I used in review of v8:
extern foo(int, Error **);
extern bar(int, Error **);
void frob(Error **errp)
{
Error *local_err = NULL;
int arg;
foo(arg, errp);
bar(arg, &local_err);
error_propagate(errp, local_err);
bar(arg + 1, &local_err);
error_propagate(errp, local_err);
}
I believe this is because rule1 does not match here.
Yes, rule1 wants at least one code flow with non-doubled propagation.
If I change the rule as follows, it catches the example:
@@ -157,24 +157,23 @@ print('Warning: function {} has several
definitions of '
// Warn several propagations in control flow.
@check2 disable optional_qualifier exists@
-identifier fn = rule1.fn;
-symbol errp;
+identifier fn, _errp;
position p1, p2;
@@
- fn(..., Error ** ____, ...)
+ fn(..., Error **_errp, ...)
{
...
(
- error_propagate_prepend(errp, ...);@p1
+ error_propagate_prepend(_errp, ...);@p1
|
- error_propagate(errp, ...);@p1
+ error_propagate(_errp, ...);@p1
)
...
(
- error_propagate_prepend(errp, ...);@p2
+ error_propagate_prepend(_errp, ...);@p2
|
- error_propagate(errp, ...);@p2
+ error_propagate(_errp, ...);@p2
)
... when any
}
To my mild surprise, it still doesn't find anything in our tree.
Should we decouple the previous rule from rule1, too? I tested the
following on the whole tree:
I don't think so. Why to check what we are not going to convert? If we want
to check side things, it's better to do it in other coccinelle script..
Misunderstanding? The rules are still chained together via the ___
hack, just not via function name, because that's unreliable and
redundant.
Strange.. Then, how can it match something not matched by rule1?
I think I got confused when I wrote the "Misunderstanding?" paragraph.
Let me try again.
First rule check2.
The common case is a at most one propagation to @errp along any control
flow path. We trust your Coccinelle script to convert that alright.
Any other propagation to @errp I want to review. Whether the script
attempts a conversion or not is unimportant, as long as it points me to
the function to review.
Rule rule1 matches functions that propagate to @errp once along at least
one control flow path.
Unchained from rule rule1, rule check2 flags any function that
propagates to @errp multiple times along any control flow path.
Chained to rule1, it flags only functions that also have a path with
single propagation.
In other words, the unchained rule flags *all* multi-propagations to
@errp, while the chained rule flags only the ones the script attempts to
convert. The former is much more useful to me.
Now rule check1. It flags functions with multiple declarations along
any control flow path. Again, chaining it to rule1 restricts it to the
functions we attempt to convert. Makes it less useful to me. However,
because my desire to review multiple declarations in function we don't
attempt to convert is lower than my desire to review multiple
propagations to @errp in such functions, chaining check1 is tolerable
for me. But why chain check1 if we don't chain check2?
OK, let's unchain them.
@@ -136,10 +136,10 @@ symbol errp;
// Warn several Error * definitions.
@check1 disable optional_qualifier exists@
-identifier fn = rule1.fn, local_err, local_err2;
+identifier fn, _errp, local_err, local_err2;
@@
- fn(..., Error ** ____, ...)
+ fn(..., Error **_errp, ...)
{
...
Error *local_err = NULL;
Warnings remain unchanged.
+@ script:python @
+fn << check2.fn;
+p1 << check2.p1;
+p2 << check2.p2;
+@@
+
+print('Warning: function {} propagates to errp several times in '
+ 'one control flow: at {}:{} and then at {}:{}'.format(
+ fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line))
+
+// Convert special case with goto separately.
+// I tried merging this into the following rule the obvious way, but
+// it made Coccinelle hang on block.c
+//
+// Note interesting thing: if we don't do it here, and try to fixup
+// "out: }" things later after all transformations (the rule will be
+// the same, just without error_propagate() call), coccinelle fails to
+// match this "out: }".
+@ disable optional_qualifier@
+identifier rule1.fn, rule1.local_err, out;
As explained above, I doubt the need for rule1.fn. We do need
rule1.local_err to avoid unwanted transformations. More of the same
below.
Logically, I want to inherit from rule1. So why not to stress it by inheriting
fn variable? It's just a correct thing to do.
And I hope it helps coccinelle to work more efficiently.
+symbol errp;
+@@
+
+ fn(..., Error ** ____, ...)
+ {
+ <...
+- goto out;
++ return;
+ ...>
+- out:
+- error_propagate(errp, local_err);
+ }
+
+// Convert most of local_err related stuff.
+//
+// Note, that we update everything related to matched by rule1
+// function name and local_err name. We may match something not
+// related to the pattern matched by rule1. For example, local_err may
+// be defined with the same name in different blocks inside one
+// function, and in one block follow the propagation pattern and in
+// other block doesn't. Or we may have several functions with the same
+// name (for different configurations).
+//
+// Note also that errp-cleaning functions
+// error_free_errp
+// error_report_errp
+// error_reportf_errp
+// warn_report_errp
+// warn_reportf_errp
+// are not yet implemented. They must call corresponding Error* -
+// freeing function and then set *errp to NULL, to avoid further
+// propagation to original errp (consider ERRP_AUTO_PROPAGATE in use).
+// For example, error_free_errp may look like this:
+//
+// void error_free_errp(Error **errp)
+// {
+// error_free(*errp);
+// *errp = NULL;
+// }
+@ disable optional_qualifier exists@
+identifier rule1.fn, rule1.local_err;
+expression list args;
+symbol errp;
+@@
+
+ fn(..., Error ** ____, ...)
+ {
+ <...
+(
+- Error *local_err = NULL;
+|
+
+// Convert error clearing functions
+(
+- error_free(local_err);
++ error_free_errp(errp);
+|
+- error_report_err(local_err);
++ error_report_errp(errp);
+|
+- error_reportf_err(local_err, args);
++ error_reportf_errp(errp, args);
+|
+- warn_report_err(local_err);
++ warn_report_errp(errp);
+|
+- warn_reportf_err(local_err, args);
++ warn_reportf_errp(errp, args);
+)
+?- local_err = NULL;
+
+|
+- error_propagate_prepend(errp, local_err, args);
++ error_prepend(errp, args);
+|
+- error_propagate(errp, local_err);
+|
+- &local_err
++ errp
+)
+ ...>
+ }
+
+// Convert remaining local_err usage. For example, different kinds of
+// error checking in if conditionals. We can't merge this into
+// previous hunk, as this conflicts with other substitutions in it (at
+// least with "- local_err = NULL").
+@ disable optional_qualifier@
+identifier rule1.fn, rule1.local_err;
+symbol errp;
+@@
+
+ fn(..., Error ** ____, ...)
+ {
+ <...
+- local_err
++ *errp
+ ...>
+ }
+
+// Always use the same pattern for checking error
+@ disable optional_qualifier@
+identifier rule1.fn;
+symbol errp;
+@@
+
+ fn(..., Error ** ____, ...)
+ {
+ <...
+- *errp != NULL
++ *errp
+ ...>
+ }
+
+// Revert temporary ___ identifier.
+@ disable optional_qualifier@
+identifier rule1.fn;
+@@
+
+ fn(..., Error **
+- ____
++ errp
+ , ...)
+ {
+ ...
+ }
diff --git a/include/qapi/error.h b/include/qapi/error.h
index 30140d9bfe..56c133520d 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -214,6 +214,9 @@
* }
* ...
* }
+ *
+ * For mass-conversion use script
+ * scripts/coccinelle/auto-propagated-errp.cocci
*/
#ifndef ERROR_H
diff --git a/MAINTAINERS b/MAINTAINERS
index 857f969aa1..047f1b9714 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1998,6 +1998,7 @@ F: include/qemu/error-report.h
F: qapi/error.json
F: util/error.c
F: util/qemu-error.c
+F: scripts/coccinelle/*err*.cocci
GDB stub
M: Alex Bennée <alex.ben...@linaro.org>
From 42a08c529024337d1b859839c9ce7f797f784555 Mon Sep 17 00:00:00 2001
From: Markus Armbruster <arm...@redhat.com>
Date: Fri, 13 Mar 2020 14:27:57 +0100
Subject: [PATCH] fixup! scripts: Coccinelle script to use
ERRP_AUTO_PROPAGATE()
---
scripts/coccinelle/auto-propagated-errp.cocci | 37 ++++++++++---------
1 file changed, 20 insertions(+), 17 deletions(-)
diff --git a/scripts/coccinelle/auto-propagated-errp.cocci
b/scripts/coccinelle/auto-propagated-errp.cocci
index 7dac2dcfa4..43b0b0e63b 100644
--- a/scripts/coccinelle/auto-propagated-errp.cocci
+++ b/scripts/coccinelle/auto-propagated-errp.cocci
@@ -136,45 +136,48 @@ symbol errp;
// Warn several Error * definitions.
@check1 disable optional_qualifier exists@
-identifier fn = rule1.fn, local_err, local_err2;
+identifier fn, _errp, local_err, local_err2;
+position p1, p2;
Hmm, seems like I forget to define ____ as symbol in my patch
Coccinelle defaults to symbol.
But for errp we saw warnings simetimes.
@@
- fn(..., Error ** ____, ...)
+ fn(..., Error **_errp, ...)
Ahmm.. it will break compilation?
Or, how will it work when _errp defined as meta variable is only in "+..."
line? Should it be symbol instead, or just not defined?
Misunderstanding? It's a diff between your .cocci and mine.
Oops, yes, sorry. Patches to coccinelle scripts are tricky thing.
My version
is
// Warn several Error * definitions.
@check1 disable optional_qualifier exists@
identifier fn, _errp, local_err, local_err2;
position p1, p2;
@@
fn(..., Error **_errp, ...)
{
...
Error *local_err = NULL;@p1
... when any
Error *local_err2 = NULL;@p2
... when any
}
@ script:python @
fn << check1.fn;
p1 << check1.p1;
p2 << check1.p2;
@@
{
...
- Error *local_err = NULL;
+ Error *local_err = NULL;@p1
Why to do -/+ here? Nothing changed..
... when any
- Error *local_err2 = NULL;
+ Error *local_err2 = NULL;@p2
... when any
}
@ script:python @
fn << check1.fn;
+p1 << check1.p1;
+p2 << check1.p2;
@@
print('Warning: function {} has several definitions of '
- 'Error * local variable'.format(fn))
+ 'Error * local variable: at {}:{} and then at {}:{}'.format(
+ fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line))
// Warn several propagations in control flow.
@check2 disable optional_qualifier exists@
-identifier fn = rule1.fn;
-symbol errp;
+identifier fn, _errp;
position p1, p2;
@@
- fn(..., Error ** ____, ...)
+ fn(..., Error **_errp, ...)
{
...
(
- error_propagate_prepend(errp, ...);@p1
+ error_propagate_prepend(_errp, ...);@p1
|
- error_propagate(errp, ...);@p1
+ error_propagate(_errp, ...);@p1
)
...
(
- error_propagate_prepend(errp, ...);@p2
+ error_propagate_prepend(_errp, ...);@p2
|
- error_propagate(errp, ...);@p2
+ error_propagate(_errp, ...);@p2
)
You change some occurrences of errp to _errp, but not all. It breaks
compilation.
... when any
}
@@ -198,7 +201,7 @@ print('Warning: function {} propagates to errp several
times in '
// the same, just without error_propagate() call), coccinelle fails to
// match this "out: }".
@ disable optional_qualifier@
-identifier rule1.fn, rule1.local_err, out;
+identifier fn, rule1.local_err, out;
Hmm. If it improves performance it is strange.. But I can live with this change.
symbol errp;
@@
@@ -239,7 +242,7 @@ symbol errp;
// *errp = NULL;
// }
@ disable optional_qualifier exists@
-identifier rule1.fn, rule1.local_err;
+identifier fn, rule1.local_err;
expression list args;
symbol errp;
@@
@@ -287,7 +290,7 @@ symbol errp;
// previous hunk, as this conflicts with other substitutions in it (at
// least with "- local_err = NULL").
@ disable optional_qualifier@
-identifier rule1.fn, rule1.local_err;
+identifier fn, rule1.local_err;
symbol errp;
@@
@@ -301,7 +304,7 @@ symbol errp;
// Always use the same pattern for checking error
@ disable optional_qualifier@
-identifier rule1.fn;
+identifier fn;
symbol errp;
@@
@@ -315,7 +318,7 @@ symbol errp;
// Revert temporary ___ identifier.
@ disable optional_qualifier@
-identifier rule1.fn;
+identifier fn;
@@
fn(..., Error **
I append my hacked up version of auto-propagated-errp.cocci. It
produces the same patch as yours for the complete tree.
// Use ERRP_AUTO_PROPAGATE (see include/qapi/error.h)
//
// Copyright (c) 2020 Virtuozzo International GmbH.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Usage example:
// spatch --sp-file scripts/coccinelle/auto-propagated-errp.cocci \
// --macro-file scripts/cocci-macro-file.h --in-place \
// --no-show-diff --max-width 80 FILES...
//
// Note: --max-width 80 is needed because coccinelle default is less
// than 80, and without this parameter coccinelle may reindent some
// lines which fit into 80 characters but not to coccinelle default,
// which in turn produces extra patch hunks for no reason.
// Switch unusual Error ** parameter names to errp
// (this is necessary to use ERRP_AUTO_PROPAGATE).
//
// Disable optional_qualifier to skip functions with
// "Error *const *errp" parameter.
//
// Skip functions with "assert(_errp && *_errp)" statement, because
// that signals unusual semantics, and the parameter name may well
// serve a purpose. (like nbd_iter_channel_error()).
//
// Skip util/error.c to not touch, for example, error_propagate() and
// error_propagate_prepend().
@ depends on !(file in "util/error.c") disable optional_qualifier@
identifier fn;
identifier _errp != errp;
@@
fn(...,
- Error **_errp
+ Error **errp
,...)
{
(
... when != assert(_errp && *_errp)
&
<...
- _errp
+ errp
...>
)
}
// Add invocation of ERRP_AUTO_PROPAGATE to errp-functions where
// necessary
//
// Note, that without "when any" the final "..." does not mach
// something matched by previous pattern, i.e. the rule will not match
// double error_prepend in control flow like in
// vfio_set_irq_signaling().
//
// Note, "exists" says that we want apply rule even if it matches not
// on all possible control flows (otherwise, it will not match
// standard pattern when error_propagate() call is in if branch).
@ disable optional_qualifier exists@
identifier fn, local_err;
symbol errp;
@@
fn(..., Error **errp, ...)
{
+ ERRP_AUTO_PROPAGATE();
... when != ERRP_AUTO_PROPAGATE();
(
(
error_append_hint(errp, ...);
|
error_prepend(errp, ...);
|
error_vprepend(errp, ...);
)
... when any
|
Error *local_err = NULL;
...
(
error_propagate_prepend(errp, local_err, ...);
|
error_propagate(errp, local_err);
)
...
)
}
// Match functions with propagation of local error to errp.
// We want to refer these functions in several following rules, but I
// don't know a proper way to inherit a function, not just its name
// (to not match another functions with same name in following rules).
// Not-proper way is as follows: rename errp parameter in functions
// header and match it in following rules. Rename it back after all
// transformations.
//
// The simplest case of propagation scheme is single definition of
// local_err with at most one error_propagate_prepend or
// error_propagate on each control-flow. Still, we want to match more
// complex schemes too. We'll warn them with help of further rules.
@rule1 disable optional_qualifier exists@
identifier fn, local_err;
symbol errp;
@@
fn(..., Error **
- errp
+ ____
, ...)
{
...
Error *local_err = NULL;
...
(
error_propagate_prepend(errp, local_err, ...);
|
error_propagate(errp, local_err);
)
...
}
// Warn several Error * definitions.
@check1 disable optional_qualifier exists@
identifier fn, _errp, local_err, local_err2;
position p1, p2;
@@
fn(..., Error **_errp, ...)
{
...
Error *local_err = NULL;@p1
... when any
Error *local_err2 = NULL;@p2
... when any
}
@ script:python @
fn << check1.fn;
p1 << check1.p1;
p2 << check1.p2;
@@
print('Warning: function {} has several definitions of '
'Error * local variable: at {}:{} and then at {}:{}'.format(
fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line))
// Warn several propagations in control flow.
@check2 disable optional_qualifier exists@
identifier fn, _errp;
position p1, p2;
@@
fn(..., Error **_errp, ...)
{
...
(
error_propagate_prepend(_errp, ...);@p1
|
error_propagate(_errp, ...);@p1
)
...
(
error_propagate_prepend(_errp, ...);@p2
|
error_propagate(_errp, ...);@p2
)
... when any
}
@ script:python @
fn << check2.fn;
p1 << check2.p1;
p2 << check2.p2;
@@
print('Warning: function {} propagates to errp several times in '
'one control flow: at {}:{} and then at {}:{}'.format(
fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line))
// Convert special case with goto separately.
// I tried merging this into the following rule the obvious way, but
// it made Coccinelle hang on block.c
//
// Note interesting thing: if we don't do it here, and try to fixup
// "out: }" things later after all transformations (the rule will be
// the same, just without error_propagate() call), coccinelle fails to
// match this "out: }".
@ disable optional_qualifier@
identifier fn, rule1.local_err, out;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
- goto out;
+ return;
...>
- out:
- error_propagate(errp, local_err);
}
// Convert most of local_err related stuff.
//
// Note, that we update everything related to matched by rule1
// function name and local_err name. We may match something not
// related to the pattern matched by rule1. For example, local_err may
// be defined with the same name in different blocks inside one
// function, and in one block follow the propagation pattern and in
// other block doesn't. Or we may have several functions with the same
// name (for different configurations).
//
// Note also that errp-cleaning functions
// error_free_errp
// error_report_errp
// error_reportf_errp
// warn_report_errp
// warn_reportf_errp
// are not yet implemented. They must call corresponding Error* -
// freeing function and then set *errp to NULL, to avoid further
// propagation to original errp (consider ERRP_AUTO_PROPAGATE in use).
// For example, error_free_errp may look like this:
//
// void error_free_errp(Error **errp)
// {
// error_free(*errp);
// *errp = NULL;
// }
@ disable optional_qualifier exists@
identifier fn, rule1.local_err;
expression list args;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
(
- Error *local_err = NULL;
|
// Convert error clearing functions
(
- error_free(local_err);
+ error_free_errp(errp);
|
- error_report_err(local_err);
+ error_report_errp(errp);
|
- error_reportf_err(local_err, args);
+ error_reportf_errp(errp, args);
|
- warn_report_err(local_err);
+ warn_report_errp(errp);
|
- warn_reportf_err(local_err, args);// Use ERRP_AUTO_PROPAGATE (see
include/qapi/error.h)
//
// Copyright (c) 2020 Virtuozzo International GmbH.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Usage example:
// spatch --sp-file scripts/coccinelle/auto-propagated-errp.cocci \
// --macro-file scripts/cocci-macro-file.h --in-place \
// --no-show-diff --max-width 80 FILES...
//
// Note: --max-width 80 is needed because coccinelle default is less
// than 80, and without this parameter coccinelle may reindent some
// lines which fit into 80 characters but not to coccinelle default,
// which in turn produces extra patch hunks for no reason.
// Switch unusual Error ** parameter names to errp
// (this is necessary to use ERRP_AUTO_PROPAGATE).
//
// Disable optional_qualifier to skip functions with
// "Error *const *errp" parameter.
//
// Skip functions with "assert(_errp && *_errp)" statement, because
// that signals unusual semantics, and the parameter name may well
// serve a purpose. (like nbd_iter_channel_error()).
//
// Skip util/error.c to not touch, for example, error_propagate() and
// error_propagate_prepend().
@ depends on !(file in "util/error.c") disable optional_qualifier@
identifier fn;
identifier _errp != errp;
@@
fn(...,
- Error **_errp
+ Error **errp
,...)
{
(
... when != assert(_errp && *_errp)
&
<...
- _errp
+ errp
...>
)
}
// Add invocation of ERRP_AUTO_PROPAGATE to errp-functions where
// necessary
//
// Note, that without "when any" the final "..." does not mach
// something matched by previous pattern, i.e. the rule will not match
// double error_prepend in control flow like in
// vfio_set_irq_signaling().
//
// Note, "exists" says that we want apply rule even if it matches not
// on all possible control flows (otherwise, it will not match
// standard pattern when error_propagate() call is in if branch).
@ disable optional_qualifier exists@
identifier fn, local_err;
symbol errp;
@@
fn(..., Error **errp, ...)
{
+ ERRP_AUTO_PROPAGATE();
... when != ERRP_AUTO_PROPAGATE();
(
(
error_append_hint(errp, ...);
|
error_prepend(errp, ...);
|
error_vprepend(errp, ...);
)
... when any
|
Error *local_err = NULL;
...
(
error_propagate_prepend(errp, local_err, ...);
|
error_propagate(errp, local_err);
)
...
)
}
// Match functions with propagation of local error to errp.
// We want to refer these functions in several following rules, but I
// don't know a proper way to inherit a function, not just its name
// (to not match another functions with same name in following rules).
// Not-proper way is as follows: rename errp parameter in functions
// header and match it in following rules. Rename it back after all
// transformations.
//
// The simplest case of propagation scheme is single definition of
// local_err with at most one error_propagate_prepend or
// error_propagate on each control-flow. Still, we want to match more
// complex schemes too. We'll warn them with help of further rules.
@rule1 disable optional_qualifier exists@
identifier fn, local_err;
symbol errp;
@@
fn(..., Error **
- errp
+ ____
, ...)
{
...
Error *local_err = NULL;
...
(
error_propagate_prepend(errp, local_err, ...);
|
error_propagate(errp, local_err);
)
...
}
// Warn several Error * definitions.
@check1 disable optional_qualifier exists@
identifier fn, _errp, local_err, local_err2;
position p1, p2;
@@
fn(..., Error **_errp, ...)
{
...
Error *local_err = NULL;@p1
... when any
Error *local_err2 = NULL;@p2
... when any
}
@ script:python @
fn << check1.fn;
p1 << check1.p1;
p2 << check1.p2;
@@
print('Warning: function {} has several definitions of '
'Error * local variable: at {}:{} and then at {}:{}'.format(
fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line))
// Warn several propagations in control flow.
@check2 disable optional_qualifier exists@
identifier fn, _errp;
position p1, p2;
@@
fn(..., Error **_errp, ...)
{
...
(
error_propagate_prepend(_errp, ...);@p1
|
error_propagate(_errp, ...);@p1
)
...
(
error_propagate_prepend(_errp, ...);@p2
|
error_propagate(_errp, ...);@p2
)
... when any
}
@ script:python @
fn << check2.fn;
p1 << check2.p1;
p2 << check2.p2;
@@
print('Warning: function {} propagates to errp several times in '
'one control flow: at {}:{} and then at {}:{}'.format(
fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line))
// Convert special case with goto separately.
// I tried merging this into the following rule the obvious way, but
// it made Coccinelle hang on block.c
//
// Note interesting thing: if we don't do it here, and try to fixup
// "out: }" things later after all transformations (the rule will be
// the same, just without error_propagate() call), coccinelle fails to
// match this "out: }".
@ disable optional_qualifier@
identifier fn, rule1.local_err, out;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
- goto out;
+ return;
...>
- out:
- error_propagate(errp, local_err);
}
// Convert most of local_err related stuff.
//
// Note, that we update everything related to matched by rule1
// function name and local_err name. We may match something not
// related to the pattern matched by rule1. For example, local_err may
// be defined with the same name in different blocks inside one
// function, and in one block follow the propagation pattern and in
// other block doesn't. Or we may have several functions with the same
// name (for different configurations).
//
// Note also that errp-cleaning functions
// error_free_errp
// error_report_errp
// error_reportf_errp
// warn_report_errp
// warn_reportf_errp
// are not yet implemented. They must call corresponding Error* -
// freeing function and then set *errp to NULL, to avoid further
// propagation to original errp (consider ERRP_AUTO_PROPAGATE in use).
// For example, error_free_errp may look like this:
//
// void error_free_errp(Error **errp)
// {
// error_free(*errp);
// *errp = NULL;
// }
@ disable optional_qualifier exists@
identifier fn, rule1.local_err;
expression list args;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
(
- Error *local_err = NULL;
|
// Convert error clearing functions
(
- error_free(local_err);
+ error_free_errp(errp);
|
- error_report_err(local_err);
+ error_report_errp(errp);
|
- error_reportf_err(local_err, args);
+ error_reportf_errp(errp, args);
|
- warn_report_err(local_err);
+ warn_report_errp(errp);
|
- warn_reportf_err(local_err, args);
+ warn_reportf_errp(errp, args);
)
?- local_err = NULL;
|
- error_propagate_prepend(errp, local_err, args);
+ error_prepend(errp, args);
|
- error_propagate(errp, local_err);
|
- &local_err
+ errp
)
...>
}
// Convert remaining local_err usage. For example, different kinds of
// error checking in if conditionals. We can't merge this into
// previous hunk, as this conflicts with other substitutions in it (at
// least with "- local_err = NULL").
@ disable optional_qualifier@
identifier fn, rule1.local_err;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
- local_err
+ *errp
...>
}
// Always use the same pattern for checking error
@ disable optional_qualifier@
identifier fn;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
- *errp != NULL
+ *errp
...>
}
// Revert temporary ___ identifier.
@ disable optional_qualifier@
identifier fn;
@@
fn(..., Error **
- ____
+ errp
, ...)
{
...
}
+ warn_reportf_errp(errp, args);
)
?- local_err = NULL;
|
- error_propagate_prepend(errp, local_err, args);
+ error_prepend(errp, args);
|
- error_propagate(errp, local_err);
|
- &local_err
+ errp
)
...>
}
// Convert remaining local_err usage. For example, different kinds of
// error checking in if conditionals. We can't merge this into
// previous hunk, as this conflicts with other substitutions in it (at
// least with "- local_err = NULL").
@ disable optional_qualifier@
identifier fn, rule1.local_err;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
- local_err
+ *errp
...>
}
// Always use the same pattern for checking error
@ disable optional_qualifier@
identifier fn;
symbol errp;
@@
fn(..., Error ** ____, ...)
{
<...
- *errp != NULL
+ *errp
...>
}
// Revert temporary ___ identifier.
@ disable optional_qualifier@
identifier fn;
@@
fn(..., Error **
- ____
+ errp
, ...)
{
...
}
OK, I almost OK with it, the only thing I doubt a bit is the following:
We want to keep rule1.local_err inheritance to keep connection with
local_err definition.
Interesting, when we have both rule1.fn and rule1.local_err inherited,
do we inherit them in separate (i.e. all possible combinations of fn
and local_err symbols from rule1) or do we inherit a pair, i.e. only
fn/local_err pairs, found by rule1? If the latter is correct, that
with your script we loss this pair inheritance, and go to all possible
combinations of fn and local_err from rule1, possibly adding some wrong
conversion (OK, you've checked that no such cases in current code tree).
So, dropping inheritance in check-rules makes sence, as it may match
(and warn) more interesting cases.
But for other rules, I'd prefere to be safer, and explictly inherit all
actually inherited identifiers.. Still, I feel, we'll never be
absolutely safe with coccinelle :)
--
Best regrads,
Vladimir