1 /*
2  * Copyright (C) 2021 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_RUNTIME_METRICS_REPORTER_H_
18 #define ART_RUNTIME_METRICS_REPORTER_H_
19 
20 #include "app_info.h"
21 #include "base/message_queue.h"
22 #include "base/metrics/metrics.h"
23 
24 #pragma clang diagnostic push
25 #pragma clang diagnostic error "-Wconversion"
26 
27 namespace art {
28 namespace metrics {
29 
30 /**
31  * Encapsulates the specification of the metric reporting periods.
32  *
33  * The period spec follows the following regex: "(S,)?(\d+,)*\*?"
34  * with the following semantics:
35  *   "S"         - will only report at startup.
36  *
37  *   "S,1,1"     - will report startup, than 1 second later, then another
38  *                 second later.
39  *
40  *   "S,1,2,4  " - will report at Startup time, then 1 seconds later,
41  *                 then 2, then finally 4 seconds later. After that, the
42  *                 reporting will stop.
43  *
44  *   "S,1,2,4,*" - same as above, but after the final 4s period, the
45  *                 reporting will continue every other 4s.
46  *                 '*' is an indication we should report continuously
47  *                 every N seconds, where N is the last period.
48  *
49  *   "2,*"       - will report every 2 seconds
50  *
51  * Note that "", "*", or "S,*" are not valid specs, and 'S' can only occur
52  * in the beginning.
53  */
54 struct ReportingPeriodSpec {
55   static std::optional<ReportingPeriodSpec> Parse(
56       const std::string& spec_str, std::string* error_msg);
57 
58   // The original spec.
59   std::string spec;
60   // The intervals when we should report.
61   std::vector<uint32_t> periods_seconds;
62   // Whether or not the reporting is continuous (contains a '*').
63   bool continuous_reporting{false};
64   // Whether or not the reporting should start after startup event (starts with an 'S').
65   bool report_startup_first{false};
66 };
67 
68 // Defines the set of options for how metrics reporting happens.
69 struct ReportingConfig {
70   static ReportingConfig FromFlags(bool is_system_server = false);
71 
72   // Causes metrics to be written to the log, which makes them show up in logcat.
73   bool dump_to_logcat{false};
74 
75   // Causes metrics to be written to statsd, which causes them to be uploaded to Westworld.
76   bool dump_to_statsd{false};
77 
78   // If set, provides a file name to enable metrics logging to a file.
79   std::optional<std::string> dump_to_file;
80 
81   // The reporting period configuration.
82   std::optional<ReportingPeriodSpec> period_spec;
83 
84   // The mods that should report metrics. Together with reporting_num_mods, they
85   // dictate what percentage of the runtime execution will report metrics.
86   // If the `session_id (a random number) % reporting_num_mods < reporting_mods`
87   // then the runtime session will report metrics.
88   uint32_t reporting_mods{0};
89   uint32_t reporting_num_mods{100};
90 };
91 
92 // MetricsReporter handles periodically reporting ART metrics.
93 class MetricsReporter {
94  public:
95   // Creates a MetricsReporter instance that matches the options selected in ReportingConfig.
96   static std::unique_ptr<MetricsReporter> Create(const ReportingConfig& config, Runtime* runtime);
97 
98   virtual ~MetricsReporter();
99 
100   // Creates and runs the background reporting thread.
101   //
102   // Does nothing if the reporting config does not have any outputs enabled.
103   //
104   // Returns true if the thread was started, false otherwise.
105   bool MaybeStartBackgroundThread(SessionData session_data);
106 
107   // Sends a request to the background thread to shutdown.
108   void MaybeStopBackgroundThread();
109 
110   // Causes metrics to be reported so we can see a snapshot of the metrics after app startup
111   // completes.
112   void NotifyStartupCompleted();
113 
114   // Notifies the reporter that the app info was updated. This is used to detect / infer
115   // the compiler filter / reason of primary apks.
116   void NotifyAppInfoUpdated(AppInfo* app_info);
117 
118   // Requests a metrics report
119   //
120   // If synchronous is set to true, this function will block until the report has completed.
121   void RequestMetricsReport(bool synchronous = true);
122 
123   // Reloads the metrics config from the given value.
124   // Can only be called before starting the background thread.
125   void ReloadConfig(const ReportingConfig& config);
126 
127   void SetCompilationInfo(CompilationReason compilation_reason,
128                           CompilerFilterReporting compiler_filter);
129 
130   static constexpr const char* kBackgroundThreadName = "Metrics Background Reporting Thread";
131 
132  protected:
133   // Returns the metrics to be reported.
134   // This exists only for testing purposes so that we can verify reporting with minimum
135   // runtime interference.
136   virtual const ArtMetrics* GetMetrics();
137 
138   MetricsReporter(const ReportingConfig& config, Runtime* runtime);
139 
140  private:
141   // Whether or not we should reporting metrics according to the sampling rate.
142   bool IsMetricsReportingEnabled(const SessionData& session_data) const;
143 
144   // The background reporting thread main loop.
145   void BackgroundThreadRun();
146 
147   // Calls messages_.SetTimeout if needed.
148   void MaybeResetTimeout();
149 
150   // Outputs the current state of the metrics to the destination set by config_.
151   void ReportMetrics();
152 
153   // Updates the session data in all the backends.
154   void UpdateSessionInBackends();
155 
156   // Whether or not we should wait for startup before reporting for the first time.
157   bool ShouldReportAtStartup() const;
158 
159   // Whether or not we should continue reporting (either because we still
160   // have periods to report, or because we are in continuous mode).
161   bool ShouldContinueReporting() const;
162 
163   // Returns the next reporting period.
164   // Must be called only if ShouldContinueReporting() is true.
165   uint32_t GetNextPeriodSeconds();
166 
167   ReportingConfig config_;
168   Runtime* runtime_;
169   std::vector<std::unique_ptr<MetricsBackend>> backends_;
170   std::optional<std::thread> thread_;
171   // Whether or not we reported the startup event.
172   bool startup_reported_;
173   // The index into period_spec.periods_seconds which tells the next delay in
174   // seconds for the next reporting.
175   uint32_t report_interval_index_;
176 
177   // A message indicating that the reporting thread should shut down.
178   struct ShutdownRequestedMessage {};
179 
180   // A message indicating that app startup has completed.
181   struct StartupCompletedMessage {};
182 
183   // A message requesting an explicit metrics report.
184   //
185   // The synchronous field specifies whether the reporting thread will send a message back when
186   // reporting is complete.
187   struct RequestMetricsReportMessage {
188     bool synchronous;
189   };
190 
191   struct CompilationInfoMessage {
192     CompilationReason compilation_reason;
193     CompilerFilterReporting compiler_filter;
194   };
195 
196   MessageQueue<ShutdownRequestedMessage,
197                StartupCompletedMessage,
198                RequestMetricsReportMessage,
199                CompilationInfoMessage>
200       messages_;
201 
202   // A message indicating a requested report has been finished.
203   struct ReportCompletedMessage {};
204 
205   MessageQueue<ReportCompletedMessage> thread_to_host_messages_;
206 
207   SessionData session_data_{};
208   bool session_started_{false};
209 
210   friend class MetricsReporterTest;
211 };
212 
213 }  // namespace metrics
214 }  // namespace art
215 
216 #pragma clang diagnostic pop  // -Wconversion
217 
218 #endif  // ART_RUNTIME_METRICS_REPORTER_H_
219