blob: d403834640533038c2a1cb6c7cafe4be8df2d111 [file] [log] [blame]
Eric Holkf1a2c0e2020-09-29 11:13:55 -07001/*
2 * Copyright (C) 2020 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 ART_LIBARTBASE_BASE_METRICS_H_
18#define ART_LIBARTBASE_BASE_METRICS_H_
19
20#include <stdint.h>
21
22#include <array>
Eric Holkc4adf542020-10-02 13:46:28 -070023#include <atomic>
Eric Holkf1a2c0e2020-09-29 11:13:55 -070024#include <ostream>
25#include <string_view>
Eric Holkd02435d2020-09-29 11:16:24 -070026#include <vector>
Eric Holkf1a2c0e2020-09-29 11:13:55 -070027
Eric Holk1cd030f2020-09-30 11:42:34 -070028#include "android-base/logging.h"
Eric Holkf1a2c0e2020-09-29 11:13:55 -070029#include "base/time_utils.h"
30
31#pragma clang diagnostic push
32#pragma clang diagnostic error "-Wconversion"
33
34// COUNTER(counter_name)
35#define ART_COUNTERS(COUNTER) COUNTER(ClassVerificationTotalTime)
36// TODO: ClassVerificationTime serves as a mock for now. Implementation will come later.
37
Eric Holkd02435d2020-09-29 11:16:24 -070038// HISTOGRAM(counter_name, num_buckets, minimum_value, maximum_value)
39//
40// The num_buckets parameter affects memory usage for the histogram and data usage for exported
41// metrics. It is recommended to keep this below 16.
42//
43// The minimum_value and maximum_value parameters are needed because we need to know what range the
44// fixed number of buckets cover. We could keep track of the observed ranges and try to rescale the
45// buckets or allocate new buckets, but this would make incrementing them more expensive than just
46// some index arithmetic and an add.
47//
48// Values outside the range get clamped to the nearest bucket (basically, the two buckets on either
49// side are infinitely long). If we see those buckets being way taller than the others, it means we
50// should consider expanding the range.
51#define ART_HISTOGRAMS(HISTOGRAM) HISTOGRAM(JitMethodCompileTime, 15, 0, 1'000'000)
52// TODO: JitMethodCompileTime serves as a mock for now. Implementation will come later.
53
54// A lot of the metrics implementation code is generated by passing one-off macros into ART_COUNTERS
55// and ART_HISTOGRAMS. This means metrics.h and metrics.cc are very #define-heavy, which can be
56// challenging to read. The alternative was to require a lot of boilerplate code for each new metric
57// added, all of which would need to be rewritten if the metrics implementation changed. Using
58// macros lets us add new metrics by adding a single line to either ART_COUNTERS or ART_HISTOGRAMS,
59// and modifying the implementation only requires changing the implementation once, instead of once
60// per metric.
61
Eric Holkf1a2c0e2020-09-29 11:13:55 -070062namespace art {
63namespace metrics {
64
65/**
66 * An enumeration of all ART counters and histograms.
67 */
68enum class DatumId {
69#define ART_COUNTER(name) k##name,
70 ART_COUNTERS(ART_COUNTER)
71#undef ART_COUNTER
Eric Holkd02435d2020-09-29 11:16:24 -070072
73#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) k##name,
74 ART_HISTOGRAMS(ART_HISTOGRAM)
75#undef ART_HISTOGRAM
Eric Holkf1a2c0e2020-09-29 11:13:55 -070076};
77
78struct SessionData {
79 const uint64_t session_id;
80 const std::string_view package_name;
81 // TODO: compiler filter / dexopt state
82};
83
84// MetricsBackends are used by a metrics reporter to write metrics to some external location. For
85// example, a backend might write to logcat, or to a file, or to statsd.
86class MetricsBackend {
87 public:
88 virtual ~MetricsBackend() {}
89
90 protected:
91 // Begins an ART metrics session.
92 //
93 // This is called by the metrics reporter when the runtime is starting up. The session_data
94 // includes a session id which is used to correlate any metric reports with the same instance of
95 // the ART runtime. Additionally, session_data includes useful metadata such as the package name
96 // for this process.
97 virtual void BeginSession(const SessionData& session_data) = 0;
98
99 // Marks the end of a metrics session.
100 //
101 // The metrics reporter will call this when metrics reported ends (e.g. when the runtime is
102 // shutting down). No further metrics will be reported for this session. Note that EndSession is
103 // not guaranteed to be called, since clean shutdowns for the runtime are quite rare in practice.
104 virtual void EndSession() = 0;
105
106 // Called by the metrics reporter to give the current value of the counter with id counter_type.
107 //
108 // This will be called multiple times for each counter based on when the metrics reporter chooses
109 // to report metrics. For example, the metrics reporter may call this at shutdown or every N
110 // minutes. Counters are not reset in between invocations, so the value should represent the
111 // total count at the point this method is called.
112 virtual void ReportCounter(DatumId counter_type, uint64_t value) = 0;
113
Eric Holkd02435d2020-09-29 11:16:24 -0700114 // Called by the metrics reporter to report a histogram.
115 //
116 // This is called similarly to ReportCounter, but instead of receiving a single value, it receives
117 // a vector of the value in each bucket. Additionally, the function receives the lower and upper
118 // limit for the histogram. Note that these limits are the allowed limits, and not the observed
119 // range. Values below the lower limit will be counted in the first bucket, and values above the
120 // upper limit will be counted in the last bucket. Backends should store the minimum and maximum
121 // values to allow comparisons across module versions, since the minimum and maximum values may
122 // change over time.
123 virtual void ReportHistogram(DatumId histogram_type,
124 int64_t minimum_value,
125 int64_t maximum_value,
126 const std::vector<uint32_t>& buckets) = 0;
127
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700128 friend class ArtMetrics;
Eric Holkd02435d2020-09-29 11:16:24 -0700129 template <size_t num_buckets, int64_t low_value, int64_t high_value>
130 friend class MetricsHistogram;
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700131};
132
133class MetricsCounter {
134 public:
Eric Holk1cd030f2020-09-30 11:42:34 -0700135 using value_t = uint64_t;
136
Eric Holkd02435d2020-09-29 11:16:24 -0700137 explicit constexpr MetricsCounter(uint64_t value = 0) : value_{value} {
138 // Ensure we do not have any unnecessary data in this class.
139 static_assert(sizeof(*this) == sizeof(uint64_t));
140 }
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700141
Eric Holkc4adf542020-10-02 13:46:28 -0700142 void AddOne() { Add(1u); }
Eric Holk1cd030f2020-09-30 11:42:34 -0700143 void Add(value_t value) { value_.fetch_add(value, std::memory_order::memory_order_relaxed); }
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700144
Eric Holk1cd030f2020-09-30 11:42:34 -0700145 value_t Value() const { return value_.load(std::memory_order::memory_order_relaxed); }
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700146
147 private:
Eric Holk1cd030f2020-09-30 11:42:34 -0700148 std::atomic<value_t> value_;
149 static_assert(std::atomic<value_t>::is_always_lock_free);
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700150};
151
Eric Holkd02435d2020-09-29 11:16:24 -0700152template <size_t num_buckets_, int64_t minimum_value_, int64_t maximum_value_>
153class MetricsHistogram {
154 static_assert(num_buckets_ >= 1);
155 static_assert(minimum_value_ < maximum_value_);
156
157 public:
Eric Holk1cd030f2020-09-30 11:42:34 -0700158 using value_t = int64_t;
159
Eric Holkd02435d2020-09-29 11:16:24 -0700160 constexpr MetricsHistogram() : buckets_{} {
161 // Ensure we do not have any unnecessary data in this class.
162 static_assert(sizeof(*this) == sizeof(uint32_t) * num_buckets_);
163 }
164
165 void Add(int64_t value) {
166 const size_t i = FindBucketId(value);
Eric Holkc4adf542020-10-02 13:46:28 -0700167 buckets_[i].fetch_add(1u, std::memory_order::memory_order_relaxed);
Eric Holkd02435d2020-09-29 11:16:24 -0700168 }
169
170 protected:
171 std::vector<uint32_t> GetBuckets() const {
Eric Holkc4adf542020-10-02 13:46:28 -0700172 // The loads from buckets_ will all be memory_order_seq_cst, which means they will be acquire
173 // loads. This is a stricter memory order than is needed, but this should not be a
174 // performance-critical section of code.
Eric Holkd02435d2020-09-29 11:16:24 -0700175 return std::vector<uint32_t>{buckets_.begin(), buckets_.end()};
176 }
177
178 private:
179 inline constexpr size_t FindBucketId(int64_t value) const {
180 // Values below the minimum are clamped into the first bucket.
181 if (value <= minimum_value_) {
182 return 0;
183 }
184 // Values above the maximum are clamped into the last bucket.
185 if (value >= maximum_value_) {
186 return num_buckets_ - 1;
187 }
188 // Otherise, linearly interpolate the value into the right bucket
189 constexpr size_t bucket_width = maximum_value_ - minimum_value_;
190 return static_cast<size_t>(value - minimum_value_) * num_buckets_ / bucket_width;
191 }
192
Eric Holkc4adf542020-10-02 13:46:28 -0700193 std::array<std::atomic<uint32_t>, num_buckets_> buckets_;
Eric Holkd02435d2020-09-29 11:16:24 -0700194
195 friend class ArtMetrics;
Eric Holkc4adf542020-10-02 13:46:28 -0700196 static_assert(std::atomic<uint32_t>::is_always_lock_free);
Eric Holkd02435d2020-09-29 11:16:24 -0700197};
198
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700199/**
Eric Holk1cd030f2020-09-30 11:42:34 -0700200 * AutoTimer simplifies time-based metrics collection.
201 *
202 * Several modes are supported. In the default case, the timer starts immediately and stops when it
203 * goes out of scope. Example:
204 *
205 * {
206 * AutoTimer timer{metric};
207 * DoStuff();
208 * // timer stops and updates metric automatically here.
209 * }
210 *
211 * You can also stop the timer early:
212 *
213 * timer.Stop();
214 *
215 * Finally, you can choose to not automatically start the timer at the beginning by passing false as
216 * the second argument to the constructor:
217 *
218 * AutoTimer timer{metric, false};
219 * DoNotTimeThis();
220 * timer.Start();
221 * TimeThis();
222 *
223 * Manually started timers will still automatically stop in the destructor, but they can be manually
224 * stopped as well.
225 *
226 * Note that AutoTimer makes calls to MicroTime(), so this may not be suitable on critical paths, or
227 * in cases where the counter needs to be started and stopped on different threads.
228 */
229template <typename Metric>
230class AutoTimer {
231 public:
232 explicit AutoTimer(Metric* metric, bool autostart = true)
233 : running_{false}, start_time_microseconds_{}, metric_{metric} {
234 if (autostart) {
235 Start();
236 }
237 }
238
239 ~AutoTimer() {
240 if (running_) {
241 Stop();
242 }
243 }
244
245 void Start() {
246 DCHECK(!running_);
247 running_ = true;
248 start_time_microseconds_ = MicroTime();
249 }
250
251 void Stop() {
252 DCHECK(running_);
253 uint64_t stop_time_microseconds = MicroTime();
254 running_ = false;
255
256 metric_->Add(
257 static_cast<typename Metric::value_t>(stop_time_microseconds - start_time_microseconds_));
258 }
259
260 private:
261 bool running_;
262 uint64_t start_time_microseconds_;
263 Metric* metric_;
264};
265
266/**
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700267 * This struct contains all of the metrics that ART reports.
268 */
269class ArtMetrics {
270 public:
271 ArtMetrics();
272
273 void ReportAllMetrics(MetricsBackend* backend) const;
274
Eric Holkd02435d2020-09-29 11:16:24 -0700275#define ART_COUNTER(name) \
276 MetricsCounter* name() { return &name##_; } \
277 const MetricsCounter* name() const { return &name##_; }
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700278 ART_COUNTERS(ART_COUNTER)
279#undef ART_COUNTER
280
Eric Holkd02435d2020-09-29 11:16:24 -0700281#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) \
282 MetricsHistogram<num_buckets, low_value, high_value>* name() { return &name##_; } \
283 const MetricsHistogram<num_buckets, low_value, high_value>* name() const { return &name##_; }
284 ART_HISTOGRAMS(ART_HISTOGRAM)
285#undef ART_HISTOGRAM
286
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700287 private:
288 // This field is only included to allow us expand the ART_COUNTERS and ART_HISTOGRAMS macro in
289 // the initializer list in ArtMetrics::ArtMetrics. See metrics.cc for how it's used.
290 //
291 // It's declared as a zero-length array so it has no runtime space impact.
292#pragma clang diagnostic push
293#pragma clang diagnostic ignored "-Wunused-private-field"
294 int unused_[0];
295#pragma clang diagnostic pop // -Wunused-private-field
Eric Holkd02435d2020-09-29 11:16:24 -0700296
297#define ART_COUNTER(name) MetricsCounter name##_;
298 ART_COUNTERS(ART_COUNTER)
299#undef ART_COUNTER
300
301#define ART_HISTOGRAM(name, num_buckets, low_value, high_value) \
302 MetricsHistogram<num_buckets, low_value, high_value> name##_;
303 ART_HISTOGRAMS(ART_HISTOGRAM)
304#undef ART_HISTOGRAM
Eric Holkf1a2c0e2020-09-29 11:13:55 -0700305};
306
307// Returns a human readable name for the given DatumId.
308std::string DatumName(DatumId datum);
309
310} // namespace metrics
311} // namespace art
312
313#pragma clang diagnostic pop // -Wconversion
314
315#endif // ART_LIBARTBASE_BASE_METRICS_H_