1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_RUNTIME_BASE_TIMING_LOGGER_H_
18 #define ART_RUNTIME_BASE_TIMING_LOGGER_H_
19 
20 #include "base/locks.h"
21 #include "base/macros.h"
22 #include "base/time_utils.h"
23 
24 #include <memory>
25 #include <set>
26 #include <string>
27 #include <vector>
28 
29 namespace art {
30 class TimingLogger;
31 
32 class CumulativeLogger {
33  public:
34   explicit CumulativeLogger(const std::string& name);
35   ~CumulativeLogger();
36   void Start();
37   void End() REQUIRES(!GetLock());
38   void Reset() REQUIRES(!GetLock());
39   void Dump(std::ostream& os) const REQUIRES(!GetLock());
GetTotalNs()40   uint64_t GetTotalNs() const {
41     return GetTotalTime() * kAdjust;
42   }
43   // Allow the name to be modified, particularly when the cumulative logger is a field within a
44   // parent class that is unable to determine the "name" of a sub-class.
45   void SetName(const std::string& name) REQUIRES(!GetLock());
46   void AddLogger(const TimingLogger& logger) REQUIRES(!GetLock());
47   size_t GetIterations() const REQUIRES(!GetLock());
48 
49  private:
50   class CumulativeTime {
51    public:
CumulativeTime(const char * name,uint64_t time)52     CumulativeTime(const char* name, uint64_t time) : name_(name), time_(time) {}
Add(uint64_t time)53     void Add(uint64_t time) { time_ += time; }
Name()54     const char* Name() const { return name_; }
Sum()55     uint64_t Sum() const { return time_; }
56     // Compare addresses of names for sorting.
57     bool operator< (const CumulativeTime& ct) const {
58       return std::less<const char*>()(name_, ct.name_);
59     }
60 
61    private:
62     const char* name_;
63     uint64_t time_;
64   };
65 
66   void DumpAverages(std::ostream &os) const REQUIRES(GetLock());
67   void AddPair(const char* label, uint64_t delta_time) REQUIRES(GetLock());
GetTotalTime()68   uint64_t GetTotalTime() const {
69     return total_time_;
70   }
71 
GetLock()72   Mutex* GetLock() const {
73     return lock_.get();
74   }
75 
76   static constexpr uint64_t kAdjust = 1000;
77   // Use a vector to keep dirty memory to minimal number of pages. Using a
78   // hashtable would be performant, but could lead to more dirty pages. Also, we
79   // don't expect this vector to be too big.
80   std::vector<CumulativeTime> cumulative_timers_ GUARDED_BY(GetLock());
81   std::string name_;
82   const std::string lock_name_;
83   mutable std::unique_ptr<Mutex> lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
84   size_t iterations_ GUARDED_BY(GetLock());
85   uint64_t total_time_;
86 
87   DISALLOW_COPY_AND_ASSIGN(CumulativeLogger);
88 };
89 
90 // A timing logger that knows when a split starts for the purposes of logging tools, like systrace.
91 class TimingLogger {
92  public:
93   static constexpr size_t kIndexNotFound = static_cast<size_t>(-1);
94 
95   // Kind of timing we are going to do. We collect time at the nano second.
96   enum class TimingKind {
97     kMonotonic,
98     kThreadCpu,
99   };
100 
101   class Timing {
102    public:
Timing(TimingKind kind,const char * name)103     Timing(TimingKind kind, const char* name) : name_(name) {
104        switch (kind) {
105         case TimingKind::kMonotonic:
106           time_ = NanoTime();
107           break;
108         case TimingKind::kThreadCpu:
109           time_ = ThreadCpuNanoTime();
110           break;
111        }
112     }
IsStartTiming()113     bool IsStartTiming() const {
114       return !IsEndTiming();
115     }
IsEndTiming()116     bool IsEndTiming() const {
117       return name_ == nullptr;
118     }
GetTime()119     uint64_t GetTime() const {
120       return time_;
121     }
GetName()122     const char* GetName() const {
123       return name_;
124     }
125 
126    private:
127     uint64_t time_;
128     const char* name_;
129   };
130 
131   // Extra data that is only calculated when you call dump to prevent excess allocation.
132   class TimingData {
133    public:
134     TimingData() = default;
TimingData(TimingData && other)135     TimingData(TimingData&& other) {
136       std::swap(data_, other.data_);
137     }
138     TimingData& operator=(TimingData&& other) {
139       std::swap(data_, other.data_);
140       return *this;
141     }
GetTotalTime(size_t idx)142     uint64_t GetTotalTime(size_t idx) {
143       return data_[idx].total_time;
144     }
GetExclusiveTime(size_t idx)145     uint64_t GetExclusiveTime(size_t idx) {
146       return data_[idx].exclusive_time;
147     }
148 
149    private:
150     // Each begin split has a total time and exclusive time. Exclusive time is total time - total
151     // time of children nodes.
152     struct CalculatedDataPoint {
CalculatedDataPointCalculatedDataPoint153       CalculatedDataPoint() : total_time(0), exclusive_time(0) {}
154       uint64_t total_time;
155       uint64_t exclusive_time;
156     };
157     std::vector<CalculatedDataPoint> data_;
158     friend class TimingLogger;
159   };
160 
161   TimingLogger(const char* name,
162                bool precise,
163                bool verbose,
164                TimingKind kind = TimingKind::kMonotonic);
165   ~TimingLogger();
166   // Verify that all open timings have related closed timings.
167   void Verify();
168   // Clears current timings and labels.
169   void Reset();
170   // Starts a timing.
171   void StartTiming(const char* new_split_label);
172   // Ends the current timing.
173   void EndTiming();
174   // End the current timing and start a new timing. Usage not recommended.
NewTiming(const char * new_split_label)175   void NewTiming(const char* new_split_label) {
176     EndTiming();
177     StartTiming(new_split_label);
178   }
179   // Returns the total duration of the timings (sum of total times).
180   uint64_t GetTotalNs() const;
181   // Find the index of a timing by name.
182   size_t FindTimingIndex(const char* name, size_t start_idx) const;
183   void Dump(std::ostream& os, const char* indent_string = "  ") const;
184 
185   // Scoped timing splits that can be nested and composed with the explicit split
186   // starts and ends.
187   class ScopedTiming {
188    public:
ScopedTiming(const char * label,TimingLogger * logger)189     ScopedTiming(const char* label, TimingLogger* logger) : logger_(logger) {
190       logger_->StartTiming(label);
191     }
~ScopedTiming()192     ~ScopedTiming() {
193       logger_->EndTiming();
194     }
195     // Closes the current timing and opens a new timing.
NewTiming(const char * label)196     void NewTiming(const char* label) {
197       logger_->NewTiming(label);
198     }
199 
200    private:
201     TimingLogger* const logger_;  // The timing logger which the scoped timing is associated with.
202     DISALLOW_COPY_AND_ASSIGN(ScopedTiming);
203   };
204 
205   // Return the time points of when each start / end timings start and finish.
GetTimings()206   const std::vector<Timing>& GetTimings() const {
207     return timings_;
208   }
209 
210   TimingData CalculateTimingData() const;
211 
212  protected:
213   // The name of the timing logger.
214   const char* const name_;
215   // Do we want to print the exactly recorded split (true) or round down to the time unit being
216   // used (false).
217   const bool precise_;
218   // Verbose logging.
219   const bool verbose_;
220   // The kind of timing we want.
221   const TimingKind kind_;
222   // Timing points that are either start or end points. For each starting point ret[i] = location
223   // of end split associated with i. If it is and end split ret[i] = i.
224   std::vector<Timing> timings_;
225 
226  private:
227   DISALLOW_COPY_AND_ASSIGN(TimingLogger);
228 };
229 
230 }  // namespace art
231 
232 #endif  // ART_RUNTIME_BASE_TIMING_LOGGER_H_
233