OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/ukm/ukm_service.h" | 5 #include "components/ukm/ukm_service.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 #include <string> | 8 #include <string> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/feature_list.h" | 12 #include "base/feature_list.h" |
13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
14 #include "base/metrics/field_trial.h" | 14 #include "base/metrics/field_trial.h" |
15 #include "base/metrics/field_trial_params.h" | 15 #include "base/metrics/field_trial_params.h" |
16 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
17 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
18 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
19 #include "base/time/time.h" | 19 #include "base/time/time.h" |
20 #include "components/metrics/metrics_log.h" | 20 #include "components/metrics/metrics_log.h" |
21 #include "components/metrics/metrics_log_uploader.h" | 21 #include "components/metrics/metrics_log_uploader.h" |
22 #include "components/metrics/metrics_service_client.h" | 22 #include "components/metrics/metrics_service_client.h" |
23 #include "components/metrics/proto/ukm/entry.pb.h" | 23 #include "components/metrics/proto/ukm/entry.pb.h" |
24 #include "components/metrics/proto/ukm/report.pb.h" | 24 #include "components/metrics/proto/ukm/report.pb.h" |
25 #include "components/metrics/proto/ukm/source.pb.h" | 25 #include "components/metrics/proto/ukm/source.pb.h" |
26 #include "components/prefs/pref_registry_simple.h" | 26 #include "components/prefs/pref_registry_simple.h" |
27 #include "components/prefs/pref_service.h" | 27 #include "components/prefs/pref_service.h" |
28 #include "components/ukm/metrics_reporting_scheduler.h" | |
29 #include "components/ukm/persisted_logs_metrics_impl.h" | 28 #include "components/ukm/persisted_logs_metrics_impl.h" |
30 #include "components/ukm/ukm_entry.h" | 29 #include "components/ukm/ukm_entry.h" |
31 #include "components/ukm/ukm_entry_builder.h" | 30 #include "components/ukm/ukm_entry_builder.h" |
32 #include "components/ukm/ukm_pref_names.h" | 31 #include "components/ukm/ukm_pref_names.h" |
| 32 #include "components/ukm/ukm_rotation_scheduler.h" |
33 #include "components/ukm/ukm_source.h" | 33 #include "components/ukm/ukm_source.h" |
34 | 34 |
35 namespace ukm { | 35 namespace ukm { |
36 | 36 |
37 namespace { | 37 namespace { |
38 | 38 |
39 constexpr char kMimeType[] = "application/vnd.chrome.ukm"; | |
40 | |
41 // The delay, in seconds, after starting recording before doing expensive | 39 // The delay, in seconds, after starting recording before doing expensive |
42 // initialization work. | 40 // initialization work. |
43 constexpr int kInitializationDelaySeconds = 5; | 41 constexpr int kInitializationDelaySeconds = 5; |
44 | 42 |
45 // The number of UKM logs that will be stored in PersistedLogs before logs | |
46 // start being dropped. | |
47 constexpr int kMinPersistedLogs = 8; | |
48 | |
49 // The number of bytes UKM logs that will be stored in PersistedLogs before | |
50 // logs start being dropped. | |
51 // This ensures that a reasonable amount of history will be stored even if there | |
52 // is a long series of very small logs. | |
53 constexpr int kMinPersistedBytes = 300000; | |
54 | |
55 // If an upload fails, and the transmission was over this byte count, then we | |
56 // will discard the log, and not try to retransmit it. We also don't persist | |
57 // the log to the prefs for transmission during the next chrome session if this | |
58 // limit is exceeded. | |
59 constexpr size_t kMaxLogRetransmitSize = 100 * 1024; | |
60 | |
61 // Gets the UKM Server URL. | |
62 std::string GetServerUrl() { | |
63 constexpr char kDefaultServerUrl[] = "https://clients4.google.com/ukm"; | |
64 std::string server_url = | |
65 base::GetFieldTrialParamValueByFeature(kUkmFeature, "ServerUrl"); | |
66 if (!server_url.empty()) | |
67 return server_url; | |
68 return kDefaultServerUrl; | |
69 } | |
70 | |
71 // Gets the maximum number of Sources we'll keep in memory before discarding any | 43 // Gets the maximum number of Sources we'll keep in memory before discarding any |
72 // new ones being added. | 44 // new ones being added. |
73 size_t GetMaxSources() { | 45 size_t GetMaxSources() { |
74 constexpr size_t kDefaultMaxSources = 500; | 46 constexpr size_t kDefaultMaxSources = 500; |
75 return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt( | 47 return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt( |
76 kUkmFeature, "MaxSources", kDefaultMaxSources)); | 48 kUkmFeature, "MaxSources", kDefaultMaxSources)); |
77 } | 49 } |
78 | 50 |
79 // Gets the maximum number of Entries we'll keep in memory before discarding any | 51 // Gets the maximum number of Entries we'll keep in memory before discarding any |
80 // new ones being added. | 52 // new ones being added. |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 | 117 |
146 const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT}; | 118 const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT}; |
147 | 119 |
148 UkmService::UkmService(PrefService* pref_service, | 120 UkmService::UkmService(PrefService* pref_service, |
149 metrics::MetricsServiceClient* client) | 121 metrics::MetricsServiceClient* client) |
150 : pref_service_(pref_service), | 122 : pref_service_(pref_service), |
151 recording_enabled_(false), | 123 recording_enabled_(false), |
152 client_id_(0), | 124 client_id_(0), |
153 session_id_(0), | 125 session_id_(0), |
154 client_(client), | 126 client_(client), |
155 persisted_logs_(std::unique_ptr<ukm::PersistedLogsMetricsImpl>( | 127 reporting_service_(client, pref_service), |
156 new ukm::PersistedLogsMetricsImpl()), | |
157 pref_service, | |
158 prefs::kUkmPersistedLogs, | |
159 kMinPersistedLogs, | |
160 kMinPersistedBytes, | |
161 kMaxLogRetransmitSize), | |
162 initialize_started_(false), | 128 initialize_started_(false), |
163 initialize_complete_(false), | 129 initialize_complete_(false), |
164 log_upload_in_progress_(false), | |
165 self_ptr_factory_(this) { | 130 self_ptr_factory_(this) { |
166 DCHECK(pref_service_); | 131 DCHECK(pref_service_); |
167 DCHECK(client_); | 132 DCHECK(client_); |
168 DVLOG(1) << "UkmService::Constructor"; | 133 DVLOG(1) << "UkmService::Constructor"; |
169 | 134 |
170 persisted_logs_.LoadPersistedUnsentLogs(); | 135 reporting_service_.Initialize(); |
171 | 136 |
172 base::Closure rotate_callback = | 137 base::Closure rotate_callback = |
173 base::Bind(&UkmService::RotateLog, self_ptr_factory_.GetWeakPtr()); | 138 base::Bind(&UkmService::RotateLog, self_ptr_factory_.GetWeakPtr()); |
174 // MetricsServiceClient outlives UkmService, and | 139 // MetricsServiceClient outlives UkmService, and |
175 // MetricsReportingScheduler is tied to the lifetime of |this|. | 140 // MetricsReportingScheduler is tied to the lifetime of |this|. |
176 const base::Callback<base::TimeDelta(void)>& get_upload_interval_callback = | 141 const base::Callback<base::TimeDelta(void)>& get_upload_interval_callback = |
177 base::Bind(&metrics::MetricsServiceClient::GetStandardUploadInterval, | 142 base::Bind(&metrics::MetricsServiceClient::GetStandardUploadInterval, |
178 base::Unretained(client_)); | 143 base::Unretained(client_)); |
179 scheduler_.reset(new ukm::MetricsReportingScheduler( | 144 scheduler_.reset(new ukm::UkmRotationScheduler(rotate_callback, |
180 rotate_callback, get_upload_interval_callback)); | 145 get_upload_interval_callback)); |
181 | 146 |
182 for (auto& provider : metrics_providers_) | 147 for (auto& provider : metrics_providers_) |
183 provider->Init(); | 148 provider->Init(); |
184 } | 149 } |
185 | 150 |
186 UkmService::~UkmService() { | 151 UkmService::~UkmService() { |
187 DisableReporting(); | 152 DisableReporting(); |
188 } | 153 } |
189 | 154 |
190 void UkmService::Initialize() { | 155 void UkmService::Initialize() { |
(...skipping 12 matching lines...) Expand all Loading... |
203 recording_enabled_ = true; | 168 recording_enabled_ = true; |
204 } | 169 } |
205 | 170 |
206 void UkmService::DisableRecording() { | 171 void UkmService::DisableRecording() { |
207 recording_enabled_ = false; | 172 recording_enabled_ = false; |
208 } | 173 } |
209 | 174 |
210 void UkmService::EnableReporting() { | 175 void UkmService::EnableReporting() { |
211 DCHECK(thread_checker_.CalledOnValidThread()); | 176 DCHECK(thread_checker_.CalledOnValidThread()); |
212 DVLOG(1) << "UkmService::EnableReporting"; | 177 DVLOG(1) << "UkmService::EnableReporting"; |
| 178 if (reporting_service_.reporting_active()) |
| 179 return; |
213 | 180 |
214 for (auto& provider : metrics_providers_) | 181 for (auto& provider : metrics_providers_) |
215 provider->OnRecordingEnabled(); | 182 provider->OnRecordingEnabled(); |
216 | 183 |
217 if (!initialize_started_) | 184 if (!initialize_started_) |
218 Initialize(); | 185 Initialize(); |
219 scheduler_->Start(); | 186 scheduler_->Start(); |
| 187 reporting_service_.EnableReporting(); |
220 } | 188 } |
221 | 189 |
222 void UkmService::DisableReporting() { | 190 void UkmService::DisableReporting() { |
223 DCHECK(thread_checker_.CalledOnValidThread()); | 191 DCHECK(thread_checker_.CalledOnValidThread()); |
224 DVLOG(1) << "UkmService::DisableReporting"; | 192 DVLOG(1) << "UkmService::DisableReporting"; |
225 | 193 |
| 194 reporting_service_.DisableReporting(); |
| 195 |
226 for (auto& provider : metrics_providers_) | 196 for (auto& provider : metrics_providers_) |
227 provider->OnRecordingDisabled(); | 197 provider->OnRecordingDisabled(); |
228 | 198 |
229 scheduler_->Stop(); | 199 scheduler_->Stop(); |
230 Flush(); | 200 Flush(); |
231 } | 201 } |
232 | 202 |
233 #if defined(OS_ANDROID) || defined(OS_IOS) | 203 #if defined(OS_ANDROID) || defined(OS_IOS) |
234 void UkmService::OnAppEnterForeground() { | 204 void UkmService::OnAppEnterForeground() { |
235 DCHECK(thread_checker_.CalledOnValidThread()); | 205 DCHECK(thread_checker_.CalledOnValidThread()); |
(...skipping 21 matching lines...) Expand all Loading... |
257 provider->OnAppEnterBackground(); | 227 provider->OnAppEnterBackground(); |
258 | 228 |
259 Flush(); | 229 Flush(); |
260 } | 230 } |
261 #endif | 231 #endif |
262 | 232 |
263 void UkmService::Flush() { | 233 void UkmService::Flush() { |
264 DCHECK(thread_checker_.CalledOnValidThread()); | 234 DCHECK(thread_checker_.CalledOnValidThread()); |
265 if (initialize_complete_) | 235 if (initialize_complete_) |
266 BuildAndStoreLog(); | 236 BuildAndStoreLog(); |
267 persisted_logs_.PersistUnsentLogs(); | 237 reporting_service_.ukm_log_store()->PersistUnsentLogs(); |
268 } | 238 } |
269 | 239 |
270 void UkmService::Purge() { | 240 void UkmService::Purge() { |
271 DCHECK(thread_checker_.CalledOnValidThread()); | 241 DCHECK(thread_checker_.CalledOnValidThread()); |
272 DVLOG(1) << "UkmService::Purge"; | 242 DVLOG(1) << "UkmService::Purge"; |
273 | 243 reporting_service_.ukm_log_store()->Purge(); |
274 persisted_logs_.Purge(); | |
275 sources_.clear(); | 244 sources_.clear(); |
276 entries_.clear(); | 245 entries_.clear(); |
277 } | 246 } |
278 | 247 |
279 // TODO(bmcquade): rename this to something more generic, like | 248 // TODO(bmcquade): rename this to something more generic, like |
280 // ResetClientState. Consider resetting all prefs here. | 249 // ResetClientState. Consider resetting all prefs here. |
281 void UkmService::ResetClientId() { | 250 void UkmService::ResetClientId() { |
282 client_id_ = GenerateClientId(pref_service_); | 251 client_id_ = GenerateClientId(pref_service_); |
283 session_id_ = LoadSessionId(pref_service_); | 252 session_id_ = LoadSessionId(pref_service_); |
284 } | 253 } |
285 | 254 |
286 void UkmService::RegisterMetricsProvider( | 255 void UkmService::RegisterMetricsProvider( |
287 std::unique_ptr<metrics::MetricsProvider> provider) { | 256 std::unique_ptr<metrics::MetricsProvider> provider) { |
288 metrics_providers_.push_back(std::move(provider)); | 257 metrics_providers_.push_back(std::move(provider)); |
289 } | 258 } |
290 | 259 |
291 // static | 260 // static |
292 void UkmService::RegisterPrefs(PrefRegistrySimple* registry) { | 261 void UkmService::RegisterPrefs(PrefRegistrySimple* registry) { |
293 registry->RegisterInt64Pref(prefs::kUkmClientId, 0); | 262 registry->RegisterInt64Pref(prefs::kUkmClientId, 0); |
294 registry->RegisterIntegerPref(prefs::kUkmSessionId, 0); | 263 registry->RegisterIntegerPref(prefs::kUkmSessionId, 0); |
295 registry->RegisterListPref(prefs::kUkmPersistedLogs); | 264 UkmReportingService::RegisterPrefs(registry); |
296 } | 265 } |
297 | 266 |
298 void UkmService::StartInitTask() { | 267 void UkmService::StartInitTask() { |
299 DCHECK(thread_checker_.CalledOnValidThread()); | 268 DCHECK(thread_checker_.CalledOnValidThread()); |
300 DVLOG(1) << "UkmService::StartInitTask"; | 269 DVLOG(1) << "UkmService::StartInitTask"; |
301 client_id_ = LoadOrGenerateClientId(pref_service_); | 270 client_id_ = LoadOrGenerateClientId(pref_service_); |
302 session_id_ = LoadSessionId(pref_service_); | 271 session_id_ = LoadSessionId(pref_service_); |
303 client_->InitializeSystemProfileMetrics(base::Bind( | 272 client_->InitializeSystemProfileMetrics(base::Bind( |
304 &UkmService::FinishedInitTask, self_ptr_factory_.GetWeakPtr())); | 273 &UkmService::FinishedInitTask, self_ptr_factory_.GetWeakPtr())); |
305 } | 274 } |
306 | 275 |
307 void UkmService::FinishedInitTask() { | 276 void UkmService::FinishedInitTask() { |
308 DCHECK(thread_checker_.CalledOnValidThread()); | 277 DCHECK(thread_checker_.CalledOnValidThread()); |
309 DVLOG(1) << "UkmService::FinishedInitTask"; | 278 DVLOG(1) << "UkmService::FinishedInitTask"; |
310 initialize_complete_ = true; | 279 initialize_complete_ = true; |
311 scheduler_->InitTaskComplete(); | 280 scheduler_->InitTaskComplete(); |
312 } | 281 } |
313 | 282 |
314 void UkmService::RotateLog() { | 283 void UkmService::RotateLog() { |
315 DCHECK(thread_checker_.CalledOnValidThread()); | 284 DCHECK(thread_checker_.CalledOnValidThread()); |
316 DCHECK(!log_upload_in_progress_); | |
317 DVLOG(1) << "UkmService::RotateLog"; | 285 DVLOG(1) << "UkmService::RotateLog"; |
318 if (!persisted_logs_.has_unsent_logs()) | 286 if (!reporting_service_.ukm_log_store()->has_unsent_logs()) |
319 BuildAndStoreLog(); | 287 BuildAndStoreLog(); |
320 StartScheduledUpload(); | 288 reporting_service_.Start(); |
321 } | 289 } |
322 | 290 |
323 void UkmService::BuildAndStoreLog() { | 291 void UkmService::BuildAndStoreLog() { |
324 DCHECK(thread_checker_.CalledOnValidThread()); | 292 DCHECK(thread_checker_.CalledOnValidThread()); |
325 DVLOG(1) << "UkmService::BuildAndStoreLog"; | 293 DVLOG(1) << "UkmService::BuildAndStoreLog"; |
326 | 294 |
327 // Suppress generating a log if we have no new data to include. | 295 // Suppress generating a log if we have no new data to include. |
328 // TODO(zhenw): add a histogram here to debug if this case is hitting a lot. | 296 // TODO(zhenw): add a histogram here to debug if this case is hitting a lot. |
329 if (sources_.empty() && entries_.empty()) | 297 if (sources_.empty() && entries_.empty()) |
330 return; | 298 return; |
(...skipping 21 matching lines...) Expand all Loading... |
352 | 320 |
353 metrics::MetricsLog::RecordCoreSystemProfile(client_, | 321 metrics::MetricsLog::RecordCoreSystemProfile(client_, |
354 report.mutable_system_profile()); | 322 report.mutable_system_profile()); |
355 | 323 |
356 for (auto& provider : metrics_providers_) { | 324 for (auto& provider : metrics_providers_) { |
357 provider->ProvideSystemProfileMetrics(report.mutable_system_profile()); | 325 provider->ProvideSystemProfileMetrics(report.mutable_system_profile()); |
358 } | 326 } |
359 | 327 |
360 std::string serialized_log; | 328 std::string serialized_log; |
361 report.SerializeToString(&serialized_log); | 329 report.SerializeToString(&serialized_log); |
362 persisted_logs_.StoreLog(serialized_log); | 330 reporting_service_.ukm_log_store()->StoreLog(serialized_log); |
363 } | |
364 | |
365 void UkmService::StartScheduledUpload() { | |
366 DCHECK(thread_checker_.CalledOnValidThread()); | |
367 DCHECK(!log_upload_in_progress_); | |
368 if (!persisted_logs_.has_unsent_logs()) { | |
369 // No logs to send, so early out and schedule the next rotation. | |
370 scheduler_->UploadFinished(true, /* has_unsent_logs */ false); | |
371 return; | |
372 } | |
373 if (!persisted_logs_.has_staged_log()) | |
374 persisted_logs_.StageNextLog(); | |
375 // TODO(holte): Handle data usage on cellular, etc. | |
376 if (!log_uploader_) { | |
377 log_uploader_ = client_->CreateUploader( | |
378 GetServerUrl(), kMimeType, metrics::MetricsLogUploader::UKM, | |
379 base::Bind(&UkmService::OnLogUploadComplete, | |
380 self_ptr_factory_.GetWeakPtr())); | |
381 } | |
382 log_upload_in_progress_ = true; | |
383 | |
384 const std::string hash = | |
385 base::HexEncode(persisted_logs_.staged_log_hash().data(), | |
386 persisted_logs_.staged_log_hash().size()); | |
387 log_uploader_->UploadLog(persisted_logs_.staged_log(), hash); | |
388 } | |
389 | |
390 void UkmService::OnLogUploadComplete(int response_code) { | |
391 DCHECK(thread_checker_.CalledOnValidThread()); | |
392 DCHECK(log_upload_in_progress_); | |
393 DVLOG(1) << "UkmService::OnLogUploadComplete"; | |
394 log_upload_in_progress_ = false; | |
395 | |
396 UMA_HISTOGRAM_SPARSE_SLOWLY("UKM.Upload.ResponseCode", response_code); | |
397 | |
398 bool upload_succeeded = response_code == 200; | |
399 | |
400 // Staged log may have been deleted by Purge already, otherwise we may | |
401 // remove it from the log store here. | |
402 if (persisted_logs_.has_staged_log()) { | |
403 // Provide boolean for error recovery (allow us to ignore response_code). | |
404 bool discard_log = false; | |
405 const size_t log_size_bytes = persisted_logs_.staged_log().length(); | |
406 if (upload_succeeded) { | |
407 UMA_HISTOGRAM_COUNTS_10000("UKM.LogSize.OnSuccess", | |
408 log_size_bytes / 1024); | |
409 } else if (response_code == 400) { | |
410 // Bad syntax. Retransmission won't work. | |
411 discard_log = true; | |
412 } | |
413 | |
414 if (upload_succeeded || discard_log) { | |
415 persisted_logs_.DiscardStagedLog(); | |
416 // Store the updated list to disk now that the removed log is uploaded. | |
417 persisted_logs_.PersistUnsentLogs(); | |
418 } | |
419 } | |
420 | |
421 // Error 400 indicates a problem with the log, not with the server, so | |
422 // don't consider that a sign that the server is in trouble. | |
423 bool server_is_healthy = upload_succeeded || response_code == 400; | |
424 scheduler_->UploadFinished(server_is_healthy, | |
425 persisted_logs_.has_unsent_logs()); | |
426 } | 331 } |
427 | 332 |
428 // static | 333 // static |
429 int32_t UkmService::GetNewSourceID() { | 334 int32_t UkmService::GetNewSourceID() { |
430 static int32_t next_source_id = 0; | 335 static int32_t next_source_id = 0; |
431 return next_source_id++; | 336 return next_source_id++; |
432 } | 337 } |
433 | 338 |
434 std::unique_ptr<UkmEntryBuilder> UkmService::GetEntryBuilder( | 339 std::unique_ptr<UkmEntryBuilder> UkmService::GetEntryBuilder( |
435 int32_t source_id, | 340 int32_t source_id, |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 } | 379 } |
475 if (entries_.size() >= GetMaxEntries()) { | 380 if (entries_.size() >= GetMaxEntries()) { |
476 RecordDroppedEntry(DroppedDataReason::MAX_HIT); | 381 RecordDroppedEntry(DroppedDataReason::MAX_HIT); |
477 return; | 382 return; |
478 } | 383 } |
479 | 384 |
480 entries_.push_back(std::move(entry)); | 385 entries_.push_back(std::move(entry)); |
481 } | 386 } |
482 | 387 |
483 } // namespace ukm | 388 } // namespace ukm |
OLD | NEW |