forked from OSchip/llvm-project
174 lines
5.2 KiB
C++
174 lines
5.2 KiB
C++
#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 <sstream>
|
|
#include <string>
|
|
|
|
using namespace clang;
|
|
using namespace clang::driver;
|
|
using namespace clang::tooling;
|
|
|
|
static llvm::cl::OptionCategory InstrCategory("LLDB Instrumentation Generator");
|
|
|
|
class SBVisitor : public RecursiveASTVisitor<SBVisitor> {
|
|
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<std::string> 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<CXXDestructorDecl>(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<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
|
|
StringRef File) override {
|
|
MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
|
|
return std::make_unique<SBConsumer>(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<PCHContainerOperations>();
|
|
PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
|
|
PCHOpts->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
|
|
|
|
ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts);
|
|
return T.run(newFrontendActionFactory<SBAction>().get());
|
|
}
|