GraphVizとCMakeおよびClangを使用したCallgraph

Nov 24 2020

私の目標は、ビルド時にCMake + Clang + GraphVizを使用してコールグラフを生成することです。

これらを使用して[ 1、2 ]私は単純なグラフを作成できるプロセス。しかし、プロセスをCMakeプロジェクトに一般化する方法がわかりません。

実行可能なターゲットがあります。

add_executable(${TARGET} ${SOURCES})

マクロ内から、グラフに関連するオプションをターゲットに追加します。

target_compile_options(${TARGET} PRIVATE -S -emit-llvm)

そして、コールグラフを生成する追加のビルド後コマンドを追加します。

add_custom_command(
    TARGET ${TARGET}
    POST_BUILD
    COMMENT "Running clang OPT"
    COMMAND opt -analyze -dot-callgraph
)

しかし、clangはターゲットの実行可能ファイルを作成しようとします。これにより、次のエラーが発生します。

[build] lld-link: error: 
Container.test.cpp.obj: unknown file type

また、カスタムコマンド(optたとえば)が生成されたLLVM表現にどのようにアクセスするかも理解していません。私のカスタムコマンドが関連ファイルの知識を持っているようには見えません(上記のエラーが修正されたとしても)。


私がこれまでに理解していること:

  1. CMakeのではadd_executable追加-o outfile.exe、打ち鳴らすに引数をリンクプロセスに示されているのと同じ手順を行ってから、この防止に私を[ 1、2 ]
  2. $<TARGET_FILE:${TARGET}> clangから生成されたファイルを見つけるために使用できますが、これがLLVM表現で機能するかどうかはわかりません。
  3. 代わりにカスタムターゲットを実行しようとしましたがTARGET、すべての設定を持つすべてのソースをカスタムターゲットに取り込む際に問題が発生しました。
  4. ここで概説されているプロセス[ 3 ]は特に関連があるかもしれませんが-Wl,-save-temps、これはIRを取得するためのかなり回りくどい方法のようです(llvm-disを使用)。
  5. このunknown file typeエラーは、オブジェクトが実際にLLVM表現されていることが原因ですが、リンカが別の形式を予期していると思われます。
  6. リンカにLLVM表現を理解さ-fltoせるには、リンカオプションに追加しますtarget_link_options(${TARGET} PRIVATE -flto)(ソース[ 4 ])。これはすごいです。これはほぼ解決したことを意味します... cmakeで生成されたビットコード出力ファイルへのパスを取得する方法がわからないので、一度取得したら、それらをoptに渡すことができます(願っています)。 ..)。
  7. ターゲットオブジェクトを取得する$<TARGET_OBJECTS:${TARGET}>には、cmakeの場合に次のcmakeコマンドを使用できます。これにより、.o.ocmakeによる名前変更が原因ですか?)LLVMビットコードファイルが一覧表示されます。
  8. .oこの場合のファイルはビットコードoptですが、ツールはllvm表現にしか見えません。これに変換するにはllvm-dis bitcode.bc –o llvm_asm.ll。クロスコンパイルのため、マングルされたシンボルは奇妙な形式だと思います。llvm-cxxfiltたとえば、それらを渡すことは成功しませんllvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z
  9. したがって、8に対処します。これはMSVC名マングリング形式です。これは、Windowsでコンパイルするときに、clangがMSVC形式の名前マングリングを使用することを示しています。私にとっては驚きです...(出典[ 5 ])。
  10. LLVMに同梱されてllvm-undnameいるのは、シンボルを解体することができます。このツールを実行すると、生の入力を与えるとエラーが大幅に発生します。正しいシンボルでのみ機能するようです。このツールdemumbleは、クロスプラットフォームであり、llvm-undnameとllvm-cxxfiltのマルチフォーマットラッパーのようです。

11.私のほぼ機能しているcmakeマクロは次のとおりです。

macro (add_clang_callgraph TARGET)
    if(CALLGRAPH)
        target_compile_options(${TARGET} PRIVATE -emit-llvm)
        target_link_options(${TARGET} PRIVATE -flto) foreach (FILE $<TARGET_OBJECTS:${TARGET}>) add_custom_command( TARGET ${TARGET}
                POST_BUILD
                COMMAND llvm-dis ${FILE} COMMAND opt -dot-callgraph ${FILE}.ll
                COMMAND demumble ${FILE}.ll.callgraph.dot > ${FILE}.dot
            )
        endforeach()
    endif()
endmacro()

ただし、これは機能しません...の内容${FILE}は常にリスト全体です...

これはまだここに当てはまります:

foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>) add_custom_command( TARGET ${TARGET}
        POST_BUILD
        COMMAND echo ${FILE}
    )
endforeach()

結果は次のようになります。

thinga.obj;thingb.obj

これは、CMakeがforループが評価されるまでジェネレーター式を評価しないためです。つまり、ここにはループが1つだけあり、ジェネレーター式(解決されたジェネレーター式ではない)が含まれています(ソース[ 6 ])。これは、オブジェクトファイルをループして、オブジェクトファイルごとに一連のカスタムコマンドを作成できないことを意味します。


私は物事を見つけたら上記に追加します、私がプロセス全体を理解したら私は解決策を投稿します。

どんな助けでも大歓迎です、これは尻の大きな痛みでした。


私が望んでいるのは、CMakeに単一のLLVM表現ファイルへの実行可能ファイルのビルドを受け入れさせる方法です。そのファイルをoptで使用してコールグラフを取得し、でコンパイルを終了しllcます。クロスコンパイルしているので、少し制約があります。最終的には、同等のものなら何でもできます...

回答

1 compor Nov 24 2020 at 20:26

これまでのコメントの回答をすべて集めるためだけに回答を試みます。

CMakeを「破壊」したい場合は、次のような方法で実行できます(上記のOPのポイント4からここから採用)。

cmake_minimum_required(VERSION 3.0.2)

project(hello)

set(CMAKE_C_COMPILER clang)
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto") add_executable(hello main.c hello.c) # decide your bitcode generation method here # target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -c -flto) # this is just to print add_custom_target(print_hello_objs COMMAND ${CMAKE_COMMAND} -E echo $<JOIN:$<TARGET_OBJECTS:hello>," ">)

# this does some linking
# fill in details here as you need them (e.g., name, location, etc.)
add_custom_target(link_hello_objs 
  COMMAND llvm-link -o foo.bc $<TARGET_OBJECTS:hello> 
  COMMAND_EXPAND_LISTS)

各ファイルの処理が必要な用途では、COMMANDはそのリストを取得して.dotファイルを生成する外部スクリプト(bash / python)にすることができます。ジェネレータ式の問題は、CMakeで生成時まで評価されず、foreachコンテキストで評価されないことです。

再コンパイルされるオブジェクト/ビットコードファイルに基づいて再生成をトリガーする場合、CMakeにはツールチェーンのコンポーネント(コンパイラ、リンクなど)を呼び出す方法が事前に設定されているため、注意が必要です。そのため、CMakeベースのプロジェクトを書き戻しました。その後、しかし、まだ何に反対しているのかわからないように聞こえるので、最初はオーバーエンジニアリングを避けることを強くお勧めします。

このマシンのATMにはそのような設定がないので、実行可能ファイルも機能させるために、LTOを完全に機能させることに悩まされていません。

他のすべての要件(Graphviz出力、デマングリングなど)は、さらにカスタムターゲット/コマンドに接続できます。

他の解決策は次のとおりです。

  1. gllvm
  2. 絶望的なllvm-ir-cmake-utilsのために