Coccinelle¶
Coccinelle 是一种用于模式匹配和文本转换的工具,在内核开发中有很多用途,包括应用复杂的、树范围的补丁和检测有问题的编程模式。
获取 Coccinelle¶
内核中包含的语义补丁使用了 Coccinelle 1.0.0-rc11 及更高版本提供的特性和选项。使用早期版本将会失败,因为 Coccinelle 文件和 coccicheck 使用的选项名称已更新。
Coccinelle 可以通过许多发行版的软件包管理器获得,例如:
Debian
Fedora
Ubuntu
OpenSUSE
Arch Linux
NetBSD
FreeBSD
一些发行版软件包已过时,建议使用 Coccinelle 主页上发布的最新版本:http://coccinelle.lip6.fr/
或者从 Github:
https://github.com/coccinelle/coccinelle
获得它后,运行以下命令
./autogen
./configure
make
作为普通用户,并使用以下命令安装它
sudo make install
从源代码构建的更详细的安装说明可以在以下位置找到:
https://github.com/coccinelle/coccinelle/blob/master/install.txt
补充文档¶
有关补充文档,请参阅 wiki
https://bottest.wiki.kernel.org/coccicheck
wiki 文档始终指的是 linux-next 版本的脚本。
有关语义补丁语言 (SmPL) 语法文档,请参阅
https://coccinelle.gitlabpages.inria.fr/website/docs/main_grammar.html
在 Linux 内核上使用 Coccinelle¶
Coccinelle 特定的目标在顶层 Makefile 中定义。 此目标名为 coccicheck
,并在 scripts
目录中调用 coccicheck
前端。
定义了四种基本模式:patch
、report
、context
和 org
。 要使用的模式通过设置 MODE 变量来指定,例如 MODE=<mode>
。
patch
尽可能提出修复方案。report
生成以下格式的列表:file:line:column-column: messagecontext
以类似于 diff 的样式突出显示感兴趣的行及其上下文。 感兴趣的行用-
表示。org
生成 Emacs 的 Org 模式格式的报告。
请注意,并非所有语义补丁都实现了所有模式。 为了便于使用 Coccinelle,默认模式为“report”。
其他两种模式提供这些模式的一些常见组合。
chain
按上述顺序尝试之前的模式,直到其中一个成功。rep+ctxt
依次运行 report 模式和 context 模式。它应该与 C 选项(稍后描述)一起使用,该选项基于文件检查代码。
示例¶
要为每个语义补丁生成报告,请运行以下命令
make coccicheck MODE=report
要生成补丁,请运行
make coccicheck MODE=patch
coccicheck 目标将 scripts/coccinelle
子目录中提供的每个语义补丁应用于整个 Linux 内核。
对于每个语义补丁,都会提出提交消息。 它给出了语义补丁正在检查的问题的描述,并包括对 Coccinelle 的引用。
与任何静态代码分析器一样,Coccinelle 也会产生误报。 因此,必须仔细检查报告,并审查补丁。
要启用详细消息,请设置 V= 变量,例如
make coccicheck MODE=report V=1
Coccinelle 并行化¶
默认情况下,coccicheck 尝试尽可能并行地运行。 要更改并行度,请设置 J= 变量。 例如,要在 4 个 CPU 上运行
make coccicheck MODE=report J=4
从 Coccinelle 1.0.2 开始,Coccinelle 使用 Ocaml parmap 进行并行化; 如果检测到对此的支持,您将受益于 parmap 并行化。
启用 parmap 后,coccicheck 将通过使用 --chunksize 1
参数启用动态负载平衡。 这确保了我们逐个地不断向线程提供工作,从而避免了大多数工作仅由少数线程完成的情况。 通过动态负载平衡,如果一个线程提前完成,我们会继续向其提供更多工作。
启用 parmap 后,如果 Coccinelle 中发生错误,则此错误值会传播回来,并且 make coccicheck
命令的返回值会捕获此返回值。
使用单个语义补丁的 Coccinelle¶
可选的 make 变量 COCCI 可用于检查单个语义补丁。 在这种情况下,必须使用要应用的语义补丁的名称初始化该变量。
例如
make coccicheck COCCI=<my_SP.cocci> MODE=patch
或
make coccicheck COCCI=<my_SP.cocci> MODE=report
控制 Coccinelle 处理的文件¶
默认情况下,会检查整个内核源代码树。
要将 Coccinelle 应用于特定目录,可以使用 M=
。 例如,要检查 drivers/net/wireless/,可以编写
make coccicheck M=drivers/net/wireless/
要基于文件而不是基于目录应用 Coccinelle,makefile 使用 C 变量来选择要处理的文件。 此变量可用于为整个内核、特定目录或单个文件运行脚本。
例如,要检查 drivers/bluetooth/bfusb.c,将值 1 传递给 C 变量以检查 make 认为需要编译的文件。
make C=1 CHECK=scripts/coccicheck drivers/bluetooth/bfusb.o
将值 2 传递给 C 变量以检查文件,无论它们是否需要编译。
make C=2 CHECK=scripts/coccicheck drivers/bluetooth/bfusb.o
在这些基于文件工作的模式中,不显示有关语义补丁的信息,也不提出提交消息。
默认情况下,这会运行 scripts/coccinelle 中的每个语义补丁。 COCCI 变量还可以用于仅应用单个语义补丁,如上一节所示。
“report”模式是默认模式。 您可以使用上面解释的 MODE 变量选择另一个模式。
调试 Coccinelle SmPL 补丁¶
最好使用 coccicheck,因为它在 spatch 命令行中提供了与编译内核时使用的选项匹配的包含选项。 您可以通过使用 V=1 了解这些选项是什么; 然后,您可以手动运行 Coccinelle 并添加调试选项。
或者,您可以通过请求将 stderr 重定向到 stderr 来调试针对 SmPL 补丁运行 Coccinelle。 默认情况下,stderr 会重定向到 /dev/null; 如果您想捕获 stderr,您可以指定 DEBUG_FILE="file.txt"
选项到 coccicheck。 例如
rm -f cocci.err
make coccicheck COCCI=scripts/coccinelle/free/kfree.cocci MODE=report DEBUG_FILE=cocci.err
cat cocci.err
您可以使用 SPFLAGS 添加调试标志; 例如,您可能希望在调试时将 --profile --show-trying
添加到 SPFLAGS。 例如,您可能想使用
rm -f err.log
export COCCI=scripts/coccinelle/misc/irqf_oneshot.cocci
make coccicheck DEBUG_FILE="err.log" MODE=report SPFLAGS="--profile --show-trying" M=./drivers/mfd
err.log 现在将具有分析信息,而 stdout 将提供一些进度信息,因为 Coccinelle 会继续工作。
注意
只有在使用 coccinelle >= 1.0.2 时才支持 DEBUG_FILE。
目前,DEBUG_FILE 支持仅适用于检查文件夹,而不适用于单个文件。 这是因为检查单个文件需要调用两次 spatch,从而导致 DEBUG_FILE 被设置为相同的值两次,从而导致错误。
.cocciconfig 支持¶
Coccinelle 支持读取 .cocciconfig 以获取每次生成 spatch 时应使用的默认 Coccinelle 选项。 .cocciconfig 的变量的优先级顺序如下:
首先处理您当前用户的主目录
接下来处理调用 spatch 的目录
如果使用
--dir
选项,则最后处理提供的目录
make coccicheck
也支持使用 M= 目标。 如果您不提供任何 M= 目标,则假定您要定位整个内核。 内核 coccicheck 脚本具有
OPTIONS="--dir $srcroot $COCCIINCLUDE"
在此,$srcroot 指的是目标的源目录:当使用 M= 时,它指向外部模块的源目录,否则,指向内核源目录。 第三条规则确保 spatch 从目标目录读取 .cocciconfig,允许外部模块拥有自己的 .cocciconfig 文件。
如果不使用内核的 coccicheck 目标,请保持上述 .cocciconfig 读取的优先级顺序逻辑。 如果使用内核的 coccicheck 目标,请使用 SPFLAGS 覆盖任何内核的 .cocciconfig 设置。
当针对 Linux 使用 Coccinelle 时,我们通过一组适用于 Linux 的合理默认选项来帮助 Coccinelle,并使用我们自己的 Linux .cocciconfig。 这向 coccinelle 提示 git 可以用于通过 coccigrep 进行 git grep
查询。 目前,200 秒的超时时间应该足够了。
当读取 .cocciconfig 时,coccinelle 拾取的选项不会显示为在您的系统上运行的 spatch 进程的参数。 要确认 Coccinelle 将使用哪些选项,请运行
spatch --print-options-only
您可以使用 SPFLAGS 覆盖您自己喜欢的索引选项。 请注意,当存在冲突选项时,Coccinelle 会优先考虑最后传递的选项。 可以使用 .cocciconfig 来使用 idutils,但是鉴于 Coccinelle 遵循的优先级顺序,由于内核现在携带自己的 .cocciconfig,因此如果需要,您需要使用 SPFLAGS 来使用 idutils。 有关如何使用 idutils 的更多详细信息,请参见下面的“附加标志”部分。
附加标志¶
可以通过 SPFLAGS 变量将附加标志传递给 spatch。 这是因为当选项冲突时,Coccinelle 会尊重传递给它的最后一个标志。
make SPFLAGS=--use-glimpse coccicheck
Coccinelle 也支持 idutils,但需要 coccinelle >= 1.0.6。 如果未指定 ID 文件,则 coccinelle 假定您的 ID 数据库文件位于内核顶层的 .id-utils.index 文件中。 Coccinelle 携带一个脚本 scripts/idutils_index.sh,该脚本使用以下命令创建数据库:
mkid -i C --output .id-utils.index
如果您有另一个数据库文件名,您也可以使用此名称进行符号链接。
make SPFLAGS=--use-idutils coccicheck
或者,您可以显式指定数据库文件名,例如
make SPFLAGS="--use-idutils /full-path/to/ID" coccicheck
请参阅 spatch --help
以了解有关 spatch 选项的更多信息。
请注意,--use-glimpse
和 --use-idutils
选项需要外部工具来索引代码。 因此,默认情况下它们都不是活动的。 但是,通过使用这些工具之一索引代码,并根据使用的 cocci 文件,spatch 可以更快地处理整个代码库。
SmPL 补丁特定选项¶
SmPL 补丁可以有自己的对传递给 Coccinelle 的选项的要求。 SmPL 补丁特定的选项可以通过在 SmPL 补丁顶部提供它们来提供,例如
// Options: --no-includes --include-headers
SmPL 补丁 Coccinelle 要求¶
随着 Coccinelle 功能的添加,一些更高级的 SmPL 补丁可能需要更新版本的 Coccinelle。 如果 SmPL 补丁需要最低版本的 Coccinelle,可以按如下方式指定,例如,如果需要至少 Coccinelle >= 1.0.5
// Requires: 1.0.5
提出新的语义补丁¶
内核开发人员可以提出和提交新的语义补丁。 为了清楚起见,它们应组织在 scripts/coccinelle/
的子目录中。
report
模式的详细描述¶
report
生成以下格式的列表
file:line:column-column: message
示例¶
运行
make coccicheck MODE=report COCCI=scripts/coccinelle/api/err_cast.cocci
将执行 SmPL 脚本的以下部分
<smpl>
@r depends on !context && !patch && (org || report)@
expression x;
position p;
@@
ERR_PTR@p(PTR_ERR(x))
@script:python depends on report@
p << r.p;
x << r.x;
@@
msg="ERR_CAST can be used with %s" % (x)
coccilib.report.print_report(p[0], msg)
</smpl>
此 SmPL 摘录在标准输出上生成条目,如下所示
/home/user/linux/crypto/ctr.c:188:9-16: ERR_CAST can be used with alg
/home/user/linux/crypto/authenc.c:619:9-16: ERR_CAST can be used with auth
/home/user/linux/crypto/xts.c:227:9-16: ERR_CAST can be used with alg
patch
模式的详细描述¶
当 patch
模式可用时,它会为每个已识别的问题提出修复方案。
示例¶
运行
make coccicheck MODE=patch COCCI=scripts/coccinelle/api/err_cast.cocci
将执行 SmPL 脚本的以下部分
<smpl>
@ depends on !context && patch && !org && !report @
expression x;
@@
- ERR_PTR(PTR_ERR(x))
+ ERR_CAST(x)
</smpl>
此 SmPL 摘录在标准输出上生成补丁 hunk,如下所示
diff -u -p a/crypto/ctr.c b/crypto/ctr.c
--- a/crypto/ctr.c 2010-05-26 10:49:38.000000000 +0200
+++ b/crypto/ctr.c 2010-06-03 23:44:49.000000000 +0200
@@ -185,7 +185,7 @@ static struct crypto_instance *crypto_ct
alg = crypto_attr_alg(tb[1], CRYPTO_ALG_TYPE_CIPHER,
CRYPTO_ALG_TYPE_MASK);
if (IS_ERR(alg))
- return ERR_PTR(PTR_ERR(alg));
+ return ERR_CAST(alg);
/* Block size must be >= 4 bytes. */
err = -EINVAL;
context
模式的详细描述¶
context
以类似于 diff 的样式突出显示感兴趣的行及其上下文。
注意:生成的类似于 diff 的输出不是适用的补丁。
context
模式的目的是突出显示重要的行(用减号-
注释),并提供一些周围的上下文行。 此输出可以与 Emacs 的 diff 模式一起使用以查看代码。
示例¶
运行
make coccicheck MODE=context COCCI=scripts/coccinelle/api/err_cast.cocci
将执行 SmPL 脚本的以下部分
<smpl>
@ depends on context && !patch && !org && !report@
expression x;
@@
* ERR_PTR(PTR_ERR(x))
</smpl>
此 SmPL 摘录在标准输出上生成 diff hunk,如下所示
diff -u -p /home/user/linux/crypto/ctr.c /tmp/nothing
--- /home/user/linux/crypto/ctr.c 2010-05-26 10:49:38.000000000 +0200
+++ /tmp/nothing
@@ -185,7 +185,6 @@ static struct crypto_instance *crypto_ct
alg = crypto_attr_alg(tb[1], CRYPTO_ALG_TYPE_CIPHER,
CRYPTO_ALG_TYPE_MASK);
if (IS_ERR(alg))
- return ERR_PTR(PTR_ERR(alg));
/* Block size must be >= 4 bytes. */
err = -EINVAL;
org
模式的详细描述¶
org
生成 Emacs 的 Org 模式格式的报告。
示例¶
运行
make coccicheck MODE=org COCCI=scripts/coccinelle/api/err_cast.cocci
将执行 SmPL 脚本的以下部分
<smpl>
@r depends on !context && !patch && (org || report)@
expression x;
position p;
@@
ERR_PTR@p(PTR_ERR(x))
@script:python depends on org@
p << r.p;
x << r.x;
@@
msg="ERR_CAST can be used with %s" % (x)
msg_safe=msg.replace("[","@(").replace("]",")")
coccilib.org.print_todo(p[0], msg_safe)
</smpl>
此 SmPL 摘录在标准输出上生成 Org 条目,如下所示
* TODO [[view:/home/user/linux/crypto/ctr.c::face=ovl-face1::linb=188::colb=9::cole=16][ERR_CAST can be used with alg]]
* TODO [[view:/home/user/linux/crypto/authenc.c::face=ovl-face1::linb=619::colb=9::cole=16][ERR_CAST can be used with auth]]
* TODO [[view:/home/user/linux/crypto/xts.c::face=ovl-face1::linb=227::colb=9::cole=16][ERR_CAST can be used with alg]]