Merge pull request #12077 from graydon/doc-typo

diff --git a/include/swift/Basic/Statistic.h b/include/swift/Basic/Statistic.h
index ae1382a..3ee26e2 100644
--- a/include/swift/Basic/Statistic.h
+++ b/include/swift/Basic/Statistic.h
@@ -15,6 +15,7 @@
 
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/Statistic.h"
+#include "swift/Basic/SourceLoc.h"
 #include "swift/Basic/Timer.h"
 
 #define SWIFT_FUNC_STAT                                                 \
@@ -53,100 +54,66 @@
 public:
   struct AlwaysOnDriverCounters
   {
-    size_t NumDriverJobsRun;
-    size_t NumDriverJobsSkipped;
-
-    size_t DriverDepCascadingTopLevel;
-    size_t DriverDepCascadingDynamic;
-    size_t DriverDepCascadingNominal;
-    size_t DriverDepCascadingMember;
-    size_t DriverDepCascadingExternal;
-
-    size_t DriverDepTopLevel;
-    size_t DriverDepDynamic;
-    size_t DriverDepNominal;
-    size_t DriverDepMember;
-    size_t DriverDepExternal;
-
-    size_t ChildrenMaxRSS;
+#define DRIVER_STATISTIC(ID) size_t ID;
+#include "Statistics.def"
+#undef DRIVER_STATISTIC
   };
 
   struct AlwaysOnFrontendCounters
   {
-    size_t NumSourceBuffers;
-    size_t NumSourceLines;
-    size_t NumSourceLinesPerSecond;
-    size_t NumLinkLibraries;
-    size_t NumLoadedModules;
-    size_t NumImportedExternalDefinitions;
-    size_t NumTotalClangImportedEntities;
-    size_t NumASTBytesAllocated;
-    size_t NumDependencies;
-    size_t NumReferencedTopLevelNames;
-    size_t NumReferencedDynamicNames;
-    size_t NumReferencedMemberNames;
-    size_t NumDecls;
-    size_t NumLocalTypeDecls;
-    size_t NumObjCMethods;
-    size_t NumInfixOperators;
-    size_t NumPostfixOperators;
-    size_t NumPrefixOperators;
-    size_t NumPrecedenceGroups;
-    size_t NumUsedConformances;
+#define FRONTEND_STATISTIC(NAME, ID) size_t ID;
+#include "Statistics.def"
+#undef FRONTEND_STATISTIC
+  };
 
-    size_t NumConformancesDeserialized;
-    size_t NumConstraintScopes;
-    size_t NumDeclsDeserialized;
-    size_t NumDeclsValidated;
-    size_t NumFunctionsTypechecked;
-    size_t NumGenericSignatureBuilders;
-    size_t NumLazyGenericEnvironments;
-    size_t NumLazyGenericEnvironmentsLoaded;
-    size_t NumLazyIterableDeclContexts;
-    size_t NominalTypeLookupDirectCount;
-    size_t NumTypesDeserialized;
-    size_t NumTypesValidated;
-    size_t NumUnloadedLazyIterableDeclContexts;
+  struct FrontendStatsTracer
+  {
+    UnifiedStatsReporter *Reporter;
+    llvm::TimeRecord SavedTime;
+    StringRef Name;
+    SourceRange Range;
+    FrontendStatsTracer(StringRef Name,
+                        SourceRange const &Range,
+                        UnifiedStatsReporter *Reporter);
+    FrontendStatsTracer();
+    FrontendStatsTracer(FrontendStatsTracer&& other);
+    FrontendStatsTracer& operator=(FrontendStatsTracer&&);
+    ~FrontendStatsTracer();
+    FrontendStatsTracer(const FrontendStatsTracer&) = delete;
+    FrontendStatsTracer& operator=(const FrontendStatsTracer&) = delete;
+  };
 
-    size_t NumSILGenFunctions;
-    size_t NumSILGenVtables;
-    size_t NumSILGenWitnessTables;
-    size_t NumSILGenDefaultWitnessTables;
-    size_t NumSILGenGlobalVariables;
-
-    size_t NumSILOptFunctions;
-    size_t NumSILOptVtables;
-    size_t NumSILOptWitnessTables;
-    size_t NumSILOptDefaultWitnessTables;
-    size_t NumSILOptGlobalVariables;
-
-    size_t NumIRGlobals;
-    size_t NumIRFunctions;
-    size_t NumIRAliases;
-    size_t NumIRIFuncs;
-    size_t NumIRNamedMetaData;
-    size_t NumIRValueSymbols;
-    size_t NumIRComdatSymbols;
-    size_t NumIRBasicBlocks;
-    size_t NumIRInsts;
-
-    size_t NumLLVMBytesOutput;
+  struct FrontendStatsEvent
+  {
+    uint64_t TimeUSec;
+    uint64_t LiveUSec;
+    bool IsEntry;
+    StringRef EventName;
+    StringRef CounterName;
+    size_t CounterDelta;
+    size_t CounterValue;
+    SourceRange SourceRange;
   };
 
 private:
-  SmallString<128> Filename;
+  SmallString<128> StatsFilename;
+  SmallString<128> TraceFilename;
   llvm::TimeRecord StartedTime;
   std::unique_ptr<llvm::NamedRegionTimer> Timer;
-
+  SourceManager *SourceMgr;
   std::unique_ptr<AlwaysOnDriverCounters> DriverCounters;
   std::unique_ptr<AlwaysOnFrontendCounters> FrontendCounters;
+  std::unique_ptr<AlwaysOnFrontendCounters> LastTracedFrontendCounters;
+  std::vector<FrontendStatsEvent> FrontendStatsEvents;
 
   void publishAlwaysOnStatsToLLVM();
   void printAlwaysOnStatsAndTimers(llvm::raw_ostream &OS);
 
   UnifiedStatsReporter(StringRef ProgramName,
                        StringRef AuxName,
-                       StringRef Directory);
+                       StringRef Directory,
+                       SourceManager *SM,
+                       bool TraceEvents);
 public:
   UnifiedStatsReporter(StringRef ProgramName,
                        StringRef ModuleName,
@@ -154,11 +121,17 @@
                        StringRef TripleName,
                        StringRef OutputType,
                        StringRef OptType,
-                       StringRef Directory);
+                       StringRef Directory,
+                       SourceManager *SM=nullptr,
+                       bool TraceEvents=false);
   ~UnifiedStatsReporter();
 
   AlwaysOnDriverCounters &getDriverCounters();
   AlwaysOnFrontendCounters &getFrontendCounters();
+  FrontendStatsTracer getStatsTracer(StringRef N,
+                                     SourceRange const &R);
+  void saveAnyFrontendStatsEvents(FrontendStatsTracer const& T,
+                                  bool IsEntry);
 };
 
 }
diff --git a/include/swift/Basic/Statistics.def b/include/swift/Basic/Statistics.def
new file mode 100644
index 0000000..343d761
--- /dev/null
+++ b/include/swift/Basic/Statistics.def
@@ -0,0 +1,103 @@
+//===--- Statistics.def - Statistics Macro Metaprogramming Database -*- C++ -*-===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the database of always-available statistic counters.
+//
+// DRIVER_STATISTIC(Id)
+//   - Id is an identifier suitable for use in C++
+//
+// FRONTEND_STATISTIC(Subsystem, Id)
+//   - Subsystem is a token to be stringified as a name prefix
+//   - Id is an identifier suitable for use in C++
+//===----------------------------------------------------------------------===//
+
+/// Driver statistics are collected for driver processes
+#ifdef DRIVER_STATISTIC
+DRIVER_STATISTIC(NumDriverJobsRun)
+DRIVER_STATISTIC(NumDriverJobsSkipped)
+
+DRIVER_STATISTIC(DriverDepCascadingTopLevel)
+DRIVER_STATISTIC(DriverDepCascadingDynamic)
+DRIVER_STATISTIC(DriverDepCascadingNominal)
+DRIVER_STATISTIC(DriverDepCascadingMember)
+DRIVER_STATISTIC(DriverDepCascadingExternal)
+
+DRIVER_STATISTIC(DriverDepTopLevel)
+DRIVER_STATISTIC(DriverDepDynamic)
+DRIVER_STATISTIC(DriverDepNominal)
+DRIVER_STATISTIC(DriverDepMember)
+DRIVER_STATISTIC(DriverDepExternal)
+
+DRIVER_STATISTIC(ChildrenMaxRSS)
+#endif
+
+/// Driver statistics are collected for frontend processes
+#ifdef FRONTEND_STATISTIC
+FRONTEND_STATISTIC(AST, NumSourceBuffers)
+FRONTEND_STATISTIC(AST, NumSourceLines)
+FRONTEND_STATISTIC(AST, NumSourceLinesPerSecond)
+FRONTEND_STATISTIC(AST, NumLinkLibraries)
+FRONTEND_STATISTIC(AST, NumLoadedModules)
+FRONTEND_STATISTIC(AST, NumImportedExternalDefinitions)
+FRONTEND_STATISTIC(AST, NumTotalClangImportedEntities)
+FRONTEND_STATISTIC(AST, NumASTBytesAllocated)
+FRONTEND_STATISTIC(AST, NumDependencies)
+FRONTEND_STATISTIC(AST, NumReferencedTopLevelNames)
+FRONTEND_STATISTIC(AST, NumReferencedDynamicNames)
+FRONTEND_STATISTIC(AST, NumReferencedMemberNames)
+FRONTEND_STATISTIC(AST, NumDecls)
+FRONTEND_STATISTIC(AST, NumLocalTypeDecls)
+FRONTEND_STATISTIC(AST, NumObjCMethods)
+FRONTEND_STATISTIC(AST, NumInfixOperators)
+FRONTEND_STATISTIC(AST, NumPostfixOperators)
+FRONTEND_STATISTIC(AST, NumPrefixOperators)
+FRONTEND_STATISTIC(AST, NumPrecedenceGroups)
+FRONTEND_STATISTIC(AST, NumUsedConformances)
+
+FRONTEND_STATISTIC(Sema, NumConformancesDeserialized)
+FRONTEND_STATISTIC(Sema, NumConstraintScopes)
+FRONTEND_STATISTIC(Sema, NumDeclsDeserialized)
+FRONTEND_STATISTIC(Sema, NumDeclsValidated)
+FRONTEND_STATISTIC(Sema, NumFunctionsTypechecked)
+FRONTEND_STATISTIC(Sema, NumGenericSignatureBuilders)
+FRONTEND_STATISTIC(Sema, NumLazyGenericEnvironments)
+FRONTEND_STATISTIC(Sema, NumLazyGenericEnvironmentsLoaded)
+FRONTEND_STATISTIC(Sema, NumLazyIterableDeclContexts)
+FRONTEND_STATISTIC(Sema, NominalTypeLookupDirectCount)
+FRONTEND_STATISTIC(Sema, NumTypesDeserialized)
+FRONTEND_STATISTIC(Sema, NumTypesValidated)
+FRONTEND_STATISTIC(Sema, NumUnloadedLazyIterableDeclContexts)
+
+FRONTEND_STATISTIC(SILModule, NumSILGenFunctions)
+FRONTEND_STATISTIC(SILModule, NumSILGenVtables)
+FRONTEND_STATISTIC(SILModule, NumSILGenWitnessTables)
+FRONTEND_STATISTIC(SILModule, NumSILGenDefaultWitnessTables)
+FRONTEND_STATISTIC(SILModule, NumSILGenGlobalVariables)
+
+FRONTEND_STATISTIC(SILModule, NumSILOptFunctions)
+FRONTEND_STATISTIC(SILModule, NumSILOptVtables)
+FRONTEND_STATISTIC(SILModule, NumSILOptWitnessTables)
+FRONTEND_STATISTIC(SILModule, NumSILOptDefaultWitnessTables)
+FRONTEND_STATISTIC(SILModule, NumSILOptGlobalVariables)
+
+FRONTEND_STATISTIC(IRModule, NumIRGlobals)
+FRONTEND_STATISTIC(IRModule, NumIRFunctions)
+FRONTEND_STATISTIC(IRModule, NumIRAliases)
+FRONTEND_STATISTIC(IRModule, NumIRIFuncs)
+FRONTEND_STATISTIC(IRModule, NumIRNamedMetaData)
+FRONTEND_STATISTIC(IRModule, NumIRValueSymbols)
+FRONTEND_STATISTIC(IRModule, NumIRComdatSymbols)
+FRONTEND_STATISTIC(IRModule, NumIRBasicBlocks)
+FRONTEND_STATISTIC(IRModule, NumIRInsts)
+
+FRONTEND_STATISTIC(LLVM, NumLLVMBytesOutput)
+#endif
diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h
index b370360..32d68b0 100644
--- a/include/swift/Frontend/FrontendOptions.h
+++ b/include/swift/Frontend/FrontendOptions.h
@@ -222,6 +222,9 @@
   /// The path to which we should output statistics files.
   std::string StatsOutputDir;
 
+  /// Trace changes to stats to files in StatsOutputDir.
+  bool TraceStats = false;
+
   /// Indicates whether function body parsing should be delayed
   /// until the end of all files.
   bool DelayedFunctionBodyParsing = false;
diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td
index 40de10c..33d80e9 100644
--- a/include/swift/Option/Options.td
+++ b/include/swift/Option/Options.td
@@ -194,6 +194,9 @@
 def stats_output_dir: Separate<["-"], "stats-output-dir">,
   Flags<[FrontendOption, HelpHidden]>,
   HelpText<"Directory to write unified compilation-statistics files to">;
+def trace_stats_events: Flag<["-"], "trace-stats-events">,
+  Flags<[FrontendOption, HelpHidden]>,
+  HelpText<"Trace changes to stats in -stats-output-dir">;
 
 def emit_dependencies : Flag<["-"], "emit-dependencies">,
   Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>,
diff --git a/lib/Basic/Statistic.cpp b/lib/Basic/Statistic.cpp
index 225f893..6165702 100644
--- a/lib/Basic/Statistic.cpp
+++ b/lib/Basic/Statistic.cpp
@@ -42,20 +42,34 @@
 }
 
 static std::string
-makeFileName(StringRef ProgramName,
-             StringRef AuxName) {
+makeFileName(StringRef Prefix,
+             StringRef ProgramName,
+             StringRef AuxName,
+             StringRef Suffix) {
   std::string tmp;
   raw_string_ostream stream(tmp);
   auto now = std::chrono::system_clock::now();
-  stream << "stats"
+  stream << Prefix
          << "-" << now.time_since_epoch().count()
          << "-" << ProgramName
          << "-" << AuxName
          << "-" << Process::GetRandomNumber()
-         << ".json";
+         << "." << Suffix;
   return stream.str();
 }
 
+static std::string
+makeStatsFileName(StringRef ProgramName,
+                  StringRef AuxName) {
+  return makeFileName("stats", ProgramName, AuxName, "json");
+}
+
+static std::string
+makeTraceFileName(StringRef ProgramName,
+                  StringRef AuxName) {
+  return makeFileName("trace", ProgramName, AuxName, "csv");
+}
+
 // LLVM's statistics-reporting machinery is sensitive to filenames containing
 // YAML-quote-requiring characters, which occur surprisingly often in the wild;
 // we only need a recognizable and likely-unique name for a target here, not an
@@ -108,29 +122,39 @@
                                            StringRef TripleName,
                                            StringRef OutputType,
                                            StringRef OptType,
-                                           StringRef Directory)
+                                           StringRef Directory,
+                                           SourceManager *SM,
+                                           bool TraceEvents)
   : UnifiedStatsReporter(ProgramName,
                          auxName(ModuleName,
                                  InputName,
                                  TripleName,
                                  OutputType,
                                  OptType),
-                         Directory)
+                         Directory,
+                         SM, TraceEvents)
 {
 }
 
 UnifiedStatsReporter::UnifiedStatsReporter(StringRef ProgramName,
                                            StringRef AuxName,
-                                           StringRef Directory)
-  : Filename(Directory),
+                                           StringRef Directory,
+                                           SourceManager *SM,
+                                           bool TraceEvents)
+  : StatsFilename(Directory),
+    TraceFilename(Directory),
     StartedTime(llvm::TimeRecord::getCurrentTime()),
     Timer(make_unique<NamedRegionTimer>(AuxName,
                                         "Building Target",
-                                        ProgramName, "Running Program"))
+                                        ProgramName, "Running Program")),
+    SourceMgr(SM)
 {
-  path::append(Filename, makeFileName(ProgramName, AuxName));
+  path::append(StatsFilename, makeStatsFileName(ProgramName, AuxName));
+  path::append(TraceFilename, makeTraceFileName(ProgramName, AuxName));
   EnableStatistics(/*PrintOnExit=*/false);
   SharedTimer::enableCompilationTimers();
+  if (TraceEvents)
+    LastTracedFrontendCounters = make_unique<AlwaysOnFrontendCounters>();
 }
 
 UnifiedStatsReporter::AlwaysOnDriverCounters &
@@ -149,102 +173,30 @@
   return *FrontendCounters;
 }
 
-#define PUBLISH_STAT(C,TY,NAME)                               \
-  do {                                                        \
-    static Statistic Stat = {TY, #NAME, #NAME, {0}, false};   \
-    Stat += (C).NAME;                                         \
-  } while (0)
-
 void
 UnifiedStatsReporter::publishAlwaysOnStatsToLLVM() {
   if (FrontendCounters) {
     auto &C = getFrontendCounters();
-
-    PUBLISH_STAT(C, "AST", NumSourceBuffers);
-    PUBLISH_STAT(C, "AST", NumSourceLines);
-    PUBLISH_STAT(C, "AST", NumSourceLinesPerSecond);
-    PUBLISH_STAT(C, "AST", NumLinkLibraries);
-    PUBLISH_STAT(C, "AST", NumLoadedModules);
-    PUBLISH_STAT(C, "AST", NumImportedExternalDefinitions);
-    PUBLISH_STAT(C, "AST", NumTotalClangImportedEntities);
-    PUBLISH_STAT(C, "AST", NumASTBytesAllocated);
-    PUBLISH_STAT(C, "AST", NumDependencies);
-    PUBLISH_STAT(C, "AST", NumReferencedTopLevelNames);
-    PUBLISH_STAT(C, "AST", NumReferencedDynamicNames);
-    PUBLISH_STAT(C, "AST", NumReferencedMemberNames);
-    PUBLISH_STAT(C, "AST", NumDecls);
-    PUBLISH_STAT(C, "AST", NumLocalTypeDecls);
-    PUBLISH_STAT(C, "AST", NumObjCMethods);
-    PUBLISH_STAT(C, "AST", NumInfixOperators);
-    PUBLISH_STAT(C, "AST", NumPostfixOperators);
-    PUBLISH_STAT(C, "AST", NumPrefixOperators);
-    PUBLISH_STAT(C, "AST", NumPrecedenceGroups);
-    PUBLISH_STAT(C, "AST", NumUsedConformances);
-
-    PUBLISH_STAT(C, "Sema", NumConformancesDeserialized);
-    PUBLISH_STAT(C, "Sema", NumConstraintScopes);
-    PUBLISH_STAT(C, "Sema", NumDeclsDeserialized);
-    PUBLISH_STAT(C, "Sema", NumDeclsValidated);
-    PUBLISH_STAT(C, "Sema", NumFunctionsTypechecked);
-    PUBLISH_STAT(C, "Sema", NumGenericSignatureBuilders);
-    PUBLISH_STAT(C, "Sema", NumLazyGenericEnvironments);
-    PUBLISH_STAT(C, "Sema", NumLazyGenericEnvironmentsLoaded);
-    PUBLISH_STAT(C, "Sema", NumLazyIterableDeclContexts);
-    PUBLISH_STAT(C, "Sema", NominalTypeLookupDirectCount);
-    PUBLISH_STAT(C, "Sema", NumTypesDeserialized);
-    PUBLISH_STAT(C, "Sema", NumTypesValidated);
-    PUBLISH_STAT(C, "Sema", NumUnloadedLazyIterableDeclContexts);
-
-    PUBLISH_STAT(C, "SILModule", NumSILGenFunctions);
-    PUBLISH_STAT(C, "SILModule", NumSILGenVtables);
-    PUBLISH_STAT(C, "SILModule", NumSILGenWitnessTables);
-    PUBLISH_STAT(C, "SILModule", NumSILGenDefaultWitnessTables);
-    PUBLISH_STAT(C, "SILModule", NumSILGenGlobalVariables);
-
-    PUBLISH_STAT(C, "SILModule", NumSILOptFunctions);
-    PUBLISH_STAT(C, "SILModule", NumSILOptVtables);
-    PUBLISH_STAT(C, "SILModule", NumSILOptWitnessTables);
-    PUBLISH_STAT(C, "SILModule", NumSILOptDefaultWitnessTables);
-    PUBLISH_STAT(C, "SILModule", NumSILOptGlobalVariables);
-
-    PUBLISH_STAT(C, "IRModule", NumIRGlobals);
-    PUBLISH_STAT(C, "IRModule", NumIRFunctions);
-    PUBLISH_STAT(C, "IRModule", NumIRAliases);
-    PUBLISH_STAT(C, "IRModule", NumIRIFuncs);
-    PUBLISH_STAT(C, "IRModule", NumIRNamedMetaData);
-    PUBLISH_STAT(C, "IRModule", NumIRValueSymbols);
-    PUBLISH_STAT(C, "IRModule", NumIRComdatSymbols);
-    PUBLISH_STAT(C, "IRModule", NumIRBasicBlocks);
-    PUBLISH_STAT(C, "IRModule", NumIRInsts);
-
-    PUBLISH_STAT(C, "LLVM", NumLLVMBytesOutput);
+#define FRONTEND_STATISTIC(TY, NAME)                            \
+    do {                                                        \
+      static Statistic Stat = {#TY, #NAME, #NAME, {0}, false};  \
+      Stat += (C).NAME;                                         \
+    } while (0);
+#include "swift/Basic/Statistics.def"
+#undef FRONTEND_STATISTIC
   }
   if (DriverCounters) {
     auto &C = getDriverCounters();
-    PUBLISH_STAT(C, "Driver", NumDriverJobsRun);
-    PUBLISH_STAT(C, "Driver", NumDriverJobsSkipped);
-
-    PUBLISH_STAT(C, "Driver", DriverDepCascadingTopLevel);
-    PUBLISH_STAT(C, "Driver", DriverDepCascadingDynamic);
-    PUBLISH_STAT(C, "Driver", DriverDepCascadingNominal);
-    PUBLISH_STAT(C, "Driver", DriverDepCascadingMember);
-    PUBLISH_STAT(C, "Driver", DriverDepCascadingExternal);
-
-    PUBLISH_STAT(C, "Driver", DriverDepTopLevel);
-    PUBLISH_STAT(C, "Driver", DriverDepDynamic);
-    PUBLISH_STAT(C, "Driver", DriverDepNominal);
-    PUBLISH_STAT(C, "Driver", DriverDepMember);
-    PUBLISH_STAT(C, "Driver", DriverDepExternal);
-    PUBLISH_STAT(C, "Driver", ChildrenMaxRSS);
+#define DRIVER_STATISTIC(NAME)                                       \
+    do {                                                             \
+      static Statistic Stat = {"Driver", #NAME, #NAME, {0}, false};  \
+      Stat += (C).NAME;                                              \
+    } while (0);
+#include "swift/Basic/Statistics.def"
+#undef DRIVER_STATISTIC
   }
 }
 
-#define PRINT_STAT(OS,DELIM,C,TY,NAME)                   \
-  do {                                                   \
-    OS << DELIM << "\t\"" TY "." #NAME "\": " << C.NAME; \
-    delim = ",\n";                                       \
-  } while (0)
-
 void
 UnifiedStatsReporter::printAlwaysOnStatsAndTimers(raw_ostream &OS) {
   // Adapted from llvm::PrintStatisticsJSON
@@ -252,83 +204,23 @@
   const char *delim = "";
   if (FrontendCounters) {
     auto &C = getFrontendCounters();
-
-    PRINT_STAT(OS, delim, C, "AST", NumSourceBuffers);
-    PRINT_STAT(OS, delim, C, "AST", NumSourceLines);
-    PRINT_STAT(OS, delim, C, "AST", NumSourceLinesPerSecond);
-    PRINT_STAT(OS, delim, C, "AST", NumLinkLibraries);
-    PRINT_STAT(OS, delim, C, "AST", NumLoadedModules);
-    PRINT_STAT(OS, delim, C, "AST", NumImportedExternalDefinitions);
-    PRINT_STAT(OS, delim, C, "AST", NumTotalClangImportedEntities);
-    PRINT_STAT(OS, delim, C, "AST", NumASTBytesAllocated);
-    PRINT_STAT(OS, delim, C, "AST", NumDependencies);
-    PRINT_STAT(OS, delim, C, "AST", NumReferencedTopLevelNames);
-    PRINT_STAT(OS, delim, C, "AST", NumReferencedDynamicNames);
-    PRINT_STAT(OS, delim, C, "AST", NumReferencedMemberNames);
-    PRINT_STAT(OS, delim, C, "AST", NumDecls);
-    PRINT_STAT(OS, delim, C, "AST", NumLocalTypeDecls);
-    PRINT_STAT(OS, delim, C, "AST", NumObjCMethods);
-    PRINT_STAT(OS, delim, C, "AST", NumInfixOperators);
-    PRINT_STAT(OS, delim, C, "AST", NumPostfixOperators);
-    PRINT_STAT(OS, delim, C, "AST", NumPrefixOperators);
-    PRINT_STAT(OS, delim, C, "AST", NumPrecedenceGroups);
-    PRINT_STAT(OS, delim, C, "AST", NumUsedConformances);
-
-    PRINT_STAT(OS, delim, C, "Sema", NumConformancesDeserialized);
-    PRINT_STAT(OS, delim, C, "Sema", NumConstraintScopes);
-    PRINT_STAT(OS, delim, C, "Sema", NumDeclsDeserialized);
-    PRINT_STAT(OS, delim, C, "Sema", NumDeclsValidated);
-    PRINT_STAT(OS, delim, C, "Sema", NumFunctionsTypechecked);
-    PRINT_STAT(OS, delim, C, "Sema", NumGenericSignatureBuilders);
-    PRINT_STAT(OS, delim, C, "Sema", NumLazyGenericEnvironments);
-    PRINT_STAT(OS, delim, C, "Sema", NumLazyGenericEnvironmentsLoaded);
-    PRINT_STAT(OS, delim, C, "Sema", NumLazyIterableDeclContexts);
-    PRINT_STAT(OS, delim, C, "Sema", NominalTypeLookupDirectCount);
-    PRINT_STAT(OS, delim, C, "Sema", NumTypesDeserialized);
-    PRINT_STAT(OS, delim, C, "Sema", NumTypesValidated);
-    PRINT_STAT(OS, delim, C, "Sema", NumUnloadedLazyIterableDeclContexts);
-
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILGenFunctions);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILGenVtables);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILGenWitnessTables);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILGenDefaultWitnessTables);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILGenGlobalVariables);
-
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILOptFunctions);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILOptVtables);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILOptWitnessTables);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILOptDefaultWitnessTables);
-    PRINT_STAT(OS, delim, C, "SILModule", NumSILOptGlobalVariables);
-
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRGlobals);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRFunctions);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRAliases);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRIFuncs);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRNamedMetaData);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRValueSymbols);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRComdatSymbols);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRBasicBlocks);
-    PRINT_STAT(OS, delim, C, "IRModule", NumIRInsts);
-
-    PRINT_STAT(OS, delim, C, "LLVM", NumLLVMBytesOutput);
+#define FRONTEND_STATISTIC(TY, NAME)                        \
+    do {                                                    \
+      OS << delim << "\t\"" #TY "." #NAME "\": " << C.NAME; \
+      delim = ",\n";                                        \
+    } while (0);
+#include "swift/Basic/Statistics.def"
+#undef FRONTEND_STATISTIC
   }
   if (DriverCounters) {
     auto &C = getDriverCounters();
-    PRINT_STAT(OS, delim, C, "Driver", NumDriverJobsRun);
-    PRINT_STAT(OS, delim, C, "Driver", NumDriverJobsSkipped);
-
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingTopLevel);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingDynamic);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingNominal);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingMember);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepCascadingExternal);
-
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepTopLevel);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepDynamic);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepNominal);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepMember);
-    PRINT_STAT(OS, delim, C, "Driver", DriverDepExternal);
-    PRINT_STAT(OS, delim, C, "Driver", ChildrenMaxRSS);
+#define DRIVER_STATISTIC(NAME)                              \
+    do {                                                    \
+      OS << delim << "\t\"Driver." #NAME "\": " << C.NAME;  \
+      delim = ",\n";                                        \
+    } while (0);
+#include "swift/Basic/Statistics.def"
+#undef DRIVER_STATISTIC
   }
   // Print timers.
   TimerGroup::printAllJSONValues(OS, delim);
@@ -336,6 +228,91 @@
   OS.flush();
 }
 
+UnifiedStatsReporter::FrontendStatsTracer::FrontendStatsTracer(
+    StringRef Name,
+    SourceRange const &Range,
+    UnifiedStatsReporter *Reporter)
+  : Reporter(Reporter),
+    SavedTime(llvm::TimeRecord::getCurrentTime()),
+    Name(Name),
+    Range(Range)
+{
+  if (Reporter)
+    Reporter->saveAnyFrontendStatsEvents(*this, true);
+}
+
+UnifiedStatsReporter::FrontendStatsTracer::FrontendStatsTracer()
+  : Reporter(nullptr)
+{
+}
+
+UnifiedStatsReporter::FrontendStatsTracer&
+UnifiedStatsReporter::FrontendStatsTracer::operator=(
+    FrontendStatsTracer&& other)
+{
+  Reporter = other.Reporter;
+  SavedTime = other.SavedTime;
+  Name = other.Name;
+  Range = other.Range;
+  other.Reporter = nullptr;
+  return *this;
+}
+
+UnifiedStatsReporter::FrontendStatsTracer::FrontendStatsTracer(
+    FrontendStatsTracer&& other)
+  : Reporter(other.Reporter),
+    SavedTime(other.SavedTime),
+    Name(other.Name),
+    Range(other.Range)
+{
+  other.Reporter = nullptr;
+}
+
+UnifiedStatsReporter::FrontendStatsTracer::~FrontendStatsTracer()
+{
+  if (Reporter)
+    Reporter->saveAnyFrontendStatsEvents(*this, false);
+}
+
+UnifiedStatsReporter::FrontendStatsTracer
+UnifiedStatsReporter::getStatsTracer(StringRef N,
+                                     SourceRange const &R)
+{
+  if (LastTracedFrontendCounters)
+    // Return live tracer object.
+    return FrontendStatsTracer(N, R, this);
+  else
+    // Return inert tracer object.
+    return FrontendStatsTracer();
+}
+
+void
+UnifiedStatsReporter::saveAnyFrontendStatsEvents(
+    FrontendStatsTracer const& T,
+    bool IsEntry)
+{
+  if (!LastTracedFrontendCounters)
+    return;
+  auto Now = llvm::TimeRecord::getCurrentTime();
+  auto StartUS = uint64_t(1000000.0 * T.SavedTime.getProcessTime());
+  auto NowUS = uint64_t(1000000.0 * Now.getProcessTime());
+  auto LiveUS = IsEntry ? 0 : NowUS - StartUS;
+  auto &C = getFrontendCounters();
+#define FRONTEND_STATISTIC(TY, NAME)                          \
+  do {                                                        \
+    auto delta = C.NAME - LastTracedFrontendCounters->NAME;   \
+    static char const *name = #TY "." #NAME;                  \
+    if (delta != 0) {                                         \
+      LastTracedFrontendCounters->NAME = C.NAME;              \
+      FrontendStatsEvents.emplace_back(FrontendStatsEvent {   \
+          NowUS, LiveUS, IsEntry, T.Name, name,               \
+            delta, C.NAME, T.Range});                         \
+    }                                                         \
+  } while (0);
+#include "swift/Basic/Statistics.def"
+#undef FRONTEND_STATISTIC
+}
+
 UnifiedStatsReporter::~UnifiedStatsReporter()
 {
   // NB: Timer needs to be Optional<> because it needs to be destructed early;
@@ -364,7 +341,7 @@
   }
 
   std::error_code EC;
-  raw_fd_ostream ostream(Filename, EC, fs::F_Append | fs::F_Text);
+  raw_fd_ostream ostream(StatsFilename, EC, fs::F_Append | fs::F_Text);
   if (EC)
     return;
 
@@ -387,6 +364,27 @@
 #else
   printAlwaysOnStatsAndTimers(ostream);
 #endif
+
+  if (LastTracedFrontendCounters && SourceMgr) {
+    std::error_code EC;
+    raw_fd_ostream tstream(TraceFilename, EC, fs::F_Append | fs::F_Text);
+    if (EC)
+      return;
+    tstream << "Time,Live,IsEntry,EventName,CounterName,"
+            << "CounterDelta,CounterValue,SourceRange\n";
+    for (auto const &E : FrontendStatsEvents) {
+      tstream << E.TimeUSec << ','
+              << E.LiveUSec << ','
+              << (E.IsEntry ? "\"entry\"," : "\"exit\",")
+              << '"' << E.EventName << '"' << ','
+              << '"' << E.CounterName << '"' << ','
+              << E.CounterDelta << ','
+              << E.CounterValue << ',';
+      tstream << '"';
+      E.SourceRange.print(tstream, *SourceMgr, false);
+      tstream << '"' << '\n';
+    }
+  }
 }
 
 } // namespace swift
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index 626df74..f0e03d0 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -151,6 +151,7 @@
   inputArgs.AddLastArg(arguments, options::OPT_swift_version);
   inputArgs.AddLastArg(arguments, options::OPT_enforce_exclusivity_EQ);
   inputArgs.AddLastArg(arguments, options::OPT_stats_output_dir);
+  inputArgs.AddLastArg(arguments, options::OPT_trace_stats_events);
   inputArgs.AddLastArg(arguments,
                        options::OPT_solver_shrink_unsolved_threshold);
   inputArgs.AddLastArg(arguments, options::OPT_O_Group);
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 0a12441..92ba309 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -194,6 +194,9 @@
   Opts.DebugTimeCompilation |= Args.hasArg(OPT_debug_time_compilation);
   if (const Arg *A = Args.getLastArg(OPT_stats_output_dir)) {
     Opts.StatsOutputDir = A->getValue();
+    if (Args.getLastArg(OPT_trace_stats_events)) {
+      Opts.TraceStats = true;
+    }
   }
 
   if (const Arg *A = Args.getLastArg(OPT_validate_tbd_against_ir_EQ)) {
diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp
index 0a0cfbb..3998777 100644
--- a/lib/FrontendTool/FrontendTool.cpp
+++ b/lib/FrontendTool/FrontendTool.cpp
@@ -1360,13 +1360,17 @@
     StringRef OutFile = FEOpts.getSingleOutputFilename();
     StringRef OutputType = llvm::sys::path::extension(OutFile);
     std::string TripleName = LangOpts.Target.normalize();
+    auto &SM = Instance->getSourceMgr();
+    auto Trace = Invocation.getFrontendOptions().TraceStats;
     StatsReporter = llvm::make_unique<UnifiedStatsReporter>("swift-frontend",
                                                             FEOpts.ModuleName,
                                                             InputName,
                                                             TripleName,
                                                             OutputType,
                                                             OptType,
-                                                            StatsOutputDir);
+                                                            StatsOutputDir,
+                                                            &SM,
+                                                            Trace);
   }
 
   const DiagnosticOptions &diagOpts = Invocation.getDiagnosticOptions();
diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp
index 8365663..a9db914 100644
--- a/lib/Sema/TypeCheckDecl.cpp
+++ b/lib/Sema/TypeCheckDecl.cpp
@@ -3937,6 +3937,10 @@
       : TC(TC), IsFirstPass(IsFirstPass), IsSecondPass(IsSecondPass) {}
 
   void visit(Decl *decl) {
+    UnifiedStatsReporter::FrontendStatsTracer Tracer;
+    if (TC.Context.Stats)
+      Tracer = TC.Context.Stats->getStatsTracer("type-checking",
+                                                decl->getSourceRange());
     PrettyStackTraceDecl StackTrace("type-checking", decl);
     
     DeclVisitor<DeclChecker>::visit(decl);