Callgraphs utilisant GraphViz avec CMake et Clang
Mon objectif est de générer des graphiques d'appels en utilisant CMake + Clang + GraphViz au moment de la construction.
En utilisant ces [ 1 , 2 ] processus, je peux créer des graphiques simples. Mais, je ne sais pas comment généraliser le processus à un projet CMake.
J'ai une cible exécutable.
add_executable(${TARGET} ${SOURCES})
Lequel à partir d'une macro, j'ajoute les options pertinentes du graphique à la cible:
target_compile_options(${TARGET} PRIVATE -S -emit-llvm)
Et, ajoutez une commande post build supplémentaire qui génère les graphes d'appel:
add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMENT "Running clang OPT"
COMMAND opt -analyze -dot-callgraph
)
Mais le cliquetis tente de créer un exécutable pour la cible. Cela entraîne cette erreur:
[build] lld-link: error:
Container.test.cpp.obj: unknown file type
Je ne comprends pas non plus comment une commande personnalisée ( opt
par exemple) accéderait à la représentation LLVM produite. Il ne semble pas que ma commande personnalisée ait aucune connaissance des fichiers pertinents (même si l'erreur ci-dessus a été corrigée).
Ce que je comprends jusqu'à présent:
- CMake
add_executable
ajoute l'-o outfile.exe
argument à clang, cela m'empêche de faire les mêmes étapes que celles indiquées dans les processus liés [ 1 , 2 ] $<TARGET_FILE:${TARGET}>
peut être utilisé pour trouver les fichiers produits à partir de clang, mais je ne sais pas si cela fonctionne pour la représentation LLVM.- J'ai essayé de créer une cible personnalisée à la place, mais j'ai eu des problèmes pour obtenir toutes les
TARGET
sources avec tous les paramètres dans la cible personnalisée. - Le processus décrit ici [ 3 ] pourrait être particulièrement pertinent,
-Wl,-save-temps
mais cela semble être une manière assez détournée d'obtenir des IR (en utilisant llvm-dis). - L'
unknown file type
erreur est due au fait que l'objet est en fait uneLLVM
représentation, mais je soupçonne que l'éditeur de liens s'attend à un format différent. - Pour que l'éditeur de liens comprenne la
LLVM
représentation, ajoutez-flto
aux options de l'éditeur de lienstarget_link_options(${TARGET} PRIVATE -flto)
, (source [ 4 ]). C'est génial, car cela signifie que j'ai presque résolu ce problème ... Je ne sais tout simplement pas comment obtenir le chemin d'accès aux fichiers de sortie bitcode produits dans cmake, une fois que je le fais, je peux les passer à opt (j'espère. ..). - Pour obtenir les objets cibles, la commande cmake suivante peut être utilisée
$<TARGET_OBJECTS:${TARGET}>
dans le cas de cmake, cela listera les fichiers de bitcode LLVM.o
(est-ce à.o
cause d'un changement de nom par cmake?). - Le
.o
fichier dans ce cas est un bitcode, mais l'opt
outil n'apparaît qu'avec une représentation llvm. Pour se convertir à celallvm-dis bitcode.bc –o llvm_asm.ll
. En raison de la compilation croisée, je pense que le symbole mutilé est d'un format étrange. Les transmettrellvm-cxxfilt
ne réussit pas, par exemplellvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z
- Donc adressage 8. c'est un format de découpage de nom MSVC. Cela indique que lors de la compilation sur Windows, clang utilise le nom de format MSVC mangling. Une surprise pour moi ... (source [ 5 ]).
- LLVM livré avec
llvm-undname
est capable de démêler les symboles. Cet outil, lorsque je l'exécute, génère des erreurs significatives lorsque je lui donne une entrée brute, il semble ne fonctionner qu'avec les symboles corrects. L'outildemumble
semble être un wrapper multi-plateforme et multi-format de llvm-undname et llvm-cxxfilt.
Ma macro cmake presque fonctionnelle est la suivante:
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()
Cependant, cela ne fonctionne pas ... Le contenu de ${FILE}
est toujours la liste entière ...
C'est toujours le cas ici:
foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>) add_custom_command( TARGET ${TARGET}
POST_BUILD
COMMAND echo ${FILE}
)
endforeach()
Le résultat ressemble à:
thinga.obj;thingb.obj
Cela est dû au fait que CMake n'évalue pas l'expression du générateur avant que la boucle for ne soit évaluée. Cela signifie qu'il n'y a qu'une seule boucle ici et qu'elle contient l'expression du générateur (pas une expression de générateur résolue) (source [ 6 ]). Cela signifie que je ne peux pas parcourir les fichiers objets et créer une série de commandes personnalisées pour chaque fichier objet.
J'ajouterai à ce qui précède au fur et à mesure que je découvre les choses, si je comprends tout le processus, je publierai une solution.
Toute aide serait grandement appréciée, cela a été une grande douleur dans le cul.
Ce que j'espère, un moyen de faire en sorte que CMake accepte de construire un exécutable dans un seul fichier de représentation LLVM, en utilisant ce fichier avec opt pour obtenir le callgraph, puis en terminant la compilation avec llc
. Je suis un peu contraint cependant, car je suis en train de compiler. En fin de compte, tout ce qui équivaut fera l'affaire ...
Réponses
Je vais tenter une réponse juste pour rassembler toutes mes réponses aux commentaires jusqu'à présent.
Si vous voulez "subvertir" CMake, cela peut être fait avec quelque chose comme ça (adapté à partir d' ici du point 4 d'OP ci-dessus):
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)
Pour les utilisations où le traitement de chaque fichier est requis, le COMMAND
peut être un script externe (bash / python) qui prend simplement cette liste et génère les fichiers .dot. Le problème avec les expressions génératrices est qu'elles ne sont pas évaluées avant la génération dans CMake et pas dans un foreach
contexte.
Si vous voulez la régénération de déclenchement en fonction de quel objet / fichier code binaire est recompilé, les choses se compliquent depuis CMake a des moyens de préréglage pour appeler les composants d'un ensemble d' outils (compilateur, lien, etc.), d' où la raison pour laquelle je l' ai écrit mon projet basé sur CMake retour alors, mais je vous recommande fortement d'éviter la sur-ingénierie au début, car il semble que vous ne soyez pas encore sûr de ce à quoi vous êtes confronté.
Je n'ai pas pris la peine de faire fonctionner complètement LTO , afin d'obtenir également un exécutable fonctionnel car je n'ai pas une telle configuration sur cet ATM de machine.
Toutes les autres exigences (par exemple, sortie Graphviz, démêlage) peuvent être associées à d'autres cibles / commandes personnalisées.
D'autres solutions pourraient être:
- gllvm
- pour les llvm-ir-cmake-utils désespérés