diff --git a/CMakeLists.txt b/CMakeLists.txt index ab4c44a2b556aa013c3f41c39783f89a94643246..1e812a7cb4c03a6cb43d45b66689b17c6379149b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,14 +16,13 @@ add_compile_definitions( RUNTIME_LOG_LEVEL=spdlog::level::${LOG_LEVEL_LOWER} ) - add_executable(CTUApex src/main.cpp src/CTUApexAction.cpp src/CTUApexConsumer.cpp src/CTUApexVisitor.cpp src/Util.cpp - src/IncludeFinder.cpp + src/PreprocessorExtract.cpp src/GlobalsMarker.cpp ) diff --git a/include/CTUApexAction.h b/include/CTUApexAction.h index 3a024865b79be978f818aebbdb36860d5ba12d89..990878f81ae8c05e8b61d329444195f783bd8b58 100644 --- a/include/CTUApexAction.h +++ b/include/CTUApexAction.h @@ -7,6 +7,7 @@ #include <clang/Frontend/FrontendAction.h> #include <Callgraph.h> +#include "PreprocessorExtract.h" class CTUApexAction : public clang::ASTFrontendAction { public: @@ -18,6 +19,9 @@ public: CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile); private: + std::vector<ExtractionBlock> includes; + std::vector<ExtractionBlock> macros; + std::vector<ExtractionBlock> pragmas; metacg::Callgraph *call_graph; std::string output_text; }; diff --git a/include/CTUApexConsumer.h b/include/CTUApexConsumer.h index 0f2fd629bc4c02974ce14d2104a2083823529bad..cd41f28a9b4b88662c4541a08b3d6ff8a5e76f04 100644 --- a/include/CTUApexConsumer.h +++ b/include/CTUApexConsumer.h @@ -13,24 +13,39 @@ #include "clang/Lex/Lexer.h" #include "GlobalsMarker.h" #include "CTUApexAction.h" -#include "IncludeFinder.h" +#include "ExtractionBlock.h" + class CTUApexConsumer : public clang::ASTConsumer { public: - explicit CTUApexConsumer(IncludeFinder* includes,metacg::Callgraph *call_graph) - : completedIncludeFinder(includes), call_graph(call_graph) {} + explicit CTUApexConsumer(metacg::Callgraph *call_graph, std::vector<ExtractionBlock> &includes, + std::vector<ExtractionBlock> ¯os, std::vector<ExtractionBlock>& pragmas) + : call_graph(call_graph), includes(includes), macros(macros), pragmas(pragmas) { + } + + + void outlineFunctions(); + void outlineGlobals(); - std::string outline(metacg::Callgraph &cg); + std::string mergeSorted(); virtual void HandleTranslationUnit(clang::ASTContext &Context); private: - void addCalleesToSet(std::unordered_set<clang::Decl *> &set, size_t nodeID, const metacg::Callgraph &cg); - std::unordered_set<clang::Decl *> getCutout(metacg::Callgraph &cg); + std::vector<ExtractionBlock> funcs; + std::vector<ExtractionBlock> globals; - IncludeFinder* completedIncludeFinder; //not owned std::vector<clang::CallExpr *> calls; - metacg::Callgraph *call_graph; // not owned + // not owned + metacg::Callgraph *call_graph; + std::vector<ExtractionBlock>& includes; + std::vector<ExtractionBlock>& macros; + std::vector<ExtractionBlock>& pragmas; + + ExtractionBlock getNextExtractionBlock(); + + void getAvailableFunctions(metacg::Callgraph callgraph); }; + #endif //CTUAPEX_CTUAPEXCONSUMER_H diff --git a/include/ExtractionBlock.h b/include/ExtractionBlock.h new file mode 100644 index 0000000000000000000000000000000000000000..64d235140222bbae9eeff56de348457ed2ddbab4 --- /dev/null +++ b/include/ExtractionBlock.h @@ -0,0 +1,20 @@ +// +// Created by tim on 02.01.24. +// + +#ifndef CTUAPEX_EXTRACTIONBLOCK_H +#define CTUAPEX_EXTRACTIONBLOCK_H +struct ExtractionBlock { + ExtractionBlock()=default; + + ExtractionBlock(clang::SourceLocation b, clang::SourceLocation e, std::string c) { + begin = b; + end = e; + contents = c; + } + + clang::SourceLocation begin; + clang::SourceLocation end; + std::string contents; +}; +#endif //CTUAPEX_EXTRACTIONBLOCK_H diff --git a/include/ExtractionMetadata.h b/include/ExtractionMetadata.h index 6e0d32b4ecca1ec14643ffdfed806da45bbf7dc0..ad733425316c25fe8bd765b65598c3a2a7143fa4 100644 --- a/include/ExtractionMetadata.h +++ b/include/ExtractionMetadata.h @@ -32,6 +32,7 @@ public: clang::FunctionDecl *get_decl_node() const {return mappedDecl;}; bool isMarkedToExtract(){return extract;}; + void setMarkedToExtract(bool shouldExtract){extract=shouldExtract;} private: clang::FunctionDecl* mappedDecl= nullptr; bool extract=false; diff --git a/include/IncludeFinder.h b/include/IncludeFinder.h deleted file mode 100644 index fa4c854e439cf38d722da953e5fc46d03c91b7f3..0000000000000000000000000000000000000000 --- a/include/IncludeFinder.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// Created by tim on 21.12.23. -// - -#ifndef CTUAPEX_INCLUDEFINDER_H -#define CTUAPEX_INCLUDEFINDER_H - -#include <clang/Lex/PPCallbacks.h> -#include <clang/Lex/Lexer.h> -#include <spdlog/spdlog.h> - -class IncludeFinder : public clang::PPCallbacks { -public: - IncludeFinder(clang::SourceManager &sm) : sourceManager(sm) {} - - inline std::unique_ptr<clang::PPCallbacks> createPreprocessorCallbacks() { - SPDLOG_INFO("Collecting include statements"); - return std::make_unique<IncludeFinder>(*this); - } - - inline void InclusionDirective(clang::SourceLocation HashLoc, const clang::Token &IncludeTok, - clang::StringRef FileName, bool IsAngled, - clang::CharSourceRange FilenameRange, - clang::Optional<clang::FileEntryRef> File, - clang::StringRef SearchPath, clang::StringRef RelativePath, - const clang::Module *Imported, - clang::SrcMgr::CharacteristicKind FileType) override { - //Note: we currently exclude headers only by name, we could also compare them according to their sourcerange - if (sourceManager.isInMainFile(HashLoc)) { - includes.emplace_back(HashLoc, FilenameRange.getEnd(), FileName.str()); - } - }; - - - void Endif(clang::SourceLocation Loc, clang::SourceLocation IfLoc) override; - - inline void PragmaDirective(clang::SourceLocation Loc, - clang::PragmaIntroducerKind Introducer) override; - - struct PreprocessorExtract { - PreprocessorExtract(clang::SourceLocation b, clang::SourceLocation e, std::string c) : begin(b), end(e), - contents(std::move(c)) {} - - clang::SourceLocation begin; - clang::SourceLocation end; - std::string contents; - }; - - std::string name; - std::vector<PreprocessorExtract> includes; - std::vector<PreprocessorExtract> macros; - clang::SourceManager &sourceManager; -}; - -#endif //CTUAPEX_INCLUDEFINDER_H diff --git a/include/PreprocessorExtract.h b/include/PreprocessorExtract.h new file mode 100644 index 0000000000000000000000000000000000000000..01fab77ae84ce7b062bde18f1b610bc05a4001a8 --- /dev/null +++ b/include/PreprocessorExtract.h @@ -0,0 +1,56 @@ +// +// Created by tim on 21.12.23. +// + +#ifndef CTUAPEX_PREPROCESSOREXTRACT_H +#define CTUAPEX_PREPROCESSOREXTRACT_H + +#include <clang/Lex/PPCallbacks.h> +#include <clang/Lex/Lexer.h> +#include <spdlog/spdlog.h> + +#include <utility> + +#include "iostream" + +#include "ExtractionBlock.h" + +class PreprocessorExtract : public clang::PPCallbacks { +public: + PreprocessorExtract(clang::SourceManager &sm, std::vector<ExtractionBlock> &includes, + std::vector<ExtractionBlock> ¯os, std::vector<ExtractionBlock> &pragmas) : sourceManager( + sm), includes(includes), macros(macros), pragmas(pragmas) {} + + inline void InclusionDirective(clang::SourceLocation HashLoc, const clang::Token &IncludeTok, + clang::StringRef FileName, bool IsAngled, + clang::CharSourceRange FilenameRange, + clang::Optional<clang::FileEntryRef> File, + clang::StringRef SearchPath, clang::StringRef RelativePath, + const clang::Module *Imported, + clang::SrcMgr::CharacteristicKind FileType) override; + + void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + ConditionValueKind ConditionValue) override; + + void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDefinition &MD) override; + + void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDefinition &MD) override; + + void Endif(clang::SourceLocation Loc, clang::SourceLocation IfLoc) override; + + inline void PragmaDirective(clang::SourceLocation Loc, + clang::PragmaIntroducerKind Introducer) override; + +private: + std::vector<ExtractionBlock> &includes; + std::vector<ExtractionBlock> ¯os; + std::vector<ExtractionBlock> &pragmas; + //FixMe: make macroBeginStack with beginSourceLoc to save space + std::stack<ExtractionBlock> macroNestingStack; + clang::SourceManager &sourceManager; + +}; + +#endif //CTUAPEX_PREPROCESSOREXTRACT_H diff --git a/include/Util.h b/include/Util.h index 5210552c2745c7cef7bda6686957b0d8ff616a2a..db77ae161e92e404204787ff0822274d83e19423 100644 --- a/include/Util.h +++ b/include/Util.h @@ -44,6 +44,7 @@ inline bool contains(const C &c, const T &val) { return std::find(c.begin(), c.end(), val) != c.end(); } +/* template <typename T> std::string col2str(const T &col, const std::string &prefix = "", const std::string &split = ",", const std::string &suffix = ""){ @@ -56,28 +57,21 @@ std::string col2str(const T &col, const std::string &prefix = "", const std::str ret += suffix; return ret; } +*/ +template<typename T> +std::string col2str(const T &col, const std::string &prefix = "", const std::string &split = ",", + const std::string &suffix = "", std::string (*transformer)( + typename std::remove_reference<decltype(*std::declval<T>().begin())>::type) = []( + typename std::remove_reference<decltype(*std::declval<T>().begin())>::type t) -> std::string { return t; }) { + std::string ret = prefix; -template <typename T> -std::string col2str(const T &col, std::string (*transformer)(typename std::remove_reference< decltype( *std::declval<T>().begin() ) >::type), const std::string &prefix = "", const std::string &split = ",", - const std::string &suffix = ""){ - std::cout<<__LINE__<<"\n"; - std::string ret=prefix; - auto begin=col.begin(); - std::cout<<__LINE__<<"\n"; - auto end=std::prev(col.end()); - std::cout<<__LINE__<<"\n"; - for (; begin != end; begin++) { - std::cout<<__LINE__<<"\n"; + for (auto begin = col.begin(), end = std::prev(col.end()); begin != end; begin++) { ret += transformer(*begin); - std::cout<<__LINE__<<"\n"; ret += split; - std::cout<<__LINE__<<"\n"; } - std::cout<<__LINE__<<"\n"; - ret += transformer(*col.end()); - std::cout<<__LINE__<<"\n"; + ret += transformer(col.back()); + ret += suffix; - std::cout<<__LINE__<<"\n"; return ret; } diff --git a/src/CTUApexAction.cpp b/src/CTUApexAction.cpp index e449c0681122ba2c11bc61785cbf2352b581af25..8c4b9261065c634de84f92950dd1622be117ffe4 100644 --- a/src/CTUApexAction.cpp +++ b/src/CTUApexAction.cpp @@ -4,11 +4,10 @@ #include "CTUApexConsumer.h" #include <clang/Frontend/CompilerInstance.h> -#include "IncludeFinder.h" std::unique_ptr<clang::ASTConsumer> CTUApexAction::CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) { - IncludeFinder includeFinder(Compiler.getSourceManager()); - Compiler.getPreprocessor().addPPCallbacks(includeFinder.createPreprocessorCallbacks()); - return std::make_unique<CTUApexConsumer>(&includeFinder, call_graph); + std::unique_ptr<PreprocessorExtract> includeFinder = std::make_unique<PreprocessorExtract>(Compiler.getSourceManager(), includes, macros, pragmas); + Compiler.getPreprocessor().addPPCallbacks(std::move(includeFinder)); + return std::make_unique<CTUApexConsumer>(call_graph,includes,macros,pragmas); } \ No newline at end of file diff --git a/src/CTUApexConsumer.cpp b/src/CTUApexConsumer.cpp index c19d7fe846b3a2cb3e5c3705b8b0d00fb9c558c2..6c979368cb07b5db4525943b9aec5c19719972c5 100644 --- a/src/CTUApexConsumer.cpp +++ b/src/CTUApexConsumer.cpp @@ -8,122 +8,148 @@ #include "llvm/Demangle/Demangle.h" #include "FunctionFinderVisitor.h" -std::unordered_set<clang::Decl *> CTUApexConsumer::getCutout(metacg::Callgraph &cg) { - std::unordered_set<clang::Decl *> retSet; - std::unordered_set<size_t> entryNodes; - for (const auto &node: cg.getNodes()) { - if (node.second->has<ExtractionMetadata>() && node.second->get<ExtractionMetadata>()->isMarkedToExtract()) - entryNodes.insert(node.first); - } - - for (auto id: entryNodes) { - addCalleesToSet(retSet, id, cg); - } - - std::cout << "Cutout for this TU is:\n"; - for (auto &elem: retSet) { - std::cout << clang::cast<clang::FunctionDecl>(elem)->getNameAsString() << "\n"; - } - std::cout << std::flush; - return retSet; - - -} - -std::string CTUApexConsumer::outline(metacg::Callgraph &cg) { - //TODO: Move this so its only computed once for each run, not once for each file - SPDLOG_INFO("Compute set of functions to be extracted"); - - std::unordered_set<clang::Decl *> availableFunctionDecls; - for (const auto &node: cg.getNodes()) { - if (node.second->has<ExtractionMetadata>() && node.second->get<ExtractionMetadata>()->isMarkedToExtract()) { - if (node.second->get<ExtractionMetadata>()->get_decl_node() == nullptr) { - std::cout << "node " << node.second->getFunctionName() - << " was marked to extract but is not part of this TU\n"; - } else { - auto functionDecl = node.second->get<ExtractionMetadata>()->get_decl_node(); - availableFunctionDecls.insert(functionDecl); +void CTUApexConsumer::outlineFunctions() { + for (const auto &node: call_graph->getNodes()) { + if (auto md = node.second->checkAndGet<ExtractionMetadata>(); md.first && md.second->get_decl_node()) { + ExtractionBlock eb; + clang::SourceRange sourceRange = md.second->get_decl_node()->getSourceRange(); + eb.begin = sourceRange.getBegin(); + eb.end = sourceRange.getEnd(); + if(md.second->isMarkedToExtract()){ + //std::cout<<"Need to extract: "<<md.second->get_decl_node()->getNameAsString()<<"\n"; + eb.contents = clang::Lexer::getSourceText(clang::CharSourceRange::getTokenRange(sourceRange), + md.second->get_decl_node()->getASTContext().getSourceManager(), + clang::LangOptions(), + nullptr).str(); + }else{ + //std::cout<<"Not extracting: "<<md.second->get_decl_node()->getNameAsString()<<"\n"; + eb.contents=""; } - addCalleesToSet(availableFunctionDecls, node.first, cg); + funcs.push_back(eb); } } - std::cout << "Function marked to be extracted from this TU:\n"; - for (auto &elem: availableFunctionDecls) { - std::cout << clang::cast<clang::FunctionDecl>(elem)->getNameAsString() << "\n"; - } + //the callgraph nodes are unsorted, we need to sort the extraction blocks afterward + std::sort(funcs.begin(),funcs.end(),[](const ExtractionBlock& a, const ExtractionBlock& b){return a.begin<b.begin;}); +} +void CTUApexConsumer::outlineGlobals() { //Fixme: does this need to extract cross TU as well? SPDLOG_INFO("Compute set of used globals"); std::unordered_set<clang::Decl *> availableGlobals; - for (const auto &node: availableFunctionDecls) { - assert(clang::isa<clang::FunctionDecl>(node) && - "Extractionset should only contain function decls at this point"); - GlobalsMarker gMarker; - auto func_set = gMarker.get_nodes_to_outline(clang::cast<clang::FunctionDecl>(node)); - availableGlobals.insert(func_set.begin(), func_set.end()); + std::vector<ExtractionBlock> returnBlocks; + for (const auto &node: call_graph->getNodes()) { + if (auto md = node.second->checkAndGet<ExtractionMetadata>(); md.first && md.second->get_decl_node()) { + assert(clang::isa<clang::FunctionDecl>(md.second->get_decl_node()) && + "Extractionset should only contain function decls at this point"); + GlobalsMarker gMarker; + auto globalsSet = gMarker.get_nodes_to_outline(clang::cast<clang::FunctionDecl>(md.second->get_decl_node())); + for (const auto &func: globalsSet) { + ExtractionBlock eb; + eb.begin = func->getBeginLoc(); + eb.end = func->getEndLoc(); + clang::SourceRange range(eb.begin, eb.end); + clang::CharSourceRange charSourceRange(range, true); + eb.contents = clang::Lexer::getSourceText(charSourceRange, func->getASTContext().getSourceManager(), + clang::LangOptions(), + nullptr).str(); + globals.push_back(eb); + } + } } +} - std::string returnString; - - for (const auto &decl: availableGlobals) { - clang::SourceRange sourceRange = decl->getSourceRange(); - returnString += clang::Lexer::getSourceText(clang::CharSourceRange::getTokenRange(sourceRange), - decl->getASTContext().getSourceManager(), clang::LangOptions(), - nullptr).str(); - returnString += "\n"; +ExtractionBlock CTUApexConsumer::getNextExtractionBlock() { + //find the earliest block by starting source location + ExtractionBlock min; + if (!includes.empty()) { + SPDLOG_TRACE("Setting include as assumed min"); + min = includes.front(); + } else if (!funcs.empty()) { + SPDLOG_TRACE("Setting funcs as assumed min"); + min = funcs.front(); + } else if (!globals.empty()) { + SPDLOG_TRACE("Setting globals as assumed min"); + min = globals.front(); + } else if (!macros.empty()) { + SPDLOG_TRACE("Setting macros as assumed min"); + min = macros.front(); + } else if (!pragmas.empty()) { + SPDLOG_TRACE("Setting pragmas as assumed min"); + min = pragmas.front(); } - std::vector<clang::Decl *> sortedFunctionDecls = {}; - - - sortedFunctionDecls.reserve(availableFunctionDecls.size()); - for (const auto &decl: availableFunctionDecls) { - sortedFunctionDecls.push_back(decl); + while (!funcs.empty() && min.begin > funcs.front().begin) { + SPDLOG_TRACE("Overwriting with funcs as min"); + min = funcs.front(); + } + while (!globals.empty() && min.begin > globals.front().begin) { + SPDLOG_TRACE("Overwriting with globals as min"); + min = globals.front(); + } + while (!macros.empty() && min.begin > macros.front().begin) { + SPDLOG_TRACE("Overwriting with macros as min"); + min = macros.front(); + } + while (!pragmas.empty() && min.begin > pragmas.front().begin) { + SPDLOG_TRACE("Overwriting with pragmas as min"); + min = pragmas.front(); } - std::sort(sortedFunctionDecls.begin(),sortedFunctionDecls.end(),[](clang::Decl* a, clang::Decl* b){return a->getBeginLoc()<b->getLocation();}); - - for (const auto &decl: sortedFunctionDecls) { - clang::SourceRange sourceRange = decl->getSourceRange(); - returnString += clang::Lexer::getSourceText(clang::CharSourceRange::getTokenRange(sourceRange), - decl->getASTContext().getSourceManager(), clang::LangOptions(), - nullptr).str(); - returnString += "\n"; + //if the earliest blocks end is after another block, we fully contain the other block + //we don't need to merge that block anymore + while (!includes.empty() && min.end >= includes.front().end) { + SPDLOG_TRACE("Erasing include entry"); + includes.erase(includes.begin()); + } + while (!funcs.empty() && min.end >= funcs.front().end) { + SPDLOG_TRACE("Erasing funcs entry"); + funcs.erase(funcs.begin()); + } + while (!globals.empty() && min.end >= globals.front().end) { + SPDLOG_TRACE("Erasing globals entry"); + globals.erase(globals.begin()); + } + while (!macros.empty() && min.end >= macros.front().end) { + SPDLOG_TRACE("Erasing macros entry"); + macros.erase(macros.begin()); + } + while (!pragmas.empty() && min.end >= pragmas.front().end) { + SPDLOG_TRACE("Erasing pragmas entry"); + pragmas.erase(pragmas.begin()); } - return returnString; + return min; } - void CTUApexConsumer::HandleTranslationUnit(clang::ASTContext &Context) { const auto &translation_unit = Context.getTranslationUnitDecl(); + SPDLOG_INFO("Mapping functions to the call graph"); FunctionFinderVisitor functionFinderVisitor(call_graph); functionFinderVisitor.TraverseTranslationUnitDecl(translation_unit); - for (auto &node: call_graph->getNodes()) { - if (node.second->has<ExtractionMetadata>() && - node.second->get<ExtractionMetadata>()->get_decl_node() != nullptr) { - std::cout << "Found " << node.second->getFunctionName() << " in TU\n"; - } - } - - SPDLOG_INFO("Generate code skeleton"); + SPDLOG_INFO("Outlining Functions"); + outlineFunctions(); - auto t = outline(*call_graph); + SPDLOG_INFO("Outlining Globals"); + outlineGlobals(); + SPDLOG_INFO("Generate code skeleton"); const auto &sm = Context.getSourceManager(); std::string filename = sm.getFileEntryForID(sm.getMainFileID())->getName().str(); filename.insert(filename.find_last_of('.') + 1, "mini."); std::ofstream outFile; outFile.open(filename); assert(outFile.is_open()); - outFile /*<< col2str(completedIncludeFinder.includes, "#include \"", "\"\n#include \"", "\"\n") */<< t << "\n"; - outFile.close(); + while (!(includes.empty() && funcs.empty() && globals.empty() && macros.empty() && pragmas.empty())) { + outFile << getNextExtractionBlock().contents << "\n"; + } + outFile.close(); SPDLOG_INFO("Generate wrapper calls"); + SPDLOG_CRITICAL("WRAPPER CALLS CAN NOT YET BE IMPLEMENTED"); // Outline the code we want //SPDLOG_DEBUG("Generating outline"); @@ -147,15 +173,4 @@ void CTUApexConsumer::HandleTranslationUnit(clang::ASTContext &Context) { } -void CTUApexConsumer::addCalleesToSet(std::unordered_set<clang::Decl *> &set, const size_t nodeID, const metacg::Callgraph &cg) { - for (const auto &call: cg.getCallees(nodeID)) { - if (call->has<ExtractionMetadata>() && call->get<ExtractionMetadata>()->get_decl_node() != nullptr) { - set.insert(call->get<ExtractionMetadata>()->get_decl_node()); - SPDLOG_DEBUG("{} attached AST node",call->getFunctionName()); - } else { - //SPDLOG_DEBUG("{} has no attached AST node",call->getFunctionName()); - } - addCalleesToSet(set, call->getId(), cg); - } -} diff --git a/src/CTUApexVisitor.cpp b/src/CTUApexVisitor.cpp index 8b458de4baa6039ac6a3b53625ec4454de740c67..fb17c50c43212fd454aeda30cf73491267b67677 100644 --- a/src/CTUApexVisitor.cpp +++ b/src/CTUApexVisitor.cpp @@ -27,8 +27,13 @@ std::vector<std::string> getMangledName(clang::NamedDecl const *const nd) { } bool FunctionFinderVisitor::VisitFunctionDecl(clang::FunctionDecl *functionDecl) { + //If the function is not from main file (e.g a header defined function, don't mark + if(!functionDecl->getASTContext().getSourceManager().isInMainFile(functionDecl->getSourceRange().getBegin())){ + return true; + } + if(functionDecl->isDependentContext()){ - //SPDLOG_TRACE("Function {} is context dependend\n",functionDecl->getNameAsString()); + SPDLOG_DEBUG("Function {} is context dependent",functionDecl->getNameAsString()); return true; } @@ -37,20 +42,13 @@ bool FunctionFinderVisitor::VisitFunctionDecl(clang::FunctionDecl *functionDecl) //std::cout<<functionDecl->getNameAsString()<<col2str(possibleNames," All possible manglings are:(",",",")")<<"\n"; for(const auto& name : possibleNames){ - // We should only override if we have a body or if it is null - if(functionDecl->getASTContext().getSourceManager().isInMainFile(functionDecl->getSourceRange().getBegin())){ - //SPDLOG_DEBUG("Function is part of main file\n"); - }else{ - //SPDLOG_DEBUG("Function is from header\n"); + // we should find every node in the whole program callgraph + if(!callgraph->hasNode(name)){ + SPDLOG_WARN("Node {} is not in the call-graph. Graph might be wrong or incomplete!",name); continue; } - if(!callgraph->hasNode(name)){ // if we do not have the node, it is not used in the program and can be ignored - //std::cout<<"Node "<<name <<" is not in the graph\n"; - continue; - } - - //SPDLOG_TRACE("Attaching AST node: {}, to the call graph",possibleNames.at(0)); + // We should only override if we have a body or if it is null if (functionDecl->hasBody() || callgraph->getNode(name)->getOrCreateMD<ExtractionMetadata>()->get_decl_node()== nullptr) { callgraph->getNode(name)->getOrCreateMD<ExtractionMetadata>()->set_decl_node(functionDecl); diff --git a/src/GlobalsMarker.cpp b/src/GlobalsMarker.cpp index 22fae1e528eed3fbd92ab5dabd6cbe35afd90222..c1b901610a4b293c39e22f1862f7f5c7d3e27b55 100644 --- a/src/GlobalsMarker.cpp +++ b/src/GlobalsMarker.cpp @@ -11,7 +11,7 @@ bool GlobalsMarker::VisitDeclRefExpr(clang::DeclRefExpr *decl_ref){ auto *decl = decl_ref->getDecl(); if(!decl_ref->getDecl()->getASTContext().getSourceManager().isInMainFile(decl_ref->getDecl()->getSourceRange().getBegin())){ - std::cout<<"We reference a declaration that is part of a header, we dont need to extract it\n"; + //std::cout<<"We reference a declaration that is part of a header, we don't need to extract it\n"; return true; } diff --git a/src/IncludeFinder.cpp b/src/IncludeFinder.cpp deleted file mode 100644 index 330ba6bc2e7d2e60fbf7c5385050f8d86780aef8..0000000000000000000000000000000000000000 --- a/src/IncludeFinder.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by tim on 21.12.23. -// -#include "IncludeFinder.h" - -#include <iostream> - -void IncludeFinder::Endif(clang::SourceLocation Loc, clang::SourceLocation IfLoc) { - clang::LangOptions langOptions; - std::cout << "Preprocessor found #if #endif: \n" << clang::Lexer::getSourceText( - clang::Lexer::getAsCharRange(clang::SourceRange(IfLoc, Loc), sourceManager, clang::LangOptions()), - sourceManager, clang::LangOptions()).str() << "\n"; - std::cout << "------------------------------------\n"; -} - -inline void IncludeFinder::PragmaDirective(clang::SourceLocation Loc, - clang::PragmaIntroducerKind Introducer) { - if (!sourceManager.isInMainFile(Loc)) { - return; - } - std::cout << "Preprocessor found pragma: " << Loc.printToString(sourceManager) << "\n"; - Loc.dump(sourceManager); -} \ No newline at end of file diff --git a/src/PreprocessorExtract.cpp b/src/PreprocessorExtract.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5b5dd4f3bed1654a27cfa11e4043a776f3230ebc --- /dev/null +++ b/src/PreprocessorExtract.cpp @@ -0,0 +1,71 @@ +// +// Created by tim on 21.12.23. +// +#include "PreprocessorExtract.h" + + +void PreprocessorExtract::If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + ConditionValueKind ConditionValue) { + if (!sourceManager.isInMainFile(Loc)) { + return; + } + assert(Loc.isValid()); + macroNestingStack.emplace(Loc, Loc, ""); +} +void PreprocessorExtract::Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDefinition &MD) { + if (!sourceManager.isInMainFile(Loc)) { + return; + } + assert(Loc.isValid()); + macroNestingStack.emplace(Loc, Loc, ""); +} +void PreprocessorExtract::Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDefinition &MD) { Ifdef(Loc, MacroNameTok, MD); }; + +void PreprocessorExtract::Endif(clang::SourceLocation Loc, clang::SourceLocation IfLoc) { + if (!sourceManager.isInMainFile(Loc)) { + return; + } + assert(macroNestingStack.top().begin.isValid()); + assert(macroNestingStack.top().end.isValid()); + assert(IfLoc.isValid()); + assert("If these are not equal, we overwrote the last found macro end location allready" && + macroNestingStack.top().begin == macroNestingStack.top().end); + assert("The last found macros start should be the if loc we found" && macroNestingStack.top().begin == IfLoc); + ExtractionBlock macro = macroNestingStack.top(); + macroNestingStack.pop(); + macro.end = Loc; + clang::SourceRange sourceRange(macro.begin,macro.end); + clang::CharSourceRange charSourceRange(sourceRange,true); + macro.contents = "#"+clang::Lexer::getSourceText(charSourceRange,sourceManager,clang::LangOptions()).str(); + macros.push_back(macro); +} + +inline void PreprocessorExtract::PragmaDirective(clang::SourceLocation Loc, + clang::PragmaIntroducerKind Introducer) { + if (!sourceManager.isInMainFile(Loc)) { + return; + } + clang::SourceRange sourceRange(Loc,Loc); + clang::CharSourceRange charSourceRange(sourceRange,true); + ExtractionBlock res; + res.begin=Loc; + res.end=charSourceRange.getEnd(); + res.contents=clang::Lexer::getSourceText(charSourceRange,sourceManager,clang::LangOptions()); + std::cout<<res.contents; + pragmas.push_back(res); +} + +void PreprocessorExtract::InclusionDirective(clang::SourceLocation HashLoc, const clang::Token &IncludeTok, + clang::StringRef FileName, bool IsAngled, + clang::CharSourceRange FilenameRange, + clang::Optional<clang::FileEntryRef> File, + clang::StringRef SearchPath, clang::StringRef RelativePath, + const clang::Module *Imported, + clang::SrcMgr::CharacteristicKind FileType) { +//Note: we currently exclude headers only by name, we could also compare them according to their sourcerange + if (sourceManager.isInMainFile(HashLoc)) { + includes.emplace_back(HashLoc, FilenameRange.getEnd(), "#include \""+FileName.str()+"\""); + } +} diff --git a/src/main.cpp b/src/main.cpp index f050d5fba574a4b9ae82fb0e04f17e0a8595ca32..9c40d69553d250867ccfc1060eea2a5b207a69da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,9 @@ class FilePropertiesMetaData : public metacg::MetaData::Registrar<FilePropertiesMetaData> { public: static constexpr const char *key = "fileProperties"; + FilePropertiesMetaData() : origin("INVALID"), fromSystemInclude(false), lineNumber(0) {} + explicit FilePropertiesMetaData(const nlohmann::json &j) { if (j.is_null()) { metacg::MCGLogger::instance().getConsole()->trace("Could not retrieve meta data for {}", key); @@ -48,10 +50,13 @@ public: bool fromSystemInclude; int lineNumber; }; + class PiraOneData : public metacg::MetaData::Registrar<PiraOneData> { public: static constexpr const char *key = "numStatements"; + PiraOneData() = default; + explicit PiraOneData(const nlohmann::json &j) { metacg::MCGLogger::instance().getConsole()->trace("Running PiraOneMetaDataRetriever::read from json"); if (j.is_null()) { @@ -72,13 +77,21 @@ public: virtual const char *getKey() const final { return key; } void setNumberOfStatements(int numStmts) { this->numStmts = numStmts; } + int getNumberOfStatements() const { return this->numStmts; } + void setHasBody(bool hasBody = true) { this->hasBody = hasBody; } + bool getHasBody() const { return this->hasBody; } + void setDominantRuntime(bool dominantRuntime = true) { this->dominantRuntime = dominantRuntime; } + bool isDominantRuntime() const { return this->dominantRuntime; } + void setComesFromCube(bool fromCube = true) { this->wasInPreviousProfile = fromCube; } + bool comesFromCube() const { return this->wasInPreviousProfile; } + bool inPreviousProfile() const { return wasInPreviousProfile; } private: @@ -122,7 +135,6 @@ static cl::opt<bool> DumpAnalysisResult("dump-analysis-result", cl::desc("Dump the dependence analysis result to stdout"), cl::cat(CTUApex)); - template<typename T, class... Args> std::unique_ptr<clang::tooling::FrontendActionFactory> argumentParsingFrontendActionFactory(Args... args) { @@ -143,10 +155,24 @@ std::unique_ptr<clang::tooling::FrontendActionFactory> argumentParsingFrontendAc return std::unique_ptr<FrontendActionFactory>(new SimpleFrontendActionFactory(args...)); } -//Der einsprungspunt in das programm + +void markCalleesToExtract(const size_t nodeID, const metacg::Callgraph &cg) { + for (const auto &call: cg.getCallees(nodeID)) { + call->getOrCreateMD<ExtractionMetadata>()->setMarkedToExtract(true); + markCalleesToExtract(call->getId(), cg); + } +} +void markFunctionsAlongCallpaths(metacg::Callgraph &cg) { + SPDLOG_INFO("Compute set of functions to be extracted"); + for (const auto &node: cg.getNodes()) { + if (auto md = node.second->checkAndGet<ExtractionMetadata>(); + md.first && md.second->isMarkedToExtract()) { + markCalleesToExtract(node.first, cg); + } + } +} + int main(int argc, const char **argv) { - //Wir wollen commandozeilen parameter verarbeiten können, also nehmen wir einen option parser - //Da beim parsen etwas schief gehen kann, machen wir den extra schritt mit expectation auto ExpectedParser = CommonOptionsParser::create(argc, argv, CTUApex); @@ -156,7 +182,6 @@ int main(int argc, const char **argv) { return 1; } - switch (LoggingLevel) { case LogLevel::Trace: spdlog::set_level(spdlog::level::trace); @@ -178,19 +203,18 @@ int main(int argc, const char **argv) { break; } - //Das ist der eigentliche option parser CommonOptionsParser &OptionsParser = ExpectedParser.get(); - //Wir erstellen ein clang tool objekt ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); - std::string error="error"; - auto fcd = FixedCompilationDatabase::loadFromDirectory("",error); + std::string error = "error"; + auto fcd = FixedCompilationDatabase::loadFromDirectory("", error); metacg::io::FileSource fs(cg_file.getValue()); metacg::io::VersionTwoMetaCGReader mcgReader(fs); - auto& mcgManager=metacg::graph::MCGManager::get(); - mcgManager.addToManagedGraphs("emptyGraph",std::make_unique<metacg::Callgraph>()); + auto &mcgManager = metacg::graph::MCGManager::get(); + mcgManager.addToManagedGraphs("emptyGraph", std::make_unique<metacg::Callgraph>()); mcgReader.read(mcgManager); - //Wir führen die frontend action mit unserem tool aus + markFunctionsAlongCallpaths(*mcgManager.getCallgraph()); + Tool.run(argumentParsingFrontendActionFactory<CTUApexAction>(mcgManager.getCallgraph()).get()); return 0; }