Home | History | Annotate | Download | only in metrics
      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 
     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 
     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 
     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 
     99 std::string FieldTrial::group_name() {
    100   group();  // call group() to make group assignment was done.
    101   return group_name_;
    102 }
    103 
    104 // static
    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
    113 void FieldTrial::EnableBenchmarking() {
    114   DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
    115   enable_benchmarking_ = true;
    116 }
    117 
    118 FieldTrial::~FieldTrial() {}
    119 
    120 // static
    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 
    139 FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) {
    140   DCHECK(!global_);
    141   DCHECK(!register_without_global_);
    142   global_ = this;
    143 }
    144 
    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
    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
    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
    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
    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
    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
    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
    259 size_t FieldTrialList::GetFieldTrialCount() {
    260   if (!global_)
    261     return 0;
    262   AutoLock auto_lock(global_->lock_);
    263   return global_->registered_.size();
    264 }
    265 
    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