X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FSupport%2FTimer.cpp;h=100b21e1221061fc034bf8097d227e79fe060a2e;hb=b1a003f37725a31e5e744c46112b628c5e0aeb8a;hp=8023c500e5d665b24993dbd57f11fdb5c7acbb69;hpb=3f39849003689a4d33aecdc744d4d27fd93a8f68;p=oota-llvm.git diff --git a/lib/Support/Timer.cpp b/lib/Support/Timer.cpp index 8023c500e5d..100b21e1221 100644 --- a/lib/Support/Timer.cpp +++ b/lib/Support/Timer.cpp @@ -1,315 +1,389 @@ //===-- 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 "Support/Timer.h" -#include "Support/CommandLine.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "llvm/Support/Timer.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#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" +using namespace llvm; + +// CreateInfoOutputFile - Return a file stream to print our output on. +namespace llvm { extern raw_ostream *CreateInfoOutputFile(); } + +// 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 LibSupportInfoOutputFilename; +static std::string &getLibSupportInfoOutputFilename() { + return *LibSupportInfoOutputFilename; +} + +static ManagedStatic > TimerLock; namespace { - cl::opt + static cl::opt TrackSpace("track-memory", cl::desc("Enable -time-passes memory " "tracking (this may be slow)"), cl::Hidden); + + static cl::opt + InfoOutputFilename("info-output-file", cl::value_desc("filename"), + cl::desc("File to append -stats and -timer output to"), + cl::Hidden, cl::location(getLibSupportInfoOutputFilename())); +} + +// CreateInfoOutputFile - Return a file stream to print our output on. +raw_ostream *llvm::CreateInfoOutputFile() { + const std::string &OutputFilename = getLibSupportInfoOutputFilename(); + if (OutputFilename.empty()) + return new raw_fd_ostream(2, false); // stderr. + if (OutputFilename == "-") + return new raw_fd_ostream(1, false); // stdout. + + // 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::string Error; + raw_ostream *Result = + new raw_fd_ostream(OutputFilename.c_str(), Error, sys::fs::F_Append); + if (Error.empty()) + return Result; + + errs() << "Error opening info-output-file '" + << OutputFilename << " for appending!\n"; + delete Result; + return new raw_fd_ostream(2, false); // stderr. } -// getNumBytesToNotCount - This function is supposed to return the number of -// bytes that are to be considered not allocated, even though malloc thinks they -// are allocated. -// -static unsigned getNumBytesToNotCount(); static TimerGroup *DefaultTimerGroup = 0; static TimerGroup *getDefaultTimerGroup() { - if (DefaultTimerGroup) return DefaultTimerGroup; - return DefaultTimerGroup = new TimerGroup("Miscellaneous Ungrouped Timers"); -} + TimerGroup *tmp = DefaultTimerGroup; + sys::MemoryFence(); + if (tmp) return tmp; + + llvm_acquire_global_lock(); + tmp = DefaultTimerGroup; + if (!tmp) { + tmp = new TimerGroup("Miscellaneous Ungrouped Timers"); + sys::MemoryFence(); + DefaultTimerGroup = tmp; + } + llvm_release_global_lock(); -Timer::Timer(const std::string &N) - : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), - Started(false), TG(getDefaultTimerGroup()) { - TG->addTimer(); + return tmp; } -Timer::Timer(const std::string &N, TimerGroup &tg) - : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), - Started(false), TG(&tg) { - TG->addTimer(); -} +//===----------------------------------------------------------------------===// +// Timer Implementation +//===----------------------------------------------------------------------===// -Timer::Timer(const Timer &T) { - TG = T.TG; - if (TG) TG->addTimer(); - operator=(T); +void Timer::init(StringRef N) { + assert(TG == 0 && "Timer already initialized"); + Name.assign(N.begin(), N.end()); + Started = false; + TG = getDefaultTimerGroup(); + TG->addTimer(*this); } - -// Copy ctor, initialize with no TG member. -Timer::Timer(bool, const Timer &T) { - TG = T.TG; // Avoid assertion in operator= - operator=(T); // Copy contents - TG = 0; +void Timer::init(StringRef N, TimerGroup &tg) { + assert(TG == 0 && "Timer already initialized"); + Name.assign(N.begin(), N.end()); + Started = false; + TG = &tg; + TG->addTimer(*this); } - Timer::~Timer() { - if (TG) { - if (Started) { - Started = false; - TG->addTimerToPrint(*this); - } - TG->removeTimer(); - } + if (!TG) return; // Never initialized, or already cleared. + TG->removeTimer(*this); } -static long getMemUsage() { - if (TrackSpace) { - struct mallinfo MI = mallinfo(); - return MI.uordblks/*+MI.hblkhd-getNumBytesToNotCount()*/; - } else { - return 0; - } +static inline size_t getMemUsage() { + if (!TrackSpace) return 0; + return sys::Process::GetMallocUsage(); } -struct TimeRecord { - double Elapsed, UserTime, SystemTime; - long MemUsed; -}; - -static TimeRecord getTimeRecord(bool Start) { - struct rusage RU; - struct timeval T; - long MemUsed; +TimeRecord TimeRecord::getCurrentTime(bool Start) { + TimeRecord Result; + sys::TimeValue now(0,0), user(0,0), sys(0,0); + if (Start) { - MemUsed = getMemUsage(); - if (getrusage(RUSAGE_SELF, &RU)) - perror("getrusage call failed: -time-passes info incorrect!"); - } - gettimeofday(&T, 0); - - if (!Start) { - MemUsed = getMemUsage(); - if (getrusage(RUSAGE_SELF, &RU)) - perror("getrusage call failed: -time-passes info incorrect!"); + Result.MemUsed = getMemUsage(); + sys::Process::GetTimeUsage(now, user, sys); + } else { + sys::Process::GetTimeUsage(now, user, sys); + Result.MemUsed = getMemUsage(); } - TimeRecord Result; - Result.Elapsed = T.tv_sec + T.tv_usec/1000000.0; - Result.UserTime = RU.ru_utime.tv_sec + RU.ru_utime.tv_usec/1000000.0; - Result.SystemTime = RU.ru_stime.tv_sec + RU.ru_stime.tv_usec/1000000.0; - Result.MemUsed = MemUsed; - + Result.WallTime = now.seconds() + now.microseconds() / 1000000.0; + Result.UserTime = user.seconds() + user.microseconds() / 1000000.0; + Result.SystemTime = sys.seconds() + sys.microseconds() / 1000000.0; return Result; } -static std::vector ActiveTimers; +static ManagedStatic > ActiveTimers; void Timer::startTimer() { Started = true; - TimeRecord TR = getTimeRecord(true); - Elapsed -= TR.Elapsed; - UserTime -= TR.UserTime; - SystemTime -= TR.SystemTime; - MemUsed -= TR.MemUsed; - PeakMemBase = TR.MemUsed; - ActiveTimers.push_back(this); + ActiveTimers->push_back(this); + Time -= TimeRecord::getCurrentTime(true); } void Timer::stopTimer() { - TimeRecord TR = getTimeRecord(false); - Elapsed += TR.Elapsed; - UserTime += TR.UserTime; - SystemTime += TR.SystemTime; - MemUsed += TR.MemUsed; - - if (ActiveTimers.back() == this) { - ActiveTimers.pop_back(); + Time += TimeRecord::getCurrentTime(false); + + if (ActiveTimers->back() == this) { + ActiveTimers->pop_back(); } else { std::vector::iterator I = - std::find(ActiveTimers.begin(), ActiveTimers.end(), this); - assert(I != ActiveTimers.end() && "stop but no startTimer?"); - ActiveTimers.erase(I); + std::find(ActiveTimers->begin(), ActiveTimers->end(), this); + assert(I != ActiveTimers->end() && "stop but no startTimer?"); + ActiveTimers->erase(I); } } -void Timer::sum(const Timer &T) { - Elapsed += T.Elapsed; - UserTime += T.UserTime; - SystemTime += T.SystemTime; - MemUsed += T.MemUsed; - PeakMem += T.PeakMem; +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); } -/// addPeakMemoryMeasurement - This method should be called whenever memory -/// usage needs to be checked. It adds a peak memory measurement to the -/// currently active timers, which will be printed when the timer group prints -/// -void Timer::addPeakMemoryMeasurement() { - long MemUsed = getMemUsage(); - - for (std::vector::iterator I = ActiveTimers.begin(), - E = ActiveTimers.end(); I != E; ++I) - (*I)->PeakMem = std::max((*I)->PeakMem, MemUsed-(*I)->PeakMemBase); +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()); } //===----------------------------------------------------------------------===// -// TimerGroup Implementation +// NamedRegionTimer Implementation //===----------------------------------------------------------------------===// -static void printVal(double Val, double Total) { - if (Total < 1e-7) // Avoid dividing by zero... - fprintf(stderr, " ----- "); - else - fprintf(stderr, " %7.4f (%5.1f%%)", Val, Val*100/Total); -} +namespace { -void Timer::print(const Timer &Total) { - if (Total.UserTime) - printVal(UserTime, Total.UserTime); - if (Total.SystemTime) - printVal(SystemTime, Total.SystemTime); - if (Total.getProcessTime()) - printVal(getProcessTime(), Total.getProcessTime()); - printVal(Elapsed, Total.Elapsed); +typedef StringMap Name2TimerMap; + +class Name2PairMap { + StringMap > Map; +public: + ~Name2PairMap() { + for (StringMap >::iterator + I = Map.begin(), E = Map.end(); I != E; ++I) + delete I->second.first; + } - fprintf(stderr, " "); - - if (Total.MemUsed) - fprintf(stderr, " %8ld ", MemUsed); - if (Total.PeakMem) { - if (PeakMem) - fprintf(stderr, " %8ld ", PeakMem); - else - fprintf(stderr, " "); + Timer &get(StringRef Name, StringRef GroupName) { + sys::SmartScopedLock L(*TimerLock); + + std::pair &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; } - std::cerr << Name << "\n"; +}; - Started = false; // Once printed, don't print again } +static ManagedStatic NamedTimers; +static ManagedStatic NamedGroupedTimers; -void TimerGroup::removeTimer() { - if (--NumTimers == 0 && !TimersToPrint.empty()) { // Print timing report... - // Sort the timers in descending order by amount of time taken... - std::sort(TimersToPrint.begin(), TimersToPrint.end(), - std::greater()); +static Timer &getNamedRegionTimer(StringRef Name) { + sys::SmartScopedLock L(*TimerLock); + + Timer &T = (*NamedTimers)[Name]; + if (!T.isInitialized()) + T.init(Name); + return T; +} - // 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 +NamedRegionTimer::NamedRegionTimer(StringRef Name, + bool Enabled) + : TimeRegion(!Enabled ? 0 : &getNamedRegionTimer(Name)) {} - ++NumTimers; - { // Scope to contain Total timer... don't allow total timer to drop us to - // zero timers... - Timer Total("TOTAL"); - - for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) - Total.sum(TimersToPrint[i]); - - // Print out timing header... - std::cerr << "===" << std::string(73, '-') << "===\n" - << std::string(Padding, ' ') << Name << "\n" - << "===" << std::string(73, '-') - << "===\n Total Execution Time: " << Total.getProcessTime() - << " seconds (" << Total.getWallTime() - << " wall clock)\n\n"; - - if (Total.UserTime) - std::cerr << " ---User Time---"; - if (Total.SystemTime) - std::cerr << " --System Time--"; - if (Total.getProcessTime()) - std::cerr << " --User+System--"; - std::cerr << " ---Wall Time---"; - if (Total.getMemUsed()) - std::cerr << " ---Mem---"; - if (Total.getPeakMem()) - std::cerr << " -PeakMem-"; - std::cerr << " --- Name ---\n"; - - // Loop through all of the timing data, printing it out... - for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) - TimersToPrint[i].print(Total); - - Total.print(Total); - std::cerr << std::endl; // Flush output - } - --NumTimers; +NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef GroupName, + bool Enabled) + : TimeRegion(!Enabled ? 0 : &NamedGroupedTimers->get(Name, GroupName)) {} - TimersToPrint.clear(); - } +//===----------------------------------------------------------------------===// +// TimerGroup Implementation +//===----------------------------------------------------------------------===// - // Delete default timer group! - if (NumTimers == 0 && this == DefaultTimerGroup) { - delete DefaultTimerGroup; - DefaultTimerGroup = 0; - } +/// TimerGroupList - This is the global list of TimerGroups, maintained by the +/// TimerGroup ctor/dtor and is protected by the TimerLock lock. +static TimerGroup *TimerGroupList = 0; + +TimerGroup::TimerGroup(StringRef name) + : Name(name.begin(), name.end()), FirstTimer(0) { + + // Add the group to TimerGroupList. + sys::SmartScopedLock L(*TimerLock); + if (TimerGroupList) + TimerGroupList->Prev = &Next; + Next = TimerGroupList; + Prev = &TimerGroupList; + TimerGroupList = this; } +TimerGroup::~TimerGroup() { + // If the timer group is destroyed before the timers it owns, accumulate and + // print the timing data. + while (FirstTimer != 0) + removeTimer(*FirstTimer); + + // Remove the group from the TimerGroupList. + sys::SmartScopedLock L(*TimerLock); + *Prev = Next; + if (Next) + Next->Prev = Prev; +} -#if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && (__GNUC_PATCHLEVEL__ == 0) -// If we have GCC 3.2.0, we can calculate pool allocation bookkeeping info -#define HAVE_POOL -extern "C" { - // Taken from GCC 3.2's stl_alloc.h file: - enum {_ALIGN = 8, _MAX_BYTES = 128, NFREE = _MAX_BYTES / _ALIGN}; - struct FreeList { FreeList *Next; }; +void TimerGroup::removeTimer(Timer &T) { + sys::SmartScopedLock L(*TimerLock); + + // If the timer was started, move its data to TimersToPrint. + if (T.Started) + TimersToPrint.push_back(std::make_pair(T.Time, T.Name)); - FreeList *_ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE[NFREE]; - char *_ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE; - char *_ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE; - size_t _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE; + T.TG = 0; + + // 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 != 0 || TimersToPrint.empty()) + return; - // Make the symbols possible to use... - FreeList* (&TheFreeList)[NFREE] = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE; - char * &StartFree = _ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE; - char * &EndFree = _ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE; - size_t &HeapSize = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE; + raw_ostream *OutStream = CreateInfoOutputFile(); + PrintQueuedTimers(*OutStream); + delete OutStream; // Close the file. } -#endif -// getNumBytesToNotCount - This function is supposed to return the number of -// bytes that are to be considered not allocated, even though malloc thinks they -// are allocated. -// -static unsigned getNumBytesToNotCount() { -#ifdef HAVE_POOL - // If we have GCC 3.2.0, we can subtract off pool allocation bookkeeping info - - // Size of the free slab section... - unsigned FreePoolMem = (unsigned)(EndFree-StartFree); - - // Walk all of the free lists, adding memory to the free counter whenever we - // have a free bucket. - for (unsigned i = 0; i != NFREE; ++i) { - unsigned NumEntries = 0; - for (FreeList *FL = TheFreeList[i]; FL; ++NumEntries, FL = FL->Next) - /*empty*/ ; - -#if 0 - if (NumEntries) - std::cerr << " For Size[" << (i+1)*_ALIGN << "]: " << NumEntries - << " Free entries\n"; -#endif - FreePoolMem += NumEntries*(i+1)*_ALIGN; +void TimerGroup::addTimer(Timer &T) { + sys::SmartScopedLock L(*TimerLock); + + // 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 (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) + Total += TimersToPrint[i].first; + + // 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()); + 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 &Entry = TimersToPrint[e-i-1]; + Entry.first.print(Total, OS); + OS << Entry.second << '\n'; } - return FreePoolMem; -#else -#warning "Don't know how to avoid pool allocation accounting overhead for this" -#warning " compiler: Space usage numbers (with -time-passes) may be off!" - return 0; -#endif + Total.print(Total, OS); + OS << "Total\n\n"; + OS.flush(); + + TimersToPrint.clear(); +} + +/// print - Print any started timers in this group and zero them. +void TimerGroup::print(raw_ostream &OS) { + sys::SmartScopedLock L(*TimerLock); + + // 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->Started) continue; + TimersToPrint.push_back(std::make_pair(T->Time, T->Name)); + + // Clear out the time. + T->Started = 0; + T->Time = TimeRecord(); + } + + // If any timers were started, print the group. + if (!TimersToPrint.empty()) + PrintQueuedTimers(OS); +} + +/// printAll - This static method prints all timers and clears them all out. +void TimerGroup::printAll(raw_ostream &OS) { + sys::SmartScopedLock L(*TimerLock); + + for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) + TG->print(OS); }