Statistic/Timer: Include timers in PrintStatisticsJSON().

Differential Revision: https://reviews.llvm.org/D25588

llvm-svn: 287370
This commit is contained in:
Matthias Braun 2016-11-18 19:43:24 +00:00
parent bcd40e41de
commit 643a05ab19
5 changed files with 102 additions and 30 deletions

View File

@ -165,7 +165,10 @@ void PrintStatistics();
/// \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

View File

@ -168,10 +168,24 @@ struct NamedRegionTimer : public TimeRegion {
/// 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<std::pair<TimeRecord, std::string>> TimersToPrint;
std::vector<PrintRecord> TimersToPrint;
TimerGroup **Prev; ///< Pointer to Next field of previous timergroup in list.
TimerGroup *Next; ///< Pointer to next timergroup in list.
@ -193,11 +207,21 @@ public:
/// 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

View File

@ -29,7 +29,9 @@
#include "llvm/Support/Format.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/YAMLTraits.h"
#include <algorithm>
#include <cstring>
using namespace llvm;
@ -60,6 +62,7 @@ class StatisticInfo {
/// Sort statistics by debugtype,name,description.
void sort();
public:
StatisticInfo();
~StatisticInfo();
void addStatistic(const Statistic *S) {
@ -90,6 +93,11 @@ void Statistic::RegisterStatistic() {
}
}
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 @@ void llvm::PrintStatistics(raw_ostream &OS) {
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,16 @@ void llvm::PrintStatisticsJSON(raw_ostream &OS) {
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();
assert(!yaml::needsQuotes(Stat->getDebugType()) &&
"Statistic group/type name is simple.");
assert(!yaml::needsQuotes(Stat->getName()) && "Statistic name is simple");
OS << "\t\"" << Stat->getDebugType() << '.' << Stat->getName() << "\": "
<< Stat->getValue();
delim = ",\n";
}
// Print timers.
TimerGroup::printAllJSONValues(OS, delim);
OS << "\n}\n";
OS.flush();
}

View File

@ -21,6 +21,7 @@
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/YAMLTraits.h"
using namespace llvm;
// This ugly hack is brought to you courtesy of constructor/destructor ordering
@ -260,7 +261,7 @@ void TimerGroup::removeTimer(Timer &T) {
// 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;
@ -294,8 +295,8 @@ void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
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";
@ -325,10 +326,10 @@ void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
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<TimeRecord, std::string> &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);
@ -338,18 +339,22 @@ void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
TimersToPrint.clear();
}
void TimerGroup::print(raw_ostream &OS) {
sys::SmartScopedLock<true> 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<true> L(*TimerLock);
prepareToPrintList();
// If any timers were started, print the group.
if (!TimersToPrint.empty())
@ -362,3 +367,38 @@ void TimerGroup::printAll(raw_ostream &OS) {
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) {
assert(!yaml::needsQuotes(Name) && "TimerGroup name needs no quotes");
assert(!yaml::needsQuotes(R.Name) && "Timer name needs no quotes");
OS << "\t\"time." << Name << '.' << 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<true> L(*TimerLock);
for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
delim = TG->printJSONValues(OS, delim);
return delim;
}
void TimerGroup::ConstructTimerLists() {
(void)*NamedGroupedTimers;
}

View File

@ -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