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 前端。

定義了四種基本模式:patchreportcontextorg。 要使用的模式透過設定 MODE 變數來指定,例如 MODE=<mode>

  • patch 儘可能提出修復方案。

  • report 生成以下格式的列表:file:line:column-column: message

  • context 以類似於 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]]