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
依次运行报告模式和上下文模式。它应该与 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 命令行中提供了与编译内核时使用的选项匹配的 include 选项。您可以通过使用 V=1 来了解这些选项是什么;然后,您可以手动运行添加了调试选项的 Coccinelle。
或者,您可以通过请求将 stderr 重定向到 stderr 来调试针对 SmPL 补丁运行的 Coccinelle。默认情况下,stderr 被重定向到 /dev/null;如果您想捕获 stderr,您可以为 coccicheck 指定 DEBUG_FILE="file.txt"
选项。例如:
rm -f cocci.err
make coccicheck COCCI=scripts/coccinelle/free/kfree.cocci MODE=report DEBUG_FILE=cocci.err
cat cocci.err
您可以使用 SPFLAGS 添加调试标志;例如,您可能希望在调试时向 SPFLAGS 添加 --profile --show-trying
。例如,您可能想使用:
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 覆盖任何内核的 .coccicheck 设置。
当针对 Linux 使用 Coccinelle 时,我们通过自己的 Linux .cocciconfig 文件为 Linux 提供一组合理的默认选项来帮助 Coccinelle。这提示 Coccinelle 可以使用 git 进行 git grep
查询(通过 coccigrep)。 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 摘录在标准输出上生成补丁块,如下所示
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 块,如下所示
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]]