使用带有 CMake 和 Clang 的 GraphViz 的调用图

Callgraphs using GraphViz with CMake and Clang

我的目标是在构建时使用 CMake + Clang + GraphViz 生成调用图。

使用这些 [1, 2] 过程,我可以创建简单的图表。但是,我不确定如何将流程推广到 CMake 项目。

我有一个可执行目标。

add_executable(${TARGET} ${SOURCES})

在宏中,我将图形相关选项添加到目标:

target_compile_options(${TARGET} PRIVATE -S -emit-llvm)

并且,添加一个额外的 post 生成调用图的构建命令:

add_custom_command(
    TARGET ${TARGET}
    POST_BUILD
    COMMENT "Running clang OPT"
    COMMAND opt -analyze -dot-callgraph
)

但是 clang 试图为目标创建一个可执行文件。 这会导致此错误:

[build] lld-link: error: 
Container.test.cpp.obj: unknown file type

我也不明白任何自定义命令(例如 opt)如何访问生成的 LLVM 表示。看起来我的自定义命令对相关文件没有任何了解(即使上述错误已修复)。


目前我的理解:

  1. CMake add_executable-o outfile.exe 参数添加到 clang,这使我无法执行链接进程 [1, 2]
  2. 中显示的相同步骤
  3. $<TARGET_FILE:${TARGET}> 可用于从 clang 中查找生成的文件,但我不知道这是否适用于 LLVM 表示。
  4. 我试过做一个自定义目标,但是在将所有 TARGET 源和所有设置放入自定义目标时遇到了问题。
  5. 此处概述的过程 [] 可能特别相关 -Wl,-save-temps 但这似乎是一种非常迂回的获取 IR 的方法(使用 llvm-dis)。
  6. unknown file type 错误是由于对象实际上是 LLVM 表示,但我怀疑链接器需要不同的格式。
  7. 为了让链接器理解 LLVM 表示,将 -flto 添加到链接器选项 target_link_options(${TARGET} PRIVATE -flto),(来源 [])。 这太棒了,因为这意味着我几乎已经解决了这个问题......我只是不知道如何在 cmake 中获取生成的位码输出文件的路径,一旦我这样做了,我就可以将它们传递给 opt(我希望。 ..).
  8. 要获取目标对象,可以使用以下 cmake 命令 $<TARGET_OBJECTS:${TARGET}> 在 cmake 的情况下,这将列出 .o.o 是因为 cmake 重命名?) LLVM 位码文件。
  9. 本例中的 .o 文件是位码,但是 opt 工具似乎只是一个 llvm 表示。转换成这个llvm-dis bitcode.bc –o llvm_asm.ll。由于交叉编译,我相信损坏的符号是一种奇怪的格式。将它们传入 llvm-cxxfilt 不会成功,例如 llvm-cxxfilt --no-strip-underscore --types ?streamReconstructedExpression@?$BinaryExpr@AEBV?$reverse_iterator@PEBD@std@@AEBV12@@Catch@@EEBAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z
  10. 因此寻址 8。这是一种 MSVC 名称修改格式。这表明在 windows 上编译时,clang 使用 MSVC 格式名称重整。给我一个惊喜...(来源 [])。
  11. LLVM 附带 llvm-undname 它能够分解符号。当我 运行 这个工具在我给它原始输入时它会严重出错,它似乎只适用于正确的符号。该工具 demumble 似乎是 llvm-undname 和 llvm-cxxfilt 的跨平台、多格式包装器。

11.My几乎可以正常工作的cmake宏如下:

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()

但是,这不起作用...${FILE} 的内容始终是整个列表...

这里还是这样:

foreach (FILE IN LISTS $<TARGET_OBJECTS:${TARGET}>)
    add_custom_command(
        TARGET ${TARGET}
        POST_BUILD
        COMMAND echo ${FILE}
    )
endforeach()

结果如下:

thinga.obj;thingb.obj

这是因为 CMake 在评估 for 循环之前不会评估生成器表达式。意思是,这里只有一个循环,它包含生成器表达式(不是已解析的生成器表达式)(来源 [6])。这意味着我无法遍历目标文件并为每个目标文件创建一系列自定义命令。


我会在发现问题后添加到上面,如果我弄清楚整个过程,我会 post 一个解决方案。

任何帮助将不胜感激,这是一个很大的痛苦。


我所希望的是,一种让 CMake 接受将可执行文件构建为单个 LLVM 表示文件的方法,使用该文件和 opt 获取调用图,然后使用 llc 完成编译。 不过我有点受限,因为我正在交叉编译。最终任何等效的东西都会做...

我会尝试一个答案,只是为了收集到目前为止我所有的评论回复。

如果你想“颠覆”CMake,可以用这样的方法来完成(改编自 上面 OP 的第 4 点):

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)

对于需要处理每个文件的用途,COMMAND 可以是一个外部脚本 (bash/python),它只获取该列表并生成 .dot 文件。生成器表达式的问题在于它们在 CMake 中的生成时间之前不会被评估,而不是在 foreach 上下文中。

如果你想根据重新编译的 object/bitcode 文件触发重新生成,事情会变得棘手,因为 CMake 已经预设了调用工具链组件(编译器、link 等)的方法,因此我当时写 CMake-based project 的原因,但我强烈建议在开始时避免过度设计,因为这听起来好像您还不确定自己要面对什么。

我没有费心让 LTO 完全工作,因为我在这台 ATM 机上没有这样的设置。

所有其他要求(例如,Graphviz 输出、分解)可以与进一步的自定义联系起来 targets/commands。

其他解决方案可能是:

  1. gllvm
  2. 绝望的人llvm-ir-cmake-utils