From f811cb82a6cd811dbb4730009d0f060503aa1c76 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Thu, 20 Jan 2022 12:08:20 -0800 Subject: [PATCH] [lldb] Revive lldb-instr I revived lldb-instr to update the macros for D117712. I think the new macros are simple enough that we add them by hand, but this tool can do it automatically for you. Differential revision: https://reviews.llvm.org/D117748 --- lldb/tools/CMakeLists.txt | 1 + lldb/tools/lldb-instr/CMakeLists.txt | 16 +++ lldb/tools/lldb-instr/Instrument.cpp | 173 +++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 lldb/tools/lldb-instr/CMakeLists.txt create mode 100644 lldb/tools/lldb-instr/Instrument.cpp diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt index a5f4ca8ec7eb..1585fd4dc4b9 100644 --- a/lldb/tools/CMakeLists.txt +++ b/lldb/tools/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(intel-features) # example is `check-lldb`. So, we pass EXCLUDE_FROM_ALL here. add_subdirectory(lldb-test EXCLUDE_FROM_ALL) +add_lldb_tool_subdirectory(lldb-instr) add_lldb_tool_subdirectory(lldb-vscode) if (CMAKE_SYSTEM_NAME MATCHES "Darwin") diff --git a/lldb/tools/lldb-instr/CMakeLists.txt b/lldb/tools/lldb-instr/CMakeLists.txt new file mode 100644 index 000000000000..8da453b2894f --- /dev/null +++ b/lldb/tools/lldb-instr/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lldb_tool(lldb-instr + Instrument.cpp + + CLANG_LIBS + clangAST + clangBasic + clangCodeGen + clangFrontend + clangLex + clangRewrite + clangSerialization + clangTooling + + LINK_COMPONENTS + Support + ) diff --git a/lldb/tools/lldb-instr/Instrument.cpp b/lldb/tools/lldb-instr/Instrument.cpp new file mode 100644 index 000000000000..4b8725396a61 --- /dev/null +++ b/lldb/tools/lldb-instr/Instrument.cpp @@ -0,0 +1,173 @@ +#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()); +}