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

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

Issue 9705074: Supporting command line argument to force field trials (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fixed some ooopss... Created 8 years, 8 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
« no previous file with comments | « base/metrics/field_trial.h ('k') | base/metrics/field_trial_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 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"
11 #include "base/stringprintf.h" 11 #include "base/stringprintf.h"
(...skipping 24 matching lines...) Expand all
36 const int day_of_month) 36 const int day_of_month)
37 : name_(name), 37 : name_(name),
38 name_hash_(HashName(name)), 38 name_hash_(HashName(name)),
39 divisor_(total_probability), 39 divisor_(total_probability),
40 default_group_name_(default_group_name), 40 default_group_name_(default_group_name),
41 random_(static_cast<Probability>(divisor_ * RandDouble())), 41 random_(static_cast<Probability>(divisor_ * RandDouble())),
42 accumulated_group_probability_(0), 42 accumulated_group_probability_(0),
43 next_group_number_(kDefaultGroupNumber + 1), 43 next_group_number_(kDefaultGroupNumber + 1),
44 group_(kNotFinalized), 44 group_(kNotFinalized),
45 group_name_hash_(kReservedHashValue), 45 group_name_hash_(kReservedHashValue),
46 enable_field_trial_(true) { 46 enable_field_trial_(true),
47 forced_(false) {
47 DCHECK_GT(total_probability, 0); 48 DCHECK_GT(total_probability, 0);
48 DCHECK(!name_.empty()); 49 DCHECK(!name_.empty());
49 DCHECK(!default_group_name_.empty()); 50 DCHECK(!default_group_name_.empty());
50 FieldTrialList::Register(this);
51 51
52 DCHECK_GT(year, 1970); 52 DCHECK_GT(year, 1970);
53 DCHECK_GT(month, 0); 53 DCHECK_GT(month, 0);
54 DCHECK_LT(month, 13); 54 DCHECK_LT(month, 13);
55 DCHECK_GT(day_of_month, 0); 55 DCHECK_GT(day_of_month, 0);
56 DCHECK_LT(day_of_month, 32); 56 DCHECK_LT(day_of_month, 32);
57 57
58 Time::Exploded exploded; 58 Time::Exploded exploded;
59 exploded.year = year; 59 exploded.year = year;
60 exploded.month = month; 60 exploded.month = month;
61 exploded.day_of_week = 0; // Should be unused. 61 exploded.day_of_week = 0; // Should be unused.
62 exploded.day_of_month = day_of_month; 62 exploded.day_of_month = day_of_month;
63 exploded.hour = 0; 63 exploded.hour = 0;
64 exploded.minute = 0; 64 exploded.minute = 0;
65 exploded.second = 0; 65 exploded.second = 0;
66 exploded.millisecond = 0; 66 exploded.millisecond = 0;
67 67
68 Time expiration_time = Time::FromLocalExploded(exploded); 68 Time expiration_time = Time::FromLocalExploded(exploded);
69 if (GetBuildTime() > expiration_time) 69 if (GetBuildTime() > expiration_time)
70 Disable(); 70 Disable();
71 } 71 }
72 72
73 void FieldTrial::UseOneTimeRandomization() { 73 void FieldTrial::UseOneTimeRandomization() {
74 // No need to specify randomization when the group choice was forced.
75 if (forced_)
76 return;
74 DCHECK_EQ(group_, kNotFinalized); 77 DCHECK_EQ(group_, kNotFinalized);
75 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); 78 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_);
76 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) { 79 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) {
77 NOTREACHED(); 80 NOTREACHED();
78 Disable(); 81 Disable();
79 return; 82 return;
80 } 83 }
81 84
82 random_ = static_cast<Probability>( 85 random_ = static_cast<Probability>(
83 divisor_ * HashClientId(FieldTrialList::client_id(), name_)); 86 divisor_ * HashClientId(FieldTrialList::client_id(), name_));
84 } 87 }
85 88
86 void FieldTrial::Disable() { 89 void FieldTrial::Disable() {
87 enable_field_trial_ = false; 90 enable_field_trial_ = false;
88 91
89 // In case we are disabled after initialization, we need to switch 92 // In case we are disabled after initialization, we need to switch
90 // the trial to the default group. 93 // the trial to the default group.
91 if (group_ != kNotFinalized) { 94 if (group_ != kNotFinalized) {
92 group_ = kDefaultGroupNumber; 95 // Only reset when not already the default group, because in case we were
93 group_name_ = default_group_name_; 96 // forced to the default group, the group number may not be
94 group_name_hash_ = HashName(group_name_); 97 // kDefaultGroupNumber, so we should keep it as is.
98 if (group_name_ != default_group_name_)
99 SetGroupChoice(default_group_name_, kDefaultGroupNumber);
95 } 100 }
96 } 101 }
97 102
98 int FieldTrial::AppendGroup(const std::string& name, 103 int FieldTrial::AppendGroup(const std::string& name,
99 Probability group_probability) { 104 Probability group_probability) {
105 // When the group choice was previously forced, we only need to return the
106 // the id of the chosen group, and anything can be returned for the others.
107 if (forced_) {
108 DCHECK(!group_name_.empty());
109 if (name == group_name_) {
110 return group_;
111 }
112 DCHECK_NE(next_group_number_, group_);
113 // We still return different numbers each time, in case some caller need
114 // them to be different.
115 return next_group_number_++;
116 }
117
100 DCHECK_LE(group_probability, divisor_); 118 DCHECK_LE(group_probability, divisor_);
101 DCHECK_GE(group_probability, 0); 119 DCHECK_GE(group_probability, 0);
102 120
103 if (enable_benchmarking_ || !enable_field_trial_) 121 if (enable_benchmarking_ || !enable_field_trial_)
104 group_probability = 0; 122 group_probability = 0;
105 123
106 accumulated_group_probability_ += group_probability; 124 accumulated_group_probability_ += group_probability;
107 125
108 DCHECK_LE(accumulated_group_probability_, divisor_); 126 DCHECK_LE(accumulated_group_probability_, divisor_);
109 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { 127 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) {
110 // This is the group that crossed the random line, so we do the assignment. 128 // This is the group that crossed the random line, so we do the assignment.
111 group_ = next_group_number_; 129 SetGroupChoice(name, next_group_number_);
112 if (name.empty())
113 StringAppendF(&group_name_, "%d", group_);
114 else
115 group_name_ = name;
116 group_name_hash_ = HashName(group_name_);
117 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); 130 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
118 } 131 }
119 return next_group_number_++; 132 return next_group_number_++;
120 } 133 }
121 134
122 int FieldTrial::group() { 135 int FieldTrial::group() {
123 if (group_ == kNotFinalized) { 136 if (group_ == kNotFinalized) {
124 accumulated_group_probability_ = divisor_; 137 accumulated_group_probability_ = divisor_;
125 group_ = kDefaultGroupNumber; 138 // Here it's OK to use kDefaultGroupNumber
126 group_name_ = default_group_name_; 139 // since we can't be forced and not finalized.
127 group_name_hash_ = HashName(group_name_); 140 DCHECK(!forced_);
141 SetGroupChoice(default_group_name_, kDefaultGroupNumber);
128 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); 142 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
129 } 143 }
130 return group_; 144 return group_;
131 } 145 }
132 146
133 std::string FieldTrial::group_name() { 147 std::string FieldTrial::group_name() {
134 group(); // call group() to make sure group assignment was done. 148 group(); // call group() to make sure group assignment was done.
135 DCHECK(!group_name_.empty()); 149 DCHECK(!group_name_.empty());
136 return group_name_; 150 return group_name_;
137 } 151 }
(...skipping 15 matching lines...) Expand all
153 } 167 }
154 168
155 // static 169 // static
156 void FieldTrial::EnableBenchmarking() { 170 void FieldTrial::EnableBenchmarking() {
157 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); 171 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
158 enable_benchmarking_ = true; 172 enable_benchmarking_ = true;
159 } 173 }
160 174
161 FieldTrial::~FieldTrial() {} 175 FieldTrial::~FieldTrial() {}
162 176
177 void FieldTrial::SetGroupChoice(const std::string& name, int number) {
178 group_ = number;
179 if (name.empty())
180 StringAppendF(&group_name_, "%d", group_);
181 else
182 group_name_ = name;
183 group_name_hash_ = HashName(group_name_);
184 }
185
163 // static 186 // static
164 double FieldTrial::HashClientId(const std::string& client_id, 187 double FieldTrial::HashClientId(const std::string& client_id,
165 const std::string& trial_name) { 188 const std::string& trial_name) {
166 // SHA-1 is designed to produce a uniformly random spread in its output space, 189 // SHA-1 is designed to produce a uniformly random spread in its output space,
167 // even for nearly-identical inputs, so it helps massage whatever client_id 190 // even for nearly-identical inputs, so it helps massage whatever client_id
168 // and trial_name we get into something with a uniform distribution, which 191 // and trial_name we get into something with a uniform distribution, which
169 // is desirable so that we don't skew any part of the 0-100% spectrum. 192 // is desirable so that we don't skew any part of the 0-100% spectrum.
170 std::string input(client_id + trial_name); 193 std::string input(client_id + trial_name);
171 unsigned char sha1_hash[kSHA1Length]; 194 unsigned char sha1_hash[kSHA1Length];
172 SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), 195 SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 while (!registered_.empty()) { 250 while (!registered_.empty()) {
228 RegistrationList::iterator it = registered_.begin(); 251 RegistrationList::iterator it = registered_.begin();
229 it->second->Release(); 252 it->second->Release();
230 registered_.erase(it->first); 253 registered_.erase(it->first);
231 } 254 }
232 DCHECK_EQ(this, global_); 255 DCHECK_EQ(this, global_);
233 global_ = NULL; 256 global_ = NULL;
234 } 257 }
235 258
236 // static 259 // static
237 void FieldTrialList::Register(FieldTrial* trial) { 260 FieldTrial* FieldTrialList::FactoryGetFieldTrial(
238 if (!global_) { 261 const std::string& name,
239 used_without_global_ = true; 262 FieldTrial::Probability total_probability,
240 return; 263 const std::string& default_group_name,
264 const int year,
265 const int month,
266 const int day_of_month,
267 int* default_group_number) {
268 if (default_group_number)
269 *default_group_number = FieldTrial::kDefaultGroupNumber;
270 // Check if the field trial has already been created in some other way.
271 FieldTrial* existing_trial = Find(name);
272 if (existing_trial) {
273 CHECK(existing_trial->forced_);
274 // If the field trial has already been forced, check whether it was forced
275 // to the default group. Return the chosen group number, in that case..
276 if (default_group_number &&
277 default_group_name == existing_trial->default_group_name()) {
278 *default_group_number = existing_trial->group();
279 }
280 return existing_trial;
241 } 281 }
242 AutoLock auto_lock(global_->lock_); 282
243 DCHECK(!global_->PreLockedFind(trial->name())); 283 FieldTrial* field_trial = new FieldTrial(
244 trial->AddRef(); 284 name, total_probability, default_group_name, year, month, day_of_month);
245 global_->registered_[trial->name()] = trial; 285 FieldTrialList::Register(field_trial);
286 return field_trial;
246 } 287 }
247 288
248 // static 289 // static
249 FieldTrial* FieldTrialList::Find(const std::string& name) { 290 FieldTrial* FieldTrialList::Find(const std::string& name) {
250 if (!global_) 291 if (!global_)
251 return NULL; 292 return NULL;
252 AutoLock auto_lock(global_->lock_); 293 AutoLock auto_lock(global_->lock_);
253 return global_->PreLockedFind(name); 294 return global_->PreLockedFind(name);
254 } 295 }
255 296
(...skipping 20 matching lines...) Expand all
276 317
277 // static 318 // static
278 void FieldTrialList::StatesToString(std::string* output) { 319 void FieldTrialList::StatesToString(std::string* output) {
279 DCHECK(output->empty()); 320 DCHECK(output->empty());
280 if (!global_) 321 if (!global_)
281 return; 322 return;
282 AutoLock auto_lock(global_->lock_); 323 AutoLock auto_lock(global_->lock_);
283 324
284 for (RegistrationList::iterator it = global_->registered_.begin(); 325 for (RegistrationList::iterator it = global_->registered_.begin();
285 it != global_->registered_.end(); ++it) { 326 it != global_->registered_.end(); ++it) {
286 const std::string name = it->first; 327 const std::string& name = it->first;
287 std::string group_name = it->second->group_name_internal(); 328 std::string group_name = it->second->group_name_internal();
288 if (group_name.empty()) 329 if (group_name.empty())
289 continue; // Should not include uninitialized trials. 330 continue; // Should not include uninitialized trials.
290 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); 331 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos);
291 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); 332 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos);
292 output->append(name); 333 output->append(name);
293 output->append(1, kPersistentStringSeparator); 334 output->append(1, kPersistentStringSeparator);
294 output->append(group_name); 335 output->append(group_name);
295 output->append(1, kPersistentStringSeparator); 336 output->append(1, kPersistentStringSeparator);
296 } 337 }
297 } 338 }
298 339
299 // static 340 // static
300 void FieldTrialList::GetFieldTrialNameGroupIds( 341 void FieldTrialList::GetFieldTrialNameGroupIds(
301 std::vector<FieldTrial::NameGroupId>* name_group_ids) { 342 std::vector<FieldTrial::NameGroupId>* name_group_ids) {
302 DCHECK(name_group_ids->empty()); 343 DCHECK(name_group_ids->empty());
303 if (!global_) 344 if (!global_)
304 return; 345 return;
305 AutoLock auto_lock(global_->lock_); 346 AutoLock auto_lock(global_->lock_);
306 347
307 for (RegistrationList::iterator it = global_->registered_.begin(); 348 for (RegistrationList::iterator it = global_->registered_.begin();
308 it != global_->registered_.end(); ++it) { 349 it != global_->registered_.end(); ++it) {
309 FieldTrial::NameGroupId name_group_id; 350 FieldTrial::NameGroupId name_group_id;
310 if (it->second->GetNameGroupId(&name_group_id)) 351 if (it->second->GetNameGroupId(&name_group_id))
311 name_group_ids->push_back(name_group_id); 352 name_group_ids->push_back(name_group_id);
312 } 353 }
313 } 354 }
314 355
315 // static 356 // static
316 bool FieldTrialList::CreateTrialsInChildProcess( 357 bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string) {
317 const std::string& parent_trials) {
318 DCHECK(global_); 358 DCHECK(global_);
319 if (parent_trials.empty() || !global_) 359 if (trials_string.empty() || !global_)
320 return true; 360 return true;
321 361
322 size_t next_item = 0; 362 size_t next_item = 0;
323 while (next_item < parent_trials.length()) { 363 while (next_item < trials_string.length()) {
324 size_t name_end = parent_trials.find(kPersistentStringSeparator, next_item); 364 size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
325 if (name_end == parent_trials.npos || next_item == name_end) 365 if (name_end == trials_string.npos || next_item == name_end)
326 return false; 366 return false;
327 size_t group_name_end = parent_trials.find(kPersistentStringSeparator, 367 size_t group_name_end = trials_string.find(kPersistentStringSeparator,
328 name_end + 1); 368 name_end + 1);
329 if (group_name_end == parent_trials.npos || name_end + 1 == group_name_end) 369 if (group_name_end == trials_string.npos || name_end + 1 == group_name_end)
330 return false; 370 return false;
331 std::string name(parent_trials, next_item, name_end - next_item); 371 std::string name(trials_string, next_item, name_end - next_item);
332 std::string group_name(parent_trials, name_end + 1, 372 std::string group_name(trials_string, name_end + 1,
333 group_name_end - name_end - 1); 373 group_name_end - name_end - 1);
334 next_item = group_name_end + 1; 374 next_item = group_name_end + 1;
335 375
336 if (!CreateFieldTrial(name, group_name)) 376 if (!CreateFieldTrial(name, group_name))
337 return false; 377 return false;
338 } 378 }
339 return true; 379 return true;
340 } 380 }
341 381
342 // static 382 // static
343 FieldTrial* FieldTrialList::CreateFieldTrial( 383 FieldTrial* FieldTrialList::CreateFieldTrial(
344 const std::string& name, 384 const std::string& name,
345 const std::string& group_name) { 385 const std::string& group_name) {
346 DCHECK(global_); 386 DCHECK(global_);
347 DCHECK_GE(name.size(), 0u); 387 DCHECK_GE(name.size(), 0u);
348 DCHECK_GE(group_name.size(), 0u); 388 DCHECK_GE(group_name.size(), 0u);
349 if (name.empty() || group_name.empty() || !global_) 389 if (name.empty() || group_name.empty() || !global_)
350 return NULL; 390 return NULL;
351 391
352 FieldTrial *field_trial(FieldTrialList::Find(name)); 392 FieldTrial* field_trial = FieldTrialList::Find(name);
353 if (field_trial) { 393 if (field_trial) {
354 // In single process mode, we may have already created the field trial. 394 // In single process mode, or when we force them from the command line,
395 // we may have already created the field trial.
355 if (field_trial->group_name_internal() != group_name) 396 if (field_trial->group_name_internal() != group_name)
356 return NULL; 397 return NULL;
357 return field_trial; 398 return field_trial;
358 } 399 }
359 const int kTotalProbability = 100; 400 const int kTotalProbability = 100;
360 field_trial = new FieldTrial(name, kTotalProbability, group_name, 401 field_trial = new FieldTrial(name, kTotalProbability, group_name,
361 kExpirationYearInFuture, 1, 1); 402 kExpirationYearInFuture, 1, 1);
403 // This is where we may assign a group number different from
404 // kDefaultGroupNumber to the default group.
362 field_trial->AppendGroup(group_name, kTotalProbability); 405 field_trial->AppendGroup(group_name, kTotalProbability);
406 field_trial->forced_ = true;
407 FieldTrialList::Register(field_trial);
363 return field_trial; 408 return field_trial;
364 } 409 }
365 410
366 // static 411 // static
367 void FieldTrialList::AddObserver(Observer* observer) { 412 void FieldTrialList::AddObserver(Observer* observer) {
368 if (!global_) 413 if (!global_)
369 return; 414 return;
370 DCHECK(global_); 415 DCHECK(global_);
371 global_->observer_list_.AddObserver(observer); 416 global_->observer_list_.AddObserver(observer);
372 } 417 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 return global_->client_id_; 463 return global_->client_id_;
419 } 464 }
420 465
421 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 466 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
422 RegistrationList::iterator it = registered_.find(name); 467 RegistrationList::iterator it = registered_.find(name);
423 if (registered_.end() == it) 468 if (registered_.end() == it)
424 return NULL; 469 return NULL;
425 return it->second; 470 return it->second;
426 } 471 }
427 472
473 // static
474 void FieldTrialList::Register(FieldTrial* trial) {
475 if (!global_) {
476 used_without_global_ = true;
477 return;
478 }
479 AutoLock auto_lock(global_->lock_);
480 DCHECK(!global_->PreLockedFind(trial->name()));
481 trial->AddRef();
482 global_->registered_[trial->name()] = trial;
483 }
484
428 } // namespace base 485 } // namespace base
OLDNEW
« no previous file with comments | « base/metrics/field_trial.h ('k') | base/metrics/field_trial_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698