• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "base/metrics/field_trial.h"
6 
7 #include <algorithm>
8 #include <string_view>
9 #include <utility>
10 
11 #include "base/auto_reset.h"
12 #include "base/base_switches.h"
13 #include "base/command_line.h"
14 #include "base/containers/span.h"
15 #include "base/debug/crash_logging.h"
16 #include "base/logging.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/metrics/field_trial_param_associator.h"
19 #include "base/metrics/histogram_functions.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/no_destructor.h"
22 #include "base/notreached.h"
23 #include "base/numerics/safe_conversions.h"
24 #include "base/process/memory.h"
25 #include "base/process/process_handle.h"
26 #include "base/process/process_info.h"
27 #include "base/rand_util.h"
28 #include "base/strings/strcat.h"
29 #include "base/strings/string_number_conversions.h"
30 
31 #include "base/strings/string_split.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/stringprintf.h"
34 #include "base/strings/utf_string_conversions.h"
35 #include "base/unguessable_token.h"
36 #include "build/build_config.h"
37 
38 #if BUILDFLAG(USE_BLINK)
39 #include "base/memory/shared_memory_switch.h"
40 #include "base/process/launch.h"
41 #endif
42 
43 #if BUILDFLAG(IS_APPLE) && BUILDFLAG(USE_BLINK)
44 #include "base/apple/mach_port_rendezvous.h"
45 #endif
46 
47 #if BUILDFLAG(IS_POSIX) && BUILDFLAG(USE_BLINK)
48 #include <unistd.h>  // For getppid().
49 #include "base/threading/platform_thread.h"
50 // On POSIX, the fd is shared using the mapping in GlobalDescriptors.
51 #include "base/posix/global_descriptors.h"
52 #endif
53 
54 #if BUILDFLAG(IS_WIN)
55 #include <windows.h>
56 #endif
57 
58 #if BUILDFLAG(IS_FUCHSIA)
59 #include <lib/zx/vmo.h>
60 #include <zircon/process.h>
61 
62 #include "base/fuchsia/fuchsia_logging.h"
63 #endif
64 
65 namespace base {
66 
67 namespace {
68 
69 #if BUILDFLAG(USE_BLINK)
70 using shared_memory::SharedMemoryError;
71 #endif
72 
73 // Define a separator character to use when creating a persistent form of an
74 // instance.  This is intended for use as a command line argument, passed to a
75 // second process to mimic our state (i.e., provide the same group name).
76 const char kPersistentStringSeparator = '/';  // Currently a slash.
77 
78 // Define a marker character to be used as a prefix to a trial name on the
79 // command line which forces its activation.
80 const char kActivationMarker = '*';
81 
82 // Constants for the field trial allocator.
83 const char kAllocatorName[] = "FieldTrialAllocator";
84 
85 // We allocate 256 KiB to hold all the field trial data. This should be enough,
86 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016).
87 // This also doesn't allocate all 256 KiB at once -- the pages only get mapped
88 // to physical memory when they are touched. If the size of the allocated field
89 // trials does get larger than 256 KiB, then we will drop some field trials in
90 // child processes, leading to an inconsistent view between browser and child
91 // processes and possibly causing crashes (see crbug.com/661617).
92 const size_t kFieldTrialAllocationSize = 256 << 10;  // 256 KiB
93 
94 #if BUILDFLAG(IS_APPLE) && BUILDFLAG(USE_BLINK)
95 constexpr MachPortsForRendezvous::key_type kFieldTrialRendezvousKey = 'fldt';
96 #endif
97 
98 // Writes out string1 and then string2 to pickle.
WriteStringPair(Pickle * pickle,std::string_view string1,std::string_view string2)99 void WriteStringPair(Pickle* pickle,
100                      std::string_view string1,
101                      std::string_view string2) {
102   pickle->WriteString(string1);
103   pickle->WriteString(string2);
104 }
105 
106 // Writes out the field trial's contents (via trial_state) to the pickle. The
107 // format of the pickle looks like:
108 // TrialName, GroupName, is_overridden, ParamKey1, ParamValue1, ParamKey2,
109 // ParamValue2, ... If there are no parameters, then it just ends at
110 // is_overridden.
PickleFieldTrial(const FieldTrial::PickleState & trial_state,Pickle * pickle)111 void PickleFieldTrial(const FieldTrial::PickleState& trial_state,
112                       Pickle* pickle) {
113   WriteStringPair(pickle, *trial_state.trial_name, *trial_state.group_name);
114   pickle->WriteBool(trial_state.is_overridden);
115 
116   // Get field trial params.
117   std::map<std::string, std::string> params;
118   FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
119       *trial_state.trial_name, *trial_state.group_name, &params);
120 
121   // Write params to pickle.
122   for (const auto& param : params)
123     WriteStringPair(pickle, param.first, param.second);
124 }
125 
126 // Returns the boundary value for comparing against the FieldTrial's added
127 // groups for a given |divisor| (total probability) and |entropy_value|.
GetGroupBoundaryValue(FieldTrial::Probability divisor,double entropy_value)128 FieldTrial::Probability GetGroupBoundaryValue(
129     FieldTrial::Probability divisor,
130     double entropy_value) {
131   // Add a tiny epsilon value to get consistent results when converting floating
132   // points to int. Without it, boundary values have inconsistent results, e.g.:
133   //
134   //   static_cast<FieldTrial::Probability>(100 * 0.56) == 56
135   //   static_cast<FieldTrial::Probability>(100 * 0.57) == 56
136   //   static_cast<FieldTrial::Probability>(100 * 0.58) == 57
137   //   static_cast<FieldTrial::Probability>(100 * 0.59) == 59
138   const double kEpsilon = 1e-8;
139   const FieldTrial::Probability result =
140       static_cast<FieldTrial::Probability>(divisor * entropy_value + kEpsilon);
141   // Ensure that adding the epsilon still results in a value < |divisor|.
142   return std::min(result, divisor - 1);
143 }
144 
OnOutOfMemory(size_t size)145 void OnOutOfMemory(size_t size) {
146   TerminateBecauseOutOfMemory(size);
147 }
148 
AppendFieldTrialGroupToString(bool activated,std::string_view trial_name,std::string_view group_name,std::string & field_trials_string)149 void AppendFieldTrialGroupToString(bool activated,
150                                    std::string_view trial_name,
151                                    std::string_view group_name,
152                                    std::string& field_trials_string) {
153   DCHECK_EQ(std::string::npos, trial_name.find(kPersistentStringSeparator))
154       << " in name " << trial_name;
155   DCHECK_EQ(std::string::npos, group_name.find(kPersistentStringSeparator))
156       << " in name " << group_name;
157 
158   if (!field_trials_string.empty()) {
159     // Add a '/' in-between field trial groups.
160     field_trials_string.push_back(kPersistentStringSeparator);
161   }
162   if (activated) {
163     field_trials_string.push_back(kActivationMarker);
164   }
165 
166   base::StrAppend(&field_trials_string,
167                   {trial_name, std::string_view(&kPersistentStringSeparator, 1),
168                    group_name});
169 }
170 
171 }  // namespace
172 
173 // statics
174 const int FieldTrial::kNotFinalized = -1;
175 const int FieldTrial::kDefaultGroupNumber = 0;
176 bool FieldTrial::enable_benchmarking_ = false;
177 
178 //------------------------------------------------------------------------------
179 // FieldTrial methods and members.
180 
181 FieldTrial::EntropyProvider::~EntropyProvider() = default;
182 
GetPseudorandomValue(uint32_t salt,uint32_t output_range) const183 uint32_t FieldTrial::EntropyProvider::GetPseudorandomValue(
184     uint32_t salt,
185     uint32_t output_range) const {
186   // Passing a different salt is sufficient to get a "different" result from
187   // GetEntropyForTrial (ignoring collisions).
188   double entropy_value = GetEntropyForTrial(/*trial_name=*/"", salt);
189 
190   // Convert the [0,1) double to an [0, output_range) integer.
191   return static_cast<uint32_t>(GetGroupBoundaryValue(
192       static_cast<FieldTrial::Probability>(output_range), entropy_value));
193 }
194 
195 FieldTrial::PickleState::PickleState() = default;
196 
197 FieldTrial::PickleState::PickleState(const PickleState& other) = default;
198 
199 FieldTrial::PickleState::~PickleState() = default;
200 
GetState(std::string_view & trial_name,std::string_view & group_name,bool & overridden) const201 bool FieldTrial::FieldTrialEntry::GetState(std::string_view& trial_name,
202                                            std::string_view& group_name,
203                                            bool& overridden) const {
204   PickleIterator iter = GetPickleIterator();
205   return ReadHeader(iter, trial_name, group_name, overridden);
206 }
207 
GetParams(std::map<std::string,std::string> * params) const208 bool FieldTrial::FieldTrialEntry::GetParams(
209     std::map<std::string, std::string>* params) const {
210   PickleIterator iter = GetPickleIterator();
211   std::string_view tmp_string;
212   bool tmp_bool;
213   // Skip reading trial and group name, and overridden bit.
214   if (!ReadHeader(iter, tmp_string, tmp_string, tmp_bool)) {
215     return false;
216   }
217 
218   while (true) {
219     std::string_view key;
220     std::string_view value;
221     if (!ReadStringPair(&iter, &key, &value))
222       return key.empty();  // Non-empty is bad: got one of a pair.
223     (*params)[std::string(key)] = std::string(value);
224   }
225 }
226 
GetPickleIterator() const227 PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const {
228   Pickle pickle = Pickle::WithUnownedBuffer(
229       // TODO(crbug.com/40284755): FieldTrialEntry should be constructed with a
230       // span over the pickle memory.
231       UNSAFE_TODO(
232           span(GetPickledDataPtr(), checked_cast<size_t>(pickle_size))));
233   return PickleIterator(pickle);
234 }
235 
ReadHeader(PickleIterator & iter,std::string_view & trial_name,std::string_view & group_name,bool & overridden) const236 bool FieldTrial::FieldTrialEntry::ReadHeader(PickleIterator& iter,
237                                              std::string_view& trial_name,
238                                              std::string_view& group_name,
239                                              bool& overridden) const {
240   return ReadStringPair(&iter, &trial_name, &group_name) &&
241          iter.ReadBool(&overridden);
242 }
243 
ReadStringPair(PickleIterator * iter,std::string_view * trial_name,std::string_view * group_name) const244 bool FieldTrial::FieldTrialEntry::ReadStringPair(
245     PickleIterator* iter,
246     std::string_view* trial_name,
247     std::string_view* group_name) const {
248   if (!iter->ReadStringPiece(trial_name))
249     return false;
250   if (!iter->ReadStringPiece(group_name))
251     return false;
252   return true;
253 }
254 
AppendGroup(const std::string & name,Probability group_probability)255 void FieldTrial::AppendGroup(const std::string& name,
256                              Probability group_probability) {
257   // When the group choice was previously forced, we only need to return the
258   // the id of the chosen group, and anything can be returned for the others.
259   if (forced_) {
260     DCHECK(!group_name_.empty());
261     if (name == group_name_) {
262       // Note that while |group_| may be equal to |kDefaultGroupNumber| on the
263       // forced trial, it will not have the same value as the default group
264       // number returned from the non-forced |FactoryGetFieldTrial()| call,
265       // which takes care to ensure that this does not happen.
266       return;
267     }
268     DCHECK_NE(next_group_number_, group_);
269     // We still return different numbers each time, in case some caller need
270     // them to be different.
271     next_group_number_++;
272     return;
273   }
274 
275   DCHECK_LE(group_probability, divisor_);
276   DCHECK_GE(group_probability, 0);
277 
278   if (enable_benchmarking_)
279     group_probability = 0;
280 
281   accumulated_group_probability_ += group_probability;
282 
283   DCHECK_LE(accumulated_group_probability_, divisor_);
284   if (group_ == kNotFinalized && accumulated_group_probability_ > random_) {
285     // This is the group that crossed the random line, so we do the assignment.
286     SetGroupChoice(name, next_group_number_);
287   }
288   next_group_number_++;
289   return;
290 }
291 
Activate()292 void FieldTrial::Activate() {
293   FinalizeGroupChoice();
294   if (trial_registered_)
295     FieldTrialList::NotifyFieldTrialGroupSelection(this);
296 }
297 
group_name()298 const std::string& FieldTrial::group_name() {
299   // Call |Activate()| to ensure group gets assigned and observers are notified.
300   Activate();
301   DCHECK(!group_name_.empty());
302   return group_name_;
303 }
304 
GetGroupNameWithoutActivation()305 const std::string& FieldTrial::GetGroupNameWithoutActivation() {
306   FinalizeGroupChoice();
307   return group_name_;
308 }
309 
SetForced()310 void FieldTrial::SetForced() {
311   // We might have been forced before (e.g., by CreateFieldTrial) and it's
312   // first come first served, e.g., command line switch has precedence.
313   if (forced_)
314     return;
315 
316   // And we must finalize the group choice before we mark ourselves as forced.
317   FinalizeGroupChoice();
318   forced_ = true;
319 }
320 
IsOverridden() const321 bool FieldTrial::IsOverridden() const {
322   return is_overridden_;
323 }
324 
325 // static
EnableBenchmarking()326 void FieldTrial::EnableBenchmarking() {
327   // We don't need to see field trials created via CreateFieldTrial() for
328   // benchmarking, because such field trials have only a single group and are
329   // not affected by randomization that |enable_benchmarking_| would disable.
330   DCHECK_EQ(0u, FieldTrialList::GetRandomizedFieldTrialCount());
331   enable_benchmarking_ = true;
332 }
333 
334 // static
CreateSimulatedFieldTrial(std::string_view trial_name,Probability total_probability,std::string_view default_group_name,double entropy_value)335 FieldTrial* FieldTrial::CreateSimulatedFieldTrial(
336     std::string_view trial_name,
337     Probability total_probability,
338     std::string_view default_group_name,
339     double entropy_value) {
340   // `is_low_anonymity` is only used for differentiating which observers of the
341   // global `FieldTrialList` should be notified. As this field trial is assumed
342   // to never be registered with the global `FieldTrialList`, `is_low_anonymity`
343   // can be set to an arbitrary value here.
344   return new FieldTrial(trial_name, total_probability, default_group_name,
345                         entropy_value, /*is_low_anonymity=*/false,
346                         /*is_overridden=*/false);
347 }
348 
349 // static
ParseFieldTrialsString(std::string_view trials_string,bool override_trials,std::vector<State> & entries)350 bool FieldTrial::ParseFieldTrialsString(std::string_view trials_string,
351                                         bool override_trials,
352                                         std::vector<State>& entries) {
353   size_t next_item = 0;
354   while (next_item < trials_string.length()) {
355     // Parse one entry. Entries have the format
356     // TrialName1/GroupName1/TrialName2/GroupName2. Each loop parses one trial
357     // and group name.
358 
359     // Find the first delimiter starting at next_item, or quit.
360     size_t trial_name_end =
361         trials_string.find(kPersistentStringSeparator, next_item);
362     if (trial_name_end == trials_string.npos || next_item == trial_name_end) {
363       return false;
364     }
365     // Find the second delimiter, or end of string.
366     size_t group_name_end =
367         trials_string.find(kPersistentStringSeparator, trial_name_end + 1);
368     if (group_name_end == trials_string.npos) {
369       group_name_end = trials_string.length();
370     }
371     // Group names should not be empty, so quit if it is.
372     if (trial_name_end + 1 == group_name_end) {
373       return false;
374     }
375 
376     FieldTrial::State entry;
377     // Verify if the trial should be activated or not.
378     if (trials_string[next_item] == kActivationMarker) {
379       // Name cannot be only the indicator.
380       if (trial_name_end - next_item == 1) {
381         return false;
382       }
383       next_item++;
384       entry.activated = true;
385     }
386     entry.trial_name =
387         trials_string.substr(next_item, trial_name_end - next_item);
388     entry.group_name = trials_string.substr(
389         trial_name_end + 1, group_name_end - trial_name_end - 1);
390     entry.is_overridden = override_trials;
391     // The next item starts after the delimiter, if it exists.
392     next_item = group_name_end + 1;
393 
394     entries.push_back(std::move(entry));
395   }
396   return true;
397 }
398 
399 // static
BuildFieldTrialStateString(const std::vector<State> & states)400 std::string FieldTrial::BuildFieldTrialStateString(
401     const std::vector<State>& states) {
402   std::string result;
403   for (const State& state : states) {
404     AppendFieldTrialGroupToString(state.activated, state.trial_name,
405                                   state.group_name, result);
406   }
407   return result;
408 }
409 
FieldTrial(std::string_view trial_name,const Probability total_probability,std::string_view default_group_name,double entropy_value,bool is_low_anonymity,bool is_overridden)410 FieldTrial::FieldTrial(std::string_view trial_name,
411                        const Probability total_probability,
412                        std::string_view default_group_name,
413                        double entropy_value,
414                        bool is_low_anonymity,
415                        bool is_overridden)
416     : trial_name_(trial_name),
417       divisor_(total_probability),
418       default_group_name_(default_group_name),
419       random_(GetGroupBoundaryValue(total_probability, entropy_value)),
420       accumulated_group_probability_(0),
421       next_group_number_(kDefaultGroupNumber + 1),
422       group_(kNotFinalized),
423       forced_(false),
424       is_overridden_(is_overridden),
425       group_reported_(false),
426       trial_registered_(false),
427       ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull),
428       is_low_anonymity_(is_low_anonymity) {
429   DCHECK_GT(total_probability, 0);
430   DCHECK(!trial_name_.empty());
431   DCHECK(!default_group_name_.empty())
432       << "Trial " << trial_name << " is missing a default group name.";
433 }
434 
435 FieldTrial::~FieldTrial() = default;
436 
SetTrialRegistered()437 void FieldTrial::SetTrialRegistered() {
438   DCHECK_EQ(kNotFinalized, group_);
439   DCHECK(!trial_registered_);
440   trial_registered_ = true;
441 }
442 
SetGroupChoice(const std::string & group_name,int number)443 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) {
444   group_ = number;
445   if (group_name.empty())
446     StringAppendF(&group_name_, "%d", group_);
447   else
448     group_name_ = group_name;
449   DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_;
450 }
451 
FinalizeGroupChoice()452 void FieldTrial::FinalizeGroupChoice() {
453   if (group_ != kNotFinalized)
454     return;
455   accumulated_group_probability_ = divisor_;
456   // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not
457   // finalized.
458   DCHECK(!forced_);
459   SetGroupChoice(default_group_name_, kDefaultGroupNumber);
460 }
461 
GetActiveGroup(ActiveGroup * active_group) const462 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const {
463   if (!group_reported_)
464     return false;
465   DCHECK_NE(group_, kNotFinalized);
466   active_group->trial_name = trial_name_;
467   active_group->group_name = group_name_;
468   active_group->is_overridden = is_overridden_;
469   return true;
470 }
471 
GetStateWhileLocked(PickleState * field_trial_state)472 void FieldTrial::GetStateWhileLocked(PickleState* field_trial_state) {
473   FinalizeGroupChoice();
474   field_trial_state->trial_name = &trial_name_;
475   field_trial_state->group_name = &group_name_;
476   field_trial_state->activated = group_reported_;
477   field_trial_state->is_overridden = is_overridden_;
478 }
479 
480 //------------------------------------------------------------------------------
481 // FieldTrialList methods and members.
482 
483 // static
484 FieldTrialList* FieldTrialList::global_ = nullptr;
485 
486 FieldTrialList::Observer::~Observer() = default;
487 
FieldTrialList()488 FieldTrialList::FieldTrialList() {
489   DCHECK(!global_);
490   global_ = this;
491 }
492 
~FieldTrialList()493 FieldTrialList::~FieldTrialList() {
494   AutoLock auto_lock(lock_);
495   while (!registered_.empty()) {
496     auto it = registered_.begin();
497     it->second->Release();
498     registered_.erase(it->first);
499   }
500   // Note: If this DCHECK fires in a test that uses ScopedFeatureList, it is
501   // likely caused by nested ScopedFeatureLists being destroyed in a different
502   // order than they are initialized.
503   if (!was_reset_) {
504     DCHECK_EQ(this, global_);
505     global_ = nullptr;
506   }
507 }
508 
509 // static
FactoryGetFieldTrial(std::string_view trial_name,FieldTrial::Probability total_probability,std::string_view default_group_name,const FieldTrial::EntropyProvider & entropy_provider,uint32_t randomization_seed,bool is_low_anonymity,bool is_overridden)510 FieldTrial* FieldTrialList::FactoryGetFieldTrial(
511     std::string_view trial_name,
512     FieldTrial::Probability total_probability,
513     std::string_view default_group_name,
514     const FieldTrial::EntropyProvider& entropy_provider,
515     uint32_t randomization_seed,
516     bool is_low_anonymity,
517     bool is_overridden) {
518   // Check if the field trial has already been created in some other way.
519   FieldTrial* existing_trial = Find(trial_name);
520   if (existing_trial) {
521     CHECK(existing_trial->forced_);
522     return existing_trial;
523   }
524 
525   double entropy_value =
526       entropy_provider.GetEntropyForTrial(trial_name, randomization_seed);
527 
528   FieldTrial* field_trial =
529       new FieldTrial(trial_name, total_probability, default_group_name,
530                      entropy_value, is_low_anonymity, is_overridden);
531   FieldTrialList::Register(field_trial, /*is_randomized_trial=*/true);
532   return field_trial;
533 }
534 
535 // static
Find(std::string_view trial_name)536 FieldTrial* FieldTrialList::Find(std::string_view trial_name) {
537   if (!global_)
538     return nullptr;
539   AutoLock auto_lock(global_->lock_);
540   return global_->PreLockedFind(trial_name);
541 }
542 
543 // static
FindFullName(std::string_view trial_name)544 std::string FieldTrialList::FindFullName(std::string_view trial_name) {
545   FieldTrial* field_trial = Find(trial_name);
546   if (field_trial)
547     return field_trial->group_name();
548   return std::string();
549 }
550 
551 // static
TrialExists(std::string_view trial_name)552 bool FieldTrialList::TrialExists(std::string_view trial_name) {
553   return Find(trial_name) != nullptr;
554 }
555 
556 // static
IsTrialActive(std::string_view trial_name)557 bool FieldTrialList::IsTrialActive(std::string_view trial_name) {
558   FieldTrial* field_trial = Find(trial_name);
559   return field_trial && field_trial->group_reported_;
560 }
561 
562 // static
GetAllFieldTrialStates(PassKey<test::ScopedFeatureList>)563 std::vector<FieldTrial::State> FieldTrialList::GetAllFieldTrialStates(
564     PassKey<test::ScopedFeatureList>) {
565   std::vector<FieldTrial::State> states;
566 
567   if (!global_)
568     return states;
569 
570   AutoLock auto_lock(global_->lock_);
571   for (const auto& registered : global_->registered_) {
572     FieldTrial::PickleState trial;
573     registered.second->GetStateWhileLocked(&trial);
574     DCHECK_EQ(std::string::npos,
575               trial.trial_name->find(kPersistentStringSeparator));
576     DCHECK_EQ(std::string::npos,
577               trial.group_name->find(kPersistentStringSeparator));
578     FieldTrial::State entry;
579     entry.activated = trial.activated;
580     entry.trial_name = *trial.trial_name;
581     entry.group_name = *trial.group_name;
582     states.push_back(std::move(entry));
583   }
584   return states;
585 }
586 
587 // static
AllStatesToString(std::string * output)588 void FieldTrialList::AllStatesToString(std::string* output) {
589   if (!global_)
590     return;
591   AutoLock auto_lock(global_->lock_);
592 
593   for (const auto& registered : global_->registered_) {
594     FieldTrial::PickleState trial;
595     registered.second->GetStateWhileLocked(&trial);
596     AppendFieldTrialGroupToString(trial.activated, *trial.trial_name,
597                                   *trial.group_name, *output);
598   }
599 }
600 
601 // static
AllParamsToString(EscapeDataFunc encode_data_func)602 std::string FieldTrialList::AllParamsToString(EscapeDataFunc encode_data_func) {
603   FieldTrialParamAssociator* params_associator =
604       FieldTrialParamAssociator::GetInstance();
605   std::string output;
606   for (const auto& registered : GetRegisteredTrials()) {
607     FieldTrial::PickleState trial;
608     registered.second->GetStateWhileLocked(&trial);
609     DCHECK_EQ(std::string::npos,
610               trial.trial_name->find(kPersistentStringSeparator));
611     DCHECK_EQ(std::string::npos,
612               trial.group_name->find(kPersistentStringSeparator));
613     std::map<std::string, std::string> params;
614     if (params_associator->GetFieldTrialParamsWithoutFallback(
615             *trial.trial_name, *trial.group_name, &params)) {
616       if (params.size() > 0) {
617         // Add comma to seprate from previous entry if it exists.
618         if (!output.empty())
619           output.append(1, ',');
620 
621         output.append(encode_data_func(*trial.trial_name));
622         output.append(1, '.');
623         output.append(encode_data_func(*trial.group_name));
624         output.append(1, ':');
625 
626         std::string param_str;
627         for (const auto& param : params) {
628           // Add separator from previous param information if it exists.
629           if (!param_str.empty()) {
630             param_str.append(1, kPersistentStringSeparator);
631           }
632           param_str.append(encode_data_func(param.first));
633           param_str.append(1, kPersistentStringSeparator);
634           param_str.append(encode_data_func(param.second));
635         }
636 
637         output.append(param_str);
638       }
639     }
640   }
641   return output;
642 }
643 
644 // static
GetActiveFieldTrialGroups(FieldTrial::ActiveGroups * active_groups)645 void FieldTrialList::GetActiveFieldTrialGroups(
646     FieldTrial::ActiveGroups* active_groups) {
647   GetActiveFieldTrialGroupsInternal(active_groups,
648                                     /*include_low_anonymity=*/false);
649 }
650 
651 // static
GetActiveTrialsOfParentProcess()652 std::set<std::string> FieldTrialList::GetActiveTrialsOfParentProcess() {
653   CHECK(global_);
654   CHECK(global_->create_trials_in_child_process_called_);
655 
656   std::set<std::string> result;
657   // CreateTrialsInChildProcess() may not have created the allocator if
658   // kFieldTrialHandle was not passed on the command line.
659   if (!global_->field_trial_allocator_) {
660     return result;
661   }
662 
663   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
664   FieldTrialAllocator::Iterator mem_iter(allocator);
665   const FieldTrial::FieldTrialEntry* entry;
666   while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
667          nullptr) {
668     std::string_view trial_name;
669     std::string_view group_name;
670     bool is_overridden;
671     if (subtle::NoBarrier_Load(&entry->activated) &&
672         entry->GetState(trial_name, group_name, is_overridden)) {
673       result.emplace(trial_name);
674     }
675   }
676   return result;
677 }
678 
679 // static
CreateTrialsFromString(const std::string & trials_string,bool override_trials)680 bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string,
681                                             bool override_trials) {
682   DCHECK(global_);
683   if (trials_string.empty() || !global_)
684     return true;
685 
686   std::vector<FieldTrial::State> entries;
687   if (!FieldTrial::ParseFieldTrialsString(trials_string, override_trials,
688                                           entries)) {
689     return false;
690   }
691 
692   return CreateTrialsFromFieldTrialStatesInternal(entries);
693 }
694 
695 // static
CreateTrialsFromFieldTrialStates(PassKey<test::ScopedFeatureList>,const std::vector<FieldTrial::State> & entries)696 bool FieldTrialList::CreateTrialsFromFieldTrialStates(
697     PassKey<test::ScopedFeatureList>,
698     const std::vector<FieldTrial::State>& entries) {
699   return CreateTrialsFromFieldTrialStatesInternal(entries);
700 }
701 
702 // static
CreateTrialsInChildProcess(const CommandLine & cmd_line)703 void FieldTrialList::CreateTrialsInChildProcess(const CommandLine& cmd_line) {
704   CHECK(!global_->create_trials_in_child_process_called_);
705   global_->create_trials_in_child_process_called_ = true;
706 
707 #if BUILDFLAG(USE_BLINK)
708   // TODO(crbug.com/41403903): Change to a CHECK.
709   if (cmd_line.HasSwitch(switches::kFieldTrialHandle)) {
710     std::string switch_value =
711         cmd_line.GetSwitchValueASCII(switches::kFieldTrialHandle);
712     SharedMemoryError result = CreateTrialsFromSwitchValue(switch_value);
713     SCOPED_CRASH_KEY_NUMBER("FieldTrialList", "SharedMemoryError",
714                             static_cast<int>(result));
715     CHECK_EQ(result, SharedMemoryError::kNoError);
716   }
717 #endif  // BUILDFLAG(USE_BLINK)
718 }
719 
720 // static
ApplyFeatureOverridesInChildProcess(FeatureList * feature_list)721 void FieldTrialList::ApplyFeatureOverridesInChildProcess(
722     FeatureList* feature_list) {
723   CHECK(global_->create_trials_in_child_process_called_);
724   // TODO(crbug.com/41403903): Change to a CHECK.
725   if (global_->field_trial_allocator_) {
726     feature_list->InitFromSharedMemory(global_->field_trial_allocator_.get());
727   }
728 }
729 
730 #if BUILDFLAG(USE_BLINK)
731 // static
PopulateLaunchOptionsWithFieldTrialState(GlobalDescriptors::Key descriptor_key,ScopedFD & descriptor_to_share,CommandLine * command_line,LaunchOptions * launch_options)732 void FieldTrialList::PopulateLaunchOptionsWithFieldTrialState(
733 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
734     GlobalDescriptors::Key descriptor_key,
735     ScopedFD& descriptor_to_share,
736 #endif
737     CommandLine* command_line,
738     LaunchOptions* launch_options) {
739   CHECK(command_line);
740 
741   // Use shared memory to communicate field trial state to child processes.
742   // The browser is the only process that has write access to the shared memory.
743   InstantiateFieldTrialAllocatorIfNeeded();
744   CHECK(global_);
745   CHECK(global_->readonly_allocator_region_.IsValid());
746 
747   global_->field_trial_allocator_->UpdateTrackingHistograms();
748   shared_memory::AddToLaunchParameters(
749       switches::kFieldTrialHandle,
750       global_->readonly_allocator_region_.Duplicate(),
751 #if BUILDFLAG(IS_APPLE)
752       kFieldTrialRendezvousKey,
753 #elif BUILDFLAG(IS_POSIX)
754       descriptor_key, descriptor_to_share,
755 #endif
756       command_line, launch_options);
757 
758   // Append --enable-features and --disable-features switches corresponding
759   // to the features enabled on the command-line, so that child and browser
760   // process command lines match and clearly show what has been specified
761   // explicitly by the user.
762   std::string enabled_features;
763   std::string disabled_features;
764   FeatureList::GetInstance()->GetCommandLineFeatureOverrides(
765       &enabled_features, &disabled_features);
766 
767   if (!enabled_features.empty()) {
768     command_line->AppendSwitchASCII(switches::kEnableFeatures,
769                                     enabled_features);
770   }
771   if (!disabled_features.empty()) {
772     command_line->AppendSwitchASCII(switches::kDisableFeatures,
773                                     disabled_features);
774   }
775 }
776 #endif  // BUILDFLAG(USE_BLINK)
777 
778 // static
779 ReadOnlySharedMemoryRegion
DuplicateFieldTrialSharedMemoryForTesting()780 FieldTrialList::DuplicateFieldTrialSharedMemoryForTesting() {
781   if (!global_)
782     return ReadOnlySharedMemoryRegion();
783 
784   return global_->readonly_allocator_region_.Duplicate();
785 }
786 
787 // static
CreateFieldTrial(std::string_view name,std::string_view group_name,bool is_low_anonymity,bool is_overridden)788 FieldTrial* FieldTrialList::CreateFieldTrial(std::string_view name,
789                                              std::string_view group_name,
790                                              bool is_low_anonymity,
791                                              bool is_overridden) {
792   DCHECK(global_);
793   DCHECK_GE(name.size(), 0u);
794   DCHECK_GE(group_name.size(), 0u);
795   if (name.empty() || group_name.empty() || !global_)
796     return nullptr;
797 
798   FieldTrial* field_trial = FieldTrialList::Find(name);
799   if (field_trial) {
800     // In single process mode, or when we force them from the command line,
801     // we may have already created the field trial.
802     if (field_trial->group_name_internal() != group_name)
803       return nullptr;
804     return field_trial;
805   }
806   const int kTotalProbability = 100;
807   field_trial = new FieldTrial(name, kTotalProbability, group_name, 0,
808                                is_low_anonymity, is_overridden);
809   // The group choice will be finalized in this method. So
810   // |is_randomized_trial| should be false.
811   FieldTrialList::Register(field_trial, /*is_randomized_trial=*/false);
812   // Force the trial, which will also finalize the group choice.
813   field_trial->SetForced();
814   return field_trial;
815 }
816 
817 // static
AddObserver(Observer * observer)818 bool FieldTrialList::AddObserver(Observer* observer) {
819   return FieldTrialList::AddObserverInternal(observer,
820                                              /*include_low_anonymity=*/false);
821 }
822 
823 // static
RemoveObserver(Observer * observer)824 void FieldTrialList::RemoveObserver(Observer* observer) {
825   FieldTrialList::RemoveObserverInternal(observer,
826                                          /*include_low_anonymity=*/false);
827 }
828 
829 // static
NotifyFieldTrialGroupSelection(FieldTrial * field_trial)830 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) {
831   if (!global_)
832     return;
833 
834   std::vector<raw_ptr<Observer, VectorExperimental>> local_observers;
835   std::vector<raw_ptr<Observer, VectorExperimental>>
836       local_observers_including_low_anonymity;
837 
838   {
839     AutoLock auto_lock(global_->lock_);
840     if (field_trial->group_reported_)
841       return;
842     field_trial->group_reported_ = true;
843 
844     ++global_->num_ongoing_notify_field_trial_group_selection_calls_;
845 
846     ActivateFieldTrialEntryWhileLocked(field_trial);
847 
848     // Copy observers to a local variable to access outside the scope of the
849     // lock. Since removing observers concurrently with this method is
850     // disallowed, pointers should remain valid while observers are notified.
851     local_observers = global_->observers_;
852     local_observers_including_low_anonymity =
853         global_->observers_including_low_anonymity_;
854   }
855 
856   if (!field_trial->is_low_anonymity_) {
857     for (Observer* observer : local_observers) {
858       observer->OnFieldTrialGroupFinalized(*field_trial,
859                                            field_trial->group_name_internal());
860     }
861   }
862 
863   for (Observer* observer : local_observers_including_low_anonymity) {
864     observer->OnFieldTrialGroupFinalized(*field_trial,
865                                          field_trial->group_name_internal());
866   }
867 
868   int previous_num_ongoing_notify_field_trial_group_selection_calls =
869       global_->num_ongoing_notify_field_trial_group_selection_calls_--;
870   DCHECK_GT(previous_num_ongoing_notify_field_trial_group_selection_calls, 0);
871 }
872 
873 // static
GetFieldTrialCount()874 size_t FieldTrialList::GetFieldTrialCount() {
875   if (!global_)
876     return 0;
877   AutoLock auto_lock(global_->lock_);
878   return global_->registered_.size();
879 }
880 
881 // static
GetRandomizedFieldTrialCount()882 size_t FieldTrialList::GetRandomizedFieldTrialCount() {
883   if (!global_)
884     return 0;
885   AutoLock auto_lock(global_->lock_);
886   return global_->num_registered_randomized_trials_;
887 }
888 
889 // static
GetParamsFromSharedMemory(FieldTrial * field_trial,std::map<std::string,std::string> * params)890 bool FieldTrialList::GetParamsFromSharedMemory(
891     FieldTrial* field_trial,
892     std::map<std::string, std::string>* params) {
893   DCHECK(global_);
894   // If the field trial allocator is not set up yet, then there are several
895   // cases:
896   //   - We are in the browser process and the allocator has not been set up
897   //   yet. If we got here, then we couldn't find the params in
898   //   FieldTrialParamAssociator, so it's definitely not here. Return false.
899   //   - Using shared memory for field trials is not enabled. If we got here,
900   //   then there's nothing in shared memory. Return false.
901   //   - We are in the child process and the allocator has not been set up yet.
902   //   If this is the case, then you are calling this too early. The field trial
903   //   allocator should get set up very early in the lifecycle. Try to see if
904   //   you can call it after it's been set up.
905   AutoLock auto_lock(global_->lock_);
906   if (!global_->field_trial_allocator_)
907     return false;
908 
909   // If ref_ isn't set, then the field trial data can't be in shared memory.
910   if (!field_trial->ref_)
911     return false;
912 
913   const FieldTrial::FieldTrialEntry* entry =
914       global_->field_trial_allocator_->GetAsObject<FieldTrial::FieldTrialEntry>(
915           field_trial->ref_);
916 
917   size_t allocated_size =
918       global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
919   uint64_t actual_size =
920       sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size;
921   if (allocated_size < actual_size)
922     return false;
923 
924   return entry->GetParams(params);
925 }
926 
927 // static
ClearParamsFromSharedMemoryForTesting()928 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
929   if (!global_)
930     return;
931 
932   AutoLock auto_lock(global_->lock_);
933   if (!global_->field_trial_allocator_)
934     return;
935 
936   // To clear the params, we iterate through every item in the allocator, copy
937   // just the trial and group name into a newly-allocated segment and then clear
938   // the existing item.
939   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
940   FieldTrialAllocator::Iterator mem_iter(allocator);
941 
942   // List of refs to eventually be made iterable. We can't make it in the loop,
943   // since it would go on forever.
944   std::vector<FieldTrial::FieldTrialRef> new_refs;
945 
946   FieldTrial::FieldTrialRef prev_ref;
947   while ((prev_ref = mem_iter.GetNextOfType<FieldTrial::FieldTrialEntry>()) !=
948          FieldTrialAllocator::kReferenceNull) {
949     // Get the existing field trial entry in shared memory.
950     const FieldTrial::FieldTrialEntry* prev_entry =
951         allocator->GetAsObject<FieldTrial::FieldTrialEntry>(prev_ref);
952     std::string_view trial_name;
953     std::string_view group_name;
954     bool is_overridden;
955     if (!prev_entry->GetState(trial_name, group_name, is_overridden)) {
956       continue;
957     }
958 
959     // Write a new entry, minus the params.
960     Pickle pickle;
961     pickle.WriteString(trial_name);
962     pickle.WriteString(group_name);
963     pickle.WriteBool(is_overridden);
964 
965     if (prev_entry->pickle_size == pickle.size() &&
966         memcmp(prev_entry->GetPickledDataPtr(), pickle.data(), pickle.size()) ==
967             0) {
968       // If the new entry is going to be the exact same as the existing one,
969       // then simply keep the existing one to avoid taking extra space in the
970       // allocator. This should mean that this trial has no params.
971       std::map<std::string, std::string> params;
972       CHECK(prev_entry->GetParams(&params));
973       CHECK(params.empty());
974       continue;
975     }
976 
977     size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
978     FieldTrial::FieldTrialEntry* new_entry =
979         allocator->New<FieldTrial::FieldTrialEntry>(total_size);
980     DCHECK(new_entry)
981         << "Failed to allocate a new entry, likely because the allocator is "
982            "full. Consider increasing kFieldTrialAllocationSize.";
983     subtle::NoBarrier_Store(&new_entry->activated,
984                             subtle::NoBarrier_Load(&prev_entry->activated));
985     new_entry->pickle_size = pickle.size();
986 
987     // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
988     // in memory, so we can avoid this memcpy.
989     memcpy(new_entry->GetPickledDataPtr(), pickle.data(), pickle.size());
990 
991     // Update the ref on the field trial and add it to the list to be made
992     // iterable.
993     FieldTrial::FieldTrialRef new_ref = allocator->GetAsReference(new_entry);
994     FieldTrial* trial = global_->PreLockedFind(trial_name);
995     trial->ref_ = new_ref;
996     new_refs.push_back(new_ref);
997 
998     // Mark the existing entry as unused.
999     allocator->ChangeType(prev_ref, 0,
1000                           FieldTrial::FieldTrialEntry::kPersistentTypeId,
1001                           /*clear=*/false);
1002   }
1003 
1004   for (const auto& ref : new_refs) {
1005     allocator->MakeIterable(ref);
1006   }
1007 }
1008 
1009 // static
DumpAllFieldTrialsToPersistentAllocator(PersistentMemoryAllocator * allocator)1010 void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(
1011     PersistentMemoryAllocator* allocator) {
1012   if (!global_)
1013     return;
1014   AutoLock auto_lock(global_->lock_);
1015   for (const auto& registered : global_->registered_) {
1016     AddToAllocatorWhileLocked(allocator, registered.second);
1017   }
1018 }
1019 
1020 // static
1021 std::vector<const FieldTrial::FieldTrialEntry*>
GetAllFieldTrialsFromPersistentAllocator(PersistentMemoryAllocator const & allocator)1022 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(
1023     PersistentMemoryAllocator const& allocator) {
1024   std::vector<const FieldTrial::FieldTrialEntry*> entries;
1025   FieldTrialAllocator::Iterator iter(&allocator);
1026   const FieldTrial::FieldTrialEntry* entry;
1027   while ((entry = iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
1028          nullptr) {
1029     entries.push_back(entry);
1030   }
1031   return entries;
1032 }
1033 
1034 // static
GetInstance()1035 FieldTrialList* FieldTrialList::GetInstance() {
1036   return global_;
1037 }
1038 
1039 // static
ResetInstance()1040 FieldTrialList* FieldTrialList::ResetInstance() {
1041   FieldTrialList* instance = global_;
1042   instance->was_reset_ = true;
1043   global_ = nullptr;
1044   return instance;
1045 }
1046 
1047 // static
BackupInstanceForTesting()1048 FieldTrialList* FieldTrialList::BackupInstanceForTesting() {
1049   FieldTrialList* instance = global_;
1050   global_ = nullptr;
1051   return instance;
1052 }
1053 
1054 // static
RestoreInstanceForTesting(FieldTrialList * instance)1055 void FieldTrialList::RestoreInstanceForTesting(FieldTrialList* instance) {
1056   global_ = instance;
1057 }
1058 
1059 #if BUILDFLAG(USE_BLINK)
1060 
1061 // static
CreateTrialsFromSwitchValue(const std::string & switch_value)1062 SharedMemoryError FieldTrialList::CreateTrialsFromSwitchValue(
1063     const std::string& switch_value) {
1064   auto shm = shared_memory::ReadOnlySharedMemoryRegionFrom(switch_value);
1065   if (!shm.has_value()) {
1066     return shm.error();
1067   }
1068   if (!FieldTrialList::CreateTrialsFromSharedMemoryRegion(shm.value())) {
1069     return SharedMemoryError::kCreateTrialsFailed;
1070   }
1071   return SharedMemoryError::kNoError;
1072 }
1073 
1074 #endif  // BUILDFLAG(USE_BLINK)
1075 
1076 // static
CreateTrialsFromSharedMemoryRegion(const ReadOnlySharedMemoryRegion & shm_region)1077 bool FieldTrialList::CreateTrialsFromSharedMemoryRegion(
1078     const ReadOnlySharedMemoryRegion& shm_region) {
1079   ReadOnlySharedMemoryMapping shm_mapping =
1080       shm_region.MapAt(0, kFieldTrialAllocationSize);
1081   if (!shm_mapping.IsValid())
1082     OnOutOfMemory(kFieldTrialAllocationSize);
1083 
1084   return FieldTrialList::CreateTrialsFromSharedMemoryMapping(
1085       std::move(shm_mapping));
1086 }
1087 
1088 // static
CreateTrialsFromSharedMemoryMapping(ReadOnlySharedMemoryMapping shm_mapping)1089 bool FieldTrialList::CreateTrialsFromSharedMemoryMapping(
1090     ReadOnlySharedMemoryMapping shm_mapping) {
1091   global_->field_trial_allocator_ =
1092       std::make_unique<ReadOnlySharedPersistentMemoryAllocator>(
1093           std::move(shm_mapping), 0, kAllocatorName);
1094   FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get();
1095   FieldTrialAllocator::Iterator mem_iter(shalloc);
1096 
1097   const FieldTrial::FieldTrialEntry* entry;
1098   while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
1099          nullptr) {
1100     std::string_view trial_name;
1101     std::string_view group_name;
1102     bool is_overridden;
1103     if (!entry->GetState(trial_name, group_name, is_overridden)) {
1104       return false;
1105     }
1106     // TODO(crbug.com/40263398): Don't set is_low_anonymity=false, but instead
1107     // propagate the is_low_anonymity state to the child process.
1108     FieldTrial* trial = CreateFieldTrial(
1109         trial_name, group_name, /*is_low_anonymity=*/false, is_overridden);
1110     trial->ref_ = mem_iter.GetAsReference(entry);
1111     if (subtle::NoBarrier_Load(&entry->activated)) {
1112       // Mark the trial as "used" and notify observers, if any.
1113       // This is useful to ensure that field trials created in child
1114       // processes are properly reported in crash reports.
1115       trial->Activate();
1116     }
1117   }
1118   return true;
1119 }
1120 
1121 // static
InstantiateFieldTrialAllocatorIfNeeded()1122 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() {
1123   if (!global_)
1124     return;
1125 
1126   AutoLock auto_lock(global_->lock_);
1127   // Create the allocator if not already created and add all existing trials.
1128   if (global_->field_trial_allocator_ != nullptr)
1129     return;
1130 
1131   MappedReadOnlyRegion shm =
1132       ReadOnlySharedMemoryRegion::Create(kFieldTrialAllocationSize);
1133 
1134   if (!shm.IsValid())
1135     OnOutOfMemory(kFieldTrialAllocationSize);
1136 
1137   global_->field_trial_allocator_ =
1138       std::make_unique<WritableSharedPersistentMemoryAllocator>(
1139           std::move(shm.mapping), 0, kAllocatorName);
1140   global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName);
1141 
1142   // Add all existing field trials.
1143   for (const auto& registered : global_->registered_) {
1144     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
1145                               registered.second);
1146   }
1147 
1148   // Add all existing features.
1149   FeatureList::GetInstance()->AddFeaturesToAllocator(
1150       global_->field_trial_allocator_.get());
1151 
1152   global_->readonly_allocator_region_ = std::move(shm.region);
1153 }
1154 
1155 // static
AddToAllocatorWhileLocked(PersistentMemoryAllocator * allocator,FieldTrial * field_trial)1156 void FieldTrialList::AddToAllocatorWhileLocked(
1157     PersistentMemoryAllocator* allocator,
1158     FieldTrial* field_trial) {
1159   // Don't do anything if the allocator hasn't been instantiated yet.
1160   if (allocator == nullptr)
1161     return;
1162 
1163   // Or if the allocator is read only, which means we are in a child process and
1164   // shouldn't be writing to it.
1165   if (allocator->IsReadonly())
1166     return;
1167 
1168   FieldTrial::PickleState trial_state;
1169   field_trial->GetStateWhileLocked(&trial_state);
1170 
1171   // Or if we've already added it. We must check after GetState since it can
1172   // also add to the allocator.
1173   if (field_trial->ref_)
1174     return;
1175 
1176   Pickle pickle;
1177   PickleFieldTrial(trial_state, &pickle);
1178 
1179   size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
1180   FieldTrial::FieldTrialRef ref = allocator->Allocate(
1181       total_size, FieldTrial::FieldTrialEntry::kPersistentTypeId);
1182   if (ref == FieldTrialAllocator::kReferenceNull) {
1183     NOTREACHED();
1184   }
1185 
1186   FieldTrial::FieldTrialEntry* entry =
1187       allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
1188   subtle::NoBarrier_Store(&entry->activated, trial_state.activated);
1189   entry->pickle_size = pickle.size();
1190 
1191   // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
1192   // memory, so we can avoid this memcpy.
1193   memcpy(entry->GetPickledDataPtr(), pickle.data(), pickle.size());
1194 
1195   allocator->MakeIterable(ref);
1196   field_trial->ref_ = ref;
1197 }
1198 
1199 // static
ActivateFieldTrialEntryWhileLocked(FieldTrial * field_trial)1200 void FieldTrialList::ActivateFieldTrialEntryWhileLocked(
1201     FieldTrial* field_trial) {
1202   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1203 
1204   // Check if we're in the child process and return early if so.
1205   if (!allocator || allocator->IsReadonly())
1206     return;
1207 
1208   FieldTrial::FieldTrialRef ref = field_trial->ref_;
1209   if (ref == FieldTrialAllocator::kReferenceNull) {
1210     // It's fine to do this even if the allocator hasn't been instantiated
1211     // yet -- it'll just return early.
1212     AddToAllocatorWhileLocked(allocator, field_trial);
1213   } else {
1214     // It's also okay to do this even though the callee doesn't have a lock --
1215     // the only thing that happens on a stale read here is a slight performance
1216     // hit from the child re-synchronizing activation state.
1217     FieldTrial::FieldTrialEntry* entry =
1218         allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
1219     subtle::NoBarrier_Store(&entry->activated, 1);
1220   }
1221 }
1222 
PreLockedFind(std::string_view name)1223 FieldTrial* FieldTrialList::PreLockedFind(std::string_view name) {
1224   auto it = registered_.find(name);
1225   if (registered_.end() == it)
1226     return nullptr;
1227   return it->second;
1228 }
1229 
1230 // static
Register(FieldTrial * trial,bool is_randomized_trial)1231 void FieldTrialList::Register(FieldTrial* trial, bool is_randomized_trial) {
1232   DCHECK(global_);
1233 
1234   AutoLock auto_lock(global_->lock_);
1235   CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1236   trial->AddRef();
1237   trial->SetTrialRegistered();
1238   global_->registered_[trial->trial_name()] = trial;
1239 
1240   if (is_randomized_trial)
1241     ++global_->num_registered_randomized_trials_;
1242 }
1243 
1244 // static
GetRegisteredTrials()1245 FieldTrialList::RegistrationMap FieldTrialList::GetRegisteredTrials() {
1246   RegistrationMap output;
1247   if (global_) {
1248     AutoLock auto_lock(global_->lock_);
1249     output = global_->registered_;
1250   }
1251   return output;
1252 }
1253 
1254 // static
CreateTrialsFromFieldTrialStatesInternal(const std::vector<FieldTrial::State> & entries)1255 bool FieldTrialList::CreateTrialsFromFieldTrialStatesInternal(
1256     const std::vector<FieldTrial::State>& entries) {
1257   DCHECK(global_);
1258 
1259   for (const auto& entry : entries) {
1260     FieldTrial* trial =
1261         CreateFieldTrial(entry.trial_name, entry.group_name,
1262                          /*is_low_anonymity=*/false, entry.is_overridden);
1263     if (!trial)
1264       return false;
1265     if (entry.activated) {
1266       // Mark the trial as "used" and notify observers, if any.
1267       // This is useful to ensure that field trials created in child
1268       // processes are properly reported in crash reports.
1269       trial->Activate();
1270     }
1271   }
1272   return true;
1273 }
1274 
1275 // static
GetActiveFieldTrialGroupsInternal(FieldTrial::ActiveGroups * active_groups,bool include_low_anonymity)1276 void FieldTrialList::GetActiveFieldTrialGroupsInternal(
1277     FieldTrial::ActiveGroups* active_groups,
1278     bool include_low_anonymity) {
1279   DCHECK(active_groups->empty());
1280   if (!global_) {
1281     return;
1282   }
1283   AutoLock auto_lock(global_->lock_);
1284 
1285   for (const auto& registered : global_->registered_) {
1286     const FieldTrial& trial = *registered.second;
1287     FieldTrial::ActiveGroup active_group;
1288     if ((include_low_anonymity || !trial.is_low_anonymity_) &&
1289         trial.GetActiveGroup(&active_group)) {
1290       active_groups->push_back(active_group);
1291     }
1292   }
1293 }
1294 
1295 // static
AddObserverInternal(Observer * observer,bool include_low_anonymity)1296 bool FieldTrialList::AddObserverInternal(Observer* observer,
1297                                          bool include_low_anonymity) {
1298   if (!global_) {
1299     return false;
1300   }
1301   AutoLock auto_lock(global_->lock_);
1302   if (include_low_anonymity) {
1303     global_->observers_including_low_anonymity_.push_back(observer);
1304   } else {
1305     global_->observers_.push_back(observer);
1306   }
1307   return true;
1308 }
1309 
1310 // static
RemoveObserverInternal(Observer * observer,bool include_low_anonymity)1311 void FieldTrialList::RemoveObserverInternal(Observer* observer,
1312                                             bool include_low_anonymity) {
1313   if (!global_) {
1314     return;
1315   }
1316   AutoLock auto_lock(global_->lock_);
1317   std::erase(include_low_anonymity ? global_->observers_including_low_anonymity_
1318                                    : global_->observers_,
1319              observer);
1320   DCHECK_EQ(global_->num_ongoing_notify_field_trial_group_selection_calls_, 0)
1321       << "Cannot call RemoveObserver while accessing FieldTrial::group_name().";
1322 }
1323 
1324 }  // namespace base
1325