| 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/atomic_sequence_num.h" | |
| 12 #include "base/bind.h" | 11 #include "base/bind.h" |
| 13 #include "base/feature_list.h" | 12 #include "base/feature_list.h" |
| 14 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 15 #include "base/metrics/field_trial.h" | 14 #include "base/metrics/field_trial.h" |
| 16 #include "base/metrics/field_trial_params.h" | 15 #include "base/metrics/field_trial_params.h" |
| 17 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
| 18 #include "base/metrics/metrics_hashes.h" | |
| 19 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
| 20 #include "base/strings/string_number_conversions.h" | |
| 21 #include "base/strings/string_split.h" | |
| 22 #include "base/threading/thread_task_runner_handle.h" | 18 #include "base/threading/thread_task_runner_handle.h" |
| 23 #include "base/time/time.h" | 19 #include "base/time/time.h" |
| 24 #include "components/metrics/metrics_log.h" | 20 #include "components/metrics/metrics_log.h" |
| 25 #include "components/metrics/metrics_log_uploader.h" | |
| 26 #include "components/metrics/metrics_service_client.h" | 21 #include "components/metrics/metrics_service_client.h" |
| 27 #include "components/metrics/proto/ukm/entry.pb.h" | |
| 28 #include "components/metrics/proto/ukm/report.pb.h" | 22 #include "components/metrics/proto/ukm/report.pb.h" |
| 29 #include "components/metrics/proto/ukm/source.pb.h" | |
| 30 #include "components/prefs/pref_registry_simple.h" | 23 #include "components/prefs/pref_registry_simple.h" |
| 31 #include "components/prefs/pref_service.h" | 24 #include "components/prefs/pref_service.h" |
| 32 #include "components/ukm/persisted_logs_metrics_impl.h" | 25 #include "components/ukm/persisted_logs_metrics_impl.h" |
| 33 #include "components/ukm/ukm_entry.h" | |
| 34 #include "components/ukm/ukm_entry_builder.h" | |
| 35 #include "components/ukm/ukm_pref_names.h" | 26 #include "components/ukm/ukm_pref_names.h" |
| 36 #include "components/ukm/ukm_rotation_scheduler.h" | 27 #include "components/ukm/ukm_rotation_scheduler.h" |
| 37 #include "components/ukm/ukm_source.h" | |
| 38 | 28 |
| 39 namespace ukm { | 29 namespace ukm { |
| 40 | 30 |
| 41 namespace { | 31 namespace { |
| 42 | 32 |
| 43 // The delay, in seconds, after starting recording before doing expensive | 33 // The delay, in seconds, after starting recording before doing expensive |
| 44 // initialization work. | 34 // initialization work. |
| 45 constexpr int kInitializationDelaySeconds = 5; | 35 constexpr int kInitializationDelaySeconds = 5; |
| 46 | 36 |
| 47 // Gets the list of whitelisted Entries as string. Format is a comma seperated | |
| 48 // list of Entry names (as strings). | |
| 49 std::string GetWhitelistEntries() { | |
| 50 return base::GetFieldTrialParamValueByFeature(kUkmFeature, | |
| 51 "WhitelistEntries"); | |
| 52 } | |
| 53 | |
| 54 // Gets the maximum number of Sources we'll keep in memory before discarding any | |
| 55 // new ones being added. | |
| 56 size_t GetMaxSources() { | |
| 57 constexpr size_t kDefaultMaxSources = 500; | |
| 58 return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt( | |
| 59 kUkmFeature, "MaxSources", kDefaultMaxSources)); | |
| 60 } | |
| 61 | |
| 62 // Gets the maximum number of Entries we'll keep in memory before discarding any | |
| 63 // new ones being added. | |
| 64 size_t GetMaxEntries() { | |
| 65 constexpr size_t kDefaultMaxEntries = 5000; | |
| 66 return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt( | |
| 67 kUkmFeature, "MaxEntries", kDefaultMaxEntries)); | |
| 68 } | |
| 69 | |
| 70 // True if we should record the initial_url field of the UKM Source proto. | |
| 71 bool ShouldRecordInitialUrl() { | |
| 72 return base::GetFieldTrialParamByFeatureAsBool(kUkmFeature, | |
| 73 "RecordInitialUrl", false); | |
| 74 } | |
| 75 | |
| 76 // True if we should record session ids in the UKM Report proto. | 37 // True if we should record session ids in the UKM Report proto. |
| 77 bool ShouldRecordSessionId() { | 38 bool ShouldRecordSessionId() { |
| 78 return base::GetFieldTrialParamByFeatureAsBool(kUkmFeature, "RecordSessionId", | 39 return base::GetFieldTrialParamByFeatureAsBool(kUkmFeature, "RecordSessionId", |
| 79 false); | 40 false); |
| 80 } | 41 } |
| 81 | 42 |
| 82 // Generates a new client id and stores it in prefs. | 43 // Generates a new client id and stores it in prefs. |
| 83 uint64_t GenerateClientId(PrefService* pref_service) { | 44 uint64_t GenerateClientId(PrefService* pref_service) { |
| 84 uint64_t client_id = 0; | 45 uint64_t client_id = 0; |
| 85 while (!client_id) | 46 while (!client_id) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 98 return client_id; | 59 return client_id; |
| 99 } | 60 } |
| 100 | 61 |
| 101 int32_t LoadSessionId(PrefService* pref_service) { | 62 int32_t LoadSessionId(PrefService* pref_service) { |
| 102 int32_t session_id = pref_service->GetInteger(prefs::kUkmSessionId); | 63 int32_t session_id = pref_service->GetInteger(prefs::kUkmSessionId); |
| 103 ++session_id; // increment session id, once per session | 64 ++session_id; // increment session id, once per session |
| 104 pref_service->SetInteger(prefs::kUkmSessionId, session_id); | 65 pref_service->SetInteger(prefs::kUkmSessionId, session_id); |
| 105 return session_id; | 66 return session_id; |
| 106 } | 67 } |
| 107 | 68 |
| 108 enum class DroppedDataReason { | |
| 109 NOT_DROPPED = 0, | |
| 110 RECORDING_DISABLED = 1, | |
| 111 MAX_HIT = 2, | |
| 112 NOT_WHITELISTED = 3, | |
| 113 NUM_DROPPED_DATA_REASONS | |
| 114 }; | |
| 115 | |
| 116 void RecordDroppedSource(DroppedDataReason reason) { | |
| 117 UMA_HISTOGRAM_ENUMERATION( | |
| 118 "UKM.Sources.Dropped", static_cast<int>(reason), | |
| 119 static_cast<int>(DroppedDataReason::NUM_DROPPED_DATA_REASONS)); | |
| 120 } | |
| 121 | |
| 122 void RecordDroppedEntry(DroppedDataReason reason) { | |
| 123 UMA_HISTOGRAM_ENUMERATION( | |
| 124 "UKM.Entries.Dropped", static_cast<int>(reason), | |
| 125 static_cast<int>(DroppedDataReason::NUM_DROPPED_DATA_REASONS)); | |
| 126 } | |
| 127 | |
| 128 } // namespace | 69 } // namespace |
| 129 | 70 |
| 130 const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT}; | |
| 131 | |
| 132 UkmService::UkmService(PrefService* pref_service, | 71 UkmService::UkmService(PrefService* pref_service, |
| 133 metrics::MetricsServiceClient* client) | 72 metrics::MetricsServiceClient* client) |
| 134 : pref_service_(pref_service), | 73 : pref_service_(pref_service), |
| 135 recording_enabled_(false), | |
| 136 client_id_(0), | 74 client_id_(0), |
| 137 session_id_(0), | 75 session_id_(0), |
| 138 client_(client), | 76 client_(client), |
| 139 reporting_service_(client, pref_service), | 77 reporting_service_(client, pref_service), |
| 140 initialize_started_(false), | 78 initialize_started_(false), |
| 141 initialize_complete_(false), | 79 initialize_complete_(false), |
| 142 self_ptr_factory_(this) { | 80 self_ptr_factory_(this) { |
| 143 DCHECK(pref_service_); | 81 DCHECK(pref_service_); |
| 144 DCHECK(client_); | 82 DCHECK(client_); |
| 145 DVLOG(1) << "UkmService::Constructor"; | 83 DVLOG(1) << "UkmService::Constructor"; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 171 DCHECK(!initialize_started_); | 109 DCHECK(!initialize_started_); |
| 172 DVLOG(1) << "UkmService::Initialize"; | 110 DVLOG(1) << "UkmService::Initialize"; |
| 173 initialize_started_ = true; | 111 initialize_started_ = true; |
| 174 | 112 |
| 175 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 113 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| 176 FROM_HERE, | 114 FROM_HERE, |
| 177 base::Bind(&UkmService::StartInitTask, self_ptr_factory_.GetWeakPtr()), | 115 base::Bind(&UkmService::StartInitTask, self_ptr_factory_.GetWeakPtr()), |
| 178 base::TimeDelta::FromSeconds(kInitializationDelaySeconds)); | 116 base::TimeDelta::FromSeconds(kInitializationDelaySeconds)); |
| 179 } | 117 } |
| 180 | 118 |
| 181 void UkmService::EnableRecording() { | |
| 182 recording_enabled_ = true; | |
| 183 } | |
| 184 | |
| 185 void UkmService::DisableRecording() { | |
| 186 recording_enabled_ = false; | |
| 187 } | |
| 188 | |
| 189 void UkmService::EnableReporting() { | 119 void UkmService::EnableReporting() { |
| 190 DCHECK(thread_checker_.CalledOnValidThread()); | 120 DCHECK(thread_checker_.CalledOnValidThread()); |
| 191 DVLOG(1) << "UkmService::EnableReporting"; | 121 DVLOG(1) << "UkmService::EnableReporting"; |
| 192 if (reporting_service_.reporting_active()) | 122 if (reporting_service_.reporting_active()) |
| 193 return; | 123 return; |
| 194 | 124 |
| 195 for (auto& provider : metrics_providers_) | 125 for (auto& provider : metrics_providers_) |
| 196 provider->OnRecordingEnabled(); | 126 provider->OnRecordingEnabled(); |
| 197 | 127 |
| 198 if (!initialize_started_) | 128 if (!initialize_started_) |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 DCHECK(thread_checker_.CalledOnValidThread()); | 178 DCHECK(thread_checker_.CalledOnValidThread()); |
| 249 if (initialize_complete_) | 179 if (initialize_complete_) |
| 250 BuildAndStoreLog(); | 180 BuildAndStoreLog(); |
| 251 reporting_service_.ukm_log_store()->PersistUnsentLogs(); | 181 reporting_service_.ukm_log_store()->PersistUnsentLogs(); |
| 252 } | 182 } |
| 253 | 183 |
| 254 void UkmService::Purge() { | 184 void UkmService::Purge() { |
| 255 DCHECK(thread_checker_.CalledOnValidThread()); | 185 DCHECK(thread_checker_.CalledOnValidThread()); |
| 256 DVLOG(1) << "UkmService::Purge"; | 186 DVLOG(1) << "UkmService::Purge"; |
| 257 reporting_service_.ukm_log_store()->Purge(); | 187 reporting_service_.ukm_log_store()->Purge(); |
| 258 sources_.clear(); | 188 UkmRecorderImpl::Purge(); |
| 259 entries_.clear(); | |
| 260 } | 189 } |
| 261 | 190 |
| 262 // TODO(bmcquade): rename this to something more generic, like | 191 // TODO(bmcquade): rename this to something more generic, like |
| 263 // ResetClientState. Consider resetting all prefs here. | 192 // ResetClientState. Consider resetting all prefs here. |
| 264 void UkmService::ResetClientId() { | 193 void UkmService::ResetClientId() { |
| 265 client_id_ = GenerateClientId(pref_service_); | 194 client_id_ = GenerateClientId(pref_service_); |
| 266 session_id_ = LoadSessionId(pref_service_); | 195 session_id_ = LoadSessionId(pref_service_); |
| 267 } | 196 } |
| 268 | 197 |
| 269 void UkmService::RegisterMetricsProvider( | 198 void UkmService::RegisterMetricsProvider( |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 BuildAndStoreLog(); | 230 BuildAndStoreLog(); |
| 302 reporting_service_.Start(); | 231 reporting_service_.Start(); |
| 303 } | 232 } |
| 304 | 233 |
| 305 void UkmService::BuildAndStoreLog() { | 234 void UkmService::BuildAndStoreLog() { |
| 306 DCHECK(thread_checker_.CalledOnValidThread()); | 235 DCHECK(thread_checker_.CalledOnValidThread()); |
| 307 DVLOG(1) << "UkmService::BuildAndStoreLog"; | 236 DVLOG(1) << "UkmService::BuildAndStoreLog"; |
| 308 | 237 |
| 309 // Suppress generating a log if we have no new data to include. | 238 // Suppress generating a log if we have no new data to include. |
| 310 // TODO(zhenw): add a histogram here to debug if this case is hitting a lot. | 239 // TODO(zhenw): add a histogram here to debug if this case is hitting a lot. |
| 311 if (sources_.empty() && entries_.empty()) | 240 if (sources().empty() && entries().empty()) |
| 312 return; | 241 return; |
| 313 | 242 |
| 314 Report report; | 243 Report report; |
| 315 report.set_client_id(client_id_); | 244 report.set_client_id(client_id_); |
| 316 if (ShouldRecordSessionId()) | 245 if (ShouldRecordSessionId()) |
| 317 report.set_session_id(session_id_); | 246 report.set_session_id(session_id_); |
| 318 | 247 |
| 319 for (const auto& kv : sources_) { | 248 StoreRecordingsInReport(&report); |
| 320 Source* proto_source = report.add_sources(); | |
| 321 kv.second->PopulateProto(proto_source); | |
| 322 if (!ShouldRecordInitialUrl()) | |
| 323 proto_source->clear_initial_url(); | |
| 324 } | |
| 325 for (const auto& entry : entries_) { | |
| 326 Entry* proto_entry = report.add_entries(); | |
| 327 entry->PopulateProto(proto_entry); | |
| 328 } | |
| 329 | |
| 330 UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.SerializedCount", sources_.size()); | |
| 331 UMA_HISTOGRAM_COUNTS_1000("UKM.Entries.SerializedCount", entries_.size()); | |
| 332 sources_.clear(); | |
| 333 entries_.clear(); | |
| 334 | 249 |
| 335 metrics::MetricsLog::RecordCoreSystemProfile(client_, | 250 metrics::MetricsLog::RecordCoreSystemProfile(client_, |
| 336 report.mutable_system_profile()); | 251 report.mutable_system_profile()); |
| 337 | 252 |
| 338 for (auto& provider : metrics_providers_) { | 253 for (auto& provider : metrics_providers_) { |
| 339 provider->ProvideSystemProfileMetrics(report.mutable_system_profile()); | 254 provider->ProvideSystemProfileMetrics(report.mutable_system_profile()); |
| 340 } | 255 } |
| 341 | 256 |
| 342 std::string serialized_log; | 257 std::string serialized_log; |
| 343 report.SerializeToString(&serialized_log); | 258 report.SerializeToString(&serialized_log); |
| 344 reporting_service_.ukm_log_store()->StoreLog(serialized_log); | 259 reporting_service_.ukm_log_store()->StoreLog(serialized_log); |
| 345 } | 260 } |
| 346 | 261 |
| 347 // static | |
| 348 int32_t UkmService::GetNewSourceID() { | |
| 349 static base::StaticAtomicSequenceNumber seq; | |
| 350 return seq.GetNext(); | |
| 351 } | |
| 352 | |
| 353 std::unique_ptr<UkmEntryBuilder> UkmService::GetEntryBuilder( | |
| 354 int32_t source_id, | |
| 355 const char* event_name) { | |
| 356 return std::unique_ptr<UkmEntryBuilder>(new UkmEntryBuilder( | |
| 357 base::Bind(&UkmService::AddEntry, base::Unretained(this)), source_id, | |
| 358 event_name)); | |
| 359 } | |
| 360 | |
| 361 void UkmService::UpdateSourceURL(int32_t source_id, const GURL& url) { | |
| 362 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 363 | |
| 364 if (!recording_enabled_) { | |
| 365 RecordDroppedSource(DroppedDataReason::RECORDING_DISABLED); | |
| 366 return; | |
| 367 } | |
| 368 | |
| 369 // Update the pre-existing source if there is any. This happens when the | |
| 370 // initial URL is different from the committed URL for the same source, e.g., | |
| 371 // when there is redirection. | |
| 372 if (base::ContainsKey(sources_, source_id)) { | |
| 373 sources_[source_id]->UpdateUrl(url); | |
| 374 return; | |
| 375 } | |
| 376 | |
| 377 if (sources_.size() >= GetMaxSources()) { | |
| 378 RecordDroppedSource(DroppedDataReason::MAX_HIT); | |
| 379 return; | |
| 380 } | |
| 381 std::unique_ptr<UkmSource> source = base::MakeUnique<UkmSource>(); | |
| 382 source->set_id(source_id); | |
| 383 source->set_url(url); | |
| 384 sources_.insert(std::make_pair(source_id, std::move(source))); | |
| 385 } | |
| 386 | |
| 387 void UkmService::AddEntry(std::unique_ptr<UkmEntry> entry) { | |
| 388 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 389 | |
| 390 if (!recording_enabled_) { | |
| 391 RecordDroppedEntry(DroppedDataReason::RECORDING_DISABLED); | |
| 392 return; | |
| 393 } | |
| 394 if (entries_.size() >= GetMaxEntries()) { | |
| 395 RecordDroppedEntry(DroppedDataReason::MAX_HIT); | |
| 396 return; | |
| 397 } | |
| 398 | |
| 399 if (!whitelisted_entry_hashes_.empty() && | |
| 400 !base::ContainsKey(whitelisted_entry_hashes_, entry->event_hash())) { | |
| 401 RecordDroppedEntry(DroppedDataReason::NOT_WHITELISTED); | |
| 402 return; | |
| 403 } | |
| 404 | |
| 405 entries_.push_back(std::move(entry)); | |
| 406 } | |
| 407 | |
| 408 void UkmService::StoreWhitelistedEntries() { | |
| 409 const auto entries = | |
| 410 base::SplitString(GetWhitelistEntries(), ",", base::TRIM_WHITESPACE, | |
| 411 base::SPLIT_WANT_NONEMPTY); | |
| 412 for (const auto& entry_string : entries) { | |
| 413 whitelisted_entry_hashes_.insert(base::HashMetricName(entry_string)); | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 } // namespace ukm | 262 } // namespace ukm |
| OLD | NEW |