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

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: Created 8 years, 9 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"
12 #include "base/string_util.h" 12 #include "base/string_util.h"
13 #include "base/utf_string_conversions.h" 13 #include "base/utf_string_conversions.h"
14 14
15 namespace base { 15 namespace base {
16 16
17 static const char kHistogramFieldTrialSeparator('_'); 17 static const char kHistogramFieldTrialSeparator('_');
18 18
19 // statics 19 // statics
20 const int FieldTrial::kNotFinalized = -1; 20 const int FieldTrial::kNotFinalized = -1;
21 const int FieldTrial::kDefaultGroupNumber = 0; 21 const int FieldTrial::kDefaultGroupNumber = 0;
22 const uint32 FieldTrial::kReservedHashValue = 0; 22 const uint32 FieldTrial::kReservedHashValue = 0;
23 bool FieldTrial::enable_benchmarking_ = false; 23 bool FieldTrial::enable_benchmarking_ = false;
24 24
25 const char FieldTrialList::kPersistentStringSeparator('/'); 25 const char FieldTrialList::kPersistentStringSeparator('/');
26 int FieldTrialList::kExpirationYearInFuture = 0; 26 int FieldTrialList::kExpirationYearInFuture = 0;
27 27
28 //------------------------------------------------------------------------------ 28 //------------------------------------------------------------------------------
29 // FieldTrial methods and members. 29 // FieldTrial methods and members.
30 30
31 FieldTrial* FieldTrial::CreateInstance(
32 const std::string& name, Probability total_probability,
33 const std::string& default_group_name, const int year, const int month,
34 const int day_of_month) {
35 // First check if the field trial has already been created in some other ways.
Alexei Svitkine (slow) 2012/03/23 18:41:55 Nit: "some other ways" -> "some other way"
MAD 2012/03/23 20:20:12 Done.
36 FieldTrial* existing_trial = FieldTrialList::Find(name);
37 if (existing_trial) {
38 return existing_trial;
39 } else {
Alexei Svitkine (slow) 2012/03/23 18:41:55 Nit: Since you're early returning, don't need an e
MAD 2012/03/23 20:20:12 Done.
40 FieldTrial* new_trial = new FieldTrial(
41 name, total_probability, default_group_name, year, month, day_of_month);
42 FieldTrialList::Register(new_trial);
43 return new_trial;
44 }
45 }
46
31 FieldTrial::FieldTrial(const std::string& name, 47 FieldTrial::FieldTrial(const std::string& name,
32 const Probability total_probability, 48 const Probability total_probability,
33 const std::string& default_group_name, 49 const std::string& default_group_name,
34 const int year, 50 const int year,
35 const int month, 51 const int month,
36 const int day_of_month) 52 const int day_of_month)
37 : name_(name), 53 : name_(name),
38 name_hash_(HashName(name)), 54 name_hash_(HashName(name)),
39 divisor_(total_probability), 55 divisor_(total_probability),
40 default_group_name_(default_group_name), 56 default_group_name_(default_group_name),
41 random_(static_cast<Probability>(divisor_ * RandDouble())), 57 random_(static_cast<Probability>(divisor_ * RandDouble())),
42 accumulated_group_probability_(0), 58 accumulated_group_probability_(0),
43 next_group_number_(kDefaultGroupNumber + 1), 59 next_group_number_(kDefaultGroupNumber + 1),
44 group_(kNotFinalized), 60 group_(kNotFinalized),
45 group_name_hash_(kReservedHashValue), 61 group_name_hash_(kReservedHashValue),
46 enable_field_trial_(true) { 62 enable_field_trial_(true),
63 forced_(false) {
47 DCHECK_GT(total_probability, 0); 64 DCHECK_GT(total_probability, 0);
48 DCHECK(!name_.empty()); 65 DCHECK(!name_.empty());
49 DCHECK(!default_group_name_.empty()); 66 DCHECK(!default_group_name_.empty());
50 FieldTrialList::Register(this);
51 67
52 DCHECK_GT(year, 1970); 68 DCHECK_GT(year, 1970);
53 DCHECK_GT(month, 0); 69 DCHECK_GT(month, 0);
54 DCHECK_LT(month, 13); 70 DCHECK_LT(month, 13);
55 DCHECK_GT(day_of_month, 0); 71 DCHECK_GT(day_of_month, 0);
56 DCHECK_LT(day_of_month, 32); 72 DCHECK_LT(day_of_month, 32);
57 73
58 Time::Exploded exploded; 74 Time::Exploded exploded;
59 exploded.year = year; 75 exploded.year = year;
60 exploded.month = month; 76 exploded.month = month;
61 exploded.day_of_week = 0; // Should be unused. 77 exploded.day_of_week = 0; // Should be unused.
62 exploded.day_of_month = day_of_month; 78 exploded.day_of_month = day_of_month;
63 exploded.hour = 0; 79 exploded.hour = 0;
64 exploded.minute = 0; 80 exploded.minute = 0;
65 exploded.second = 0; 81 exploded.second = 0;
66 exploded.millisecond = 0; 82 exploded.millisecond = 0;
67 83
68 Time expiration_time = Time::FromLocalExploded(exploded); 84 Time expiration_time = Time::FromLocalExploded(exploded);
69 if (GetBuildTime() > expiration_time) 85 if (GetBuildTime() > expiration_time)
70 Disable(); 86 Disable();
71 } 87 }
72 88
73 void FieldTrial::UseOneTimeRandomization() { 89 void FieldTrial::UseOneTimeRandomization() {
90 if (forced_)
91 return;
74 DCHECK_EQ(group_, kNotFinalized); 92 DCHECK_EQ(group_, kNotFinalized);
75 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); 93 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_);
76 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) { 94 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) {
77 NOTREACHED(); 95 NOTREACHED();
78 Disable(); 96 Disable();
79 return; 97 return;
80 } 98 }
81 99
82 random_ = static_cast<Probability>( 100 random_ = static_cast<Probability>(
83 divisor_ * HashClientId(FieldTrialList::client_id(), name_)); 101 divisor_ * HashClientId(FieldTrialList::client_id(), name_));
84 } 102 }
85 103
86 void FieldTrial::Disable() { 104 void FieldTrial::Disable() {
87 enable_field_trial_ = false; 105 enable_field_trial_ = false;
88 106
89 // In case we are disabled after initialization, we need to switch 107 // In case we are disabled after initialization, we need to switch
90 // the trial to the default group. 108 // the trial to the default group.
91 if (group_ != kNotFinalized) { 109 if (group_ != kNotFinalized) {
92 group_ = kDefaultGroupNumber; 110 group_ = kDefaultGroupNumber;
93 group_name_ = default_group_name_; 111 group_name_ = default_group_name_;
94 group_name_hash_ = HashName(group_name_); 112 group_name_hash_ = HashName(group_name_);
95 } 113 }
96 } 114 }
97 115
98 int FieldTrial::AppendGroup(const std::string& name, 116 int FieldTrial::AppendGroup(const std::string& name,
99 Probability group_probability) { 117 Probability group_probability) {
118 if (forced_) {
119 if (name == group_name_)
120 return group_;
121 else
122 return group_ + 1;
123 }
124
100 DCHECK_LE(group_probability, divisor_); 125 DCHECK_LE(group_probability, divisor_);
101 DCHECK_GE(group_probability, 0); 126 DCHECK_GE(group_probability, 0);
102 127
103 if (enable_benchmarking_ || !enable_field_trial_) 128 if (enable_benchmarking_ || !enable_field_trial_)
104 group_probability = 0; 129 group_probability = 0;
105 130
106 accumulated_group_probability_ += group_probability; 131 accumulated_group_probability_ += group_probability;
107 132
108 DCHECK_LE(accumulated_group_probability_, divisor_); 133 DCHECK_LE(accumulated_group_probability_, divisor_);
109 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { 134 if (group_ == kNotFinalized && (accumulated_group_probability_ > random_)) {
110 // This is the group that crossed the random line, so we do the assignment. 135 // This is the group that crossed the random line, so we do the assignment.
111 group_ = next_group_number_; 136 group_ = next_group_number_;
112 if (name.empty()) 137 if (name.empty())
113 StringAppendF(&group_name_, "%d", group_); 138 StringAppendF(&group_name_, "%d", group_);
114 else 139 else
115 group_name_ = name; 140 group_name_ = name;
116 group_name_hash_ = HashName(group_name_); 141 group_name_hash_ = HashName(group_name_);
117 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); 142 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
118 } 143 }
119 return next_group_number_++; 144 return next_group_number_++;
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 while (!registered_.empty()) { 252 while (!registered_.empty()) {
228 RegistrationList::iterator it = registered_.begin(); 253 RegistrationList::iterator it = registered_.begin();
229 it->second->Release(); 254 it->second->Release();
230 registered_.erase(it->first); 255 registered_.erase(it->first);
231 } 256 }
232 DCHECK_EQ(this, global_); 257 DCHECK_EQ(this, global_);
233 global_ = NULL; 258 global_ = NULL;
234 } 259 }
235 260
236 // static 261 // static
237 void FieldTrialList::Register(FieldTrial* trial) {
238 if (!global_) {
239 used_without_global_ = true;
240 return;
241 }
242 AutoLock auto_lock(global_->lock_);
243 DCHECK(!global_->PreLockedFind(trial->name()));
244 trial->AddRef();
245 global_->registered_[trial->name()] = trial;
246 }
247
248 // static
249 FieldTrial* FieldTrialList::Find(const std::string& name) { 262 FieldTrial* FieldTrialList::Find(const std::string& name) {
250 if (!global_) 263 if (!global_)
251 return NULL; 264 return NULL;
252 AutoLock auto_lock(global_->lock_); 265 AutoLock auto_lock(global_->lock_);
253 return global_->PreLockedFind(name); 266 return global_->PreLockedFind(name);
254 } 267 }
255 268
256 // static 269 // static
257 int FieldTrialList::FindValue(const std::string& name) { 270 int FieldTrialList::FindValue(const std::string& name) {
258 FieldTrial* field_trial = Find(name); 271 FieldTrial* field_trial = Find(name);
(...skipping 17 matching lines...) Expand all
276 289
277 // static 290 // static
278 void FieldTrialList::StatesToString(std::string* output) { 291 void FieldTrialList::StatesToString(std::string* output) {
279 DCHECK(output->empty()); 292 DCHECK(output->empty());
280 if (!global_) 293 if (!global_)
281 return; 294 return;
282 AutoLock auto_lock(global_->lock_); 295 AutoLock auto_lock(global_->lock_);
283 296
284 for (RegistrationList::iterator it = global_->registered_.begin(); 297 for (RegistrationList::iterator it = global_->registered_.begin();
285 it != global_->registered_.end(); ++it) { 298 it != global_->registered_.end(); ++it) {
286 const std::string name = it->first; 299 const std::string& name = it->first;
287 std::string group_name = it->second->group_name_internal(); 300 std::string group_name = it->second->group_name_internal();
288 if (group_name.empty()) 301 if (group_name.empty())
289 continue; // Should not include uninitialized trials. 302 continue; // Should not include uninitialized trials.
290 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); 303 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos);
291 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); 304 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos);
292 output->append(name); 305 output->append(name);
293 output->append(1, kPersistentStringSeparator); 306 output->append(1, kPersistentStringSeparator);
294 output->append(group_name); 307 output->append(group_name);
295 output->append(1, kPersistentStringSeparator); 308 output->append(1, kPersistentStringSeparator);
296 } 309 }
297 } 310 }
298 311
299 // static 312 // static
300 void FieldTrialList::GetFieldTrialNameGroupIds( 313 void FieldTrialList::GetFieldTrialNameGroupIds(
301 std::vector<FieldTrial::NameGroupId>* name_group_ids) { 314 std::vector<FieldTrial::NameGroupId>* name_group_ids) {
302 DCHECK(name_group_ids->empty()); 315 DCHECK(name_group_ids->empty());
303 if (!global_) 316 if (!global_)
304 return; 317 return;
305 AutoLock auto_lock(global_->lock_); 318 AutoLock auto_lock(global_->lock_);
306 319
307 for (RegistrationList::iterator it = global_->registered_.begin(); 320 for (RegistrationList::iterator it = global_->registered_.begin();
308 it != global_->registered_.end(); ++it) { 321 it != global_->registered_.end(); ++it) {
309 FieldTrial::NameGroupId name_group_id; 322 FieldTrial::NameGroupId name_group_id;
310 if (it->second->GetNameGroupId(&name_group_id)) 323 if (it->second->GetNameGroupId(&name_group_id))
311 name_group_ids->push_back(name_group_id); 324 name_group_ids->push_back(name_group_id);
312 } 325 }
313 } 326 }
314 327
315 // static 328 // static
316 bool FieldTrialList::CreateTrialsInChildProcess( 329 bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string) {
317 const std::string& parent_trials) {
318 DCHECK(global_); 330 DCHECK(global_);
319 if (parent_trials.empty() || !global_) 331 if (trials_string.empty() || !global_)
320 return true; 332 return true;
321 333
322 size_t next_item = 0; 334 size_t next_item = 0;
323 while (next_item < parent_trials.length()) { 335 while (next_item < trials_string.length()) {
324 size_t name_end = parent_trials.find(kPersistentStringSeparator, next_item); 336 size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
325 if (name_end == parent_trials.npos || next_item == name_end) 337 if (name_end == trials_string.npos || next_item == name_end)
326 return false; 338 return false;
327 size_t group_name_end = parent_trials.find(kPersistentStringSeparator, 339 size_t group_name_end = trials_string.find(kPersistentStringSeparator,
328 name_end + 1); 340 name_end + 1);
329 if (group_name_end == parent_trials.npos || name_end + 1 == group_name_end) 341 if (group_name_end == trials_string.npos || name_end + 1 == group_name_end)
330 return false; 342 return false;
331 std::string name(parent_trials, next_item, name_end - next_item); 343 std::string name(trials_string, next_item, name_end - next_item);
332 std::string group_name(parent_trials, name_end + 1, 344 std::string group_name(trials_string, name_end + 1,
333 group_name_end - name_end - 1); 345 group_name_end - name_end - 1);
334 next_item = group_name_end + 1; 346 next_item = group_name_end + 1;
335 347
336 if (!CreateFieldTrial(name, group_name)) 348 if (!CreateFieldTrial(name, group_name))
337 return false; 349 return false;
338 } 350 }
339 return true; 351 return true;
340 } 352 }
341 353
342 // static 354 // static
343 FieldTrial* FieldTrialList::CreateFieldTrial( 355 FieldTrial* FieldTrialList::CreateFieldTrial(
344 const std::string& name, 356 const std::string& name,
345 const std::string& group_name) { 357 const std::string& group_name) {
346 DCHECK(global_); 358 DCHECK(global_);
347 DCHECK_GE(name.size(), 0u); 359 DCHECK_GE(name.size(), 0u);
348 DCHECK_GE(group_name.size(), 0u); 360 DCHECK_GE(group_name.size(), 0u);
349 if (name.empty() || group_name.empty() || !global_) 361 if (name.empty() || group_name.empty() || !global_)
350 return NULL; 362 return NULL;
351 363
352 FieldTrial *field_trial(FieldTrialList::Find(name)); 364 FieldTrial* field_trial(FieldTrialList::Find(name));
353 if (field_trial) { 365 if (field_trial) {
354 // In single process mode, we may have already created the field trial. 366 // In single process mode, or when we force them from the command line,
367 // we may have already created the field trial.
355 if (field_trial->group_name_internal() != group_name) 368 if (field_trial->group_name_internal() != group_name)
356 return NULL; 369 return NULL;
357 return field_trial; 370 return field_trial;
358 } 371 }
359 const int kTotalProbability = 100; 372 const int kTotalProbability = 100;
360 field_trial = new FieldTrial(name, kTotalProbability, group_name, 373 field_trial = new FieldTrial(name, kTotalProbability, group_name,
361 kExpirationYearInFuture, 1, 1); 374 kExpirationYearInFuture, 1, 1);
362 field_trial->AppendGroup(group_name, kTotalProbability); 375 field_trial->AppendGroup(group_name, kTotalProbability);
376 field_trial->forced_ = true;
377 FieldTrialList::Register(field_trial);
363 return field_trial; 378 return field_trial;
364 } 379 }
365 380
366 // static 381 // static
367 void FieldTrialList::AddObserver(Observer* observer) { 382 void FieldTrialList::AddObserver(Observer* observer) {
368 if (!global_) 383 if (!global_)
369 return; 384 return;
370 DCHECK(global_); 385 DCHECK(global_);
371 global_->observer_list_.AddObserver(observer); 386 global_->observer_list_.AddObserver(observer);
372 } 387 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 return global_->client_id_; 433 return global_->client_id_;
419 } 434 }
420 435
421 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 436 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
422 RegistrationList::iterator it = registered_.find(name); 437 RegistrationList::iterator it = registered_.find(name);
423 if (registered_.end() == it) 438 if (registered_.end() == it)
424 return NULL; 439 return NULL;
425 return it->second; 440 return it->second;
426 } 441 }
427 442
443 // static
444 void FieldTrialList::Register(FieldTrial* trial) {
445 if (!global_) {
446 used_without_global_ = true;
447 return;
448 }
449 AutoLock auto_lock(global_->lock_);
450 DCHECK(!global_->PreLockedFind(trial->name()));
451 trial->AddRef();
452 global_->registered_[trial->name()] = trial;
453 }
454
428 } // namespace base 455 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698