Callgraph utilizzando GraphViz con CMake e Clang
Il mio obiettivo è generare grafici delle chiamate utilizzando CMake + Clang + GraphViz in fase di compilazione.
Usando questi [ 1 , 2 ] processi posso creare semplici grafici. Ma non sono sicuro di come generalizzare il processo a un progetto CMake.
Ho una destinazione eseguibile.
add_executable(${TARGET} ${SOURCES})
Quale dall'interno di una macro, aggiungo le opzioni relative al grafico al target:
target_compile_options(${TARGET} PRIVATE -S -emit-llvm)
E aggiungi un comando di post build aggiuntivo che genera i grafici delle chiamate:
add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMENT "Running clang OPT"
COMMAND opt -analyze -dot-callgraph
)
Ma il clang tenta di creare un eseguibile per l'obiettivo. Ciò si traduce in questo errore:
[build] lld-link: error:
Container.test.cpp.obj: unknown file type
Inoltre, non capisco come qualsiasi comando personalizzato ( optad esempio) possa accedere alla rappresentazione LLVM prodotta. Non sembra che il mio comando personalizzato abbia alcuna conoscenza dei file rilevanti (anche se l'errore di cui sopra è stato corretto).
Quello che ho capito finora:
- CMake
add_executableaggiunge l'-o outfile.exeargomento a clang, questo mi impedisce di fare gli stessi passaggi mostrati nei processi collegati [ 1 , 2 ] $<TARGET_FILE:${TARGET}>può essere utilizzato per trovare i file prodotti da clang, ma non so se funziona per la rappresentazione LLVM.- Ho provato invece a fare un target personalizzato, ma ho avuto problemi a ottenere tutte le
TARGETsorgenti con tutte le impostazioni nel target personalizzato. - Il processo qui delineato [ 3 ] potrebbe essere particolarmente rilevante,
-Wl,-save-tempsma questo sembra essere un modo piuttosto indiretto per ottenere IR (usando llvm-dis). - L'
unknown file typeerrore è dovuto al fatto che l'oggetto è effettivamenteLLVMrappresentato, ma sospetto che il linker si aspetti un formato diverso. - Per fare in modo che il linker comprenda la
LLVMrappresentazione, aggiungi-fltoalle opzioni del linkertarget_link_options(${TARGET} PRIVATE -flto), (fonte [ 4 ]). Questo è fantastico, perché significa che ho quasi risolto questo ... Non so come ottenere il percorso dei file di output bitcode prodotti in cmake, una volta fatto, posso passarli a opt (spero. ..). - Per ottenere gli oggetti di destinazione, è possibile utilizzare il seguente comando cmake
$<TARGET_OBJECTS:${TARGET}>nel caso di cmake, che elencherà i file di bitcode LLVM.o(è a.ocausa di una ridenominazione da parte di cmake?). - Il
.ofile in questo caso è bitcode, tuttavia looptstrumento appare solo come una rappresentazione llvm. Per convertire in questollvm-dis bitcode.bc –o llvm_asm.ll. A causa della compilazione incrociata, credo che il simbolo alterato abbia un formato strano. Trasmetterli allvm-cxxfiltnon riesce, ad esempiollvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z - Quindi l'indirizzo 8. questo è un formato di manipolazione del nome MSVC. Ciò indica che durante la compilazione su Windows clang utilizza la modifica del nome del formato MSVC. Una sorpresa per me ... (fonte [ 5 ]).
- LLVM viene fornito con
llvm-undnameesso è in grado di districare i simboli. Questo strumento quando lo eseguo errori in modo significativo quando gli fornisco un input grezzo, sembra funzionare solo con i simboli corretti. Lo strumentodemumblesembra essere un wrapper multiformato e multipiattaforma di llvm-undname e llvm-cxxfilt.
11.La mia macro cmake quasi funzionante è la seguente:
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()
Tuttavia, questo non funziona ... Il contenuto di ${FILE}è sempre l'intero elenco ...
Questo è ancora il caso qui:
foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>) add_custom_command( TARGET ${TARGET}
POST_BUILD
COMMAND echo ${FILE}
)
endforeach()
Il risultato è simile a:
thinga.obj;thingb.obj
Questo perché CMake non valuta l'espressione del generatore finché DOPO non viene valutato il ciclo for. Significa che qui c'è un solo loop e contiene l'espressione del generatore (non un'espressione del generatore risolta) (sorgente [ 6 ]). Ciò significa che non posso scorrere i file oggetto e creare una serie di comandi personalizzati per ogni file oggetto.
Aggiungerò a quanto sopra man mano che scopro le cose, se capisco l'intero processo posterò una soluzione.
Qualsiasi aiuto sarebbe molto apprezzato, questo è stato un grande rompicoglioni.
Quello che spero, un modo per fare in modo che CMake accetti la creazione di un eseguibile in un singolo file di rappresentazione LLVM, utilizzando quel file con opt per ottenere il callgraph e quindi terminare la compilazione con llc. Sono un po 'limitato però, dato che sto compilando in modo incrociato. Alla fine tutto ciò che equivale andrà bene ...
Risposte
Tenterò una risposta solo per raccogliere tutte le mie risposte ai commenti finora.
Se vuoi "sovvertire" CMake, puoi farlo con qualcosa del genere (adattato da qui dal punto 4 di OP sopra):
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)
Per gli usi in cui è richiesta l'elaborazione su ogni file, COMMANDpuò essere uno script esterno (bash / python) che prende solo quell'elenco e genera i file .dot. Il problema con le espressioni del generatore è che non vengono valutate fino al momento della generazione in CMake e non in un foreachcontesto.
Se si desidera la rigenerazione grilletto sulla base di quello file oggetto / codice binario che viene ricompilato, le cose si fanno difficili dal CMake ha preimpostato modi per richiamare i componenti di una toolchain (compilatore, collegamento, ecc), da qui il motivo per cui ho scritto il mio progetto CMake a base di schiena quindi, ma ti consiglio vivamente di evitare l'eccessiva ingegneria all'inizio poiché sembra che tu non sia ancora sicuro di cosa stai affrontando.
Non mi sono preoccupato di far funzionare completamente LTO , al fine di ottenere anche un eseguibile funzionante poiché non ho una tale configurazione su questa macchina ATM.
Tutti gli altri requisiti (ad esempio, output di Graphviz, demangling) possono essere collegati con ulteriori target / comandi personalizzati.
Altre soluzioni potrebbero essere:
- gllvm
- per i disperati llvm-ir-cmake-utils