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