PVS-Studioを使用したHPXライブラリの静的分析

この記事は、あらゆる規模の分散/並列コンピューティングのためのHPX -C ++ライブラリ上でPVS-Studioを使用した経験に関するHartmouth Kaiser ブログ投稿の翻訳です。



HPX用のPVS-Studioアナライザーの試用版を既に使用した後、その冗長性のためにそれを思い出しました。 最近、このユーティリティに関する多くの記事がありました。 その使用から多くの時間が経過したため、開発者に連絡して、オープンソース製品をサポートする準備ができているかどうかを確認することにしました。 発見された問題に関する記事と引き換えに1年間ライセンスを受け取ったとき、私たちはとても幸せでした。



一般的な印象



PVS-Studio V5.26をダウンロードした 、Visual Studio 2013 Professionalの拡張機能としてインストールするのに問題はありませんでしたが、メインの開発環境であるバージョン2015RC1でテストすることをお勧めします。 残念ながら、VS 2015は現在サポートされていませんが、Microsoftの新しいリリースの直後にこれが行われることを願っています。



Visual StudioでのPVS-Studioの統合は非常に良い印象を与えました。 追加のメニューボタンを使用すると、すべてのコマンドとオプションにアクセスできます。 すべての診断メッセージは、ソースコードに直接アクセスできる特別なウィンドウに表示されます。 Webベースの状況依存ヘルプを開いて、各診断を個別に説明することもできます。 すべて私が望むように。



メッセージには、高、中、低の3つの異なる重大度レベルがあり、さらに、一般、最適化、64ビット互換性分析の3つのカテゴリの分析にグループ化されます。 ユーザーインターフェイスでは、表示される診断を制限し、結果をフィルタリングできます。 メインHPXモジュールの場合、アナライザーは約1000個の.hおよび.cppファイル(約140,000行のコード)から約70のメッセージを生成しましたが、これは悪いことではありません(重大度:5、中:44、低:21)。 私のラップトップでの最初の分析には約10分かかりました。



エラーの例



HPXの隠れたバグを見たいと思いました。 私たちのチームはコードの品質に非常に敬意を表しており、メインブランチへのマージの前に、少なくとも1人の開発者が各リクエストプールをレビューしています。 そのため、アナライザーは何も検出しないと確信していました。



まず、重大度の高いエラーを見てみましょう。 それらの4つは非常に似ており、共通の性質がありました。



template <typename Archive> void load(Archive& ar) { actions::manage_object_action_base* act = 0; ar >> hpx::serialization::detail::raw_ptr(act); // V522: Dereferencing of the null pointer 'act' might take place. HPX_ASSERT(act->is_valid()); // ... }
      
      







このコードは、基本クラスへのポインターを介してポリモーフィックオブジェクトを逆シリアル化します。 raw_ptr(act)が動的に新しいオブジェクトを作成し、渡されたポインターにアドレスを割り当てることを知っています。 また、エラーが発生した場合、関数が例外をスローすることもわかっています。 これはすべて、静的アナライザーによって次のように表示されます。 HPXのシリアル化モジュール全体がヘッダーにあります。 アナライザーは、明らかにこれを認識せず、エラーを生成しました。 幸い、ワンクリックでこのエラーを無視するようにPVS-Studioに明示的に指示できます。これにより、//-V522-非常に便利な魔法のコメントが追加されます。 さらに、PVS-Studioは、ファイルまたはディレクトリ、ファイル名のパターンに基づいてメッセージを抑制するための多くのオプションを提供します-すべてのオプションは簡単にアクセスでき、説明が豊富です。



2番目のミスは私を驚かせました:



 #define HPX_VERSION_MAJOR 0 #define HPX_VERSION_MINOR 9 #define HPX_VERSION_SUBMINOR 11 std::string full_version_as_string() { // V609 Mod by zero. Denominator '0' == 0. return boost::str( boost::format("%d.%d.%d") % HPX_VERSION_MAJOR % HPX_VERSION_MINOR % HPX_VERSION_SUBMINOR); }
      
      







アナライザーが何を言いたかったのかを理解するには、時間がかかりました。 Boost.Formatのoperator%は、私にはまったく目立たないように見えました。 ただし、operator%がオーバーロードされておらず、コードに実際に問題がある場合でも、エラーメッセージはわかりにくくなります。 メッセージを抑制することで、同じ方法でこの問題を「解決」しました。



最後の「高重大度」メッセージは、最適化の結果でした:



 // V808 'hostname' object of 'basic_string' type was created // but was not utilized. std::string hostname = boost::asio::ip::host_name();
      
      







アナライザーは正しかったので、このコンテキストではホスト名変数はまったく使用されませんでした。 20を超えるプラットフォームでの定期的なテストに使用しているコンパイラは、これを発見したことはありません-素晴らしい点です!



重大度が中および低のメッセージは、符号付き整数から大きな符号なし整数への暗黙的なキャスト(たとえば、int32_t-> uint64_t)など、良性の32ビット/ 64ビット互換性の問題に主に関連していました。



しかし、2つのエラーが実際のバグであることが判明しました。



 int runtime_support::load_components(util::section& ini) { // load all components as described in the configuration information if (!ini.has_section("hpx.components")) { // V601 The 'true' value is implicitly cast to the integer type. return true; // no components to load } // ... }
      
      







メッセージ自体は問題を完全に示しています。関数の戻り値の型をboolからintに変更すると、戻り式の1つを変更するのを忘れてしまいました。 将来的には、これにより再現が難しい問題が生じる可能性があります。



別の間違いはより深刻であることが判明しました。



 struct when_each_frame { // ... private: // V690 Copy constructor is declared as private in the // 'when_each_frame' class, but the default '=' operator // will still be generated by compiler. It is dangerous // to use such a class. when_each_frame(); when_each_frame(when_each_frame const&); public: // ... };
      
      







特に、デフォルトの実装を誤ってインスタンス化したgccの古いバージョンの回避策としてコンストラクター宣言を追加したため、これは本当に素晴らしい点です。



最終的に、HPXコードベース全体のアナライザーの実行に時間を費やすことができてうれしかったです。 ずいぶん前に導入したレビューポリシーの確認として、深刻な問題が見つからなかったことを嬉しく思いました。 また、コミットのたびに開始する連続した統合プロセスにPVS-Studioを導入することにしました。



おわりに



静的解析は間違いなく重要です。 PVS-Studioのようなユーティリティを起動するには、多少の時間と労力がかかりますが、私の意見では、時間とお金の正しい投資です。 コンパイラは、PVS-Studioが行う詳細な分析には適していません。 そうしないと、コンパイル時間が大幅に長くなります。



私たちにとって最も便利な機能の1つは、CIプロセスへの簡単な統合でした。 さまざまなプラットフォーム(Windowsを含む)で毎日テストを実行しているため、分析結果は他のOS(Linux、Mac OS)で作業している開発者が利用できます。



PVS-Studioのもう1つのオプションは、プロジェクトのビルドが成功した後に毎回分析を開始することです。 驚いたことに、これは通常のビルドプロセスに大きなオーバーヘッドを追加せず、実装のごく初期の段階で微妙なコードの問題に関するフィードバックを提供します。 プロセスでのツールの有用性を評価するために、オプションをオンのままにしました。



生成されたメッセージを分析する際、ユーティリティには可能な限り詳細な分析の時間がありますが、PVS-Studioはデフォルト以外のセマンティクスを決定するために特定の演算子のオーバーロードを分析できませんでした。 Boost.Formatのoperator%の例がこれを示しています。 私は同意します-これは本当に非常にデリケートな瞬間であり、この場合に正しい診断を提供できるかどうかはわかりません。 一方、ここでは、コードのセマンティクスの詳細な分析において、ツールにすべてのパワーが必要です。



HPXに興味がある場合は、 githubでライブラリをフォークしてください。



All Articles