Index: clang-tools-extra/trunk/clang-tidy/ClangTidy.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidy.h +++ clang-tools-extra/trunk/clang-tidy/ClangTidy.h @@ -152,6 +152,7 @@ private: void run(const ast_matchers::MatchFinder::MatchResult &Result) override; + StringRef getID() const override { return CheckName; } std::string CheckName; ClangTidyContext *Context; @@ -196,11 +197,15 @@ ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options); /// \brief Run a set of clang-tidy checks on a set of files. +/// +/// \param Profile if provided, it enables check profile collection in +/// MatchFinder, and will contain the result of the profile. ClangTidyStats runClangTidy(std::unique_ptr OptionsProvider, const tooling::CompilationDatabase &Compilations, ArrayRef InputFiles, - std::vector *Errors); + std::vector *Errors, + ProfileData *Profile = nullptr); // FIXME: This interface will need to be significantly extended to be useful. // FIXME: Implement confidence levels for displaying/fixing errors. Index: clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp +++ clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp @@ -216,8 +216,13 @@ std::vector> Checks; CheckFactories->createChecks(&Context, Checks); + ast_matchers::MatchFinder::MatchFinderOptions FinderOptions; + if (auto *P = Context.getCheckProfileData()) + FinderOptions.CheckProfiling.emplace(P->Records); + std::unique_ptr Finder( - new ast_matchers::MatchFinder); + new ast_matchers::MatchFinder(std::move(FinderOptions))); + for (auto &Check : Checks) { Check->registerMatchers(&*Finder); Check->registerPPCallbacks(Compiler); @@ -356,9 +361,12 @@ runClangTidy(std::unique_ptr OptionsProvider, const tooling::CompilationDatabase &Compilations, ArrayRef InputFiles, - std::vector *Errors) { + std::vector *Errors, ProfileData *Profile) { ClangTool Tool(Compilations, InputFiles); clang::tidy::ClangTidyContext Context(std::move(OptionsProvider)); + if (Profile) + Context.setCheckProfileData(Profile); + ClangTidyDiagnosticConsumer DiagConsumer(Context); Tool.setDiagnosticConsumer(&DiagConsumer); Index: clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.h +++ clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -15,7 +15,9 @@ #include "clang/Basic/SourceManager.h" #include "clang/Tooling/Refactoring.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Support/Regex.h" +#include "llvm/Support/Timer.h" namespace clang { @@ -105,6 +107,11 @@ } }; +/// \brief Container for clang-tidy profiling data. +struct ProfileData { + llvm::StringMap Records; +}; + /// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticEngine /// provided by this context. /// @@ -162,6 +169,13 @@ /// \brief Clears collected errors. void clearErrors() { Errors.clear(); } + /// \brief Set the output struct for profile data. + /// + /// Setting a non-null pointer here will enable profile collection in + /// clang-tidy. + void setCheckProfileData(ProfileData* Profile); + ProfileData* getCheckProfileData() const { return Profile; } + private: // Calls setDiagnosticsEngine() and storeError(). friend class ClangTidyDiagnosticConsumer; @@ -184,6 +198,8 @@ ClangTidyStats Stats; llvm::DenseMap CheckNamesByDiagnosticID; + + ProfileData *Profile; }; /// \brief A diagnostic consumer that turns each \c Diagnostic into a Index: clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -162,7 +162,8 @@ ClangTidyContext::ClangTidyContext( std::unique_ptr OptionsProvider) - : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)) { + : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)), + Profile(nullptr) { // Before the first translation unit we can get errors related to command-line // parsing, use empty string for the file name in this case. setCurrentFile(""); @@ -221,6 +222,10 @@ return CurrentOptions; } +void ClangTidyContext::setCheckProfileData(ProfileData *P) { + Profile = P; +} + GlobList &ClangTidyContext::getChecksFilter() { assert(CheckFilter != nullptr); return *CheckFilter; Index: clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp +++ clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp @@ -98,6 +98,11 @@ cl::desc("Dumps configuration in the YAML format to stdout."), cl::init(false), cl::cat(ClangTidyCategory)); +static cl::opt EnableCheckProfile( + "enable-check-profile", + cl::desc("Enable per-check timing profiles, and print a report to stderr."), + cl::init(false), cl::cat(ClangTidyCategory)); + static cl::opt AnalyzeTemporaryDtors( "analyze-temporary-dtors", cl::desc("Enable temporary destructor-aware analysis in\n" @@ -143,6 +148,45 @@ } } +static void printProfileData(const ProfileData &Profile, + llvm::raw_ostream &OS) { + // Time is first to allow for sorting by it. + std::vector> Timers; + TimeRecord Total; + + for (const auto& P : Profile.Records) { + Timers.emplace_back(P.getValue(), P.getKey()); + Total += P.getValue(); + } + + std::sort(Timers.begin(), Timers.end()); + + std::string Line = "===" + std::string(73, '-') + "===\n"; + OS << Line; + + if (Total.getUserTime()) + OS << " ---User Time---"; + if (Total.getSystemTime()) + OS << " --System Time--"; + if (Total.getProcessTime()) + OS << " --User+System--"; + OS << " ---Wall Time---"; + if (Total.getMemUsed()) + OS << " ---Mem---"; + OS << " --- Name ---\n"; + + // Loop through all of the timing data, printing it out. + for (auto I = Timers.rbegin(), E = Timers.rend(); I != E; ++I) { + I->first.print(Total, OS); + OS << I->second << '\n'; + } + + Total.print(Total, OS); + OS << "Total\n"; + OS << Line << "\n"; + OS.flush(); +} + std::unique_ptr createOptionsProvider() { ClangTidyGlobalOptions GlobalOptions; if (std::error_code Err = parseLineFilter(LineFilter, GlobalOptions)) { @@ -220,10 +264,13 @@ return 1; } + ProfileData Profile; + std::vector Errors; ClangTidyStats Stats = runClangTidy(std::move(OptionsProvider), OptionsParser.getCompilations(), - OptionsParser.getSourcePathList(), &Errors); + OptionsParser.getSourcePathList(), &Errors, + EnableCheckProfile ? &Profile : nullptr); handleErrors(Errors, Fix); if (!ExportFixes.empty() && !Errors.empty()) { @@ -237,6 +284,9 @@ } printStats(Stats); + if (EnableCheckProfile) + printProfileData(Profile, llvm::errs()); + return 0; }