field_trial.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/metrics/field_trial.h" 6 7#include "base/logging.h" 8#include "base/rand_util.h" 9#include "base/stringprintf.h" 10 11namespace base { 12 13// static 14const int FieldTrial::kNotParticipating = -1; 15 16// static 17const int FieldTrial::kAllRemainingProbability = -2; 18 19// static 20bool FieldTrial::enable_benchmarking_ = false; 21 22// static 23const char FieldTrialList::kPersistentStringSeparator('/'); 24 25static const char kHistogramFieldTrialSeparator('_'); 26 27//------------------------------------------------------------------------------ 28// FieldTrial methods and members. 29 30FieldTrial::FieldTrial(const std::string& name, 31 const Probability total_probability) 32 : name_(name), 33 divisor_(total_probability), 34 random_(static_cast<Probability>(divisor_ * base::RandDouble())), 35 accumulated_group_probability_(0), 36 next_group_number_(0), 37 group_(kNotParticipating) { 38 FieldTrialList::Register(this); 39} 40 41int FieldTrial::AppendGroup(const std::string& name, 42 Probability group_probability) { 43 DCHECK(group_probability <= divisor_); 44 DCHECK(group_probability >=0 || 45 group_probability == kAllRemainingProbability); 46 if (group_probability == kAllRemainingProbability) { 47 accumulated_group_probability_ = divisor_; 48 } else { 49 if (enable_benchmarking_) 50 group_probability = 0; 51 accumulated_group_probability_ += group_probability; 52 } 53 DCHECK(accumulated_group_probability_ <= divisor_); 54 if (group_ == kNotParticipating && accumulated_group_probability_ > random_) { 55 // This is the group that crossed the random line, so we do the assignment. 56 group_ = next_group_number_; 57 if (name.empty()) 58 base::StringAppendF(&group_name_, "%d", group_); 59 else 60 group_name_ = name; 61 } 62 return next_group_number_++; 63} 64 65// static 66std::string FieldTrial::MakeName(const std::string& name_prefix, 67 const std::string& trial_name) { 68 std::string big_string(name_prefix); 69 big_string.append(1, kHistogramFieldTrialSeparator); 70 return big_string.append(FieldTrialList::FindFullName(trial_name)); 71} 72 73// static 74void FieldTrial::EnableBenchmarking() { 75 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); 76 enable_benchmarking_ = true; 77} 78 79FieldTrial::~FieldTrial() {} 80 81//------------------------------------------------------------------------------ 82// FieldTrialList methods and members. 83 84// static 85FieldTrialList* FieldTrialList::global_ = NULL; 86 87// static 88bool FieldTrialList::register_without_global_ = false; 89 90FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) { 91 DCHECK(!global_); 92 DCHECK(!register_without_global_); 93 global_ = this; 94} 95 96FieldTrialList::~FieldTrialList() { 97 AutoLock auto_lock(lock_); 98 while (!registered_.empty()) { 99 RegistrationList::iterator it = registered_.begin(); 100 it->second->Release(); 101 registered_.erase(it->first); 102 } 103 DCHECK(this == global_); 104 global_ = NULL; 105} 106 107// static 108void FieldTrialList::Register(FieldTrial* trial) { 109 if (!global_) { 110 register_without_global_ = true; 111 return; 112 } 113 AutoLock auto_lock(global_->lock_); 114 DCHECK(!global_->PreLockedFind(trial->name())); 115 trial->AddRef(); 116 global_->registered_[trial->name()] = trial; 117} 118 119// static 120int FieldTrialList::FindValue(const std::string& name) { 121 FieldTrial* field_trial = Find(name); 122 if (field_trial) 123 return field_trial->group(); 124 return FieldTrial::kNotParticipating; 125} 126 127// static 128std::string FieldTrialList::FindFullName(const std::string& name) { 129 FieldTrial* field_trial = Find(name); 130 if (field_trial) 131 return field_trial->group_name(); 132 return ""; 133} 134 135// static 136FieldTrial* FieldTrialList::Find(const std::string& name) { 137 if (!global_) 138 return NULL; 139 AutoLock auto_lock(global_->lock_); 140 return global_->PreLockedFind(name); 141} 142 143FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 144 RegistrationList::iterator it = registered_.find(name); 145 if (registered_.end() == it) 146 return NULL; 147 return it->second; 148} 149 150// static 151void FieldTrialList::StatesToString(std::string* output) { 152 if (!global_) 153 return; 154 DCHECK(output->empty()); 155 AutoLock auto_lock(global_->lock_); 156 for (RegistrationList::iterator it = global_->registered_.begin(); 157 it != global_->registered_.end(); ++it) { 158 const std::string name = it->first; 159 const std::string group_name = it->second->group_name(); 160 if (group_name.empty()) 161 continue; // No definitive winner in this trial. 162 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); 163 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); 164 output->append(name); 165 output->append(1, kPersistentStringSeparator); 166 output->append(group_name); 167 output->append(1, kPersistentStringSeparator); 168 } 169} 170 171// static 172bool FieldTrialList::StringAugmentsState(const std::string& prior_state) { 173 DCHECK(global_); 174 if (prior_state.empty() || !global_) 175 return true; 176 177 size_t next_item = 0; 178 while (next_item < prior_state.length()) { 179 size_t name_end = prior_state.find(kPersistentStringSeparator, next_item); 180 if (name_end == prior_state.npos || next_item == name_end) 181 return false; 182 size_t group_name_end = prior_state.find(kPersistentStringSeparator, 183 name_end + 1); 184 if (group_name_end == prior_state.npos || name_end + 1 == group_name_end) 185 return false; 186 std::string name(prior_state, next_item, name_end - next_item); 187 std::string group_name(prior_state, name_end + 1, 188 group_name_end - name_end - 1); 189 next_item = group_name_end + 1; 190 191 FieldTrial *field_trial(FieldTrialList::Find(name)); 192 if (field_trial) { 193 // In single process mode, we may have already created the field trial. 194 if (field_trial->group_name() != group_name) 195 return false; 196 continue; 197 } 198 const int kTotalProbability = 100; 199 field_trial = new FieldTrial(name, kTotalProbability); 200 field_trial->AppendGroup(group_name, kTotalProbability); 201 } 202 return true; 203} 204 205// static 206size_t FieldTrialList::GetFieldTrialCount() { 207 if (!global_) 208 return 0; 209 AutoLock auto_lock(global_->lock_); 210 return global_->registered_.size(); 211} 212 213} // namespace base 214