Hi alexfh,

Add support for profiling the matchers used.
This will be connected with clang-tidy to generate a report to determine
and debug slow checks.

http://reviews.llvm.org/D5911

Files:
  include/clang/ASTMatchers/ASTMatchFinder.h
  lib/ASTMatchers/ASTMatchFinder.cpp
  unittests/ASTMatchers/ASTMatchersTest.cpp
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 "<unknown>".
+    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<llvm::raw_ostream> OS;
+    };
+    /// \brief Enables per-check timers.
+    ///
+    /// It prints a report after match.
+    std::unique_ptr<ProfileChecks> 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 <deque>
+#include <memory>
 #include <set>
 
 namespace clang {
@@ -292,17 +295,35 @@
 class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
                         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<llvm::Timer>(Bucket, MatchTimerGroup);
+    }
+    return Timer.get();
+  }
+
   /// \brief Runs all the \p Matchers on \p Node.
   ///
   /// Used by \c matchDispatch() below.
   template <typename T, typename MC>
   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<std::unique_ptr<llvm::Timer>> 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 "<unknown>"; }
+
 } // 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<IfStmt>()));
 }
 
+TEST(MatchFinder, CheckProfiling) {
+  MatchFinder::MatchFinderOptions Options;
+  Options.EnableCheckProfiling =
+      llvm::make_unique<MatchFinder::MatchFinderOptions::ProfileChecks>();
+  std::string Report;
+  Options.EnableCheckProfiling->OS =
+      llvm::make_unique<llvm::raw_string_ostream>(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<FrontendActionFactory> 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) {}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to