Callgraphs przy użyciu GraphViz z CMake i Clang
Moim celem jest generowanie wykresów połączeń przy użyciu CMake + Clang + GraphViz w czasie kompilacji.
Korzystając z tych [ 1 , 2 ] procesów mogę tworzyć proste wykresy. Ale nie jestem pewien, jak uogólnić proces na projekt CMake.
Mam wykonywalny cel.
add_executable(${TARGET} ${SOURCES})
Które z poziomu makra dodaję wykresowi odpowiednie opcje do celu:
target_compile_options(${TARGET} PRIVATE -S -emit-llvm)
I dodaj dodatkowe polecenie po kompilacji, które generuje wykresy wywołań:
add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMENT "Running clang OPT"
COMMAND opt -analyze -dot-callgraph
)
Ale brzęk próbuje stworzyć plik wykonywalny dla celu. Powoduje to ten błąd:
[build] lld-link: error:
Container.test.cpp.obj: unknown file type
Nie rozumiem również, w jaki sposób dowolne niestandardowe polecenie ( opt
na przykład) mogłoby uzyskać dostęp do utworzonej reprezentacji LLVM. Wygląda na to, że moje polecenie niestandardowe nie ma wiedzy o odpowiednich plikach (nawet jeśli powyższy błąd został naprawiony).
Co do tej pory rozumiem:
- CMake
add_executable
dodaje-o outfile.exe
argument clang, co uniemożliwia mi wykonanie tych samych kroków, co w połączonych procesach [ 1 , 2 ] $<TARGET_FILE:${TARGET}>
można użyć do znalezienia utworzonych plików z clang, ale nie wiem, czy to działa dla reprezentacji LLVM.- Próbowałem zamiast tego zrobić cel niestandardowy, ale miałem problemy z przeniesieniem wszystkich
TARGET
źródeł ze wszystkimi ustawieniami do celu niestandardowego. - Proces przedstawiony tutaj [ 3 ] może być szczególnie istotny,
-Wl,-save-temps
ale wydaje się, że jest to dość okrężny sposób na uzyskanie IR (przy użyciu llvm-dis). unknown file type
Błąd jest spowodowany obiektu rzeczywiścieLLVM
reprezentacji, ale podejrzewam, że łącznik oczekuje innego formatu.- Aby konsolidator zrozumiał
LLVM
reprezentację, dodaj-flto
do opcji konsolidatoratarget_link_options(${TARGET} PRIVATE -flto)
(źródło [ 4 ]). To jest niesamowite, ponieważ oznacza, że prawie rozwiązałem ten problem ... Po prostu nie wiem, jak uzyskać ścieżkę do utworzonych plików wyjściowych kodu bitowego w cmake, kiedy to zrobię, mogę przekazać je do opt (mam nadzieję. ..). - Aby uzyskać obiekty docelowe, można użyć następującego polecenia cmake
$<TARGET_OBJECTS:${TARGET}>
w przypadku cmake spowoduje to wyświetlenie plików kodu bitowego LLVM.o
(Czy jest to.o
spowodowane zmianą nazwy przez cmake?). .o
Plik jest w tym przypadku bitcode, jednakopt
pojawia się tylko narzędziem do reprezentacji LLVM. Aby przekonwertować na tollvm-dis bitcode.bc –o llvm_asm.ll
. Uważam, że z powodu kompilacji krzyżowej zniekształcone symbole mają dziwny format.llvm-cxxfilt
Nie udaje się na przykład przekazać ich dollvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z
- Adresowanie 8. jest to format zniekształcania nazw MSVC. Oznacza to, że podczas kompilacji w systemie Windows clang używa zniekształcenia nazw formatu MSVC. Zaskoczenie dla mnie ... (źródło [ 5 ]).
- LLVM jest dostarczany wraz z
llvm-undname
nim, jest w stanie rozszyfrować symbole. To narzędzie, gdy uruchamiam go w znacznym stopniu, popełnia błędy, gdy podam surowe dane wejściowe, wydaje się działać tylko z poprawnymi symbolami. Narzędziedemumble
wydaje się być wieloplatformowym, wieloformatowym opakowaniem llvm-undname i llvm-cxxfilt.
Moje prawie działające makro cmake wygląda następująco:
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()
Jednak to nie działa ... Zawartość ${FILE}
to zawsze cała lista ...
Tak jest nadal w tym przypadku:
foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>) add_custom_command( TARGET ${TARGET}
POST_BUILD
COMMAND echo ${FILE}
)
endforeach()
Wynik wygląda następująco:
thinga.obj;thingb.obj
Dzieje się tak, ponieważ CMake nie ocenia wyrażenia generatora, dopóki nie zostanie sprawdzona PO pętli for. Oznacza to, że jest tu tylko jedna pętla i zawiera ona wyrażenie generatora (nie rozwiązane wyrażenie generatora) (źródło [ 6 ]). Oznacza to, że nie mogę przeglądać plików obiektowych i tworzyć serii niestandardowych poleceń dla każdego pliku obiektowego.
Dodam do powyższego, gdy się dowiem, jeśli wymyślę cały proces, opublikuję rozwiązanie.
Każda pomoc byłaby bardzo mile widziana, to był wielki ból w dupie.
Mam nadzieję, że sposób, aby CMake zaakceptował budowanie pliku wykonywalnego do pojedynczego pliku reprezentacji LLVM, używając tego pliku z opcją uzyskania callgraph, a następnie kończąc kompilację llc
. Jestem jednak trochę ograniczony, ponieważ kompiluję krzyżowo. Ostatecznie wszystko, co równoważne, zrobi ...
Odpowiedzi
Spróbuję odpowiedzieć tylko po to, aby zebrać wszystkie moje dotychczasowe odpowiedzi na komentarze.
Jeśli chcesz "obalić" CMake, możesz to zrobić za pomocą czegoś takiego (zaadaptowanego stąd z punktu 4 OP powyżej):
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)
W przypadku zastosowań, w których wymagane jest przetwarzanie każdego pliku, COMMAND
może to być skrypt zewnętrzny (bash / python), który po prostu pobiera tę listę i generuje pliki .dot. Problem z wyrażeniami generatora polega na tym, że nie są one oceniane do czasu wygenerowania w CMake, a nie w foreach
kontekście.
Jeśli chcesz wyzwolić regenerację na podstawie tego, który plik obiektu / kodu bitowego jest ponownie kompilowany, sprawy stają się trudne, ponieważ CMake ma predefiniowane sposoby wywoływania komponentów łańcucha narzędzi (kompilator, link itp.), Stąd dlaczego napisałem mój projekt oparty na CMake z powrotem wtedy, ale zdecydowanie odradzam unikanie przepracowania na początku, ponieważ brzmi to tak, jakbyś nie był jeszcze pewien, z czym masz do czynienia.
Nie przejmowałem się tym, aby LTO działało w pełni, aby również uzyskać działający plik wykonywalny, ponieważ nie mam takiej konfiguracji na tym bankomacie.
Wszystkie inne wymagania (np. Wyjście Graphviz, demangling) można połączyć z dalszymi niestandardowymi celami / poleceniami.
Inne rozwiązania to:
- gllvm
- dla zdesperowanych llvm-ir-cmake-utils