Index: include/clang/ASTMatchers/ASTMatchFinder.h =================================================================== --- include/clang/ASTMatchers/ASTMatchFinder.h +++ include/clang/ASTMatchers/ASTMatchFinder.h @@ -42,6 +42,8 @@ #define LLVM_CLANG_ASTMATCHERS_ASTMATCHFINDER_H #include "clang/ASTMatchers/ASTMatchers.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Timer.h" namespace clang { @@ -102,6 +104,12 @@ /// /// Optionally override to do per translation unit tasks. virtual void onEndOfTranslationUnit() {} + + /// \brief An id used to group the matchers. + /// + /// This id is used, for example, for the profiling output. + /// It defaults to "". + virtual StringRef getID() const; }; /// \brief Called when parsing is finished. Intended for testing only. @@ -111,7 +119,22 @@ virtual void run() = 0; }; - MatchFinder(); + struct MatchFinderOptions { + struct ProfileChecks { + ProfileChecks(llvm::StringMap &Records) + : Records(Records) {} + + /// \brief Per bucket timing information. + llvm::StringMap &Records; + }; + + /// \brief Enables per-check timers. + /// + /// It prints a report after match. + std::unique_ptr EnableCheckProfiling; + }; + + MatchFinder(MatchFinderOptions Options = MatchFinderOptions()); ~MatchFinder(); /// \brief Adds a matcher to execute when running over the AST. @@ -191,6 +214,8 @@ private: MatchersByType Matchers; + MatchFinderOptions Options; + /// \brief Called when parsing is done. ParsingDoneTestCallback *ParsingDone; }; Index: lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- lib/ASTMatchers/ASTMatchFinder.cpp +++ lib/ASTMatchers/ASTMatchFinder.cpp @@ -20,7 +20,10 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Timer.h" #include +#include #include namespace clang { @@ -292,17 +295,32 @@ class MatchASTVisitor : public RecursiveASTVisitor, public ASTMatchFinder { public: - MatchASTVisitor(const MatchFinder::MatchersByType *Matchers) - : Matchers(Matchers), ActiveASTContext(nullptr) {} + MatchASTVisitor(const MatchFinder::MatchersByType *Matchers, + const MatchFinder::MatchFinderOptions &Options) + : Matchers(Matchers), Options(Options), ActiveASTContext(nullptr) {} + + ~MatchASTVisitor() { + if (Options.EnableCheckProfiling) { + Options.EnableCheckProfiling->Records = std::move(TimeByBucket); + } + } void onStartOfTranslationUnit() { - for (MatchCallback *MC : Matchers->AllCallbacks) + const bool EnableCheckProfiling = Options.EnableCheckProfiling != nullptr; + for (MatchCallback *MC : Matchers->AllCallbacks) { + TimeRegion Timer(EnableCheckProfiling ? &TimeByBucket[MC->getID()] + : nullptr); MC->onStartOfTranslationUnit(); + } } void onEndOfTranslationUnit() { - for (MatchCallback *MC : Matchers->AllCallbacks) + const bool EnableCheckProfiling = Options.EnableCheckProfiling != nullptr; + for (MatchCallback *MC : Matchers->AllCallbacks) { + TimeRegion Timer(EnableCheckProfiling ? &TimeByBucket[MC->getID()] + : nullptr); MC->onEndOfTranslationUnit(); + } } void set_active_ast_context(ASTContext *NewActiveASTContext) { @@ -471,12 +489,30 @@ bool shouldUseDataRecursionFor(clang::Stmt *S) const { return false; } private: + class TimeRegion { + public: + TimeRegion(llvm::TimeRecord *Record) : Record(Record) { + if (Record) + *Record -= llvm::TimeRecord::getCurrentTime(true); + } + ~TimeRegion() { + if (Record) + *Record += llvm::TimeRecord::getCurrentTime(false); + } + + private: + llvm::TimeRecord *Record; + }; + /// \brief Runs all the \p Matchers on \p Node. /// /// Used by \c matchDispatch() below. template void matchImpl(const T &Node, const MC &Matchers) { + const bool EnableCheckProfiling = Options.EnableCheckProfiling != nullptr; for (const auto &MP : Matchers) { + TimeRegion Timer(EnableCheckProfiling ? &TimeByBucket[MP.second->getID()] + : nullptr); BoundNodesTreeBuilder Builder; if (MP.first.matches(Node, this, &Builder)) { MatchVisitor Visitor(ActiveASTContext, MP.second); @@ -627,7 +663,13 @@ return false; } + /// \brief Bucket to record map. + /// + /// Used to get the appropriate bucket for each matcher. + llvm::StringMap TimeByBucket; + const MatchFinder::MatchersByType *Matchers; + const MatchFinder::MatchFinderOptions &Options; ASTContext *ActiveASTContext; // Maps a canonical type to its TypedefDecls. @@ -790,7 +832,8 @@ MatchFinder::MatchCallback::~MatchCallback() {} MatchFinder::ParsingDoneTestCallback::~ParsingDoneTestCallback() {} -MatchFinder::MatchFinder() : ParsingDone(nullptr) {} +MatchFinder::MatchFinder(MatchFinderOptions Options) + : Options(std::move(Options)), ParsingDone(nullptr) {} MatchFinder::~MatchFinder() {} @@ -860,13 +903,13 @@ void MatchFinder::match(const clang::ast_type_traits::DynTypedNode &Node, ASTContext &Context) { - internal::MatchASTVisitor Visitor(&Matchers); + internal::MatchASTVisitor Visitor(&Matchers, Options); Visitor.set_active_ast_context(&Context); Visitor.match(Node); } void MatchFinder::matchAST(ASTContext &Context) { - internal::MatchASTVisitor Visitor(&Matchers); + internal::MatchASTVisitor Visitor(&Matchers, Options); Visitor.set_active_ast_context(&Context); Visitor.onStartOfTranslationUnit(); Visitor.TraverseDecl(Context.getTranslationUnitDecl()); @@ -878,5 +921,7 @@ ParsingDone = NewParsingDone; } +StringRef MatchFinder::MatchCallback::getID() const { return ""; } + } // end namespace ast_matchers } // end namespace clang Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -4421,6 +4421,27 @@ new VerifyAncestorHasChildIsEqual())); } +TEST(MatchFinder, CheckProfiling) { + MatchFinder::MatchFinderOptions Options; + llvm::StringMap Records; + Options.EnableCheckProfiling = + llvm::make_unique( + Records); + MatchFinder Finder(std::move(Options)); + + struct NamedCallback : public MatchFinder::MatchCallback { + void run(const MatchFinder::MatchResult &Result) override {} + StringRef getID() const override { return "MyID"; } + } Callback; + Finder.addMatcher(decl(), &Callback); + std::unique_ptr Factory( + newFrontendActionFactory(&Finder)); + ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;")); + + EXPECT_EQ(1u, Records.size()); + EXPECT_EQ("MyID", Records.begin()->getKey()); +} + class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { public: VerifyStartOfTranslationUnit() : Called(false) {}