From 482c931b74e72194275cb4da92918aca3a9b9ce8 Mon Sep 17 00:00:00 2001 From: George Karpenkov Date: Sat, 10 Feb 2018 01:49:20 +0000 Subject: [PATCH] [analyzer] Serialize statistics to plist when serialize-stats=true is set Differential Revision: https://reviews.llvm.org/D43131 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@324793 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../StaticAnalyzer/Core/AnalyzerOptions.h | 10 +++++++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp | 6 +++++ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp | 16 +++++++++++- .../Frontend/AnalysisConsumer.cpp | 26 +++++++++---------- test/Analysis/analyzer-config.c | 1 + test/Analysis/analyzer-config.cpp | 1 + test/Analysis/plist-stats-output.c | 14 ++++++++++ 7 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 test/Analysis/plist-stats-output.c diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index f6bd78884f..9968797d53 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -277,6 +277,8 @@ private: /// \sa StableReportFilename Optional StableReportFilename; + Optional SerializeStats; + /// \sa getGraphTrimInterval Optional GraphTrimInterval; @@ -538,6 +540,14 @@ public: /// which accepts the values "true" and "false". Default = false bool shouldWriteStableReportFilename(); + /// \return Whether the analyzer should + /// serialize statistics to plist output. + /// Statistics would be serialized in JSON format inside the main dictionary + /// under the \c statistics key. + /// Available only if compiled in assert mode or with LLVM statistics + /// explicitly enabled. + bool shouldSerializeStats(); + /// Returns whether irrelevant parts of a bug report path should be pruned /// out of the final output. /// diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 0e3e207c15..0f07c54e39 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -287,6 +287,12 @@ bool AnalyzerOptions::shouldWriteStableReportFilename() { /* Default = */ false); } +bool AnalyzerOptions::shouldSerializeStats() { + return getBooleanOption(SerializeStats, + "serialize-stats", + /* Default = */ false); +} + int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, const CheckerBase *C, bool SearchInParents) { diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 66812ed8ff..d3e7fe7541 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -16,9 +16,12 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/Statistic.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" using namespace clang; @@ -30,6 +33,7 @@ namespace { const std::string OutputFile; const LangOptions &LangOpts; const bool SupportsCrossFileDiagnostics; + const bool SerializeStatistics; public: PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string& prefix, @@ -61,7 +65,8 @@ PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, bool supportsMultipleFiles) : OutputFile(output), LangOpts(LO), - SupportsCrossFileDiagnostics(supportsMultipleFiles) {} + SupportsCrossFileDiagnostics(supportsMultipleFiles), + SerializeStatistics(AnalyzerOpts.shouldSerializeStats()) {} void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, @@ -484,6 +489,15 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " \n"; + if (llvm::AreStatisticsEnabled() && SerializeStatistics) { + o << " statistics\n"; + std::string stats; + llvm::raw_string_ostream os(stats); + llvm::PrintStatisticsJSON(os); + os.flush(); + EmitString(o, html::EscapeText(stats)) << '\n'; + } + // Finish. o << "\n"; } diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 48b0e2dcbf..a982a4a856 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -188,7 +188,8 @@ public: std::unique_ptr Mgr; /// Time the analyzes time of each translation unit. - static llvm::Timer* TUTotalTimer; + std::unique_ptr AnalyzerTimers; + std::unique_ptr TUTotalTimer; /// The information about analyzed functions shared throughout the /// translation unit. @@ -201,15 +202,17 @@ public: OutDir(outdir), Opts(std::move(opts)), Plugins(plugins), Injector(injector) { DigestAnalyzerOptions(); - if (Opts->PrintStats) { - llvm::EnableStatistics(false); - TUTotalTimer = new llvm::Timer("time", "Analyzer Total Time"); + if (Opts->PrintStats || Opts->shouldSerializeStats()) { + AnalyzerTimers = llvm::make_unique( + "analyzer", "Analyzer timers"); + TUTotalTimer = llvm::make_unique( + "time", "Analyzer total time", *AnalyzerTimers); + llvm::EnableStatistics(/* PrintOnExit= */ false); } } ~AnalysisConsumer() override { if (Opts->PrintStats) { - delete TUTotalTimer; llvm::PrintStatistics(); } } @@ -394,8 +397,6 @@ private: //===----------------------------------------------------------------------===// // AnalysisConsumer implementation. //===----------------------------------------------------------------------===// -llvm::Timer* AnalysisConsumer::TUTotalTimer = nullptr; - bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) { storeTopLevelDecls(DG); return true; @@ -557,12 +558,6 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { RecVisitorBR = nullptr; } - // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. - // FIXME: This should be replaced with something that doesn't rely on - // side-effects in PathDiagnosticConsumer's destructor. This is required when - // used with option -disable-free. - Mgr.reset(); - if (TUTotalTimer) TUTotalTimer->stopTimer(); // Count how many basic blocks we have not covered. @@ -574,6 +569,11 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) / NumBlocksInAnalyzedFunctions; + // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. + // FIXME: This should be replaced with something that doesn't rely on + // side-effects in PathDiagnosticConsumer's destructor. This is required when + // used with option -disable-free. + Mgr.reset(); } std::string AnalysisConsumer::getFunctionName(const Decl *D) { diff --git a/test/Analysis/analyzer-config.c b/test/Analysis/analyzer-config.c index 89d6ccca5a..9575784e08 100644 --- a/test/Analysis/analyzer-config.c +++ b/test/Analysis/analyzer-config.c @@ -30,6 +30,7 @@ void foo() { // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: serialize-stats = false // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp index 2629aeeb96..c89077f534 100644 --- a/test/Analysis/analyzer-config.cpp +++ b/test/Analysis/analyzer-config.cpp @@ -41,6 +41,7 @@ public: // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: serialize-stats = false // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] diff --git a/test/Analysis/plist-stats-output.c b/test/Analysis/plist-stats-output.c new file mode 100644 index 0000000000..072fe5e297 --- /dev/null +++ b/test/Analysis/plist-stats-output.c @@ -0,0 +1,14 @@ +// RUN: %clang_analyze_cc1 %s -analyzer-checker=core -analyzer-output=plist -analyzer-config serialize-stats=true -o %t.plist +// REQUIRES: asserts +// RUN: FileCheck --input-file=%t.plist %s + +int foo() {} + + +// CHECK: diagnostics +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: statistics +// CHECK-NEXT: { +// CHECK: } +// CHECK-NEXT: -- 2.40.0