2012年3月23日金曜日

Cppcheck を使ってみた

正しくは、「使ってみてたんだけど最近ようやく使えるようになった」です。

Cppcheck 1.53 にしてから、ずーと悩んでいたことが解決しちゃいました。
excluding 指定がうまくいかなくて悩んでいたのですが、
バージョンが上がったのを機に再チャレンジしたら、あっけなくできちゃいました。

前はダメで今回できたのはなぜだろうと調べてみたところ、こんな記事を見つけました。
CppCheckでフォルダを除外する
末尾に区切り文字が必要だったのですね。。。

とにかく、一歩前進できたのでブログにまとめておこうと思います。
※この記事では、Cppcheck 1.53 + Windows7 を想定しています。

基本的な使い方
詳しいことは、マニュアル見ましょう。
ですが、まずは簡単に解析をしてみたいと思います。

1つのファイルを解析
cppcheck --enable=all main.cpp
フォルダ以下を解析
cppcheck --enable=all folder

--enable=all ですべての警告を報告するように指定しています。
これが無いと何も教えてくれません。(詳細はマニュアル参照)
解析対象はフォルダ・ファイルどちらも OK です。
標準入力からの指定も可能なので、このようなこともできます。

ファルダ内の特定ファイルを解析
dir /s /b Gx*.cpp | cppcheck --enable=all --file-list=-
これで、カレントディレクトリ以下の Gx*.cpp にマッチするファイルが解析されます。

特定のフォルダ/ファイルを無視する
冒頭でも触れましたが、cppcheck --enable=all myproject の用にプロジェクトフォルダ以下すべてを解析するように設定していると、テストフォルダやメンテナンスされていないフォルダなど解析してほしくないところがでてきます。
その場合、-i オプションを使用します。
cppcheck --enable=all -imyproject\test\ myproject
末尾に区切り文字が必要です!

これで、myproject\test\ フォルダを除く myproject フォルダを解析できます。

期待する結果が得られない場合
この時点で、期待する結果が得られない場合 --force オプションを使用してみてください。
デフォルトでは --max-configs が 12 となっているため、#ifdef などを多用していると目的のコードに達する前に解析が終わってしまいます。

Jenkins との連携
Cppcheck のプラグインがあるので使ってみました。
Cppcheck の出力xmlを利用するので、xml を出力させます。
cppcheck --enable=all --xml myproject 2> result.xml
--xml を指定すると標準エラーにxmlが吐き出されるので、ファイルにリダイレクトさせてます。

実際に実行してみた画像がこちら。
Cppcheck のアイコンが追加されてますね。
エラー内容はもちろん、エラー数の推移が見れたりします。
収集結果やグラフなどのサンプルがあるので、それらは Cppcheck Plugin の Wiki をご覧下さい。


さて、こちらの画面ですが、実は所要時間が2時間超となってます。
解析対象の iutest は、コード量は少なめです。
これを仕事のプロジェクトでやったらどんだけかかってしまうのか?!という状態です。

そうなると、解析時間の短縮をしたくなりますよね。

並列処理
マニュアルを見てみると [2.7. Multithreaded checking] という項目があります。
これで時間短縮になるんじゃないかと思ったのです。が、
$ cppcheck -j4 --enable=style main.cpp
No thread support yet implemented for this platform.
Windows じゃ使えないっぽいです。残念。。。
2013/01/17 追記:バージョン 1.58 から使えるようになりました。

ちなみに、並列オプションを使うと未使用関数の検出ができないようです。
$ cppcheck -j4 --enable=all main.cpp
cppcheck: unusedFunction check can't be used with '-j' option, so it's disabled.
No thread support yet implemented for this platform.
ま、どのみち Windows ではサポートされてませんが。。。

Configuration
解析時間の短縮のために、解析したい箇所を限定します。
方法としては、 -D や -U オプションを使います。
-D オプションは #define 定義をするオプションです。
-D によって定義されたプリプロセッサディレクティブを解析するようになります。
-U オプションは、-D オプションの逆で #undef のようなものです。
-U で指定したマクロ名(ID)のディレクティブを解析から除外します。※1

例えば、Windows 向けに開発している場合、
cppcheck --enable=all -f -D_WIN32 -D_MSC_VER=1500 myproject
のように限定できます。

ここで注意が必要なのは -D オプションを使う場合、目的のコードに必要な定義をしっかりしないと解析されないことがあることです。
もし、-D オプションを使ってみて目的の警告が検出されなくなってしまったら、定義を見なおしてみると良いかもしれません。

では、実際に試してみた画面がこちら。


所要時間はなんと3分ちょっと
2時間が3分!
これなら、ビルド毎に Cppcheck を走らせても問題なさそうですよね。

アップしている画像だとわかりにくいですが、警告も変わらずです。
エラーIDから無視
再度、エラー無視の方法です。今度はエラーID、ファイル、行番号から設定します。
それには --suppress または、--suppressions-list オプションを使います。
cppcheck --enable=all --suppress=<error id>:[filename]:[line] myproject
<error id > にはエラーIDを指定します。(uninitVar など)
[filename],[line] はオプションで、指定した場合指定ファイルの指定行のエラーを無視します。

実は [filename] の記述の仕方がイマイチわかってません。。。
ファイル名や絶対パス、相対パスと試してみたのですが、うまくいかず…
しかたがないので、今はワイルドカードを使って指定しています。
cppcheck --enable=all --suppress=uninitVar:*test.cpp myproject

--suppressions-list の場合、上記のエラー無視指定を1行ずつ書いたテキストファイルを指定できます。

日本語対応
日本語を含むファイル名がダメらしいが、それとは違う話。
Visual C++ では、C# の #region のように使える #pragma region があります。
#pragma region 名前
void func()
{
   // ながーいコード
}
#pragma endregion
このコードを解析すると以下のようにエラーになってしまいます。
(error) The code contains characters that are unhandled. Neither unicode nor extended ASCII are supported. 
region名 に日本語があるとエラーになります。

日本語使うな!という話ではありますが、ライブラリ側でこれがあったりすると面倒です。
なので、これは無視しちゃいます。
上記のエラーの種類は syntaxError なのでこれを無視するように設定します。
cppcheck --enable=all --suppress=syntaxError myproject
他の syntaxError も無視することになってしまいますが、そもそも syntaxError ならコンパイルエラーになるはずなので Cppcheck で検出できなくても問題ないと思います。

まとめ
有償の静的解析ツールは高機能ですが、みんながみんな導入できるとは限らないでしょう。(お金がね…)
そんな時は、Cppcheck を使ってみてはどうでしょうか?
Cppcheck でも十分間違いを指摘してくれますし、Jenkins のプラグインを使えば結果も一目瞭然です。

なにもやらないよりはやった方が絶対イイので、やりましょう!Cppcheck。



※1. きちんと調べたわけではないが、#ifdef ID の場合は除外されるが、#if defined(ID) とか #ifndef ID 場合は意図通りになってくれない気がする。


追記
Visual Studio との連携 > Cppcheck + Visual Studio

0 件のコメント:

コメントを投稿