Callgraphs usando GraphViz con CMake y Clang
Mi objetivo es generar gráficos de llamadas usando CMake + Clang + GraphViz en el momento de la compilación.
Usando estos procesos [ 1 , 2 ] puedo crear gráficos simples. Pero no estoy seguro de cómo generalizar el proceso a un proyecto de CMake.
Tengo un objetivo ejecutable.
add_executable(${TARGET} ${SOURCES})
Que desde dentro de una macro, agrego las opciones relevantes del gráfico al objetivo:
target_compile_options(${TARGET} PRIVATE -S -emit-llvm)
Y agregue un comando adicional de compilación posterior que genere los gráficos de llamadas:
add_custom_command(
TARGET ${TARGET}
POST_BUILD
COMMENT "Running clang OPT"
COMMAND opt -analyze -dot-callgraph
)
Pero el sonido metálico intenta crear un ejecutable para el objetivo. Esto da como resultado este error:
[build] lld-link: error:
Container.test.cpp.obj: unknown file type
Tampoco entiendo cómo cualquier comando personalizado ( optpor ejemplo) accedería a la representación LLVM producida. No parece que mi comando personalizado tenga conocimiento de los archivos relevantes (incluso si se corrigió el error anterior).
Lo que entiendo hasta ahora:
- CMake
add_executableagrega el-o outfile.exeargumento a clang, esto me impide hacer los mismos pasos que se muestran en los procesos vinculados [ 1 , 2 ] $<TARGET_FILE:${TARGET}>se puede usar para encontrar los archivos producidos por clang, pero no sé si esto funciona para la representación LLVM.- En su lugar, intenté hacer un objetivo personalizado, pero tuve problemas para obtener todas las
TARGETfuentes con todas las configuraciones en el objetivo personalizado. - El proceso descrito aquí [ 3 ] podría ser especialmente relevante,
-Wl,-save-tempspero esta parece ser una forma bastante indirecta de obtener IR (usando llvm-dis). - El
unknown file typeerror se debe a que el objeto en realidad es unaLLVMrepresentación, pero sospecho que el vinculador espera un formato diferente. - Para que el enlazador comprenda la
LLVMrepresentación, agregue-fltoa las opciones del enlazadortarget_link_options(${TARGET} PRIVATE -flto)(fuente [ 4 ]). Esto es increíble, porque significa que casi lo he resuelto ... Simplemente no sé cómo obtener la ruta a los archivos de salida de código de bits producidos en cmake, una vez que lo haga, puedo pasarlos a optar (espero. ..). - Para obtener los objetos de destino, se puede usar el siguiente comando cmake
$<TARGET_OBJECTS:${TARGET}>en el caso de cmake, esto enumerará los archivos de código de bits LLVM.o(¿se.odebe a un cambio de nombre por cmake?). - En
.oeste caso, el archivo es de código de bits, sin embargo, laoptherramienta aparece solo en una representación llvm. Para convertir a estollvm-dis bitcode.bc –o llvm_asm.ll. Debido a la compilación cruzada, creo que el símbolo destrozado tiene un formato extraño. Pasarlos allvm-cxxfiltno tiene éxito, por ejemplollvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z - Entonces, al abordar 8. este es un formato de modificación de nombres MSVC. Esto indica que cuando se compila en Windows, clang usa el formato MSVC de manipulación de nombres. Una sorpresa para mí ... (fuente [ 5 ]).
- LLVM se envía con
llvm-undnameél es capaz de exigir los símbolos. Esta herramienta, cuando la ejecuto, tiene errores significativos cuando le doy una entrada sin procesar, parece que solo funciona con los símbolos correctos. La herramientademumbleparece ser un contenedor multiplataforma y multiformato de llvm-undname y llvm-cxxfilt.
Mi macro cmake casi funcional es la siguiente:
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()
Sin embargo, esto no funciona ... El contenido de ${FILE}es siempre la lista completa ...
Este sigue siendo el caso aquí:
foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>) add_custom_command( TARGET ${TARGET}
POST_BUILD
COMMAND echo ${FILE}
)
endforeach()
El resultado se parece a:
thinga.obj;thingb.obj
Esto se debe a que CMake no evalúa la expresión del generador hasta DESPUÉS de que se evalúe el bucle for. Es decir, solo hay un bucle aquí y contiene la expresión generadora (no una expresión generadora resuelta) (fuente [ 6 ]). Esto significa que no puedo recorrer los archivos de objeto y crear una serie de comandos personalizados para cada archivo de objeto.
Agregaré a lo anterior a medida que descubra las cosas. Si descubro todo el proceso, publicaré una solución.
Cualquier ayuda sería muy apreciada, esto ha sido un gran dolor de cabeza.
Lo que espero, una forma de hacer que CMake acepte construir un ejecutable en un solo archivo de representación LLVM, usar ese archivo con optar por obtener el gráfico de llamadas y luego terminar la compilación con llc. Sin embargo, estoy un poco limitado, ya que estoy compilando de forma cruzada. En última instancia, cualquier cosa equivalente servirá ...
Respuestas
Intentaré una respuesta solo para recopilar todas las respuestas a mis comentarios hasta ahora.
Si desea "subvertir" CMake, puede hacerlo con algo como esto (adaptado de aquí del punto 4 de OP anterior):
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)
Para usos donde se requiere el procesamiento de cada archivo, COMMANDpuede ser un script externo (bash / python) que simplemente toma esa lista y genera los archivos .dot. El problema con las expresiones generadoras es que no se evalúan hasta el momento de la generación en CMake y no en un foreachcontexto.
Si desea activar la regeneración en función del objeto / archivo de código de bits que se recompila, las cosas se complican ya que CMake tiene formas preestablecidas de invocar los componentes de una cadena de herramientas (compilador, enlace, etc.), por eso escribí mi proyecto basado en CMake . luego, pero recomiendo encarecidamente evitar la sobreingeniería al principio, ya que parece que aún no estás seguro de a qué te enfrentas.
No me he molestado en hacer que LTO funcione completamente, para obtener también un ejecutable que funcione, ya que no tengo esa configuración en este cajero automático.
Todos los demás requisitos (por ejemplo, salida Graphviz, demanda) se pueden conectar con más objetivos / comandos personalizados.
Otras soluciones pueden ser:
- gllvm
- para los desesperados llvm-ir-cmake-utils