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