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