xref: /freebsd/contrib/llvm-project/compiler-rt/lib/scudo/standalone/timing.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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