diff --git a/include/CommandLineArgs.h b/include/CommandLineArgs.h new file mode 100644 index 0000000000000000000000000000000000000000..bd24f4a09daef14aec11d936c6a9f15bea57666b --- /dev/null +++ b/include/CommandLineArgs.h @@ -0,0 +1,40 @@ +// +// Created by tim on 28.11.24. +// + +#ifndef CTUAPEX_COMMANDLINEARGS_H +#define CTUAPEX_COMMANDLINEARGS_H +#include <clang/Tooling/CommonOptionsParser.h> +using namespace llvm; +//Die commandozeilen optionen die wir parsen wollen, gehören zu unserem program +static cl::OptionCategory CTUApex("Cross Translation Unit App Extraction"); + +static cl::opt<std::string> cg_file("cg-file", cl::desc("MetaCG file containing a whole programm call graph"), + cl::value_desc("filename"), cl::Required, cl::cat(CTUApex)); + +enum class LogLevel { + Trace, Debug, Info, Warning, Error, /* Critical,*/ Off +}; + +static cl::list<std::string> extractionPoints("extract", + cl::desc("Qualified names of the function to start the extraction from"), + cl::value_desc("Comma separated list of function names"), cl::Optional, + cl::cat(CTUApex)); + +static cl::opt<LogLevel> LoggingLevel("log-level", cl::desc("Select log level"), + cl::values(clEnumValN(LogLevel::Trace, "trace", "Enable Trace and higher logs"), + clEnumValN(LogLevel::Debug, "debug", "Enable Debug and higher logs"), + clEnumValN(LogLevel::Info, "info", "Enable Info and higher logs"), + clEnumValN(LogLevel::Warning, "warning", + "Enable Warning and higher logs"), + clEnumValN(LogLevel::Error, "error", "Enable only Error logs"), + clEnumValN(LogLevel::Off, "off", "Disable all logging")), + cl::init(LogLevel::Info), cl::cat(CTUApex)); + +static cl::opt<bool> OnlyWrapper("generate-only-wrapper", cl::desc("Output only the wrapper function"), + cl::cat(CTUApex)); +static cl::opt<bool> DumpAnalysisResult("dump-analysis-result", + cl::desc("Dump the dependence analysis result to stdout"), cl::cat(CTUApex)); + + +#endif //CTUAPEX_COMMANDLINEARGS_H diff --git a/include/FunctionFinderVisitor.h b/include/FunctionFinderVisitor.h index 0d9ce1130110901b069ae6a903d5aed3abdb03cf..1298599970224c03557bfa6a7808ce94f589c02e 100644 --- a/include/FunctionFinderVisitor.h +++ b/include/FunctionFinderVisitor.h @@ -17,9 +17,11 @@ public: //This visits function declarations with the visitor pattern bool VisitFunctionDecl(clang::FunctionDecl *functionDecl); + bool VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *D); private: metacg::Callgraph* callgraph; + }; diff --git a/src/CTUApexVisitor.cpp b/src/CTUApexVisitor.cpp index fbb2d0834f2730e938625a260709e7ec3d7d437d..761560c8a59d845e99476b3e003f6c318ddea69e 100644 --- a/src/CTUApexVisitor.cpp +++ b/src/CTUApexVisitor.cpp @@ -26,33 +26,64 @@ std::vector<std::string> getMangledName(clang::NamedDecl const *const nd) { return {NG.getName(nd)}; } +bool FunctionFinderVisitor::VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *D) { + if (!D->getASTContext().getSourceManager().isInMainFile(D->getSourceRange().getBegin())) { + return true; + } + SPDLOG_DEBUG("FunctionTemplateDecl {} is visited", D->getNameAsString()); + + for (const auto &s: D->specializations()) { + for (const auto &name: getMangledName(s)) { + + assert(callgraph->hasNode(name)); + + // 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; + } + + // We should only override if we have a body or if it is null + if (s->hasBody() || callgraph->getNode(name)->getOrCreateMD<ExtractionMetadata>()->get_decl_node() == nullptr) { + callgraph->getNode(name)->getOrCreateMD<ExtractionMetadata>()->set_decl_node(s); + } + + } + } + + return true; +} + 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())){ + if (!functionDecl->getASTContext().getSourceManager().isInMainFile(functionDecl->getSourceRange().getBegin())) { return true; } - if(functionDecl->isDependentContext()){ - SPDLOG_DEBUG("Function {} is context dependent",functionDecl->getNameAsString()); + + if (functionDecl->isDependentContext()) { + SPDLOG_DEBUG("Function {} is context dependent", functionDecl->getNameAsString()); return true; } - const auto possibleNames = getMangledName(functionDecl); + SPDLOG_DEBUG("Function {} is visited", functionDecl->getNameAsString()); + + const auto &possibleNames = getMangledName(functionDecl); //std::cout<<functionDecl->getNameAsString()<<col2str(possibleNames," All possible manglings are:(",",",")")<<"\n"; - for(const auto& name : possibleNames){ + for (const auto &name: possibleNames) { assert(callgraph->hasNode(name)); // 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); + if (!callgraph->hasNode(name)) { + SPDLOG_WARN("Node {} is not in the call-graph. Graph might be wrong or incomplete!", name); continue; } // 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()== + 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/main.cpp b/src/main.cpp index c7e9e8efa23da6537ae933a5f2492b4ab9817dd9..35f25c794aae69d2fc2835f4df849fe967449dc9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,46 +19,34 @@ #include "Util.h" #include "ExtractionMetadata.h" +#include "CommandLineArgs.h" + +#include "llvm/Demangle/Demangle.h" //Das sind convenience definitionen, dass wir nicht so viel tippen müssen using namespace llvm; using namespace clang; using namespace clang::tooling; -using namespace clang; -//Die commandozeilen optionen die wir parsen wollen, gehören zu unserem program -static cl::OptionCategory CTUApex("Cross Translation Unit App Extraction"); -//static cl::opt<std::string> callsites_file_opt("callsites-file", cl::desc("Config file with the callsites to outline"), -// cl::value_desc("filename"), cl::Required, cl::cat(CTUApex)); -static cl::opt<std::string> cg_file("cg-file", cl::desc("MetaCG file containing a whole programm call graph"), - cl::value_desc("filename"), cl::Required, cl::cat(CTUApex)); -enum class LogLevel { - Trace, Debug, Info, Warning, Error, /* Critical,*/ Off -}; +template<typename T, class... Args> +std::unique_ptr<clang::tooling::FrontendActionFactory> argumentParsingFrontendActionFactory(Args... args) { + + class SimpleFrontendActionFactory : public clang::tooling::FrontendActionFactory { + public: + explicit SimpleFrontendActionFactory(Args... constructorArgs) { + fn1 = [=]() { return std::make_unique<T>(constructorArgs...); }; + } + + std::unique_ptr<clang::FrontendAction> create() override { + return fn1(); + } + + private: + std::function<std::unique_ptr<T>()> fn1; // function + }; -//static cl::opt<std::string> extractionStart("extract", -// cl::desc("Qualified name of the function to start the extraction from"), -// cl::value_desc("functionname"), cl::Optional, cl::cat(CTUApex)); - -static cl::list<std::string> extractionPoints("extract", - cl::desc("Qualified names of the function to start the extraction from"), - cl::value_desc("Comma separated list of function names"), cl::Optional, - cl::cat(CTUApex)); - -static cl::opt<LogLevel> LoggingLevel("log-level", cl::desc("Select log level"), - cl::values(clEnumValN(LogLevel::Trace, "trace", "Enable Trace and higher logs"), - clEnumValN(LogLevel::Debug, "debug", "Enable Debug and higher logs"), - clEnumValN(LogLevel::Info, "info", "Enable Info and higher logs"), - clEnumValN(LogLevel::Warning, "warning", - "Enable Warning and higher logs"), - clEnumValN(LogLevel::Error, "error", "Enable only Error logs"), - clEnumValN(LogLevel::Off, "off", "Disable all logging")), - cl::init(LogLevel::Info), cl::cat(CTUApex)); - -static cl::opt<bool> OnlyWrapper("generate-only-wrapper", cl::desc("Output only the wrapper function"), - cl::cat(CTUApex)); -static cl::opt<bool> DumpAnalysisResult("dump-analysis-result", - cl::desc("Dump the dependence analysis result to stdout"), cl::cat(CTUApex)); + return std::unique_ptr<FrontendActionFactory>(new SimpleFrontendActionFactory(args...)); +} class CTUApexFrontendAction : clang::ASTFrontendAction { public: @@ -99,23 +87,14 @@ void markFunctionsAlongCallpaths(metacg::Callgraph &cg) { } } -#include "clang/Basic/SourceManager.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Lex/HeaderSearch.h" -#include "clang/Tooling/Tooling.h" - -using namespace clang; -using namespace clang::tooling; -std::vector<std::string> clangIncludeBasedArgumentAdjustor(const CommandLineArguments & cla , StringRef sr){ +std::vector<std::string> clangIncludeBasedArgumentAdjustor(const CommandLineArguments &cla, StringRef sr) { //call the compiler specified in cla[0] with -S and -v like so std::string command = "clang"; //command.append(" -S -v"); command.append(" -W,p -v -fsyntax-only"); - for(int i=1;i<cla.size();i++){ - command.append(" "+cla[i]); + for (int i = 1; i < cla.size(); i++) { + command.append(" " + cla[i]); } command.append(" 2>&1"); std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose); @@ -126,20 +105,20 @@ std::vector<std::string> clangIncludeBasedArgumentAdjustor(const CommandLineArgu txt += buffer.data(); } //search for "include search starts here" string and add all the paths with -I to the tool invocation - txt=txt.substr(txt.rfind("search starts here:")+20,txt.rfind("End of search list.")-txt.rfind("search starts here:")-20); + txt = txt.substr(txt.rfind("search starts here:") + 20, + txt.rfind("End of search list.") - txt.rfind("search starts here:") - 20); std::vector<std::string> ret; - ret.assign(cla.begin(),cla.end()); + ret.assign(cla.begin(), cla.end()); std::string::size_type pos; std::string::size_type prev = 0; - while ((pos = txt.find('\n', prev)) != std::string::npos) - { - ret.push_back("-I"+txt.substr(prev, pos - prev)); + while ((pos = txt.find('\n', prev)) != std::string::npos) { + ret.push_back("-I" + txt.substr(prev, pos - prev)); prev = pos + 2; } return ret; } -std::vector<std::string> clangResourceDirBasedAdjuster(const CommandLineArguments & cla , StringRef){ +std::vector<std::string> clangResourceDirBasedAdjuster(const CommandLineArguments &cla, StringRef) { //call the compiler specified in cla[0] with -S and -v like so std::string command = "clang --print-resource-dir 2>1&"; std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose); @@ -150,13 +129,15 @@ std::vector<std::string> clangResourceDirBasedAdjuster(const CommandLineArgument txt += buffer.data(); } std::vector<std::string> ret; - ret.assign(cla.begin(),cla.end()); + ret.assign(cla.begin(), cla.end()); trim(txt); txt.pop_back(); - ret.push_back("-I"+txt+"/include"); + ret.push_back("-I" + txt + "/include"); return ret; } + + int main(int argc, const char **argv) { auto ExpectedParser = CommonOptionsParser::create(argc, argv, CTUApex); @@ -192,7 +173,7 @@ int main(int argc, const char **argv) { ClangTool tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); - tool.clearArgumentsAdjusters(); + //tool.clearArgumentsAdjusters(); tool.appendArgumentsAdjuster(clangResourceDirBasedAdjuster); metacg::io::FileSource fs(cg_file.getValue()); @@ -208,38 +189,28 @@ int main(int argc, const char **argv) { } auto &mcgManager = metacg::graph::MCGManager::get(); - mcgManager.addToManagedGraphs("activeGraph",mcgReader->read()); + + //As the mcgReader spits out a warning for every occurance of a missing metadata, + //we silence all warnings for the duration of the read + const auto &lv = spdlog::get_level(); + spdlog::set_level(spdlog::level::off); + mcgManager.addToManagedGraphs("activeGraph", mcgReader->read()); + spdlog::set_level(lv); auto &cg = *mcgManager.getCallgraph(); //Add extraction point gotten via the CLI - for(const auto& extractionFunc : extractionPoints){ + for (const auto &extractionFunc: extractionPoints) { if (cg.hasNode(extractionFunc)) { auto *md = new ExtractionMetadata(); md->setMarkedToExtract(true); cg.getNode(extractionFunc)->addMetaData(md); - }else{ - SPDLOG_ERROR("The given function: "+ extractionFunc + " does not exist in the given callgraph"); + } else { + SPDLOG_ERROR("The given function: " + extractionFunc + " does not exist in the given callgraph"); } } markFunctionsAlongCallpaths(cg); - - tool.run(clang::tooling::newFrontendActionFactory<CTUApexFrontendAction>( - new CTUApexFrontendAction(*mcgManager.getCallgraph())).get()); + tool.run(argumentParsingFrontendActionFactory<CTUApexAction>(mcgManager.getCallgraph()).get()); return 0; } - - - - - - - - - - - - - -