Index: clang-tidy/CMakeLists.txt =================================================================== --- clang-tidy/CMakeLists.txt +++ clang-tidy/CMakeLists.txt @@ -7,6 +7,7 @@ ClangTidyModule.cpp ClangTidyDiagnosticConsumer.cpp ClangTidyOptions.cpp + ClangTidyProfiling.cpp DEPENDS ClangSACheckers Index: clang-tidy/ClangTidy.h =================================================================== --- clang-tidy/ClangTidy.h +++ clang-tidy/ClangTidy.h @@ -228,7 +228,7 @@ const tooling::CompilationDatabase &Compilations, ArrayRef InputFiles, llvm::IntrusiveRefCntPtr BaseFS, - ProfileData *Profile = nullptr); + bool EnableCheckProfile = false); // FIXME: This interface will need to be significantly extended to be useful. // FIXME: Implement confidence levels for displaying/fixing errors. Index: clang-tidy/ClangTidy.cpp =================================================================== --- clang-tidy/ClangTidy.cpp +++ clang-tidy/ClangTidy.cpp @@ -18,6 +18,7 @@ #include "ClangTidy.h" #include "ClangTidyDiagnosticConsumer.h" #include "ClangTidyModuleRegistry.h" +#include "ClangTidyProfiling.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -267,12 +268,17 @@ class ClangTidyASTConsumer : public MultiplexConsumer { public: ClangTidyASTConsumer(std::vector> Consumers, + std::unique_ptr Profiling, std::unique_ptr Finder, std::vector> Checks) - : MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)), + : MultiplexConsumer(std::move(Consumers)), + Profiling(std::move(Profiling)), Finder(std::move(Finder)), Checks(std::move(Checks)) {} private: + // Destructor order matters! Profiling must be destructed last. + // Or at least after Finder. + std::unique_ptr Profiling; std::unique_ptr Finder; std::vector> Checks; }; @@ -353,8 +359,12 @@ CheckFactories->createChecks(&Context, Checks); ast_matchers::MatchFinder::MatchFinderOptions FinderOptions; - if (auto *P = Context.getCheckProfileData()) - FinderOptions.CheckProfiling.emplace(P->Records); + + std::unique_ptr Profiling; + if (Context.getCheckProfileData()) { + Profiling = llvm::make_unique(); + FinderOptions.CheckProfiling.emplace(Profiling->Records); + } std::unique_ptr Finder( new ast_matchers::MatchFinder(std::move(FinderOptions))); @@ -383,7 +393,8 @@ Consumers.push_back(std::move(AnalysisConsumer)); } return llvm::make_unique( - std::move(Consumers), std::move(Finder), std::move(Checks)); + std::move(Consumers), std::move(Profiling), std::move(Finder), + std::move(Checks)); } std::vector ClangTidyASTConsumerFactory::getCheckNames() { @@ -472,7 +483,7 @@ const CompilationDatabase &Compilations, ArrayRef InputFiles, llvm::IntrusiveRefCntPtr BaseFS, - ProfileData *Profile) { + bool EnableCheckProfile) { ClangTool Tool(Compilations, InputFiles, std::make_shared(), BaseFS); @@ -512,8 +523,7 @@ Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter); Tool.appendArgumentsAdjuster(PluginArgumentsRemover); - if (Profile) - Context.setCheckProfileData(Profile); + Context.setCheckProfileData(EnableCheckProfile); ClangTidyDiagnosticConsumer DiagConsumer(Context); Index: clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.h +++ clang-tidy/ClangTidyDiagnosticConsumer.h @@ -87,11 +87,6 @@ } }; -/// \brief Container for clang-tidy profiling data. -struct ProfileData { - llvm::StringMap Records; -}; - /// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine /// provided by this context. /// @@ -169,12 +164,9 @@ /// \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; } + /// \brief Control profile collection in clang-tidy. + void setCheckProfileData(bool Profile); + bool getCheckProfileData() const { return Profile; } /// \brief Should be called when starting to process new translation unit. void setCurrentBuildDirectory(StringRef BuildDirectory) { @@ -216,7 +208,7 @@ llvm::DenseMap CheckNamesByDiagnosticID; - ProfileData *Profile; + bool Profile = false; }; /// \brief A diagnostic consumer that turns each \c Diagnostic into a Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -178,8 +178,7 @@ ClangTidyContext::ClangTidyContext( std::unique_ptr OptionsProvider) - : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)), - Profile(nullptr) { + : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)) { // 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(""); @@ -233,7 +232,7 @@ OptionsProvider->getOptions(File)); } -void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; } +void ClangTidyContext::setCheckProfileData(bool P) { Profile = P; } bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const { assert(CheckFilter != nullptr); Index: clang-tidy/ClangTidyProfiling.h =================================================================== --- /dev/null +++ clang-tidy/ClangTidyProfiling.h @@ -0,0 +1,41 @@ +//===--- ClangTidyProfiling.h - clang-tidy ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYPROFILING_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYPROFILING_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace clang { +namespace tidy { + +class ClangTidyProfiling { + // Time is first to allow for sorting by it. + std::vector> Timers; + llvm::TimeRecord Total; + + void preprocess(); + + void printProfileData(llvm::raw_ostream &OS) const; + +public: + llvm::StringMap Records; + + ~ClangTidyProfiling(); +}; + +} // end namespace tidy +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYPROFILING_H Index: clang-tidy/ClangTidyProfiling.cpp =================================================================== --- /dev/null +++ clang-tidy/ClangTidyProfiling.cpp @@ -0,0 +1,65 @@ +//===--- ClangTidyProfiling.cpp - clang-tidy --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangTidyProfiling.h" +#include "llvm/ADT/STLExtras.h" + +#define DEBUG_TYPE "clang-tidy-profiling" + +namespace clang { +namespace tidy { + +void ClangTidyProfiling::preprocess() { + // Convert from a insertion-friendly map to sort-friendly vector. + Timers.clear(); + Timers.reserve(Records.size()); + for (const auto &P : Records) { + Timers.emplace_back(P.getValue(), P.getKey()); + Total += P.getValue(); + } + assert(Timers.size() == Records.size() && "Size mismatch after processing"); + + // We want the measurements to be sorted by decreasing time spent. + llvm::sort(Timers.begin(), Timers.end()); +} + +void ClangTidyProfiling::printProfileData(llvm::raw_ostream &OS) const { + 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(); +} + +ClangTidyProfiling::~ClangTidyProfiling() { + preprocess(); + printProfileData(llvm::errs()); +} + +} // namespace tidy +} // namespace clang Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -236,45 +236,6 @@ } } -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(); -} - static std::unique_ptr createOptionsProvider( llvm::IntrusiveRefCntPtr FS) { ClangTidyGlobalOptions GlobalOptions; @@ -424,7 +385,6 @@ llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true); return 1; } - ProfileData Profile; llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); @@ -432,7 +392,7 @@ ClangTidyContext Context(std::move(OwningOptionsProvider)); runClangTidy(Context, OptionsParser.getCompilations(), PathList, BaseFS, - EnableCheckProfile ? &Profile : nullptr); + EnableCheckProfile); ArrayRef Errors = Context.getErrors(); bool FoundErrors = llvm::find_if(Errors, [](const ClangTidyError &E) { return E.DiagLevel == ClangTidyError::Error; @@ -464,9 +424,6 @@ "Fixes have NOT been applied.\n\n"; } - if (EnableCheckProfile) - printProfileData(Profile, llvm::errs()); - if (WErrorCount) { if (!Quiet) { StringRef Plural = WErrorCount == 1 ? "" : "s"; Index: test/clang-tidy/clang-tidy-enable-check-profile-one-tu.cpp =================================================================== --- /dev/null +++ test/clang-tidy/clang-tidy-enable-check-profile-one-tu.cpp @@ -0,0 +1,13 @@ +// RUN: clang-tidy -enable-check-profile -checks='-*,readability-function-size' %s 2>&1 | FileCheck --match-full-lines -implicit-check-not='{{warning:|error:}}' %s + +// CHECK: Running without flags. +// CHECK-NEXT: ===-------------------------------------------------------------------------=== +// CHECK-NEXT: {{.*}} --- Name --- +// CHECK-NEXT: {{.*}} readability-function-size +// CHECK-NEXT: {{.*}} Total +// CHECK-NEXT: ===-------------------------------------------------------------------------=== + +class A { + A() {} + ~A() {} +}; Index: test/clang-tidy/clang-tidy-enable-check-profile-two-tu.cpp =================================================================== --- /dev/null +++ test/clang-tidy/clang-tidy-enable-check-profile-two-tu.cpp @@ -0,0 +1,19 @@ +// RUN: clang-tidy -enable-check-profile -checks='-*,readability-function-size' %s %s 2>&1 | FileCheck --match-full-lines -implicit-check-not='{{warning:|error:}}' %s + +// CHECK: Running without flags. +// CHECK-NEXT: ===-------------------------------------------------------------------------=== +// CHECK-NEXT: {{.*}} --- Name --- +// CHECK-NEXT: {{.*}} readability-function-size +// CHECK-NEXT: {{.*}} Total +// CHECK-NEXT: ===-------------------------------------------------------------------------=== + +// CHECK: ===-------------------------------------------------------------------------=== +// CHECK-NEXT: {{.*}} --- Name --- +// CHECK-NEXT: {{.*}} readability-function-size +// CHECK-NEXT: {{.*}} Total +// CHECK-NEXT: ===-------------------------------------------------------------------------=== + +class A { + A() {} + ~A() {} +};