1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // The FieldTrial class handles the lower level configuration of running A/B 6 // tests. 7 // 8 // Most server-side experiments should be configured using Features which 9 // have a simpler interface. See base/feature_list.h for details on 10 // configurating a Feature for an experiment. 11 12 // In certain cases you may still need to use FieldTrial directly. This is 13 // generally for either: 14 // - Client-configured experiments: 15 // The experiment is controlled directly in the code. For example, if the 16 // server controlled behavior is not yet available. See below documentation. 17 // - Synthetic field trials: 18 // These act like field trials for reporting purposes, but the group 19 // placement is controlled directly. See RegisterSyntheticFieldTrial(). 20 21 // If you have access, see go/client-side-field-trials for additional context. 22 23 //------------------------------------------------------------------------------ 24 // Details: 25 26 // FieldTrial is a class for handling details of statistical experiments 27 // performed by actual users in the field (i.e., in a shipped or beta product). 28 // All code is called exclusively on the UI thread currently. It only handles 29 // the lower level details, server-side experiments should use 30 // generally use Features (see above). 31 // 32 // The simplest example is an experiment to see whether one of two options 33 // produces "better" results across our user population. In that scenario, UMA 34 // data is uploaded to aggregate the test results, and this FieldTrial class 35 // manages the state of each such experiment (state == which option was 36 // pseudo-randomly selected). 37 // 38 // States are typically generated randomly, either based on a one time 39 // randomization (which will yield the same results, in terms of selecting 40 // the client for a field trial or not, for every run of the program on a 41 // given machine), or by a session randomization (generated each time the 42 // application starts up, but held constant during the duration of the 43 // process). 44 45 //------------------------------------------------------------------------------ 46 // Example: Suppose we have an experiment involving memory, such as determining 47 // the impact of some pruning algorithm. Note that using this API directly is 48 // not recommended, see above. 49 50 // // FieldTrials are reference counted, and persist automagically until 51 // // process teardown, courtesy of their automatic registration in 52 // // FieldTrialList. 53 // scoped_refptr<base::FieldTrial> trial( 54 // base::FieldTrialList::FactoryGetFieldTrial( 55 // "MemoryExperiment", 1000, "StandardMem", entropy_provider); 56 // 57 // trial->AppendGroup("HighMem", 20); // 2% in HighMem group. 58 // trial->AppendGroup("LowMem", 20); // 2% in LowMem group. 59 // // Take action depending of which group we randomly land in. 60 // if (trial->group_name() == "HighMem") 61 // SetPruningAlgorithm(kType1); 62 // else if (trial->group_name() == "LowMem") 63 // SetPruningAlgorithm(kType2); 64 65 //------------------------------------------------------------------------------ 66 67 #ifndef BASE_METRICS_FIELD_TRIAL_H_ 68 #define BASE_METRICS_FIELD_TRIAL_H_ 69 70 #include <stddef.h> 71 #include <stdint.h> 72 73 #include <atomic> 74 #include <functional> 75 #include <map> 76 #include <memory> 77 #include <set> 78 #include <string> 79 #include <string_view> 80 #include <vector> 81 82 #include "base/atomicops.h" 83 #include "base/base_export.h" 84 #include "base/command_line.h" 85 #include "base/feature_list.h" 86 #include "base/gtest_prod_util.h" 87 #include "base/memory/raw_ptr.h" 88 #include "base/memory/read_only_shared_memory_region.h" 89 #include "base/memory/ref_counted.h" 90 #include "base/memory/shared_memory_mapping.h" 91 #include "base/metrics/persistent_memory_allocator.h" 92 #include "base/pickle.h" 93 #include "base/synchronization/lock.h" 94 #include "base/types/expected.h" 95 #include "base/types/pass_key.h" 96 #include "build/blink_buildflags.h" 97 #include "build/build_config.h" 98 99 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) 100 #include "base/files/platform_file.h" 101 #include "base/posix/global_descriptors.h" 102 #endif 103 104 namespace base { 105 106 namespace test { 107 class ScopedFeatureList; 108 } // namespace test 109 110 class CompareActiveGroupToFieldTrialMatcher; 111 class FieldTrialList; 112 struct LaunchOptions; 113 114 #if BUILDFLAG(USE_BLINK) 115 namespace shared_memory { 116 enum class SharedMemoryError; 117 } // namespace shared_memory 118 #endif 119 120 class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { 121 public: 122 typedef int Probability; // Probability type for being selected in a trial. 123 124 // EntropyProvider is an interface for providing entropy for one-time 125 // randomized (persistent) field trials. 126 class BASE_EXPORT EntropyProvider { 127 public: 128 virtual ~EntropyProvider(); 129 130 // Returns a double in the range of [0, 1) to be used for the dice roll for 131 // the specified field trial. If |randomization_seed| is not 0, it will be 132 // used in preference to |trial_name| for generating the entropy by entropy 133 // providers that support it. A given instance should always return the same 134 // value given the same input |trial_name| and |randomization_seed| values. 135 virtual double GetEntropyForTrial(std::string_view trial_name, 136 uint32_t randomization_seed) const = 0; 137 138 // Returns a pseudorandom integer in [0, output_range). 139 // |salt| is a data parameter for the pseudorandom function. 140 uint32_t GetPseudorandomValue(uint32_t salt, uint32_t output_range) const; 141 }; 142 143 // Separate type from FieldTrial::PickleState so that it can use StringPieces. 144 struct State { 145 std::string_view trial_name; 146 std::string_view group_name; 147 bool activated = false; 148 // Whether the trial was overridden, see `FieldTrial::SetOverridden()`. 149 bool is_overridden = false; 150 }; 151 152 // Represents a Field Trial, its selected group, and override state. 153 struct ActiveGroup { 154 std::string trial_name; 155 std::string group_name; 156 // Whether the trial was overridden, see `FieldTrial::SetOverridden()`. 157 bool is_overridden = false; 158 }; 159 160 // Represents a FieldTrial, its selected group, whether it's active, and 161 // whether it's overridden. String members are pointers to the underlying 162 // strings owned by the FieldTrial object. Does not use std::string_view to 163 // avoid conversions back to std::string. 164 struct BASE_EXPORT PickleState { 165 raw_ptr<const std::string> trial_name = nullptr; 166 raw_ptr<const std::string> group_name = nullptr; 167 bool activated = false; 168 bool is_overridden = false; 169 170 PickleState(); 171 PickleState(const PickleState& other); 172 ~PickleState(); 173 }; 174 175 // We create one FieldTrialEntry per field trial in shared memory, via 176 // AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a 177 // base::Pickle object that we unpickle and read from. 178 struct BASE_EXPORT FieldTrialEntry { 179 // SHA1(FieldTrialEntry): Increment this if structure changes! 180 static constexpr uint32_t kPersistentTypeId = 0xABA17E13 + 3; 181 182 // Expected size for 32/64-bit check. 183 static constexpr size_t kExpectedInstanceSize = 16; 184 185 // Return a pointer to the data area immediately following the entry. GetPickledDataPtrFieldTrialEntry186 uint8_t* GetPickledDataPtr() { 187 return reinterpret_cast<uint8_t*>(this + 1); 188 } GetPickledDataPtrFieldTrialEntry189 const uint8_t* GetPickledDataPtr() const { 190 return reinterpret_cast<const uint8_t*>(this + 1); 191 } 192 193 // Whether or not this field trial is activated. This is really just a 194 // boolean but using a 32 bit value for portability reasons. It should be 195 // accessed via NoBarrier_Load()/NoBarrier_Store() to prevent the compiler 196 // from doing unexpected optimizations because it thinks that only one 197 // thread is accessing the memory location. 198 subtle::Atomic32 activated; 199 200 // On e.g. x86, alignof(uint64_t) is 4. Ensure consistent size and 201 // alignment of `pickle_size` across platforms. This can be considered 202 // to be padding for the final 32 bit value (activated). If this struct 203 // gains or loses fields, consider if this padding is still needed. 204 uint32_t padding; 205 206 // Size of the pickled structure, NOT the total size of this entry. 207 uint64_t pickle_size; 208 209 // Calling this is only valid when the entry is initialized. That is, it 210 // resides in shared memory and has a pickle containing the trial name, 211 // group name, and is_overridden. 212 bool GetState(std::string_view& trial_name, 213 std::string_view& group_name, 214 bool& is_overridden) const; 215 216 // Calling this is only valid when the entry is initialized as well. Reads 217 // the parameters following the trial and group name and stores them as 218 // key-value mappings in |params|. 219 bool GetParams(std::map<std::string, std::string>* params) const; 220 221 private: 222 // Returns an iterator over the data containing names and params. 223 PickleIterator GetPickleIterator() const; 224 225 // Takes the iterator and writes out the first two items into |trial_name| 226 // and |group_name|. 227 bool ReadStringPair(PickleIterator* iter, 228 std::string_view* trial_name, 229 std::string_view* group_name) const; 230 231 // Reads the field trial header, which includes the name of the trial and 232 // group, and the is_overridden bool. 233 bool ReadHeader(PickleIterator& iter, 234 std::string_view& trial_name, 235 std::string_view& group_name, 236 bool& is_overridden) const; 237 }; 238 239 typedef std::vector<ActiveGroup> ActiveGroups; 240 241 // A return value to indicate that a given instance has not yet had a group 242 // assignment (and hence is not yet participating in the trial). 243 static const int kNotFinalized; 244 245 FieldTrial(const FieldTrial&) = delete; 246 FieldTrial& operator=(const FieldTrial&) = delete; 247 248 // Establishes the name and probability of the next group in this trial. 249 // Sometimes, based on construction randomization, this call may cause the 250 // provided group to be *THE* group selected for use in this instance. 251 // AppendGroup can be called after calls to group() but it should be avoided 252 // if possible. Doing so may be confusing since it won't change the group 253 // selection. 254 void AppendGroup(const std::string& name, Probability group_probability); 255 256 // Return the name of the FieldTrial (excluding the group name). trial_name()257 const std::string& trial_name() const { return trial_name_; } 258 259 // Finalizes the group assignment and notifies any/all observers. This is a 260 // no-op if the trial is already active. Note this will force an instance to 261 // participate, and make it illegal to attempt to probabilistically add any 262 // other groups to the trial. 263 void Activate(); 264 265 // If the group's name is empty, a string version containing the group number 266 // is used as the group name. This causes a winner to be chosen if none was. 267 const std::string& group_name(); 268 269 // Finalizes the group choice and returns the chosen group, but does not mark 270 // the trial as active - so its state will not be reported until group_name() 271 // or similar is called. 272 const std::string& GetGroupNameWithoutActivation(); 273 274 // Set the field trial as forced, meaning that it was setup earlier than 275 // the hard coded registration of the field trial to override it. 276 // This allows the code that was hard coded to register the field trial to 277 // still succeed even though the field trial has already been registered. 278 // This must be called after appending all the groups, since we will make 279 // the group choice here. Note that this is a NOOP for already forced trials. 280 // And, as the rest of the FieldTrial code, this is not thread safe and must 281 // be done from the UI thread. 282 void SetForced(); 283 284 // Returns whether the trial was overridden. 285 bool IsOverridden() const; 286 287 // Supports benchmarking by causing field trials' default groups to be chosen. 288 static void EnableBenchmarking(); 289 290 // Creates a FieldTrial object with the specified parameters, to be used for 291 // simulation of group assignment without actually affecting global field 292 // trial state in the running process. Group assignment will be done based on 293 // |entropy_value|, which must have a range of [0, 1). 294 // 295 // Note: Using this function will not register the field trial globally in the 296 // running process - for that, use FieldTrialList::FactoryGetFieldTrial(). 297 // 298 // The ownership of the returned FieldTrial is transfered to the caller which 299 // is responsible for deref'ing it (e.g. by using scoped_refptr<FieldTrial>). 300 static FieldTrial* CreateSimulatedFieldTrial( 301 std::string_view trial_name, 302 Probability total_probability, 303 std::string_view default_group_name, 304 double entropy_value); 305 306 // Parses a '--force-fieldtrials' formatted string into entries. 307 // Returns true if the string was parsed correctly. On failure, the |entries| 308 // array may end up being partially filled. 309 // 310 // Note that currently, States returned here have is_overridden=false, but we 311 // are in the process of migrating to marking field trials set manually by 312 // command line as overridden. See b/284986126. 313 static bool ParseFieldTrialsString(std::string_view field_trials_string, 314 bool override_trials, 315 std::vector<State>& entries); 316 317 // Returns a '--force-fieldtrials' formatted string representing the list of 318 // provided trial states. 319 static std::string BuildFieldTrialStateString( 320 const std::vector<State>& states); 321 322 // Whether this field trial is low anonymity or not (see 323 // |FieldTrialListIncludingLowAnonymity|). 324 // TODO(crbug.com/1431156): remove this once all call sites have been properly 325 // migrated to use an appropriate observer. is_low_anonymity()326 bool is_low_anonymity() const { return is_low_anonymity_; } 327 328 private: 329 // Allow tests to access our innards for testing purposes. 330 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration); 331 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AbsoluteProbabilities); 332 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, RemainingProbability); 333 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FiftyFiftyProbability); 334 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MiddleProbabilities); 335 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner); 336 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability); 337 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups); 338 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AllGroups); 339 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized); 340 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save); 341 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SaveAll); 342 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore); 343 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOff); 344 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn); 345 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default); 346 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault); 347 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ObserveReentrancy); 348 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes); 349 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability); 350 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, 351 DoNotAddSimulatedFieldTrialsToAllocator); 352 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory); 353 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, 354 TestGetRandomizedFieldTrialCount); 355 FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetLowAnonymity); 356 357 // MATCHER(CompareActiveGroupToFieldTrialMatcher, "") 358 friend class base::CompareActiveGroupToFieldTrialMatcher; 359 360 friend class base::FieldTrialList; 361 362 friend class RefCounted<FieldTrial>; 363 364 using FieldTrialRef = PersistentMemoryAllocator::Reference; 365 366 // This is the group number of the 'default' group when a choice wasn't forced 367 // by a call to FieldTrialList::CreateFieldTrial. It is kept private so that 368 // consumers don't use it by mistake in cases where the group was forced. 369 static const int kDefaultGroupNumber; 370 371 // Creates a field trial with the specified parameters. Group assignment will 372 // be done based on |entropy_value|, which must have a range of [0, 1). 373 FieldTrial(std::string_view trial_name, 374 Probability total_probability, 375 std::string_view default_group_name, 376 double entropy_value, 377 bool is_low_anonymity, 378 bool is_overridden); 379 380 virtual ~FieldTrial(); 381 382 // Marks this trial as having been registered with the FieldTrialList. Must be 383 // called no more than once and before any |group()| calls have occurred. 384 void SetTrialRegistered(); 385 386 // Sets the chosen group name and number. 387 void SetGroupChoice(const std::string& group_name, int number); 388 389 // Ensures that a group is chosen, if it hasn't yet been. The field trial 390 // might yet be disabled, so this call will *not* notify observers of the 391 // status. 392 void FinalizeGroupChoice(); 393 394 // Returns the trial name and selected group name for this field trial via 395 // the output parameter |active_group|, but only if the group has already 396 // been chosen and has been externally observed via |group()| and the trial 397 // has not been disabled. In that case, true is returned and |active_group| 398 // is filled in; otherwise, the result is false and |active_group| is left 399 // untouched. 400 bool GetActiveGroup(ActiveGroup* active_group) const; 401 402 // Returns the trial name and selected group name for this field trial via 403 // the output parameter |field_trial_state| for all the studies. 404 void GetStateWhileLocked(PickleState* field_trial_state); 405 406 // Returns the group_name. A winner need not have been chosen. group_name_internal()407 const std::string& group_name_internal() const { return group_name_; } 408 409 // The name of the field trial, as can be found via the FieldTrialList. 410 const std::string trial_name_; 411 412 // The maximum sum of all probabilities supplied, which corresponds to 100%. 413 // This is the scaling factor used to adjust supplied probabilities. 414 const Probability divisor_; 415 416 // The name of the default group. 417 const std::string default_group_name_; 418 419 // The randomly selected probability that is used to select a group (or have 420 // the instance not participate). It is the product of divisor_ and a random 421 // number between [0, 1). 422 Probability random_; 423 424 // Sum of the probabilities of all appended groups. 425 Probability accumulated_group_probability_; 426 427 // The number that will be returned by the next AppendGroup() call. 428 int next_group_number_; 429 430 // The pseudo-randomly assigned group number. 431 // This is kNotFinalized if no group has been assigned. 432 int group_; 433 434 // A textual name for the randomly selected group. Valid after |group()| 435 // has been called. 436 std::string group_name_; 437 438 // When forced_ is true, we return the chosen group from AppendGroup when 439 // appropriate. 440 bool forced_; 441 442 // Whether the field trial was manually overridden using a command-line flag 443 // or internals page. 444 const bool is_overridden_; 445 446 // Specifies whether the group choice has been reported to observers. 447 bool group_reported_; 448 449 // Whether this trial is registered with the global FieldTrialList and thus 450 // should notify it when its group is queried. 451 bool trial_registered_; 452 453 // Reference to related field trial struct and data in shared memory. 454 FieldTrialRef ref_; 455 456 // Denotes whether benchmarking is enabled. In this case, field trials all 457 // revert to the default group. 458 static bool enable_benchmarking_; 459 460 // Whether this field trial is potentially low anonymity (eg. only a small 461 // set of users are included). 462 const bool is_low_anonymity_ = false; 463 }; 464 465 //------------------------------------------------------------------------------ 466 // Class with a list of all active field trials. A trial is active if it has 467 // been registered, which includes evaluating its state based on its 468 // probability. Only one instance of this class exists and outside of testing, 469 // will live for the entire life time of the process. 470 class BASE_EXPORT FieldTrialList { 471 public: 472 using FieldTrialAllocator = PersistentMemoryAllocator; 473 474 // Type for function pointer passed to |AllParamsToString| used to escape 475 // special characters from |input|. 476 typedef std::string (*EscapeDataFunc)(const std::string& input); 477 478 // Observer is notified when a FieldTrial's group is selected. 479 class BASE_EXPORT Observer { 480 public: 481 // Notify observers when FieldTrials's group is selected. 482 // Note that it should be safe to eliminate the `group_name` parameter, in 483 // favor of callers using `trial.group_name()`. This wasn't done yet because 484 // `FieldTrial::group_name()` has a non-trivial implementation. 485 virtual void OnFieldTrialGroupFinalized(const FieldTrial& trial, 486 const std::string& group_name) = 0; 487 488 protected: 489 virtual ~Observer(); 490 }; 491 492 // This singleton holds the global list of registered FieldTrials. 493 FieldTrialList(); 494 FieldTrialList(const FieldTrialList&) = delete; 495 FieldTrialList& operator=(const FieldTrialList&) = delete; 496 497 // Destructor Release()'s references to all registered FieldTrial instances. 498 ~FieldTrialList(); 499 500 // Gets a FieldTrial instance from the factory. 501 // 502 // |trial_name| (a) is used to register the instance with the FieldTrialList 503 // class and (b) can be used to find the trial (only one trial can be present 504 // for each name). |default_group_name| is the name of the group that is 505 // chosen if none of the subsequent appended groups are chosen. Note that the 506 // default group is also chosen whenever |enable_benchmarking_| is true. 507 // 508 // Group probabilities that are later supplied must sum to less than or equal 509 // to the |total_probability|. 510 // 511 // The |entropy_provider| is used for randomizing group selection. The 512 // |randomization_seed| will be passed to the EntropyProvider in addition 513 // to the trial name, and it's handling is defined by the EntropyProvider. 514 // * SessionEntropyProvider requires it to be 0 by DCHECK. 515 // * SHA1 and NormalizedMurmurHash providers will use a non-zero value as a 516 // salt _instead_ of using the trial name. 517 // 518 // Some field trials may be targeted in such way that a relatively small 519 // number of users are in a particular experiment group. Such trials should 520 // have |is_low_anonymity| set to true, and their visitbility is restricted 521 // to specific callers only, via |FieldTrialListIncludingLowAnonymity|. 522 // 523 // This static method can be used to get a startup-randomized FieldTrial or a 524 // previously created forced FieldTrial. 525 static FieldTrial* FactoryGetFieldTrial( 526 std::string_view trial_name, 527 FieldTrial::Probability total_probability, 528 std::string_view default_group_name, 529 const FieldTrial::EntropyProvider& entropy_provider, 530 uint32_t randomization_seed = 0, 531 bool is_low_anonymity = false, 532 bool is_overridden = false); 533 534 // The Find() method can be used to test to see if a named trial was already 535 // registered, or to retrieve a pointer to it from the global map. 536 static FieldTrial* Find(std::string_view trial_name); 537 538 // Returns the group name chosen for the named trial, or the empty string if 539 // the trial does not exist. The first call of this function on a given field 540 // trial will mark it as active, so that its state will be reported with usage 541 // metrics, crashes, etc. 542 // Note: Direct use of this function and related FieldTrial functions is 543 // generally discouraged - instead please use base::Feature when possible. 544 static std::string FindFullName(std::string_view trial_name); 545 546 // Returns true if the named trial has been registered. 547 static bool TrialExists(std::string_view trial_name); 548 549 // Returns true if the named trial exists and has been activated. 550 static bool IsTrialActive(std::string_view trial_name); 551 552 // Creates a persistent representation of all FieldTrial instances for 553 // resurrection in another process. This allows randomization to be done in 554 // one process, and secondary processes can be synchronized on the result. 555 // The resulting string contains the name and group name pairs of all 556 // registered FieldTrials, 557 // with "/" used to separate all names and to terminate the string. All 558 // activated trials have their name prefixed with "*". This string is parsed 559 // by |CreateTrialsFromString()|. 560 static void AllStatesToString(std::string* output); 561 562 // Creates a persistent representation of all FieldTrial params for 563 // resurrection in another process. The returned string contains the trial 564 // name and group name pairs of all registered FieldTrials. The pair is 565 // followed by ':' separator and list of param name and values separated by 566 // '/'. It also takes |encode_data_func| function pointer for encodeing 567 // special characters. This string is parsed by 568 // |AssociateParamsFromString()|. 569 static std::string AllParamsToString(EscapeDataFunc encode_data_func); 570 571 // Fills in the supplied vector |active_groups| (which must be empty when 572 // called) with a snapshot of all registered FieldTrials for which the group 573 // has been chosen and externally observed (via |group()|) and which have 574 // not been disabled. 575 // 576 // This does not return low anonymity field trials. Callers who need access to 577 // low anonymity field trials should use 578 // |FieldTrialListIncludingLowAnonymity.GetActiveFieldTrialGroups()|. 579 static void GetActiveFieldTrialGroups( 580 FieldTrial::ActiveGroups* active_groups); 581 582 // Returns the names of field trials that are active in the parent process. 583 // If this process is not a child process with inherited field trials passed 584 // to it through PopulateLaunchOptionsWithFieldTrialState(), an empty set will 585 // be returned. 586 // Must be called only after a call to CreateTrialsInChildProcess(). 587 static std::set<std::string> GetActiveTrialsOfParentProcess(); 588 589 // Use a state string (re: AllStatesToString()) to augment the current list of 590 // field trials to include the supplied trials, and using a 100% probability 591 // for each trial, force them to have the same group string. This is commonly 592 // used in a non-browser process, to carry randomly selected state in a 593 // browser process into this non-browser process, but could also be invoked 594 // through a command line argument to the browser process. Created field 595 // trials will be marked "used" for the purposes of active trial reporting 596 // if they are prefixed with |kActivationMarker|. 597 // If `override_trials` is true, `FieldTrial::SetOverridden()` is called for 598 // created trials. 599 static bool CreateTrialsFromString(const std::string& trials_string, 600 bool override_trials = false); 601 602 // Creates trials in a child process from a command line that was produced 603 // via PopulateLaunchOptionsWithFieldTrialState() in the parent process. 604 // Trials are retrieved from a shared memory segment that has been shared with 605 // the child process. 606 static void CreateTrialsInChildProcess(const CommandLine& cmd_line); 607 608 // Creates base::Feature overrides in a child process using shared memory. 609 // Requires CreateTrialsInChildProcess() to have been called first which 610 // initializes access to the shared memory segment. 611 static void ApplyFeatureOverridesInChildProcess(FeatureList* feature_list); 612 613 #if BUILDFLAG(USE_BLINK) 614 // Populates |command_line| and |launch_options| with the handles and command 615 // line arguments necessary for a child process to inherit the shared-memory 616 // object containing the FieldTrial configuration. 617 static void PopulateLaunchOptionsWithFieldTrialState( 618 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) 619 GlobalDescriptors::Key descriptor_key, 620 ScopedFD& descriptor_to_share, 621 #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) 622 CommandLine* command_line, 623 LaunchOptions* launch_options); 624 #endif // !BUILDFLAG(USE_BLINK) 625 626 static ReadOnlySharedMemoryRegion DuplicateFieldTrialSharedMemoryForTesting(); 627 628 // Create a FieldTrial with the given |name| and using 100% probability for 629 // the FieldTrial, force FieldTrial to have the same group string as 630 // |group_name|. This is commonly used in a non-browser process, to carry 631 // randomly selected state in a browser process into this non-browser process. 632 // It returns NULL if there is a FieldTrial that is already registered with 633 // the same |name| but has different finalized group string (|group_name|). 634 // 635 // Visibility of field trials with |is_low_anonymity| set to true is 636 // restricted to specific callers only, see 637 // |FieldTrialListIncludingLowAnonymity|. 638 static FieldTrial* CreateFieldTrial(std::string_view name, 639 std::string_view group_name, 640 bool is_low_anonymity = false, 641 bool is_overridden = false); 642 643 // Add an observer to be notified when a field trial is irrevocably committed 644 // to being part of some specific field_group (and hence the group_name is 645 // also finalized for that field_trial). Returns false and does nothing if 646 // there is no FieldTrialList singleton. The observer can be notified on any 647 // sequence; it must be thread-safe. 648 // 649 // Low anonymity field trials are not notified to this observer. Callers 650 // who need to be notified of low anonymity field trials should use 651 // |FieldTrialListIncludingLowAnonymity.AddObserver()|. 652 static bool AddObserver(Observer* observer); 653 654 // Remove an observer. This cannot be invoked concurrently with 655 // FieldTrial::group() (typically, this means that no other thread should be 656 // running when this is invoked). 657 // 658 // Removes observers added via the |AddObserver()| method of this class. 659 static void RemoveObserver(Observer* observer); 660 661 // Notify all observers that a group has been finalized for |field_trial|. 662 static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial); 663 664 // Return the number of active field trials. 665 static size_t GetFieldTrialCount(); 666 667 // Return the number of active field trials registered as randomized trials. 668 // Trials created using the CreateFieldTrial() do not count towards this 669 // total. 670 static size_t GetRandomizedFieldTrialCount(); 671 672 // Gets the parameters for |field_trial| from shared memory and stores them in 673 // |params|. This is only exposed for use by FieldTrialParamAssociator and 674 // shouldn't be used by anything else. 675 static bool GetParamsFromSharedMemory( 676 FieldTrial* field_trial, 677 std::map<std::string, std::string>* params); 678 679 // Clears all the params in the allocator. 680 static void ClearParamsFromSharedMemoryForTesting(); 681 682 // Dumps field trial state to an allocator so that it can be analyzed after a 683 // crash. 684 static void DumpAllFieldTrialsToPersistentAllocator( 685 PersistentMemoryAllocator* allocator); 686 687 // Retrieves field trial state from an allocator so that it can be analyzed 688 // after a crash. The pointers in the returned vector are into the persistent 689 // memory segment and so are only valid as long as the allocator is valid. 690 static std::vector<const FieldTrial::FieldTrialEntry*> 691 GetAllFieldTrialsFromPersistentAllocator( 692 PersistentMemoryAllocator const& allocator); 693 694 // Returns a pointer to the global instance. This is exposed so that it can 695 // be used in a DCHECK in FeatureList and ScopedFeatureList test-only logic 696 // and is not intended to be used widely beyond those cases. 697 static FieldTrialList* GetInstance(); 698 699 // Returns a pointer to the global instance, and resets the global instance 700 // to null. The returned instance can be destroyed if it is no longer needed. 701 static FieldTrialList* ResetInstance(); 702 703 // For testing, sets the global instance to null and returns the previous one. 704 static FieldTrialList* BackupInstanceForTesting(); 705 706 // For testing, sets the global instance to |instance|. 707 static void RestoreInstanceForTesting(FieldTrialList* instance); 708 709 // Creates a list of FieldTrial::State for all FieldTrial instances. 710 // std::string_view members are bound to the lifetime of the corresponding 711 // FieldTrial. 712 static std::vector<FieldTrial::State> GetAllFieldTrialStates( 713 PassKey<test::ScopedFeatureList>); 714 715 // Create FieldTrials from a list of FieldTrial::State. This method is only 716 // available to ScopedFeatureList for testing. The most typical usescase is: 717 // (1) AllStatesToFieldTrialStates(&field_trials); 718 // (2) backup_ = BackupInstanceForTesting(); 719 // // field_trials depends on backup_'s lifetype. 720 // (3) field_trial_list_ = new FieldTrialList(); 721 // (4) CreateTrialsFromFieldTrialStates(field_trials); 722 // // Copy backup_'s fieldtrials to the new field_trial_list_ while 723 // // backup_ is alive. 724 // For resurrestion in another process, need to use AllStatesToString and 725 // CreateFieldTrialsFromString. 726 static bool CreateTrialsFromFieldTrialStates( 727 PassKey<test::ScopedFeatureList>, 728 const std::vector<FieldTrial::State>& entries); 729 730 private: 731 // Allow tests to access our innards for testing purposes. 732 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator); 733 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AddTrialsToAllocator); 734 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, 735 DoNotAddSimulatedFieldTrialsToAllocator); 736 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AssociateFieldTrialParams); 737 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory); 738 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, 739 SerializeSharedMemoryRegionMetadata); 740 friend int SerializeSharedMemoryRegionMetadata(); 741 FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, CheckReadOnlySharedMemoryRegion); 742 743 // Required so that |FieldTrialListIncludingLowAnonymity| can expose APIs from 744 // this class to its friends. 745 friend class FieldTrialListIncludingLowAnonymity; 746 747 #if BUILDFLAG(USE_BLINK) 748 // Serialization is used to pass information about the shared memory handle 749 // to child processes. This is achieved by passing a stringified reference to 750 // the relevant OS resources to the child process. 751 // 752 // Serialization populates |launch_options| with the relevant OS handles to 753 // transfer or copy to the child process and returns serialized information 754 // to be passed to the kFieldTrialHandle command-line switch. 755 // Note: On non-Mac POSIX platforms, it is necessary to pass down the file 756 // descriptor for the shared memory separately. It can be accessed via the 757 // GetFieldTrialDescriptor() API. 758 static std::string SerializeSharedMemoryRegionMetadata( 759 const ReadOnlySharedMemoryRegion& shm, 760 LaunchOptions* launch_options); 761 762 // Takes in |handle_switch| from the command line which represents the shared 763 // memory handle for field trials, parses it, and creates the field trials. 764 // Returns true on success, false on failure. 765 // |switch_value| also contains the serialized GUID. 766 static base::shared_memory::SharedMemoryError CreateTrialsFromSwitchValue( 767 const std::string& switch_value); 768 #endif // BUILDFLAG(USE_BLINK) 769 770 // Takes an unmapped ReadOnlySharedMemoryRegion, maps it with the correct size 771 // and creates field trials via CreateTrialsFromSharedMemoryMapping(). Returns 772 // true if successful and false otherwise. 773 static bool CreateTrialsFromSharedMemoryRegion( 774 const ReadOnlySharedMemoryRegion& shm_region); 775 776 // Expects a mapped piece of shared memory |shm_mapping| that was created from 777 // the browser process's field_trial_allocator and shared via the command 778 // line. This function recreates the allocator, iterates through all the field 779 // trials in it, and creates them via CreateFieldTrial(). Returns true if 780 // successful and false otherwise. 781 static bool CreateTrialsFromSharedMemoryMapping( 782 ReadOnlySharedMemoryMapping shm_mapping); 783 784 // Instantiate the field trial allocator, add all existing field trials to it, 785 // and duplicates its handle to a read-only handle, which gets stored in 786 // |readonly_allocator_handle|. 787 static void InstantiateFieldTrialAllocatorIfNeeded(); 788 789 // Adds the field trial to the allocator. Caller must hold a lock before 790 // calling this. 791 static void AddToAllocatorWhileLocked(PersistentMemoryAllocator* allocator, 792 FieldTrial* field_trial); 793 794 // Activate the corresponding field trial entry struct in shared memory. 795 static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial); 796 797 // A map from FieldTrial names to the actual instances. 798 typedef std::map<std::string, FieldTrial*, std::less<>> RegistrationMap; 799 800 // Helper function should be called only while holding lock_. 801 FieldTrial* PreLockedFind(std::string_view name) 802 EXCLUSIVE_LOCKS_REQUIRED(lock_); 803 804 // Register() stores a pointer to the given trial in a global map. 805 // This method also AddRef's the indicated trial. 806 // This should always be called after creating a new FieldTrial instance. 807 // If the caller wants to select the instance's group randomly, 808 // |is_randomized_trial| should be true to count the number of randomized 809 // trials correctly. Otherwise, false. 810 static void Register(FieldTrial* trial, bool is_randomized_trial); 811 812 // Returns all the registered trials. 813 static RegistrationMap GetRegisteredTrials(); 814 815 // Create field trials from a list of FieldTrial::State. 816 // CreateTrialsFromString() and CreateTrialsFromFieldTrialStates() use this 817 // method internally. 818 static bool CreateTrialsFromFieldTrialStatesInternal( 819 const std::vector<FieldTrial::State>& entries); 820 821 // The same as |GetActiveFieldTrialGroups| but also gives access to low 822 // anonymity field trials. 823 // Restricted to specifically allowed friends - access via 824 // |FieldTrialListIncludingLowAnonymity::GetActiveFieldTrialGroups|. 825 static void GetActiveFieldTrialGroupsInternal( 826 FieldTrial::ActiveGroups* active_groups, 827 bool include_low_anonymity); 828 829 // The same as |AddObserver| but is notified for low anonymity field trials 830 // too. 831 // Restricted to specifically allowed friends - access via 832 // |FieldTrialListIncludingLowAnonymity::AddObserver|. 833 static bool AddObserverInternal(Observer* observer, 834 bool include_low_anonymity); 835 836 // The same as |RemoveObserver| but is notified for low anonymity field trials 837 // too. 838 // Restricted to specifically allowed friends - access via 839 // |FieldTrialListIncludingLowAnonymity::RemoveObserver|. 840 static void RemoveObserverInternal(Observer* observer, 841 bool include_low_anonymity); 842 843 static FieldTrialList* global_; // The singleton of this class. 844 845 // Lock for access to |registered_|, |observers_|, 846 // |observers_including_low_anonymity_|, 847 // |count_of_manually_created_field_trials_|. 848 Lock lock_; 849 RegistrationMap registered_ GUARDED_BY(lock_); 850 851 // Counts the number of field trials whose groups are selected randomly. 852 size_t num_registered_randomized_trials_ GUARDED_BY(lock_) = 0; 853 854 // List of observers to be notified when a group is selected for a FieldTrial. 855 // Excludes low anonymity field trials. 856 std::vector<raw_ptr<Observer, VectorExperimental>> observers_ 857 GUARDED_BY(lock_); 858 859 // List of observers to be notified when a group is selected for a FieldTrial. 860 // Includes low anonymity field trials. 861 std::vector<raw_ptr<Observer, VectorExperimental>> 862 observers_including_low_anonymity_ GUARDED_BY(lock_); 863 864 // Counts the ongoing calls to 865 // FieldTrialList::NotifyFieldTrialGroupSelection(). Used to ensure that 866 // RemoveObserver() isn't called while notifying observers. 867 std::atomic_int num_ongoing_notify_field_trial_group_selection_calls_{0}; 868 869 // Allocator in shared memory containing field trial data. Used in both 870 // browser and child processes, but readonly in the child. 871 // In the future, we may want to move this to a more generic place if we want 872 // to start passing more data other than field trials. 873 std::unique_ptr<FieldTrialAllocator> field_trial_allocator_; 874 875 // Readonly copy of the region to the allocator. Needs to be a member variable 876 // because it's needed from multiple methods. 877 ReadOnlySharedMemoryRegion readonly_allocator_region_; 878 879 // Tracks whether CreateTrialsInChildProcess() has been called. 880 bool create_trials_in_child_process_called_ = false; 881 882 // Tracks if ResetInstance was called for this instance, to avoid resetting 883 // `global_` in the destructor. 884 bool was_reset_ = false; 885 }; 886 887 } // namespace base 888 889 #endif // BASE_METRICS_FIELD_TRIAL_H_ 890