Statistic: Add machine parseable json output

- We lacked a short unique identifier for a statistics, so I renamed the
  current "Name" field that just contained the DEBUG_TYPE name of the
  current file to DebugType and added a new "Name" field that contains
  the C++ identifier of the statistic variable.
- Add the -stats-json option which outputs statistics in json format.

Differential Revision: http://reviews.llvm.org/D20995

llvm-svn: 272826
diff --git a/llvm/lib/Support/Statistic.cpp b/llvm/lib/Support/Statistic.cpp
index e49d1cb..cdd3679 100644
--- a/llvm/lib/Support/Statistic.cpp
+++ b/llvm/lib/Support/Statistic.cpp
@@ -43,6 +43,9 @@
     cl::desc("Enable statistics output from program (available with Asserts)"));
 
 
+static cl::opt<bool> 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<const Statistic*> 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<raw_ostream> OutStream = CreateInfoOutputFile();
-  PrintStatistics(*OutStream);
+  if (StatsAsJSON)
+    PrintStatisticsJSON(*OutStream);
+  else
+    PrintStatistics(*OutStream);
 
 #else
   // Check if the -stats option is set instead of checking