Index: include/clang/ASTMatchers/ASTMatchFinder.h =================================================================== --- include/clang/ASTMatchers/ASTMatchFinder.h +++ include/clang/ASTMatchers/ASTMatchFinder.h @@ -102,6 +102,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 +117,18 @@ virtual void run() = 0; }; - MatchFinder(); + struct MatchFinderOptions { + struct ProfileChecks { + /// \brief Output for the report. If null, it prints to llvm::errs() + std::unique_ptr OS; + }; + /// \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 +208,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,35 @@ 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) + : MatchTimerGroup("ASTMatchFinder"), Matchers(Matchers), Options(Options), + ActiveASTContext(nullptr) {} + + ~MatchASTVisitor() { + if (Options.EnableCheckProfiling) { + MatchTimerGroup.print(Options.EnableCheckProfiling->OS + ? *Options.EnableCheckProfiling->OS + : llvm::errs()); + } + } void onStartOfTranslationUnit() { - for (MatchCallback *MC : Matchers->AllCallbacks) + const bool EnableCheckProfiling = Options.EnableCheckProfiling != nullptr; + for (MatchCallback *MC : Matchers->AllCallbacks) { + llvm::TimeRegion Timer( + EnableCheckProfiling ? getTimerForBucket(MC->getID()) : nullptr); MC->onStartOfTranslationUnit(); + } } void onEndOfTranslationUnit() { - for (MatchCallback *MC : Matchers->AllCallbacks) + const bool EnableCheckProfiling = Options.EnableCheckProfiling != nullptr; + for (MatchCallback *MC : Matchers->AllCallbacks) { + llvm::TimeRegion Timer( + EnableCheckProfiling ? getTimerForBucket(MC->getID()) : nullptr); MC->onEndOfTranslationUnit(); + } } void set_active_ast_context(ASTContext *NewActiveASTContext) { @@ -471,12 +492,25 @@ bool shouldUseDataRecursionFor(clang::Stmt *S) const { return false; } private: + /// \brief Returns a timer for the bucket represented by \p Bucket + llvm::Timer *getTimerForBucket(StringRef Bucket) { + auto &Timer = TimersByBucket[Bucket]; + if (Timer == nullptr) { + Timer = llvm::make_unique(Bucket, MatchTimerGroup); + } + return Timer.get(); + } + /// \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) { + llvm::TimeRegion Timer(EnableCheckProfiling + ? getTimerForBucket(MP.second->getID()) + : nullptr); BoundNodesTreeBuilder Builder; if (MP.first.matches(Node, this, &Builder)) { MatchVisitor Visitor(ActiveASTContext, MP.second); @@ -627,7 +661,18 @@ return false; } + /// \brief Timer group used by the finder instance. + /// + /// All check timers will be registered here. + llvm::TimerGroup MatchTimerGroup; + + /// \brief Bucket to Timer map. + /// + /// Used to get the appropriate bucket for each matcher. + llvm::StringMap> TimersByBucket; + const MatchFinder::MatchersByType *Matchers; + const MatchFinder::MatchFinderOptions &Options; ASTContext *ActiveASTContext; // Maps a canonical type to its TypedefDecls. @@ -790,7 +835,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 +906,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 +924,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,30 @@ new VerifyAncestorHasChildIsEqual())); } +TEST(MatchFinder, CheckProfiling) { + MatchFinder::MatchFinderOptions Options; + Options.EnableCheckProfiling = + llvm::make_unique(); + std::string Report; + Options.EnableCheckProfiling->OS = + llvm::make_unique(Report); + 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;")); + + // Verify that the report was printed and that it contains the callback + // defined above. + EXPECT_TRUE(Report.find("ASTMatchFinder") != Report.npos); + EXPECT_TRUE(Report.find("MyID") != Report.npos); +} + class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { public: VerifyStartOfTranslationUnit() : Called(false) {}