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