diff --git a/.github/workflows/mcg-ci.yml b/.github/workflows/mcg-ci.yml index 15014527..00b27900 100644 --- a/.github/workflows/mcg-ci.yml +++ b/.github/workflows/mcg-ci.yml @@ -124,6 +124,13 @@ jobs: run: | cd /opt/metacg/tools/cgcollector2/test bash testBase.sh + - name: Run CaGe tests + uses: addnab/docker-run-action@v3 + with: + image: metacg-devel:latest + run: | + cd /opt/metacg/build/tools/cage/test + ./runCaGeTests.sh - name: Run cgvalidate tests uses: addnab/docker-run-action@v3 with: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1efa8722..c5c43ee1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -145,6 +145,7 @@ build-mcg: -DMETACG_BUILD_CGCOLLECTOR=ON -DMETACG_BUILD_GRAPH_TOOLS=ON -DMETACG_BUILD_CGPATCH=ON + -DCAGE_USE_METAVIRT=ON -DCGPATCH_USE_MPI=OFF -DMETACG_BUILD_PYMETACG=ON -DMETACG_BUILD_PYMETACG_TESTS=ON @@ -269,6 +270,19 @@ test-cgcollector2: - cd tools/cgcollector2/test/ - ./testBase.sh -b $MCG_BUILD +test-cage: + <<: *job-setup + parallel: + matrix: + - GCC: 11 + LLVM: [16.0.6, 17.0.6, 18.1.8] + stage: integration-test + needs: ["build-mcg"] + script: + - module load clang/$LLVM + - cd $MCG_BUILD/tools/cage/test + - ./runCaGeTests.sh + test-basic-pgis: <<: *job-setup stage: integration-test diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c57df7d..cf535882 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,29 @@ option( if(METACG_BUILD_GRAPH_TOOLS) # New MetaCG tools based on the graphlib include(ClangLLVM) + + # Determine if we can build CaGe + if(${LLVM_PACKAGE_VERSION} + GREATER + 15 + ) + set(BUILD_CAGE ON) + + option( + CAGE_USE_METAVIRT + ON + "Rely on metavirt for virtual call handling" + ) + + if(${CAGE_USE_METAVIRT}) + include(metavirt) + endif() + + else() + set(BUILD_CAGE OFF) + message(STATUS "Skipping CaGe: requires LLVM version 16 or higher") + endif() + add_subdirectory(tools) endif() @@ -125,6 +148,7 @@ if(METACG_BUILD_CGPATCH) message(STATUS "Skipping CGPatch: requires LLVM version 15 or higher") endif() endif() + # PIRA analyzer Should PGIS be built option( METACG_BUILD_PGIS diff --git a/cmake/metavirt.cmake b/cmake/metavirt.cmake new file mode 100644 index 00000000..cc60f666 --- /dev/null +++ b/cmake/metavirt.cmake @@ -0,0 +1,14 @@ +set(METAVIRT_LOG_LEVEL + "0" + CACHE STRING "MetaVirt log level from 0 (least verbose) to 4 (most verbose)" +) + +FetchContent_Declare( + metavirt + GIT_REPOSITORY https://github.com/ahueck/llvm-metavirt.git + GIT_TAG devel + GIT_SHALLOW 1 + FIND_PACKAGE_ARGS +) + +FetchContent_MakeAvailable(metavirt) diff --git a/container/full-build b/container/full-build index db94b115..0adf7929 100644 --- a/container/full-build +++ b/container/full-build @@ -47,6 +47,7 @@ RUN cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Debug \ -DMETACG_BUILD_GRAPH_TOOLS=ON \ -DMETACG_BUILD_CGPATCH=ON \ -DMETACG_BUILD_GRAPH_TOOLS=ON \ + -DCAGE_USE_METAVIRT=ON \ -DMETACG_BUILD_PYMETACG=ON \ -DPython_ROOT_DIR=/opt/metacg/.venv \ -DPYTEST_EXECUTABLE=/opt/metacg/.venv/bin/pytest \ diff --git a/graph/src/Callgraph.cpp b/graph/src/Callgraph.cpp index 0cd10ed8..8dcd4ee1 100644 --- a/graph/src/Callgraph.cpp +++ b/graph/src/Callgraph.cpp @@ -42,6 +42,7 @@ CgNode* Callgraph::getMain(bool forceRecompute) const { CgNode& Callgraph::insert(const std::string& function, std::optional origin, bool isVirtual, bool hasBody) { + assert(!function.empty() && "Function name must not be empty"); NodeId id = nodes.size(); // Note: Can't use make_unique here because make_unqiue is not (and should not be) a friend of the CgNode constructor. nodes.emplace_back(new CgNode(id, function, std::move(origin), isVirtual, hasBody)); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index dcf82e8b..6ddc252a 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -4,3 +4,6 @@ add_subdirectory(cgconvert) add_subdirectory(cgformat) add_subdirectory(cgdiff) add_subdirectory(cgquery) +if(BUILD_CAGE) + add_subdirectory(cage) +endif() diff --git a/tools/README.md b/tools/README.md index 9a1bec46..b9788eed 100644 --- a/tools/README.md +++ b/tools/README.md @@ -2,6 +2,49 @@ This folder contains tools for creating and merging call graphs using the MetaCG graph library. +## CaGe + +CaGe is MetaCG's link-time call graph generator. +To use it, the target application needs to be built with (full) LTO using the LLD linker. + +### Basic Usage +Let's consider a project that consists of two source files, `example_a.cpp` and `example_b.cpp`. +A standard build process consists of a compile and a link step: +``` +# Compile step +clang++ example_a.cpp -o example_a.o +clang++ example_b.cpp -o example_b.o + +# Link step +clang++ example_a.o example_b.o -o example +``` + +Modifying this build process to generate a call graph with CaGe is straightforward. +All necessary compile flags can be generated with `metacg-config`: + +``` +# Compile step +clang++ $(metacg-config --cage-cxxflags) example_a.cpp -o example_a.o +clang++ $(metacg-config --cage-cxxflags) example_b.cpp -o example_b.o + +# Link step +clang++ $(metacg-config --cage-ldflags --cage-pass-option -cg-file=example.mcg) example_a.o example_b.o -o example +``` + +### Build system integration +Integrating CaGe into build systems, e.g. Make and CMake, is simple. +Many Make projects already define `CXXFLAGS` and `LDFLAGS` variables, which can be extended with the respective +`metacg-config` output. +For CMake projects, the relevant options are `CMAKE_CXX_FLAGS`, `CMAKE_EXE_LINKER_FLAGS` and `CMAKE_SHARED_LINKER_FLAGS`. + +### Pass options +Pass options can be set by passing `--cage-pass-option