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 #include "base/utf_string_conversions.h" 11 12 namespace base { 13 14 // static 15 const int FieldTrial::kNotFinalized = -1; 16 17 // static 18 const int FieldTrial::kDefaultGroupNumber = 0; 19 20 // static 21 bool FieldTrial::enable_benchmarking_ = false; 22 23 // static 24 const char FieldTrialList::kPersistentStringSeparator('/'); 25 26 static const char kHistogramFieldTrialSeparator('_'); 27 28 //------------------------------------------------------------------------------ 29 // FieldTrial methods and members. 30 31 FieldTrial::FieldTrial(const std::string& name, 32 const Probability total_probability, 33 const std::string& default_group_name, 34 const int year, 35 const int month, 36 const int day_of_month) 37 : name_(name), 38 divisor_(total_probability), 39 default_group_name_(default_group_name), 40 random_(static_cast<Probability>(divisor_ * base::RandDouble())), 41 accumulated_group_probability_(0), 42 next_group_number_(kDefaultGroupNumber+1), 43 group_(kNotFinalized) { 44 DCHECK_GT(total_probability, 0); 45 DCHECK(!default_group_name_.empty()); 46 FieldTrialList::Register(this); 47 48 DCHECK_GT(year, 1970); 49 DCHECK_GT(month, 0); 50 DCHECK_LT(month, 13); 51 DCHECK_GT(day_of_month, 0); 52 DCHECK_LT(day_of_month, 32); 53 54 base::Time::Exploded exploded; 55 exploded.year = year; 56 exploded.month = month; 57 exploded.day_of_week = 0; // Should be unused. 58 exploded.day_of_month = day_of_month; 59 exploded.hour = 0; 60 exploded.minute = 0; 61 exploded.second = 0; 62 exploded.millisecond = 0; 63 64 base::Time expiration_time = Time::FromLocalExploded(exploded); 65 disable_field_trial_ = (GetBuildTime() > expiration_time) ? true : false; 66 } 67 68 int FieldTrial::AppendGroup(const std::string& name, 69 Probability group_probability) { 70 DCHECK(group_probability <= divisor_); 71 DCHECK_GE(group_probability, 0); 72 73 if (enable_benchmarking_ || disable_field_trial_) 74 group_probability = 0; 75 76 accumulated_group_probability_ += group_probability; 77 78 DCHECK(accumulated_group_probability_ <= divisor_); 79 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { 80 // This is the group that crossed the random line, so we do the assignment. 81 group_ = next_group_number_; 82 if (name.empty()) 83 base::StringAppendF(&group_name_, "%d", group_); 84 else 85 group_name_ = name; 86 } 87 return next_group_number_++; 88 } 89 90 int FieldTrial::group() { 91 if (group_ == kNotFinalized) { 92 accumulated_group_probability_ = divisor_; 93 group_ = kDefaultGroupNumber; 94 group_name_ = default_group_name_; 95 } 96 return group_; 97 } 98 99 std::string FieldTrial::group_name() { 100 group(); // call group() to make group assignment was done. 101 return group_name_; 102 } 103 104 // static 105 std::string FieldTrial::MakeName(const std::string& name_prefix, 106 const std::string& trial_name) { 107 std::string big_string(name_prefix); 108 big_string.append(1, kHistogramFieldTrialSeparator); 109 return big_string.append(FieldTrialList::FindFullName(trial_name)); 110 } 111 112 // static 113 void FieldTrial::EnableBenchmarking() { 114 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); 115 enable_benchmarking_ = true; 116 } 117 118 FieldTrial::~FieldTrial() {} 119 120 // static 121 Time FieldTrial::GetBuildTime() { 122 Time integral_build_time; 123 const char* kDateTime = __DATE__ " " __TIME__; 124 bool result = Time::FromString(ASCIIToWide(kDateTime).c_str(), 125 &integral_build_time); 126 DCHECK(result); 127 return integral_build_time; 128 } 129 130 //------------------------------------------------------------------------------ 131 // FieldTrialList methods and members. 132 133 // static 134 FieldTrialList* FieldTrialList::global_ = NULL; 135 136 // static 137 bool FieldTrialList::register_without_global_ = false; 138 139 FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) { 140 DCHECK(!global_); 141 DCHECK(!register_without_global_); 142 global_ = this; 143 } 144 145 FieldTrialList::~FieldTrialList() { 146 AutoLock auto_lock(lock_); 147 while (!registered_.empty()) { 148 RegistrationList::iterator it = registered_.begin(); 149 it->second->Release(); 150 registered_.erase(it->first); 151 } 152 DCHECK(this == global_); 153 global_ = NULL; 154 } 155 156 // static 157 void FieldTrialList::Register(FieldTrial* trial) { 158 if (!global_) { 159 register_without_global_ = true; 160 return; 161 } 162 AutoLock auto_lock(global_->lock_); 163 DCHECK(!global_->PreLockedFind(trial->name())); 164 trial->AddRef(); 165 global_->registered_[trial->name()] = trial; 166 } 167 168 // static 169 FieldTrial* FieldTrialList::Find(const std::string& name) { 170 if (!global_) 171 return NULL; 172 AutoLock auto_lock(global_->lock_); 173 return global_->PreLockedFind(name); 174 } 175 176 // static 177 int FieldTrialList::FindValue(const std::string& name) { 178 FieldTrial* field_trial = Find(name); 179 if (field_trial) 180 return field_trial->group(); 181 return FieldTrial::kNotFinalized; 182 } 183 184 // static 185 std::string FieldTrialList::FindFullName(const std::string& name) { 186 FieldTrial* field_trial = Find(name); 187 if (field_trial) 188 return field_trial->group_name(); 189 return ""; 190 } 191 192 // static 193 void FieldTrialList::StatesToString(std::string* output) { 194 if (!global_) 195 return; 196 DCHECK(output->empty()); 197 AutoLock auto_lock(global_->lock_); 198 for (RegistrationList::iterator it = global_->registered_.begin(); 199 it != global_->registered_.end(); ++it) { 200 const std::string name = it->first; 201 std::string group_name = it->second->group_name_internal(); 202 if (group_name.empty()) 203 // No definitive winner in this trial, use default_group_name as the 204 // group_name. 205 group_name = it->second->default_group_name(); 206 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); 207 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); 208 output->append(name); 209 output->append(1, kPersistentStringSeparator); 210 output->append(group_name); 211 output->append(1, kPersistentStringSeparator); 212 } 213 } 214 215 // static 216 bool FieldTrialList::CreateTrialsInChildProcess( 217 const std::string& parent_trials) { 218 DCHECK(global_); 219 if (parent_trials.empty() || !global_) 220 return true; 221 222 Time::Exploded exploded; 223 Time two_years_from_now = 224 Time::NowFromSystemTime() + TimeDelta::FromDays(730); 225 two_years_from_now.LocalExplode(&exploded); 226 const int kTwoYearsFromNow = exploded.year; 227 228 size_t next_item = 0; 229 while (next_item < parent_trials.length()) { 230 size_t name_end = parent_trials.find(kPersistentStringSeparator, next_item); 231 if (name_end == parent_trials.npos || next_item == name_end) 232 return false; 233 size_t group_name_end = parent_trials.find(kPersistentStringSeparator, 234 name_end + 1); 235 if (group_name_end == parent_trials.npos || name_end + 1 == group_name_end) 236 return false; 237 std::string name(parent_trials, next_item, name_end - next_item); 238 std::string group_name(parent_trials, name_end + 1, 239 group_name_end - name_end - 1); 240 next_item = group_name_end + 1; 241 242 FieldTrial *field_trial(FieldTrialList::Find(name)); 243 if (field_trial) { 244 // In single process mode, we may have already created the field trial. 245 if ((field_trial->group_name_internal() != group_name) && 246 (field_trial->default_group_name() != group_name)) 247 return false; 248 continue; 249 } 250 const int kTotalProbability = 100; 251 field_trial = new FieldTrial(name, kTotalProbability, group_name, 252 kTwoYearsFromNow, 1, 1); 253 field_trial->AppendGroup(group_name, kTotalProbability); 254 } 255 return true; 256 } 257 258 // static 259 size_t FieldTrialList::GetFieldTrialCount() { 260 if (!global_) 261 return 0; 262 AutoLock auto_lock(global_->lock_); 263 return global_->registered_.size(); 264 } 265 266 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 267 RegistrationList::iterator it = registered_.find(name); 268 if (registered_.end() == it) 269 return NULL; 270 return it->second; 271 } 272 273 } // namespace base 274