| 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 "base/build_time.h" | 7 #include "base/build_time.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/rand_util.h" | 9 #include "base/rand_util.h" |
| 10 #include "base/sha1.h" | 10 #include "base/sha1.h" |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 | 49 |
| 50 const char FieldTrialList::kPersistentStringSeparator('/'); | 50 const char FieldTrialList::kPersistentStringSeparator('/'); |
| 51 int FieldTrialList::kExpirationYearInFuture = 0; | 51 int FieldTrialList::kExpirationYearInFuture = 0; |
| 52 | 52 |
| 53 //------------------------------------------------------------------------------ | 53 //------------------------------------------------------------------------------ |
| 54 // FieldTrial methods and members. | 54 // FieldTrial methods and members. |
| 55 | 55 |
| 56 FieldTrial::FieldTrial(const std::string& name, | 56 FieldTrial::FieldTrial(const std::string& name, |
| 57 const Probability total_probability, | 57 const Probability total_probability, |
| 58 const std::string& default_group_name) | 58 const std::string& default_group_name) |
| 59 : name_(name), | 59 : name_(name), |
| 60 divisor_(total_probability), | 60 divisor_(total_probability), |
| 61 default_group_name_(default_group_name), | 61 default_group_name_(default_group_name), |
| 62 random_(static_cast<Probability>(divisor_ * RandDouble())), | 62 random_(static_cast<Probability>(divisor_ * RandDouble())), |
| 63 accumulated_group_probability_(0), | 63 accumulated_group_probability_(0), |
| 64 next_group_number_(kDefaultGroupNumber + 1), | 64 next_group_number_(kDefaultGroupNumber + 1), |
| 65 group_(kNotFinalized), | 65 group_(kNotFinalized), |
| 66 enable_field_trial_(true), | 66 enable_field_trial_(true), |
| 67 forced_(false) { | 67 forced_(false) { |
| 68 DCHECK_GT(total_probability, 0); | 68 DCHECK_GT(total_probability, 0); |
| 69 DCHECK(!name_.empty()); | 69 DCHECK(!name_.empty()); |
| 70 DCHECK(!default_group_name_.empty()); | 70 DCHECK(!default_group_name_.empty()); |
| 71 } | 71 } |
| 72 | 72 |
| 73 FieldTrial::EntropyProvider::~EntropyProvider() { |
| 74 } |
| 75 |
| 73 void FieldTrial::UseOneTimeRandomization() { | 76 void FieldTrial::UseOneTimeRandomization() { |
| 74 // No need to specify randomization when the group choice was forced. | 77 // No need to specify randomization when the group choice was forced. |
| 75 if (forced_) | 78 if (forced_) |
| 76 return; | 79 return; |
| 77 DCHECK_EQ(group_, kNotFinalized); | 80 DCHECK_EQ(group_, kNotFinalized); |
| 78 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); | 81 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); |
| 79 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) { | 82 const EntropyProvider* entropy_provider = |
| 83 FieldTrialList::GetEntropyProviderForOneTimeRandomization(); |
| 84 if (!entropy_provider) { |
| 80 NOTREACHED(); | 85 NOTREACHED(); |
| 81 Disable(); | 86 Disable(); |
| 82 return; | 87 return; |
| 83 } | 88 } |
| 84 | 89 |
| 85 random_ = static_cast<Probability>( | 90 random_ = static_cast<Probability>( |
| 86 divisor_ * HashClientId(FieldTrialList::client_id(), name_)); | 91 divisor_ * entropy_provider->GetEntropyForTrial(name_)); |
| 87 } | 92 } |
| 88 | 93 |
| 89 void FieldTrial::Disable() { | 94 void FieldTrial::Disable() { |
| 90 enable_field_trial_ = false; | 95 enable_field_trial_ = false; |
| 91 | 96 |
| 92 // In case we are disabled after initialization, we need to switch | 97 // In case we are disabled after initialization, we need to switch |
| 93 // the trial to the default group. | 98 // the trial to the default group. |
| 94 if (group_ != kNotFinalized) { | 99 if (group_ != kNotFinalized) { |
| 95 // Only reset when not already the default group, because in case we were | 100 // Only reset when not already the default group, because in case we were |
| 96 // forced to the default group, the group number may not be | 101 // forced to the default group, the group number may not be |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 | 196 |
| 192 void FieldTrial::SetGroupChoice(const std::string& name, int number) { | 197 void FieldTrial::SetGroupChoice(const std::string& name, int number) { |
| 193 group_ = number; | 198 group_ = number; |
| 194 if (name.empty()) | 199 if (name.empty()) |
| 195 StringAppendF(&group_name_, "%d", group_); | 200 StringAppendF(&group_name_, "%d", group_); |
| 196 else | 201 else |
| 197 group_name_ = name; | 202 group_name_ = name; |
| 198 DVLOG(1) << "Field trial: " << name_ << " Group choice:" << group_name_; | 203 DVLOG(1) << "Field trial: " << name_ << " Group choice:" << group_name_; |
| 199 } | 204 } |
| 200 | 205 |
| 201 // static | |
| 202 double FieldTrial::HashClientId(const std::string& client_id, | |
| 203 const std::string& trial_name) { | |
| 204 // SHA-1 is designed to produce a uniformly random spread in its output space, | |
| 205 // even for nearly-identical inputs, so it helps massage whatever client_id | |
| 206 // and trial_name we get into something with a uniform distribution, which | |
| 207 // is desirable so that we don't skew any part of the 0-100% spectrum. | |
| 208 std::string input(client_id + trial_name); | |
| 209 unsigned char sha1_hash[kSHA1Length]; | |
| 210 SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), | |
| 211 input.size(), | |
| 212 sha1_hash); | |
| 213 | |
| 214 COMPILE_ASSERT(sizeof(uint64) < sizeof(sha1_hash), need_more_data); | |
| 215 uint64 bits; | |
| 216 memcpy(&bits, sha1_hash, sizeof(bits)); | |
| 217 bits = base::ByteSwapToLE64(bits); | |
| 218 | |
| 219 return BitsToOpenEndedUnitInterval(bits); | |
| 220 } | |
| 221 | |
| 222 //------------------------------------------------------------------------------ | 206 //------------------------------------------------------------------------------ |
| 223 // FieldTrialList methods and members. | 207 // FieldTrialList methods and members. |
| 224 | 208 |
| 225 // static | 209 // static |
| 226 FieldTrialList* FieldTrialList::global_ = NULL; | 210 FieldTrialList* FieldTrialList::global_ = NULL; |
| 227 | 211 |
| 228 // static | 212 // static |
| 229 bool FieldTrialList::used_without_global_ = false; | 213 bool FieldTrialList::used_without_global_ = false; |
| 230 | 214 |
| 231 FieldTrialList::FieldTrialList(const std::string& client_id) | 215 FieldTrialList::Observer::~Observer() { |
| 216 } |
| 217 |
| 218 FieldTrialList::FieldTrialList( |
| 219 const FieldTrial::EntropyProvider* entropy_provider) |
| 232 : application_start_time_(TimeTicks::Now()), | 220 : application_start_time_(TimeTicks::Now()), |
| 233 client_id_(client_id), | 221 entropy_provider_(entropy_provider), |
| 234 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( | 222 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( |
| 235 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { | 223 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { |
| 236 DCHECK(!global_); | 224 DCHECK(!global_); |
| 237 DCHECK(!used_without_global_); | 225 DCHECK(!used_without_global_); |
| 238 global_ = this; | 226 global_ = this; |
| 239 | 227 |
| 240 Time::Exploded exploded; | 228 Time::Exploded exploded; |
| 241 Time two_years_from_now = | 229 Time two_years_from_now = |
| 242 Time::NowFromSystemTime() + TimeDelta::FromDays(730); | 230 Time::NowFromSystemTime() + TimeDelta::FromDays(730); |
| 243 two_years_from_now.LocalExplode(&exploded); | 231 two_years_from_now.LocalExplode(&exploded); |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 439 | 427 |
| 440 // static | 428 // static |
| 441 size_t FieldTrialList::GetFieldTrialCount() { | 429 size_t FieldTrialList::GetFieldTrialCount() { |
| 442 if (!global_) | 430 if (!global_) |
| 443 return 0; | 431 return 0; |
| 444 AutoLock auto_lock(global_->lock_); | 432 AutoLock auto_lock(global_->lock_); |
| 445 return global_->registered_.size(); | 433 return global_->registered_.size(); |
| 446 } | 434 } |
| 447 | 435 |
| 448 // static | 436 // static |
| 437 const FieldTrial::EntropyProvider* |
| 438 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { |
| 439 if (!global_) { |
| 440 used_without_global_ = true; |
| 441 return NULL; |
| 442 } |
| 443 |
| 444 return global_->entropy_provider_.get(); |
| 445 } |
| 446 |
| 447 // static |
| 449 bool FieldTrialList::IsOneTimeRandomizationEnabled() { | 448 bool FieldTrialList::IsOneTimeRandomizationEnabled() { |
| 450 if (!global_) { | 449 return GetEntropyProviderForOneTimeRandomization() != NULL; |
| 451 used_without_global_ = true; | |
| 452 return false; | |
| 453 } | |
| 454 | |
| 455 return !global_->client_id_.empty(); | |
| 456 } | |
| 457 | |
| 458 // static | |
| 459 const std::string& FieldTrialList::client_id() { | |
| 460 DCHECK(global_); | |
| 461 if (!global_) | |
| 462 return EmptyString(); | |
| 463 | |
| 464 return global_->client_id_; | |
| 465 } | 450 } |
| 466 | 451 |
| 467 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { | 452 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { |
| 468 RegistrationList::iterator it = registered_.find(name); | 453 RegistrationList::iterator it = registered_.find(name); |
| 469 if (registered_.end() == it) | 454 if (registered_.end() == it) |
| 470 return NULL; | 455 return NULL; |
| 471 return it->second; | 456 return it->second; |
| 472 } | 457 } |
| 473 | 458 |
| 474 // static | 459 // static |
| 475 void FieldTrialList::Register(FieldTrial* trial) { | 460 void FieldTrialList::Register(FieldTrial* trial) { |
| 476 if (!global_) { | 461 if (!global_) { |
| 477 used_without_global_ = true; | 462 used_without_global_ = true; |
| 478 return; | 463 return; |
| 479 } | 464 } |
| 480 AutoLock auto_lock(global_->lock_); | 465 AutoLock auto_lock(global_->lock_); |
| 481 DCHECK(!global_->PreLockedFind(trial->name())); | 466 DCHECK(!global_->PreLockedFind(trial->name())); |
| 482 trial->AddRef(); | 467 trial->AddRef(); |
| 483 global_->registered_[trial->name()] = trial; | 468 global_->registered_[trial->name()] = trial; |
| 484 } | 469 } |
| 485 | 470 |
| 486 } // namespace base | 471 } // namespace base |
| OLD | NEW |