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