Callgraphs mit GraphViz mit CMake und Clang
Mein Ziel ist es, Anrufgraphen mit CMake + Clang + GraphViz zur Erstellungszeit zu generieren.
Mit diesen [ 1 , 2 ] Prozessen kann ich einfache Diagramme erstellen. Ich bin mir jedoch nicht sicher, wie ich den Prozess auf ein CMake-Projekt verallgemeinern soll.
Ich habe ein ausführbares Ziel.
add_executable(${TARGET} ${SOURCES})
Welche innerhalb eines Makros füge ich dem Ziel die graphrelevanten Optionen hinzu:
target_compile_options(${TARGET} PRIVATE -S -emit-llvm)
Fügen Sie einen zusätzlichen Post-Build-Befehl hinzu, der die Aufrufdiagramme generiert:
add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMENT "Running clang OPT"
COMMAND opt -analyze -dot-callgraph
)
Der Clang versucht jedoch, eine ausführbare Datei für das Ziel zu erstellen. Dies führt zu diesem Fehler:
[build] lld-link: error:
Container.test.cpp.obj: unknown file type
Ich verstehe auch nicht, wie ein benutzerdefinierter Befehl ( opt
zum Beispiel) auf die erzeugte LLVM-Darstellung zugreifen würde. Es sieht nicht so aus, als ob mein benutzerdefinierter Befehl die relevanten Dateien kennt (selbst wenn der obige Fehler behoben wurde).
Was ich bisher verstehe:
- CMake
add_executable
fügt das-o outfile.exe
Argument zu clang hinzu. Dies hindert mich daran, die gleichen Schritte auszuführen, die in den verknüpften Prozessen gezeigt werden [ 1 , 2 ]. $<TARGET_FILE:${TARGET}>
kann verwendet werden, um die erzeugten Dateien von clang zu finden, aber ich weiß nicht, ob dies für die LLVM-Darstellung funktioniert.- Ich habe stattdessen versucht, ein benutzerdefiniertes Ziel zu erstellen, hatte jedoch Probleme, alle
TARGET
Quellen mit allen Einstellungen in das benutzerdefinierte Ziel zu übernehmen. - Der hier beschriebene Prozess [ 3 ] mag besonders relevant sein,
-Wl,-save-temps
aber dies scheint ein ziemlich umständlicher Weg zu sein, um IR zu erhalten (unter Verwendung von llvm-dis). - Der
unknown file type
Fehler ist darauf zurückzuführen, dass das Objekt tatsächlichLLVM
dargestellt wird, aber ich vermute, dass der Linker ein anderes Format erwartet. - Fügen Sie den Linker-Optionen hinzu (Quelle [ 4 ]), damit der Linker die
LLVM
Darstellung versteht . Das ist großartig, weil es bedeutet, dass ich das fast gelöst habe ... Ich weiß nur nicht, wie ich den Pfad zu den produzierten Bitcode-Ausgabedateien in cmake bekommen soll. Sobald ich das tue, kann ich sie an opt übergeben (ich hoffe). ..).-flto
target_link_options(${TARGET} PRIVATE -flto)
- Um die Zielobjekte abzurufen, kann
$<TARGET_OBJECTS:${TARGET}>
im Fall von cmake der folgende Befehl cmake verwendet werden. Hier werden die LLVM-Bitcodedateien aufgelistet.o
(liegt das.o
an einer Umbenennung durch cmake?). - Die
.o
Datei ist in diesem Fall Bitcode, dasopt
Tool wird jedoch nur in einer llvm-Darstellung angezeigt. Um dies zu konvertierenllvm-dis bitcode.bc –o llvm_asm.ll
. Aufgrund der Kreuzkompilierung glaube ich, dass das verstümmelte Symbol ein seltsames Format hat. Ihre Weitergabellvm-cxxfilt
ist beispielsweise nicht erfolgreichllvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z
- Adressierung 8. Dies ist also ein MSVC-Format für die Namensverfälschung. Dies zeigt an, dass beim Kompilieren unter Windows Clang der Name Mangling im MSVC-Format verwendet wird. Eine Überraschung für mich ... (Quelle [ 5 ]).
- LLVM
llvm-undname
wird mitgeliefert und kann die Symbole entwirren. Dieses Tool, wenn ich es ausführe, weist erhebliche Fehler auf, wenn ich ihm eine Rohdateneingabe gebe. Es scheint nur mit korrekten Symbolen zu funktionieren. Das Tooldemumble
scheint ein plattformübergreifender Multiformat-Wrapper von llvm-undname und llvm-cxxfilt zu sein.
11. Mein fast funktionierendes cmake-Makro lautet wie folgt:
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()
Dies funktioniert jedoch nicht ... Der Inhalt von ${FILE}
ist immer die gesamte Liste ...
Dies ist hier immer noch der Fall:
foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>) add_custom_command( TARGET ${TARGET}
POST_BUILD
COMMAND echo ${FILE}
)
endforeach()
Das Ergebnis sieht aus wie:
thinga.obj;thingb.obj
Dies liegt daran, dass CMake den Generatorausdruck erst auswertet, nachdem die for-Schleife ausgewertet wurde. Das heißt, es gibt hier nur eine Schleife, die den Generatorausdruck (keinen aufgelösten Generatorausdruck) enthält (Quelle [ 6 ]). Dies bedeutet, dass ich keine Objektdateien durchlaufen und für jede Objektdatei eine Reihe von benutzerdefinierten Befehlen erstellen kann.
Ich werde das oben Gesagte ergänzen, wenn ich etwas herausfinde. Wenn ich den gesamten Prozess herausfinde, werde ich eine Lösung veröffentlichen.
Jede Hilfe wäre sehr dankbar, das war ein großer Schmerz im Arsch.
Was ich mir erhoffe, ist eine Möglichkeit, CMake dazu zu bringen, das Erstellen einer ausführbaren Datei für eine einzelne LLVM-Repräsentationsdatei zu akzeptieren. Verwenden Sie diese Datei mit opt, um den Callgraph abzurufen, und beenden Sie dann die Kompilierung mit llc
. Ich bin allerdings ein wenig eingeschränkt, da ich Cross-Compilieren bin. Letztendlich wird alles Äquivalente tun ...
Antworten
Ich werde versuchen, eine Antwort zu finden, um alle meine bisherigen Kommentarantworten zu sammeln.
Wenn Sie CMake "untergraben" möchten, können Sie dies mit etwas Ähnlichem tun (angepasst von hier aus OPs Punkt 4 oben):
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)
Für Anwendungen, bei denen die Verarbeitung für jede Datei erforderlich ist, COMMAND
kann dies ein externes Skript (Bash / Python) sein, das nur diese Liste verwendet und die DOT-Dateien generiert. Das Problem mit Generatorausdrücken besteht darin, dass sie erst in CMake und nicht in einem foreach
Kontext bis zur Generierungszeit ausgewertet werden .
Wenn Sie die Regeneration basierend auf der neu kompilierten Objekt- / Bitcodedatei auslösen möchten, wird es schwierig, da CMake voreingestellte Möglichkeiten zum Aufrufen der Komponenten einer Toolchain (Compiler, Link usw.) hat. Deshalb habe ich mein CMake-basiertes Projekt zurückgeschrieben dann, aber ich würde dringend empfehlen, zu Beginn ein Überengineering zu vermeiden, da es so klingt, als ob Sie sich noch nicht sicher sind, was Sie erwartet.
Ich habe mich nicht darum gekümmert, LTO vollständig zum Laufen zu bringen , um auch eine funktionierende ausführbare Datei zu erhalten, da ich auf diesem Geldautomaten kein solches Setup habe.
Alle anderen Anforderungen (z. B. Graphviz-Ausgabe, Entwirrung) können mit weiteren benutzerdefinierten Zielen / Befehlen verknüpft werden.
Andere Lösungen könnten sein:
- gllvm
- für die verzweifelten llvm-ir-cmake-utils