field_trial.h 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. // Copyright (c) 2012 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. // FieldTrial is a class for handling details of statistical experiments
  5. // performed by actual users in the field (i.e., in a shipped or beta product).
  6. // All code is called exclusively on the UI thread currently.
  7. //
  8. // The simplest example is an experiment to see whether one of two options
  9. // produces "better" results across our user population. In that scenario, UMA
  10. // data is uploaded to aggregate the test results, and this FieldTrial class
  11. // manages the state of each such experiment (state == which option was
  12. // pseudo-randomly selected).
  13. //
  14. // States are typically generated randomly, either based on a one time
  15. // randomization (which will yield the same results, in terms of selecting
  16. // the client for a field trial or not, for every run of the program on a
  17. // given machine), or by a session randomization (generated each time the
  18. // application starts up, but held constant during the duration of the
  19. // process).
  20. //------------------------------------------------------------------------------
  21. // Example: Suppose we have an experiment involving memory, such as determining
  22. // the impact of some pruning algorithm.
  23. // We assume that we already have a histogram of memory usage, such as:
  24. // UMA_HISTOGRAM_COUNTS_1M("Memory.RendererTotal", count);
  25. // Somewhere in main thread initialization code, we'd probably define an
  26. // instance of a FieldTrial, with code such as:
  27. // // FieldTrials are reference counted, and persist automagically until
  28. // // process teardown, courtesy of their automatic registration in
  29. // // FieldTrialList.
  30. // // Note: This field trial will run in Chrome instances compiled through
  31. // // 8 July, 2015, and after that all instances will be in "StandardMem".
  32. // scoped_refptr<base::FieldTrial> trial(
  33. // base::FieldTrialList::FactoryGetFieldTrial(
  34. // "MemoryExperiment", 1000, "StandardMem",
  35. // base::FieldTrial::ONE_TIME_RANDOMIZED, nullptr));
  36. //
  37. // const int high_mem_group =
  38. // trial->AppendGroup("HighMem", 20); // 2% in HighMem group.
  39. // const int low_mem_group =
  40. // trial->AppendGroup("LowMem", 20); // 2% in LowMem group.
  41. // // Take action depending of which group we randomly land in.
  42. // if (trial->group() == high_mem_group)
  43. // SetPruningAlgorithm(kType1); // Sample setting of browser state.
  44. // else if (trial->group() == low_mem_group)
  45. // SetPruningAlgorithm(kType2); // Sample alternate setting.
  46. //------------------------------------------------------------------------------
  47. #ifndef BASE_METRICS_FIELD_TRIAL_H_
  48. #define BASE_METRICS_FIELD_TRIAL_H_
  49. #include <stddef.h>
  50. #include <stdint.h>
  51. #include <atomic>
  52. #include <functional>
  53. #include <map>
  54. #include <memory>
  55. #include <string>
  56. #include <vector>
  57. #include "base/atomicops.h"
  58. #include "base/base_export.h"
  59. #include "base/command_line.h"
  60. #include "base/feature_list.h"
  61. #include "base/gtest_prod_util.h"
  62. #include "base/memory/raw_ptr.h"
  63. #include "base/memory/read_only_shared_memory_region.h"
  64. #include "base/memory/ref_counted.h"
  65. #include "base/memory/shared_memory_mapping.h"
  66. #include "base/metrics/persistent_memory_allocator.h"
  67. #include "base/pickle.h"
  68. #include "base/strings/string_piece.h"
  69. #include "base/synchronization/lock.h"
  70. #include "base/types/pass_key.h"
  71. #include "build/build_config.h"
  72. namespace base {
  73. namespace test {
  74. class ScopedFeatureList;
  75. } // namespace test
  76. class FieldTrialList;
  77. struct LaunchOptions;
  78. class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
  79. public:
  80. typedef int Probability; // Probability type for being selected in a trial.
  81. // Specifies the persistence of the field trial group choice.
  82. enum RandomizationType {
  83. // One time randomized trials will persist the group choice between
  84. // restarts, which is recommended for most trials, especially those that
  85. // change user visible behavior.
  86. ONE_TIME_RANDOMIZED,
  87. // Session randomized trials will roll the dice to select a group on every
  88. // process restart.
  89. SESSION_RANDOMIZED,
  90. };
  91. // EntropyProvider is an interface for providing entropy for one-time
  92. // randomized (persistent) field trials.
  93. class BASE_EXPORT EntropyProvider {
  94. public:
  95. virtual ~EntropyProvider();
  96. // Returns a double in the range of [0, 1) to be used for the dice roll for
  97. // the specified field trial. If |randomization_seed| is not 0, it will be
  98. // used in preference to |trial_name| for generating the entropy by entropy
  99. // providers that support it. A given instance should always return the same
  100. // value given the same input |trial_name| and |randomization_seed| values.
  101. virtual double GetEntropyForTrial(StringPiece trial_name,
  102. uint32_t randomization_seed) const = 0;
  103. };
  104. // Separate type from FieldTrial::PickleState so that it can use StringPieces.
  105. struct State {
  106. StringPiece trial_name;
  107. StringPiece group_name;
  108. bool activated = false;
  109. };
  110. // A pair representing a Field Trial and its selected group.
  111. struct ActiveGroup {
  112. std::string trial_name;
  113. std::string group_name;
  114. };
  115. // A triplet representing a FieldTrial, its selected group and whether it's
  116. // active. String members are pointers to the underlying strings owned by the
  117. // FieldTrial object. Does not use StringPiece to avoid conversions back to
  118. // std::string.
  119. struct BASE_EXPORT PickleState {
  120. raw_ptr<const std::string, DanglingUntriaged> trial_name = nullptr;
  121. raw_ptr<const std::string, DanglingUntriaged> group_name = nullptr;
  122. bool activated = false;
  123. PickleState();
  124. PickleState(const PickleState& other);
  125. ~PickleState();
  126. };
  127. // We create one FieldTrialEntry per field trial in shared memory, via
  128. // AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a
  129. // base::Pickle object that we unpickle and read from.
  130. struct BASE_EXPORT FieldTrialEntry {
  131. // SHA1(FieldTrialEntry): Increment this if structure changes!
  132. static constexpr uint32_t kPersistentTypeId = 0xABA17E13 + 3;
  133. // Expected size for 32/64-bit check.
  134. static constexpr size_t kExpectedInstanceSize = 16;
  135. // Whether or not this field trial is activated. This is really just a
  136. // boolean but using a 32 bit value for portability reasons. It should be
  137. // accessed via NoBarrier_Load()/NoBarrier_Store() to prevent the compiler
  138. // from doing unexpected optimizations because it thinks that only one
  139. // thread is accessing the memory location.
  140. subtle::Atomic32 activated;
  141. // On e.g. x86, alignof(uint64_t) is 4. Ensure consistent size and
  142. // alignment of `pickle_size` across platforms.
  143. uint32_t padding;
  144. // Size of the pickled structure, NOT the total size of this entry.
  145. uint64_t pickle_size;
  146. // Calling this is only valid when the entry is initialized. That is, it
  147. // resides in shared memory and has a pickle containing the trial name and
  148. // group name following it.
  149. bool GetTrialAndGroupName(StringPiece* trial_name,
  150. StringPiece* group_name) const;
  151. // Calling this is only valid when the entry is initialized as well. Reads
  152. // the parameters following the trial and group name and stores them as
  153. // key-value mappings in |params|.
  154. bool GetParams(std::map<std::string, std::string>* params) const;
  155. private:
  156. // Returns an iterator over the data containing names and params.
  157. PickleIterator GetPickleIterator() const;
  158. // Takes the iterator and writes out the first two items into |trial_name|
  159. // and |group_name|.
  160. bool ReadStringPair(PickleIterator* iter,
  161. StringPiece* trial_name,
  162. StringPiece* group_name) const;
  163. };
  164. typedef std::vector<ActiveGroup> ActiveGroups;
  165. // A return value to indicate that a given instance has not yet had a group
  166. // assignment (and hence is not yet participating in the trial).
  167. static const int kNotFinalized;
  168. FieldTrial(const FieldTrial&) = delete;
  169. FieldTrial& operator=(const FieldTrial&) = delete;
  170. // Disables this trial, meaning the default group is always selected. May be
  171. // called immediately after construction or at any time after initialization;
  172. // however, it cannot be called after group(). Once disabled, there is no way
  173. // to re-enable a trial.
  174. void Disable();
  175. // Establishes the name and probability of the next group in this trial.
  176. // Sometimes, based on construction randomization, this call may cause the
  177. // provided group to be *THE* group selected for use in this instance.
  178. // The return value is the group number of the new group. AppendGroup can be
  179. // called after calls to group() but it should be avoided if possible. Doing
  180. // so may be confusing since it won't change the group selection.
  181. int AppendGroup(const std::string& name, Probability group_probability);
  182. // Return the name of the FieldTrial (excluding the group name).
  183. const std::string& trial_name() const { return trial_name_; }
  184. // Return the randomly selected group number that was assigned, and notify
  185. // any/all observers that this finalized group number has presumably been used
  186. // (queried), and will never change. Note that this will force an instance to
  187. // participate, and make it illegal to attempt to probabilistically add any
  188. // other groups to the trial.
  189. int group();
  190. // If the group's name is empty, a string version containing the group number
  191. // is used as the group name. This causes a winner to be chosen if none was.
  192. const std::string& group_name();
  193. // Finalizes the group choice and returns the chosen group, but does not mark
  194. // the trial as active - so its state will not be reported until group_name()
  195. // or similar is called.
  196. const std::string& GetGroupNameWithoutActivation();
  197. // Set the field trial as forced, meaning that it was setup earlier than
  198. // the hard coded registration of the field trial to override it.
  199. // This allows the code that was hard coded to register the field trial to
  200. // still succeed even though the field trial has already been registered.
  201. // This must be called after appending all the groups, since we will make
  202. // the group choice here. Note that this is a NOOP for already forced trials.
  203. // And, as the rest of the FieldTrial code, this is not thread safe and must
  204. // be done from the UI thread.
  205. void SetForced();
  206. // Supports benchmarking by causing field trials' default groups to be chosen.
  207. static void EnableBenchmarking();
  208. // Creates a FieldTrial object with the specified parameters, to be used for
  209. // simulation of group assignment without actually affecting global field
  210. // trial state in the running process. Group assignment will be done based on
  211. // |entropy_value|, which must have a range of [0, 1).
  212. //
  213. // Note: Using this function will not register the field trial globally in the
  214. // running process - for that, use FieldTrialList::FactoryGetFieldTrial().
  215. //
  216. // The ownership of the returned FieldTrial is transfered to the caller which
  217. // is responsible for deref'ing it (e.g. by using scoped_refptr<FieldTrial>).
  218. static FieldTrial* CreateSimulatedFieldTrial(StringPiece trial_name,
  219. Probability total_probability,
  220. StringPiece default_group_name,
  221. double entropy_value);
  222. private:
  223. // Allow tests to access our innards for testing purposes.
  224. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration);
  225. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AbsoluteProbabilities);
  226. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, RemainingProbability);
  227. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FiftyFiftyProbability);
  228. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MiddleProbabilities);
  229. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner);
  230. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability);
  231. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups);
  232. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AllGroups);
  233. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized);
  234. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save);
  235. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SaveAll);
  236. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore);
  237. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOff);
  238. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn);
  239. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default);
  240. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault);
  241. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ObserveReentrancy);
  242. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes);
  243. FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability);
  244. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
  245. DoNotAddSimulatedFieldTrialsToAllocator);
  246. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory);
  247. friend class base::FieldTrialList;
  248. friend class RefCounted<FieldTrial>;
  249. using FieldTrialRef = PersistentMemoryAllocator::Reference;
  250. // This is the group number of the 'default' group when a choice wasn't forced
  251. // by a call to FieldTrialList::CreateFieldTrial. It is kept private so that
  252. // consumers don't use it by mistake in cases where the group was forced.
  253. static const int kDefaultGroupNumber;
  254. // Creates a field trial with the specified parameters. Group assignment will
  255. // be done based on |entropy_value|, which must have a range of [0, 1).
  256. FieldTrial(StringPiece trial_name,
  257. Probability total_probability,
  258. StringPiece default_group_name,
  259. double entropy_value);
  260. virtual ~FieldTrial();
  261. // Return the default group name of the FieldTrial.
  262. const std::string& default_group_name() const { return default_group_name_; }
  263. // Marks this trial as having been registered with the FieldTrialList. Must be
  264. // called no more than once and before any |group()| calls have occurred.
  265. void SetTrialRegistered();
  266. // Sets the chosen group name and number.
  267. void SetGroupChoice(const std::string& group_name, int number);
  268. // Ensures that a group is chosen, if it hasn't yet been. The field trial
  269. // might yet be disabled, so this call will *not* notify observers of the
  270. // status.
  271. void FinalizeGroupChoice();
  272. // Implements FinalizeGroupChoice() with the added flexibility of being
  273. // deadlock-free if |is_locked| is true and the caller is holding a lock.
  274. void FinalizeGroupChoiceImpl(bool is_locked);
  275. // Returns the trial name and selected group name for this field trial via
  276. // the output parameter |active_group|, but only if the group has already
  277. // been chosen and has been externally observed via |group()| and the trial
  278. // has not been disabled. In that case, true is returned and |active_group|
  279. // is filled in; otherwise, the result is false and |active_group| is left
  280. // untouched.
  281. bool GetActiveGroup(ActiveGroup* active_group) const;
  282. // Returns the trial name and selected group name for this field trial via
  283. // the output parameter |field_trial_state| for all the studies when
  284. // |include_disabled| is true. In case when |include_disabled| is false, if
  285. // the trial has not been disabled true is returned and |field_trial_state|
  286. // is filled in; otherwise, the result is false and |field_trial_state| is
  287. // left untouched.
  288. bool GetStateWhileLocked(PickleState* field_trial_state,
  289. bool include_disabled);
  290. // Returns the group_name. A winner need not have been chosen.
  291. const std::string& group_name_internal() const { return group_name_; }
  292. // The name of the field trial, as can be found via the FieldTrialList.
  293. const std::string trial_name_;
  294. // The maximum sum of all probabilities supplied, which corresponds to 100%.
  295. // This is the scaling factor used to adjust supplied probabilities.
  296. const Probability divisor_;
  297. // The name of the default group.
  298. const std::string default_group_name_;
  299. // The randomly selected probability that is used to select a group (or have
  300. // the instance not participate). It is the product of divisor_ and a random
  301. // number between [0, 1).
  302. Probability random_;
  303. // Sum of the probabilities of all appended groups.
  304. Probability accumulated_group_probability_;
  305. // The number that will be returned by the next AppendGroup() call.
  306. int next_group_number_;
  307. // The pseudo-randomly assigned group number.
  308. // This is kNotFinalized if no group has been assigned.
  309. int group_;
  310. // A textual name for the randomly selected group. Valid after |group()|
  311. // has been called.
  312. std::string group_name_;
  313. // When enable_field_trial_ is false, field trial reverts to the 'default'
  314. // group.
  315. bool enable_field_trial_;
  316. // When forced_ is true, we return the chosen group from AppendGroup when
  317. // appropriate.
  318. bool forced_;
  319. // Specifies whether the group choice has been reported to observers.
  320. bool group_reported_;
  321. // Whether this trial is registered with the global FieldTrialList and thus
  322. // should notify it when its group is queried.
  323. bool trial_registered_;
  324. // Reference to related field trial struct and data in shared memory.
  325. FieldTrialRef ref_;
  326. // Denotes whether benchmarking is enabled. In this case, field trials all
  327. // revert to the default group.
  328. static bool enable_benchmarking_;
  329. };
  330. //------------------------------------------------------------------------------
  331. // Class with a list of all active field trials. A trial is active if it has
  332. // been registered, which includes evaluating its state based on its probaility.
  333. // Only one instance of this class exists and outside of testing, will live for
  334. // the entire life time of the process.
  335. class BASE_EXPORT FieldTrialList {
  336. public:
  337. using FieldTrialAllocator = PersistentMemoryAllocator;
  338. // Type for function pointer passed to |AllParamsToString| used to escape
  339. // special characters from |input|.
  340. typedef std::string (*EscapeDataFunc)(const std::string& input);
  341. // Observer is notified when a FieldTrial's group is selected.
  342. class BASE_EXPORT Observer {
  343. public:
  344. // Notify observers when FieldTrials's group is selected.
  345. virtual void OnFieldTrialGroupFinalized(const std::string& trial_name,
  346. const std::string& group_name) = 0;
  347. protected:
  348. virtual ~Observer();
  349. };
  350. // This singleton holds the global list of registered FieldTrials.
  351. //
  352. // To support one-time randomized field trials, specify a non-null
  353. // |entropy_provider| which should be a source of uniformly distributed
  354. // entropy values. If one time randomization is not desired, pass in null for
  355. // |entropy_provider|.
  356. explicit FieldTrialList(
  357. std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider);
  358. FieldTrialList(const FieldTrialList&) = delete;
  359. FieldTrialList& operator=(const FieldTrialList&) = delete;
  360. // Destructor Release()'s references to all registered FieldTrial instances.
  361. ~FieldTrialList();
  362. // Gets a FieldTrial instance from the factory.
  363. //
  364. // |trial_name| (a) is used to register the instance with the FieldTrialList
  365. // class and (b) can be used to find the trial (only one trial can be present
  366. // for each name). |default_group_name| is the name of the group that is
  367. // chosen if none of the subsequent appended groups are chosen. Note that the
  368. // default group is also chosen whenever |enable_benchmarking_| is true.
  369. // |default_group_number| can receive the group number of the default group as
  370. // AppendGroup returns the number of the subsequence groups. |trial_name| and
  371. // |default_group_name| must not be empty, but |default_group_number| can be
  372. // null if the value is not needed.
  373. //
  374. // Group probabilities that are later supplied must sum to less than or equal
  375. // to the |total_probability|.
  376. //
  377. // Use this static method to get a startup-randomized FieldTrial or a
  378. // previously created forced FieldTrial.
  379. static FieldTrial* FactoryGetFieldTrial(
  380. StringPiece trial_name,
  381. FieldTrial::Probability total_probability,
  382. StringPiece default_group_name,
  383. FieldTrial::RandomizationType randomization_type,
  384. int* default_group_number);
  385. // Same as FactoryGetFieldTrial(), but allows specifying a custom seed to be
  386. // used on one-time randomized field trials (instead of a hash of the trial
  387. // name, which is used otherwise or if |randomization_seed| has value 0). The
  388. // |randomization_seed| value (other than 0) should never be the same for two
  389. // trials, else this would result in correlated group assignments. Note:
  390. // Using a custom randomization seed is only supported by the
  391. // NormalizedMurmurHashEntropyProvider, which is used when UMA is not enabled
  392. // (and is always used in Android WebView, where UMA is enabled
  393. // asyncronously). If |override_entropy_provider| is not null, then it will be
  394. // used for randomization instead of the provider given when the
  395. // FieldTrialList was instantiated.
  396. static FieldTrial* FactoryGetFieldTrialWithRandomizationSeed(
  397. StringPiece trial_name,
  398. FieldTrial::Probability total_probability,
  399. StringPiece default_group_name,
  400. FieldTrial::RandomizationType randomization_type,
  401. uint32_t randomization_seed,
  402. int* default_group_number,
  403. const FieldTrial::EntropyProvider* override_entropy_provider);
  404. // The Find() method can be used to test to see if a named trial was already
  405. // registered, or to retrieve a pointer to it from the global map.
  406. static FieldTrial* Find(StringPiece trial_name);
  407. // Returns the group number chosen for the named trial, or
  408. // FieldTrial::kNotFinalized if the trial does not exist.
  409. static int FindValue(StringPiece trial_name);
  410. // Returns the group name chosen for the named trial, or the empty string if
  411. // the trial does not exist. The first call of this function on a given field
  412. // trial will mark it as active, so that its state will be reported with usage
  413. // metrics, crashes, etc.
  414. // Note: Direct use of this function and related FieldTrial functions is
  415. // generally discouraged - instead please use base::Feature when possible.
  416. static std::string FindFullName(StringPiece trial_name);
  417. // Returns true if the named trial has been registered.
  418. static bool TrialExists(StringPiece trial_name);
  419. // Returns true if the named trial exists and has been activated.
  420. static bool IsTrialActive(StringPiece trial_name);
  421. // Creates a persistent representation of active FieldTrial instances for
  422. // resurrection in another process. This allows randomization to be done in
  423. // one process, and secondary processes can be synchronized on the result.
  424. // The resulting string contains the name and group name pairs of all
  425. // registered FieldTrials for which the group has been chosen and externally
  426. // observed (via |group()|) and which have not been disabled, with "/" used
  427. // to separate all names and to terminate the string. This string is parsed
  428. // by |CreateTrialsFromString()|.
  429. static void StatesToString(std::string* output);
  430. // Creates a persistent representation of all FieldTrial instances for
  431. // resurrection in another process. This allows randomization to be done in
  432. // one process, and secondary processes can be synchronized on the result.
  433. // The resulting string contains the name and group name pairs of all
  434. // registered FieldTrials including disabled based on |include_disabled|,
  435. // with "/" used to separate all names and to terminate the string. All
  436. // activated trials have their name prefixed with "*". This string is parsed
  437. // by |CreateTrialsFromString()|.
  438. static void AllStatesToString(std::string* output, bool include_disabled);
  439. // Creates a persistent representation of all FieldTrial params for
  440. // resurrection in another process. The returned string contains the trial
  441. // name and group name pairs of all registered FieldTrials including disabled
  442. // based on |include_disabled| separated by '.'. The pair is followed by ':'
  443. // separator and list of param name and values separated by '/'. It also takes
  444. // |encode_data_func| function pointer for encodeing special charactors.
  445. // This string is parsed by |AssociateParamsFromString()|.
  446. static std::string AllParamsToString(bool include_disabled,
  447. EscapeDataFunc encode_data_func);
  448. // Fills in the supplied vector |active_groups| (which must be empty when
  449. // called) with a snapshot of all registered FieldTrials for which the group
  450. // has been chosen and externally observed (via |group()|) and which have
  451. // not been disabled.
  452. static void GetActiveFieldTrialGroups(
  453. FieldTrial::ActiveGroups* active_groups);
  454. // Returns the field trials that are marked active in |trials_string|.
  455. static void GetActiveFieldTrialGroupsFromString(
  456. const std::string& trials_string,
  457. FieldTrial::ActiveGroups* active_groups);
  458. // Returns the field trials that were active when the process was
  459. // created. Either parses the field trial string or the shared memory
  460. // holding field trial information.
  461. // Must be called only after a call to CreateTrialsFromCommandLine().
  462. static void GetInitiallyActiveFieldTrials(
  463. const CommandLine& command_line,
  464. FieldTrial::ActiveGroups* active_groups);
  465. // Use a state string (re: StatesToString()) to augment the current list of
  466. // field trials to include the supplied trials, and using a 100% probability
  467. // for each trial, force them to have the same group string. This is commonly
  468. // used in a non-browser process, to carry randomly selected state in a
  469. // browser process into this non-browser process, but could also be invoked
  470. // through a command line argument to the browser process. Created field
  471. // trials will be marked "used" for the purposes of active trial reporting
  472. // if they are prefixed with |kActivationMarker|.
  473. static bool CreateTrialsFromString(const std::string& trials_string);
  474. // Achieves the same thing as CreateTrialsFromString, except wraps the logic
  475. // by taking in the trials from the command line, either via shared memory
  476. // handle or command line argument.
  477. // On non-Mac POSIX platforms, we simply get the trials from opening |fd_key|
  478. // if using shared memory. The argument is needed here since //base can't
  479. // depend on //content. |fd_key| is unused on other platforms.
  480. // On other platforms, we expect the |cmd_line| switch for kFieldTrialHandle
  481. // to contain the shared memory handle that contains the field trial
  482. // allocator.
  483. static void CreateTrialsFromCommandLine(const CommandLine& cmd_line,
  484. uint32_t fd_key);
  485. // Creates base::Feature overrides from the command line by first trying to
  486. // use shared memory and then falling back to the command line if it fails.
  487. static void CreateFeaturesFromCommandLine(const CommandLine& command_line,
  488. FeatureList* feature_list);
  489. #if !BUILDFLAG(IS_IOS)
  490. // Populates |command_line| and |launch_options| with the handles and command
  491. // line arguments necessary for a child process to inherit the shared-memory
  492. // object containing the FieldTrial configuration.
  493. static void PopulateLaunchOptionsWithFieldTrialState(
  494. CommandLine* command_line,
  495. LaunchOptions* launch_options);
  496. #endif // !BUILDFLAG(IS_IOS)
  497. #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_NACL)
  498. // On POSIX, we also need to explicitly pass down this file descriptor that
  499. // should be shared with the child process. Returns -1 if it was not
  500. // initialized properly. The current process remains the onwer of the passed
  501. // descriptor.
  502. static int GetFieldTrialDescriptor();
  503. #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_NACL)
  504. static ReadOnlySharedMemoryRegion DuplicateFieldTrialSharedMemoryForTesting();
  505. // Create a FieldTrial with the given |name| and using 100% probability for
  506. // the FieldTrial, force FieldTrial to have the same group string as
  507. // |group_name|. This is commonly used in a non-browser process, to carry
  508. // randomly selected state in a browser process into this non-browser process.
  509. // It returns NULL if there is a FieldTrial that is already registered with
  510. // the same |name| but has different finalized group string (|group_name|).
  511. static FieldTrial* CreateFieldTrial(StringPiece name, StringPiece group_name);
  512. // Add an observer to be notified when a field trial is irrevocably committed
  513. // to being part of some specific field_group (and hence the group_name is
  514. // also finalized for that field_trial). Returns false and does nothing if
  515. // there is no FieldTrialList singleton. The observer can be notified on any
  516. // sequence; it must be thread-safe.
  517. static bool AddObserver(Observer* observer);
  518. // Remove an observer. This cannot be invoked concurrently with
  519. // FieldTrial::group() (typically, this means that no other thread should be
  520. // running when this is invoked).
  521. static void RemoveObserver(Observer* observer);
  522. // Grabs the lock if necessary and adds the field trial to the allocator. This
  523. // should only be called from FinalizeGroupChoice().
  524. static void OnGroupFinalized(bool is_locked, FieldTrial* field_trial);
  525. // Notify all observers that a group has been finalized for |field_trial|.
  526. static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial);
  527. // Return the number of active field trials.
  528. static size_t GetFieldTrialCount();
  529. // Gets the parameters for |field_trial| from shared memory and stores them in
  530. // |params|. This is only exposed for use by FieldTrialParamAssociator and
  531. // shouldn't be used by anything else.
  532. static bool GetParamsFromSharedMemory(
  533. FieldTrial* field_trial,
  534. std::map<std::string, std::string>* params);
  535. // Clears all the params in the allocator.
  536. static void ClearParamsFromSharedMemoryForTesting();
  537. // Dumps field trial state to an allocator so that it can be analyzed after a
  538. // crash.
  539. static void DumpAllFieldTrialsToPersistentAllocator(
  540. PersistentMemoryAllocator* allocator);
  541. // Retrieves field trial state from an allocator so that it can be analyzed
  542. // after a crash. The pointers in the returned vector are into the persistent
  543. // memory segment and so are only valid as long as the allocator is valid.
  544. static std::vector<const FieldTrial::FieldTrialEntry*>
  545. GetAllFieldTrialsFromPersistentAllocator(
  546. PersistentMemoryAllocator const& allocator);
  547. // If one-time randomization is enabled, returns a weak pointer to the
  548. // corresponding EntropyProvider. Otherwise, returns nullptr.
  549. static const FieldTrial::EntropyProvider*
  550. GetEntropyProviderForOneTimeRandomization();
  551. // Returns a pointer to the global instance. This is exposed so that it can
  552. // be used in a DCHECK in FeatureList and ScopedFeatureList test-only logic
  553. // and is not intended to be used widely beyond those cases.
  554. static FieldTrialList* GetInstance();
  555. // For testing, sets the global instance to null and returns the previous one.
  556. static FieldTrialList* BackupInstanceForTesting();
  557. // For testing, sets the global instance to |instance|.
  558. static void RestoreInstanceForTesting(FieldTrialList* instance);
  559. // Creates a list of FieldTrial::State for all FieldTrial instances.
  560. // StringPiece members are bound to the lifetime of the corresponding
  561. // FieldTrial.
  562. static std::vector<FieldTrial::State> GetAllFieldTrialStates(
  563. PassKey<test::ScopedFeatureList>);
  564. // Create FieldTrials from a list of FieldTrial::State. This method is only
  565. // available to ScopedFeatureList for testing. The most typical usescase is:
  566. // (1) AllStatesToFieldTrialStates(&field_trials);
  567. // (2) backup_ = BackupInstanceForTesting();
  568. // // field_trials depends on backup_'s lifetype.
  569. // (3) field_trial_list_ = new FieldTrialList();
  570. // (4) CreateTrialsFromFieldTrialStates(field_trials);
  571. // // Copy backup_'s fieldtrials to the new field_trial_list_ while
  572. // // backup_ is alive.
  573. // For resurrestion in another process, need to use AllStatesToString and
  574. // CreateFieldTrialsFromString.
  575. static bool CreateTrialsFromFieldTrialStates(
  576. PassKey<test::ScopedFeatureList>,
  577. const std::vector<FieldTrial::State>& entries);
  578. private:
  579. // Allow tests to access our innards for testing purposes.
  580. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator);
  581. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AddTrialsToAllocator);
  582. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
  583. DoNotAddSimulatedFieldTrialsToAllocator);
  584. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AssociateFieldTrialParams);
  585. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory);
  586. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
  587. SerializeSharedMemoryRegionMetadata);
  588. friend int SerializeSharedMemoryRegionMetadata();
  589. FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, CheckReadOnlySharedMemoryRegion);
  590. #if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS)
  591. // Serialization is used to pass information about the shared memory handle
  592. // to child processes. This is achieved by passing a stringified reference to
  593. // the relevant OS resources to the child process.
  594. //
  595. // Serialization populates |launch_options| with the relevant OS handles to
  596. // transfer or copy to the child process and returns serialized information
  597. // to be passed to the kFieldTrialHandle command-line switch.
  598. // Note: On non-Mac POSIX platforms, it is necessary to pass down the file
  599. // descriptor for the shared memory separately. It can be accessed via the
  600. // GetFieldTrialDescriptor() API.
  601. static std::string SerializeSharedMemoryRegionMetadata(
  602. const ReadOnlySharedMemoryRegion& shm,
  603. LaunchOptions* launch_options);
  604. // Deserialization instantiates the shared memory region for FieldTrials from
  605. // the serialized information contained in |switch_value|. Returns an invalid
  606. // ReadOnlySharedMemoryRegion on failure.
  607. // |fd| is used on non-Mac POSIX platforms to instantiate the shared memory
  608. // region via a file descriptor.
  609. static ReadOnlySharedMemoryRegion DeserializeSharedMemoryRegionMetadata(
  610. const std::string& switch_value,
  611. int fd);
  612. // Takes in |handle_switch| from the command line which represents the shared
  613. // memory handle for field trials, parses it, and creates the field trials.
  614. // Returns true on success, false on failure.
  615. // |switch_value| also contains the serialized GUID.
  616. // |fd_key| is used on non-Mac POSIX platforms as the file descriptor passed
  617. // down to the child process for the shared memory region.
  618. static bool CreateTrialsFromSwitchValue(const std::string& switch_value,
  619. uint32_t fd_key);
  620. #endif // !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_IOS)
  621. // Takes an unmapped ReadOnlySharedMemoryRegion, maps it with the correct size
  622. // and creates field trials via CreateTrialsFromSharedMemoryMapping(). Returns
  623. // true if successful and false otherwise.
  624. static bool CreateTrialsFromSharedMemoryRegion(
  625. const ReadOnlySharedMemoryRegion& shm_region);
  626. // Expects a mapped piece of shared memory |shm_mapping| that was created from
  627. // the browser process's field_trial_allocator and shared via the command
  628. // line. This function recreates the allocator, iterates through all the field
  629. // trials in it, and creates them via CreateFieldTrial(). Returns true if
  630. // successful and false otherwise.
  631. static bool CreateTrialsFromSharedMemoryMapping(
  632. ReadOnlySharedMemoryMapping shm_mapping);
  633. // Instantiate the field trial allocator, add all existing field trials to it,
  634. // and duplicates its handle to a read-only handle, which gets stored in
  635. // |readonly_allocator_handle|.
  636. static void InstantiateFieldTrialAllocatorIfNeeded();
  637. // Adds the field trial to the allocator. Caller must hold a lock before
  638. // calling this.
  639. static void AddToAllocatorWhileLocked(PersistentMemoryAllocator* allocator,
  640. FieldTrial* field_trial);
  641. // Activate the corresponding field trial entry struct in shared memory.
  642. static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial);
  643. // A map from FieldTrial names to the actual instances.
  644. typedef std::map<std::string, FieldTrial*, std::less<>> RegistrationMap;
  645. // Helper function should be called only while holding lock_.
  646. FieldTrial* PreLockedFind(StringPiece name) EXCLUSIVE_LOCKS_REQUIRED(lock_);
  647. // Register() stores a pointer to the given trial in a global map.
  648. // This method also AddRef's the indicated trial.
  649. // This should always be called after creating a new FieldTrial instance.
  650. static void Register(FieldTrial* trial);
  651. // Returns all the registered trials.
  652. static RegistrationMap GetRegisteredTrials();
  653. // Create field trials from a list of FieldTrial::State.
  654. // CreateTrialsFromString() and CreateTrialsFromFieldTrialStates() use this
  655. // method internally.
  656. static bool CreateTrialsFromFieldTrialStatesInternal(
  657. const std::vector<FieldTrial::State>& entries);
  658. static FieldTrialList* global_; // The singleton of this class.
  659. // This will tell us if there is an attempt to register a field
  660. // trial or check if one-time randomization is enabled without
  661. // creating the FieldTrialList. This is not an error, unless a
  662. // FieldTrialList is created after that.
  663. static bool used_without_global_;
  664. // Lock for access to |registered_|, |observers_|.
  665. Lock lock_;
  666. RegistrationMap registered_ GUARDED_BY(lock_);
  667. // Entropy provider to be used for one-time randomized field trials. If NULL,
  668. // one-time randomization is not supported.
  669. std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider_;
  670. // List of observers to be notified when a group is selected for a FieldTrial.
  671. std::vector<Observer*> observers_ GUARDED_BY(lock_);
  672. // Counts the ongoing calls to
  673. // FieldTrialList::NotifyFieldTrialGroupSelection(). Used to ensure that
  674. // RemoveObserver() isn't called while notifying observers.
  675. std::atomic_int num_ongoing_notify_field_trial_group_selection_calls_{0};
  676. // Allocator in shared memory containing field trial data. Used in both
  677. // browser and child processes, but readonly in the child.
  678. // In the future, we may want to move this to a more generic place if we want
  679. // to start passing more data other than field trials.
  680. std::unique_ptr<FieldTrialAllocator> field_trial_allocator_;
  681. // Readonly copy of the region to the allocator. Needs to be a member variable
  682. // because it's needed from multiple methods.
  683. ReadOnlySharedMemoryRegion readonly_allocator_region_;
  684. // Tracks whether CreateTrialsFromCommandLine() has been called.
  685. bool create_trials_from_command_line_called_ = false;
  686. };
  687. } // namespace base
  688. #endif // BASE_METRICS_FIELD_TRIAL_H_