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

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: No more static default group number 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
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_) {
Alexei Svitkine (slow) 2012/03/29 19:19:07 Hmm. If group_name_ == default_group_name_, then w
MAD 2012/04/02 18:31:15 Filed a bug for this since it's already broken wit
99 group_ = kDefaultGroupNumber;
100 group_name_ = default_group_name_;
101 group_name_hash_ = HashName(group_name_);
102 }
95 } 103 }
96 } 104 }
97 105
98 int FieldTrial::AppendGroup(const std::string& name, 106 int FieldTrial::AppendGroup(const std::string& name,
99 Probability group_probability) { 107 Probability group_probability) {
108 // When the group choice was previously forced, we only need to return the
109 // the id of the chosen group, and anything can be returned for the others.
110 if (forced_) {
111 DCHECK(!group_name_.empty());
112 if (name == group_name_) {
113 return group_;
114 } else {
115 DCHECK_NE(next_group_number_, group_);
116 // We still return different numbers each time, in case some caller need
117 // them to be different.
118 return next_group_number_++;
119 }
120 }
121
100 DCHECK_LE(group_probability, divisor_); 122 DCHECK_LE(group_probability, divisor_);
101 DCHECK_GE(group_probability, 0); 123 DCHECK_GE(group_probability, 0);
102 124
103 if (enable_benchmarking_ || !enable_field_trial_) 125 if (enable_benchmarking_ || !enable_field_trial_)
104 group_probability = 0; 126 group_probability = 0;
105 127
106 accumulated_group_probability_ += group_probability; 128 accumulated_group_probability_ += group_probability;
107 129
108 DCHECK_LE(accumulated_group_probability_, divisor_); 130 DCHECK_LE(accumulated_group_probability_, divisor_);
109 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { 131 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) {
110 // This is the group that crossed the random line, so we do the assignment. 132 // This is the group that crossed the random line, so we do the assignment.
111 group_ = next_group_number_; 133 group_ = next_group_number_;
112 if (name.empty()) 134 if (name.empty())
113 StringAppendF(&group_name_, "%d", group_); 135 StringAppendF(&group_name_, "%d", group_);
114 else 136 else
115 group_name_ = name; 137 group_name_ = name;
116 group_name_hash_ = HashName(group_name_); 138 group_name_hash_ = HashName(group_name_);
117 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); 139 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
118 } 140 }
119 return next_group_number_++; 141 return next_group_number_++;
120 } 142 }
121 143
122 int FieldTrial::group() { 144 int FieldTrial::group() {
123 if (group_ == kNotFinalized) { 145 if (group_ == kNotFinalized) {
124 accumulated_group_probability_ = divisor_; 146 accumulated_group_probability_ = divisor_;
147 // Here is't OK to use kDefaultGroupNumber
148 // since we can't be forced and not finalized.
Alexei Svitkine (slow) 2012/03/29 19:19:07 Add a DCHECK(!forced_);
MAD 2012/04/02 18:31:15 Done.
125 group_ = kDefaultGroupNumber; 149 group_ = kDefaultGroupNumber;
126 group_name_ = default_group_name_; 150 group_name_ = default_group_name_;
127 group_name_hash_ = HashName(group_name_); 151 group_name_hash_ = HashName(group_name_);
128 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); 152 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
129 } 153 }
130 return group_; 154 return group_;
131 } 155 }
132 156
133 std::string FieldTrial::group_name() { 157 std::string FieldTrial::group_name() {
134 group(); // call group() to make sure group assignment was done. 158 group(); // call group() to make sure group assignment was done.
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 while (!registered_.empty()) { 251 while (!registered_.empty()) {
228 RegistrationList::iterator it = registered_.begin(); 252 RegistrationList::iterator it = registered_.begin();
229 it->second->Release(); 253 it->second->Release();
230 registered_.erase(it->first); 254 registered_.erase(it->first);
231 } 255 }
232 DCHECK_EQ(this, global_); 256 DCHECK_EQ(this, global_);
233 global_ = NULL; 257 global_ = NULL;
234 } 258 }
235 259
236 // static 260 // static
237 void FieldTrialList::Register(FieldTrial* trial) { 261 FieldTrial* FieldTrialList::GetFieldTrialInstance(
238 if (!global_) { 262 const std::string& name, FieldTrial::Probability total_probability,
239 used_without_global_ = true; 263 const std::string& default_group_name, int* default_group_number,
240 return; 264 const int year, const int month, const int day_of_month) {
265 // First check if the field trial has already been created in some other way.
266 FieldTrial* existing_trial = Find(name);
267 if (existing_trial) {
268 DCHECK(existing_trial->forced_);
269 if (default_group_number) {
270 // If the default group was forced, it won't have kDefaultGroupNumber
Alexei Svitkine (slow) 2012/03/29 19:19:07 I don't think this comment is clear. How about: "
MAD 2012/04/02 18:31:15 Done.
271 // as a group number, so we must return the chosen group number.
Alexei Svitkine (slow) 2012/03/29 19:19:07 Add a test for this logic.
MAD 2012/04/02 18:31:15 I already added one, but it was missing one expect
272 if (default_group_name == existing_trial->default_group_name())
273 *default_group_number = existing_trial->group();
274 else
275 *default_group_number = FieldTrial::kDefaultGroupNumber;
276 }
277 return existing_trial;
241 } 278 }
242 AutoLock auto_lock(global_->lock_); 279
243 DCHECK(!global_->PreLockedFind(trial->name())); 280 FieldTrial* field_trial = new FieldTrial(
244 trial->AddRef(); 281 name, total_probability, default_group_name, year, month, day_of_month);
245 global_->registered_[trial->name()] = trial; 282 FieldTrialList::Register(field_trial);
283 if (default_group_number)
284 *default_group_number = FieldTrial::kDefaultGroupNumber;
Alexei Svitkine (slow) 2012/03/29 19:19:07 Nit: Move this to the top of the function. Then, i
MAD 2012/04/02 18:31:15 Done.
285 return field_trial;
246 } 286 }
247 287
248 // static 288 // static
249 FieldTrial* FieldTrialList::Find(const std::string& name) { 289 FieldTrial* FieldTrialList::Find(const std::string& name) {
250 if (!global_) 290 if (!global_)
251 return NULL; 291 return NULL;
252 AutoLock auto_lock(global_->lock_); 292 AutoLock auto_lock(global_->lock_);
253 return global_->PreLockedFind(name); 293 return global_->PreLockedFind(name);
254 } 294 }
255 295
(...skipping 20 matching lines...) Expand all
276 316
277 // static 317 // static
278 void FieldTrialList::StatesToString(std::string* output) { 318 void FieldTrialList::StatesToString(std::string* output) {
279 DCHECK(output->empty()); 319 DCHECK(output->empty());
280 if (!global_) 320 if (!global_)
281 return; 321 return;
282 AutoLock auto_lock(global_->lock_); 322 AutoLock auto_lock(global_->lock_);
283 323
284 for (RegistrationList::iterator it = global_->registered_.begin(); 324 for (RegistrationList::iterator it = global_->registered_.begin();
285 it != global_->registered_.end(); ++it) { 325 it != global_->registered_.end(); ++it) {
286 const std::string name = it->first; 326 const std::string& name = it->first;
287 std::string group_name = it->second->group_name_internal(); 327 std::string group_name = it->second->group_name_internal();
288 if (group_name.empty()) 328 if (group_name.empty())
289 continue; // Should not include uninitialized trials. 329 continue; // Should not include uninitialized trials.
290 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); 330 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos);
291 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); 331 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos);
292 output->append(name); 332 output->append(name);
293 output->append(1, kPersistentStringSeparator); 333 output->append(1, kPersistentStringSeparator);
294 output->append(group_name); 334 output->append(group_name);
295 output->append(1, kPersistentStringSeparator); 335 output->append(1, kPersistentStringSeparator);
296 } 336 }
297 } 337 }
298 338
299 // static 339 // static
300 void FieldTrialList::GetFieldTrialNameGroupIds( 340 void FieldTrialList::GetFieldTrialNameGroupIds(
301 std::vector<FieldTrial::NameGroupId>* name_group_ids) { 341 std::vector<FieldTrial::NameGroupId>* name_group_ids) {
302 DCHECK(name_group_ids->empty()); 342 DCHECK(name_group_ids->empty());
303 if (!global_) 343 if (!global_)
304 return; 344 return;
305 AutoLock auto_lock(global_->lock_); 345 AutoLock auto_lock(global_->lock_);
306 346
307 for (RegistrationList::iterator it = global_->registered_.begin(); 347 for (RegistrationList::iterator it = global_->registered_.begin();
308 it != global_->registered_.end(); ++it) { 348 it != global_->registered_.end(); ++it) {
309 FieldTrial::NameGroupId name_group_id; 349 FieldTrial::NameGroupId name_group_id;
310 if (it->second->GetNameGroupId(&name_group_id)) 350 if (it->second->GetNameGroupId(&name_group_id))
311 name_group_ids->push_back(name_group_id); 351 name_group_ids->push_back(name_group_id);
312 } 352 }
313 } 353 }
314 354
315 // static 355 // static
316 bool FieldTrialList::CreateTrialsInChildProcess( 356 bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string) {
317 const std::string& parent_trials) {
318 DCHECK(global_); 357 DCHECK(global_);
319 if (parent_trials.empty() || !global_) 358 if (trials_string.empty() || !global_)
320 return true; 359 return true;
321 360
322 size_t next_item = 0; 361 size_t next_item = 0;
323 while (next_item < parent_trials.length()) { 362 while (next_item < trials_string.length()) {
324 size_t name_end = parent_trials.find(kPersistentStringSeparator, next_item); 363 size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
325 if (name_end == parent_trials.npos || next_item == name_end) 364 if (name_end == trials_string.npos || next_item == name_end)
326 return false; 365 return false;
327 size_t group_name_end = parent_trials.find(kPersistentStringSeparator, 366 size_t group_name_end = trials_string.find(kPersistentStringSeparator,
328 name_end + 1); 367 name_end + 1);
Alexei Svitkine (slow) 2012/03/29 19:19:07 Nit: Align this with kPersistentStringSeparator, s
MAD 2012/04/02 18:31:15 Done.
329 if (group_name_end == parent_trials.npos || name_end + 1 == group_name_end) 368 if (group_name_end == trials_string.npos || name_end + 1 == group_name_end)
330 return false; 369 return false;
331 std::string name(parent_trials, next_item, name_end - next_item); 370 std::string name(trials_string, next_item, name_end - next_item);
332 std::string group_name(parent_trials, name_end + 1, 371 std::string group_name(trials_string, name_end + 1,
333 group_name_end - name_end - 1); 372 group_name_end - name_end - 1);
Alexei Svitkine (slow) 2012/03/29 19:19:07 Nit: Align this with trials_string, so that there'
MAD 2012/04/02 18:31:15 Done.
334 next_item = group_name_end + 1; 373 next_item = group_name_end + 1;
335 374
336 if (!CreateFieldTrial(name, group_name)) 375 if (!CreateFieldTrial(name, group_name))
337 return false; 376 return false;
338 } 377 }
339 return true; 378 return true;
340 } 379 }
341 380
342 // static 381 // static
343 FieldTrial* FieldTrialList::CreateFieldTrial( 382 FieldTrial* FieldTrialList::CreateFieldTrial(
344 const std::string& name, 383 const std::string& name,
345 const std::string& group_name) { 384 const std::string& group_name) {
346 DCHECK(global_); 385 DCHECK(global_);
347 DCHECK_GE(name.size(), 0u); 386 DCHECK_GE(name.size(), 0u);
348 DCHECK_GE(group_name.size(), 0u); 387 DCHECK_GE(group_name.size(), 0u);
349 if (name.empty() || group_name.empty() || !global_) 388 if (name.empty() || group_name.empty() || !global_)
350 return NULL; 389 return NULL;
351 390
352 FieldTrial *field_trial(FieldTrialList::Find(name)); 391 FieldTrial* field_trial = FieldTrialList::Find(name);
353 if (field_trial) { 392 if (field_trial) {
354 // In single process mode, we may have already created the field trial. 393 // In single process mode, or when we force them from the command line,
394 // we may have already created the field trial.
355 if (field_trial->group_name_internal() != group_name) 395 if (field_trial->group_name_internal() != group_name)
356 return NULL; 396 return NULL;
357 return field_trial; 397 return field_trial;
358 } 398 }
359 const int kTotalProbability = 100; 399 const int kTotalProbability = 100;
360 field_trial = new FieldTrial(name, kTotalProbability, group_name, 400 field_trial = new FieldTrial(name, kTotalProbability, group_name,
361 kExpirationYearInFuture, 1, 1); 401 kExpirationYearInFuture, 1, 1);
402 // This is where we may assign a group number different from
403 // kDefaultGroupNumber to the default group.
362 field_trial->AppendGroup(group_name, kTotalProbability); 404 field_trial->AppendGroup(group_name, kTotalProbability);
405 field_trial->forced_ = true;
406 FieldTrialList::Register(field_trial);
363 return field_trial; 407 return field_trial;
364 } 408 }
365 409
366 // static 410 // static
367 void FieldTrialList::AddObserver(Observer* observer) { 411 void FieldTrialList::AddObserver(Observer* observer) {
368 if (!global_) 412 if (!global_)
369 return; 413 return;
370 DCHECK(global_); 414 DCHECK(global_);
371 global_->observer_list_.AddObserver(observer); 415 global_->observer_list_.AddObserver(observer);
372 } 416 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 return global_->client_id_; 462 return global_->client_id_;
419 } 463 }
420 464
421 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 465 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
422 RegistrationList::iterator it = registered_.find(name); 466 RegistrationList::iterator it = registered_.find(name);
423 if (registered_.end() == it) 467 if (registered_.end() == it)
424 return NULL; 468 return NULL;
425 return it->second; 469 return it->second;
426 } 470 }
427 471
472 // static
473 void FieldTrialList::Register(FieldTrial* trial) {
474 if (!global_) {
475 used_without_global_ = true;
476 return;
477 }
478 AutoLock auto_lock(global_->lock_);
479 DCHECK(!global_->PreLockedFind(trial->name()));
480 trial->AddRef();
481 global_->registered_[trial->name()] = trial;
482 }
483
428 } // namespace base 484 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698