freebsd-dev/contrib/llvm/lib/Support/Timer.cpp

376 lines
12 KiB
C++
Raw Normal View History

2009-06-02 17:52:33 +00:00
//===-- Timer.cpp - Interval Timing Support -------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Interval Timing implementation.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Timer.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringMap.h"
2009-06-02 17:52:33 +00:00
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
2009-10-14 17:57:32 +00:00
#include "llvm/Support/Format.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
2009-06-02 17:52:33 +00:00
using namespace llvm;
// getLibSupportInfoOutputFilename - This ugly hack is brought to you courtesy
// of constructor/destructor ordering being unspecified by C++. Basically the
// problem is that a Statistic object gets destroyed, which ends up calling
// 'GetLibSupportInfoOutputFile()' (below), which calls this function.
// LibSupportInfoOutputFilename used to be a global variable, but sometimes it
// would get destroyed before the Statistic, causing havoc to ensue. We "fix"
// this by creating the string the first time it is needed and never destroying
// it.
static ManagedStatic<std::string> LibSupportInfoOutputFilename;
static std::string &getLibSupportInfoOutputFilename() {
return *LibSupportInfoOutputFilename;
}
2009-06-27 10:44:33 +00:00
static ManagedStatic<sys::SmartMutex<true> > TimerLock;
2009-06-02 17:52:33 +00:00
namespace {
static cl::opt<bool>
TrackSpace("track-memory", cl::desc("Enable -time-passes memory "
"tracking (this may be slow)"),
cl::Hidden);
static cl::opt<std::string, true>
InfoOutputFilename("info-output-file", cl::value_desc("filename"),
cl::desc("File to append -stats and -timer output to"),
cl::Hidden, cl::location(getLibSupportInfoOutputFilename()));
}
2009-06-02 17:52:33 +00:00
// Return a file stream to print our output on.
std::unique_ptr<raw_fd_ostream> llvm::CreateInfoOutputFile() {
2010-04-02 08:54:30 +00:00
const std::string &OutputFilename = getLibSupportInfoOutputFilename();
if (OutputFilename.empty())
return llvm::make_unique<raw_fd_ostream>(2, false); // stderr.
2010-04-02 08:54:30 +00:00
if (OutputFilename == "-")
return llvm::make_unique<raw_fd_ostream>(1, false); // stdout.
2010-05-27 15:15:58 +00:00
// Append mode is used because the info output file is opened and closed
// each time -stats or -time-passes wants to print output to it. To
// compensate for this, the test-suite Makefiles have code to delete the
// info output file before running commands which write to it.
std::error_code EC;
auto Result = llvm::make_unique<raw_fd_ostream>(
OutputFilename, EC, sys::fs::F_Append | sys::fs::F_Text);
if (!EC)
2010-04-02 08:54:30 +00:00
return Result;
2010-04-02 08:54:30 +00:00
errs() << "Error opening info-output-file '"
<< OutputFilename << " for appending!\n";
return llvm::make_unique<raw_fd_ostream>(2, false); // stderr.
2010-04-02 08:54:30 +00:00
}
static TimerGroup *DefaultTimerGroup = nullptr;
2009-06-02 17:52:33 +00:00
static TimerGroup *getDefaultTimerGroup() {
2010-04-02 08:54:30 +00:00
TimerGroup *tmp = DefaultTimerGroup;
sys::MemoryFence();
2010-04-02 08:54:30 +00:00
if (tmp) return tmp;
sys::SmartScopedLock<true> Lock(*TimerLock);
2010-04-02 08:54:30 +00:00
tmp = DefaultTimerGroup;
if (!tmp) {
2010-04-02 08:54:30 +00:00
tmp = new TimerGroup("Miscellaneous Ungrouped Timers");
sys::MemoryFence();
DefaultTimerGroup = tmp;
}
2009-11-18 14:58:34 +00:00
return tmp;
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
//===----------------------------------------------------------------------===//
// Timer Implementation
//===----------------------------------------------------------------------===//
2009-06-02 17:52:33 +00:00
2010-04-02 08:54:30 +00:00
void Timer::init(StringRef N) {
init(N, *getDefaultTimerGroup());
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
void Timer::init(StringRef N, TimerGroup &tg) {
assert(!TG && "Timer already initialized");
2010-04-02 08:54:30 +00:00
Name.assign(N.begin(), N.end());
Running = Triggered = false;
2010-04-02 08:54:30 +00:00
TG = &tg;
TG->addTimer(*this);
2009-06-02 17:52:33 +00:00
}
Timer::~Timer() {
2010-04-02 08:54:30 +00:00
if (!TG) return; // Never initialized, or already cleared.
TG->removeTimer(*this);
2009-06-02 17:52:33 +00:00
}
static inline size_t getMemUsage() {
2010-04-02 08:54:30 +00:00
if (!TrackSpace) return 0;
return sys::Process::GetMallocUsage();
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
TimeRecord TimeRecord::getCurrentTime(bool Start) {
2009-06-02 17:52:33 +00:00
TimeRecord Result;
2010-04-02 08:54:30 +00:00
sys::TimeValue now(0,0), user(0,0), sys(0,0);
2009-06-02 17:52:33 +00:00
if (Start) {
2010-04-02 08:54:30 +00:00
Result.MemUsed = getMemUsage();
sys::Process::GetTimeUsage(now, user, sys);
2009-06-02 17:52:33 +00:00
} else {
2010-04-02 08:54:30 +00:00
sys::Process::GetTimeUsage(now, user, sys);
Result.MemUsed = getMemUsage();
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
Result.WallTime = now.seconds() + now.microseconds() / 1000000.0;
Result.UserTime = user.seconds() + user.microseconds() / 1000000.0;
Result.SystemTime = sys.seconds() + sys.microseconds() / 1000000.0;
2009-06-02 17:52:33 +00:00
return Result;
}
void Timer::startTimer() {
assert(!Running && "Cannot start a running timer");
Running = Triggered = true;
StartTime = TimeRecord::getCurrentTime(true);
2009-06-02 17:52:33 +00:00
}
void Timer::stopTimer() {
assert(Running && "Cannot stop a paused timer");
Running = false;
2010-04-02 08:54:30 +00:00
Time += TimeRecord::getCurrentTime(false);
Time -= StartTime;
}
2009-06-02 17:52:33 +00:00
void Timer::clear() {
Running = Triggered = false;
Time = StartTime = TimeRecord();
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
static void printVal(double Val, double Total, raw_ostream &OS) {
if (Total < 1e-7) // Avoid dividing by zero.
OS << " ----- ";
else
OS << format(" %7.4f (%5.1f%%)", Val, Val*100/Total);
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const {
if (Total.getUserTime())
printVal(getUserTime(), Total.getUserTime(), OS);
if (Total.getSystemTime())
printVal(getSystemTime(), Total.getSystemTime(), OS);
if (Total.getProcessTime())
printVal(getProcessTime(), Total.getProcessTime(), OS);
printVal(getWallTime(), Total.getWallTime(), OS);
OS << " ";
if (Total.getMemUsed())
OS << format("%9" PRId64 " ", (int64_t)getMemUsed());
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
2009-06-02 17:52:33 +00:00
//===----------------------------------------------------------------------===//
// NamedRegionTimer Implementation
//===----------------------------------------------------------------------===//
2010-05-04 16:11:02 +00:00
namespace {
2010-04-02 08:54:30 +00:00
typedef StringMap<Timer> Name2TimerMap;
2009-06-02 17:52:33 +00:00
2010-04-02 08:54:30 +00:00
class Name2PairMap {
StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map;
public:
~Name2PairMap() {
for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator
I = Map.begin(), E = Map.end(); I != E; ++I)
delete I->second.first;
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
Timer &get(StringRef Name, StringRef GroupName) {
sys::SmartScopedLock<true> L(*TimerLock);
std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName];
if (!GroupEntry.first)
GroupEntry.first = new TimerGroup(GroupName);
Timer &T = GroupEntry.second[Name];
if (!T.isInitialized())
T.init(Name, *GroupEntry.first);
return T;
}
};
2009-06-02 17:52:33 +00:00
}
2010-05-04 16:11:02 +00:00
2010-04-02 08:54:30 +00:00
static ManagedStatic<Name2TimerMap> NamedTimers;
static ManagedStatic<Name2PairMap> NamedGroupedTimers;
2009-06-02 17:52:33 +00:00
2010-04-02 08:54:30 +00:00
static Timer &getNamedRegionTimer(StringRef Name) {
sys::SmartScopedLock<true> L(*TimerLock);
Timer &T = (*NamedTimers)[Name];
if (!T.isInitialized())
T.init(Name);
return T;
2009-06-02 17:52:33 +00:00
}
2010-07-13 17:19:57 +00:00
NamedRegionTimer::NamedRegionTimer(StringRef Name,
bool Enabled)
: TimeRegion(!Enabled ? nullptr : &getNamedRegionTimer(Name)) {}
2009-06-02 17:52:33 +00:00
2010-07-13 17:19:57 +00:00
NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef GroupName,
bool Enabled)
: TimeRegion(!Enabled ? nullptr : &NamedGroupedTimers->get(Name, GroupName)){}
2009-06-02 17:52:33 +00:00
//===----------------------------------------------------------------------===//
// TimerGroup Implementation
//===----------------------------------------------------------------------===//
2010-04-02 08:54:30 +00:00
/// TimerGroupList - This is the global list of TimerGroups, maintained by the
/// TimerGroup ctor/dtor and is protected by the TimerLock lock.
static TimerGroup *TimerGroupList = nullptr;
2009-06-02 17:52:33 +00:00
2010-04-02 08:54:30 +00:00
TimerGroup::TimerGroup(StringRef name)
: Name(name.begin(), name.end()), FirstTimer(nullptr) {
2010-04-02 08:54:30 +00:00
// Add the group to TimerGroupList.
sys::SmartScopedLock<true> L(*TimerLock);
if (TimerGroupList)
TimerGroupList->Prev = &Next;
Next = TimerGroupList;
Prev = &TimerGroupList;
TimerGroupList = this;
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
TimerGroup::~TimerGroup() {
// If the timer group is destroyed before the timers it owns, accumulate and
// print the timing data.
while (FirstTimer)
2010-04-02 08:54:30 +00:00
removeTimer(*FirstTimer);
// Remove the group from the TimerGroupList.
2009-11-18 14:58:34 +00:00
sys::SmartScopedLock<true> L(*TimerLock);
2010-04-02 08:54:30 +00:00
*Prev = Next;
if (Next)
Next->Prev = Prev;
2009-06-02 17:52:33 +00:00
}
2009-11-18 14:58:34 +00:00
2010-04-02 08:54:30 +00:00
void TimerGroup::removeTimer(Timer &T) {
sys::SmartScopedLock<true> L(*TimerLock);
// If the timer was started, move its data to TimersToPrint.
if (T.hasTriggered())
TimersToPrint.emplace_back(T.Time, T.Name);
2010-04-02 08:54:30 +00:00
T.TG = nullptr;
2010-04-02 08:54:30 +00:00
// Unlink the timer from our list.
*T.Prev = T.Next;
if (T.Next)
T.Next->Prev = T.Prev;
// Print the report when all timers in this group are destroyed if some of
// them were started.
if (FirstTimer || TimersToPrint.empty())
2010-04-02 08:54:30 +00:00
return;
std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile();
2010-04-02 08:54:30 +00:00
PrintQueuedTimers(*OutStream);
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
void TimerGroup::addTimer(Timer &T) {
2009-10-14 17:57:32 +00:00
sys::SmartScopedLock<true> L(*TimerLock);
2010-04-02 08:54:30 +00:00
// Add the timer to our list.
if (FirstTimer)
FirstTimer->Prev = &T.Next;
T.Next = FirstTimer;
T.Prev = &FirstTimer;
FirstTimer = &T;
}
void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
// Sort the timers in descending order by amount of time taken.
std::sort(TimersToPrint.begin(), TimersToPrint.end());
TimeRecord Total;
for (auto &RecordNamePair : TimersToPrint)
Total += RecordNamePair.first;
2010-04-02 08:54:30 +00:00
// Print out timing header.
OS << "===" << std::string(73, '-') << "===\n";
// Figure out how many spaces to indent TimerGroup name.
unsigned Padding = (80-Name.length())/2;
if (Padding > 80) Padding = 0; // Don't allow "negative" numbers
OS.indent(Padding) << Name << '\n';
OS << "===" << std::string(73, '-') << "===\n";
// If this is not an collection of ungrouped times, print the total time.
// Ungrouped timers don't really make sense to add up. We still print the
// TOTAL line to make the percentages make sense.
if (this != DefaultTimerGroup)
OS << format(" Total Execution Time: %5.4f seconds (%5.4f wall clock)\n",
Total.getProcessTime(), Total.getWallTime());
2010-04-02 08:54:30 +00:00
OS << '\n';
if (Total.getUserTime())
OS << " ---User Time---";
if (Total.getSystemTime())
OS << " --System Time--";
if (Total.getProcessTime())
OS << " --User+System--";
OS << " ---Wall Time---";
if (Total.getMemUsed())
OS << " ---Mem---";
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';
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
Total.print(Total, OS);
OS << "Total\n\n";
OS.flush();
TimersToPrint.clear();
2009-06-02 17:52:33 +00:00
}
2010-04-02 08:54:30 +00:00
/// print - Print any started timers in this group and zero them.
void TimerGroup::print(raw_ostream &OS) {
2009-10-14 17:57:32 +00:00
sys::SmartScopedLock<true> L(*TimerLock);
2010-04-02 08:54:30 +00:00
// 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->Name);
2010-04-02 08:54:30 +00:00
// Clear out the time.
T->clear();
2010-04-02 08:54:30 +00:00
}
// If any timers were started, print the group.
if (!TimersToPrint.empty())
PrintQueuedTimers(OS);
2009-06-27 10:44:33 +00:00
}
2010-04-02 08:54:30 +00:00
/// printAll - This static method prints all timers and clears them all out.
void TimerGroup::printAll(raw_ostream &OS) {
2009-10-14 17:57:32 +00:00
sys::SmartScopedLock<true> L(*TimerLock);
2009-06-27 10:44:33 +00:00
2010-04-02 08:54:30 +00:00
for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
TG->print(OS);
}