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