OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "base/metrics/field_trial.h" | 5 #include "base/metrics/field_trial.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/build_time.h" | 9 #include "base/build_time.h" |
10 #include "base/lazy_instance.h" | |
11 #include "base/logging.h" | 10 #include "base/logging.h" |
12 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
13 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
14 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
15 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
16 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
17 | 16 |
18 namespace base { | 17 namespace base { |
19 | 18 |
20 namespace { | 19 namespace { |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 trials_string_piece.substr(next_item, name_end - next_item); | 99 trials_string_piece.substr(next_item, name_end - next_item); |
101 entry.group_name = | 100 entry.group_name = |
102 trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1); | 101 trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1); |
103 next_item = group_name_end + 1; | 102 next_item = group_name_end + 1; |
104 | 103 |
105 entries->push_back(entry); | 104 entries->push_back(entry); |
106 } | 105 } |
107 return true; | 106 return true; |
108 } | 107 } |
109 | 108 |
110 void CheckTrialGroup(const std::string& trial_name, | |
111 const std::string& trial_group, | |
112 std::map<std::string, std::string>* seen_states) { | |
113 if (ContainsKey(*seen_states, trial_name)) { | |
114 CHECK_EQ((*seen_states)[trial_name], trial_group) << trial_name; | |
115 } else { | |
116 (*seen_states)[trial_name] = trial_group; | |
117 } | |
118 } | |
119 | |
120 // A second copy of FieldTrialList::seen_states_ that is meant to outlive the | |
121 // FieldTrialList object to determine if the inconsistency happens because there | |
122 // might be multiple FieldTrialList objects. | |
123 // TODO(asvitkine): Remove when crbug.com/359406 is resolved. | |
124 base::LazyInstance<std::map<std::string, std::string>>::Leaky g_seen_states = | |
125 LAZY_INSTANCE_INITIALIZER; | |
126 | |
127 // A debug token generated during FieldTrialList construction. Used to diagnose | |
128 // crbug.com/359406. | |
129 // TODO(asvitkine): Remove when crbug.com/359406 is resolved. | |
130 int32_t g_debug_token = -1; | |
131 | |
132 // Whether to append the debug token to the child process --force-fieldtrials | |
133 // command line. Used to diagnose crbug.com/359406. | |
134 // TODO(asvitkine): Remove when crbug.com/359406 is resolved. | |
135 bool g_append_debug_token_to_trial_string = false; | |
136 | |
137 // Tracks whether |g_seen_states| is used. Defaults to false, because unit tests | |
138 // will create multiple FieldTrialList instances. Also controls whether | |
139 // |g_debug_token| is included in the field trial state string. | |
140 bool g_use_global_check_states = false; | |
141 | |
142 } // namespace | 109 } // namespace |
143 | 110 |
144 // statics | 111 // statics |
145 const int FieldTrial::kNotFinalized = -1; | 112 const int FieldTrial::kNotFinalized = -1; |
146 const int FieldTrial::kDefaultGroupNumber = 0; | 113 const int FieldTrial::kDefaultGroupNumber = 0; |
147 bool FieldTrial::enable_benchmarking_ = false; | 114 bool FieldTrial::enable_benchmarking_ = false; |
148 | 115 |
149 int FieldTrialList::kNoExpirationYear = 0; | 116 int FieldTrialList::kNoExpirationYear = 0; |
150 | 117 |
151 //------------------------------------------------------------------------------ | 118 //------------------------------------------------------------------------------ |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 if (forced_) | 202 if (forced_) |
236 return; | 203 return; |
237 | 204 |
238 // And we must finalize the group choice before we mark ourselves as forced. | 205 // And we must finalize the group choice before we mark ourselves as forced. |
239 FinalizeGroupChoice(); | 206 FinalizeGroupChoice(); |
240 forced_ = true; | 207 forced_ = true; |
241 } | 208 } |
242 | 209 |
243 // static | 210 // static |
244 void FieldTrial::EnableBenchmarking() { | 211 void FieldTrial::EnableBenchmarking() { |
245 // TODO(asvitkine): Change this back to 0u after the trial in FieldTrialList | 212 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); |
246 // constructor is removed. | |
247 DCHECK_EQ(1u, FieldTrialList::GetFieldTrialCount()); | |
248 enable_benchmarking_ = true; | 213 enable_benchmarking_ = true; |
249 } | 214 } |
250 | 215 |
251 // static | 216 // static |
252 FieldTrial* FieldTrial::CreateSimulatedFieldTrial( | 217 FieldTrial* FieldTrial::CreateSimulatedFieldTrial( |
253 const std::string& trial_name, | 218 const std::string& trial_name, |
254 Probability total_probability, | 219 Probability total_probability, |
255 const std::string& default_group_name, | 220 const std::string& default_group_name, |
256 double entropy_value) { | 221 double entropy_value) { |
257 return new FieldTrial(trial_name, total_probability, default_group_name, | 222 return new FieldTrial(trial_name, total_probability, default_group_name, |
(...skipping 11 matching lines...) Expand all Loading... |
269 accumulated_group_probability_(0), | 234 accumulated_group_probability_(0), |
270 next_group_number_(kDefaultGroupNumber + 1), | 235 next_group_number_(kDefaultGroupNumber + 1), |
271 group_(kNotFinalized), | 236 group_(kNotFinalized), |
272 enable_field_trial_(true), | 237 enable_field_trial_(true), |
273 forced_(false), | 238 forced_(false), |
274 group_reported_(false), | 239 group_reported_(false), |
275 trial_registered_(false) { | 240 trial_registered_(false) { |
276 DCHECK_GT(total_probability, 0); | 241 DCHECK_GT(total_probability, 0); |
277 DCHECK(!trial_name_.empty()); | 242 DCHECK(!trial_name_.empty()); |
278 DCHECK(!default_group_name_.empty()); | 243 DCHECK(!default_group_name_.empty()); |
279 | |
280 if (g_debug_token == -1) | |
281 g_debug_token = RandInt(1, INT32_MAX); | |
282 } | 244 } |
283 | 245 |
284 FieldTrial::~FieldTrial() {} | 246 FieldTrial::~FieldTrial() {} |
285 | 247 |
286 void FieldTrial::SetTrialRegistered() { | 248 void FieldTrial::SetTrialRegistered() { |
287 DCHECK_EQ(kNotFinalized, group_); | 249 DCHECK_EQ(kNotFinalized, group_); |
288 DCHECK(!trial_registered_); | 250 DCHECK(!trial_registered_); |
289 trial_registered_ = true; | 251 trial_registered_ = true; |
290 } | 252 } |
291 | 253 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 bool FieldTrialList::used_without_global_ = false; | 299 bool FieldTrialList::used_without_global_ = false; |
338 | 300 |
339 FieldTrialList::Observer::~Observer() { | 301 FieldTrialList::Observer::~Observer() { |
340 } | 302 } |
341 | 303 |
342 FieldTrialList::FieldTrialList( | 304 FieldTrialList::FieldTrialList( |
343 const FieldTrial::EntropyProvider* entropy_provider) | 305 const FieldTrial::EntropyProvider* entropy_provider) |
344 : entropy_provider_(entropy_provider), | 306 : entropy_provider_(entropy_provider), |
345 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( | 307 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( |
346 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { | 308 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { |
347 // TODO(asvitkine): Turn into a DCHECK after http://crbug.com/359406 is fixed. | 309 DCHECK(!global_); |
348 CHECK(!global_); | |
349 DCHECK(!used_without_global_); | 310 DCHECK(!used_without_global_); |
350 global_ = this; | 311 global_ = this; |
351 | 312 |
352 Time two_years_from_build_time = GetBuildTime() + TimeDelta::FromDays(730); | 313 Time two_years_from_build_time = GetBuildTime() + TimeDelta::FromDays(730); |
353 Time::Exploded exploded; | 314 Time::Exploded exploded; |
354 two_years_from_build_time.LocalExplode(&exploded); | 315 two_years_from_build_time.LocalExplode(&exploded); |
355 kNoExpirationYear = exploded.year; | 316 kNoExpirationYear = exploded.year; |
356 | |
357 // Run a 50/50 experiment that enables |g_use_global_check_states| only for | |
358 // half the users, to investigate if this instrumentation is causing the | |
359 // crashes to disappear for http://crbug.com/359406. Done here instead of a | |
360 // server-side trial because this needs to be done early during FieldTrialList | |
361 // initialization. | |
362 // | |
363 // Note: |g_use_global_check_states| is set via EnableGlobalStateChecks() | |
364 // prior to the FieldTrialList being created. We only want to do the trial | |
365 // check once the first time FieldTrialList is created, so use a static | |
366 // |first_time| variable to track this. | |
367 // | |
368 // TODO(asvitkine): Remove after http://crbug.com/359406 is fixed. | |
369 static bool first_time = true; | |
370 if (first_time && g_use_global_check_states) { | |
371 first_time = false; | |
372 base::FieldTrial* trial = | |
373 FactoryGetFieldTrial("UMA_CheckStates", 100, "NoChecks", | |
374 kNoExpirationYear, 1, 1, | |
375 FieldTrial::SESSION_RANDOMIZED, nullptr); | |
376 trial->AppendGroup("Checks", 50); | |
377 if (trial->group_name() == "NoChecks") | |
378 g_use_global_check_states = false; | |
379 } | |
380 } | 317 } |
381 | 318 |
382 FieldTrialList::~FieldTrialList() { | 319 FieldTrialList::~FieldTrialList() { |
383 AutoLock auto_lock(lock_); | 320 AutoLock auto_lock(lock_); |
384 while (!registered_.empty()) { | 321 while (!registered_.empty()) { |
385 RegistrationMap::iterator it = registered_.begin(); | 322 RegistrationMap::iterator it = registered_.begin(); |
386 it->second->Release(); | 323 it->second->Release(); |
387 registered_.erase(it->first); | 324 registered_.erase(it->first); |
388 } | 325 } |
389 DCHECK_EQ(this, global_); | 326 DCHECK_EQ(this, global_); |
390 global_ = NULL; | 327 global_ = NULL; |
391 } | 328 } |
392 | 329 |
393 // static | 330 // static |
394 void FieldTrialList::EnableGlobalStateChecks() { | |
395 CHECK(!g_use_global_check_states); | |
396 g_use_global_check_states = true; | |
397 g_append_debug_token_to_trial_string = true; | |
398 } | |
399 | |
400 // static | |
401 int32_t FieldTrialList::GetDebugToken() { | |
402 return g_debug_token; | |
403 } | |
404 | |
405 // static | |
406 FieldTrial* FieldTrialList::FactoryGetFieldTrial( | 331 FieldTrial* FieldTrialList::FactoryGetFieldTrial( |
407 const std::string& trial_name, | 332 const std::string& trial_name, |
408 FieldTrial::Probability total_probability, | 333 FieldTrial::Probability total_probability, |
409 const std::string& default_group_name, | 334 const std::string& default_group_name, |
410 const int year, | 335 const int year, |
411 const int month, | 336 const int month, |
412 const int day_of_month, | 337 const int day_of_month, |
413 FieldTrial::RandomizationType randomization_type, | 338 FieldTrial::RandomizationType randomization_type, |
414 int* default_group_number) { | 339 int* default_group_number) { |
415 return FactoryGetFieldTrialWithRandomizationSeed( | 340 return FactoryGetFieldTrialWithRandomizationSeed( |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
527 it != active_groups.end(); ++it) { | 452 it != active_groups.end(); ++it) { |
528 DCHECK_EQ(std::string::npos, | 453 DCHECK_EQ(std::string::npos, |
529 it->trial_name.find(kPersistentStringSeparator)); | 454 it->trial_name.find(kPersistentStringSeparator)); |
530 DCHECK_EQ(std::string::npos, | 455 DCHECK_EQ(std::string::npos, |
531 it->group_name.find(kPersistentStringSeparator)); | 456 it->group_name.find(kPersistentStringSeparator)); |
532 output->append(it->trial_name); | 457 output->append(it->trial_name); |
533 output->append(1, kPersistentStringSeparator); | 458 output->append(1, kPersistentStringSeparator); |
534 output->append(it->group_name); | 459 output->append(it->group_name); |
535 output->append(1, kPersistentStringSeparator); | 460 output->append(1, kPersistentStringSeparator); |
536 } | 461 } |
537 if (g_append_debug_token_to_trial_string) { | |
538 output->append("DebugToken"); | |
539 output->append(1, kPersistentStringSeparator); | |
540 output->append(IntToString(g_debug_token)); | |
541 output->append(1, kPersistentStringSeparator); | |
542 } | |
543 } | 462 } |
544 | 463 |
545 // static | 464 // static |
546 void FieldTrialList::AllStatesToString(std::string* output) { | 465 void FieldTrialList::AllStatesToString(std::string* output) { |
547 if (!global_) | 466 if (!global_) |
548 return; | 467 return; |
549 AutoLock auto_lock(global_->lock_); | 468 AutoLock auto_lock(global_->lock_); |
550 | 469 |
551 for (const auto& registered : global_->registered_) { | 470 for (const auto& registered : global_->registered_) { |
552 FieldTrial::State trial; | 471 FieldTrial::State trial; |
553 if (!registered.second->GetState(&trial)) | 472 if (!registered.second->GetState(&trial)) |
554 continue; | 473 continue; |
555 DCHECK_EQ(std::string::npos, | 474 DCHECK_EQ(std::string::npos, |
556 trial.trial_name.find(kPersistentStringSeparator)); | 475 trial.trial_name.find(kPersistentStringSeparator)); |
557 DCHECK_EQ(std::string::npos, | 476 DCHECK_EQ(std::string::npos, |
558 trial.group_name.find(kPersistentStringSeparator)); | 477 trial.group_name.find(kPersistentStringSeparator)); |
559 if (trial.activated) | 478 if (trial.activated) |
560 output->append(1, kActivationMarker); | 479 output->append(1, kActivationMarker); |
561 trial.trial_name.AppendToString(output); | 480 trial.trial_name.AppendToString(output); |
562 output->append(1, kPersistentStringSeparator); | 481 output->append(1, kPersistentStringSeparator); |
563 trial.group_name.AppendToString(output); | 482 trial.group_name.AppendToString(output); |
564 output->append(1, kPersistentStringSeparator); | 483 output->append(1, kPersistentStringSeparator); |
565 | |
566 // TODO(asvitkine): Remove these when http://crbug.com/359406 is fixed. | |
567 CheckTrialGroup(trial.trial_name.as_string(), trial.group_name.as_string(), | |
568 &global_->seen_states_); | |
569 if (g_use_global_check_states) { | |
570 CheckTrialGroup(trial.trial_name.as_string(), | |
571 trial.group_name.as_string(), &g_seen_states.Get()); | |
572 } | |
573 } | 484 } |
574 } | 485 } |
575 | 486 |
576 // static | 487 // static |
577 void FieldTrialList::GetActiveFieldTrialGroups( | 488 void FieldTrialList::GetActiveFieldTrialGroups( |
578 FieldTrial::ActiveGroups* active_groups) { | 489 FieldTrial::ActiveGroups* active_groups) { |
579 DCHECK(active_groups->empty()); | 490 DCHECK(active_groups->empty()); |
580 if (!global_) | 491 if (!global_) |
581 return; | 492 return; |
582 AutoLock auto_lock(global_->lock_); | 493 AutoLock auto_lock(global_->lock_); |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
687 { | 598 { |
688 AutoLock auto_lock(global_->lock_); | 599 AutoLock auto_lock(global_->lock_); |
689 if (field_trial->group_reported_) | 600 if (field_trial->group_reported_) |
690 return; | 601 return; |
691 field_trial->group_reported_ = true; | 602 field_trial->group_reported_ = true; |
692 } | 603 } |
693 | 604 |
694 if (!field_trial->enable_field_trial_) | 605 if (!field_trial->enable_field_trial_) |
695 return; | 606 return; |
696 | 607 |
697 // TODO(asvitkine): Remove this block when http://crbug.com/359406 is fixed. | |
698 { | |
699 AutoLock auto_lock(global_->lock_); | |
700 CheckTrialGroup(field_trial->trial_name(), | |
701 field_trial->group_name_internal(), &global_->seen_states_); | |
702 if (g_use_global_check_states) { | |
703 CheckTrialGroup(field_trial->trial_name(), | |
704 field_trial->group_name_internal(), &g_seen_states.Get()); | |
705 } | |
706 } | |
707 global_->observer_list_->Notify( | 608 global_->observer_list_->Notify( |
708 FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, | 609 FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, |
709 field_trial->trial_name(), field_trial->group_name_internal()); | 610 field_trial->trial_name(), field_trial->group_name_internal()); |
710 } | 611 } |
711 | 612 |
712 // static | 613 // static |
713 size_t FieldTrialList::GetFieldTrialCount() { | 614 size_t FieldTrialList::GetFieldTrialCount() { |
714 if (!global_) | 615 if (!global_) |
715 return 0; | 616 return 0; |
716 AutoLock auto_lock(global_->lock_); | 617 AutoLock auto_lock(global_->lock_); |
(...skipping 25 matching lines...) Expand all Loading... |
742 return; | 643 return; |
743 } | 644 } |
744 AutoLock auto_lock(global_->lock_); | 645 AutoLock auto_lock(global_->lock_); |
745 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 646 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
746 trial->AddRef(); | 647 trial->AddRef(); |
747 trial->SetTrialRegistered(); | 648 trial->SetTrialRegistered(); |
748 global_->registered_[trial->trial_name()] = trial; | 649 global_->registered_[trial->trial_name()] = trial; |
749 } | 650 } |
750 | 651 |
751 } // namespace base | 652 } // namespace base |
OLD | NEW |