106c3fb27SDimitry Andric //===-- timing.h ------------------------------------------------*- C++ -*-===// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 806c3fb27SDimitry Andric 906c3fb27SDimitry Andric #ifndef SCUDO_TIMING_H_ 1006c3fb27SDimitry Andric #define SCUDO_TIMING_H_ 1106c3fb27SDimitry Andric 1206c3fb27SDimitry Andric #include "common.h" 1306c3fb27SDimitry Andric #include "mutex.h" 1406c3fb27SDimitry Andric #include "string_utils.h" 1506c3fb27SDimitry Andric #include "thread_annotations.h" 1606c3fb27SDimitry Andric 1706c3fb27SDimitry Andric #include <inttypes.h> 1806c3fb27SDimitry Andric #include <string.h> 1906c3fb27SDimitry Andric 2006c3fb27SDimitry Andric namespace scudo { 2106c3fb27SDimitry Andric 2206c3fb27SDimitry Andric class TimingManager; 2306c3fb27SDimitry Andric 2406c3fb27SDimitry Andric // A simple timer for evaluating execution time of code snippets. It can be used 2506c3fb27SDimitry Andric // along with TimingManager or standalone. 2606c3fb27SDimitry Andric class Timer { 2706c3fb27SDimitry Andric public: 2806c3fb27SDimitry Andric // The use of Timer without binding to a TimingManager is supposed to do the 2906c3fb27SDimitry Andric // timer logging manually. Otherwise, TimingManager will do the logging stuff 3006c3fb27SDimitry Andric // for you. 3106c3fb27SDimitry Andric Timer() = default; Timer(Timer && Other)3206c3fb27SDimitry Andric Timer(Timer &&Other) 3306c3fb27SDimitry Andric : StartTime(0), AccTime(Other.AccTime), Manager(Other.Manager), 3406c3fb27SDimitry Andric HandleId(Other.HandleId) { 3506c3fb27SDimitry Andric Other.Manager = nullptr; 3606c3fb27SDimitry Andric } 3706c3fb27SDimitry Andric 3806c3fb27SDimitry Andric Timer(const Timer &) = delete; 3906c3fb27SDimitry Andric 4006c3fb27SDimitry Andric ~Timer(); 4106c3fb27SDimitry Andric start()4206c3fb27SDimitry Andric void start() { 4306c3fb27SDimitry Andric CHECK_EQ(StartTime, 0U); 4406c3fb27SDimitry Andric StartTime = getMonotonicTime(); 4506c3fb27SDimitry Andric } stop()4606c3fb27SDimitry Andric void stop() { 4706c3fb27SDimitry Andric AccTime += getMonotonicTime() - StartTime; 4806c3fb27SDimitry Andric StartTime = 0; 4906c3fb27SDimitry Andric } getAccumulatedTime()5006c3fb27SDimitry Andric u64 getAccumulatedTime() const { return AccTime; } 5106c3fb27SDimitry Andric 5206c3fb27SDimitry Andric // Unset the bound TimingManager so that we don't report the data back. This 5306c3fb27SDimitry Andric // is useful if we only want to track subset of certain scope events. ignore()5406c3fb27SDimitry Andric void ignore() { 5506c3fb27SDimitry Andric StartTime = 0; 5606c3fb27SDimitry Andric AccTime = 0; 5706c3fb27SDimitry Andric Manager = nullptr; 5806c3fb27SDimitry Andric } 5906c3fb27SDimitry Andric 6006c3fb27SDimitry Andric protected: 6106c3fb27SDimitry Andric friend class TimingManager; Timer(TimingManager & Manager,u32 HandleId)6206c3fb27SDimitry Andric Timer(TimingManager &Manager, u32 HandleId) 6306c3fb27SDimitry Andric : Manager(&Manager), HandleId(HandleId) {} 6406c3fb27SDimitry Andric 6506c3fb27SDimitry Andric u64 StartTime = 0; 6606c3fb27SDimitry Andric u64 AccTime = 0; 6706c3fb27SDimitry Andric TimingManager *Manager = nullptr; 6806c3fb27SDimitry Andric u32 HandleId; 6906c3fb27SDimitry Andric }; 7006c3fb27SDimitry Andric 7106c3fb27SDimitry Andric // A RAII-style wrapper for easy scope execution measurement. Note that in order 7206c3fb27SDimitry Andric // not to take additional space for the message like `Name`. It only works with 7306c3fb27SDimitry Andric // TimingManager. 7406c3fb27SDimitry Andric class ScopedTimer : public Timer { 7506c3fb27SDimitry Andric public: 7606c3fb27SDimitry Andric ScopedTimer(TimingManager &Manager, const char *Name); 7706c3fb27SDimitry Andric ScopedTimer(TimingManager &Manager, const Timer &Nest, const char *Name); ~ScopedTimer()7806c3fb27SDimitry Andric ~ScopedTimer() { stop(); } 7906c3fb27SDimitry Andric }; 8006c3fb27SDimitry Andric 8106c3fb27SDimitry Andric // In Scudo, the execution time of single run of code snippets may not be 8206c3fb27SDimitry Andric // useful, we are more interested in the average time from several runs. 8306c3fb27SDimitry Andric // TimingManager lets the registered timer report their data and reports the 8406c3fb27SDimitry Andric // average execution time for each timer periodically. 8506c3fb27SDimitry Andric class TimingManager { 8606c3fb27SDimitry Andric public: 8706c3fb27SDimitry Andric TimingManager(u32 PrintingInterval = DefaultPrintingInterval) PrintingInterval(PrintingInterval)8806c3fb27SDimitry Andric : PrintingInterval(PrintingInterval) {} ~TimingManager()8906c3fb27SDimitry Andric ~TimingManager() { 9006c3fb27SDimitry Andric if (NumAllocatedTimers != 0) 9106c3fb27SDimitry Andric printAll(); 9206c3fb27SDimitry Andric } 9306c3fb27SDimitry Andric getOrCreateTimer(const char * Name)9406c3fb27SDimitry Andric Timer getOrCreateTimer(const char *Name) EXCLUDES(Mutex) { 9506c3fb27SDimitry Andric ScopedLock L(Mutex); 9606c3fb27SDimitry Andric 9706c3fb27SDimitry Andric CHECK_LT(strlen(Name), MaxLenOfTimerName); 9806c3fb27SDimitry Andric for (u32 I = 0; I < NumAllocatedTimers; ++I) { 9906c3fb27SDimitry Andric if (strncmp(Name, Timers[I].Name, MaxLenOfTimerName) == 0) 10006c3fb27SDimitry Andric return Timer(*this, I); 10106c3fb27SDimitry Andric } 10206c3fb27SDimitry Andric 10306c3fb27SDimitry Andric CHECK_LT(NumAllocatedTimers, MaxNumberOfTimers); 10406c3fb27SDimitry Andric strncpy(Timers[NumAllocatedTimers].Name, Name, MaxLenOfTimerName); 10506c3fb27SDimitry Andric TimerRecords[NumAllocatedTimers].AccumulatedTime = 0; 10606c3fb27SDimitry Andric TimerRecords[NumAllocatedTimers].Occurrence = 0; 107*0fca6ea1SDimitry Andric TimerRecords[NumAllocatedTimers].MaxTime = 0; 10806c3fb27SDimitry Andric return Timer(*this, NumAllocatedTimers++); 10906c3fb27SDimitry Andric } 11006c3fb27SDimitry Andric 11106c3fb27SDimitry Andric // Add a sub-Timer associated with another Timer. This is used when we want to 11206c3fb27SDimitry Andric // detail the execution time in the scope of a Timer. 11306c3fb27SDimitry Andric // For example, 11406c3fb27SDimitry Andric // void Foo() { 11506c3fb27SDimitry Andric // // T1 records the time spent in both first and second tasks. 11606c3fb27SDimitry Andric // ScopedTimer T1(getTimingManager(), "Task1"); 11706c3fb27SDimitry Andric // { 11806c3fb27SDimitry Andric // // T2 records the time spent in first task 11906c3fb27SDimitry Andric // ScopedTimer T2(getTimingManager, T1, "Task2"); 12006c3fb27SDimitry Andric // // Do first task. 12106c3fb27SDimitry Andric // } 12206c3fb27SDimitry Andric // // Do second task. 12306c3fb27SDimitry Andric // } 12406c3fb27SDimitry Andric // 12506c3fb27SDimitry Andric // The report will show proper indents to indicate the nested relation like, 12606c3fb27SDimitry Andric // -- Average Operation Time -- -- Name (# of Calls) -- 12706c3fb27SDimitry Andric // 10.0(ns) Task1 (1) 12806c3fb27SDimitry Andric // 5.0(ns) Task2 (1) nest(const Timer & T,const char * Name)12906c3fb27SDimitry Andric Timer nest(const Timer &T, const char *Name) EXCLUDES(Mutex) { 13006c3fb27SDimitry Andric CHECK_EQ(T.Manager, this); 13106c3fb27SDimitry Andric Timer Nesting = getOrCreateTimer(Name); 13206c3fb27SDimitry Andric 13306c3fb27SDimitry Andric ScopedLock L(Mutex); 13406c3fb27SDimitry Andric CHECK_NE(Nesting.HandleId, T.HandleId); 13506c3fb27SDimitry Andric Timers[Nesting.HandleId].Nesting = T.HandleId; 13606c3fb27SDimitry Andric return Nesting; 13706c3fb27SDimitry Andric } 13806c3fb27SDimitry Andric report(const Timer & T)13906c3fb27SDimitry Andric void report(const Timer &T) EXCLUDES(Mutex) { 14006c3fb27SDimitry Andric ScopedLock L(Mutex); 14106c3fb27SDimitry Andric 14206c3fb27SDimitry Andric const u32 HandleId = T.HandleId; 14306c3fb27SDimitry Andric CHECK_LT(HandleId, MaxNumberOfTimers); 144*0fca6ea1SDimitry Andric u64 AccTime = T.getAccumulatedTime(); 145*0fca6ea1SDimitry Andric TimerRecords[HandleId].AccumulatedTime += AccTime; 146*0fca6ea1SDimitry Andric if (AccTime > TimerRecords[HandleId].MaxTime) { 147*0fca6ea1SDimitry Andric TimerRecords[HandleId].MaxTime = AccTime; 148*0fca6ea1SDimitry Andric } 14906c3fb27SDimitry Andric ++TimerRecords[HandleId].Occurrence; 15006c3fb27SDimitry Andric ++NumEventsReported; 151*0fca6ea1SDimitry Andric if (NumEventsReported % PrintingInterval == 0) { 152*0fca6ea1SDimitry Andric ScopedString Str; 153*0fca6ea1SDimitry Andric getAllImpl(Str); 154*0fca6ea1SDimitry Andric Str.output(); 155*0fca6ea1SDimitry Andric } 15606c3fb27SDimitry Andric } 15706c3fb27SDimitry Andric printAll()15806c3fb27SDimitry Andric void printAll() EXCLUDES(Mutex) { 159*0fca6ea1SDimitry Andric ScopedString Str; 160*0fca6ea1SDimitry Andric getAll(Str); 161*0fca6ea1SDimitry Andric Str.output(); 162*0fca6ea1SDimitry Andric } 163*0fca6ea1SDimitry Andric getAll(ScopedString & Str)164*0fca6ea1SDimitry Andric void getAll(ScopedString &Str) EXCLUDES(Mutex) { 16506c3fb27SDimitry Andric ScopedLock L(Mutex); 166*0fca6ea1SDimitry Andric getAllImpl(Str); 16706c3fb27SDimitry Andric } 16806c3fb27SDimitry Andric 16906c3fb27SDimitry Andric private: getAllImpl(ScopedString & Str)170*0fca6ea1SDimitry Andric void getAllImpl(ScopedString &Str) REQUIRES(Mutex) { 17106c3fb27SDimitry Andric static char AvgHeader[] = "-- Average Operation Time --"; 172*0fca6ea1SDimitry Andric static char MaxHeader[] = "-- Maximum Operation Time --"; 173*0fca6ea1SDimitry Andric static char NameHeader[] = "-- Name (# of Calls) --"; 174*0fca6ea1SDimitry Andric Str.append("%-15s %-15s %-15s\n", AvgHeader, MaxHeader, NameHeader); 17506c3fb27SDimitry Andric 17606c3fb27SDimitry Andric for (u32 I = 0; I < NumAllocatedTimers; ++I) { 17706c3fb27SDimitry Andric if (Timers[I].Nesting != MaxNumberOfTimers) 17806c3fb27SDimitry Andric continue; 179*0fca6ea1SDimitry Andric getImpl(Str, I); 180*0fca6ea1SDimitry Andric } 18106c3fb27SDimitry Andric } 18206c3fb27SDimitry Andric 183*0fca6ea1SDimitry Andric void getImpl(ScopedString &Str, const u32 HandleId, const u32 ExtraIndent = 0) REQUIRES(Mutex)184*0fca6ea1SDimitry Andric REQUIRES(Mutex) { 18506c3fb27SDimitry Andric const u64 AccumulatedTime = TimerRecords[HandleId].AccumulatedTime; 18606c3fb27SDimitry Andric const u64 Occurrence = TimerRecords[HandleId].Occurrence; 18706c3fb27SDimitry Andric const u64 Integral = Occurrence == 0 ? 0 : AccumulatedTime / Occurrence; 18806c3fb27SDimitry Andric // Only keep single digit of fraction is enough and it enables easier layout 18906c3fb27SDimitry Andric // maintenance. 19006c3fb27SDimitry Andric const u64 Fraction = 19106c3fb27SDimitry Andric Occurrence == 0 ? 0 19206c3fb27SDimitry Andric : ((AccumulatedTime % Occurrence) * 10) / Occurrence; 19306c3fb27SDimitry Andric 194*0fca6ea1SDimitry Andric // Average time. 195*0fca6ea1SDimitry Andric Str.append("%14" PRId64 ".%" PRId64 "(ns) %-8s", Integral, Fraction, " "); 19606c3fb27SDimitry Andric 197*0fca6ea1SDimitry Andric // Maximum time. 198*0fca6ea1SDimitry Andric Str.append("%16" PRId64 "(ns) %-11s", TimerRecords[HandleId].MaxTime, " "); 199*0fca6ea1SDimitry Andric 200*0fca6ea1SDimitry Andric // Name and num occurrences. 20106c3fb27SDimitry Andric for (u32 I = 0; I < ExtraIndent; ++I) 20206c3fb27SDimitry Andric Str.append("%s", " "); 20306c3fb27SDimitry Andric Str.append("%s (%" PRId64 ")\n", Timers[HandleId].Name, Occurrence); 20406c3fb27SDimitry Andric 20506c3fb27SDimitry Andric for (u32 I = 0; I < NumAllocatedTimers; ++I) 20606c3fb27SDimitry Andric if (Timers[I].Nesting == HandleId) 207*0fca6ea1SDimitry Andric getImpl(Str, I, ExtraIndent + 1); 20806c3fb27SDimitry Andric } 20906c3fb27SDimitry Andric 21006c3fb27SDimitry Andric // Instead of maintaining pages for timer registration, a static buffer is 21106c3fb27SDimitry Andric // sufficient for most use cases in Scudo. 21206c3fb27SDimitry Andric static constexpr u32 MaxNumberOfTimers = 50; 21306c3fb27SDimitry Andric static constexpr u32 MaxLenOfTimerName = 50; 21406c3fb27SDimitry Andric static constexpr u32 DefaultPrintingInterval = 100; 21506c3fb27SDimitry Andric 21606c3fb27SDimitry Andric struct Record { 21706c3fb27SDimitry Andric u64 AccumulatedTime = 0; 21806c3fb27SDimitry Andric u64 Occurrence = 0; 219*0fca6ea1SDimitry Andric u64 MaxTime = 0; 22006c3fb27SDimitry Andric }; 22106c3fb27SDimitry Andric 22206c3fb27SDimitry Andric struct TimerInfo { 22306c3fb27SDimitry Andric char Name[MaxLenOfTimerName + 1]; 22406c3fb27SDimitry Andric u32 Nesting = MaxNumberOfTimers; 22506c3fb27SDimitry Andric }; 22606c3fb27SDimitry Andric 22706c3fb27SDimitry Andric HybridMutex Mutex; 22806c3fb27SDimitry Andric // The frequency of proactively dumping the timer statistics. For example, the 22906c3fb27SDimitry Andric // default setting is to dump the statistics every 100 reported events. 23006c3fb27SDimitry Andric u32 PrintingInterval GUARDED_BY(Mutex); 23106c3fb27SDimitry Andric u64 NumEventsReported GUARDED_BY(Mutex) = 0; 23206c3fb27SDimitry Andric u32 NumAllocatedTimers GUARDED_BY(Mutex) = 0; 23306c3fb27SDimitry Andric TimerInfo Timers[MaxNumberOfTimers] GUARDED_BY(Mutex); 23406c3fb27SDimitry Andric Record TimerRecords[MaxNumberOfTimers] GUARDED_BY(Mutex); 23506c3fb27SDimitry Andric }; 23606c3fb27SDimitry Andric 23706c3fb27SDimitry Andric } // namespace scudo 23806c3fb27SDimitry Andric 23906c3fb27SDimitry Andric #endif // SCUDO_TIMING_H_ 240