| /* | |
| * Copyright (C) 2022 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #ifndef ANDROIDFW_RESOURCETIMER_H_ | |
| #define ANDROIDFW_RESOURCETIMER_H_ | |
| #include <time.h> | |
| #include <atomic> | |
| #include <vector> | |
| #include <utils/Mutex.h> | |
| #include <android-base/macros.h> | |
| #include <androidfw/Util.h> | |
| namespace android { | |
| // ResourceTimer captures the duration of short functions. Durations are accumulated in registers | |
| // and statistics are pulled back to the Java layer as needed. | |
| // To monitor an API, first add it to the Counter enumeration. Then, inside the API, create an | |
| // instance of ResourceTimer with the appropriate enumeral. The corresponding counter will be | |
| // updated when the ResourceTimer destructor is called, normally at the end of the enclosing block. | |
| class ResourceTimer { | |
| public: | |
| enum class Counter { | |
| GetResourceValue, | |
| RetrieveAttributes, | |
| LastCounter = RetrieveAttributes, | |
| }; | |
| static const int counterSize = static_cast<int>(Counter::LastCounter) + 1; | |
| static char const *toString(Counter); | |
| // Start a timer for the specified counter. | |
| ResourceTimer(Counter); | |
| // The block is exiting. If the timer is active, record it. | |
| ~ResourceTimer(); | |
| // This records the elapsed time and disables further recording. Use this if the containing | |
| // block includes extra processing that should not be included in the timer. The method is | |
| // destructive in that the timer is no longer valid and further calls to record() will be | |
| // ignored. | |
| void record(); | |
| // This cancels a timer. Elapsed time will neither be computed nor recorded. | |
| void cancel(); | |
| // A single timer contains the count of events and the cumulative time spent handling the | |
| // events. It also includes the smallest value seen and 10 largest values seen. Finally, it | |
| // includes a histogram of values that approximates a semi-log. | |
| // The timer can compute percentiles of recorded events. For example, the p50 value is a time | |
| // such that 50% of the readings are below the value and 50% are above the value. The | |
| // granularity in the readings means that a percentile cannot always be computed. In this case, | |
| // the percentile is reported as zero. (The simplest example is when there is a single | |
| // reading.) Even if the value can be computed, it will not be exact. Therefore, a percentile | |
| // is actually reported as two values: the lowest time at which it might be valid and the | |
| // highest time at which it might be valid. | |
| struct Timer { | |
| static const size_t MaxLargest = 5; | |
| // The construct zeros all the fields. The destructor releases memory allocated to the | |
| // buckets. | |
| Timer(); | |
| ~Timer(); | |
| // The following summary values are set to zero on a reset. All times are in ns. | |
| // The total number of events recorded. | |
| int count; | |
| // The total duration of events. | |
| int64_t total; | |
| // The smallest event duration seen. This is guaranteed to be non-zero if count is greater | |
| // than 0. | |
| int mintime; | |
| // The largest event duration seen. | |
| int maxtime; | |
| // The largest values seen. Element 0 is the largest value seen (and is the same as maxtime, | |
| // above). Element 1 is the next largest, and so on. If count is less than MaxLargest, | |
| // unused elements will be zero. | |
| int largest[MaxLargest]; | |
| // The p50 value is a time such that 50% of the readings are below that time and 50% of the | |
| // readings. | |
| // A single percentile is defined by the lowest value supported by the readings and the | |
| // highest value supported by the readings. | |
| struct Percentile { | |
| // The nominal time (in ns) of the percentile. The true percentile is guaranteed to be less | |
| // than or equal to this time. | |
| int nominal; | |
| // The actual percentile of the nominal time. | |
| int nominal_actual; | |
| // The time of the next lower bin. The true percentile is guaranteed to be greater than | |
| // this time. | |
| int floor; | |
| // The actual percentile of the floor time. | |
| int floor_actual; | |
| // Fill in a percentile given the cumulative to the bin, the count in the current bin, the | |
| // total count, the width of the bin, and the time of the bin. | |
| void compute(int cumulative, int current, int count, int width, int time); | |
| }; | |
| // The structure that holds the percentiles. | |
| struct { | |
| Percentile p50; | |
| Percentile p90; | |
| Percentile p95; | |
| Percentile p99; | |
| } pvalues; | |
| // Set all counters to zero. | |
| void reset(); | |
| // Record an event. The input time is in ns. | |
| void record(int); | |
| // Compute the percentiles. Percentiles are computed on demand, as the computation is too | |
| // expensive to be done inline. | |
| void compute(); | |
| // Copy one timer to another. If reset is true then the src is reset immediately after the | |
| // copy. The reset flag is exploited to make the copy faster. Any data in dst is lost. | |
| static void copy(Timer &dst, Timer &src, bool reset); | |
| private: | |
| // Free any buckets. | |
| void freeBuckets(); | |
| // Readings are placed in bins, which are orgzanized into decades. The decade 0 covers | |
| // [0,100) in steps of 1us. Decade 1 covers [0,1000) in steps of 10us. Decade 2 covers | |
| // [0,10000) in steps of 100us. And so on. | |
| // An event is placed in the first bin that can hold it. This means that events in the range | |
| // of [0,100) are placed in the first decade, events in the range of [0,1000) are placed in | |
| // the second decade, and so on. This also means that the first 10% of the bins are unused | |
| // in each decade after the first. | |
| // The design provides at least two significant digits across the range of [0,10000). | |
| static const size_t MaxDimension = 4; | |
| static const size_t MaxBuckets = 100; | |
| // The range of each dimension. The lower value is always zero. | |
| static const int range[MaxDimension]; | |
| // The width of each bin, by dimension | |
| static const int width[MaxDimension]; | |
| // A histogram of the values seen. Centuries are allocated as needed, to minimize the memory | |
| // impact. | |
| int *buckets[MaxDimension]; | |
| }; | |
| // Fetch one Timer. The function has a short-circuit behavior: if the count is zero then | |
| // destination count is set to zero and the function returns false. Otherwise, the destination | |
| // is a copy of the source and the function returns true. This behavior lowers the cost of | |
| // handling unused timers. | |
| static bool copy(int src, Timer &dst, bool reset); | |
| // Enable the timers. Timers are initially disabled. Enabling timers allocates memory for the | |
| // counters. Timers cannot be disabled. | |
| static void enable(); | |
| private: | |
| // An internal reset method. This does not take a lock. | |
| static void reset(); | |
| // Helper method to convert a counter into an enum. Presumably, this will be inlined into zero | |
| // actual cpu instructions. | |
| static inline std::vector<unsigned int>::size_type toIndex(Counter c) { | |
| return static_cast<std::vector<unsigned int>::size_type>(c); | |
| } | |
| // Every counter has an associated lock. The lock has been factored into a separate class to | |
| // keep the Timer class a POD. | |
| struct GuardedTimer { | |
| Mutex lock_; | |
| Timer timer_; | |
| }; | |
| // Scoped timer | |
| struct ScopedTimer { | |
| AutoMutex _l; | |
| Timer &t; | |
| ScopedTimer(GuardedTimer &g) : | |
| _l(g.lock_), t(g.timer_) { | |
| } | |
| Timer *operator->() { | |
| return &t; | |
| } | |
| Timer& operator*() { | |
| return t; | |
| } | |
| }; | |
| // An individual timer is active (or not), is tracking a specific API, and has a start time. | |
| // The api and the start time are undefined if the timer is not active. | |
| bool active_; | |
| Counter api_; | |
| struct timespec start_; | |
| // The global enable flag. This is initially false and may be set true by the java runtime. | |
| static std::atomic<bool> enabled_; | |
| // The global timers. The memory for the timers is not allocated until the timers are enabled. | |
| static std::atomic<GuardedTimer *> counter_; | |
| }; | |
| } // namespace android | |
| #endif /* ANDROIDFW_RESOURCETIMER_H_ */ |