diff options
Diffstat (limited to 'base/metrics/field_trial.cc')
-rw-r--r-- | base/metrics/field_trial.cc | 98 |
1 files changed, 85 insertions, 13 deletions
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc index 03e5809..b86b9cc 100644 --- a/base/metrics/field_trial.cc +++ b/base/metrics/field_trial.cc @@ -6,6 +6,8 @@ #include "base/logging.h" #include "base/rand_util.h" +#include "base/sha1.h" +#include "base/string_util.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" @@ -41,11 +43,13 @@ FieldTrial::FieldTrial(const std::string& name, : name_(name), divisor_(total_probability), default_group_name_(default_group_name), - random_(static_cast<Probability>(divisor_ * base::RandDouble())), + random_(static_cast<Probability>(divisor_ * RandDouble())), accumulated_group_probability_(0), - next_group_number_(kDefaultGroupNumber+1), - group_(kNotFinalized) { + next_group_number_(kDefaultGroupNumber + 1), + group_(kNotFinalized), + enable_field_trial_(true) { DCHECK_GT(total_probability, 0); + DCHECK(!name_.empty()); DCHECK(!default_group_name_.empty()); FieldTrialList::Register(this); @@ -55,7 +59,7 @@ FieldTrial::FieldTrial(const std::string& name, DCHECK_GT(day_of_month, 0); DCHECK_LT(day_of_month, 32); - base::Time::Exploded exploded; + Time::Exploded exploded; exploded.year = year; exploded.month = month; exploded.day_of_week = 0; // Should be unused. @@ -65,8 +69,33 @@ FieldTrial::FieldTrial(const std::string& name, exploded.second = 0; exploded.millisecond = 0; - base::Time expiration_time = Time::FromLocalExploded(exploded); - disable_field_trial_ = (GetBuildTime() > expiration_time) ? true : false; + Time expiration_time = Time::FromLocalExploded(exploded); + if (GetBuildTime() > expiration_time) + Disable(); +} + +void FieldTrial::UseOneTimeRandomization() { + DCHECK_EQ(group_, kNotFinalized); + DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_); + if (!FieldTrialList::IsOneTimeRandomizationEnabled()) { + NOTREACHED(); + Disable(); + return; + } + + random_ = static_cast<Probability>( + divisor_ * HashClientId(FieldTrialList::client_id(), name_)); +} + +void FieldTrial::Disable() { + enable_field_trial_ = false; + + // In case we are disabled after initialization, we need to switch + // the trial to the default group. + if (group_ != kNotFinalized) { + group_ = kDefaultGroupNumber; + group_name_ = default_group_name_; + } } int FieldTrial::AppendGroup(const std::string& name, @@ -74,7 +103,7 @@ int FieldTrial::AppendGroup(const std::string& name, DCHECK_LE(group_probability, divisor_); DCHECK_GE(group_probability, 0); - if (enable_benchmarking_ || disable_field_trial_) + if (enable_benchmarking_ || !enable_field_trial_) group_probability = 0; accumulated_group_probability_ += group_probability; @@ -84,7 +113,7 @@ int FieldTrial::AppendGroup(const std::string& name, // 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_); + StringAppendF(&group_name_, "%d", group_); else group_name_ = name; FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); @@ -103,7 +132,8 @@ int FieldTrial::group() { } std::string FieldTrial::group_name() { - group(); // call group() to make group assignment was done. + group(); // call group() to make sure group assignment was done. + DCHECK(!group_name_.empty()); return group_name_; } @@ -133,6 +163,25 @@ Time FieldTrial::GetBuildTime() { return integral_build_time; } +// static +double FieldTrial::HashClientId(const std::string& client_id, + const std::string& trial_name) { + // SHA-1 is designed to produce a uniformly random spread in its output space, + // even for nearly-identical inputs, so it helps massage whatever client_id + // and trial_name we get into something with a uniform distribution, which + // is desirable so that we don't skew any part of the 0-100% spectrum. + std::string input(client_id + trial_name); + unsigned char sha1_hash[SHA1_LENGTH]; + SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), + input.size(), + sha1_hash); + + COMPILE_ASSERT(sizeof(uint64) < sizeof(sha1_hash), need_more_data); + uint64* bits = reinterpret_cast<uint64*>(&sha1_hash[0]); + + return BitsToOpenEndedUnitInterval(*bits); +} + //------------------------------------------------------------------------------ // FieldTrialList methods and members. @@ -142,8 +191,9 @@ FieldTrialList* FieldTrialList::global_ = NULL; // static bool FieldTrialList::register_without_global_ = false; -FieldTrialList::FieldTrialList() +FieldTrialList::FieldTrialList(const std::string& client_id) : application_start_time_(TimeTicks::Now()), + client_id_(client_id), observer_list_(ObserverList<Observer>::NOTIFY_EXISTING_ONLY) { DCHECK(!global_); DCHECK(!register_without_global_); @@ -204,19 +254,23 @@ std::string FieldTrialList::FindFullName(const std::string& name) { } // static +bool FieldTrialList::TrialExists(const std::string& name) { + return Find(name) != NULL; +} + +// 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; std::string group_name = it->second->group_name_internal(); if (group_name.empty()) - // No definitive winner in this trial, use default_group_name as the - // group_name. - group_name = it->second->default_group_name(); + continue; // Should not include uninitialized trials. DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); output->append(name); @@ -313,6 +367,24 @@ size_t FieldTrialList::GetFieldTrialCount() { return global_->registered_.size(); } +// static +bool FieldTrialList::IsOneTimeRandomizationEnabled() { + DCHECK(global_); + if (!global_) + return false; + + return !global_->client_id_.empty(); +} + +// static +const std::string& FieldTrialList::client_id() { + DCHECK(global_); + if (!global_) + return EmptyString(); + + return global_->client_id_; +} + FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { RegistrationList::iterator it = registered_.find(name); if (registered_.end() == it) |