c++ - 使用带有CMake和Clang的GraphViz的Callgraphs

标签 c++ cmake graphviz

我的目标是在构建时使用CMake + Clang + GraphViz生成调用图。
使用这些[12]进程,我可以创建简单的图形。但是,我不确定如何将流程推广到CMake项目。
我有一个可执行目标。

add_executable(${TARGET} ${SOURCES})
从宏中,我将图形的相关选项添加到目标中:
target_compile_options(${TARGET} PRIVATE -S -emit-llvm)
并且,添加一个附加的post build命令来生成调用图:
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表示形式。我的自定义命令似乎不了解相关文件(即使上述错误已修复)。

到目前为止,我的理解是:
  • CMake add_executable-o outfile.exe参数添加到clang,这使我无法执行链接过程中所示的相同步骤[12]
  • $<TARGET_FILE:${TARGET}>可用于从clang查找生成的文件,但是我不知道这是否适用于LLVM表示。
  • 我尝试改用自定义目标,但是在将所有TARGET源以及所有设置都放入自定义目标时遇到了问题。
  • 此处概述的过程[3]可能与-Wl,-save-temps特别相关,但这似乎是获取IR的一种round回方式(使用llvm-dis)。
  • unknown file type错误是由于对象实际上是LLVM表示形式引起的,但是我怀疑链接器期望使用其他格式。
  • 要使链接器了解LLVM的表示形式,请将-flto添加到链接器选项target_link_options(${TARGET} PRIVATE -flto)(源[4])。
    这很棒,因为这意味着我几乎解决了这个问题……我只是不知道如何在cmake中获取生成的位码输出文件的路径,一旦这样做,我就可以将它们传递给opt(我希望。 ..)。
  • 要获取目标对象,可以在cmake的情况下使用以下cmake命令$<TARGET_OBJECTS:${TARGET}>,这将列出.o(由于cmake重命名,所以.o吗?)LLVM位代码文件。
  • 在这种情况下,.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
  • 因此,寻址8.这是MSVC名称处理格式。这表明在Windows上进行编译时,clang使用MSVC格式名称修饰。给我一个惊喜...(来源[5])。
  • LLVM随llvm-undname一起提供,它可以使符号脱嵌。当我运行该工具时,如果给它原始输入,就会出现很大的错误,它似乎只能使用正确的符号。工具demumble似乎是llvm-undname和llvm-cxxfilt的跨平台,多格式包装器。

  • 11,我几乎可以正常工作的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])。这意味着我无法遍历目标文件,也无法为每个目标文件创建一系列自定义命令。

    找到问题后,我将在上面添加内容。如果确定了整个过程,我将发布解决方案。
    任何帮助将不胜感激,这是一个巨大的痛苦。

    我希望的是一种使CMake接受为单个LLVM表示文件构建可执行文件的方法,该文件与opt一起使用来获取调用图,然后使用llc完成编译。
    但是,在交叉编译时,我受到了一些限制。最终,任何等价的东西都会做...

    最佳答案

    我将尝试回答一个问题,以收集到目前为止的所有评论答复。
    如果您想“颠覆” CMake,可以使用以下方法(从上面OP的第4点中的here改编而成):

    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上下文中时,才对它们进行求值。
    如果要基于重新编译的对象/位代码文件来触发重新生成,由于CMake具有预置的方法来调用工具链的组件(编译器,链接等),因此事情会变得棘手,因此为什么我要编写CMake-based project,但是我强烈建议一开始就避免过度设计,因为听起来好像您不确定自己要面对的是什么。
    我还没有为使LTO完全工作而烦恼,因为我在这台机器的ATM上没有这样的设置,因此也可以获得可以工作的可执行文件。
    所有其他要求(例如Graphviz输出,分解)可以与其他自定义目标/命令关联。
    其他解决方案可能是:
  • gllvm
  • 代表绝望的llvm-ir-cmake-utils
  • 关于c++ - 使用带有CMake和Clang的GraphViz的Callgraphs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64980827/

    相关文章:

    c++ - CMake 测试在 MacOS 上找不到库

    .net - 如何使用 .NET Standard 目标框架和 CMake 生成 Visual Studio 项目

    windows - 为QT编译opencv时出错

    graphviz - 在 Graphviz 中将边缘位置从节点下方更改为上方节点

    c++ - 访问一些在 .cpp 中定义的静态变量,而它的类类型也在 .cpp 中定义

    c++ - 使用共享逻辑 (MVC) 将 Qt 桌面应用程序扩展到 Web 服务

    c++ - 异构数组实现

    c++ - DuplicateHandle : need to OpenProcess, 但访问被拒绝

    graphviz - Graphviz 中的图形图

    Graphviz子图顺序