summaryrefslogtreecommitdiffstats
path: root/base/metrics/field_trial.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/metrics/field_trial.cc')
-rw-r--r--base/metrics/field_trial.cc193
1 files changed, 193 insertions, 0 deletions
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
new file mode 100644
index 0000000..63d9ed5
--- /dev/null
+++ b/base/metrics/field_trial.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/field_trial.h"
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/stringprintf.h"
+
+namespace base {
+
+// static
+const int FieldTrial::kNotParticipating = -1;
+
+// static
+const int FieldTrial::kAllRemainingProbability = -2;
+
+// static
+const char FieldTrialList::kPersistentStringSeparator('/');
+
+static const char kHistogramFieldTrialSeparator('_');
+
+//------------------------------------------------------------------------------
+// FieldTrial methods and members.
+
+FieldTrial::FieldTrial(const std::string& name,
+ const Probability total_probability)
+ : name_(name),
+ divisor_(total_probability),
+ random_(static_cast<Probability>(divisor_ * base::RandDouble())),
+ accumulated_group_probability_(0),
+ next_group_number_(0),
+ group_(kNotParticipating) {
+ FieldTrialList::Register(this);
+}
+
+int FieldTrial::AppendGroup(const std::string& name,
+ Probability group_probability) {
+ DCHECK(group_probability <= divisor_);
+ DCHECK(group_probability >=0 ||
+ group_probability == kAllRemainingProbability);
+ if (group_probability == kAllRemainingProbability)
+ accumulated_group_probability_ = divisor_;
+ else
+ accumulated_group_probability_ += group_probability;
+ DCHECK(accumulated_group_probability_ <= divisor_);
+ if (group_ == kNotParticipating && accumulated_group_probability_ > random_) {
+ // This is the group that crossed the random line, so we do the assignment.
+ group_ = next_group_number_;
+ if (name.empty())
+ base::StringAppendF(&group_name_, "%d", group_);
+ else
+ group_name_ = name;
+ }
+ return next_group_number_++;
+}
+
+// static
+std::string FieldTrial::MakeName(const std::string& name_prefix,
+ const std::string& trial_name) {
+ std::string big_string(name_prefix);
+ big_string.append(1, kHistogramFieldTrialSeparator);
+ return big_string.append(FieldTrialList::FindFullName(trial_name));
+}
+
+FieldTrial::~FieldTrial() {}
+
+//------------------------------------------------------------------------------
+// FieldTrialList methods and members.
+
+// static
+FieldTrialList* FieldTrialList::global_ = NULL;
+
+// static
+bool FieldTrialList::register_without_global_ = false;
+
+FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) {
+ DCHECK(!global_);
+ DCHECK(!register_without_global_);
+ global_ = this;
+}
+
+FieldTrialList::~FieldTrialList() {
+ AutoLock auto_lock(lock_);
+ while (!registered_.empty()) {
+ RegistrationList::iterator it = registered_.begin();
+ it->second->Release();
+ registered_.erase(it->first);
+ }
+ DCHECK(this == global_);
+ global_ = NULL;
+}
+
+// static
+void FieldTrialList::Register(FieldTrial* trial) {
+ if (!global_) {
+ register_without_global_ = true;
+ return;
+ }
+ AutoLock auto_lock(global_->lock_);
+ DCHECK(!global_->PreLockedFind(trial->name()));
+ trial->AddRef();
+ global_->registered_[trial->name()] = trial;
+}
+
+// static
+int FieldTrialList::FindValue(const std::string& name) {
+ FieldTrial* field_trial = Find(name);
+ if (field_trial)
+ return field_trial->group();
+ return FieldTrial::kNotParticipating;
+}
+
+// static
+std::string FieldTrialList::FindFullName(const std::string& name) {
+ FieldTrial* field_trial = Find(name);
+ if (field_trial)
+ return field_trial->group_name();
+ return "";
+}
+
+// static
+FieldTrial* FieldTrialList::Find(const std::string& name) {
+ if (!global_)
+ return NULL;
+ AutoLock auto_lock(global_->lock_);
+ return global_->PreLockedFind(name);
+}
+
+FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
+ RegistrationList::iterator it = registered_.find(name);
+ if (registered_.end() == it)
+ return NULL;
+ return it->second;
+}
+
+// static
+void FieldTrialList::StatesToString(std::string* output) {
+ if (!global_)
+ return;
+ DCHECK(output->empty());
+ AutoLock auto_lock(global_->lock_);
+ for (RegistrationList::iterator it = global_->registered_.begin();
+ it != global_->registered_.end(); ++it) {
+ const std::string name = it->first;
+ const std::string group_name = it->second->group_name();
+ if (group_name.empty())
+ continue; // No definitive winner in this trial.
+ DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos);
+ DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos);
+ output->append(name);
+ output->append(1, kPersistentStringSeparator);
+ output->append(group_name);
+ output->append(1, kPersistentStringSeparator);
+ }
+}
+
+// static
+bool FieldTrialList::StringAugmentsState(const std::string& prior_state) {
+ DCHECK(global_);
+ if (prior_state.empty() || !global_)
+ return true;
+
+ size_t next_item = 0;
+ while (next_item < prior_state.length()) {
+ size_t name_end = prior_state.find(kPersistentStringSeparator, next_item);
+ if (name_end == prior_state.npos || next_item == name_end)
+ return false;
+ size_t group_name_end = prior_state.find(kPersistentStringSeparator,
+ name_end + 1);
+ if (group_name_end == prior_state.npos || name_end + 1 == group_name_end)
+ return false;
+ std::string name(prior_state, next_item, name_end - next_item);
+ std::string group_name(prior_state, name_end + 1,
+ group_name_end - name_end - 1);
+ next_item = group_name_end + 1;
+
+ FieldTrial *field_trial(FieldTrialList::Find(name));
+ if (field_trial) {
+ // In single process mode, we may have already created the field trial.
+ if (field_trial->group_name() != group_name)
+ return false;
+ continue;
+ }
+ const int kTotalProbability = 100;
+ field_trial = new FieldTrial(name, kTotalProbability);
+ field_trial->AppendGroup(group_name, kTotalProbability);
+ }
+ return true;
+}
+
+} // namespace base