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 |