• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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