Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(403)

Side by Side Diff: base/metrics/field_trial.cc

Issue 10830318: Use a different algorithm with the low entropy source for field trials. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698