• 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 #include <unistd.h>
18 #include <string.h>
19 
20 #include <map>
21 #include <atomic>
22 
23 #include <utils/Log.h>
24 #include <androidfw/ResourceTimer.h>
25 
26 // The following block allows compilation on windows, which does not have getuid().
27 #ifdef _WIN32
28 #ifdef ERROR
29 #undef ERROR
30 #endif
31 #define getuid() (getUidWindows_)
32 #endif
33 
34 namespace android {
35 
36 namespace {
37 
38 #ifdef _WIN32
39 // A temporary to confuse lint into thinking that getuid() on windows might return something other
40 // than zero.
41 int getUidWindows_ = 0;
42 #endif
43 
44 // The number of nanoseconds in a microsecond.
45 static const unsigned int US = 1000;
46 // The number of nanoseconds in a second.
47 static const unsigned int S = 1000 * 1000 * 1000;
48 
49 // Return the difference between two timespec values.  The difference is in nanoseconds.  If the
50 // return value would exceed 2s (2^31 nanoseconds) then UINT_MAX is returned.
diffInNs(timespec const & a,timespec const & b)51 unsigned int diffInNs(timespec const &a, timespec const &b) {
52   timespec r = { 0, 0 };
53   r.tv_nsec = a.tv_nsec - b.tv_nsec;
54   if (r.tv_nsec < 0) {
55     r.tv_sec = -1;
56     r.tv_nsec += S;
57   }
58   r.tv_sec = r.tv_sec + (a.tv_sec - b.tv_sec);
59   if (r.tv_sec > 2) return UINT_MAX;
60   unsigned int result = (r.tv_sec * S) + r.tv_nsec;
61   if (result > 2 * S) return UINT_MAX;
62   return result;
63 }
64 
65 }
66 
ResourceTimer(Counter api)67 ResourceTimer::ResourceTimer(Counter api)
68     : active_(enabled_.load()),
69       api_(api) {
70   if (active_) {
71     clock_gettime(CLOCK_MONOTONIC, &start_);
72   }
73 }
74 
~ResourceTimer()75 ResourceTimer::~ResourceTimer() {
76   record();
77 }
78 
enable()79 void ResourceTimer::enable() {
80   if (!enabled_.load()) counter_ = new GuardedTimer[ResourceTimer::counterSize];
81   enabled_.store(true);
82 }
83 
cancel()84 void ResourceTimer::cancel() {
85   active_ = false;
86 }
87 
record()88 void ResourceTimer::record() {
89   if (!active_) return;
90 
91   struct timespec end;
92   clock_gettime(CLOCK_MONOTONIC, &end);
93   // Get the difference in microseconds.
94   const unsigned int ticks = diffInNs(end, start_);
95   ScopedTimer t(counter_[toIndex(api_)]);
96   t->record(ticks);
97   active_ = false;
98 }
99 
copy(int counter,Timer & dst,bool reset)100 bool ResourceTimer::copy(int counter, Timer &dst, bool reset) {
101   ScopedTimer t(counter_[counter]);
102   if (t->count == 0) {
103     dst.reset();
104     if (reset) t->reset();
105     return false;
106   }
107   Timer::copy(dst, *t, reset);
108   return true;
109 }
110 
reset()111 void ResourceTimer::reset() {
112   for (int i = 0; i < counterSize; i++) {
113     ScopedTimer t(counter_[i]);
114     t->reset();
115   }
116 }
117 
Timer()118 ResourceTimer::Timer::Timer() {
119   // Ensure newly-created objects are zeroed.
120   memset(buckets, 0, sizeof(buckets));
121   reset();
122 }
123 
~Timer()124 ResourceTimer::Timer::~Timer() {
125   for (int d = 0; d < MaxDimension; d++) {
126     delete[] buckets[d];
127   }
128 }
129 
freeBuckets()130 void ResourceTimer::Timer::freeBuckets() {
131   for (int d = 0; d < MaxDimension; d++) {
132     delete[] buckets[d];
133     buckets[d] = 0;
134   }
135 }
136 
reset()137 void ResourceTimer::Timer::reset() {
138   count = total = mintime = maxtime = 0;
139   memset(largest, 0, sizeof(largest));
140   memset(&pvalues, 0, sizeof(pvalues));
141   // Zero the histogram, keeping any allocated dimensions.
142   for (int d = 0; d < MaxDimension; d++) {
143     if (buckets[d] != 0) memset(buckets[d], 0, sizeof(int) * MaxBuckets);
144   }
145 }
146 
copy(Timer & dst,Timer & src,bool reset)147 void ResourceTimer::Timer::copy(Timer &dst, Timer &src, bool reset) {
148   dst.freeBuckets();
149   dst = src;
150   // Clean up the histograms.
151   if (reset) {
152     // Do NOT free the src buckets because they being used by dst.
153     memset(src.buckets, 0, sizeof(src.buckets));
154     src.reset();
155   } else {
156     for (int d = 0; d < MaxDimension; d++) {
157       if (src.buckets[d] != nullptr) {
158         dst.buckets[d] = new int[MaxBuckets];
159         memcpy(dst.buckets[d], src.buckets[d], sizeof(int) * MaxBuckets);
160       }
161     }
162   }
163 }
164 
record(int ticks)165 void ResourceTimer::Timer::record(int ticks) {
166   // Record that the event happened.
167   count++;
168 
169   total += ticks;
170   if (mintime == 0 || ticks < mintime) mintime = ticks;
171   if (ticks > maxtime) maxtime = ticks;
172 
173   // Do not add oversized events to the histogram.
174   if (ticks != UINT_MAX) {
175     for (int d = 0; d < MaxDimension; d++) {
176       if (ticks < range[d]) {
177         if (buckets[d] == 0) {
178           buckets[d] = new int[MaxBuckets];
179           memset(buckets[d], 0, sizeof(int) * MaxBuckets);
180         }
181         if (ticks < width[d]) {
182           // Special case: never write to bucket 0 because it complicates the percentile logic.
183           // However, this is always the smallest possible value to it is very unlikely to ever
184           // affect any of the percentile results.
185           buckets[d][1]++;
186         } else {
187           buckets[d][ticks / width[d]]++;
188         }
189         break;
190       }
191     }
192   }
193 
194   // The list of largest times is sorted with the biggest value at index 0 and the smallest at
195   // index MaxLargest-1.  The incoming tick count should be added to the array only if it is
196   // larger than the current value at MaxLargest-1.
197   if (ticks > largest[Timer::MaxLargest-1]) {
198     for (size_t i = 0; i < Timer::MaxLargest; i++) {
199       if (ticks > largest[i]) {
200         if (i < Timer::MaxLargest-1) {
201           for (size_t j = Timer::MaxLargest - 1; j > i; j--) {
202             largest[j] = largest[j-1];
203           }
204         }
205         largest[i] = ticks;
206         break;
207       }
208     }
209   }
210 }
211 
compute(int cumulative,int current,int count,int width,int time)212 void ResourceTimer::Timer::Percentile::compute(
213     int cumulative, int current, int count, int width, int time) {
214   nominal = time;
215   nominal_actual = (cumulative * 100) / count;
216   floor = nominal - width;
217   floor_actual = ((cumulative - current) * 100) / count;
218 }
219 
compute()220 void ResourceTimer::Timer::compute() {
221   memset(&pvalues, 0, sizeof(pvalues));
222 
223   float l50 = count / 2.0;
224   float l90 = (count * 9.0) / 10.0;
225   float l95 = (count * 95.0) / 100.0;
226   float l99 = (count * 99.0) / 100.0;
227 
228   int sum = 0;
229   for (int d = 0; d < MaxDimension; d++) {
230     if (buckets[d] == 0) continue;
231     for (int j = 0; j < MaxBuckets && sum < count; j++) {
232       // Empty buckets don't contribute to the answers.  Skip them.
233       if (buckets[d][j] == 0) continue;
234       sum += buckets[d][j];
235       // A word on indexing.  j is never zero in the following lines.  buckets[0][0] corresponds
236       // to a delay of 0us, which cannot happen.  buckets[n][0], for n > 0 overlaps a value in
237       // buckets[n-1], and the code would have stopped there.
238       if (sum >= l50 && pvalues.p50.nominal == 0) {
239         pvalues.p50.compute(sum, buckets[d][j], count, width[d], j * width[d]);
240       }
241       if (sum >= l90 && pvalues.p90.nominal == 0) {
242         pvalues.p90.compute(sum, buckets[d][j], count, width[d], j * width[d]);
243       }
244       if (sum >= l95 && pvalues.p95.nominal == 0) {
245         pvalues.p95.compute(sum, buckets[d][j], count, width[d], j * width[d]);
246       }
247       if (sum >= l99 && pvalues.p99.nominal == 0) {
248         pvalues.p99.compute(sum, buckets[d][j], count, width[d], j * width[d]);
249       }
250     }
251   }
252 }
253 
toString(ResourceTimer::Counter counter)254 char const *ResourceTimer::toString(ResourceTimer::Counter counter) {
255   switch (counter) {
256     case Counter::GetResourceValue:
257       return "GetResourceValue";
258     case Counter::RetrieveAttributes:
259       return "RetrieveAttributes";
260   };
261   return "Unknown";
262 }
263 
264 std::atomic<bool> ResourceTimer::enabled_(false);
265 std::atomic<ResourceTimer::GuardedTimer *> ResourceTimer::counter_(nullptr);
266 
267 const int ResourceTimer::Timer::range[] = { 100 * US, 1000 * US, 10*1000 * US, 100*1000 * US };
268 const int ResourceTimer::Timer::width[] = {   1 * US,   10 * US,     100 * US,     1000 * US };
269 
270 
271 }  // namespace android
272