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

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(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),
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
73 void FieldTrial::UseOneTimeRandomization() { 75 void FieldTrial::UseOneTimeRandomization() {
74 // No need to specify randomization when the group choice was forced. 76 // No need to specify randomization when the group choice was forced.
75 if (forced_) 77 if (forced_)
76 return; 78 return;
77 DCHECK_EQ(group_, kNotFinalized); 79 DCHECK_EQ(group_, kNotFinalized);
78 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); 80 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_);
79 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) { 81 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) {
80 NOTREACHED(); 82 NOTREACHED();
81 Disable(); 83 Disable();
82 return; 84 return;
83 } 85 }
84 86
85 random_ = static_cast<Probability>( 87 random_ = static_cast<Probability>(
86 divisor_ * HashClientId(FieldTrialList::client_id(), name_)); 88 divisor_ * entropy_provider_->GetEntropyForTrial(name_));
87 } 89 }
88 90
89 void FieldTrial::Disable() { 91 void FieldTrial::Disable() {
90 enable_field_trial_ = false; 92 enable_field_trial_ = false;
91 93
92 // In case we are disabled after initialization, we need to switch 94 // In case we are disabled after initialization, we need to switch
93 // the trial to the default group. 95 // the trial to the default group.
94 if (group_ != kNotFinalized) { 96 if (group_ != kNotFinalized) {
95 // Only reset when not already the default group, because in case we were 97 // 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 98 // forced to the default group, the group number may not be
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 193
192 void FieldTrial::SetGroupChoice(const std::string& name, int number) { 194 void FieldTrial::SetGroupChoice(const std::string& name, int number) {
193 group_ = number; 195 group_ = number;
194 if (name.empty()) 196 if (name.empty())
195 StringAppendF(&group_name_, "%d", group_); 197 StringAppendF(&group_name_, "%d", group_);
196 else 198 else
197 group_name_ = name; 199 group_name_ = name;
198 DVLOG(1) << "Field trial: " << name_ << " Group choice:" << group_name_; 200 DVLOG(1) << "Field trial: " << name_ << " Group choice:" << group_name_;
199 } 201 }
200 202
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 //------------------------------------------------------------------------------ 203 //------------------------------------------------------------------------------
223 // FieldTrialList methods and members. 204 // FieldTrialList methods and members.
224 205
225 // static 206 // static
226 FieldTrialList* FieldTrialList::global_ = NULL; 207 FieldTrialList* FieldTrialList::global_ = NULL;
227 208
228 // static 209 // static
229 bool FieldTrialList::used_without_global_ = false; 210 bool FieldTrialList::used_without_global_ = false;
230 211
231 FieldTrialList::FieldTrialList(const std::string& client_id) 212 FieldTrialList::FieldTrialList(FieldTrial::EntropyProvider* entropy_provider)
232 : application_start_time_(TimeTicks::Now()), 213 : application_start_time_(TimeTicks::Now()),
233 client_id_(client_id), 214 entropy_provider_(entropy_provider),
234 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( 215 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>(
235 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { 216 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) {
236 DCHECK(!global_); 217 DCHECK(!global_);
237 DCHECK(!used_without_global_); 218 DCHECK(!used_without_global_);
238 global_ = this; 219 global_ = this;
239 220
240 Time::Exploded exploded; 221 Time::Exploded exploded;
241 Time two_years_from_now = 222 Time two_years_from_now =
242 Time::NowFromSystemTime() + TimeDelta::FromDays(730); 223 Time::NowFromSystemTime() + TimeDelta::FromDays(730);
243 two_years_from_now.LocalExplode(&exploded); 224 two_years_from_now.LocalExplode(&exploded);
(...skipping 28 matching lines...) Expand all
272 CHECK(existing_trial->forced_); 253 CHECK(existing_trial->forced_);
273 // If the field trial has already been forced, check whether it was forced 254 // 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.. 255 // to the default group. Return the chosen group number, in that case..
275 if (default_group_number && 256 if (default_group_number &&
276 default_group_name == existing_trial->default_group_name()) { 257 default_group_name == existing_trial->default_group_name()) {
277 *default_group_number = existing_trial->group(); 258 *default_group_number = existing_trial->group();
278 } 259 }
279 return existing_trial; 260 return existing_trial;
280 } 261 }
281 262
282 FieldTrial* field_trial = 263 FieldTrial* field_trial = new FieldTrial(global_->entropy_provider_.get(),
283 new FieldTrial(name, total_probability, default_group_name); 264 name, total_probability,
265 default_group_name);
284 if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month)) 266 if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month))
285 field_trial->Disable(); 267 field_trial->Disable();
286 FieldTrialList::Register(field_trial); 268 FieldTrialList::Register(field_trial);
287 return field_trial; 269 return field_trial;
288 } 270 }
289 271
290 // static 272 // static
291 FieldTrial* FieldTrialList::Find(const std::string& name) { 273 FieldTrial* FieldTrialList::Find(const std::string& name) {
292 if (!global_) 274 if (!global_)
293 return NULL; 275 return NULL;
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 374
393 FieldTrial* field_trial = FieldTrialList::Find(name); 375 FieldTrial* field_trial = FieldTrialList::Find(name);
394 if (field_trial) { 376 if (field_trial) {
395 // In single process mode, or when we force them from the command line, 377 // In single process mode, or when we force them from the command line,
396 // we may have already created the field trial. 378 // we may have already created the field trial.
397 if (field_trial->group_name_internal() != group_name) 379 if (field_trial->group_name_internal() != group_name)
398 return NULL; 380 return NULL;
399 return field_trial; 381 return field_trial;
400 } 382 }
401 const int kTotalProbability = 100; 383 const int kTotalProbability = 100;
402 field_trial = new FieldTrial(name, kTotalProbability, group_name); 384 field_trial = new FieldTrial(global_->entropy_provider_.get(),
385 name, kTotalProbability, group_name);
403 // This is where we may assign a group number different from 386 // This is where we may assign a group number different from
404 // kDefaultGroupNumber to the default group. 387 // kDefaultGroupNumber to the default group.
405 field_trial->AppendGroup(group_name, kTotalProbability); 388 field_trial->AppendGroup(group_name, kTotalProbability);
406 field_trial->forced_ = true; 389 field_trial->forced_ = true;
407 FieldTrialList::Register(field_trial); 390 FieldTrialList::Register(field_trial);
408 return field_trial; 391 return field_trial;
409 } 392 }
410 393
411 // static 394 // static
412 void FieldTrialList::AddObserver(Observer* observer) { 395 void FieldTrialList::AddObserver(Observer* observer) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 return global_->registered_.size(); 428 return global_->registered_.size();
446 } 429 }
447 430
448 // static 431 // static
449 bool FieldTrialList::IsOneTimeRandomizationEnabled() { 432 bool FieldTrialList::IsOneTimeRandomizationEnabled() {
450 if (!global_) { 433 if (!global_) {
451 used_without_global_ = true; 434 used_without_global_ = true;
452 return false; 435 return false;
453 } 436 }
454 437
455 return !global_->client_id_.empty(); 438 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 } 439 }
466 440
467 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 441 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
468 RegistrationList::iterator it = registered_.find(name); 442 RegistrationList::iterator it = registered_.find(name);
469 if (registered_.end() == it) 443 if (registered_.end() == it)
470 return NULL; 444 return NULL;
471 return it->second; 445 return it->second;
472 } 446 }
473 447
474 // static 448 // static
475 void FieldTrialList::Register(FieldTrial* trial) { 449 void FieldTrialList::Register(FieldTrial* trial) {
476 if (!global_) { 450 if (!global_) {
477 used_without_global_ = true; 451 used_without_global_ = true;
478 return; 452 return;
479 } 453 }
480 AutoLock auto_lock(global_->lock_); 454 AutoLock auto_lock(global_->lock_);
481 DCHECK(!global_->PreLockedFind(trial->name())); 455 DCHECK(!global_->PreLockedFind(trial->name()));
482 trial->AddRef(); 456 trial->AddRef();
483 global_->registered_[trial->name()] = trial; 457 global_->registered_[trial->name()] = trial;
484 } 458 }
485 459
486 } // namespace base 460 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698