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