forked from OSchip/llvm-project
Add support for profiling the matchers used.
Summary: Add support for profiling the matchers used. This will be connected with clang-tidy to generate a report to determine and debug slow checks. Reviewers: alexfh Subscribers: klimek, cfe-commits Differential Revision: http://reviews.llvm.org/D5911 llvm-svn: 220418
This commit is contained in:
parent
2633341b4a
commit
43dcf2172a
|
@ -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 @@ public:
|
|||
///
|
||||
/// 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 +119,22 @@ public:
|
|||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
MatchFinder();
|
||||
struct MatchFinderOptions {
|
||||
struct Profiling {
|
||||
Profiling(llvm::StringMap<llvm::TimeRecord> &Records)
|
||||
: Records(Records) {}
|
||||
|
||||
/// \brief Per bucket timing information.
|
||||
llvm::StringMap<llvm::TimeRecord> &Records;
|
||||
};
|
||||
|
||||
/// \brief Enables per-check timers.
|
||||
///
|
||||
/// It prints a report after match.
|
||||
llvm::Optional<Profiling> CheckProfiling;
|
||||
};
|
||||
|
||||
MatchFinder(MatchFinderOptions Options = MatchFinderOptions());
|
||||
~MatchFinder();
|
||||
|
||||
/// \brief Adds a matcher to execute when running over the AST.
|
||||
|
@ -191,6 +214,8 @@ public:
|
|||
private:
|
||||
MatchersByType Matchers;
|
||||
|
||||
MatchFinderOptions Options;
|
||||
|
||||
/// \brief Called when parsing is done.
|
||||
ParsingDoneTestCallback *ParsingDone;
|
||||
};
|
||||
|
|
|
@ -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,32 @@ private:
|
|||
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)
|
||||
: Matchers(Matchers), Options(Options), ActiveASTContext(nullptr) {}
|
||||
|
||||
~MatchASTVisitor() {
|
||||
if (Options.CheckProfiling) {
|
||||
Options.CheckProfiling->Records = std::move(TimeByBucket);
|
||||
}
|
||||
}
|
||||
|
||||
void onStartOfTranslationUnit() {
|
||||
for (MatchCallback *MC : Matchers->AllCallbacks)
|
||||
const bool EnableCheckProfiling = Options.CheckProfiling.hasValue();
|
||||
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.CheckProfiling.hasValue();
|
||||
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 @@ public:
|
|||
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 <typename T, typename MC>
|
||||
void matchImpl(const T &Node, const MC &Matchers) {
|
||||
const bool EnableCheckProfiling = Options.CheckProfiling.hasValue();
|
||||
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 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Bucket to record map.
|
||||
///
|
||||
/// Used to get the appropriate bucket for each matcher.
|
||||
llvm::StringMap<llvm::TimeRecord> TimeByBucket;
|
||||
|
||||
const MatchFinder::MatchersByType *Matchers;
|
||||
const MatchFinder::MatchFinderOptions &Options;
|
||||
ASTContext *ActiveASTContext;
|
||||
|
||||
// Maps a canonical type to its TypedefDecls.
|
||||
|
@ -790,7 +832,8 @@ MatchFinder::MatchResult::MatchResult(const BoundNodes &Nodes,
|
|||
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 @@ std::unique_ptr<ASTConsumer> MatchFinder::newASTConsumer() {
|
|||
|
||||
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 @@ void MatchFinder::registerTestCallbackAfterParsing(
|
|||
ParsingDone = NewParsingDone;
|
||||
}
|
||||
|
||||
StringRef MatchFinder::MatchCallback::getID() const { return "<unknown>"; }
|
||||
|
||||
} // end namespace ast_matchers
|
||||
} // end namespace clang
|
||||
|
|
|
@ -4421,6 +4421,25 @@ TEST(IsEqualTo, MatchesNodesByIdentity) {
|
|||
new VerifyAncestorHasChildIsEqual<IfStmt>()));
|
||||
}
|
||||
|
||||
TEST(MatchFinder, CheckProfiling) {
|
||||
MatchFinder::MatchFinderOptions Options;
|
||||
llvm::StringMap<llvm::TimeRecord> Records;
|
||||
Options.CheckProfiling.emplace(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<FrontendActionFactory> 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) {}
|
||||
|
|
Loading…
Reference in New Issue