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