Windows環境でclang(LLVM)でC++ソースコードからいい感じのコールグラフを作成するまでのメモ

# Windows環境でclang(LLVM)でC++ソースコードからいい感じのコールグラフを作成するまでのメモ
普段はMSVCを使っていて、コールグラフを作る場合はdoxygenなどを利用しているが、どうしても精度は低くなってしまう。  
clang(LLVM)でも作れるらしいとのことで、試行錯誤してみた。

## 前提
下記をインスールして、PATHを通しているものとする。

- LLVM 6.0.1 Pre-build
- Graphviz 2.38

## 基本的なやり方
こちら(https://yutopp.hateblo.jp/entry/2014/05/03/004049) などを参考にすると、以下のようなコマンドでコールグラフを作成することができるようだ。

```cmd
clang++ -S -emit-llvm main.cpp -o - | opt -analyze -dot-callgraph
dot -Tpng callgraph.dot > callgraph.f.png
```

## clangでコンパイルする
MSVCでコンパイルしているソースコード(プロジェクト)だと、まずclangでコンパイルできるようにする必要がある。
方法としては

- Visual Studio で調べたコマンドラインを、` clang-cl ...`で実行さる
- Visual Studio で調べたコマンドラインを、` clang-cl -v ...`で実行させて、 `clang`  のコマンドラインに変換する

### あるC++ソースでコールグラフを作成した結果
![callgraph1.png](https://qiita-image-store.s3.amazonaws.com/0/124276/dd2ebfc0-ce96-e957-5b5c-87461ef951bb.png)

## イマイチなコールグラフを調整する。
作成されたコールグラフはいろいろと問題がある。

1. 作成されたコールグラフの関数名はマングリングされている
2. 仮想的なノード("external code" / "Node????????")が邪魔
3. 組み込み関数("llvm*"/ "__*")が邪魔

### デマングルする
デマングリングしないととても見れたものではない。 .dotファイルを`Undname.exe` などでデマングリングさせる。  
しかし、.dotファイル内のラベルでは`<`と`>`は特殊文字らしいので、エスケープさせる必要がある。  
`sed`を使えばいいが、Windowsらしく`powershell`でエスケープ処理をする。  
面倒なことに、`opt` では既にエスケープされている場合がある(ラムダとか)ので、デマングル前に一旦エスケープを外す。

```cmd
REM {$_  -replace "\<","<" -replace "\>",">" }
powershell.exe -Command "Get-Content callgraph.dot | %% { $_ -replace \"\\^<\",\"^<\" -replace \"\\^>\",\"^>\" } " > callgraph1.dot 
REM { $_ -replace "<","\<" -replace ">","\>"  -replace "-\\>","->" } 
Undname.exe callgraph1.dot | powershell.exe -Command "$input | %% { $_ -replace \"\\^<\",\"^<\" -replace \"\\^>\",\"^>\" -replace \"^<\",\"\^<\" -replace \"^>\",\"\^>\" -replace \"-\\^>\",\"-^>\" } " > callgraph_demangled.dot
```

### 邪魔なノードを消す
DOT言語で特定のノードを消す(非表示にする)には、`[style=invis]`を指定すると消すことができるようだが、`opt`の引数や後処理などでうまいこと消すことができなかった。  
結局、LLVMのソースコードに手を入れた。

```patch
--- CallPrinter.cpp.bak 2017-06-06 20:49:48.000000000 +0900
+++ CallPrinter.cpp     2018-09-09 23:18:40.844019700 +0900
@@ -33,6 +33,15 @@

     return "external node";
   }
+
+  /// isNodeHidden - If the function returns true, the given node is not
+  /// displayed in the graph.
+  static bool isNodeHidden(CallGraphNode *Node){
+    if (Function *Func = Node->getFunction())
+      return Func->isIntrinsic() || Func->getName().startswith("__");
+    else
+         return true;
+  }
 };

 struct AnalysisCallGraphWrapperPassTraits {
```

## 改善した結果
![callgraph.png](https://qiita-image-store.s3.amazonaws.com/0/124276/2b8b753d-4f24-b940-072c-ea4d71d15552.png)

## 最終的なコマンド(バッチファイル)
```cmd
@setlocal
@set LLVM=D:\tmp\llvm-6.0.1.build\RelWithDebInfo
@set PATH=%LLVM%\bin;%PATH%
@set DOT=C:\usr\graphviz\bin\dot
@set CXXFILT="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\undname.exe"
@set PS=powershell.exe

@clang -cc1 -S -emit-llvm -D_DEBUG -D_MT -D_DLL -fcxx-exceptions -fexceptions -fms-volatile -v -D WIN32 -D _DEBUG -D _CONSOLE -D LLVM=C:/usr/LLVM -D _UNICODE -D UNICODE -internal-isystem "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Tools\\MSVC\\14.15.26726\\include" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17134.0\\ucrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17134.0\\shared" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17134.0\\um" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17134.0\\winrt" -O0 -Wno-error -fms-extensions -fms-compatibility -fms-compatibility-version=19.15.26726 -std=c++2a -fdelayed-template-parsing -fdiagnostics-show-option -fcolor-diagnostics -isystemC:/usr/llvm/include -o - -x c++ main.cpp | opt -dot-callgraph

@REM デマングル前に、エスケープされている"\<"を"<"に置換する
@%PS% -Command "Get-Content callgraph.dot | %% { $_ -replace \"\\^<\",\"^<\" -replace \"\\^>\",\"^>\" } " > callgraph1.dot 
@REM { $_ -replace "<","\<" -replace ">","\>"  -replace "-\\>","->" } 
%CXXFILT% callgraph1.dot | %PS% -Command "$input | %% { $_ -replace \"\\^<\",\"^<\" -replace \"\\^>\",\"^>\" -replace \"^<\",\"\^<\" -replace \"^>\",\"\^>\" -replace \"-\\^>\",\"-^>\" } " > callgraph_demangled.dot

@%DOT% -x -Tsvg -ocallgraph.svg callgraph_demangled.dot
@endlocal
```

## 残課題
1. デマングルを`LLVM`の中で出来るようにする。(`UnDecorateSymbolName` 関数を呼びだけでいいと思うけど。)
2. ラムダ関数がデマングルできていない。
3. STL内の関数を省けるようにしたい。
4. powershellの使い方が下手。