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