• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FieldTrial(const std::string & name,const Probability total_probability,const std::string & default_group_name,const int year,const int month,const int day_of_month)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 
AppendGroup(const std::string & name,Probability group_probability)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 
group()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 
group_name()99 std::string FieldTrial::group_name() {
100   group();  // call group() to make group assignment was done.
101   return group_name_;
102 }
103 
104 // static
MakeName(const std::string & name_prefix,const std::string & trial_name)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
EnableBenchmarking()113 void FieldTrial::EnableBenchmarking() {
114   DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
115   enable_benchmarking_ = true;
116 }
117 
~FieldTrial()118 FieldTrial::~FieldTrial() {}
119 
120 // static
GetBuildTime()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 
FieldTrialList()139 FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) {
140   DCHECK(!global_);
141   DCHECK(!register_without_global_);
142   global_ = this;
143 }
144 
~FieldTrialList()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
Register(FieldTrial * trial)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
Find(const std::string & name)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
FindValue(const std::string & name)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
FindFullName(const std::string & name)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
StatesToString(std::string * output)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
CreateTrialsInChildProcess(const std::string & parent_trials)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
GetFieldTrialCount()259 size_t FieldTrialList::GetFieldTrialCount() {
260   if (!global_)
261     return 0;
262   AutoLock auto_lock(global_->lock_);
263   return global_->registered_.size();
264 }
265 
PreLockedFind(const std::string & name)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