| 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 |