The Linux community reads lot of diff files for following the kernel
evolution. As a consequence the diff file syntax is widely spread and
commonly understood. However this syntax concerns a particular change
between two files, its does not allow to match a generic pattern.
The Coccinelle's solution is to build its own langage allowing to declare
rules describing a code pattern and a possible transformation. This
langage is the Semantic Patch Langage (SmPL), based on the declarative
approach of the diff file syntax. It allows to propagate a change rule to
many files by generating diff files. Then those results can be directly
applied by using the patch command but most of the time they will be
reviewed and may be slightly adapted to the programmer's need.
A Coccinelle's rule is made of two parts: metavariable declaration and a
code pattern match followed by a possible transformation. A metavariable
means a control flow variable, its possibles names inside the program
do not matter. Then the code pattern will describe a particular control
flow in the program by using the C and SmPL syntaxes manipulating the
metavariables. As a result, Coccinelle succeeds to generate diff files
because it works on the C program control flow.
A complete SmPL description will not be given here because it can be found
in the Coccinelle's documentation. However a brief introduction will be
made on a rule declaration. The metavariable part will look like this:
@@
expression E;
constant C;
@@
'expression' means a variable or the result of a function. However
'constant' means a C constant. Then for negating the result of an and
operation between an expression and a constant instead of negating the
expression first, the transformation part will be:
- !E & C
+ !(E & C)
A file containing several rules like that will be called a semantic
patch. It will be applied by using the Coccinelle 'spatch' command that
will generate a change written in the diff file syntax each time the above
pattern is matched. The next section will illustrate this way of work.
You can download and install Coccinelle 'spatch' command from its
website: http://coccinelle.lip6.fr/ if you want to execute the following
example. Let's first consider the following structure with accessors in
the header 'device.h':
struct device {
void *driver_data;
};
static inline void *dev_get_drvdata(const struct device *dev)
{
return dev->driver_data;
}
static inline void dev_set_drvdata(struct device *dev, void* data)
{
dev->driver_data = data;
}
it imitates the 2.6.30 kernel header 'include/linux/device.h'. Let's now
consider the following client code that does not make use of the accessors:
#include <stdlib.h>
#include <assert.h>
#include "device.h"
int main()
{
struct device devs[2], *dev_ptr;
int data[2] = {3, 7};
void *a = NULL, *b = NULL;
devs[0].driver_data = (void*)(&data[0]);
a = devs[0].driver_data;
dev_ptr = &devs[1];
dev_ptr->driver_data = (void*)(&data[1]);
b = dev_ptr->driver_data;
assert(*((int*)a) == 3);
assert(*((int*)b) == 7);
return 0;
}
Once this code saved in the file 'fake_device.c', we can check that the
code compiles and runs by:
$ gcc fake_device.c && ./a.out
We will now create a semantic patch 'device_data.cocci' trying to add the
getter accessor with this first rule:
@@
struct device dev;
@@
- dev.driver_data
+ dev_get_drvdata(&dev)
The 'spatch' command is then run by:
$ spatch -sp_file device_data.cocci fake_device.c
producing the following change in a diff file:
- devs[0].driver_data = (void*)(&data[0]);
- a = devs[0].driver_data;
+ dev_get_drvdata(&devs[0]) = (void*)(&data[0]);
+ a = dev_get_drvdata(&devs[0]);
which illustrates the great Coccinelle's way of work on program flow control.
However the transformation has also matched code where the setter accessor
should be used. We will thus add a rule above the previous one, the semantic
patch becomes:
@@
struct device dev;
expression data;
@@
- dev.driver_data = data
+ dev_set_drvdata(&dev, data)
@@
struct device dev;
@@
- dev.driver_data
+ dev_get_drvdata(&dev)
Running the command again will produce the wanted output:
$ spatch -sp_file device_data.cocci fake_device.c
- devs[0].driver_data = (void*)(&data[0]);
- a = devs[0].driver_data;
+ dev_set_drvdata(&devs[0], (void *)(&data[0]));
+ a = dev_get_drvdata(&devs[0]);
It is important to write the setter rule before the getter rule else
the getter rule will be applied first to the whole file.
At this point our semantic patch is still incomplete because it does
not work on 'device' structure pointers. By using the same logic,
let's add it to the 'device_data.cocci' semantic patch:
@@
struct device dev;
expression data;
@@
- dev.driver_data = data
+ dev_set_drvdata(&dev, data)
@@
struct device * dev;
expression data;
@@
- dev->driver_data = data
+ dev_set_drvdata(dev, data)
@@
struct device dev;
@@
- dev.driver_data
+ dev_get_drvdata(&dev)
@@
struct device * dev;
@@
- dev->driver_data
+ dev_get_drvdata(dev)
Running Coccinelle again:
$ spatch -sp_file device_data.cocci fake_device.c
will add the remaining transformations for the 'fake_device.c' file:
- dev_ptr->driver_data = (void*)(&data[1]);
- b = dev_ptr->driver_data;
+ dev_set_drvdata(dev_ptr, (void *)(&data[1]));
+ b = dev_get_drvdata(dev_ptr);
but a new problem appears: the 'device.h' header is also modified.
We meet here an important point of the Coccinelle's philosophy described
in the first section. 'spatch' is a tool to ease code maintenance by
propagating a code pattern change to many files. However the resulting
diff files are supposed to be reviewed and in our case the unwanted
modification should be removed. Note that it would be possible to
avoid the 'device.h' header modification by using SmPL syntax but the
explanation would be too much for a starting tutorial. Instead,
we will simply cut the unwanted part:
$ spatch -sp_file device_data.cocci fake_device.c | cut -d $'\n' -f 16-34
This result will now be kept in a diff file by moreover asking 'spatch' to
produce it for the current working directory:
$ spatch -sp_file device_data.cocci -patch "" fake_device.c | \
cut -d $'\n' -f 16-34 > device_data.patch
It is now time to apply the change for getting a working C code using
accessors:
$ patch -p1 < device_data.patch
The final result for 'fake_device.c' should be:
#include <stdlib.h>
#include <assert.h>
#include "device.h"
int main()
{
struct device devs[2], *dev_ptr;
int data[2] = {3, 7};
void *a = NULL, *b = NULL;
dev_set_drvdata(&devs[0], (void *)(&data[0]));
a = dev_get_drvdata(&devs[0]);
dev_ptr = &devs[1];
dev_set_drvdata(dev_ptr, (void *)(&data[1]));
b = dev_get_drvdata(dev_ptr);
assert(*((int*)a) == 3);
assert(*((int*)b) == 7);
return 0;
}
Finally, we can test that the code compiles and runs:
.. sourcecode:: sh
$ gcc fake_device.c && ./a.out
The semantic patch is now ready to be used on the Linux's 2.6.30 kernel:
$ wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.30.tar.bz2
$ tar xjf linux-2.6.30.tar.bz2
$ spatch -sp_file device_data.cocci -dir linux-2.6.30/drivers/net/ \
> device_drivers_net.patch
$ wc -l device_drivers_net.patch
642
You may also try the 'drivers/ieee1394' directory.