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