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