summaryrefslogtreecommitdiff
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.cc166
1 files changed, 110 insertions, 56 deletions
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 639f6d38e2..b417b056d2 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -9,16 +9,23 @@
#include "base/build_time.h"
#include "base/logging.h"
#include "base/rand_util.h"
-#include "base/sha1.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/sys_byteorder.h"
namespace base {
namespace {
+// Define a separator character to use when creating a persistent form of an
+// instance. This is intended for use as a command line argument, passed to a
+// second process to mimic our state (i.e., provide the same group name).
+const char kPersistentStringSeparator = '/'; // Currently a slash.
+
+// Define a marker character to be used as a prefix to a trial name on the
+// command line which forces its activation.
+const char kActivationMarker = '*';
+
// Created a time value based on |year|, |month| and |day_of_month| parameters.
Time CreateTimeFromParams(int year, int month, int day_of_month) {
DCHECK_GT(year, 1970);
@@ -59,6 +66,45 @@ FieldTrial::Probability GetGroupBoundaryValue(
return std::min(result, divisor - 1);
}
+// Parses the --force-fieldtrials string |trials_string| into |entries|.
+// Returns true if the string was parsed correctly. On failure, the |entries|
+// array may end up being partially filled.
+bool ParseFieldTrialsString(const std::string& trials_string,
+ std::vector<FieldTrial::State>* entries) {
+ const StringPiece trials_string_piece(trials_string);
+
+ size_t next_item = 0;
+ while (next_item < trials_string.length()) {
+ size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
+ if (name_end == trials_string.npos || next_item == name_end)
+ return false;
+ size_t group_name_end =
+ trials_string.find(kPersistentStringSeparator, name_end + 1);
+ if (name_end + 1 == group_name_end)
+ return false;
+ if (group_name_end == trials_string.npos)
+ group_name_end = trials_string.length();
+
+ FieldTrial::State entry;
+ // Verify if the trial should be activated or not.
+ if (trials_string[next_item] == kActivationMarker) {
+ // Name cannot be only the indicator.
+ if (name_end - next_item == 1)
+ return false;
+ next_item++;
+ entry.activated = true;
+ }
+ entry.trial_name =
+ trials_string_piece.substr(next_item, name_end - next_item);
+ entry.group_name =
+ trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1);
+ next_item = group_name_end + 1;
+
+ entries->push_back(entry);
+ }
+ return true;
+}
+
} // namespace
// statics
@@ -66,8 +112,6 @@ const int FieldTrial::kNotFinalized = -1;
const int FieldTrial::kDefaultGroupNumber = 0;
bool FieldTrial::enable_benchmarking_ = false;
-const char FieldTrialList::kPersistentStringSeparator('/');
-const char FieldTrialList::kActivationMarker('*');
int FieldTrialList::kNoExpirationYear = 0;
//------------------------------------------------------------------------------
@@ -76,6 +120,10 @@ int FieldTrialList::kNoExpirationYear = 0;
FieldTrial::EntropyProvider::~EntropyProvider() {
}
+FieldTrial::State::State() : activated(false) {}
+
+FieldTrial::State::~State() {}
+
void FieldTrial::Disable() {
DCHECK(!group_reported_);
enable_field_trial_ = false;
@@ -140,6 +188,11 @@ const std::string& FieldTrial::group_name() {
return group_name_;
}
+const std::string& FieldTrial::GetGroupNameWithoutActivation() {
+ FinalizeGroupChoice();
+ return group_name_;
+}
+
void FieldTrial::SetForced() {
// We might have been forced before (e.g., by CreateFieldTrial) and it's
// first come first served, e.g., command line switch has precedence.
@@ -223,16 +276,12 @@ bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const {
return true;
}
-bool FieldTrial::GetState(FieldTrialState* field_trial_state) const {
+bool FieldTrial::GetState(State* field_trial_state) {
if (!enable_field_trial_)
return false;
+ FinalizeGroupChoice();
field_trial_state->trial_name = trial_name_;
- // If the group name is empty (hasn't been finalized yet), use the default
- // group name instead.
- if (!group_name_.empty())
- field_trial_state->group_name = group_name_;
- else
- field_trial_state->group_name = default_group_name_;
+ field_trial_state->group_name = group_name_;
field_trial_state->activated = group_reported_;
return true;
}
@@ -299,7 +348,7 @@ FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
const int month,
const int day_of_month,
FieldTrial::RandomizationType randomization_type,
- uint32 randomization_seed,
+ uint32_t randomization_seed,
int* default_group_number) {
if (default_group_number)
*default_group_number = FieldTrial::kDefaultGroupNumber;
@@ -321,12 +370,11 @@ FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
// group number, so that it does not conflict with the |AppendGroup()|
// result for the chosen group.
const int kNonConflictingGroupNumber = -2;
- COMPILE_ASSERT(
+ static_assert(
kNonConflictingGroupNumber != FieldTrial::kDefaultGroupNumber,
- conflicting_default_group_number);
- COMPILE_ASSERT(
- kNonConflictingGroupNumber != FieldTrial::kNotFinalized,
- conflicting_default_group_number);
+ "The 'non-conflicting' group number conflicts");
+ static_assert(kNonConflictingGroupNumber != FieldTrial::kNotFinalized,
+ "The 'non-conflicting' group number conflicts");
*default_group_number = kNonConflictingGroupNumber;
}
}
@@ -355,32 +403,39 @@ FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
}
// static
-FieldTrial* FieldTrialList::Find(const std::string& name) {
+FieldTrial* FieldTrialList::Find(const std::string& trial_name) {
if (!global_)
return NULL;
AutoLock auto_lock(global_->lock_);
- return global_->PreLockedFind(name);
+ return global_->PreLockedFind(trial_name);
}
// static
-int FieldTrialList::FindValue(const std::string& name) {
- FieldTrial* field_trial = Find(name);
+int FieldTrialList::FindValue(const std::string& trial_name) {
+ FieldTrial* field_trial = Find(trial_name);
if (field_trial)
return field_trial->group();
return FieldTrial::kNotFinalized;
}
// static
-std::string FieldTrialList::FindFullName(const std::string& name) {
- FieldTrial* field_trial = Find(name);
+std::string FieldTrialList::FindFullName(const std::string& trial_name) {
+ FieldTrial* field_trial = Find(trial_name);
if (field_trial)
return field_trial->group_name();
return std::string();
}
// static
-bool FieldTrialList::TrialExists(const std::string& name) {
- return Find(name) != NULL;
+bool FieldTrialList::TrialExists(const std::string& trial_name) {
+ return Find(trial_name) != NULL;
+}
+
+// static
+bool FieldTrialList::IsTrialActive(const std::string& trial_name) {
+ FieldTrial* field_trial = Find(trial_name);
+ FieldTrial::ActiveGroup active_group;
+ return field_trial && field_trial->GetActiveGroup(&active_group);
}
// static
@@ -407,7 +462,7 @@ void FieldTrialList::AllStatesToString(std::string* output) {
AutoLock auto_lock(global_->lock_);
for (const auto& registered : global_->registered_) {
- FieldTrial::FieldTrialState trial;
+ FieldTrial::State trial;
if (!registered.second->GetState(&trial))
continue;
DCHECK_EQ(std::string::npos,
@@ -416,9 +471,9 @@ void FieldTrialList::AllStatesToString(std::string* output) {
trial.group_name.find(kPersistentStringSeparator));
if (trial.activated)
output->append(1, kActivationMarker);
- output->append(trial.trial_name);
+ trial.trial_name.AppendToString(output);
output->append(1, kPersistentStringSeparator);
- output->append(trial.group_name);
+ trial.group_name.AppendToString(output);
output->append(1, kPersistentStringSeparator);
}
}
@@ -440,6 +495,24 @@ void FieldTrialList::GetActiveFieldTrialGroups(
}
// static
+void FieldTrialList::GetActiveFieldTrialGroupsFromString(
+ const std::string& trials_string,
+ FieldTrial::ActiveGroups* active_groups) {
+ std::vector<FieldTrial::State> entries;
+ if (!ParseFieldTrialsString(trials_string, &entries))
+ return;
+
+ for (const auto& entry : entries) {
+ if (entry.activated) {
+ FieldTrial::ActiveGroup group;
+ group.trial_name = entry.trial_name.as_string();
+ group.group_name = entry.group_name.as_string();
+ active_groups->push_back(group);
+ }
+ }
+}
+
+// static
bool FieldTrialList::CreateTrialsFromString(
const std::string& trials_string,
FieldTrialActivationMode mode,
@@ -448,40 +521,21 @@ bool FieldTrialList::CreateTrialsFromString(
if (trials_string.empty() || !global_)
return true;
- size_t next_item = 0;
- while (next_item < trials_string.length()) {
- size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
- if (name_end == trials_string.npos || next_item == name_end)
- return false;
- size_t group_name_end = trials_string.find(kPersistentStringSeparator,
- name_end + 1);
- if (name_end + 1 == group_name_end)
- return false;
- if (group_name_end == trials_string.npos)
- group_name_end = trials_string.length();
+ std::vector<FieldTrial::State> entries;
+ if (!ParseFieldTrialsString(trials_string, &entries))
+ return false;
- // Verify if the trial should be activated or not.
- std::string name;
- bool force_activation = false;
- if (trials_string[next_item] == kActivationMarker) {
- // Name cannot be only the indicator.
- if (name_end - next_item == 1)
- return false;
- next_item++;
- force_activation = true;
- }
- name.append(trials_string, next_item, name_end - next_item);
- std::string group_name(trials_string, name_end + 1,
- group_name_end - name_end - 1);
- next_item = group_name_end + 1;
+ for (const auto& entry : entries) {
+ const std::string trial_name = entry.trial_name.as_string();
+ const std::string group_name = entry.group_name.as_string();
- if (ignored_trial_names.find(name) != ignored_trial_names.end())
+ if (ContainsKey(ignored_trial_names, trial_name))
continue;
- FieldTrial* trial = CreateFieldTrial(name, group_name);
+ FieldTrial* trial = CreateFieldTrial(trial_name, group_name);
if (!trial)
return false;
- if (mode == ACTIVATE_TRIALS || force_activation) {
+ if (mode == ACTIVATE_TRIALS || entry.activated) {
// Call |group()| to mark the trial as "used" and notify observers, if
// any. This is useful to ensure that field trials created in child
// processes are properly reported in crash reports.