web-dev-qa-db-ja.com

C ++コードの呼び出しグラフを生成する方法

特定の関数にヒットしている可能性のあるすべての実行パスを見つけるための呼び出しグラフを生成しようとしています(この関数につながる多くのパスがあるため、すべてのパスを手動で把握する必要はありません) )。例えば:

path 1: A -> B -> C -> D  
path 2: A -> B -> X -> Y -> D  
path 3: A -> G -> M -> N -> O -> P -> S -> D  
...  
path n: ...

CodevizとDoxygenを試しましたが、どちらの結果もターゲット関数Dの呼び出し先以外は何も表示されません。私の場合、Dはオブジェクトがスマートポインタでラップされるクラスのメンバー関数です。クライアントは、Dを呼び出すために、常にファクトリーを通じてスマートポインターオブジェクトを取得します。

誰もこれを達成する方法を知っていますか?

78
shiouming
static void D() { }
static void Y() { D(); }
static void X() { Y(); }
static void C() { D(); X(); }
static void B() { C(); }
static void S() { D(); }
static void P() { S(); }
static void O() { P(); }
static void N() { O(); }
static void M() { N(); }
static void G() { M(); }
static void A() { B(); G(); }

int main() {
  A();
}

それから

$ clang++ -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
$ dot -Tpng -ocallgraph.png callgraph.dot

光沢のある画像を生成します(mainには外部リンケージがあり、その翻訳単位の外部からも呼び出される可能性があるため、「外部ノード」があります)。

Callgraph

c++filtを使用してこれを後処理すると、関連する関数とクラスのマングルされていない名前を取得できます。次のように

#include <vector>

struct A { 
  A(int);
  void f(); // not defined, prevents inlining it!
};

int main() {
  std::vector<A> v;
  v.Push_back(42);
  v[0].f();
}

$ clang++ -S -emit-llvm main1.cpp -o - |
   opt -analyze -std-link-opts -dot-callgraph
$ cat callgraph.dot | 
   c++filt | 
   sed 's,>,\\>,g; s,-\\>,->,g; s,<,\\<,g' | 
   gawk '/external node/{id=$1} $1 != id' | 
   dot -Tpng -ocallgraph.png    

この美しさをもたらします(ああ、最適化が有効になっていないサイズが大きすぎました!)

Beauty

その神秘的な名前のない関数Node0x884c4e0は、定義が不明な関数によって呼び出されると想定されるプレースホルダーです。

107

それを実現するには、doxygenを使用します(グラフ生成にドットを使用するオプションを使用)。

enter image description here

Johannes Schaub-litb main.cppを使用すると、これが生成されます。

enter image description here

doxygen/dotはおそらくclang/optよりもインストールと実行が簡単です。自分でインストールすることができなかったので、代替ソリューションを見つけようとしました。

14
jpo38

正確なC++コールグラフを静的に計算するのは困難です。正確な言語パーサー、正しい名前検索、および言語のセマンティクスを適切に尊重する適切なポイントツーアナライザーが必要だからです。 Doxygenにはこれらのいずれも含まれていません。なぜ人々がC++で気に入っていると主張するのかわかりません。 Doxygenが誤って分析する10行のC++サンプルを作成するのは簡単です)。

コールグラフを動的に収集するタイミングプロファイラー (これは私たちのものを説明しています)を実行し、多くの場合を実行する方が良いかもしれません。このようなプロファイラーは、実際に実行されたコールグラフを表示します。

編集:私は突然 C++の理解 を思い出しました。これはコールグラフを構築すると主張しています。パーサーに何を使用しているのか、または詳細な分析を正しく行うかどうかはわかりません。私は彼らの製品に特別な経験はありません。

Clangを使用したSchaubの答えに感心しました。 Clangはすべての要素を正しく持っていると思います。

6
Ira Baxter

CppDepend を使用すると、多くの種類のグラフを生成できます

  • 依存関係グラフ
  • コールグラフ
  • クラス継承グラフ
  • 結合グラフ
  • パスグラフ
  • すべてのパスグラフ
  • サイクルグラフ

enter image description here

5
Issam

clang++コマンドがmpi.hなどの標準ヘッダーファイルを見つけるには、2つの追加オプションを使用する必要があります-### -fsyntax-only、つまり、完全なコマンドは次のようになります。

clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
2
mabalenk

「C++ Bsc Analyzer」は、bscmakeユーティリティによって生成されたファイルを読み取ることにより、コールグラフを表示できます。

1
Resonantium

doxygen + graphvizは、次に人手に渡されるコールグラフを生成するときに、ほとんどの問題を解決できます。

0
Crawl.W

Scitools Understandfantasticツールであり、私が知っているすべてのものよりも優れていますリバースエンジニアリング、および高品質グラフを生成します

しかし、それは非常に高価であり、試用版にはバタフライコールグラフがあります1レベルのコールに限定されていることに注意してください(私は彼らがそうするのを助けていないと信じています...)

0
franckspike