3

Cプロジェクトで静的分析を実行して、デッドコード、つまり、呼び出されることのない関数やコード行を特定しようとしています。このプロジェクトは、Windows用のVisual Studio .Net、またはLinux用のgccを使用してビルドできます。私は私のためにこれを行うことができるいくつかの合理的なツールを見つけようとしてきましたが、今のところ私は成功していません。Stack Overflowに関する関連する質問、つまりこれこれを読み、gccで使用しようとしまし-Wunreachable-codeたが、gccでの出力はあまり役に立ちません。次の形式です

/home/adnan/my_socket.c: In function ‘my_sockNtoH32’: 
/home/adnan/my_socket.c:666: warning: will never be executed

しかし、my_socket.cの666行目を見ると、実際には関数my_sockNtoH32()から呼び出されている別の関数内にあり、この特定のインスタンスでは実行されませんが、他の関数から呼び出されたときに実行されます。

私が必要としているのは、決して実行されないコードを見つけることです。誰かplzはこれを手伝うことができますか?

PS:このタスク用のツールを購入するように経営陣を説得することはできないので、フリー/オープンソースツールに固執してください。

4

3 に答える 3

3

GCC がうまくいかない場合は、clang (より正確には、静的アナライザー) を試してください。GCC よりもはるかに優れた静的分析を備えています (そして、はるかに優れた出力を生成します)。Apple の Xcode で使用されていますが、オープンソースであり、個別に使用できます。

于 2011-06-17T15:34:34.243 に答える
1

GCC は、コンパイルのデッド コードを見つけるのに役立ちます。複数のコンパイル単位でデッド コードを検出できるとしたら、私は驚きます。コンパイル ユニット内の関数または変数のファイル レベル宣言は、他のコンパイル ユニットがそれを参照する可能性があることを意味します。したがって、ファイルの最上位で宣言されたものはすべて、GCC は一度に 1 つのコンパイル単位しか認識しないため、削除できません。

問題が難しくなります。コンパイル単位 A が関数 a を宣言し、コンパイル単位 B が a を呼び出す関数 b を持っているとします。死人ですか?一見、いいえ。しかし、実際には状況によります。b が死んでいて、a への唯一の参照が b にある場合、a も死んでいます。b が単に& a を取り、それを配列 X に入れると、同じ問題が発生します。 a が死んでいるかどうかを判断するには、システム全体でポイントツー分析を行い、a へのポインターがどこでも使用されているかどうかを確認する必要があります。

この種の正確な「デッド」情報を取得するには、コンパイル ユニットのセット全体のグローバル ビューが必要であり、ポイント ツー分析を計算し、その後、そのポイント ツー分析に基づいてコール グラフを構築する必要があります。 . 関数 a は、コール グラフ (ルートとして main を持つツリーとして) がどこかでそれを参照しない場合にのみ、死んでいます。(いくつかの注意が必要です: 分析が何であれ、実際問題としては保守的でなければならないため、分析の完全なポイントでさえ、機能が死んでいると正しく識別できない場合があります。また、外部からの C アーティファクトの使用についても心配する必要があります。 C 関数のセット (たとえば、アセンブラ コードの一部からの呼び出し)。

スレッディングはこれをさらに悪化させます。各スレッドには、おそらく呼び出し DAG の先頭にあるいくつかのルート関数があります。スレッドがどのように開始されるかは C コンパイラによって定義されていないため、マルチスレッド C アプリケーションにデッド コードがあるかどうかを判断するには、何らかの方法で分析をスレッド ルート関数に通知するか、次の方法でそれらを発見する方法を通知する必要があることは明らかです。スレッド初期化プリミティブを探しています。

正しい答えを得る方法について、多くの回答が得られていません。オープン ソースではありませんが、C フロント エンドを備えたDMS Software Reengineering Toolkitには、C パーサー、制御およびデータフロー分析、ローカルおよびグローバル ポイントツー分析、グローバル コール グラフ構築など、これを行うためのすべての機構が備わっています。 . DMS は、アセンブラーからの外部呼び出し、および/またはスレッド初期化呼び出しであるスレッド ルートまたは特定のソース パターンのリストなどの追加情報を含めるように簡単にカスタマイズできます。いくつかの大規模な組み込みエンジン コントローラーについては、実際にそれを (簡単に) 実行しました。何百万行ものコードで。DMS は、このようなコール グラフを作成する目的で、2,600 万行のコード (約 18,000 のコンパイル ユニット) にも及ぶシステムに適用されています。

[余談ですが、個々のコンパイル ユニットを処理する際に、DMS はスケーリング上の理由から、そのコンパイル ユニットで使用されていないシンボルと関連コードを実質的に削除します。インクルード ファイルのネストに通常隠れている氷山を考慮すると、驚くべきことに、これによりコードの約 95% が削減されます。Cソフトウェアは通常、インクルードファイルを十分に分解していないと述べています。皆さんはすでにそれを知っていると思います。]

GCC などのツールは、コンパイル中にデッド コードを削除します。これは役に立ちますが、デッド コードはコンパイル ユニットのソース コードにまだ散らばっていて、開発者の注意を奪っています (デッド コードかどうかも確認する必要があります)。プログラム変換モードの DMS は、一部のプリプロセッサの問題をモジュロとして、そのデッド コードをソースから実際に削除するように構成できます。非常に大規模なソフトウェア システムでは、手動でこれを行うことはあまり望ましくありません。

于 2011-07-06T07:45:51.190 に答える
1

GCC が「決して実行されない」と言うとき、それはそれを意味します。実際には、そのデッド コードを作成するバグがある可能性があります。たとえば、次のようなものです。

if (a = 42) {
    // some code
} else {
    // warning: unreachable code
}

もちろん、コードを見なければ具体的なことは言えません。

666 行目にマクロがある場合、GCC がそのマクロの一部も参照している可能性があることに注意してください。

于 2011-06-17T19:24:40.967 に答える