#include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Rewrite/Core/Rewriter.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace clang; using namespace clang::driver; using namespace clang::tooling; static llvm::cl::OptionCategory InstrCategory("LLDB Instrumentation Generator"); class SBVisitor : public RecursiveASTVisitor { public: SBVisitor(Rewriter &R, ASTContext &Context) : MyRewriter(R), Context(Context) {} bool VisitCXXMethodDecl(CXXMethodDecl *Decl) { // Not all decls should be registered. Please refer to that method's // comment for details. if (ShouldSkip(Decl)) return false; // Print 'bool' instead of '_Bool'. PrintingPolicy Policy(Context.getLangOpts()); Policy.Bool = true; // Collect the functions parameter types and names. std::vector ParamNames; if (!Decl->isStatic()) ParamNames.push_back("this"); for (auto *P : Decl->parameters()) ParamNames.push_back(P->getNameAsString()); // Construct the macros. std::string Buffer; llvm::raw_string_ostream Macro(Buffer); if (ParamNames.empty()) { Macro << "LLDB_INSTRUMENT()"; } else { Macro << "LLDB_INSTRUMENT_VA(" << llvm::join(ParamNames, ", ") << ")"; } Stmt *Body = Decl->getBody(); for (auto &C : Body->children()) { if (C->getBeginLoc().isMacroID()) { CharSourceRange Range = MyRewriter.getSourceMgr().getExpansionRange(C->getSourceRange()); MyRewriter.ReplaceText(Range, Macro.str()); } else { Macro << ";"; SourceLocation InsertLoc = Lexer::getLocForEndOfToken( Body->getBeginLoc(), 0, MyRewriter.getSourceMgr(), MyRewriter.getLangOpts()); MyRewriter.InsertTextAfter(InsertLoc, Macro.str()); } break; } return true; } private: /// Determine whether we need to consider the given CXXMethodDecl. /// /// Currently we skip the following cases: /// 1. Decls outside the main source file, /// 2. Decls that are only present in the source file, /// 3. Decls that are not definitions, /// 4. Non-public methods, /// 5. Variadic methods. /// 6. Destructors. bool ShouldSkip(CXXMethodDecl *Decl) { // Skip anything outside the main file. if (!MyRewriter.getSourceMgr().isInMainFile(Decl->getBeginLoc())) return true; // Skip if the canonical decl in the current decl. It means that the method // is declared in the implementation and is therefore not exposed as part // of the API. if (Decl == Decl->getCanonicalDecl()) return true; // Skip decls that have no body, i.e. are just declarations. Stmt *Body = Decl->getBody(); if (!Body) return true; // Skip non-public methods. AccessSpecifier AS = Decl->getAccess(); if (AS != AccessSpecifier::AS_public) return true; // Skip variadic methods. if (Decl->isVariadic()) return true; // Skip destructors. if (isa(Decl)) return true; return false; } Rewriter &MyRewriter; ASTContext &Context; }; class SBConsumer : public ASTConsumer { public: SBConsumer(Rewriter &R, ASTContext &Context) : Visitor(R, Context) {} // Override the method that gets called for each parsed top-level // declaration. bool HandleTopLevelDecl(DeclGroupRef DR) override { for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) { Visitor.TraverseDecl(*b); } return true; } private: SBVisitor Visitor; }; class SBAction : public ASTFrontendAction { public: SBAction() = default; bool BeginSourceFileAction(CompilerInstance &CI) override { return true; } void EndSourceFileAction() override { MyRewriter.overwriteChangedFiles(); } std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef File) override { MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); return std::make_unique(MyRewriter, CI.getASTContext()); } private: Rewriter MyRewriter; }; int main(int argc, const char **argv) { auto ExpectedParser = CommonOptionsParser::create( argc, argv, InstrCategory, llvm::cl::OneOrMore, "Utility for generating the macros for LLDB's " "instrumentation framework."); if (!ExpectedParser) { llvm::errs() << ExpectedParser.takeError(); return 1; } CommonOptionsParser &OP = ExpectedParser.get(); auto PCHOpts = std::make_shared(); PCHOpts->registerWriter(std::make_unique()); PCHOpts->registerReader(std::make_unique()); ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts); return T.run(newFrontendActionFactory().get()); }