• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 ANDROIDFW_RESOURCETIMER_H_
18 #define ANDROIDFW_RESOURCETIMER_H_
19 
20 #include <time.h>
21 #include <atomic>
22 #include <vector>
23 
24 #include <utils/Mutex.h>
25 #include <android-base/macros.h>
26 #include <androidfw/Util.h>
27 
28 namespace android {
29 
30 // ResourceTimer captures the duration of short functions.  Durations are accumulated in registers
31 // and statistics are pulled back to the Java layer as needed.
32 // To monitor an API, first add it to the Counter enumeration.  Then, inside the API, create an
33 // instance of ResourceTimer with the appropriate enumeral.  The corresponding counter will be
34 // updated when the ResourceTimer destructor is called, normally at the end of the enclosing block.
35 class ResourceTimer {
36  public:
37   enum class Counter {
38     GetResourceValue,
39     RetrieveAttributes,
40 
41     LastCounter = RetrieveAttributes,
42   };
43   static const int counterSize = static_cast<int>(Counter::LastCounter) + 1;
44   static char const *toString(Counter);
45 
46   // Start a timer for the specified counter.
47   ResourceTimer(Counter);
48   // The block is exiting.  If the timer is active, record it.
49   ~ResourceTimer();
50   // This records the elapsed time and disables further recording.  Use this if the containing
51   // block includes extra processing that should not be included in the timer.  The method is
52   // destructive in that the timer is no longer valid and further calls to record() will be
53   // ignored.
54   void record();
55   // This cancels a timer.  Elapsed time will neither be computed nor recorded.
56   void cancel();
57 
58   // A single timer contains the count of events and the cumulative time spent handling the
59   // events.  It also includes the smallest value seen and 10 largest values seen.  Finally, it
60   // includes a histogram of values that approximates a semi-log.
61 
62   // The timer can compute percentiles of recorded events.  For example, the p50 value is a time
63   // such that 50% of the readings are below the value and 50% are above the value.  The
64   // granularity in the readings means that a percentile cannot always be computed.  In this case,
65   // the percentile is reported as zero.  (The simplest example is when there is a single
66   // reading.)  Even if the value can be computed, it will not be exact.  Therefore, a percentile
67   // is actually reported as two values: the lowest time at which it might be valid and the
68   // highest time at which it might be valid.
69   struct Timer {
70     static const size_t MaxLargest = 5;
71 
72     // The construct zeros all the fields.  The destructor releases memory allocated to the
73     // buckets.
74     Timer();
75     ~Timer();
76 
77     // The following summary values are set to zero on a reset.  All times are in ns.
78 
79     // The total number of events recorded.
80     int count;
81     // The total duration of events.
82     int64_t total;
83     // The smallest event duration seen.  This is guaranteed to be non-zero if count is greater
84     // than 0.
85     int mintime;
86     // The largest event duration seen.
87     int maxtime;
88 
89     // The largest values seen.  Element 0 is the largest value seen (and is the same as maxtime,
90     // above).  Element 1 is the next largest, and so on.  If count is less than MaxLargest,
91     // unused elements will be zero.
92     int largest[MaxLargest];
93 
94     // The p50 value is a time such that 50% of the readings are below that time and 50% of the
95     // readings.
96 
97     // A single percentile is defined by the lowest value supported by the readings and the
98     // highest value supported by the readings.
99     struct Percentile {
100       // The nominal time (in ns) of the percentile.  The true percentile is guaranteed to be less
101       // than or equal to this time.
102       int nominal;
103       // The actual percentile of the nominal time.
104       int nominal_actual;
105       // The time of the next lower bin.  The true percentile is guaranteed to be greater than
106       // this time.
107       int floor;
108       // The actual percentile of the floor time.
109       int floor_actual;
110 
111       // Fill in a percentile given the cumulative to the bin, the count in the current bin, the
112       // total count, the width of the bin, and the time of the bin.
113       void compute(int cumulative, int current, int count, int width, int time);
114     };
115 
116     // The structure that holds the percentiles.
117     struct {
118       Percentile p50;
119       Percentile p90;
120       Percentile p95;
121       Percentile p99;
122     } pvalues;
123 
124     // Set all counters to zero.
125     void reset();
126     // Record an event.  The input time is in ns.
127     void record(int);
128     // Compute the percentiles.  Percentiles are computed on demand, as the computation is too
129     // expensive to be done inline.
130     void compute();
131 
132     // Copy one timer to another.  If reset is true then the src is reset immediately after the
133     // copy.  The reset flag is exploited to make the copy faster.  Any data in dst is lost.
134     static void copy(Timer &dst, Timer &src, bool reset);
135 
136    private:
137     // Free any buckets.
138     void freeBuckets();
139 
140     // Readings are placed in bins, which are orgzanized into decades.  The decade 0 covers
141     // [0,100) in steps of 1us.  Decade 1 covers [0,1000) in steps of 10us.  Decade 2 covers
142     // [0,10000) in steps of 100us.  And so on.
143 
144     // An event is placed in the first bin that can hold it.  This means that events in the range
145     // of [0,100) are placed in the first decade, events in the range of [0,1000) are placed in
146     // the second decade, and so on.  This also means that the first 10% of the bins are unused
147     // in each decade after the first.
148 
149     // The design provides at least two significant digits across the range of [0,10000).
150 
151     static const size_t MaxDimension = 4;
152     static const size_t MaxBuckets = 100;
153 
154     // The range of each dimension.  The lower value is always zero.
155     static const int range[MaxDimension];
156     // The width of each bin, by dimension
157     static const int width[MaxDimension];
158 
159     // A histogram of the values seen. Centuries are allocated as needed, to minimize the memory
160     // impact.
161     int *buckets[MaxDimension];
162   };
163 
164   // Fetch one Timer.  The function has a short-circuit behavior: if the count is zero then
165   // destination count is set to zero and the function returns false.  Otherwise, the destination
166   // is a copy of the source and the function returns true.  This behavior lowers the cost of
167   // handling unused timers.
168   static bool copy(int src, Timer &dst, bool reset);
169 
170   // Enable the timers.  Timers are initially disabled.  Enabling timers allocates memory for the
171   // counters.  Timers cannot be disabled.
172   static void enable();
173 
174  private:
175   // An internal reset method.  This does not take a lock.
176   static void reset();
177 
178   // Helper method to convert a counter into an enum.  Presumably, this will be inlined into zero
179   // actual cpu instructions.
toIndex(Counter c)180   static inline std::vector<unsigned int>::size_type toIndex(Counter c) {
181     return static_cast<std::vector<unsigned int>::size_type>(c);
182   }
183 
184   // Every counter has an associated lock.  The lock has been factored into a separate class to
185   // keep the Timer class a POD.
186   struct GuardedTimer {
187     Mutex lock_;
188     Timer timer_;
189   };
190 
191   // Scoped timer
192   struct ScopedTimer {
193     AutoMutex _l;
194     Timer &t;
ScopedTimerScopedTimer195     ScopedTimer(GuardedTimer &g) :
196         _l(g.lock_), t(g.timer_) {
197     }
198     Timer *operator->() {
199       return &t;
200     }
201     Timer& operator*() {
202       return t;
203     }
204   };
205 
206   // An individual timer is active (or not), is tracking a specific API, and has a start time.
207   // The api and the start time are undefined if the timer is not active.
208   bool active_;
209   Counter api_;
210   struct timespec start_;
211 
212   // The global enable flag.  This is initially false and may be set true by the java runtime.
213   static std::atomic<bool> enabled_;
214 
215   // The global timers.  The memory for the timers is not allocated until the timers are enabled.
216   static std::atomic<GuardedTimer *> counter_;
217 };
218 
219 }  // namespace android
220 
221 #endif /* ANDROIDFW_RESOURCETIMER_H_ */
222