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