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