[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // FieldTrial is a class for handling details of statistical experiments |
| 6 | // performed by actual users in the field (i.e., in a shipped or beta product). |
| 7 | // All code is called exclusively on the UI thread currently. |
| 8 | // |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 9 | // The simplest example is an experiment to see whether one of two options |
| 10 | // produces "better" results across our user population. In that scenario, UMA |
| 11 | // data is uploaded to aggregate the test results, and this FieldTrial class |
| 12 | // manages the state of each such experiment (state == which option was |
| 13 | // pseudo-randomly selected). |
| 14 | // |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 15 | // States are typically generated randomly, either based on a one time |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 16 | // randomization (which will yield the same results, in terms of selecting |
| 17 | // the client for a field trial or not, for every run of the program on a |
| 18 | // given machine), or by a startup randomization (generated each time the |
| 19 | // application starts up, but held constant during the duration of the |
| 20 | // process), or by continuous randomization across a run (where the state |
| 21 | // can be recalculated again and again, many times during a process). |
| 22 | // Continuous randomization is not yet implemented. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 23 | |
| 24 | //------------------------------------------------------------------------------ |
| 25 | // Example: Suppose we have an experiment involving memory, such as determining |
[email protected] | 57a336a | 2009-09-30 20:42:27 | [diff] [blame] | 26 | // the impact of some pruning algorithm. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 27 | // We assume that we already have a histogram of memory usage, such as: |
| 28 | |
| 29 | // HISTOGRAM_COUNTS("Memory.RendererTotal", count); |
| 30 | |
| 31 | // Somewhere in main thread initialization code, we'd probably define an |
| 32 | // instance of a FieldTrial, with code such as: |
| 33 | |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 34 | // // FieldTrials are reference counted, and persist automagically until |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 35 | // // process teardown, courtesy of their automatic registration in |
| 36 | // // FieldTrialList. |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 37 | // // Note: This field trial will run in Chrome instances compiled through |
| 38 | // // 8 July, 2015, and after that all instances will be in "StandardMem". |
[email protected] | 9f8c0a2 | 2012-06-13 02:01:24 | [diff] [blame] | 39 | // scoped_refptr<base::FieldTrial> trial( |
| 40 | // base::FieldTrialList::FactoryGetFieldTrial("MemoryExperiment", 1000, |
[email protected] | 70d7ca9 | 2012-08-17 22:43:10 | [diff] [blame] | 41 | // "StandardMem", 2015, 7, 8, |
| 42 | // NULL)); |
| 43 | // const int high_mem_group = |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 44 | // trial->AppendGroup("HighMem", 20); // 2% in HighMem group. |
[email protected] | 70d7ca9 | 2012-08-17 22:43:10 | [diff] [blame] | 45 | // const int low_mem_group = |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 46 | // trial->AppendGroup("LowMem", 20); // 2% in LowMem group. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 47 | // // Take action depending of which group we randomly land in. |
[email protected] | 70d7ca9 | 2012-08-17 22:43:10 | [diff] [blame] | 48 | // if (trial->group() == high_mem_group) |
[email protected] | 57a336a | 2009-09-30 20:42:27 | [diff] [blame] | 49 | // SetPruningAlgorithm(kType1); // Sample setting of browser state. |
[email protected] | 70d7ca9 | 2012-08-17 22:43:10 | [diff] [blame] | 50 | // else if (trial->group() == low_mem_group) |
[email protected] | 57a336a | 2009-09-30 20:42:27 | [diff] [blame] | 51 | // SetPruningAlgorithm(kType2); // Sample alternate setting. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 52 | |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 53 | // We then, in addition to our original histogram, output histograms which have |
| 54 | // slightly different names depending on what group the trial instance happened |
| 55 | // to randomly be assigned: |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 56 | |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 57 | // HISTOGRAM_COUNTS("Memory.RendererTotal", count); // The original histogram. |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 58 | // static const bool memory_renderer_total_trial_exists = |
[email protected] | a140efe | 2011-09-15 23:15:02 | [diff] [blame] | 59 | // FieldTrialList::TrialExists("MemoryExperiment"); |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 60 | // if (memory_renderer_total_trial_exists) { |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 61 | // HISTOGRAM_COUNTS(FieldTrial::MakeName("Memory.RendererTotal", |
| 62 | // "MemoryExperiment"), count); |
| 63 | // } |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 64 | |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 65 | // The above code will create four distinct histograms, with each run of the |
[email protected] | b37fdaa | 2009-07-01 01:14:56 | [diff] [blame] | 66 | // application being assigned to of of the three groups, and for each group, the |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 67 | // correspondingly named histogram will be populated: |
| 68 | |
[email protected] | f1d16d4a | 2011-03-21 14:04:01 | [diff] [blame] | 69 | // Memory.RendererTotal // 100% of users still fill this histogram. |
| 70 | // Memory.RendererTotal_HighMem // 2% of users will fill this histogram. |
| 71 | // Memory.RendererTotal_LowMem // 2% of users will fill this histogram. |
| 72 | // Memory.RendererTotal_StandardMem // 96% of users will fill this histogram. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 73 | |
| 74 | //------------------------------------------------------------------------------ |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 75 | |
[email protected] | 835d7c8 | 2010-10-14 04:38:38 | [diff] [blame] | 76 | #ifndef BASE_METRICS_FIELD_TRIAL_H_ |
| 77 | #define BASE_METRICS_FIELD_TRIAL_H_ |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 78 | |
| 79 | #include <map> |
| 80 | #include <string> |
[email protected] | ad2461c | 2012-04-27 21:11:03 | [diff] [blame] | 81 | #include <vector> |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 82 | |
[email protected] | 0bea725 | 2011-08-05 15:34:00 | [diff] [blame] | 83 | #include "base/base_export.h" |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 84 | #include "base/gtest_prod_util.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 85 | #include "base/memory/ref_counted.h" |
[email protected] | 8cffde0e | 2012-05-04 01:14:14 | [diff] [blame] | 86 | #include "base/observer_list_threadsafe.h" |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 87 | #include "base/synchronization/lock.h" |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 88 | #include "base/time.h" |
| 89 | |
[email protected] | 835d7c8 | 2010-10-14 04:38:38 | [diff] [blame] | 90 | namespace base { |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 91 | |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 92 | class FieldTrialList; |
| 93 | |
[email protected] | 0bea725 | 2011-08-05 15:34:00 | [diff] [blame] | 94 | class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 95 | public: |
[email protected] | e695fbd6 | 2009-06-30 16:31:54 | [diff] [blame] | 96 | typedef int Probability; // Probability type for being selected in a trial. |
[email protected] | ad2461c | 2012-04-27 21:11:03 | [diff] [blame] | 97 | |
[email protected] | 20f999b5 | 2012-08-24 22:32:59 | [diff] [blame] | 98 | // EntropyProvider is an interface for providing entropy for one-time |
| 99 | // randomized (persistent) field trials. |
| 100 | class BASE_EXPORT EntropyProvider { |
| 101 | public: |
| 102 | virtual ~EntropyProvider(); |
| 103 | |
| 104 | // Returns a double in the range of [0, 1) based on |trial_name| that will |
| 105 | // be used for the dice roll for the specified field trial. A given instance |
| 106 | // should always return the same value given the same input |trial_name|. |
| 107 | virtual double GetEntropyForTrial(const std::string& trial_name) const = 0; |
| 108 | }; |
| 109 | |
[email protected] | ad2461c | 2012-04-27 21:11:03 | [diff] [blame] | 110 | // A pair representing a Field Trial and its selected group. |
[email protected] | 0c8b7ad | 2012-11-06 07:08:14 | [diff] [blame] | 111 | struct ActiveGroup { |
[email protected] | bb65ef7 | 2012-11-19 20:07:59 | [diff] [blame] | 112 | std::string trial_name; |
| 113 | std::string group_name; |
[email protected] | 25655dd | 2012-01-27 13:50:26 | [diff] [blame] | 114 | }; |
[email protected] | e695fbd6 | 2009-06-30 16:31:54 | [diff] [blame] | 115 | |
[email protected] | 0c8b7ad | 2012-11-06 07:08:14 | [diff] [blame] | 116 | typedef std::vector<ActiveGroup> ActiveGroups; |
[email protected] | ad2461c | 2012-04-27 21:11:03 | [diff] [blame] | 117 | |
[email protected] | e695fbd6 | 2009-06-30 16:31:54 | [diff] [blame] | 118 | // A return value to indicate that a given instance has not yet had a group |
| 119 | // assignment (and hence is not yet participating in the trial). |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 120 | static const int kNotFinalized; |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 121 | |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 122 | // Changes the field trial to use one-time randomization, i.e. produce the |
| 123 | // same result for the current trial on every run of this client. Must be |
| 124 | // called right after construction. |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 125 | void UseOneTimeRandomization(); |
| 126 | |
| 127 | // Disables this trial, meaning it always determines the default group |
| 128 | // has been selected. May be called immediately after construction, or |
| 129 | // at any time after initialization (should not be interleaved with |
| 130 | // AppendGroup calls). Once disabled, there is no way to re-enable a |
| 131 | // trial. |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 132 | // TODO(mad): http://code.google.com/p/chromium/issues/detail?id=121446 |
| 133 | // This doesn't properly reset to Default when a group was forced. |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 134 | void Disable(); |
| 135 | |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 136 | // Establish the name and probability of the next group in this trial. |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 137 | // Sometimes, based on construction randomization, this call may cause the |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 138 | // provided group to be *THE* group selected for use in this instance. |
[email protected] | 9238ab9 | 2011-02-25 17:22:46 | [diff] [blame] | 139 | // The return value is the group number of the new group. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 140 | int AppendGroup(const std::string& name, Probability group_probability); |
| 141 | |
| 142 | // Return the name of the FieldTrial (excluding the group name). |
[email protected] | bb65ef7 | 2012-11-19 20:07:59 | [diff] [blame] | 143 | const std::string& trial_name() const { return trial_name_; } |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 144 | |
[email protected] | 7aea117 | 2012-11-01 18:59:47 | [diff] [blame] | 145 | // Return the randomly selected group number that was assigned, and notify |
| 146 | // any/all observers that this finalized group number has presumably been used |
| 147 | // (queried), and will never change. Note that this will force an instance to |
| 148 | // participate, and make it illegal to attempt to probabilistically add any |
| 149 | // other groups to the trial. |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 150 | int group(); |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 151 | |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 152 | // If the group's name is empty, a string version containing the group number |
| 153 | // is used as the group name. This causes a winner to be chosen if none was. |
[email protected] | bb65ef7 | 2012-11-19 20:07:59 | [diff] [blame] | 154 | const std::string& group_name(); |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 155 | |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 156 | // Helper function for the most common use: as an argument to specify the |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 157 | // name of a HISTOGRAM. Use the original histogram name as the name_prefix. |
| 158 | static std::string MakeName(const std::string& name_prefix, |
| 159 | const std::string& trial_name); |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 160 | |
[email protected] | e8d82c61 | 2010-12-07 22:54:27 | [diff] [blame] | 161 | // Enable benchmarking sets field trials to a common setting. |
| 162 | static void EnableBenchmarking(); |
| 163 | |
[email protected] | 0284bf7c | 2012-05-07 22:48:19 | [diff] [blame] | 164 | // Set the field trial as forced, meaning that it was setup earlier than |
| 165 | // the hard coded registration of the field trial to override it. |
| 166 | // This allows the code that was hard coded to register the field trial to |
| 167 | // still succeed even though the field trial has already been registered. |
| 168 | // This must be called after appending all the groups, since we will make |
| 169 | // the group choice here. Note that this is a NOOP for already forced trials. |
| 170 | // And, as the rest of the FieldTrial code, this is not thread safe and must |
| 171 | // be done from the UI thread. |
| 172 | void SetForced(); |
| 173 | |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 174 | private: |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 175 | // Allow tests to access our innards for testing purposes. |
[email protected] | 225020ce | 2011-11-29 14:45:53 | [diff] [blame] | 176 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration); |
| 177 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AbsoluteProbabilities); |
| 178 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, RemainingProbability); |
| 179 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FiftyFiftyProbability); |
| 180 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MiddleProbabilities); |
| 181 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner); |
| 182 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability); |
[email protected] | 0c8b7ad | 2012-11-06 07:08:14 | [diff] [blame] | 183 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups); |
| 184 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized); |
[email protected] | 225020ce | 2011-11-29 14:45:53 | [diff] [blame] | 185 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save); |
| 186 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore); |
| 187 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MakeName); |
| 188 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, HashClientId); |
| 189 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, HashClientIdIsUniform); |
[email protected] | 25655dd | 2012-01-27 13:50:26 | [diff] [blame] | 190 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, NameGroupIds); |
[email protected] | 225020ce | 2011-11-29 14:45:53 | [diff] [blame] | 191 | FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, UseOneTimeRandomization); |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 192 | |
| 193 | friend class base::FieldTrialList; |
| 194 | |
[email protected] | 835d7c8 | 2010-10-14 04:38:38 | [diff] [blame] | 195 | friend class RefCounted<FieldTrial>; |
[email protected] | 877d55d | 2009-11-05 21:53:08 | [diff] [blame] | 196 | |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 197 | // This is the group number of the 'default' group when a choice wasn't forced |
| 198 | // by a call to FieldTrialList::CreateFieldTrial. It is kept private so that |
| 199 | // consumers don't use it by mistake in cases where the group was forced. |
| 200 | static const int kDefaultGroupNumber; |
| 201 | |
[email protected] | 8826a6e8 | 2012-05-11 02:16:32 | [diff] [blame] | 202 | FieldTrial(const std::string& name, |
| 203 | Probability total_probability, |
| 204 | const std::string& default_group_name); |
[email protected] | 9b2331d9 | 2010-10-04 23:11:19 | [diff] [blame] | 205 | virtual ~FieldTrial(); |
[email protected] | 877d55d | 2009-11-05 21:53:08 | [diff] [blame] | 206 | |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 207 | // Return the default group name of the FieldTrial. |
| 208 | std::string default_group_name() const { return default_group_name_; } |
| 209 | |
[email protected] | bb65ef7 | 2012-11-19 20:07:59 | [diff] [blame] | 210 | // Sets the chosen group name and number. |
| 211 | void SetGroupChoice(const std::string& group_name, int number); |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 212 | |
[email protected] | 7aea117 | 2012-11-01 18:59:47 | [diff] [blame] | 213 | // Ensures that a group is chosen, if it hasn't yet been. The field trial |
| 214 | // might yet be disabled, so this call will *not* notify observers of the |
| 215 | // status. |
| 216 | void FinalizeGroupChoice(); |
| 217 | |
[email protected] | 0c8b7ad | 2012-11-06 07:08:14 | [diff] [blame] | 218 | // Returns the trial name and selected group name for this field trial via |
| 219 | // the output parameter |active_group|, but only if the group has already |
[email protected] | 8b18dd4 | 2012-11-10 03:19:43 | [diff] [blame] | 220 | // been chosen and has been externally observed via |group()| and the trial |
| 221 | // has not been disabled. In that case, true is returned and |active_group| |
| 222 | // is filled in; otherwise, the result is false and |active_group| is left |
| 223 | // untouched. |
[email protected] | 0c8b7ad | 2012-11-06 07:08:14 | [diff] [blame] | 224 | bool GetActiveGroup(ActiveGroup* active_group) const; |
| 225 | |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 226 | // Returns the group_name. A winner need not have been chosen. |
| 227 | std::string group_name_internal() const { return group_name_; } |
| 228 | |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 229 | // The name of the field trial, as can be found via the FieldTrialList. |
[email protected] | bb65ef7 | 2012-11-19 20:07:59 | [diff] [blame] | 230 | const std::string trial_name_; |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 231 | |
[email protected] | 58d2d2d | 2010-08-05 22:46:33 | [diff] [blame] | 232 | // The maximum sum of all probabilities supplied, which corresponds to 100%. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 233 | // This is the scaling factor used to adjust supplied probabilities. |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 234 | const Probability divisor_; |
| 235 | |
| 236 | // The name of the default group. |
| 237 | const std::string default_group_name_; |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 238 | |
| 239 | // The randomly selected probability that is used to select a group (or have |
| 240 | // the instance not participate). It is the product of divisor_ and a random |
| 241 | // number between [0, 1). |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 242 | Probability random_; |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 243 | |
| 244 | // Sum of the probabilities of all appended groups. |
| 245 | Probability accumulated_group_probability_; |
| 246 | |
| 247 | int next_group_number_; |
| 248 | |
| 249 | // The pseudo-randomly assigned group number. |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 250 | // This is kNotFinalized if no group has been assigned. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 251 | int group_; |
| 252 | |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 253 | // A textual name for the randomly selected group. Valid after |group()| |
| 254 | // has been called. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 255 | std::string group_name_; |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 256 | |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 257 | // When enable_field_trial_ is false, field trial reverts to the 'default' |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 258 | // group. |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 259 | bool enable_field_trial_; |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 260 | |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 261 | // When forced_ is true, we return the chosen group from AppendGroup when |
| 262 | // appropriate. |
| 263 | bool forced_; |
| 264 | |
[email protected] | 7aea117 | 2012-11-01 18:59:47 | [diff] [blame] | 265 | // Specifies whether the group choice has been reported to observers. |
| 266 | bool group_reported_; |
| 267 | |
[email protected] | e8d82c61 | 2010-12-07 22:54:27 | [diff] [blame] | 268 | // When benchmarking is enabled, field trials all revert to the 'default' |
[email protected] | 933729bc | 2011-01-19 18:52:32 | [diff] [blame] | 269 | // group. |
[email protected] | e8d82c61 | 2010-12-07 22:54:27 | [diff] [blame] | 270 | static bool enable_benchmarking_; |
| 271 | |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 272 | DISALLOW_COPY_AND_ASSIGN(FieldTrial); |
| 273 | }; |
| 274 | |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 275 | //------------------------------------------------------------------------------ |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 276 | // Class with a list of all active field trials. A trial is active if it has |
| 277 | // been registered, which includes evaluating its state based on its probaility. |
| 278 | // Only one instance of this class exists. |
[email protected] | 0bea725 | 2011-08-05 15:34:00 | [diff] [blame] | 279 | class BASE_EXPORT FieldTrialList { |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 280 | public: |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 281 | // Define a separator character to use when creating a persistent form of an |
[email protected] | e695fbd6 | 2009-06-30 16:31:54 | [diff] [blame] | 282 | // instance. This is intended for use as a command line argument, passed to a |
| 283 | // second process to mimic our state (i.e., provide the same group name). |
| 284 | static const char kPersistentStringSeparator; // Currently a slash. |
| 285 | |
[email protected] | d0c6929 | 2013-01-09 18:15:26 | [diff] [blame^] | 286 | // Year that is guaranteed to not be expired when instantiating a field trial |
| 287 | // via |FactoryGetFieldTrial()|. Set to two years from the build date. |
| 288 | static int kNoExpirationYear; |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 289 | |
| 290 | // Observer is notified when a FieldTrial's group is selected. |
[email protected] | 8cffde0e | 2012-05-04 01:14:14 | [diff] [blame] | 291 | class BASE_EXPORT Observer { |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 292 | public: |
| 293 | // Notify observers when FieldTrials's group is selected. |
| 294 | virtual void OnFieldTrialGroupFinalized(const std::string& trial_name, |
| 295 | const std::string& group_name) = 0; |
| 296 | |
| 297 | protected: |
[email protected] | 20f999b5 | 2012-08-24 22:32:59 | [diff] [blame] | 298 | virtual ~Observer(); |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 299 | }; |
| 300 | |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 301 | // This singleton holds the global list of registered FieldTrials. |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 302 | // |
[email protected] | 20f999b5 | 2012-08-24 22:32:59 | [diff] [blame] | 303 | // To support one-time randomized field trials, specify a non-NULL |
| 304 | // |entropy_provider| which should be a source of uniformly distributed |
| 305 | // entropy values. Takes ownership of |entropy_provider|. If one time |
| 306 | // randomization is not desired, pass in NULL for |entropy_provider|. |
| 307 | explicit FieldTrialList(const FieldTrial::EntropyProvider* entropy_provider); |
| 308 | |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 309 | // Destructor Release()'s references to all registered FieldTrial instances. |
| 310 | ~FieldTrialList(); |
| 311 | |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 312 | // Get a FieldTrial instance from the factory. |
| 313 | // |
| 314 | // |name| is used to register the instance with the FieldTrialList class, |
| 315 | // and can be used to find the trial (only one trial can be present for each |
| 316 | // name). |default_group_name| is the name of the default group which will |
| 317 | // be chosen if none of the subsequent appended groups get to be chosen. |
| 318 | // |default_group_number| can receive the group number of the default group as |
[email protected] | bb65ef7 | 2012-11-19 20:07:59 | [diff] [blame] | 319 | // AppendGroup returns the number of the subsequence groups. |trial_name| and |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 320 | // |default_group_name| may not be empty but |default_group_number| can be |
| 321 | // NULL if the value is not needed. |
| 322 | // |
| 323 | // Group probabilities that are later supplied must sum to less than or equal |
| 324 | // to the |total_probability|. Arguments |year|, |month| and |day_of_month| |
| 325 | // specify the expiration time. If the build time is after the expiration time |
| 326 | // then the field trial reverts to the 'default' group. |
| 327 | // |
| 328 | // Use this static method to get a startup-randomized FieldTrial or a |
| 329 | // previously created forced FieldTrial. If you want a one-time randomized |
| 330 | // trial, call UseOneTimeRandomization() right after creation. |
| 331 | static FieldTrial* FactoryGetFieldTrial( |
[email protected] | bb65ef7 | 2012-11-19 20:07:59 | [diff] [blame] | 332 | const std::string& trial_name, |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 333 | FieldTrial::Probability total_probability, |
| 334 | const std::string& default_group_name, |
| 335 | const int year, |
| 336 | const int month, |
| 337 | const int day_of_month, |
| 338 | int* default_group_number); |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 339 | |
| 340 | // The Find() method can be used to test to see if a named Trial was already |
| 341 | // registered, or to retrieve a pointer to it from the global map. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 342 | static FieldTrial* Find(const std::string& name); |
| 343 | |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 344 | // Returns the group number chosen for the named trial, or |
| 345 | // FieldTrial::kNotFinalized if the trial does not exist. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 346 | static int FindValue(const std::string& name); |
| 347 | |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 348 | // Returns the group name chosen for the named trial, or the |
| 349 | // empty string if the trial does not exist. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 350 | static std::string FindFullName(const std::string& name); |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 351 | |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 352 | // Returns true if the named trial has been registered. |
| 353 | static bool TrialExists(const std::string& name); |
| 354 | |
[email protected] | e6ade47d | 2012-11-07 19:46:28 | [diff] [blame] | 355 | // Creates a persistent representation of active FieldTrial instances for |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 356 | // resurrection in another process. This allows randomization to be done in |
| 357 | // one process, and secondary processes can be synchronized on the result. |
[email protected] | e6ade47d | 2012-11-07 19:46:28 | [diff] [blame] | 358 | // The resulting string contains the name and group name pairs of all |
| 359 | // registered FieldTrials for which the group has been chosen and externally |
[email protected] | 8b18dd4 | 2012-11-10 03:19:43 | [diff] [blame] | 360 | // observed (via |group()|) and which have not been disabled, with "/" used |
| 361 | // to separate all names and to terminate the string. This string is parsed |
| 362 | // by |CreateTrialsFromString()|. |
[email protected] | e695fbd6 | 2009-06-30 16:31:54 | [diff] [blame] | 363 | static void StatesToString(std::string* output); |
| 364 | |
[email protected] | 0c8b7ad | 2012-11-06 07:08:14 | [diff] [blame] | 365 | // Fills in the supplied vector |active_groups| (which must be empty when |
| 366 | // called) with a snapshot of all registered FieldTrials for which the group |
[email protected] | 8b18dd4 | 2012-11-10 03:19:43 | [diff] [blame] | 367 | // has been chosen and externally observed (via |group()|) and which have |
| 368 | // not been disabled. |
[email protected] | 0c8b7ad | 2012-11-06 07:08:14 | [diff] [blame] | 369 | static void GetActiveFieldTrialGroups( |
| 370 | FieldTrial::ActiveGroups* active_groups); |
[email protected] | 25655dd | 2012-01-27 13:50:26 | [diff] [blame] | 371 | |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 372 | // Use a state string (re: StatesToString()) to augment the current list of |
[email protected] | e290b03 | 2012-11-13 06:31:09 | [diff] [blame] | 373 | // field trials to include the supplied trials, and using a 100% probability |
| 374 | // for each trial, force them to have the same group string. This is commonly |
| 375 | // used in a non-browser process, to carry randomly selected state in a |
| 376 | // browser process into this non-browser process, but could also be invoked |
| 377 | // through a command line argument to the browser process. The created field |
| 378 | // trials are marked as "used" for the purposes of active trial reporting. |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 379 | static bool CreateTrialsFromString(const std::string& prior_trials); |
[email protected] | e695fbd6 | 2009-06-30 16:31:54 | [diff] [blame] | 380 | |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 381 | // Create a FieldTrial with the given |name| and using 100% probability for |
| 382 | // the FieldTrial, force FieldTrial to have the same group string as |
| 383 | // |group_name|. This is commonly used in a non-browser process, to carry |
| 384 | // randomly selected state in a browser process into this non-browser process. |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 385 | // It returns NULL if there is a FieldTrial that is already registered with |
| 386 | // the same |name| but has different finalized group string (|group_name|). |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 387 | static FieldTrial* CreateFieldTrial(const std::string& name, |
| 388 | const std::string& group_name); |
| 389 | |
| 390 | // Add an observer to be notified when a field trial is irrevocably committed |
| 391 | // to being part of some specific field_group (and hence the group_name is |
| 392 | // also finalized for that field_trial). |
| 393 | static void AddObserver(Observer* observer); |
| 394 | |
| 395 | // Remove an observer. |
| 396 | static void RemoveObserver(Observer* observer); |
| 397 | |
[email protected] | 907432e | 2012-12-12 04:16:21 | [diff] [blame] | 398 | // Notify all observers that a group has been finalized for |field_trial|. |
| 399 | static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial); |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 400 | |
[email protected] | e8d82c61 | 2010-12-07 22:54:27 | [diff] [blame] | 401 | // Return the number of active field trials. |
| 402 | static size_t GetFieldTrialCount(); |
| 403 | |
[email protected] | 20f999b5 | 2012-08-24 22:32:59 | [diff] [blame] | 404 | // If one-time randomization is enabled, returns a weak pointer to the |
| 405 | // corresponding EntropyProvider. Otherwise, returns NULL. |
| 406 | static const FieldTrial::EntropyProvider* |
| 407 | GetEntropyProviderForOneTimeRandomization(); |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 408 | |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 409 | private: |
[email protected] | e695fbd6 | 2009-06-30 16:31:54 | [diff] [blame] | 410 | // A map from FieldTrial names to the actual instances. |
[email protected] | 9660b97 | 2009-03-02 19:02:56 | [diff] [blame] | 411 | typedef std::map<std::string, FieldTrial*> RegistrationList; |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 412 | |
[email protected] | a502bbe7 | 2011-01-07 18:06:45 | [diff] [blame] | 413 | // Helper function should be called only while holding lock_. |
| 414 | FieldTrial* PreLockedFind(const std::string& name); |
| 415 | |
[email protected] | 2d472958 | 2012-04-12 07:08:07 | [diff] [blame] | 416 | // Register() stores a pointer to the given trial in a global map. |
| 417 | // This method also AddRef's the indicated trial. |
| 418 | // This should always be called after creating a new FieldTrial instance. |
| 419 | static void Register(FieldTrial* trial); |
| 420 | |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 421 | static FieldTrialList* global_; // The singleton of this class. |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 422 | |
[email protected] | 0f1373a | 2011-06-22 23:51:24 | [diff] [blame] | 423 | // This will tell us if there is an attempt to register a field |
| 424 | // trial or check if one-time randomization is enabled without |
| 425 | // creating the FieldTrialList. This is not an error, unless a |
| 426 | // FieldTrialList is created after that. |
| 427 | static bool used_without_global_; |
[email protected] | 7e05f6c4 | 2009-07-11 01:50:48 | [diff] [blame] | 428 | |
[email protected] | 0b48db4 | 2009-03-23 02:45:11 | [diff] [blame] | 429 | // Lock for access to registered_. |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 430 | base::Lock lock_; |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 431 | RegistrationList registered_; |
| 432 | |
[email protected] | 20f999b5 | 2012-08-24 22:32:59 | [diff] [blame] | 433 | // Entropy provider to be used for one-time randomized field trials. If NULL, |
| 434 | // one-time randomization is not supported. |
| 435 | scoped_ptr<const FieldTrial::EntropyProvider> entropy_provider_; |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 436 | |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 437 | // List of observers to be notified when a group is selected for a FieldTrial. |
[email protected] | 8cffde0e | 2012-05-04 01:14:14 | [diff] [blame] | 438 | scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_; |
[email protected] | 63a8ba1 | 2011-04-29 05:42:22 | [diff] [blame] | 439 | |
[email protected] | ac262c9f | 2008-10-19 17:45:21 | [diff] [blame] | 440 | DISALLOW_COPY_AND_ASSIGN(FieldTrialList); |
| 441 | }; |
| 442 | |
[email protected] | 835d7c8 | 2010-10-14 04:38:38 | [diff] [blame] | 443 | } // namespace base |
| 444 | |
| 445 | #endif // BASE_METRICS_FIELD_TRIAL_H_ |