blob: 3b831d8fc4d72c03b972ae3c1227e8e9ab6dc64f [file] [log] [blame]
[email protected]a502bbe72011-01-07 18:06:451// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]ac262c9f2008-10-19 17:45:212// 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]9660b972009-03-02 19:02:569// 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]ac262c9f2008-10-19 17:45:2115// States are typically generated randomly, either based on a one time
[email protected]edafd4c2011-05-10 17:18:5316// 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]9660b972009-03-02 19:02:5623
24//------------------------------------------------------------------------------
25// Example: Suppose we have an experiment involving memory, such as determining
[email protected]57a336a2009-09-30 20:42:2726// the impact of some pruning algorithm.
[email protected]9660b972009-03-02 19:02:5627// 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]f1d16d4a2011-03-21 14:04:0134// // FieldTrials are reference counted, and persist automagically until
[email protected]9660b972009-03-02 19:02:5635// // process teardown, courtesy of their automatic registration in
36// // FieldTrialList.
[email protected]f1d16d4a2011-03-21 14:04:0137// // Note: This field trial will run in Chrome instances compiled through
38// // 8 July, 2015, and after that all instances will be in "StandardMem".
39// scoped_refptr<FieldTrial> trial = new FieldTrial("MemoryExperiment", 1000,
40// "StandardMem", 2015, 7, 8);
41// const int kHighMemGroup =
42// trial->AppendGroup("HighMem", 20); // 2% in HighMem group.
43// const int kLowMemGroup =
44// trial->AppendGroup("LowMem", 20); // 2% in LowMem group.
[email protected]9660b972009-03-02 19:02:5645// // Take action depending of which group we randomly land in.
[email protected]f1d16d4a2011-03-21 14:04:0146// if (trial->group() == kHighMemGroup)
[email protected]57a336a2009-09-30 20:42:2747// SetPruningAlgorithm(kType1); // Sample setting of browser state.
[email protected]f1d16d4a2011-03-21 14:04:0148// else if (trial->group() == kLowMemGroup)
[email protected]57a336a2009-09-30 20:42:2749// SetPruningAlgorithm(kType2); // Sample alternate setting.
[email protected]9660b972009-03-02 19:02:5650
[email protected]f1d16d4a2011-03-21 14:04:0151// We then, in addition to our original histogram, output histograms which have
52// slightly different names depending on what group the trial instance happened
53// to randomly be assigned:
[email protected]9660b972009-03-02 19:02:5654
[email protected]f1d16d4a2011-03-21 14:04:0155// HISTOGRAM_COUNTS("Memory.RendererTotal", count); // The original histogram.
[email protected]edafd4c2011-05-10 17:18:5356// static const bool memory_renderer_total_trial_exists =
57// FieldTrialList::TrialExists("Memory.RendererTotal");
58// if (memory_renderer_total_trial_exists) {
[email protected]f1d16d4a2011-03-21 14:04:0159// HISTOGRAM_COUNTS(FieldTrial::MakeName("Memory.RendererTotal",
60// "MemoryExperiment"), count);
61// }
[email protected]9660b972009-03-02 19:02:5662
[email protected]f1d16d4a2011-03-21 14:04:0163// The above code will create four distinct histograms, with each run of the
[email protected]b37fdaa2009-07-01 01:14:5664// application being assigned to of of the three groups, and for each group, the
[email protected]9660b972009-03-02 19:02:5665// correspondingly named histogram will be populated:
66
[email protected]f1d16d4a2011-03-21 14:04:0167// Memory.RendererTotal // 100% of users still fill this histogram.
68// Memory.RendererTotal_HighMem // 2% of users will fill this histogram.
69// Memory.RendererTotal_LowMem // 2% of users will fill this histogram.
70// Memory.RendererTotal_StandardMem // 96% of users will fill this histogram.
[email protected]9660b972009-03-02 19:02:5671
72//------------------------------------------------------------------------------
[email protected]ac262c9f2008-10-19 17:45:2173
[email protected]835d7c82010-10-14 04:38:3874#ifndef BASE_METRICS_FIELD_TRIAL_H_
75#define BASE_METRICS_FIELD_TRIAL_H_
[email protected]32b76ef2010-07-26 23:08:2476#pragma once
[email protected]ac262c9f2008-10-19 17:45:2177
78#include <map>
79#include <string>
80
[email protected]f5661ca2011-03-24 19:00:2081#include "base/base_api.h"
[email protected]933729bc2011-01-19 18:52:3282#include "base/gtest_prod_util.h"
[email protected]3b63f8f42011-03-28 01:54:1583#include "base/memory/ref_counted.h"
[email protected]63a8ba12011-04-29 05:42:2284#include "base/observer_list.h"
[email protected]20305ec2011-01-21 04:55:5285#include "base/synchronization/lock.h"
[email protected]ac262c9f2008-10-19 17:45:2186#include "base/time.h"
87
[email protected]835d7c82010-10-14 04:38:3888namespace base {
[email protected]9660b972009-03-02 19:02:5689
[email protected]933729bc2011-01-19 18:52:3290class FieldTrialList;
91
[email protected]f5661ca2011-03-24 19:00:2092class BASE_API FieldTrial : public RefCounted<FieldTrial> {
[email protected]ac262c9f2008-10-19 17:45:2193 public:
[email protected]e695fbd62009-06-30 16:31:5494 typedef int Probability; // Probability type for being selected in a trial.
95
96 // A return value to indicate that a given instance has not yet had a group
97 // assignment (and hence is not yet participating in the trial).
[email protected]933729bc2011-01-19 18:52:3298 static const int kNotFinalized;
[email protected]9660b972009-03-02 19:02:5699
[email protected]933729bc2011-01-19 18:52:32100 // This is the group number of the 'default' group. This provides an easy way
101 // to assign all the remaining probability to a group ('default').
102 static const int kDefaultGroupNumber;
[email protected]9660b972009-03-02 19:02:56103
[email protected]ac262c9f2008-10-19 17:45:21104 // The name is used to register the instance with the FieldTrialList class,
105 // and can be used to find the trial (only one trial can be present for each
[email protected]edafd4c2011-05-10 17:18:53106 // name). |name| and |default_group_name| may not be empty.
107 //
[email protected]9660b972009-03-02 19:02:56108 // Group probabilities that are later supplied must sum to less than or equal
[email protected]933729bc2011-01-19 18:52:32109 // to the total_probability. Arguments year, month and day_of_month specify
110 // the expiration time. If the build time is after the expiration time then
111 // the field trial reverts to the 'default' group.
[email protected]edafd4c2011-05-10 17:18:53112 //
113 // Using this constructor creates a startup-randomized FieldTrial. If you
114 // want a one-time randomized trial, call UseOneTimeRandomization() right
115 // after construction.
[email protected]933729bc2011-01-19 18:52:32116 FieldTrial(const std::string& name, Probability total_probability,
117 const std::string& default_group_name, const int year,
118 const int month, const int day_of_month);
[email protected]ac262c9f2008-10-19 17:45:21119
[email protected]edafd4c2011-05-10 17:18:53120 // Changes the field trial to use one-time randomization, i.e. produce the
121 // same result for the current trial on every run of this client. Must be
122 // called right after construction.
123 //
124 // Before using this method, |FieldTrialList::EnableOneTimeRandomization()|
125 // must be called exactly once.
126 void UseOneTimeRandomization();
127
128 // Disables this trial, meaning it always determines the default group
129 // has been selected. May be called immediately after construction, or
130 // at any time after initialization (should not be interleaved with
131 // AppendGroup calls). Once disabled, there is no way to re-enable a
132 // trial.
133 void Disable();
134
[email protected]9660b972009-03-02 19:02:56135 // Establish the name and probability of the next group in this trial.
[email protected]933729bc2011-01-19 18:52:32136 // Sometimes, based on construction randomization, this call may cause the
[email protected]9660b972009-03-02 19:02:56137 // provided group to be *THE* group selected for use in this instance.
[email protected]9238ab92011-02-25 17:22:46138 // The return value is the group number of the new group.
[email protected]9660b972009-03-02 19:02:56139 int AppendGroup(const std::string& name, Probability group_probability);
140
141 // Return the name of the FieldTrial (excluding the group name).
142 std::string name() const { return name_; }
143
144 // Return the randomly selected group number that was assigned.
[email protected]933729bc2011-01-19 18:52:32145 // Return kDefaultGroupNumber if the instance is in the 'default' group.
146 // Note that this will force an instance to participate, and make it illegal
[email protected]edafd4c2011-05-10 17:18:53147 // to attempt to probabilistically add any other groups to the trial.
[email protected]933729bc2011-01-19 18:52:32148 int group();
[email protected]9660b972009-03-02 19:02:56149
[email protected]edafd4c2011-05-10 17:18:53150 // If the group's name is empty, a string version containing the group
[email protected]9660b972009-03-02 19:02:56151 // number is used as the group name.
[email protected]933729bc2011-01-19 18:52:32152 std::string group_name();
153
154 // Return the default group name of the FieldTrial.
155 std::string default_group_name() const { return default_group_name_; }
[email protected]9660b972009-03-02 19:02:56156
[email protected]edafd4c2011-05-10 17:18:53157 // Helper function for the most common use: as an argument to specify the
[email protected]9660b972009-03-02 19:02:56158 // name of a HISTOGRAM. Use the original histogram name as the name_prefix.
159 static std::string MakeName(const std::string& name_prefix,
160 const std::string& trial_name);
[email protected]ac262c9f2008-10-19 17:45:21161
[email protected]e8d82c612010-12-07 22:54:27162 // Enable benchmarking sets field trials to a common setting.
163 static void EnableBenchmarking();
164
[email protected]ac262c9f2008-10-19 17:45:21165 private:
[email protected]933729bc2011-01-19 18:52:32166 // Allow tests to access our innards for testing purposes.
167 FRIEND_TEST(FieldTrialTest, Registration);
168 FRIEND_TEST(FieldTrialTest, AbsoluteProbabilities);
169 FRIEND_TEST(FieldTrialTest, RemainingProbability);
170 FRIEND_TEST(FieldTrialTest, FiftyFiftyProbability);
171 FRIEND_TEST(FieldTrialTest, MiddleProbabilities);
172 FRIEND_TEST(FieldTrialTest, OneWinner);
173 FRIEND_TEST(FieldTrialTest, DisableProbability);
174 FRIEND_TEST(FieldTrialTest, Save);
175 FRIEND_TEST(FieldTrialTest, DuplicateRestore);
176 FRIEND_TEST(FieldTrialTest, MakeName);
[email protected]edafd4c2011-05-10 17:18:53177 FRIEND_TEST(FieldTrialTest, HashClientId);
178 FRIEND_TEST(FieldTrialTest, HashClientIdIsUniform);
179 FRIEND_TEST(FieldTrialTest, UseOneTimeRandomization);
[email protected]933729bc2011-01-19 18:52:32180
181 friend class base::FieldTrialList;
182
[email protected]835d7c82010-10-14 04:38:38183 friend class RefCounted<FieldTrial>;
[email protected]877d55d2009-11-05 21:53:08184
[email protected]9b2331d92010-10-04 23:11:19185 virtual ~FieldTrial();
[email protected]877d55d2009-11-05 21:53:08186
[email protected]933729bc2011-01-19 18:52:32187 // Returns the group_name. A winner need not have been chosen.
188 std::string group_name_internal() const { return group_name_; }
189
190 // Get build time.
191 static Time GetBuildTime();
192
[email protected]edafd4c2011-05-10 17:18:53193 // Calculates a uniformly-distributed double between [0.0, 1.0) given
194 // a |client_id| and a |trial_name| (the latter is used as salt to avoid
195 // separate one-time randomized trials from all having the same results).
196 static double HashClientId(const std::string& client_id,
197 const std::string& trial_name);
198
[email protected]9660b972009-03-02 19:02:56199 // The name of the field trial, as can be found via the FieldTrialList.
[email protected]9660b972009-03-02 19:02:56200 const std::string name_;
201
[email protected]58d2d2d2010-08-05 22:46:33202 // The maximum sum of all probabilities supplied, which corresponds to 100%.
[email protected]9660b972009-03-02 19:02:56203 // This is the scaling factor used to adjust supplied probabilities.
[email protected]933729bc2011-01-19 18:52:32204 const Probability divisor_;
205
206 // The name of the default group.
207 const std::string default_group_name_;
[email protected]9660b972009-03-02 19:02:56208
209 // The randomly selected probability that is used to select a group (or have
210 // the instance not participate). It is the product of divisor_ and a random
211 // number between [0, 1).
[email protected]edafd4c2011-05-10 17:18:53212 Probability random_;
[email protected]9660b972009-03-02 19:02:56213
214 // Sum of the probabilities of all appended groups.
215 Probability accumulated_group_probability_;
216
217 int next_group_number_;
218
219 // The pseudo-randomly assigned group number.
[email protected]933729bc2011-01-19 18:52:32220 // This is kNotFinalized if no group has been assigned.
[email protected]9660b972009-03-02 19:02:56221 int group_;
222
[email protected]edafd4c2011-05-10 17:18:53223 // A textual name for the randomly selected group. Valid after |group()|
224 // has been called.
[email protected]9660b972009-03-02 19:02:56225 std::string group_name_;
[email protected]ac262c9f2008-10-19 17:45:21226
[email protected]edafd4c2011-05-10 17:18:53227 // When enable_field_trial_ is false, field trial reverts to the 'default'
[email protected]933729bc2011-01-19 18:52:32228 // group.
[email protected]edafd4c2011-05-10 17:18:53229 bool enable_field_trial_;
[email protected]933729bc2011-01-19 18:52:32230
[email protected]e8d82c612010-12-07 22:54:27231 // When benchmarking is enabled, field trials all revert to the 'default'
[email protected]933729bc2011-01-19 18:52:32232 // group.
[email protected]e8d82c612010-12-07 22:54:27233 static bool enable_benchmarking_;
234
[email protected]ac262c9f2008-10-19 17:45:21235 DISALLOW_COPY_AND_ASSIGN(FieldTrial);
236};
237
[email protected]9660b972009-03-02 19:02:56238//------------------------------------------------------------------------------
[email protected]ac262c9f2008-10-19 17:45:21239// Class with a list of all active field trials. A trial is active if it has
240// been registered, which includes evaluating its state based on its probaility.
241// Only one instance of this class exists.
[email protected]f5661ca2011-03-24 19:00:20242class BASE_API FieldTrialList {
[email protected]ac262c9f2008-10-19 17:45:21243 public:
[email protected]e695fbd62009-06-30 16:31:54244 // Define a separator charactor to use when creating a persistent form of an
245 // instance. This is intended for use as a command line argument, passed to a
246 // second process to mimic our state (i.e., provide the same group name).
247 static const char kPersistentStringSeparator; // Currently a slash.
248
[email protected]63a8ba12011-04-29 05:42:22249 // Define expiration year in future. It is initialized to two years from Now.
250 static int kExpirationYearInFuture;
251
252 // Observer is notified when a FieldTrial's group is selected.
253 class Observer {
254 public:
255 // Notify observers when FieldTrials's group is selected.
256 virtual void OnFieldTrialGroupFinalized(const std::string& trial_name,
257 const std::string& group_name) = 0;
258
259 protected:
260 virtual ~Observer() {}
261 };
262
[email protected]ac262c9f2008-10-19 17:45:21263 // This singleton holds the global list of registered FieldTrials.
[email protected]edafd4c2011-05-10 17:18:53264 //
265 // |client_id| should be an opaque, diverse ID for this client that does not
266 // change between sessions, to enable one-time randomized trials. The empty
267 // string may be provided, in which case one-time randomized trials will
268 // not be available.
269 explicit FieldTrialList(const std::string& client_id);
[email protected]ac262c9f2008-10-19 17:45:21270 // Destructor Release()'s references to all registered FieldTrial instances.
271 ~FieldTrialList();
272
273 // Register() stores a pointer to the given trial in a global map.
274 // This method also AddRef's the indicated trial.
275 static void Register(FieldTrial* trial);
276
277 // The Find() method can be used to test to see if a named Trial was already
278 // registered, or to retrieve a pointer to it from the global map.
[email protected]9660b972009-03-02 19:02:56279 static FieldTrial* Find(const std::string& name);
280
[email protected]edafd4c2011-05-10 17:18:53281 // Returns the group number chosen for the named trial, or
282 // FieldTrial::kNotFinalized if the trial does not exist.
[email protected]9660b972009-03-02 19:02:56283 static int FindValue(const std::string& name);
284
[email protected]edafd4c2011-05-10 17:18:53285 // Returns the group name chosen for the named trial, or the
286 // empty string if the trial does not exist.
[email protected]9660b972009-03-02 19:02:56287 static std::string FindFullName(const std::string& name);
[email protected]ac262c9f2008-10-19 17:45:21288
[email protected]edafd4c2011-05-10 17:18:53289 // Returns true if the named trial has been registered.
290 static bool TrialExists(const std::string& name);
291
292 // Create a persistent representation of all FieldTrial instances and the
293 // |client_id()| state for resurrection in another process. This allows
294 // randomization to be done in one process, and secondary processes can by
295 // synchronized on the result. The resulting string contains the
296 // |client_id()|, the names, the trial name, and a "/" separator.
[email protected]e695fbd62009-06-30 16:31:54297 static void StatesToString(std::string* output);
298
299 // Use a previously generated state string (re: StatesToString()) augment the
300 // current list of field tests to include the supplied tests, and using a 100%
301 // probability for each test, force them to have the same group string. This
[email protected]63a8ba12011-04-29 05:42:22302 // is commonly used in a non-browser process, to carry randomly selected state
303 // in a browser process into this non-browser process. This method calls
304 // CreateFieldTrial to create the FieldTrial in the non-browser process.
[email protected]edafd4c2011-05-10 17:18:53305 // Currently only the group_name_ and name_ are restored, as well as the
306 // |client_id()| state, that could be used for one-time randomized trials
307 // set up only in child processes.
[email protected]933729bc2011-01-19 18:52:32308 static bool CreateTrialsInChildProcess(const std::string& prior_trials);
[email protected]e695fbd62009-06-30 16:31:54309
[email protected]63a8ba12011-04-29 05:42:22310 // Create a FieldTrial with the given |name| and using 100% probability for
311 // the FieldTrial, force FieldTrial to have the same group string as
312 // |group_name|. This is commonly used in a non-browser process, to carry
313 // randomly selected state in a browser process into this non-browser process.
314 // Currently only the group_name_ and name_ are set in the FieldTrial. It
315 // returns NULL if there is a FieldTrial that is already registered with the
316 // same |name| but has different finalized group string (|group_name|).
317 static FieldTrial* CreateFieldTrial(const std::string& name,
318 const std::string& group_name);
319
320 // Add an observer to be notified when a field trial is irrevocably committed
321 // to being part of some specific field_group (and hence the group_name is
322 // also finalized for that field_trial).
323 static void AddObserver(Observer* observer);
324
325 // Remove an observer.
326 static void RemoveObserver(Observer* observer);
327
328 // Notify all observers that a group is finalized for the named Trial.
329 static void NotifyFieldTrialGroupSelection(const std::string& name,
330 const std::string& group_name);
331
[email protected]ac262c9f2008-10-19 17:45:21332 // The time of construction of the global map is recorded in a static variable
333 // and is commonly used by experiments to identify the time since the start
334 // of the application. In some experiments it may be useful to discount
[email protected]9660b972009-03-02 19:02:56335 // data that is gathered before the application has reached sufficient
[email protected]ac262c9f2008-10-19 17:45:21336 // stability (example: most DLL have loaded, etc.)
[email protected]835d7c82010-10-14 04:38:38337 static TimeTicks application_start_time() {
[email protected]423041b2008-10-27 17:39:28338 if (global_)
339 return global_->application_start_time_;
340 // For testing purposes only, or when we don't yet have a start time.
[email protected]835d7c82010-10-14 04:38:38341 return TimeTicks::Now();
[email protected]ac262c9f2008-10-19 17:45:21342 }
343
[email protected]e8d82c612010-12-07 22:54:27344 // Return the number of active field trials.
345 static size_t GetFieldTrialCount();
346
[email protected]edafd4c2011-05-10 17:18:53347 // Returns true if you can call |FieldTrial::UseOneTimeRandomization()|
348 // without error, i.e. if a non-empty string was provided as the client_id
349 // when constructing the FieldTrialList singleton.
350 static bool IsOneTimeRandomizationEnabled();
351
352 // Returns an opaque, diverse ID for this client that does not change
353 // between sessions.
354 //
355 // Returns the empty string if one-time randomization is not enabled.
356 static const std::string& client_id();
357
[email protected]ac262c9f2008-10-19 17:45:21358 private:
[email protected]e695fbd62009-06-30 16:31:54359 // A map from FieldTrial names to the actual instances.
[email protected]9660b972009-03-02 19:02:56360 typedef std::map<std::string, FieldTrial*> RegistrationList;
[email protected]ac262c9f2008-10-19 17:45:21361
[email protected]a502bbe72011-01-07 18:06:45362 // Helper function should be called only while holding lock_.
363 FieldTrial* PreLockedFind(const std::string& name);
364
[email protected]ac262c9f2008-10-19 17:45:21365 static FieldTrialList* global_; // The singleton of this class.
[email protected]ac262c9f2008-10-19 17:45:21366
[email protected]0f1373a2011-06-22 23:51:24367 // This will tell us if there is an attempt to register a field
368 // trial or check if one-time randomization is enabled without
369 // creating the FieldTrialList. This is not an error, unless a
370 // FieldTrialList is created after that.
371 static bool used_without_global_;
[email protected]7e05f6c42009-07-11 01:50:48372
[email protected]edafd4c2011-05-10 17:18:53373 // A helper value made available to users, that shows when the FieldTrialList
[email protected]e695fbd62009-06-30 16:31:54374 // was initialized. Note that this is a singleton instance, and hence is a
375 // good approximation to the start of the process.
[email protected]835d7c82010-10-14 04:38:38376 TimeTicks application_start_time_;
[email protected]0b48db42009-03-23 02:45:11377
378 // Lock for access to registered_.
[email protected]20305ec2011-01-21 04:55:52379 base::Lock lock_;
[email protected]ac262c9f2008-10-19 17:45:21380 RegistrationList registered_;
381
[email protected]edafd4c2011-05-10 17:18:53382 // An opaque, diverse ID for this client that does not change
383 // between sessions, or the empty string if not initialized.
384 std::string client_id_;
385
[email protected]63a8ba12011-04-29 05:42:22386 // List of observers to be notified when a group is selected for a FieldTrial.
387 ObserverList<Observer> observer_list_;
388
[email protected]ac262c9f2008-10-19 17:45:21389 DISALLOW_COPY_AND_ASSIGN(FieldTrialList);
390};
391
[email protected]835d7c82010-10-14 04:38:38392} // namespace base
393
394#endif // BASE_METRICS_FIELD_TRIAL_H_