Skip to content

Commit

Permalink
Statistic/Timer: Include timers in PrintStatisticsJSON().
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D25588

llvm-svn: 287370
  • Loading branch information
MatzeB committed Nov 18, 2016
1 parent 9f15a79 commit db39fd6
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 30 deletions.
5 changes: 4 additions & 1 deletion llvm/include/llvm/ADT/Statistic.h
Expand Up @@ -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
Expand Down
26 changes: 25 additions & 1 deletion llvm/include/llvm/Support/Timer.h
Expand Up @@ -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.
Expand All @@ -193,11 +207,21 @@ class TimerGroup {
/// 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
Expand Down
32 changes: 16 additions & 16 deletions llvm/lib/Support/Statistic.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -60,6 +62,7 @@ class StatisticInfo {
/// Sort statistics by debugtype,name,description.
void sort();
public:
StatisticInfo();
~StatisticInfo();

void addStatistic(const Statistic *S) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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();
}
Expand Down
62 changes: 51 additions & 11 deletions llvm/lib/Support/Timer.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand All @@ -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())
Expand All @@ -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;
}
7 changes: 6 additions & 1 deletion llvm/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
Expand Down

0 comments on commit db39fd6

Please sign in to comment.