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