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