Index: include/llvm/ADT/Statistic.h =================================================================== --- include/llvm/ADT/Statistic.h +++ include/llvm/ADT/Statistic.h @@ -37,19 +37,24 @@ class Statistic { public: + const char *DebugType; const char *Name; const char *Desc; std::atomic Value; bool Initialized; unsigned getValue() const { return Value.load(std::memory_order_relaxed); } + const char *getDebugType() const { return DebugType; } const char *getName() const { return Name; } const char *getDesc() const { return Desc; } /// construct - This should only be called for non-global statistics. - void construct(const char *name, const char *desc) { - Name = name; Desc = desc; - Value = 0; Initialized = false; + void construct(const char *debugtype, const char *name, const char *desc) { + DebugType = debugtype; + Name = name; + Desc = desc; + Value = 0; + Initialized = false; } // Allow use of this class as the value itself. @@ -141,7 +146,7 @@ // STATISTIC - A macro to make definition of statistics really simple. This // automatically passes the DEBUG_TYPE of the file into the statistic. #define STATISTIC(VARNAME, DESC) \ - static llvm::Statistic VARNAME = {DEBUG_TYPE, DESC, {0}, 0} + static llvm::Statistic VARNAME = {DEBUG_TYPE, #VARNAME, DESC, {0}, 0} /// \brief Enable the collection and printing of statistics. void EnableStatistics(); @@ -158,6 +163,9 @@ /// \brief Print statistics to the given output stream. void PrintStatistics(raw_ostream &OS); +/// Print statistics in JSON format. +void PrintStatisticsJSON(raw_ostream &OS); + } // end llvm namespace #endif // LLVM_ADT_STATISTIC_H Index: lib/Support/Statistic.cpp =================================================================== --- lib/Support/Statistic.cpp +++ lib/Support/Statistic.cpp @@ -43,6 +43,9 @@ cl::desc("Enable statistics output from program (available with Asserts)")); +static cl::opt StatsAsJSON("stats-json", + cl::desc("Display statistics as json data")); + namespace { /// StatisticInfo - This class is used in a ManagedStatic so that it is created /// on demand (when the first statistic is bumped) and destroyed only when @@ -51,6 +54,10 @@ std::vector Stats; friend void llvm::PrintStatistics(); friend void llvm::PrintStatistics(raw_ostream &OS); + friend void llvm::PrintStatisticsJSON(raw_ostream &OS); + + /// Sort statistics by debugtype,name,description. + void sort(); public: ~StatisticInfo(); @@ -95,27 +102,32 @@ return Enabled; } +void StatisticInfo::sort() { + std::stable_sort(Stats.begin(), Stats.end(), + [](const Statistic *LHS, const Statistic *RHS) { + if (int Cmp = std::strcmp(LHS->getDebugType(), RHS->getDebugType())) + return Cmp < 0; + + if (int Cmp = std::strcmp(LHS->getName(), RHS->getName())) + return Cmp < 0; + + return std::strcmp(LHS->getDesc(), RHS->getDesc()) < 0; + }); +} + void llvm::PrintStatistics(raw_ostream &OS) { StatisticInfo &Stats = *StatInfo; // Figure out how long the biggest Value and Name fields are. - unsigned MaxNameLen = 0, MaxValLen = 0; + unsigned MaxDebugTypeLen = 0, MaxValLen = 0; for (size_t i = 0, e = Stats.Stats.size(); i != e; ++i) { MaxValLen = std::max(MaxValLen, (unsigned)utostr(Stats.Stats[i]->getValue()).size()); - MaxNameLen = std::max(MaxNameLen, - (unsigned)std::strlen(Stats.Stats[i]->getName())); + MaxDebugTypeLen = std::max(MaxDebugTypeLen, + (unsigned)std::strlen(Stats.Stats[i]->getDebugType())); } - // Sort the fields by name. - std::stable_sort(Stats.Stats.begin(), Stats.Stats.end(), - [](const Statistic *LHS, const Statistic *RHS) { - if (int Cmp = std::strcmp(LHS->getName(), RHS->getName())) - return Cmp < 0; - - // Secondary key is the description. - return std::strcmp(LHS->getDesc(), RHS->getDesc()) < 0; - }); + Stats.sort(); // Print out the statistics header... OS << "===" << std::string(73, '-') << "===\n" @@ -126,12 +138,43 @@ for (size_t i = 0, e = Stats.Stats.size(); i != e; ++i) OS << format("%*u %-*s - %s\n", MaxValLen, Stats.Stats[i]->getValue(), - MaxNameLen, Stats.Stats[i]->getName(), + MaxDebugTypeLen, Stats.Stats[i]->getDebugType(), Stats.Stats[i]->getDesc()); OS << '\n'; // Flush the output stream. OS.flush(); +} + +static void write_json_string_escaped(raw_ostream &OS, const char *string) { + // Out current usage should not need any escaping. Keep it simple and just + // check that the input is pure ASCII without special characers. +#ifndef NDEBUG + for (const unsigned char *c = (const unsigned char*)string; *c != '\0'; ++c) { + assert(*c != '\\' && *c != '\"' && *c >= 0x20 && *c < 0x80); + } +#endif + OS << string; +} + +void llvm::PrintStatisticsJSON(raw_ostream &OS) { + StatisticInfo &Stats = *StatInfo; + + Stats.sort(); + // Print all of the statistics. + OS << "{\n"; + const char *delim = ""; + for (const Statistic *Stat : Stats.Stats) { + OS << delim; + OS << "\t\""; + write_json_string_escaped(OS, Stat->getDebugType()); + OS << '.'; + write_json_string_escaped(OS, Stat->getName()); + OS << "\": " << Stat->getValue(); + delim = ",\n"; + } + OS << "\n}\n"; + OS.flush(); } void llvm::PrintStatistics() { @@ -143,7 +186,10 @@ // Get the stream to write to. std::unique_ptr OutStream = CreateInfoOutputFile(); - PrintStatistics(*OutStream); + if (StatsAsJSON) + PrintStatisticsJSON(*OutStream); + else + PrintStatistics(*OutStream); #else // Check if the -stats option is set instead of checking