Index: include/llvm/ADT/Statistic.h =================================================================== --- include/llvm/ADT/Statistic.h +++ include/llvm/ADT/Statistic.h @@ -165,7 +165,10 @@ /// \brief Print statistics to the given output stream. void PrintStatistics(raw_ostream &OS); -/// Print statistics in JSON format. +/// Print statistics in JSON format. This does include all global timers (\see +/// Timer, TimerGroup). Note that the timers are cleared after printing and will +/// not be printed in human readable form or in a second call of +/// PrintStatisticsJSON(). void PrintStatisticsJSON(raw_ostream &OS); } // end namespace llvm Index: include/llvm/Support/JSON.h =================================================================== --- /dev/null +++ include/llvm/Support/JSON.h @@ -0,0 +1,37 @@ +//===-- llvm/Support/JSON.h - JSON print helpers ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file Utiliy functions to help output JSON. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_JSON_H +#define LLVM_SUPPORT_JSON_H + +#include +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +/// Return a string suitably escaped for output in a JSON string. +static inline StringRef JSONEscape(StringRef S) { + // Our current usage should not need any escaping. Keep it simple and just + // check that the input is pure ASCII without special characters. +#ifndef NDEBUG + for (char c : S) { + unsigned char uc = (unsigned char) c; + assert(uc != '\\' && uc != '\"' && uc >= 0x20 && uc < 0x80); + } +#endif + return S; +} + +} // end namespace llvm + +#endif Index: include/llvm/Support/Timer.h =================================================================== --- include/llvm/Support/Timer.h +++ include/llvm/Support/Timer.h @@ -168,10 +168,24 @@ /// destroy a TimerGroup object before all of the Timers in it are gone. A /// TimerGroup can be specified for a newly created timer in its constructor. class TimerGroup { + struct PrintRecord { + TimeRecord Time; + std::string Name; + std::string Description; + + PrintRecord(const PrintRecord &Other) = default; + PrintRecord(const TimeRecord &Time, const std::string &Name, + const std::string &Description) + : Time(Time), Name(Name), Description(Description) {} + + bool operator <(const PrintRecord &Other) const { + return Time < Other.Time; + } + }; std::string Name; std::string Description; Timer *FirstTimer = nullptr; ///< First timer in the group. - std::vector> TimersToPrint; + std::vector TimersToPrint; TimerGroup **Prev; ///< Pointer to Next field of previous timergroup in list. TimerGroup *Next; ///< Pointer to next timergroup in list. @@ -193,11 +207,21 @@ /// This static method prints all timers and clears them all out. static void printAll(raw_ostream &OS); + /// Ensure global timer group lists are initialized. This function is mostly + /// used by the Statistic code to influence the construction and destruction + /// order of the global timer lists. + static void ConstructTimerLists(); private: friend class Timer; + friend void PrintStatisticsJSON(raw_ostream &OS); void addTimer(Timer &T); void removeTimer(Timer &T); + void prepareToPrintList(); void PrintQueuedTimers(raw_ostream &OS); + void printJSONValue(raw_ostream &OS, const PrintRecord &R, + const char *suffix, double Value); + const char *printJSONValues(raw_ostream &OS, const char *delim); + static const char *printAllJSONValues(raw_ostream &OS, const char *delim); }; } // end namespace llvm Index: lib/Support/Statistic.cpp =================================================================== --- lib/Support/Statistic.cpp +++ lib/Support/Statistic.cpp @@ -27,8 +27,10 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Mutex.h" +#include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -60,6 +62,7 @@ /// Sort statistics by debugtype,name,description. void sort(); public: + StatisticInfo(); ~StatisticInfo(); void addStatistic(const Statistic *S) { @@ -90,6 +93,11 @@ } } +StatisticInfo::StatisticInfo() { + // Ensure timergroup lists are created first so they are destructed after us. + TimerGroup::ConstructTimerLists(); +} + // Print information when destroyed, iff command line option is specified. StatisticInfo::~StatisticInfo() { if (::Stats || PrintOnExit) @@ -148,17 +156,6 @@ 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; @@ -169,13 +166,13 @@ 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(); + OS << "\t\"" << JSONEscape(Stat->getDebugType()) + << '.' << JSONEscape(Stat->getName()) << "\": " << Stat->getValue(); delim = ",\n"; } + // Print timers. + TimerGroup::printAllJSONValues(OS, delim); + OS << "\n}\n"; OS.flush(); } Index: lib/Support/Timer.cpp =================================================================== --- lib/Support/Timer.cpp +++ lib/Support/Timer.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Mutex.h" #include "llvm/Support/Process.h" @@ -258,7 +259,7 @@ // If the timer was started, move its data to TimersToPrint. if (T.hasTriggered()) - TimersToPrint.emplace_back(T.Time, T.Description); + TimersToPrint.emplace_back(T.Time, T.Name, T.Description); T.TG = nullptr; @@ -292,8 +293,8 @@ std::sort(TimersToPrint.begin(), TimersToPrint.end()); TimeRecord Total; - for (auto &RecordNamePair : TimersToPrint) - Total += RecordNamePair.first; + for (const PrintRecord &Record : TimersToPrint) + Total += Record.Time; // Print out timing header. OS << "===" << std::string(73, '-') << "===\n"; @@ -323,10 +324,10 @@ OS << " --- Name ---\n"; // Loop through all of the timing data, printing it out. - for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) { - const std::pair &Entry = TimersToPrint[e-i-1]; - Entry.first.print(Total, OS); - OS << Entry.second << '\n'; + for (const PrintRecord &Record : make_range(TimersToPrint.rbegin(), + TimersToPrint.rend())) { + Record.Time.print(Total, OS); + OS << Record.Description << '\n'; } Total.print(Total, OS); @@ -336,18 +337,22 @@ TimersToPrint.clear(); } -void TimerGroup::print(raw_ostream &OS) { - sys::SmartScopedLock L(*TimerLock); - +void TimerGroup::prepareToPrintList() { // See if any of our timers were started, if so add them to TimersToPrint and // reset them. for (Timer *T = FirstTimer; T; T = T->Next) { if (!T->hasTriggered()) continue; - TimersToPrint.emplace_back(T->Time, T->Description); + TimersToPrint.emplace_back(T->Time, T->Name, T->Description); // Clear out the time. T->clear(); } +} + +void TimerGroup::print(raw_ostream &OS) { + sys::SmartScopedLock L(*TimerLock); + + prepareToPrintList(); // If any timers were started, print the group. if (!TimersToPrint.empty()) @@ -360,3 +365,37 @@ for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) TG->print(OS); } + +void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R, + const char *suffix, double Value) { + OS << "\t\"time." << JSONEscape(Name) << '.' << JSONEscape(R.Name) + << suffix << "\": " << Value; +} + +const char *TimerGroup::printJSONValues(raw_ostream &OS, const char *delim) { + prepareToPrintList(); + for (const PrintRecord &R : TimersToPrint) { + OS << delim; + delim = ",\n"; + + const TimeRecord &T = R.Time; + printJSONValue(OS, R, ".wall", T.getWallTime()); + OS << delim; + printJSONValue(OS, R, ".user", T.getUserTime()); + OS << delim; + printJSONValue(OS, R, ".sys", T.getSystemTime()); + } + TimersToPrint.clear(); + return delim; +} + +const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) { + sys::SmartScopedLock L(*TimerLock); + for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) + delim = TG->printJSONValues(OS, delim); + return delim; +} + +void TimerGroup::ConstructTimerLists() { + (void)*NamedGroupedTimers; +} Index: test/Other/statistic.ll =================================================================== --- test/Other/statistic.ll +++ test/Other/statistic.ll @@ -1,11 +1,16 @@ ; RUN: opt < %s -o /dev/null -instsimplify -stats -stats-json 2>&1 | FileCheck %s --check-prefix=JSON ; RUN: opt < %s -o /dev/null -instsimplify -stats -stats-json -info-output-file %t && FileCheck %s < %t --check-prefix=JSON +; RUN: opt < %s -o /dev/null -instsimplify -stats -stats-json -time-passes 2>&1 | FileCheck %s --check-prefixes=JSON,JSONTIME +; RUN: opt < %s -o /dev/null -instsimplify -stats -stats-json -time-passes -info-output-file %t && FileCheck %s < %t --check-prefixes=JSON,JSONTIME ; RUN: opt < %s -o /dev/null -instsimplify -stats 2>&1 | FileCheck %s --check-prefix=DEFAULT ; RUN: opt < %s -o /dev/null -instsimplify -stats -info-output-file %t && FileCheck %s < %t --check-prefix=DEFAULT ; REQUIRES: asserts ; JSON: { -; JSON: "instsimplify.NumSimplified": 1 +; JSON-DAG: "instsimplify.NumSimplified": 1 +; JSONTIME-DAG: "time.pass.Remove redundant instructions.wall" +; JSONTIME-DAG: "time.pass.Remove redundant instructions.user" +; JSONTIME-DAG: "time.pass.Remove redundant instructions.sys" ; JSON: } ; DEFAULT: 1 instsimplify - Number of redundant instructions removed