1*76559068SAndroid Build Coastguard Worker //===-- timing.h ------------------------------------------------*- C++ -*-===// 2*76559068SAndroid Build Coastguard Worker // 3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information. 5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*76559068SAndroid Build Coastguard Worker // 7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===// 8*76559068SAndroid Build Coastguard Worker 9*76559068SAndroid Build Coastguard Worker #ifndef SCUDO_TIMING_H_ 10*76559068SAndroid Build Coastguard Worker #define SCUDO_TIMING_H_ 11*76559068SAndroid Build Coastguard Worker 12*76559068SAndroid Build Coastguard Worker #include "common.h" 13*76559068SAndroid Build Coastguard Worker #include "mutex.h" 14*76559068SAndroid Build Coastguard Worker #include "string_utils.h" 15*76559068SAndroid Build Coastguard Worker #include "thread_annotations.h" 16*76559068SAndroid Build Coastguard Worker 17*76559068SAndroid Build Coastguard Worker #ifndef __STDC_FORMAT_MACROS 18*76559068SAndroid Build Coastguard Worker // Ensure PRId64 macro is available 19*76559068SAndroid Build Coastguard Worker #define __STDC_FORMAT_MACROS 1 20*76559068SAndroid Build Coastguard Worker #endif 21*76559068SAndroid Build Coastguard Worker #include <inttypes.h> 22*76559068SAndroid Build Coastguard Worker #include <string.h> 23*76559068SAndroid Build Coastguard Worker 24*76559068SAndroid Build Coastguard Worker namespace scudo { 25*76559068SAndroid Build Coastguard Worker 26*76559068SAndroid Build Coastguard Worker class TimingManager; 27*76559068SAndroid Build Coastguard Worker 28*76559068SAndroid Build Coastguard Worker // A simple timer for evaluating execution time of code snippets. It can be used 29*76559068SAndroid Build Coastguard Worker // along with TimingManager or standalone. 30*76559068SAndroid Build Coastguard Worker class Timer { 31*76559068SAndroid Build Coastguard Worker public: 32*76559068SAndroid Build Coastguard Worker // The use of Timer without binding to a TimingManager is supposed to do the 33*76559068SAndroid Build Coastguard Worker // timer logging manually. Otherwise, TimingManager will do the logging stuff 34*76559068SAndroid Build Coastguard Worker // for you. 35*76559068SAndroid Build Coastguard Worker Timer() = default; Timer(Timer && Other)36*76559068SAndroid Build Coastguard Worker Timer(Timer &&Other) 37*76559068SAndroid Build Coastguard Worker : StartTime(0), AccTime(Other.AccTime), Manager(Other.Manager), 38*76559068SAndroid Build Coastguard Worker HandleId(Other.HandleId) { 39*76559068SAndroid Build Coastguard Worker Other.Manager = nullptr; 40*76559068SAndroid Build Coastguard Worker } 41*76559068SAndroid Build Coastguard Worker 42*76559068SAndroid Build Coastguard Worker Timer(const Timer &) = delete; 43*76559068SAndroid Build Coastguard Worker 44*76559068SAndroid Build Coastguard Worker ~Timer(); 45*76559068SAndroid Build Coastguard Worker start()46*76559068SAndroid Build Coastguard Worker void start() { 47*76559068SAndroid Build Coastguard Worker CHECK_EQ(StartTime, 0U); 48*76559068SAndroid Build Coastguard Worker StartTime = getMonotonicTime(); 49*76559068SAndroid Build Coastguard Worker } stop()50*76559068SAndroid Build Coastguard Worker void stop() { 51*76559068SAndroid Build Coastguard Worker AccTime += getMonotonicTime() - StartTime; 52*76559068SAndroid Build Coastguard Worker StartTime = 0; 53*76559068SAndroid Build Coastguard Worker } getAccumulatedTime()54*76559068SAndroid Build Coastguard Worker u64 getAccumulatedTime() const { return AccTime; } 55*76559068SAndroid Build Coastguard Worker 56*76559068SAndroid Build Coastguard Worker // Unset the bound TimingManager so that we don't report the data back. This 57*76559068SAndroid Build Coastguard Worker // is useful if we only want to track subset of certain scope events. ignore()58*76559068SAndroid Build Coastguard Worker void ignore() { 59*76559068SAndroid Build Coastguard Worker StartTime = 0; 60*76559068SAndroid Build Coastguard Worker AccTime = 0; 61*76559068SAndroid Build Coastguard Worker Manager = nullptr; 62*76559068SAndroid Build Coastguard Worker } 63*76559068SAndroid Build Coastguard Worker 64*76559068SAndroid Build Coastguard Worker protected: 65*76559068SAndroid Build Coastguard Worker friend class TimingManager; Timer(TimingManager & Manager,u32 HandleId)66*76559068SAndroid Build Coastguard Worker Timer(TimingManager &Manager, u32 HandleId) 67*76559068SAndroid Build Coastguard Worker : Manager(&Manager), HandleId(HandleId) {} 68*76559068SAndroid Build Coastguard Worker 69*76559068SAndroid Build Coastguard Worker u64 StartTime = 0; 70*76559068SAndroid Build Coastguard Worker u64 AccTime = 0; 71*76559068SAndroid Build Coastguard Worker TimingManager *Manager = nullptr; 72*76559068SAndroid Build Coastguard Worker u32 HandleId; 73*76559068SAndroid Build Coastguard Worker }; 74*76559068SAndroid Build Coastguard Worker 75*76559068SAndroid Build Coastguard Worker // A RAII-style wrapper for easy scope execution measurement. Note that in order 76*76559068SAndroid Build Coastguard Worker // not to take additional space for the message like `Name`. It only works with 77*76559068SAndroid Build Coastguard Worker // TimingManager. 78*76559068SAndroid Build Coastguard Worker class ScopedTimer : public Timer { 79*76559068SAndroid Build Coastguard Worker public: 80*76559068SAndroid Build Coastguard Worker ScopedTimer(TimingManager &Manager, const char *Name); 81*76559068SAndroid Build Coastguard Worker ScopedTimer(TimingManager &Manager, const Timer &Nest, const char *Name); ~ScopedTimer()82*76559068SAndroid Build Coastguard Worker ~ScopedTimer() { stop(); } 83*76559068SAndroid Build Coastguard Worker }; 84*76559068SAndroid Build Coastguard Worker 85*76559068SAndroid Build Coastguard Worker // In Scudo, the execution time of single run of code snippets may not be 86*76559068SAndroid Build Coastguard Worker // useful, we are more interested in the average time from several runs. 87*76559068SAndroid Build Coastguard Worker // TimingManager lets the registered timer report their data and reports the 88*76559068SAndroid Build Coastguard Worker // average execution time for each timer periodically. 89*76559068SAndroid Build Coastguard Worker class TimingManager { 90*76559068SAndroid Build Coastguard Worker public: 91*76559068SAndroid Build Coastguard Worker TimingManager(u32 PrintingInterval = DefaultPrintingInterval) PrintingInterval(PrintingInterval)92*76559068SAndroid Build Coastguard Worker : PrintingInterval(PrintingInterval) {} ~TimingManager()93*76559068SAndroid Build Coastguard Worker ~TimingManager() { 94*76559068SAndroid Build Coastguard Worker if (NumAllocatedTimers != 0) 95*76559068SAndroid Build Coastguard Worker printAll(); 96*76559068SAndroid Build Coastguard Worker } 97*76559068SAndroid Build Coastguard Worker getOrCreateTimer(const char * Name)98*76559068SAndroid Build Coastguard Worker Timer getOrCreateTimer(const char *Name) EXCLUDES(Mutex) { 99*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex); 100*76559068SAndroid Build Coastguard Worker 101*76559068SAndroid Build Coastguard Worker CHECK_LT(strlen(Name), MaxLenOfTimerName); 102*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < NumAllocatedTimers; ++I) { 103*76559068SAndroid Build Coastguard Worker if (strncmp(Name, Timers[I].Name, MaxLenOfTimerName) == 0) 104*76559068SAndroid Build Coastguard Worker return Timer(*this, I); 105*76559068SAndroid Build Coastguard Worker } 106*76559068SAndroid Build Coastguard Worker 107*76559068SAndroid Build Coastguard Worker CHECK_LT(NumAllocatedTimers, MaxNumberOfTimers); 108*76559068SAndroid Build Coastguard Worker strncpy(Timers[NumAllocatedTimers].Name, Name, MaxLenOfTimerName); 109*76559068SAndroid Build Coastguard Worker TimerRecords[NumAllocatedTimers].AccumulatedTime = 0; 110*76559068SAndroid Build Coastguard Worker TimerRecords[NumAllocatedTimers].Occurrence = 0; 111*76559068SAndroid Build Coastguard Worker TimerRecords[NumAllocatedTimers].MaxTime = 0; 112*76559068SAndroid Build Coastguard Worker return Timer(*this, NumAllocatedTimers++); 113*76559068SAndroid Build Coastguard Worker } 114*76559068SAndroid Build Coastguard Worker 115*76559068SAndroid Build Coastguard Worker // Add a sub-Timer associated with another Timer. This is used when we want to 116*76559068SAndroid Build Coastguard Worker // detail the execution time in the scope of a Timer. 117*76559068SAndroid Build Coastguard Worker // For example, 118*76559068SAndroid Build Coastguard Worker // void Foo() { 119*76559068SAndroid Build Coastguard Worker // // T1 records the time spent in both first and second tasks. 120*76559068SAndroid Build Coastguard Worker // ScopedTimer T1(getTimingManager(), "Task1"); 121*76559068SAndroid Build Coastguard Worker // { 122*76559068SAndroid Build Coastguard Worker // // T2 records the time spent in first task 123*76559068SAndroid Build Coastguard Worker // ScopedTimer T2(getTimingManager, T1, "Task2"); 124*76559068SAndroid Build Coastguard Worker // // Do first task. 125*76559068SAndroid Build Coastguard Worker // } 126*76559068SAndroid Build Coastguard Worker // // Do second task. 127*76559068SAndroid Build Coastguard Worker // } 128*76559068SAndroid Build Coastguard Worker // 129*76559068SAndroid Build Coastguard Worker // The report will show proper indents to indicate the nested relation like, 130*76559068SAndroid Build Coastguard Worker // -- Average Operation Time -- -- Name (# of Calls) -- 131*76559068SAndroid Build Coastguard Worker // 10.0(ns) Task1 (1) 132*76559068SAndroid Build Coastguard Worker // 5.0(ns) Task2 (1) nest(const Timer & T,const char * Name)133*76559068SAndroid Build Coastguard Worker Timer nest(const Timer &T, const char *Name) EXCLUDES(Mutex) { 134*76559068SAndroid Build Coastguard Worker CHECK_EQ(T.Manager, this); 135*76559068SAndroid Build Coastguard Worker Timer Nesting = getOrCreateTimer(Name); 136*76559068SAndroid Build Coastguard Worker 137*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex); 138*76559068SAndroid Build Coastguard Worker CHECK_NE(Nesting.HandleId, T.HandleId); 139*76559068SAndroid Build Coastguard Worker Timers[Nesting.HandleId].Nesting = T.HandleId; 140*76559068SAndroid Build Coastguard Worker return Nesting; 141*76559068SAndroid Build Coastguard Worker } 142*76559068SAndroid Build Coastguard Worker report(const Timer & T)143*76559068SAndroid Build Coastguard Worker void report(const Timer &T) EXCLUDES(Mutex) { 144*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex); 145*76559068SAndroid Build Coastguard Worker 146*76559068SAndroid Build Coastguard Worker const u32 HandleId = T.HandleId; 147*76559068SAndroid Build Coastguard Worker CHECK_LT(HandleId, MaxNumberOfTimers); 148*76559068SAndroid Build Coastguard Worker u64 AccTime = T.getAccumulatedTime(); 149*76559068SAndroid Build Coastguard Worker TimerRecords[HandleId].AccumulatedTime += AccTime; 150*76559068SAndroid Build Coastguard Worker if (AccTime > TimerRecords[HandleId].MaxTime) { 151*76559068SAndroid Build Coastguard Worker TimerRecords[HandleId].MaxTime = AccTime; 152*76559068SAndroid Build Coastguard Worker } 153*76559068SAndroid Build Coastguard Worker ++TimerRecords[HandleId].Occurrence; 154*76559068SAndroid Build Coastguard Worker ++NumEventsReported; 155*76559068SAndroid Build Coastguard Worker if (NumEventsReported % PrintingInterval == 0) { 156*76559068SAndroid Build Coastguard Worker ScopedString Str; 157*76559068SAndroid Build Coastguard Worker getAllImpl(Str); 158*76559068SAndroid Build Coastguard Worker Str.output(); 159*76559068SAndroid Build Coastguard Worker } 160*76559068SAndroid Build Coastguard Worker } 161*76559068SAndroid Build Coastguard Worker printAll()162*76559068SAndroid Build Coastguard Worker void printAll() EXCLUDES(Mutex) { 163*76559068SAndroid Build Coastguard Worker ScopedString Str; 164*76559068SAndroid Build Coastguard Worker getAll(Str); 165*76559068SAndroid Build Coastguard Worker Str.output(); 166*76559068SAndroid Build Coastguard Worker } 167*76559068SAndroid Build Coastguard Worker getAll(ScopedString & Str)168*76559068SAndroid Build Coastguard Worker void getAll(ScopedString &Str) EXCLUDES(Mutex) { 169*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex); 170*76559068SAndroid Build Coastguard Worker getAllImpl(Str); 171*76559068SAndroid Build Coastguard Worker } 172*76559068SAndroid Build Coastguard Worker 173*76559068SAndroid Build Coastguard Worker private: getAllImpl(ScopedString & Str)174*76559068SAndroid Build Coastguard Worker void getAllImpl(ScopedString &Str) REQUIRES(Mutex) { 175*76559068SAndroid Build Coastguard Worker static char AvgHeader[] = "-- Average Operation Time --"; 176*76559068SAndroid Build Coastguard Worker static char MaxHeader[] = "-- Maximum Operation Time --"; 177*76559068SAndroid Build Coastguard Worker static char NameHeader[] = "-- Name (# of Calls) --"; 178*76559068SAndroid Build Coastguard Worker Str.append("%-15s %-15s %-15s\n", AvgHeader, MaxHeader, NameHeader); 179*76559068SAndroid Build Coastguard Worker 180*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < NumAllocatedTimers; ++I) { 181*76559068SAndroid Build Coastguard Worker if (Timers[I].Nesting != MaxNumberOfTimers) 182*76559068SAndroid Build Coastguard Worker continue; 183*76559068SAndroid Build Coastguard Worker getImpl(Str, I); 184*76559068SAndroid Build Coastguard Worker } 185*76559068SAndroid Build Coastguard Worker } 186*76559068SAndroid Build Coastguard Worker 187*76559068SAndroid Build Coastguard Worker void getImpl(ScopedString &Str, const u32 HandleId, const u32 ExtraIndent = 0) REQUIRES(Mutex)188*76559068SAndroid Build Coastguard Worker REQUIRES(Mutex) { 189*76559068SAndroid Build Coastguard Worker const u64 AccumulatedTime = TimerRecords[HandleId].AccumulatedTime; 190*76559068SAndroid Build Coastguard Worker const u64 Occurrence = TimerRecords[HandleId].Occurrence; 191*76559068SAndroid Build Coastguard Worker const u64 Integral = Occurrence == 0 ? 0 : AccumulatedTime / Occurrence; 192*76559068SAndroid Build Coastguard Worker // Only keep single digit of fraction is enough and it enables easier layout 193*76559068SAndroid Build Coastguard Worker // maintenance. 194*76559068SAndroid Build Coastguard Worker const u64 Fraction = 195*76559068SAndroid Build Coastguard Worker Occurrence == 0 ? 0 196*76559068SAndroid Build Coastguard Worker : ((AccumulatedTime % Occurrence) * 10) / Occurrence; 197*76559068SAndroid Build Coastguard Worker 198*76559068SAndroid Build Coastguard Worker // Average time. 199*76559068SAndroid Build Coastguard Worker Str.append("%14" PRId64 ".%" PRId64 "(ns) %-8s", Integral, Fraction, " "); 200*76559068SAndroid Build Coastguard Worker 201*76559068SAndroid Build Coastguard Worker // Maximum time. 202*76559068SAndroid Build Coastguard Worker Str.append("%16" PRId64 "(ns) %-11s", TimerRecords[HandleId].MaxTime, " "); 203*76559068SAndroid Build Coastguard Worker 204*76559068SAndroid Build Coastguard Worker // Name and num occurrences. 205*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < ExtraIndent; ++I) 206*76559068SAndroid Build Coastguard Worker Str.append("%s", " "); 207*76559068SAndroid Build Coastguard Worker Str.append("%s (%" PRId64 ")\n", Timers[HandleId].Name, Occurrence); 208*76559068SAndroid Build Coastguard Worker 209*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < NumAllocatedTimers; ++I) 210*76559068SAndroid Build Coastguard Worker if (Timers[I].Nesting == HandleId) 211*76559068SAndroid Build Coastguard Worker getImpl(Str, I, ExtraIndent + 1); 212*76559068SAndroid Build Coastguard Worker } 213*76559068SAndroid Build Coastguard Worker 214*76559068SAndroid Build Coastguard Worker // Instead of maintaining pages for timer registration, a static buffer is 215*76559068SAndroid Build Coastguard Worker // sufficient for most use cases in Scudo. 216*76559068SAndroid Build Coastguard Worker static constexpr u32 MaxNumberOfTimers = 50; 217*76559068SAndroid Build Coastguard Worker static constexpr u32 MaxLenOfTimerName = 50; 218*76559068SAndroid Build Coastguard Worker static constexpr u32 DefaultPrintingInterval = 100; 219*76559068SAndroid Build Coastguard Worker 220*76559068SAndroid Build Coastguard Worker struct Record { 221*76559068SAndroid Build Coastguard Worker u64 AccumulatedTime = 0; 222*76559068SAndroid Build Coastguard Worker u64 Occurrence = 0; 223*76559068SAndroid Build Coastguard Worker u64 MaxTime = 0; 224*76559068SAndroid Build Coastguard Worker }; 225*76559068SAndroid Build Coastguard Worker 226*76559068SAndroid Build Coastguard Worker struct TimerInfo { 227*76559068SAndroid Build Coastguard Worker char Name[MaxLenOfTimerName + 1]; 228*76559068SAndroid Build Coastguard Worker u32 Nesting = MaxNumberOfTimers; 229*76559068SAndroid Build Coastguard Worker }; 230*76559068SAndroid Build Coastguard Worker 231*76559068SAndroid Build Coastguard Worker HybridMutex Mutex; 232*76559068SAndroid Build Coastguard Worker // The frequency of proactively dumping the timer statistics. For example, the 233*76559068SAndroid Build Coastguard Worker // default setting is to dump the statistics every 100 reported events. 234*76559068SAndroid Build Coastguard Worker u32 PrintingInterval GUARDED_BY(Mutex); 235*76559068SAndroid Build Coastguard Worker u64 NumEventsReported GUARDED_BY(Mutex) = 0; 236*76559068SAndroid Build Coastguard Worker u32 NumAllocatedTimers GUARDED_BY(Mutex) = 0; 237*76559068SAndroid Build Coastguard Worker TimerInfo Timers[MaxNumberOfTimers] GUARDED_BY(Mutex); 238*76559068SAndroid Build Coastguard Worker Record TimerRecords[MaxNumberOfTimers] GUARDED_BY(Mutex); 239*76559068SAndroid Build Coastguard Worker }; 240*76559068SAndroid Build Coastguard Worker 241*76559068SAndroid Build Coastguard Worker } // namespace scudo 242*76559068SAndroid Build Coastguard Worker 243*76559068SAndroid Build Coastguard Worker #endif // SCUDO_TIMING_H_ 244