xref: /aosp_15_r20/external/scudo/standalone/timing.h (revision 76559068c068bd27e82aff38fac3bfc865233bca)
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