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