1 /*
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_METRICS_H_
18 #define ART_LIBARTBASE_BASE_METRICS_METRICS_H_
19
20 #include <stdint.h>
21
22 #include <array>
23 #include <atomic>
24 #include <optional>
25 #include <sstream>
26 #include <string_view>
27 #include <thread>
28 #include <vector>
29
30 #include "android-base/logging.h"
31 #include "base/bit_utils.h"
32 #include "base/macros.h"
33 #include "base/time_utils.h"
34 #include "jni.h"
35 #include "tinyxml2.h"
36
37 #pragma clang diagnostic push
38 #pragma clang diagnostic error "-Wconversion"
39
40 // See README.md in this directory for how to define metrics.
41
42 // Metrics reported as Event Metrics.
43 #define ART_EVENT_METRICS(METRIC) \
44 METRIC(ClassLoadingTotalTime, MetricsCounter) \
45 METRIC(ClassVerificationTotalTime, MetricsCounter) \
46 METRIC(ClassVerificationCount, MetricsCounter) \
47 METRIC(WorldStopTimeDuringGCAvg, MetricsAverage) \
48 METRIC(YoungGcCount, MetricsCounter) \
49 METRIC(FullGcCount, MetricsCounter) \
50 METRIC(TotalBytesAllocated, MetricsCounter) \
51 METRIC(TotalGcCollectionTime, MetricsCounter) \
52 METRIC(YoungGcThroughputAvg, MetricsAverage) \
53 METRIC(FullGcThroughputAvg, MetricsAverage) \
54 METRIC(YoungGcTracingThroughputAvg, MetricsAverage) \
55 METRIC(FullGcTracingThroughputAvg, MetricsAverage) \
56 METRIC(JitMethodCompileTotalTime, MetricsCounter) \
57 METRIC(JitMethodCompileCount, MetricsCounter) \
58 METRIC(YoungGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
59 METRIC(FullGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
60 METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 10'000) \
61 METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 10'000) \
62 METRIC(YoungGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
63 METRIC(FullGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
64 METRIC(GcWorldStopTime, MetricsCounter) \
65 METRIC(GcWorldStopCount, MetricsCounter) \
66 METRIC(YoungGcScannedBytes, MetricsCounter) \
67 METRIC(YoungGcFreedBytes, MetricsCounter) \
68 METRIC(YoungGcDuration, MetricsCounter) \
69 METRIC(FullGcScannedBytes, MetricsCounter) \
70 METRIC(FullGcFreedBytes, MetricsCounter) \
71 METRIC(FullGcDuration, MetricsCounter)
72
73 // Increasing counter metrics, reported as Value Metrics in delta increments.
74 #define ART_VALUE_METRICS(METRIC) \
75 METRIC(GcWorldStopTimeDelta, MetricsDeltaCounter) \
76 METRIC(GcWorldStopCountDelta, MetricsDeltaCounter) \
77 METRIC(YoungGcScannedBytesDelta, MetricsDeltaCounter) \
78 METRIC(YoungGcFreedBytesDelta, MetricsDeltaCounter) \
79 METRIC(YoungGcDurationDelta, MetricsDeltaCounter) \
80 METRIC(FullGcScannedBytesDelta, MetricsDeltaCounter) \
81 METRIC(FullGcFreedBytesDelta, MetricsDeltaCounter) \
82 METRIC(FullGcDurationDelta, MetricsDeltaCounter) \
83 METRIC(JitMethodCompileTotalTimeDelta, MetricsDeltaCounter) \
84 METRIC(JitMethodCompileCountDelta, MetricsDeltaCounter) \
85 METRIC(ClassVerificationTotalTimeDelta, MetricsDeltaCounter) \
86 METRIC(ClassVerificationCountDelta, MetricsDeltaCounter) \
87 METRIC(ClassLoadingTotalTimeDelta, MetricsDeltaCounter) \
88 METRIC(TotalBytesAllocatedDelta, MetricsDeltaCounter) \
89 METRIC(TotalGcCollectionTimeDelta, MetricsDeltaCounter) \
90 METRIC(YoungGcCountDelta, MetricsDeltaCounter) \
91 METRIC(FullGcCountDelta, MetricsDeltaCounter) \
92 METRIC(TimeElapsedDelta, MetricsDeltaCounter) \
93 METRIC(AppSlowPathDuringYoungGcDurationDelta, MetricsDeltaCounter) \
94 METRIC(AppSlowPathDuringFullGcDurationDelta, MetricsDeltaCounter)
95
96 #define ART_METRICS(METRIC) \
97 ART_EVENT_METRICS(METRIC) \
98 ART_VALUE_METRICS(METRIC)
99
100 // A lot of the metrics implementation code is generated by passing one-off macros into ART_COUNTERS
101 // and ART_HISTOGRAMS. This means metrics.h and metrics.cc are very #define-heavy, which can be
102 // challenging to read. The alternative was to require a lot of boilerplate code for each new metric
103 // added, all of which would need to be rewritten if the metrics implementation changed. Using
104 // macros lets us add new metrics by adding a single line to either ART_COUNTERS or ART_HISTOGRAMS,
105 // and modifying the implementation only requires changing the implementation once, instead of once
106 // per metric.
107
108 namespace art {
109
110 class Runtime;
111 struct RuntimeArgumentMap;
112
113 [[maybe_unused]] static jlong VMRuntime_getFullGcCount(JNIEnv* env, jclass klass);
114
115 namespace metrics {
116 template <typename value_t>
117 class MetricsBase;
118 } // namespace metrics
119
120 namespace gc {
121 class HeapTest_GCMetrics_Test;
122 } // namespace gc
123
124 namespace metrics {
125
126 /**
127 * An enumeration of all ART counters and histograms.
128 */
129 enum class DatumId {
130 #define METRIC(name, type, ...) k##name,
131 ART_METRICS(METRIC)
132 #undef METRIC
133 };
134
135 // Names come from PackageManagerServiceCompilerMapping.java
136 #define REASON_NAME_LIST(V) \
137 V(kError, "error") \
138 V(kUnknown, "unknown") \
139 V(kFirstBoot, "first-boot") \
140 V(kBootAfterOTA, "boot-after-ota") \
141 V(kPostBoot, "post-boot") \
142 V(kInstall, "install") \
143 V(kInstallFast, "install-fast") \
144 V(kInstallBulk, "install-bulk") \
145 V(kInstallBulkSecondary, "install-bulk-secondary") \
146 V(kInstallBulkDowngraded, "install-bulk-downgraded") \
147 V(kInstallBulkSecondaryDowngraded, "install-bulk-secondary-downgraded") \
148 V(kBgDexopt, "bg-dexopt") \
149 V(kABOTA, "ab-ota") \
150 V(kInactive, "inactive") \
151 V(kShared, "shared") \
152 V(kInstallWithDexMetadata, "install-with-dex-metadata") \
153 V(kPrebuilt, "prebuilt") \
154 V(kCmdLine, "cmdline") \
155 V(kVdex, "vdex") \
156 V(kBootAfterMainlineUpdate, "boot-after-mainline-update")
157
158 // We log compilation reasons as part of the metadata we report. Since elsewhere compilation reasons
159 // are specified as a string, we define them as an enum here which indicates the reasons that we
160 // support.
161 enum class CompilationReason {
162 #define REASON(kind, name) kind,
163 REASON_NAME_LIST(REASON)
164 #undef REASON
165 };
166
167 #define REASON_NAME(kind, kind_name) \
168 case CompilationReason::kind: return kind_name;
169 #define REASON_FROM_NAME(kind, kind_name) \
170 if (name == (kind_name)) { return CompilationReason::kind; }
171
CompilationReasonName(CompilationReason reason)172 constexpr const char* CompilationReasonName(CompilationReason reason) {
173 switch (reason) {
174 REASON_NAME_LIST(REASON_NAME)
175 }
176 }
177
CompilationReasonFromName(std::string_view name)178 constexpr CompilationReason CompilationReasonFromName(std::string_view name) {
179 REASON_NAME_LIST(REASON_FROM_NAME)
180 return CompilationReason::kError;
181 }
182
183 #undef REASON_NAME
184 #undef REASON_FROM_NAME
185
186 #define COMPILER_FILTER_REPORTING_LIST(V) \
187 V(kError, "error") /* Error (invalid value) condition */ \
188 V(kUnknown, "unknown") /* Unknown (not set) condition */ \
189 V(kAssumeVerified, "assume-verified") /* Standard compiler filters */ \
190 V(kExtract, "extract") \
191 V(kVerify, "verify") \
192 V(kSpaceProfile, "space-profile") \
193 V(kSpace, "space") \
194 V(kSpeedProfile, "speed-profile") \
195 V(kSpeed, "speed") \
196 V(kEverythingProfile, "everything-profile") \
197 V(kEverything, "everything") \
198 V(kRunFromApk, "run-from-apk") /* Augmented compiler filters as produces by OatFileAssistant#GetOptimizationStatus */ \
199 V(kRunFromApkFallback, "run-from-apk-fallback")
200
201 // Augmented compiler filter enum, used in the reporting infra.
202 enum class CompilerFilterReporting {
203 #define FILTER(kind, name) kind,
204 COMPILER_FILTER_REPORTING_LIST(FILTER)
205 #undef FILTER
206 };
207
208 #define FILTER_NAME(kind, kind_name) \
209 case CompilerFilterReporting::kind: return kind_name;
210 #define FILTER_FROM_NAME(kind, kind_name) \
211 if (name == (kind_name)) { return CompilerFilterReporting::kind; }
212
CompilerFilterReportingName(CompilerFilterReporting filter)213 constexpr const char* CompilerFilterReportingName(CompilerFilterReporting filter) {
214 switch (filter) {
215 COMPILER_FILTER_REPORTING_LIST(FILTER_NAME)
216 }
217 }
218
CompilerFilterReportingFromName(std::string_view name)219 constexpr CompilerFilterReporting CompilerFilterReportingFromName(std::string_view name) {
220 COMPILER_FILTER_REPORTING_LIST(FILTER_FROM_NAME)
221 return CompilerFilterReporting::kError;
222 }
223
224 #undef FILTER_NAME
225 #undef FILTER_FROM_NAME
226
227 // SessionData contains metadata about a metrics session (basically the lifetime of an ART process).
228 // This information should not change for the lifetime of the session.
229 struct SessionData {
230 static SessionData CreateDefault();
231
232 static constexpr int64_t kInvalidSessionId = -1;
233 static constexpr int32_t kInvalidUserId = -1;
234
235 int64_t session_id;
236 int32_t uid;
237 CompilationReason compilation_reason;
238 CompilerFilterReporting compiler_filter;
239 };
240
241 // MetricsBackends are used by a metrics reporter to write metrics to some external location. For
242 // example, a backend might write to logcat, or to a file, or to statsd.
243 class MetricsBackend {
244 public:
~MetricsBackend()245 virtual ~MetricsBackend() {}
246
247 // Begins an ART metrics session.
248 //
249 // This is called by the metrics reporter when the runtime is starting up. The session_data
250 // includes a session id which is used to correlate any metric reports with the same instance of
251 // the ART runtime. Additionally, session_data includes useful metadata such as the package name
252 // for this process.
253 //
254 // It may also be called whenever there is an update to the session metadata (e.g. optimization
255 // state).
256 virtual void BeginOrUpdateSession(const SessionData& session_data) = 0;
257
258 protected:
259 // Called by the metrics reporter to indicate that a new metrics report is starting.
260 virtual void BeginReport(uint64_t timestamp_since_start_ms) = 0;
261
262 // Called by the metrics reporter to give the current value of the counter with id counter_type.
263 //
264 // This will be called multiple times for each counter based on when the metrics reporter chooses
265 // to report metrics. For example, the metrics reporter may call this at shutdown or every N
266 // minutes. Counters are not reset in between invocations, so the value should represent the
267 // total count at the point this method is called.
268 virtual void ReportCounter(DatumId counter_type, uint64_t value) = 0;
269
270 // Called by the metrics reporter to report a histogram.
271 //
272 // This is called similarly to ReportCounter, but instead of receiving a single value, it receives
273 // a vector of the value in each bucket. Additionally, the function receives the lower and upper
274 // limit for the histogram. Note that these limits are the allowed limits, and not the observed
275 // range. Values below the lower limit will be counted in the first bucket, and values above the
276 // upper limit will be counted in the last bucket. Backends should store the minimum and maximum
277 // values to allow comparisons across module versions, since the minimum and maximum values may
278 // change over time.
279 virtual void ReportHistogram(DatumId histogram_type,
280 int64_t minimum_value,
281 int64_t maximum_value,
282 const std::vector<uint32_t>& buckets) = 0;
283
284 // Called by the metrics reporter to indicate that the current metrics report is complete.
285 virtual void EndReport() = 0;
286
287 template <DatumId counter_type, typename T>
288 friend class MetricsCounter;
289 template <DatumId counter_type, typename T>
290 friend class MetricsDeltaCounter;
291 template <DatumId histogram_type, size_t num_buckets, int64_t low_value, int64_t high_value>
292 friend class MetricsHistogram;
293 template <DatumId datum_id, typename T, const T& AccumulatorFunction(const T&, const T&)>
294 friend class MetricsAccumulator;
295 template <DatumId datum_id, typename T>
296 friend class MetricsAverage;
297 friend class ArtMetrics;
298 };
299
300 template <typename value_t>
301 class MetricsBase {
302 public:
303 virtual void Add(value_t value) = 0;
~MetricsBase()304 virtual ~MetricsBase() { }
305
306 private:
307 // Is the metric "null", i.e. never updated or freshly reset?
308 // Used for testing purpose only.
309 virtual bool IsNull() const = 0;
310
311 ART_FRIEND_TEST(gc::HeapTest, GCMetrics);
312 };
313
314 template <DatumId counter_type, typename T = uint64_t>
315 class MetricsCounter : public MetricsBase<T> {
316 public:
317 using value_t = T;
318 explicit constexpr MetricsCounter(uint64_t value = 0) : value_{value} {
319 // Ensure we do not have any unnecessary data in this class.
320 // Adding intptr_t to accommodate vtable, and rounding up to incorporate
321 // padding.
322 static_assert(RoundUp(sizeof(*this), sizeof(uint64_t))
323 == RoundUp(sizeof(intptr_t) + sizeof(value_t), sizeof(uint64_t)));
324 }
325
AddOne()326 void AddOne() { Add(1u); }
Add(value_t value)327 void Add(value_t value) override {
328 value_.fetch_add(value, std::memory_order_relaxed);
329 }
330
Report(const std::vector<MetricsBackend * > & backends)331 void Report(const std::vector<MetricsBackend*>& backends) const {
332 for (MetricsBackend* backend : backends) {
333 backend->ReportCounter(counter_type, Value());
334 }
335 }
336
337 protected:
Reset()338 void Reset() { value_ = 0; }
Value()339 value_t Value() const { return value_.load(std::memory_order_relaxed); }
340
341 private:
IsNull()342 bool IsNull() const override { return Value() == 0; }
343
344 std::atomic<value_t> value_;
345 static_assert(std::atomic<value_t>::is_always_lock_free);
346
347 friend class ArtMetrics;
348 friend jlong art::VMRuntime_getFullGcCount(JNIEnv* env, jclass klass);
349 };
350
351 template <DatumId datum_id, typename T = uint64_t>
352 class MetricsAverage final : public MetricsCounter<datum_id, T> {
353 public:
354 using value_t = T;
355 using count_t = T;
356 explicit constexpr MetricsAverage(uint64_t value = 0, uint64_t count = 0) :
357 MetricsCounter<datum_id, value_t>(value), count_(count) {
358 // Ensure we do not have any unnecessary data in this class.
359 // Adding intptr_t to accommodate vtable, and rounding up to incorporate
360 // padding.
361 static_assert(RoundUp(sizeof(*this), sizeof(uint64_t))
362 == RoundUp(sizeof(intptr_t) + sizeof(value_t) + sizeof(count_t),
363 sizeof(uint64_t)));
364 }
365
366 // We use release memory-order here and then acquire in Report() to ensure
367 // that at least the non-racy reads/writes to this metric are consistent. This
368 // doesn't guarantee the atomicity of the change to both fields, but that
369 // may not be desired because:
370 // 1. The metric eventually becomes consistent.
371 // 2. For sufficiently large count_, a few data points which are off shouldn't
372 // make a huge difference to the reporter.
Add(value_t value)373 void Add(value_t value) override {
374 MetricsCounter<datum_id, value_t>::Add(value);
375 count_.fetch_add(1, std::memory_order_release);
376 }
377
Report(const std::vector<MetricsBackend * > & backends)378 void Report(const std::vector<MetricsBackend*>& backends) const {
379 count_t value = MetricsCounter<datum_id, value_t>::Value();
380 count_t count = count_.load(std::memory_order_acquire);
381 // Avoid divide-by-0.
382 count_t average_value = count != 0 ? value / count : 0;
383 for (MetricsBackend* backend : backends) {
384 backend->ReportCounter(datum_id, average_value);
385 }
386 }
387
388 protected:
Reset()389 void Reset() {
390 count_ = 0;
391 MetricsCounter<datum_id, value_t>::Reset();
392 }
393
394 private:
Count()395 count_t Count() const { return count_.load(std::memory_order_relaxed); }
396
IsNull()397 bool IsNull() const override { return Count() == 0; }
398
399 std::atomic<count_t> count_;
400 static_assert(std::atomic<count_t>::is_always_lock_free);
401
402 friend class ArtMetrics;
403 };
404
405 template <DatumId datum_id, typename T = uint64_t>
406 class MetricsDeltaCounter : public MetricsBase<T> {
407 public:
408 using value_t = T;
409
410 explicit constexpr MetricsDeltaCounter(uint64_t value = 0) : value_{value} {
411 // Ensure we do not have any unnecessary data in this class.
412 // Adding intptr_t to accommodate vtable, and rounding up to incorporate
413 // padding.
414 static_assert(RoundUp(sizeof(*this), sizeof(uint64_t)) ==
415 RoundUp(sizeof(intptr_t) + sizeof(value_t), sizeof(uint64_t)));
416 }
417
Add(value_t value)418 void Add(value_t value) override {
419 value_.fetch_add(value, std::memory_order_relaxed);
420 }
AddOne()421 void AddOne() { Add(1u); }
422
ReportAndReset(const std::vector<MetricsBackend * > & backends)423 void ReportAndReset(const std::vector<MetricsBackend*>& backends) {
424 value_t value = value_.exchange(0, std::memory_order_relaxed);
425 for (MetricsBackend* backend : backends) {
426 backend->ReportCounter(datum_id, value);
427 }
428 }
429
Reset()430 void Reset() { value_ = 0; }
431
432 private:
Value()433 value_t Value() const { return value_.load(std::memory_order_relaxed); }
434
IsNull()435 bool IsNull() const override { return Value() == 0; }
436
437 std::atomic<value_t> value_;
438 static_assert(std::atomic<value_t>::is_always_lock_free);
439
440 friend class ArtMetrics;
441 };
442
443 template <DatumId histogram_type_,
444 size_t num_buckets_,
445 int64_t minimum_value_,
446 int64_t maximum_value_>
447 class MetricsHistogram final : public MetricsBase<int64_t> {
448 static_assert(num_buckets_ >= 1);
449 static_assert(minimum_value_ < maximum_value_);
450
451 public:
452 using value_t = uint32_t;
453
MetricsHistogram()454 constexpr MetricsHistogram() : buckets_{} {
455 // Ensure we do not have any unnecessary data in this class.
456 // Adding intptr_t to accommodate vtable, and rounding up to incorporate
457 // padding.
458 static_assert(RoundUp(sizeof(*this), sizeof(uint64_t))
459 == RoundUp(sizeof(intptr_t) + sizeof(value_t) * num_buckets_, sizeof(uint64_t)));
460 }
461
Add(int64_t value)462 void Add(int64_t value) override {
463 const size_t i = FindBucketId(value);
464 buckets_[i].fetch_add(1u, std::memory_order_relaxed);
465 }
466
Report(const std::vector<MetricsBackend * > & backends)467 void Report(const std::vector<MetricsBackend*>& backends) const {
468 for (MetricsBackend* backend : backends) {
469 backend->ReportHistogram(histogram_type_, minimum_value_, maximum_value_, GetBuckets());
470 }
471 }
472
473 protected:
Reset()474 void Reset() {
475 for (auto& bucket : buckets_) {
476 bucket = 0;
477 }
478 }
479
480 private:
FindBucketId(int64_t value)481 inline constexpr size_t FindBucketId(int64_t value) const {
482 // Values below the minimum are clamped into the first bucket.
483 if (value <= minimum_value_) {
484 return 0;
485 }
486 // Values above the maximum are clamped into the last bucket.
487 if (value >= maximum_value_) {
488 return num_buckets_ - 1;
489 }
490 // Otherise, linearly interpolate the value into the right bucket
491 constexpr size_t bucket_width = maximum_value_ - minimum_value_;
492 return static_cast<size_t>(value - minimum_value_) * num_buckets_ / bucket_width;
493 }
494
GetBuckets()495 std::vector<value_t> GetBuckets() const {
496 // The loads from buckets_ will all be memory_order_seq_cst, which means they will be acquire
497 // loads. This is a stricter memory order than is needed, but this should not be a
498 // performance-critical section of code.
499 return std::vector<value_t>{buckets_.begin(), buckets_.end()};
500 }
501
IsNull()502 bool IsNull() const override {
503 std::vector<value_t> buckets = GetBuckets();
504 return std::all_of(buckets.cbegin(), buckets.cend(), [](value_t i) { return i == 0; });
505 }
506
507 std::array<std::atomic<value_t>, num_buckets_> buckets_;
508 static_assert(std::atomic<value_t>::is_always_lock_free);
509
510 friend class ArtMetrics;
511 };
512
513 template <DatumId datum_id, typename T, const T& AccumulatorFunction(const T&, const T&)>
514 class MetricsAccumulator final : MetricsBase<T> {
515 public:
516 explicit constexpr MetricsAccumulator(T value = 0) : value_{value} {
517 // Ensure we do not have any unnecessary data in this class.
518 // Adding intptr_t to accommodate vtable, and rounding up to incorporate
519 // padding.
520 static_assert(RoundUp(sizeof(*this), sizeof(uint64_t)) ==
521 RoundUp(sizeof(intptr_t) + sizeof(T), sizeof(uint64_t)));
522 }
523
Add(T value)524 void Add(T value) override {
525 T current = value_.load(std::memory_order_relaxed);
526 T new_value;
527 do {
528 new_value = AccumulatorFunction(current, value);
529 // If the value didn't change, don't bother storing it.
530 if (current == new_value) {
531 break;
532 }
533 } while (!value_.compare_exchange_weak(
534 current, new_value, std::memory_order_relaxed));
535 }
536
537 // Report the metric as a counter, since this has only a single value.
Report(MetricsBackend * backend)538 void Report(MetricsBackend* backend) const {
539 backend->ReportCounter(datum_id, static_cast<uint64_t>(Value()));
540 }
541
542 protected:
Reset()543 void Reset() {
544 value_ = 0;
545 }
546
547 private:
Value()548 T Value() const { return value_.load(std::memory_order_relaxed); }
549
IsNull()550 bool IsNull() const override { return Value() == 0; }
551
552 std::atomic<T> value_;
553
554 friend class ArtMetrics;
555 };
556
557 // Base class for formatting metrics into different formats
558 // (human-readable text, JSON, etc.)
559 class MetricsFormatter {
560 public:
561 virtual ~MetricsFormatter() = default;
562
563 virtual void FormatBeginReport(uint64_t timestamp_since_start_ms,
564 const std::optional<SessionData>& session_data) = 0;
565 virtual void FormatEndReport() = 0;
566 virtual void FormatReportCounter(DatumId counter_type, uint64_t value) = 0;
567 virtual void FormatReportHistogram(DatumId histogram_type,
568 int64_t low_value,
569 int64_t high_value,
570 const std::vector<uint32_t>& buckets) = 0;
571 virtual std::string GetAndResetBuffer() = 0;
572
573 protected:
574 const std::string version = "1.0";
575 };
576
577 // Formatter outputting metrics in human-readable text format
578 class TextFormatter : public MetricsFormatter {
579 public:
580 TextFormatter() = default;
581
582 void FormatBeginReport(uint64_t timestamp_millis,
583 const std::optional<SessionData>& session_data) override;
584
585 void FormatReportCounter(DatumId counter_type, uint64_t value) override;
586
587 void FormatReportHistogram(DatumId histogram_type,
588 int64_t low_value,
589 int64_t high_value,
590 const std::vector<uint32_t>& buckets) override;
591
592 void FormatEndReport() override;
593
594 std::string GetAndResetBuffer() override;
595
596 private:
597 std::ostringstream os_;
598 };
599
600 // Formatter outputting metrics in XML format
601 class XmlFormatter : public MetricsFormatter {
602 public:
603 XmlFormatter() = default;
604
605 void FormatBeginReport(uint64_t timestamp_millis,
606 const std::optional<SessionData>& session_data) override;
607
608 void FormatReportCounter(DatumId counter_type, uint64_t value) override;
609
610 void FormatReportHistogram(DatumId histogram_type,
611 int64_t low_value,
612 int64_t high_value,
613 const std::vector<uint32_t>& buckets) override;
614
615 void FormatEndReport() override;
616
617 std::string GetAndResetBuffer() override;
618
619 private:
620 tinyxml2::XMLDocument document_;
621 };
622
623 // A backend that writes metrics to a string.
624 // The format of the metrics' output is delegated
625 // to the MetricsFormatter class.
626 //
627 // This is used as a base for LogBackend and FileBackend.
628 class StringBackend : public MetricsBackend {
629 public:
630 explicit StringBackend(std::unique_ptr<MetricsFormatter> formatter);
631
632 void BeginOrUpdateSession(const SessionData& session_data) override;
633
634 void BeginReport(uint64_t timestamp_millis) override;
635
636 void ReportCounter(DatumId counter_type, uint64_t value) override;
637
638 void ReportHistogram(DatumId histogram_type,
639 int64_t low_value,
640 int64_t high_value,
641 const std::vector<uint32_t>& buckets) override;
642
643 void EndReport() override;
644
645 std::string GetAndResetBuffer();
646
647 private:
648 std::unique_ptr<MetricsFormatter> formatter_;
649 std::optional<SessionData> session_data_;
650 };
651
652 // A backend that writes metrics in human-readable format to the log (i.e. logcat).
653 class LogBackend : public StringBackend {
654 public:
655 explicit LogBackend(std::unique_ptr<MetricsFormatter> formatter,
656 android::base::LogSeverity level);
657
658 void BeginReport(uint64_t timestamp_millis) override;
659 void EndReport() override;
660
661 private:
662 android::base::LogSeverity level_;
663 };
664
665 // A backend that writes metrics to a file.
666 class FileBackend : public StringBackend {
667 public:
668 explicit FileBackend(std::unique_ptr<MetricsFormatter> formatter,
669 const std::string& filename);
670
671 void BeginReport(uint64_t timestamp_millis) override;
672 void EndReport() override;
673
674 private:
675 std::string filename_;
676 };
677
678 /**
679 * AutoTimer simplifies time-based metrics collection.
680 *
681 * Several modes are supported. In the default case, the timer starts immediately and stops when it
682 * goes out of scope. Example:
683 *
684 * {
685 * AutoTimer timer{metric};
686 * DoStuff();
687 * // timer stops and updates metric automatically here.
688 * }
689 *
690 * You can also stop the timer early:
691 *
692 * timer.Stop();
693 *
694 * Finally, you can choose to not automatically start the timer at the beginning by passing false as
695 * the second argument to the constructor:
696 *
697 * AutoTimer timer{metric, false};
698 * DoNotTimeThis();
699 * timer.Start();
700 * TimeThis();
701 *
702 * Manually started timers will still automatically stop in the destructor, but they can be manually
703 * stopped as well.
704 *
705 * Note that AutoTimer makes calls to MicroTime(), so this may not be suitable on critical paths, or
706 * in cases where the counter needs to be started and stopped on different threads.
707 */
708 template <typename Metric>
709 class AutoTimer {
710 public:
711 explicit AutoTimer(Metric* metric, bool autostart = true)
712 : running_{false}, start_time_microseconds_{}, metric_{metric} {
713 if (autostart) {
714 Start();
715 }
716 }
717
~AutoTimer()718 ~AutoTimer() {
719 if (running_) {
720 Stop();
721 }
722 }
723
Start()724 void Start() {
725 DCHECK(!running_);
726 running_ = true;
727 start_time_microseconds_ = MicroTime();
728 }
729
730 // Stops a running timer. Returns the time elapsed since starting the timer in microseconds.
Stop()731 uint64_t Stop() {
732 DCHECK(running_);
733 uint64_t stop_time_microseconds = MicroTime();
734 running_ = false;
735
736 uint64_t elapsed_time = stop_time_microseconds - start_time_microseconds_;
737 metric_->Add(static_cast<typename Metric::value_t>(elapsed_time));
738 return elapsed_time;
739 }
740
741 private:
742 bool running_;
743 uint64_t start_time_microseconds_;
744 Metric* metric_;
745 };
746
747 /**
748 * This struct contains all of the metrics that ART reports.
749 */
750 class ArtMetrics {
751 public:
752 ArtMetrics();
753
754 void ReportAllMetricsAndResetValueMetrics(const std::vector<MetricsBackend*>& backends);
755 void DumpForSigQuit(std::ostream& os);
756
757 // Resets all metrics to their initial value. This is intended to be used after forking from the
758 // zygote so we don't attribute parent values to the child process.
759 void Reset();
760
761 #define METRIC_ACCESSORS(name, Kind, ...) \
762 Kind<DatumId::k##name, ##__VA_ARGS__>* name() { return &name##_; } \
763 const Kind<DatumId::k##name, ##__VA_ARGS__>* name() const { return &name##_; }
764 ART_METRICS(METRIC_ACCESSORS)
765 #undef METRIC_ACCESSORS
766
767 private:
768 uint64_t beginning_timestamp_;
769 uint64_t last_report_timestamp_;
770
771 #define METRIC(name, Kind, ...) Kind<DatumId::k##name, ##__VA_ARGS__> name##_;
772 ART_METRICS(METRIC)
773 #undef METRIC
774 };
775
776 // Returns a human readable name for the given DatumId.
777 std::string DatumName(DatumId datum);
778
779 // We also log the thread type for metrics so we can distinguish things that block the UI thread
780 // from things that happen on the background thread. This enum keeps track of what thread types we
781 // support.
782 enum class ThreadType {
783 kMain,
784 kBackground,
785 };
786
787 } // namespace metrics
788 } // namespace art
789
790 #pragma clang diagnostic pop // -Wconversion
791
792 #endif // ART_LIBARTBASE_BASE_METRICS_METRICS_H_
793