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 |