1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/structured/structured_metrics_service.h"
6
7 #include <memory>
8
9 #include "base/functional/callback_forward.h"
10 #include "base/memory/scoped_refptr.h"
11 #include "base/run_loop.h"
12 #include "base/task/bind_post_task.h"
13 #include "base/task/sequenced_task_runner.h"
14 #include "base/task/task_traits.h"
15 #include "base/task/thread_pool.h"
16 #include "components/metrics/metrics_log.h"
17 #include "components/metrics/metrics_service_client.h"
18 #include "components/metrics/structured/reporting/structured_metrics_reporting_service.h"
19 #include "components/metrics/structured/structured_metrics_features.h"
20 #include "components/metrics/structured/structured_metrics_scheduler.h"
21 #include "third_party/metrics_proto/system_profile.pb.h"
22
23 namespace metrics::structured {
24
25 #if BUILDFLAG(IS_CHROMEOS_ASH)
ServiceIOHelper(scoped_refptr<StructuredMetricsRecorder> recorder)26 StructuredMetricsService::ServiceIOHelper::ServiceIOHelper(
27 scoped_refptr<StructuredMetricsRecorder> recorder)
28 : recorder_(std::move(recorder)) {}
29
30 StructuredMetricsService::ServiceIOHelper::~ServiceIOHelper() = default;
31
32 ChromeUserMetricsExtension
ProvideEvents()33 StructuredMetricsService::ServiceIOHelper::ProvideEvents() {
34 ChromeUserMetricsExtension uma_proto;
35 recorder_->ProvideEventMetrics(uma_proto);
36 return uma_proto;
37 }
38 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
39
StructuredMetricsService(MetricsServiceClient * client,PrefService * local_state,scoped_refptr<StructuredMetricsRecorder> recorder)40 StructuredMetricsService::StructuredMetricsService(
41 MetricsServiceClient* client,
42 PrefService* local_state,
43 scoped_refptr<StructuredMetricsRecorder> recorder)
44 : recorder_(std::move(recorder)),
45 // This service is only enabled if both structured metrics and the service
46 // flags are enabled.
47 structured_metrics_enabled_(
48 base::FeatureList::IsEnabled(metrics::features::kStructuredMetrics) &&
49 base::FeatureList::IsEnabled(kEnabledStructuredMetricsService)),
50 client_(client) {
51 CHECK(client_);
52 CHECK(local_state);
53 CHECK(recorder_);
54
55 #if BUILDFLAG(IS_CHROMEOS_ASH)
56 task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
57 {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
58 // Blocking because the works being done isn't to expensive.
59 base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
60
61 io_helper_.emplace(task_runner_, recorder_);
62 #endif
63
64 // If the StructuredMetricsService is not enabled then return early. The
65 // recorder needs to be initialized, but not the reporting service or
66 // scheduler.
67 if (!structured_metrics_enabled_) {
68 return;
69 }
70
71 #if BUILDFLAG(IS_CHROMEOS_ASH)
72 // Because of construction order of the recorder and service, the service
73 // needs to be set on the storage manager after it is created.
74 if (base::FeatureList::IsEnabled(kEventStorageManager)) {
75 StorageManager* storage_manager =
76 static_cast<StorageManager*>(recorder_->event_storage());
77 storage_manager->set_delegate(this);
78 }
79 #endif
80
81 // Setup the reporting service.
82 const UnsentLogStore::UnsentLogStoreLimits storage_limits =
83 GetLogStoreLimits();
84
85 reporting_service_ =
86 std::make_unique<reporting::StructuredMetricsReportingService>(
87 client_, local_state, storage_limits);
88
89 reporting_service_->Initialize();
90
91 // Setup the log rotation scheduler.
92 base::RepeatingClosure rotate_callback = base::BindRepeating(
93 &StructuredMetricsService::RotateLogsAndSend, weak_factory_.GetWeakPtr());
94 base::RepeatingCallback<base::TimeDelta(void)> get_upload_interval_callback =
95 base::BindRepeating(&StructuredMetricsService::GetUploadTimeInterval,
96 base::Unretained(this));
97
98 const bool fast_startup_for_test = client->ShouldStartUpFastForTesting();
99 scheduler_ = std::make_unique<StructuredMetricsScheduler>(
100 rotate_callback, get_upload_interval_callback, fast_startup_for_test);
101 }
102
~StructuredMetricsService()103 StructuredMetricsService::~StructuredMetricsService() {
104 // Will create a new log for all in-memory events.
105 // With this, we may be able to add a fast path initialization because flushed
106 // events do not need to be loaded.
107 if (recorder_ && recorder_->CanProvideMetrics() &&
108 recorder_->event_storage()->HasEvents()) {
109 Flush(metrics::MetricsLogsEventManager::CreateReason::kServiceShutdown);
110 }
111
112 #if BUILDFLAG(IS_CHROMEOS_ASH)
113 // Because of construction order of the recorder and service, the delegate
114 // must be unset here to avoid dangling pointers.
115 if (base::FeatureList::IsEnabled(kEventStorageManager)) {
116 StorageManager* storage_manager =
117 static_cast<StorageManager*>(recorder_->event_storage());
118 storage_manager->unset_delegate(this);
119 }
120 #endif
121 }
122
EnableRecording()123 void StructuredMetricsService::EnableRecording() {
124 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
125 if (!structured_metrics_enabled_) {
126 return;
127 }
128 if (!initialize_complete_) {
129 Initialize();
130 }
131 recorder_->EnableRecording();
132
133 // Attempt an upload if reporting is also active.
134 if (initialize_complete_ && reporting_active()) {
135 MaybeStartUpload();
136 }
137 }
138
DisableRecording()139 void StructuredMetricsService::DisableRecording() {
140 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
141 if (!structured_metrics_enabled_) {
142 return;
143 }
144 recorder_->DisableRecording();
145 }
146
EnableReporting()147 void StructuredMetricsService::EnableReporting() {
148 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
149 if (!structured_metrics_enabled_) {
150 return;
151 }
152 if (!reporting_active()) {
153 scheduler_->Start();
154 }
155 reporting_service_->EnableReporting();
156
157 // Attempt an upload if recording is also enabled.
158 if (initialize_complete_ && recording_enabled()) {
159 MaybeStartUpload();
160 }
161 }
162
DisableReporting()163 void StructuredMetricsService::DisableReporting() {
164 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
165 if (!structured_metrics_enabled_) {
166 return;
167 }
168 reporting_service_->DisableReporting();
169 scheduler_->Stop();
170 }
171
Flush(metrics::MetricsLogsEventManager::CreateReason reason)172 void StructuredMetricsService::Flush(
173 metrics::MetricsLogsEventManager::CreateReason reason) {
174 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
175 // The log should not be built if there aren't any events to log.
176 if (!recorder_->event_storage()->HasEvents()) {
177 return;
178 }
179
180 ChromeUserMetricsExtension uma_proto;
181 InitializeUmaProto(uma_proto);
182 recorder_->ProvideEventMetrics(uma_proto);
183 const std::string serialized_log = SerializeLog(uma_proto);
184 reporting_service_->StoreLog(serialized_log, reason);
185
186 reporting_service_->log_store()->TrimAndPersistUnsentLogs(true);
187 }
188
Purge()189 void StructuredMetricsService::Purge() {
190 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
191 if (!structured_metrics_enabled_) {
192 return;
193 }
194 recorder_->Purge();
195 reporting_service_->Purge();
196 }
197
GetUploadTimeInterval()198 base::TimeDelta StructuredMetricsService::GetUploadTimeInterval() {
199 return base::Seconds(GetUploadInterval());
200 }
201
RotateLogsAndSend()202 void StructuredMetricsService::RotateLogsAndSend() {
203 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
204
205 // Verify that the recorder has been initialized and can be providing metrics.
206 // And if it is, then see if there are any events ready to be uploaded.
207 if (!recorder_->CanProvideMetrics() ||
208 !recorder_->event_storage()->HasEvents()) {
209 return;
210 }
211
212 // If we do not have any logs then nothing to do.
213 if (!reporting_service_->log_store()->has_unsent_logs()) {
214 CreateLogs(metrics::MetricsLogsEventManager::CreateReason::kPeriodic,
215 /*notify_scheduler=*/true);
216 return;
217 }
218
219 // If we already have a completed log then we can upload here.
220 reporting_service_->Start();
221 scheduler_->RotationFinished();
222 }
223
CreateLogs(metrics::MetricsLogsEventManager::CreateReason reason,bool notify_scheduler)224 void StructuredMetricsService::CreateLogs(
225 metrics::MetricsLogsEventManager::CreateReason reason,
226 bool notify_scheduler) {
227 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
228
229 // An async version is used on Ash because events could potentially be stored on
230 // disk and must be accessed from an IO sequence.
231 // Other platforms (Windows, Mac, and Linux), the events are stored only
232 // in-memory and thus a blocking function isn't needed.
233 #if BUILDFLAG(IS_CHROMEOS_ASH)
234 BuildAndStoreLog(reason, notify_scheduler);
235 #else
236 BuildAndStoreLogSync(reason, notify_scheduler);
237 #endif
238 }
239
240 #if BUILDFLAG(IS_CHROMEOS_ASH)
BuildAndStoreLog(metrics::MetricsLogsEventManager::CreateReason reason,bool notify_scheduler)241 void StructuredMetricsService::BuildAndStoreLog(
242 metrics::MetricsLogsEventManager::CreateReason reason,
243 bool notify_scheduler) {
244 ChromeUserMetricsExtension uma_proto;
245 InitializeUmaProto(uma_proto);
246
247 io_helper_.AsyncCall(&ServiceIOHelper::ProvideEvents)
248 .Then(base::BindOnce(&StructuredMetricsService::StoreLogAndStartUpload,
249 weak_factory_.GetWeakPtr(), reason,
250 notify_scheduler));
251 }
252 #endif
253
BuildAndStoreLogSync(metrics::MetricsLogsEventManager::CreateReason reason,bool notify_scheduler)254 void StructuredMetricsService::BuildAndStoreLogSync(
255 metrics::MetricsLogsEventManager::CreateReason reason,
256 bool notify_scheduler) {
257 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
258
259 ChromeUserMetricsExtension uma_proto;
260 InitializeUmaProto(uma_proto);
261 recorder_->ProvideEventMetrics(uma_proto);
262
263 StoreLogAndStartUpload(reason, notify_scheduler, std::move(uma_proto));
264 }
265
StoreLogAndStartUpload(metrics::MetricsLogsEventManager::CreateReason reason,bool notify_scheduler,ChromeUserMetricsExtension uma_proto)266 void StructuredMetricsService::StoreLogAndStartUpload(
267 metrics::MetricsLogsEventManager::CreateReason reason,
268 bool notify_scheduler,
269 ChromeUserMetricsExtension uma_proto) {
270 // The |uma_proto| is created by |io_helper_|, this adds all additional
271 // metadata to the output proto.
272 InitializeUmaProto(uma_proto);
273
274 const std::string serialized_log = SerializeLog(uma_proto);
275 reporting_service_->StoreLog(serialized_log, reason);
276
277 // If this callback is set, then run it and return.
278 // It will only be set from tests where we do not want to upload.
279 if (create_log_callback_for_tests_) {
280 std::move(create_log_callback_for_tests_).Run();
281 return;
282 }
283
284 reporting_service_->Start();
285 if (notify_scheduler) {
286 scheduler_->RotationFinished();
287 }
288 }
289
Initialize()290 void StructuredMetricsService::Initialize() {
291 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
292 DCHECK(!initialize_complete_);
293
294 initialize_complete_ = true;
295
296 // Notifies the scheduler that it is ready to start creating logs.
297 scheduler_->InitTaskComplete();
298 }
299
InitializeUmaProto(ChromeUserMetricsExtension & uma_proto)300 void StructuredMetricsService::InitializeUmaProto(
301 ChromeUserMetricsExtension& uma_proto) {
302 const int32_t product = client_->GetProduct();
303 if (product != uma_proto.product()) {
304 uma_proto.set_product(product);
305 }
306
307 recorder_->ProvideLogMetadata(uma_proto);
308
309 SystemProfileProto* system_profile = uma_proto.mutable_system_profile();
310 metrics::MetricsLog::RecordCoreSystemProfile(client_, system_profile);
311 }
312
RegisterPrefs(PrefRegistrySimple * registry)313 void StructuredMetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
314 reporting::StructuredMetricsReportingService::RegisterPrefs(registry);
315 }
316
SetRecorderForTest(scoped_refptr<StructuredMetricsRecorder> recorder)317 void StructuredMetricsService::SetRecorderForTest(
318 scoped_refptr<StructuredMetricsRecorder> recorder) {
319 recorder_ = std::move(recorder);
320
321 #if BUILDFLAG(IS_CHROMEOS_ASH)
322 // Reset the |io_helper_| with the new recorder.
323 io_helper_.emplace(task_runner_, recorder_);
324 #endif
325 }
326
GetMetricsServiceClient() const327 MetricsServiceClient* StructuredMetricsService::GetMetricsServiceClient()
328 const {
329 return client_;
330 }
331
ManualUpload()332 void StructuredMetricsService::ManualUpload() {
333 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
334
335 if (!recorder_->CanProvideMetrics() ||
336 !recorder_->event_storage()->HasEvents()) {
337 return;
338 }
339
340 if (!reporting_service_->log_store()->has_unsent_logs()) {
341 CreateLogs(metrics::MetricsLogsEventManager::CreateReason::kUnknown,
342 /*notify_scheduler=*/false);
343 return;
344 }
345 reporting_service_->Start();
346 }
347
MaybeStartUpload()348 void StructuredMetricsService::MaybeStartUpload() {
349 // We do not have any logs to upload. Nothing to do.
350 if (!reporting_service_->log_store()->has_unsent_logs()) {
351 return;
352 }
353
354 if (initial_upload_started_) {
355 return;
356 }
357
358 initial_upload_started_ = true;
359
360 // Starts an upload. If a log is not staged the next log will be staged for
361 // upload.
362 reporting_service_->Start();
363 }
364
SetCreateLogsCallbackInTests(base::OnceClosure callback)365 void StructuredMetricsService::SetCreateLogsCallbackInTests(
366 base::OnceClosure callback) {
367 create_log_callback_for_tests_ = std::move(callback);
368 }
369
OnFlushed(const FlushedKey & key)370 void StructuredMetricsService::OnFlushed(const FlushedKey& key) {
371 // TODO(b/327269939) Implement telemetry for flushed events.
372 }
373
OnDeleted(const FlushedKey & key,DeleteReason reason)374 void StructuredMetricsService::OnDeleted(const FlushedKey& key,
375 DeleteReason reason) {
376 // TODO(b/327269939) Implement telemetry for deleted events.
377 }
378
379 // static:
SerializeLog(const ChromeUserMetricsExtension & uma_proto)380 std::string StructuredMetricsService::SerializeLog(
381 const ChromeUserMetricsExtension& uma_proto) {
382 std::string log_data;
383 const bool status = uma_proto.SerializeToString(&log_data);
384 DCHECK(status);
385 return log_data;
386 }
387
388 // static:
389 UnsentLogStore::UnsentLogStoreLimits
GetLogStoreLimits()390 StructuredMetricsService::GetLogStoreLimits() {
391 return UnsentLogStore::UnsentLogStoreLimits{
392 .min_log_count = static_cast<size_t>(kMinLogQueueCount.Get()),
393 .min_queue_size_bytes = static_cast<size_t>(kMinLogQueueSizeBytes.Get()),
394 .max_log_size_bytes = static_cast<size_t>(kMaxLogSizeBytes.Get()),
395 };
396 }
397
398 } // namespace metrics::structured
399