Skip to content

Commit

Permalink
Merge branch 'feat/cgmerge2_tool' into 'devel'
Browse files Browse the repository at this point in the history
CMerge2

See merge request tuda-sc/projects/metacg!189
  • Loading branch information
Kreutzer, Sebastian committed Feb 25, 2025
2 parents f6e3440 + 0f12683 commit 2178f16
Show file tree
Hide file tree
Showing 52 changed files with 3,084 additions and 38 deletions.
8 changes: 8 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ test-pgis:


# Stage: integration-test
test-graphlib-merge:
<<: *job-setup
stage: integration-test
script:
- module load clang/$LLVM
- cd graph/test/integration/CallgraphMerge
- ./MergeTestRunner.sh -b $MCG_BUILD

test-cgvalidate:
<<: *job-setup
stage: integration-test
Expand Down
2 changes: 2 additions & 0 deletions graph/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,5 @@ add_spdlog_libraries(metacg)
if(METACG_BUILD_UNIT_TESTS)
add_subdirectory(test/unit)
endif()

add_subdirectory(test/integration/CallgraphMerge)
7 changes: 7 additions & 0 deletions graph/include/Callgraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ class Callgraph {
*/
CgNode* getOrInsertNode(const std::string& name, const std::string& origin = "unknownOrigin");

/**
* Merges the given call graph into this one.
* The other call graph remains unchanged.
* @param other The call graph to merge.
*/
void merge(const Callgraph& other);

/**
* Clears the graph to an empty graph with no main node.
*/
Expand Down
9 changes: 9 additions & 0 deletions graph/include/io/MCGWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class MCGWriter {
*/
void writeActiveGraph(JsonSink& js);

virtual ~MCGWriter() = default;

protected:
/**
* Adds the CG version data to the MetaCG in json Format.
Expand All @@ -87,6 +89,13 @@ class MCGWriter {
MCGFileInfo fileInfo;
};

/**
* Factory function to instantiate the correct writer implementation for the given format.
* @param version The format version.
* @return A unique pointer to the instantiated writer. Empty, if there is no writer matching the format version.
*/
std::unique_ptr<MCGWriter> createWriter(int version);

} // namespace metacg::io

#endif // METACG_MCGWRITER_H
42 changes: 42 additions & 0 deletions graph/src/Callgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,48 @@ CgNode* Callgraph::getOrInsertNode(const std::string& name, const std::string& o
assert(nodes.find(node_id) != nodes.end());
return nodes[node_id].get();
}

void metacg::Callgraph::merge(const metacg::Callgraph& other) {
// Lambda function to clone nodes from the other call graph
std::function<void(metacg::Callgraph*, const metacg::Callgraph&, metacg::CgNode*)> copyNode =
[&](metacg::Callgraph* destination, const metacg::Callgraph& source, metacg::CgNode* node) {
std::string functionName = node->getFunctionName();
metacg::CgNode* mergeNode = destination->getOrInsertNode(functionName, node->getOrigin());

if (node->getHasBody()) {
auto callees = source.getCallees(node);

for (auto* c : callees) {
std::string calleeName = c->getFunctionName();
if (!destination->hasNode(calleeName)) {
copyNode(destination, source, c);
}

if (!destination->existEdgeFromTo(functionName, calleeName)) {
destination->addEdge(functionName, calleeName);
}
}

mergeNode->setHasBody(node->getHasBody());
mergeNode->setIsVirtual(node->isVirtual());
}

for (const auto& it : node->getMetaDataContainer()) {
if (mergeNode->has(it.first)) {
mergeNode->get(it.first)->merge(*(it.second));
} else {
mergeNode->addMetaData(it.second->clone());
}
}
};

// Iterate over all nodes and merge them into this graph
for (auto it = other.nodes.begin(); it != other.nodes.end(); ++it) {
auto& currentNode = it->second;
copyNode(this, other, currentNode.get());
}
}

const metacg::Callgraph::NodeContainer& Callgraph::getNodes() const { return nodes; }

const metacg::Callgraph::EdgeContainer& Callgraph::getEdges() const { return edges; }
Expand Down
38 changes: 2 additions & 36 deletions graph/src/MCGManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,50 +98,16 @@ bool MCGManager::addToManagedGraphs(const std::string& name, std::unique_ptr<Cal
void MCGManager::mergeIntoActiveGraph() {
assert(activeGraph && "Graph manager could not merge into active Graph, no active graph exists");

std::function<void(metacg::Callgraph*, metacg::Callgraph*, metacg::CgNode*)> copyNode =
[&](metacg::Callgraph* destination, metacg::Callgraph* source, metacg::CgNode* node) {
std::string functionName = node->getFunctionName();
metacg::CgNode* mergeNode = destination->getOrInsertNode(functionName, node->getOrigin());

if (node->getHasBody()) {
auto callees = source->getCallees(node);

for (auto* c : callees) {
std::string calleeName = c->getFunctionName();
if (!destination->hasNode(calleeName)) {
copyNode(destination, source, c);
}

if (!destination->existEdgeFromTo(functionName, calleeName)) {
destination->addEdge(functionName, calleeName);
}
}

mergeNode->setHasBody(node->getHasBody());
mergeNode->setIsVirtual(node->isVirtual());
}

for (const auto& it : node->getMetaDataContainer()) {
if (mergeNode->has(it.first)) {
mergeNode->get(it.first)->merge(*(it.second));
} else {
mergeNode->addMetaData(it.second->clone());
}
}
};

// We merge into whatever the active callgraph is
auto* activeCallgraph = getCallgraph();
for (const auto& callgraphName : getAllManagedGraphNames()) {
if (assertActive(callgraphName))
continue;

auto* currentCallgraph = getCallgraph(callgraphName);
assert(currentCallgraph && "Callgraph is null!");

for (auto it = currentCallgraph->getNodes().begin(); it != currentCallgraph->getNodes().end(); ++it) {
auto& currentNode = it->second;
copyNode(activeCallgraph, currentCallgraph, currentNode.get());
}
activeCallgraph->merge(*currentCallgraph);
}
}

Expand Down
12 changes: 12 additions & 0 deletions graph/src/io/MCGWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "io/MCGWriter.h"
#include "LoggerUtil.h"
#include "MCGManager.h"
#include "io/VersionThreeMCGWriter.h"
#include "io/VersionTwoMCGWriter.h"

void metacg::io::MCGWriter::writeActiveGraph(metacg::io::JsonSink& js) {
const auto* cg = metacg::graph::MCGManager::get().getCallgraph();
Expand All @@ -24,4 +26,14 @@ void metacg::io::MCGWriter::writeNamedGraph(const std::string& cgName, metacg::i
return;
}
write(cg, js);
}

std::unique_ptr<metacg::io::MCGWriter> metacg::io::createWriter(int version) {
if (version == 2) {
return std::make_unique<metacg::io::VersionTwoMCGWriter>();
}
if (version == 3) {
return std::make_unique<metacg::io::VersionThreeMCGWriter>();
}
return {};
}
12 changes: 12 additions & 0 deletions graph/test/integration/CallgraphMerge/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
set(PROJECT_NAME CallgraphMergeIntegrationTest)
set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-target)

add_executable(mergetester MergeTester.cpp)
target_include_directories(mergetester PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(mergetester PUBLIC metacg::metacg)

add_json(mergetester)
add_spdlog_libraries(mergetester)
add_config_include(mergetester)

add_test(NAME mergeTests COMMAND mergetester)
73 changes: 73 additions & 0 deletions graph/test/integration/CallgraphMerge/MergeTestRunner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env bash

timeStamp=$(date +%s)
: ${CI_CONCURRENT_ID:=$timeStamp}

mkdir -p log

build_dir=build
generate_gt=0

function merge {
fail=0
tc=$1

ipcgTaFile="${tc}_a.ipcg"
ipcgTbFile="${tc}_b.ipcg"
gtCombFile="${tc}_both.gtmcg"

combFile=${tc}_both-${CI_CONCURRENT_ID}.ipcg

${PWD}/../../../../${build_dir}/graph/test/integration/CallgraphMerge/mergetester ./input/${ipcgTaFile} ./input/${ipcgTbFile} ./input/${gtCombFile} ./input/${combFile} >>log/testrun.log 2>&1
mErr=$?

if [ ${generate_gt} -eq 1 ]; then
mv combFile gtCombFile
fi

if [ ${mErr} -ne 0 ]; then
fail=$((fail + 1))
echo "Failure for file: $combFile. Keeping generated file for inspection"
fi

return $fail
}

while getopts ":b:h" opt; do
case $opt in
b)
if [ -z $OPTARG ]; then
echo "no build directory given, assuming \"build\""
fi
build_dir=$OPTARG
;;
h)
echo "use -b to provide a build directory NAME"
echo "use -h to print this help"
exit 0
;;
g)
echo "Regenerating ground truth files"
generate_gt=1
;;
\?)
echo "Invalid option -$OPTARG"
exit 1
;;
esac
done

echo "Running integration test for CallgraphMerge"

tests=(0042 0043 0044 0050 0053 0060)
fails=0

for tc in "${tests[@]}"; do
echo "Running test ${tc}"
# Input files
merge ${tc}
fail=$?
fails=$((fails + fail))
done
echo "Test failures: $fails"
exit $fails
76 changes: 76 additions & 0 deletions graph/test/integration/CallgraphMerge/MergeTester.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

#include "MCGManager.h"
#include "io/VersionTwoMCGReader.h"
#include "io/VersionTwoMCGWriter.h"

#include "metadata/BuiltinMD.h"

#include <filesystem>
#include <iostream>

bool check(nlohmann::json testGraph, nlohmann::json groundTruth) {
for (auto& elem : groundTruth.at("_CG")) {
for (auto& member : elem) {
if (member.is_array()) {
std::sort(member.begin(), member.end());
}
}
}

for (auto& elem : testGraph.at("_CG")) {
for (auto& member : elem) {
if (member.is_array()) {
std::sort(member.begin(), member.end());
}
}
}

return groundTruth.at("_CG") == testGraph.at("_CG");
}

int main(int argc, char** argv) {
if (argc != 5) {
std::cerr << "Usage: " << argv[0] << " cg_a.ipcg cg_b.ipcg groundtruth.gtmcg result.ipcg" << std::endl;
return -1;
}

std::cout << "Running test for " << argv[1] << " merged with " << argv[2] << " == " << argv[3] << std::endl;

const std::string inputA(argv[1]);
const std::string inputB(argv[2]);
const std::string inputGroundTruth(argv[3]);
const std::string outputFile(argv[4]);

auto& mcgManager = metacg::graph::MCGManager::get();

metacg::io::FileSource fsA(inputA);
metacg::io::FileSource fsB(inputB);

metacg::io::VersionTwoMetaCGReader mcgReaderA(fsA);
metacg::io::VersionTwoMetaCGReader mcgReaderB(fsB);

mcgManager.addToManagedGraphs("cg_a", mcgReaderA.read());
mcgManager.addToManagedGraphs("cg_b", mcgReaderB.read());

metacg::io::VersionTwoMCGWriter mcgWriter;
mcgManager.mergeIntoActiveGraph();

metacg::io::JsonSink jsonSink;
mcgWriter.writeActiveGraph(jsonSink);

nlohmann::json groundtruthJson;
std::ifstream groundtruthFile(inputGroundTruth);
groundtruthFile >> groundtruthJson;

// If both are equal, we are done
if (check(groundtruthJson, jsonSink.getJson())) {
return 0;
}
// Keep file for inspection
std::ofstream file;
file.open(outputFile);
file << jsonSink.getJson().dump(4);
file.close();
std::cout << "Test failure: Keeping wrong results for inspection" << std::endl;
return 1;
}
3 changes: 3 additions & 0 deletions graph/test/integration/CallgraphMerge/input/0042_a.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int foo() { return 42; }

int baz() { return foo(); }
Loading

0 comments on commit 2178f16

Please sign in to comment.