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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
46 const int FieldTrial::kNotFinalized = -1; | 46 const int FieldTrial::kNotFinalized = -1; |
47 const int FieldTrial::kDefaultGroupNumber = 0; | 47 const int FieldTrial::kDefaultGroupNumber = 0; |
48 bool FieldTrial::enable_benchmarking_ = false; | 48 bool FieldTrial::enable_benchmarking_ = false; |
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 EntropyProvider* entropy_provider, |
57 const std::string& name, | |
57 const Probability total_probability, | 58 const Probability total_probability, |
58 const std::string& default_group_name) | 59 const std::string& default_group_name) |
59 : name_(name), | 60 : entropy_provider_(entropy_provider), |
jar (doing other things)
2012/08/17 19:06:19
nit: Please indent the ":" by four spaces.
Alexei Svitkine (slow)
2012/08/20 15:57:40
Done.
| |
61 name_(name), | |
60 divisor_(total_probability), | 62 divisor_(total_probability), |
61 default_group_name_(default_group_name), | 63 default_group_name_(default_group_name), |
62 random_(static_cast<Probability>(divisor_ * RandDouble())), | 64 random_(static_cast<Probability>(divisor_ * RandDouble())), |
63 accumulated_group_probability_(0), | 65 accumulated_group_probability_(0), |
64 next_group_number_(kDefaultGroupNumber + 1), | 66 next_group_number_(kDefaultGroupNumber + 1), |
65 group_(kNotFinalized), | 67 group_(kNotFinalized), |
66 enable_field_trial_(true), | 68 enable_field_trial_(true), |
67 forced_(false) { | 69 forced_(false) { |
68 DCHECK_GT(total_probability, 0); | 70 DCHECK_GT(total_probability, 0); |
69 DCHECK(!name_.empty()); | 71 DCHECK(!name_.empty()); |
70 DCHECK(!default_group_name_.empty()); | 72 DCHECK(!default_group_name_.empty()); |
71 } | 73 } |
72 | 74 |
75 FieldTrial::EntropyProvider::~EntropyProvider() { | |
76 } | |
77 | |
73 void FieldTrial::UseOneTimeRandomization() { | 78 void FieldTrial::UseOneTimeRandomization() { |
74 // No need to specify randomization when the group choice was forced. | 79 // No need to specify randomization when the group choice was forced. |
75 if (forced_) | 80 if (forced_) |
76 return; | 81 return; |
77 DCHECK_EQ(group_, kNotFinalized); | 82 DCHECK_EQ(group_, kNotFinalized); |
78 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); | 83 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); |
79 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) { | 84 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) { |
jar (doing other things)
2012/08/17 19:06:19
This function, could, for example, return an entro
Alexei Svitkine (slow)
2012/08/20 15:57:40
Done.
By having the entropy provider passed in ex
| |
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(FieldTrial::EntropyProvider* entropy_provider) | |
232 : application_start_time_(TimeTicks::Now()), | 219 : application_start_time_(TimeTicks::Now()), |
233 client_id_(client_id), | 220 entropy_provider_(entropy_provider), |
234 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( | 221 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( |
235 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { | 222 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { |
236 DCHECK(!global_); | 223 DCHECK(!global_); |
237 DCHECK(!used_without_global_); | 224 DCHECK(!used_without_global_); |
238 global_ = this; | 225 global_ = this; |
239 | 226 |
240 Time::Exploded exploded; | 227 Time::Exploded exploded; |
241 Time two_years_from_now = | 228 Time two_years_from_now = |
242 Time::NowFromSystemTime() + TimeDelta::FromDays(730); | 229 Time::NowFromSystemTime() + TimeDelta::FromDays(730); |
243 two_years_from_now.LocalExplode(&exploded); | 230 two_years_from_now.LocalExplode(&exploded); |
(...skipping 28 matching lines...) Expand all Loading... | |
272 CHECK(existing_trial->forced_); | 259 CHECK(existing_trial->forced_); |
273 // If the field trial has already been forced, check whether it was forced | 260 // If the field trial has already been forced, check whether it was forced |
274 // to the default group. Return the chosen group number, in that case.. | 261 // to the default group. Return the chosen group number, in that case.. |
275 if (default_group_number && | 262 if (default_group_number && |
276 default_group_name == existing_trial->default_group_name()) { | 263 default_group_name == existing_trial->default_group_name()) { |
277 *default_group_number = existing_trial->group(); | 264 *default_group_number = existing_trial->group(); |
278 } | 265 } |
279 return existing_trial; | 266 return existing_trial; |
280 } | 267 } |
281 | 268 |
282 FieldTrial* field_trial = | 269 FieldTrial* field_trial = new FieldTrial(global_->entropy_provider_.get(), |
283 new FieldTrial(name, total_probability, default_group_name); | 270 name, total_probability, |
271 default_group_name); | |
284 if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month)) | 272 if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month)) |
285 field_trial->Disable(); | 273 field_trial->Disable(); |
286 FieldTrialList::Register(field_trial); | 274 FieldTrialList::Register(field_trial); |
287 return field_trial; | 275 return field_trial; |
288 } | 276 } |
289 | 277 |
290 // static | 278 // static |
291 FieldTrial* FieldTrialList::Find(const std::string& name) { | 279 FieldTrial* FieldTrialList::Find(const std::string& name) { |
292 if (!global_) | 280 if (!global_) |
293 return NULL; | 281 return NULL; |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
392 | 380 |
393 FieldTrial* field_trial = FieldTrialList::Find(name); | 381 FieldTrial* field_trial = FieldTrialList::Find(name); |
394 if (field_trial) { | 382 if (field_trial) { |
395 // In single process mode, or when we force them from the command line, | 383 // In single process mode, or when we force them from the command line, |
396 // we may have already created the field trial. | 384 // we may have already created the field trial. |
397 if (field_trial->group_name_internal() != group_name) | 385 if (field_trial->group_name_internal() != group_name) |
398 return NULL; | 386 return NULL; |
399 return field_trial; | 387 return field_trial; |
400 } | 388 } |
401 const int kTotalProbability = 100; | 389 const int kTotalProbability = 100; |
402 field_trial = new FieldTrial(name, kTotalProbability, group_name); | 390 field_trial = new FieldTrial(global_->entropy_provider_.get(), |
391 name, kTotalProbability, group_name); | |
403 // This is where we may assign a group number different from | 392 // This is where we may assign a group number different from |
404 // kDefaultGroupNumber to the default group. | 393 // kDefaultGroupNumber to the default group. |
405 field_trial->AppendGroup(group_name, kTotalProbability); | 394 field_trial->AppendGroup(group_name, kTotalProbability); |
406 field_trial->forced_ = true; | 395 field_trial->forced_ = true; |
407 FieldTrialList::Register(field_trial); | 396 FieldTrialList::Register(field_trial); |
408 return field_trial; | 397 return field_trial; |
409 } | 398 } |
410 | 399 |
411 // static | 400 // static |
412 void FieldTrialList::AddObserver(Observer* observer) { | 401 void FieldTrialList::AddObserver(Observer* observer) { |
(...skipping 26 matching lines...) Expand all Loading... | |
439 | 428 |
440 // static | 429 // static |
441 size_t FieldTrialList::GetFieldTrialCount() { | 430 size_t FieldTrialList::GetFieldTrialCount() { |
442 if (!global_) | 431 if (!global_) |
443 return 0; | 432 return 0; |
444 AutoLock auto_lock(global_->lock_); | 433 AutoLock auto_lock(global_->lock_); |
445 return global_->registered_.size(); | 434 return global_->registered_.size(); |
446 } | 435 } |
447 | 436 |
448 // static | 437 // static |
449 bool FieldTrialList::IsOneTimeRandomizationEnabled() { | 438 bool FieldTrialList::IsOneTimeRandomizationEnabled() { |
jar (doing other things)
2012/08/17 19:06:19
You might change this to:
GetEntropyProviderForOne
Alexei Svitkine (slow)
2012/08/20 15:57:40
Done.
| |
450 if (!global_) { | 439 if (!global_) { |
451 used_without_global_ = true; | 440 used_without_global_ = true; |
452 return false; | 441 return false; |
453 } | 442 } |
454 | 443 |
455 return !global_->client_id_.empty(); | 444 return global_->entropy_provider_.get() != NULL; |
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 } | 445 } |
466 | 446 |
467 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { | 447 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { |
468 RegistrationList::iterator it = registered_.find(name); | 448 RegistrationList::iterator it = registered_.find(name); |
469 if (registered_.end() == it) | 449 if (registered_.end() == it) |
470 return NULL; | 450 return NULL; |
471 return it->second; | 451 return it->second; |
472 } | 452 } |
473 | 453 |
474 // static | 454 // static |
475 void FieldTrialList::Register(FieldTrial* trial) { | 455 void FieldTrialList::Register(FieldTrial* trial) { |
476 if (!global_) { | 456 if (!global_) { |
477 used_without_global_ = true; | 457 used_without_global_ = true; |
478 return; | 458 return; |
479 } | 459 } |
480 AutoLock auto_lock(global_->lock_); | 460 AutoLock auto_lock(global_->lock_); |
481 DCHECK(!global_->PreLockedFind(trial->name())); | 461 DCHECK(!global_->PreLockedFind(trial->name())); |
482 trial->AddRef(); | 462 trial->AddRef(); |
483 global_->registered_[trial->name()] = trial; | 463 global_->registered_[trial->name()] = trial; |
484 } | 464 } |
485 | 465 |
486 } // namespace base | 466 } // namespace base |
OLD | NEW |