Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/mcg-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 9 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,15 @@ test-cgcollector2:
- cd tools/cgcollector2/test/
- ./testBase.sh -b $MCG_BUILD

test-cage:
<<: *job-setup
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
Expand Down
10 changes: 10 additions & 0 deletions cmake/metavirt.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
add_compile_definitions(VIRTCALL_LOG_LEVEL)
FetchContent_Declare(
metavirt
GIT_REPOSITORY https://github.com/ahueck/llvm-metavirt.git
GIT_TAG devel
GIT_SHALLOW 1
FIND_PACKAGE_ARGS
)

FetchContent_MakeAvailable(metavirt)
1 change: 1 addition & 0 deletions graph/src/Callgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ CgNode* Callgraph::getMain(bool forceRecompute) const {

CgNode& Callgraph::insert(const std::string& function, std::optional<std::string> 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));
Expand Down
1 change: 1 addition & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ add_subdirectory(cgmerge2)
add_subdirectory(cgconvert)
add_subdirectory(cgformat)
add_subdirectory(cgdiff)
add_subdirectory(cage)
43 changes: 43 additions & 0 deletions tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 applications 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 --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 `--pass-option <option>(=<val>)` to `metacg-config`.
Available options:
- `-cg-file=<filename>`: The output file for the generated call graph.
- `-pta=no/signature`: Controls the behavior of the points-to analysis for resolving indirect calls. Available options are:
- `no`: Indirect calls are ignored.
- `signature`: Adds all functions with matching signature as potential call targets.

## CGMerge2

CGMerge2 is a front-end for MetaCG's call graph merging functionality.
Expand Down
14 changes: 14 additions & 0 deletions tools/cage/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
option(
CAGE_USE_METAVIRT
ON
"Rely on metavirt for virtual call handling"
)

if(${CAGE_USE_METAVIRT})
include(metavirt)
endif()

add_subdirectory(src)
add_subdirectory(test)

install(TARGETS cage cage-plugin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
22 changes: 22 additions & 0 deletions tools/cage/include/cage/Plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* File: Plugin.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/
#ifndef METACG_CAGE_PLUGIN_H
#define METACG_CAGE_PLUGIN_H

#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"

namespace cage {

struct CaGe : llvm::PassInfoMixin<CaGe> {
llvm::PreservedAnalyses run(llvm::Module& M, llvm::ModuleAnalysisManager& MA);

static llvm::StringRef name() { return "CaGe"; }
};
} // namespace cage

#endif // METACG_CAGE_PLUGIN_H
22 changes: 22 additions & 0 deletions tools/cage/include/cage/generator/CallGraphConsumer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* File: CallGraphConsumer.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/
#ifndef METACG_CALLGRAPHCONSUMER_H
#define METACG_CALLGRAPHCONSUMER_H

namespace metacg {
class Callgraph;
}

namespace cage {

struct CallGraphConsumer {
virtual ~CallGraphConsumer() = default;
virtual void consumeCallGraph(metacg::Callgraph&) = 0;
};

} // namespace cage

#endif // METACG_CALLGRAPHCONSUMER_H
30 changes: 30 additions & 0 deletions tools/cage/include/cage/generator/CallgraphGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* File: CallGraphGenerator.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/
#include "cage/generator/CallGraphConsumer.h"

#include "io/MCGWriter.h"

#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"

namespace cage {

enum PTAType { No, BySignature };

class Generator {
public:
Generator(PTAType ptaType) : ptaType(ptaType) {};

void addConsumer(std::unique_ptr<CallGraphConsumer> consumer) { consumers.push_back(std::move(consumer)); }

bool run(llvm::Module& M, llvm::ModuleAnalysisManager* MA);

private:
PTAType ptaType;
std::vector<std::unique_ptr<CallGraphConsumer>> consumers;
};

} // namespace cage
26 changes: 26 additions & 0 deletions tools/cage/include/cage/generator/FileExporter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* File: FileExporter.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/
#ifndef METACG_FILEEXPORTER_H
#define METACG_FILEEXPORTER_H

#include "cage/generator/CallGraphConsumer.h"

#include <string>

namespace cage {

class FileExporter : public CallGraphConsumer {
public:
FileExporter(std::string outfile) : outfile(outfile) {}
void consumeCallGraph(metacg::Callgraph&) override;

private:
std::string outfile;
};

} // namespace cage

#endif // METACG_FILEEXPORTER_H
38 changes: 38 additions & 0 deletions tools/cage/include/cage/generator/MetaCollector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* File: MetaCollector.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/
#ifndef METACG_METACOLLECTOR_H
#define METACG_METACOLLECTOR_H

#include "Callgraph.h"
#include "llvm/IR/Module.h"

namespace cage {

class MetaCollector {
public:
virtual void run(llvm::Module&, metacg::Callgraph&) = 0;
};

class FunctionLocalMetaCollector : public MetaCollector {
public:
virtual std::unique_ptr<metacg::MetaData> runOnFunction(llvm::Function&) = 0;

void run(llvm::Module& M, metacg::Callgraph& cg) override {
for (auto& F : M) {
auto node = cg.getFirstNode(F.getName().str());
if (!node) {
continue;
}
if (auto md = runOnFunction(F)) {
node->addMetaData(std::move(md));
}
}
}
};

} // namespace cage

#endif // METACG_METACOLLECTOR_H
23 changes: 23 additions & 0 deletions tools/cage/include/cage/generator/NumInstructionsCollector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* File: NumInstructionsCollector.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/

#ifndef METACG_NUMINSTRUCTIONSCOLLECTOR_H
#define METACG_NUMINSTRUCTIONSCOLLECTOR_H

#include "MetaCollector.h"
#include "NumInstructionsMD.h"

namespace cage {

class NumInstructionsCollector : public FunctionLocalMetaCollector {
public:
std::unique_ptr<metacg::MetaData> runOnFunction(llvm::Function& F) override {
return std::make_unique<NumInstructionsMD>(F.getInstructionCount());
}
};

} // namespace cage
#endif
64 changes: 64 additions & 0 deletions tools/cage/include/cage/generator/NumInstructionsMD.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* File: NumInstructionsMD.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/
#ifndef CAGE_NUMINSTRUCTIONSMD_H
#define CAGE_NUMINSTRUCTIONSMD_H

#include "metadata/MetaData.h"

using namespace metacg;

namespace cage {

class NumInstructionsMD : public metacg::MetaData::Registrar<NumInstructionsMD> {
public:
static constexpr const char* key = "numInstructions";
NumInstructionsMD() = default;
explicit NumInstructionsMD(const nlohmann::json& j, StrToNodeMapping&) {
metacg::MCGLogger::instance().getConsole()->trace("Reading NumInstructionsMD from json");
if (j.is_null()) {
metacg::MCGLogger::instance().getConsole()->trace("Could not retrieve meta data for {}", "NumInstructionsMD");
return;
}
auto jsonnumInstructions = j.get<long long int>();
setNumberOfInstructions(jsonnumInstructions);
}

explicit NumInstructionsMD(int num) : numInstructions(num) {}

private:
NumInstructionsMD(const NumInstructionsMD& other) : numInstructions(other.numInstructions) {}

public:
nlohmann::json toJson(NodeToStrMapping&) const final { return getNumberOfInstructions(); }

const char* getKey() const override { return key; }

void merge(const MetaData& toMerge, std::optional<MergeAction>, const GraphMapping&) final {
assert(toMerge.getKey() == getKey() && "Trying to merge NumInstructionsMD with meta data of different types");

const NumInstructionsMD* toMergeDerived = static_cast<const NumInstructionsMD*>(&toMerge);

if (numInstructions != 0 && toMergeDerived->getNumberOfInstructions() != 0 &&
numInstructions != toMergeDerived->getNumberOfInstructions()) {
metacg::MCGLogger::instance().getErrConsole()->warn(
"Same function defined with different number of instructions found on merge.");
}
numInstructions = std::max(numInstructions, toMergeDerived->getNumberOfInstructions());
}

std::unique_ptr<MetaData> clone() const final { return std::unique_ptr<MetaData>(new NumInstructionsMD(*this)); }

void applyMapping(const GraphMapping&) override {}

void setNumberOfInstructions(int numInstructions) { this->numInstructions = numInstructions; }
int getNumberOfInstructions() const { return this->numInstructions; }

private:
int numInstructions{0};
};
} // namespace cage

#endif // CAGE_NUMINSTRUCTIONSMD_H
7 changes: 7 additions & 0 deletions tools/cage/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include(AddLLVM)

add_llvm_pass_plugin(cage-plugin Plugin.cpp)
target_include_directories(cage-plugin PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/tools/cage/include>)
target_link_libraries(cage-plugin PRIVATE cage)

add_subdirectory(generator)
Loading