• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/metrics/field_trial.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/base_switches.h"
11 #include "base/build_time.h"
12 #include "base/command_line.h"
13 #include "base/debug/activity_tracker.h"
14 #include "base/logging.h"
15 #include "base/metrics/field_trial_param_associator.h"
16 #include "base/process/memory.h"
17 #include "base/process/process_handle.h"
18 #include "base/process/process_info.h"
19 #include "base/rand_util.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/unguessable_token.h"
26 
27 // On POSIX, the fd is shared using the mapping in GlobalDescriptors.
28 #if defined(OS_POSIX) && !defined(OS_NACL)
29 #include "base/posix/global_descriptors.h"
30 #endif
31 
32 namespace base {
33 
34 namespace {
35 
36 // Define a separator character to use when creating a persistent form of an
37 // instance.  This is intended for use as a command line argument, passed to a
38 // second process to mimic our state (i.e., provide the same group name).
39 const char kPersistentStringSeparator = '/';  // Currently a slash.
40 
41 // Define a marker character to be used as a prefix to a trial name on the
42 // command line which forces its activation.
43 const char kActivationMarker = '*';
44 
45 // Use shared memory to communicate field trial (experiment) state. Set to false
46 // for now while the implementation is fleshed out (e.g. data format, single
47 // shared memory segment). See https://codereview.chromium.org/2365273004/ and
48 // crbug.com/653874
49 // The browser is the only process that has write access to the shared memory.
50 // This is safe from race conditions because MakeIterable is a release operation
51 // and GetNextOfType is an acquire operation, so memory writes before
52 // MakeIterable happen before memory reads after GetNextOfType.
53 #if defined(OS_FUCHSIA)  // TODO(752368): Not yet supported on Fuchsia.
54 const bool kUseSharedMemoryForFieldTrials = false;
55 #else
56 const bool kUseSharedMemoryForFieldTrials = true;
57 #endif
58 
59 // Constants for the field trial allocator.
60 const char kAllocatorName[] = "FieldTrialAllocator";
61 
62 // We allocate 128 KiB to hold all the field trial data. This should be enough,
63 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016).
64 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped
65 // to physical memory when they are touched. If the size of the allocated field
66 // trials does get larger than 128 KiB, then we will drop some field trials in
67 // child processes, leading to an inconsistent view between browser and child
68 // processes and possibly causing crashes (see crbug.com/661617).
69 const size_t kFieldTrialAllocationSize = 128 << 10;  // 128 KiB
70 
71 // Writes out string1 and then string2 to pickle.
WriteStringPair(Pickle * pickle,const StringPiece & string1,const StringPiece & string2)72 void WriteStringPair(Pickle* pickle,
73                      const StringPiece& string1,
74                      const StringPiece& string2) {
75   pickle->WriteString(string1);
76   pickle->WriteString(string2);
77 }
78 
79 // Writes out the field trial's contents (via trial_state) to the pickle. The
80 // format of the pickle looks like:
81 // TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ...
82 // If there are no parameters, then it just ends at GroupName.
PickleFieldTrial(const FieldTrial::State & trial_state,Pickle * pickle)83 void PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) {
84   WriteStringPair(pickle, *trial_state.trial_name, *trial_state.group_name);
85 
86   // Get field trial params.
87   std::map<std::string, std::string> params;
88   FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
89       *trial_state.trial_name, *trial_state.group_name, &params);
90 
91   // Write params to pickle.
92   for (const auto& param : params)
93     WriteStringPair(pickle, param.first, param.second);
94 }
95 
96 // Created a time value based on |year|, |month| and |day_of_month| parameters.
CreateTimeFromParams(int year,int month,int day_of_month)97 Time CreateTimeFromParams(int year, int month, int day_of_month) {
98   DCHECK_GT(year, 1970);
99   DCHECK_GT(month, 0);
100   DCHECK_LT(month, 13);
101   DCHECK_GT(day_of_month, 0);
102   DCHECK_LT(day_of_month, 32);
103 
104   Time::Exploded exploded;
105   exploded.year = year;
106   exploded.month = month;
107   exploded.day_of_week = 0;  // Should be unused.
108   exploded.day_of_month = day_of_month;
109   exploded.hour = 0;
110   exploded.minute = 0;
111   exploded.second = 0;
112   exploded.millisecond = 0;
113   Time out_time;
114   if (!Time::FromLocalExploded(exploded, &out_time)) {
115     // TODO(maksims): implement failure handling.
116     // We might just return |out_time|, which is Time(0).
117     NOTIMPLEMENTED();
118   }
119 
120   return out_time;
121 }
122 
123 // Returns the boundary value for comparing against the FieldTrial's added
124 // groups for a given |divisor| (total probability) and |entropy_value|.
GetGroupBoundaryValue(FieldTrial::Probability divisor,double entropy_value)125 FieldTrial::Probability GetGroupBoundaryValue(
126     FieldTrial::Probability divisor,
127     double entropy_value) {
128   // Add a tiny epsilon value to get consistent results when converting floating
129   // points to int. Without it, boundary values have inconsistent results, e.g.:
130   //
131   //   static_cast<FieldTrial::Probability>(100 * 0.56) == 56
132   //   static_cast<FieldTrial::Probability>(100 * 0.57) == 56
133   //   static_cast<FieldTrial::Probability>(100 * 0.58) == 57
134   //   static_cast<FieldTrial::Probability>(100 * 0.59) == 59
135   const double kEpsilon = 1e-8;
136   const FieldTrial::Probability result =
137       static_cast<FieldTrial::Probability>(divisor * entropy_value + kEpsilon);
138   // Ensure that adding the epsilon still results in a value < |divisor|.
139   return std::min(result, divisor - 1);
140 }
141 
142 // Separate type from FieldTrial::State so that it can use StringPieces.
143 struct FieldTrialStringEntry {
144   StringPiece trial_name;
145   StringPiece group_name;
146   bool activated = false;
147 };
148 
149 // Parses the --force-fieldtrials string |trials_string| into |entries|.
150 // Returns true if the string was parsed correctly. On failure, the |entries|
151 // array may end up being partially filled.
ParseFieldTrialsString(const std::string & trials_string,std::vector<FieldTrialStringEntry> * entries)152 bool ParseFieldTrialsString(const std::string& trials_string,
153                             std::vector<FieldTrialStringEntry>* entries) {
154   const StringPiece trials_string_piece(trials_string);
155 
156   size_t next_item = 0;
157   while (next_item < trials_string.length()) {
158     size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
159     if (name_end == trials_string.npos || next_item == name_end)
160       return false;
161     size_t group_name_end =
162         trials_string.find(kPersistentStringSeparator, name_end + 1);
163     if (name_end + 1 == group_name_end)
164       return false;
165     if (group_name_end == trials_string.npos)
166       group_name_end = trials_string.length();
167 
168     FieldTrialStringEntry entry;
169     // Verify if the trial should be activated or not.
170     if (trials_string[next_item] == kActivationMarker) {
171       // Name cannot be only the indicator.
172       if (name_end - next_item == 1)
173         return false;
174       next_item++;
175       entry.activated = true;
176     }
177     entry.trial_name =
178         trials_string_piece.substr(next_item, name_end - next_item);
179     entry.group_name =
180         trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1);
181     next_item = group_name_end + 1;
182 
183     entries->push_back(std::move(entry));
184   }
185   return true;
186 }
187 
AddFeatureAndFieldTrialFlags(const char * enable_features_switch,const char * disable_features_switch,CommandLine * cmd_line)188 void AddFeatureAndFieldTrialFlags(const char* enable_features_switch,
189                                   const char* disable_features_switch,
190                                   CommandLine* cmd_line) {
191   std::string enabled_features;
192   std::string disabled_features;
193   FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features,
194                                                   &disabled_features);
195 
196   if (!enabled_features.empty())
197     cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features);
198   if (!disabled_features.empty())
199     cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features);
200 
201   std::string field_trial_states;
202   FieldTrialList::AllStatesToString(&field_trial_states, false);
203   if (!field_trial_states.empty()) {
204     cmd_line->AppendSwitchASCII(switches::kForceFieldTrials,
205                                 field_trial_states);
206   }
207 }
208 
OnOutOfMemory(size_t size)209 void OnOutOfMemory(size_t size) {
210 #if defined(OS_NACL)
211   NOTREACHED();
212 #else
213   TerminateBecauseOutOfMemory(size);
214 #endif
215 }
216 
217 #if !defined(OS_NACL)
218 // Returns whether the operation succeeded.
DeserializeGUIDFromStringPieces(base::StringPiece first,base::StringPiece second,base::UnguessableToken * guid)219 bool DeserializeGUIDFromStringPieces(base::StringPiece first,
220                                      base::StringPiece second,
221                                      base::UnguessableToken* guid) {
222   uint64_t high = 0;
223   uint64_t low = 0;
224   if (!base::StringToUint64(first, &high) ||
225       !base::StringToUint64(second, &low)) {
226     return false;
227   }
228 
229   *guid = base::UnguessableToken::Deserialize(high, low);
230   return true;
231 }
232 
233 // Extract a read-only SharedMemoryHandle from an existing |shared_memory|
234 // handle. Note that on Android, this also makes the whole region read-only.
GetSharedMemoryReadOnlyHandle(SharedMemory * shared_memory)235 SharedMemoryHandle GetSharedMemoryReadOnlyHandle(SharedMemory* shared_memory) {
236   SharedMemoryHandle result = shared_memory->GetReadOnlyHandle();
237 #if defined(OS_ANDROID)
238   // On Android, turn the region read-only. This prevents any future
239   // writable mapping attempts, but the original one in |shm| survives
240   // and is still usable in the current process.
241   result.SetRegionReadOnly();
242 #endif  // OS_ANDROID
243   return result;
244 }
245 #endif  // !OS_NACL
246 
247 }  // namespace
248 
249 // statics
250 const int FieldTrial::kNotFinalized = -1;
251 const int FieldTrial::kDefaultGroupNumber = 0;
252 bool FieldTrial::enable_benchmarking_ = false;
253 
254 int FieldTrialList::kNoExpirationYear = 0;
255 
256 //------------------------------------------------------------------------------
257 // FieldTrial methods and members.
258 
259 FieldTrial::EntropyProvider::~EntropyProvider() = default;
260 
261 FieldTrial::State::State() = default;
262 
263 FieldTrial::State::State(const State& other) = default;
264 
265 FieldTrial::State::~State() = default;
266 
GetTrialAndGroupName(StringPiece * trial_name,StringPiece * group_name) const267 bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName(
268     StringPiece* trial_name,
269     StringPiece* group_name) const {
270   PickleIterator iter = GetPickleIterator();
271   return ReadStringPair(&iter, trial_name, group_name);
272 }
273 
GetParams(std::map<std::string,std::string> * params) const274 bool FieldTrial::FieldTrialEntry::GetParams(
275     std::map<std::string, std::string>* params) const {
276   PickleIterator iter = GetPickleIterator();
277   StringPiece tmp;
278   // Skip reading trial and group name.
279   if (!ReadStringPair(&iter, &tmp, &tmp))
280     return false;
281 
282   while (true) {
283     StringPiece key;
284     StringPiece value;
285     if (!ReadStringPair(&iter, &key, &value))
286       return key.empty();  // Non-empty is bad: got one of a pair.
287     (*params)[key.as_string()] = value.as_string();
288   }
289 }
290 
GetPickleIterator() const291 PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const {
292   const char* src =
293       reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry);
294 
295   Pickle pickle(src, pickle_size);
296   return PickleIterator(pickle);
297 }
298 
ReadStringPair(PickleIterator * iter,StringPiece * trial_name,StringPiece * group_name) const299 bool FieldTrial::FieldTrialEntry::ReadStringPair(
300     PickleIterator* iter,
301     StringPiece* trial_name,
302     StringPiece* group_name) const {
303   if (!iter->ReadStringPiece(trial_name))
304     return false;
305   if (!iter->ReadStringPiece(group_name))
306     return false;
307   return true;
308 }
309 
Disable()310 void FieldTrial::Disable() {
311   DCHECK(!group_reported_);
312   enable_field_trial_ = false;
313 
314   // In case we are disabled after initialization, we need to switch
315   // the trial to the default group.
316   if (group_ != kNotFinalized) {
317     // Only reset when not already the default group, because in case we were
318     // forced to the default group, the group number may not be
319     // kDefaultGroupNumber, so we should keep it as is.
320     if (group_name_ != default_group_name_)
321       SetGroupChoice(default_group_name_, kDefaultGroupNumber);
322   }
323 }
324 
AppendGroup(const std::string & name,Probability group_probability)325 int FieldTrial::AppendGroup(const std::string& name,
326                             Probability group_probability) {
327   // When the group choice was previously forced, we only need to return the
328   // the id of the chosen group, and anything can be returned for the others.
329   if (forced_) {
330     DCHECK(!group_name_.empty());
331     if (name == group_name_) {
332       // Note that while |group_| may be equal to |kDefaultGroupNumber| on the
333       // forced trial, it will not have the same value as the default group
334       // number returned from the non-forced |FactoryGetFieldTrial()| call,
335       // which takes care to ensure that this does not happen.
336       return group_;
337     }
338     DCHECK_NE(next_group_number_, group_);
339     // We still return different numbers each time, in case some caller need
340     // them to be different.
341     return next_group_number_++;
342   }
343 
344   DCHECK_LE(group_probability, divisor_);
345   DCHECK_GE(group_probability, 0);
346 
347   if (enable_benchmarking_ || !enable_field_trial_)
348     group_probability = 0;
349 
350   accumulated_group_probability_ += group_probability;
351 
352   DCHECK_LE(accumulated_group_probability_, divisor_);
353   if (group_ == kNotFinalized && accumulated_group_probability_ > random_) {
354     // This is the group that crossed the random line, so we do the assignment.
355     SetGroupChoice(name, next_group_number_);
356   }
357   return next_group_number_++;
358 }
359 
group()360 int FieldTrial::group() {
361   FinalizeGroupChoice();
362   if (trial_registered_)
363     FieldTrialList::NotifyFieldTrialGroupSelection(this);
364   return group_;
365 }
366 
group_name()367 const std::string& FieldTrial::group_name() {
368   // Call |group()| to ensure group gets assigned and observers are notified.
369   group();
370   DCHECK(!group_name_.empty());
371   return group_name_;
372 }
373 
GetGroupNameWithoutActivation()374 const std::string& FieldTrial::GetGroupNameWithoutActivation() {
375   FinalizeGroupChoice();
376   return group_name_;
377 }
378 
SetForced()379 void FieldTrial::SetForced() {
380   // We might have been forced before (e.g., by CreateFieldTrial) and it's
381   // first come first served, e.g., command line switch has precedence.
382   if (forced_)
383     return;
384 
385   // And we must finalize the group choice before we mark ourselves as forced.
386   FinalizeGroupChoice();
387   forced_ = true;
388 }
389 
390 // static
EnableBenchmarking()391 void FieldTrial::EnableBenchmarking() {
392   DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
393   enable_benchmarking_ = true;
394 }
395 
396 // static
CreateSimulatedFieldTrial(const std::string & trial_name,Probability total_probability,const std::string & default_group_name,double entropy_value)397 FieldTrial* FieldTrial::CreateSimulatedFieldTrial(
398     const std::string& trial_name,
399     Probability total_probability,
400     const std::string& default_group_name,
401     double entropy_value) {
402   return new FieldTrial(trial_name, total_probability, default_group_name,
403                         entropy_value);
404 }
405 
FieldTrial(const std::string & trial_name,const Probability total_probability,const std::string & default_group_name,double entropy_value)406 FieldTrial::FieldTrial(const std::string& trial_name,
407                        const Probability total_probability,
408                        const std::string& default_group_name,
409                        double entropy_value)
410     : trial_name_(trial_name),
411       divisor_(total_probability),
412       default_group_name_(default_group_name),
413       random_(GetGroupBoundaryValue(total_probability, entropy_value)),
414       accumulated_group_probability_(0),
415       next_group_number_(kDefaultGroupNumber + 1),
416       group_(kNotFinalized),
417       enable_field_trial_(true),
418       forced_(false),
419       group_reported_(false),
420       trial_registered_(false),
421       ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull) {
422   DCHECK_GT(total_probability, 0);
423   DCHECK(!trial_name_.empty());
424   DCHECK(!default_group_name_.empty())
425       << "Trial " << trial_name << " is missing a default group name.";
426 }
427 
428 FieldTrial::~FieldTrial() = default;
429 
SetTrialRegistered()430 void FieldTrial::SetTrialRegistered() {
431   DCHECK_EQ(kNotFinalized, group_);
432   DCHECK(!trial_registered_);
433   trial_registered_ = true;
434 }
435 
SetGroupChoice(const std::string & group_name,int number)436 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) {
437   group_ = number;
438   if (group_name.empty())
439     StringAppendF(&group_name_, "%d", group_);
440   else
441     group_name_ = group_name;
442   DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_;
443 }
444 
FinalizeGroupChoice()445 void FieldTrial::FinalizeGroupChoice() {
446   FinalizeGroupChoiceImpl(false);
447 }
448 
FinalizeGroupChoiceImpl(bool is_locked)449 void FieldTrial::FinalizeGroupChoiceImpl(bool is_locked) {
450   if (group_ != kNotFinalized)
451     return;
452   accumulated_group_probability_ = divisor_;
453   // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not
454   // finalized.
455   DCHECK(!forced_);
456   SetGroupChoice(default_group_name_, kDefaultGroupNumber);
457 
458   // Add the field trial to shared memory.
459   if (kUseSharedMemoryForFieldTrials && trial_registered_)
460     FieldTrialList::OnGroupFinalized(is_locked, this);
461 }
462 
GetActiveGroup(ActiveGroup * active_group) const463 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const {
464   if (!group_reported_ || !enable_field_trial_)
465     return false;
466   DCHECK_NE(group_, kNotFinalized);
467   active_group->trial_name = trial_name_;
468   active_group->group_name = group_name_;
469   return true;
470 }
471 
GetStateWhileLocked(State * field_trial_state,bool include_expired)472 bool FieldTrial::GetStateWhileLocked(State* field_trial_state,
473                                      bool include_expired) {
474   if (!include_expired && !enable_field_trial_)
475     return false;
476   FinalizeGroupChoiceImpl(true);
477   field_trial_state->trial_name = &trial_name_;
478   field_trial_state->group_name = &group_name_;
479   field_trial_state->activated = group_reported_;
480   return true;
481 }
482 
483 //------------------------------------------------------------------------------
484 // FieldTrialList methods and members.
485 
486 // static
487 FieldTrialList* FieldTrialList::global_ = nullptr;
488 
489 // static
490 bool FieldTrialList::used_without_global_ = false;
491 
492 FieldTrialList::Observer::~Observer() = default;
493 
FieldTrialList(std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider)494 FieldTrialList::FieldTrialList(
495     std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider)
496     : entropy_provider_(std::move(entropy_provider)),
497       observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>(
498           ObserverListPolicy::EXISTING_ONLY)) {
499   DCHECK(!global_);
500   DCHECK(!used_without_global_);
501   global_ = this;
502 
503   Time two_years_from_build_time = GetBuildTime() + TimeDelta::FromDays(730);
504   Time::Exploded exploded;
505   two_years_from_build_time.LocalExplode(&exploded);
506   kNoExpirationYear = exploded.year;
507 }
508 
~FieldTrialList()509 FieldTrialList::~FieldTrialList() {
510   AutoLock auto_lock(lock_);
511   while (!registered_.empty()) {
512     RegistrationMap::iterator it = registered_.begin();
513     it->second->Release();
514     registered_.erase(it->first);
515   }
516   DCHECK_EQ(this, global_);
517   global_ = nullptr;
518 }
519 
520 // static
FactoryGetFieldTrial(const std::string & trial_name,FieldTrial::Probability total_probability,const std::string & default_group_name,const int year,const int month,const int day_of_month,FieldTrial::RandomizationType randomization_type,int * default_group_number)521 FieldTrial* FieldTrialList::FactoryGetFieldTrial(
522     const std::string& trial_name,
523     FieldTrial::Probability total_probability,
524     const std::string& default_group_name,
525     const int year,
526     const int month,
527     const int day_of_month,
528     FieldTrial::RandomizationType randomization_type,
529     int* default_group_number) {
530   return FactoryGetFieldTrialWithRandomizationSeed(
531       trial_name, total_probability, default_group_name, year, month,
532       day_of_month, randomization_type, 0, default_group_number, nullptr);
533 }
534 
535 // static
FactoryGetFieldTrialWithRandomizationSeed(const std::string & trial_name,FieldTrial::Probability total_probability,const std::string & default_group_name,const int year,const int month,const int day_of_month,FieldTrial::RandomizationType randomization_type,uint32_t randomization_seed,int * default_group_number,const FieldTrial::EntropyProvider * override_entropy_provider)536 FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
537     const std::string& trial_name,
538     FieldTrial::Probability total_probability,
539     const std::string& default_group_name,
540     const int year,
541     const int month,
542     const int day_of_month,
543     FieldTrial::RandomizationType randomization_type,
544     uint32_t randomization_seed,
545     int* default_group_number,
546     const FieldTrial::EntropyProvider* override_entropy_provider) {
547   if (default_group_number)
548     *default_group_number = FieldTrial::kDefaultGroupNumber;
549   // Check if the field trial has already been created in some other way.
550   FieldTrial* existing_trial = Find(trial_name);
551   if (existing_trial) {
552     CHECK(existing_trial->forced_);
553     // If the default group name differs between the existing forced trial
554     // and this trial, then use a different value for the default group number.
555     if (default_group_number &&
556         default_group_name != existing_trial->default_group_name()) {
557       // If the new default group number corresponds to the group that was
558       // chosen for the forced trial (which has been finalized when it was
559       // forced), then set the default group number to that.
560       if (default_group_name == existing_trial->group_name_internal()) {
561         *default_group_number = existing_trial->group_;
562       } else {
563         // Otherwise, use |kNonConflictingGroupNumber| (-2) for the default
564         // group number, so that it does not conflict with the |AppendGroup()|
565         // result for the chosen group.
566         const int kNonConflictingGroupNumber = -2;
567         static_assert(
568             kNonConflictingGroupNumber != FieldTrial::kDefaultGroupNumber,
569             "The 'non-conflicting' group number conflicts");
570         static_assert(kNonConflictingGroupNumber != FieldTrial::kNotFinalized,
571                       "The 'non-conflicting' group number conflicts");
572         *default_group_number = kNonConflictingGroupNumber;
573       }
574     }
575     return existing_trial;
576   }
577 
578   double entropy_value;
579   if (randomization_type == FieldTrial::ONE_TIME_RANDOMIZED) {
580     // If an override entropy provider is given, use it.
581     const FieldTrial::EntropyProvider* entropy_provider =
582         override_entropy_provider ? override_entropy_provider
583                                   : GetEntropyProviderForOneTimeRandomization();
584     CHECK(entropy_provider);
585     entropy_value = entropy_provider->GetEntropyForTrial(trial_name,
586                                                          randomization_seed);
587   } else {
588     DCHECK_EQ(FieldTrial::SESSION_RANDOMIZED, randomization_type);
589     DCHECK_EQ(0U, randomization_seed);
590     entropy_value = RandDouble();
591   }
592 
593   FieldTrial* field_trial = new FieldTrial(trial_name, total_probability,
594                                            default_group_name, entropy_value);
595   if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month))
596     field_trial->Disable();
597   FieldTrialList::Register(field_trial);
598   return field_trial;
599 }
600 
601 // static
Find(const std::string & trial_name)602 FieldTrial* FieldTrialList::Find(const std::string& trial_name) {
603   if (!global_)
604     return nullptr;
605   AutoLock auto_lock(global_->lock_);
606   return global_->PreLockedFind(trial_name);
607 }
608 
609 // static
FindValue(const std::string & trial_name)610 int FieldTrialList::FindValue(const std::string& trial_name) {
611   FieldTrial* field_trial = Find(trial_name);
612   if (field_trial)
613     return field_trial->group();
614   return FieldTrial::kNotFinalized;
615 }
616 
617 // static
FindFullName(const std::string & trial_name)618 std::string FieldTrialList::FindFullName(const std::string& trial_name) {
619   FieldTrial* field_trial = Find(trial_name);
620   if (field_trial)
621     return field_trial->group_name();
622   return std::string();
623 }
624 
625 // static
TrialExists(const std::string & trial_name)626 bool FieldTrialList::TrialExists(const std::string& trial_name) {
627   return Find(trial_name) != nullptr;
628 }
629 
630 // static
IsTrialActive(const std::string & trial_name)631 bool FieldTrialList::IsTrialActive(const std::string& trial_name) {
632   FieldTrial* field_trial = Find(trial_name);
633   FieldTrial::ActiveGroup active_group;
634   return field_trial && field_trial->GetActiveGroup(&active_group);
635 }
636 
637 // static
StatesToString(std::string * output)638 void FieldTrialList::StatesToString(std::string* output) {
639   FieldTrial::ActiveGroups active_groups;
640   GetActiveFieldTrialGroups(&active_groups);
641   for (FieldTrial::ActiveGroups::const_iterator it = active_groups.begin();
642        it != active_groups.end(); ++it) {
643     DCHECK_EQ(std::string::npos,
644               it->trial_name.find(kPersistentStringSeparator));
645     DCHECK_EQ(std::string::npos,
646               it->group_name.find(kPersistentStringSeparator));
647     output->append(it->trial_name);
648     output->append(1, kPersistentStringSeparator);
649     output->append(it->group_name);
650     output->append(1, kPersistentStringSeparator);
651   }
652 }
653 
654 // static
AllStatesToString(std::string * output,bool include_expired)655 void FieldTrialList::AllStatesToString(std::string* output,
656                                        bool include_expired) {
657   if (!global_)
658     return;
659   AutoLock auto_lock(global_->lock_);
660 
661   for (const auto& registered : global_->registered_) {
662     FieldTrial::State trial;
663     if (!registered.second->GetStateWhileLocked(&trial, include_expired))
664       continue;
665     DCHECK_EQ(std::string::npos,
666               trial.trial_name->find(kPersistentStringSeparator));
667     DCHECK_EQ(std::string::npos,
668               trial.group_name->find(kPersistentStringSeparator));
669     if (trial.activated)
670       output->append(1, kActivationMarker);
671     output->append(*trial.trial_name);
672     output->append(1, kPersistentStringSeparator);
673     output->append(*trial.group_name);
674     output->append(1, kPersistentStringSeparator);
675   }
676 }
677 
678 // static
AllParamsToString(bool include_expired,EscapeDataFunc encode_data_func)679 std::string FieldTrialList::AllParamsToString(bool include_expired,
680                                               EscapeDataFunc encode_data_func) {
681   FieldTrialParamAssociator* params_associator =
682       FieldTrialParamAssociator::GetInstance();
683   std::string output;
684   for (const auto& registered : GetRegisteredTrials()) {
685     FieldTrial::State trial;
686     if (!registered.second->GetStateWhileLocked(&trial, include_expired))
687       continue;
688     DCHECK_EQ(std::string::npos,
689               trial.trial_name->find(kPersistentStringSeparator));
690     DCHECK_EQ(std::string::npos,
691               trial.group_name->find(kPersistentStringSeparator));
692     std::map<std::string, std::string> params;
693     if (params_associator->GetFieldTrialParamsWithoutFallback(
694             *trial.trial_name, *trial.group_name, &params)) {
695       if (params.size() > 0) {
696         // Add comma to seprate from previous entry if it exists.
697         if (!output.empty())
698           output.append(1, ',');
699 
700         output.append(encode_data_func(*trial.trial_name));
701         output.append(1, '.');
702         output.append(encode_data_func(*trial.group_name));
703         output.append(1, ':');
704 
705         std::string param_str;
706         for (const auto& param : params) {
707           // Add separator from previous param information if it exists.
708           if (!param_str.empty())
709             param_str.append(1, kPersistentStringSeparator);
710           param_str.append(encode_data_func(param.first));
711           param_str.append(1, kPersistentStringSeparator);
712           param_str.append(encode_data_func(param.second));
713         }
714 
715         output.append(param_str);
716       }
717     }
718   }
719   return output;
720 }
721 
722 // static
GetActiveFieldTrialGroups(FieldTrial::ActiveGroups * active_groups)723 void FieldTrialList::GetActiveFieldTrialGroups(
724     FieldTrial::ActiveGroups* active_groups) {
725   DCHECK(active_groups->empty());
726   if (!global_)
727     return;
728   AutoLock auto_lock(global_->lock_);
729 
730   for (RegistrationMap::iterator it = global_->registered_.begin();
731        it != global_->registered_.end(); ++it) {
732     FieldTrial::ActiveGroup active_group;
733     if (it->second->GetActiveGroup(&active_group))
734       active_groups->push_back(active_group);
735   }
736 }
737 
738 // static
GetActiveFieldTrialGroupsFromString(const std::string & trials_string,FieldTrial::ActiveGroups * active_groups)739 void FieldTrialList::GetActiveFieldTrialGroupsFromString(
740     const std::string& trials_string,
741     FieldTrial::ActiveGroups* active_groups) {
742   std::vector<FieldTrialStringEntry> entries;
743   if (!ParseFieldTrialsString(trials_string, &entries))
744     return;
745 
746   for (const auto& entry : entries) {
747     if (entry.activated) {
748       FieldTrial::ActiveGroup group;
749       group.trial_name = entry.trial_name.as_string();
750       group.group_name = entry.group_name.as_string();
751       active_groups->push_back(group);
752     }
753   }
754 }
755 
756 // static
GetInitiallyActiveFieldTrials(const base::CommandLine & command_line,FieldTrial::ActiveGroups * active_groups)757 void FieldTrialList::GetInitiallyActiveFieldTrials(
758     const base::CommandLine& command_line,
759     FieldTrial::ActiveGroups* active_groups) {
760   DCHECK(global_);
761   DCHECK(global_->create_trials_from_command_line_called_);
762 
763   if (!global_->field_trial_allocator_) {
764     GetActiveFieldTrialGroupsFromString(
765         command_line.GetSwitchValueASCII(switches::kForceFieldTrials),
766         active_groups);
767     return;
768   }
769 
770   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
771   FieldTrialAllocator::Iterator mem_iter(allocator);
772   const FieldTrial::FieldTrialEntry* entry;
773   while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
774          nullptr) {
775     StringPiece trial_name;
776     StringPiece group_name;
777     if (subtle::NoBarrier_Load(&entry->activated) &&
778         entry->GetTrialAndGroupName(&trial_name, &group_name)) {
779       FieldTrial::ActiveGroup group;
780       group.trial_name = trial_name.as_string();
781       group.group_name = group_name.as_string();
782       active_groups->push_back(group);
783     }
784   }
785 }
786 
787 // static
CreateTrialsFromString(const std::string & trials_string,const std::set<std::string> & ignored_trial_names)788 bool FieldTrialList::CreateTrialsFromString(
789     const std::string& trials_string,
790     const std::set<std::string>& ignored_trial_names) {
791   DCHECK(global_);
792   if (trials_string.empty() || !global_)
793     return true;
794 
795   std::vector<FieldTrialStringEntry> entries;
796   if (!ParseFieldTrialsString(trials_string, &entries))
797     return false;
798 
799   for (const auto& entry : entries) {
800     const std::string trial_name = entry.trial_name.as_string();
801     const std::string group_name = entry.group_name.as_string();
802 
803     if (ContainsKey(ignored_trial_names, trial_name)) {
804       // This is to warn that the field trial forced through command-line
805       // input is unforcable.
806       // Use --enable-logging or --enable-logging=stderr to see this warning.
807       LOG(WARNING) << "Field trial: " << trial_name << " cannot be forced.";
808       continue;
809     }
810 
811     FieldTrial* trial = CreateFieldTrial(trial_name, group_name);
812     if (!trial)
813       return false;
814     if (entry.activated) {
815       // Call |group()| to mark the trial as "used" and notify observers, if
816       // any. This is useful to ensure that field trials created in child
817       // processes are properly reported in crash reports.
818       trial->group();
819     }
820   }
821   return true;
822 }
823 
824 // static
CreateTrialsFromCommandLine(const CommandLine & cmd_line,const char * field_trial_handle_switch,int fd_key)825 void FieldTrialList::CreateTrialsFromCommandLine(
826     const CommandLine& cmd_line,
827     const char* field_trial_handle_switch,
828     int fd_key) {
829   global_->create_trials_from_command_line_called_ = true;
830 
831 #if defined(OS_WIN) || defined(OS_FUCHSIA)
832   if (cmd_line.HasSwitch(field_trial_handle_switch)) {
833     std::string switch_value =
834         cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
835     bool result = CreateTrialsFromSwitchValue(switch_value);
836     DCHECK(result);
837   }
838 #elif defined(OS_POSIX) && !defined(OS_NACL)
839   // On POSIX, we check if the handle is valid by seeing if the browser process
840   // sent over the switch (we don't care about the value). Invalid handles
841   // occur in some browser tests which don't initialize the allocator.
842   if (cmd_line.HasSwitch(field_trial_handle_switch)) {
843     std::string switch_value =
844         cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
845     bool result = CreateTrialsFromDescriptor(fd_key, switch_value);
846     DCHECK(result);
847   }
848 #endif
849 
850   if (cmd_line.HasSwitch(switches::kForceFieldTrials)) {
851     bool result = FieldTrialList::CreateTrialsFromString(
852         cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials),
853         std::set<std::string>());
854     DCHECK(result);
855   }
856 }
857 
858 // static
CreateFeaturesFromCommandLine(const base::CommandLine & command_line,const char * enable_features_switch,const char * disable_features_switch,FeatureList * feature_list)859 void FieldTrialList::CreateFeaturesFromCommandLine(
860     const base::CommandLine& command_line,
861     const char* enable_features_switch,
862     const char* disable_features_switch,
863     FeatureList* feature_list) {
864   // Fallback to command line if not using shared memory.
865   if (!kUseSharedMemoryForFieldTrials ||
866       !global_->field_trial_allocator_.get()) {
867     return feature_list->InitializeFromCommandLine(
868         command_line.GetSwitchValueASCII(enable_features_switch),
869         command_line.GetSwitchValueASCII(disable_features_switch));
870   }
871 
872   feature_list->InitializeFromSharedMemory(
873       global_->field_trial_allocator_.get());
874 }
875 
876 #if defined(OS_WIN)
877 // static
AppendFieldTrialHandleIfNeeded(HandlesToInheritVector * handles)878 void FieldTrialList::AppendFieldTrialHandleIfNeeded(
879     HandlesToInheritVector* handles) {
880   if (!global_)
881     return;
882   if (kUseSharedMemoryForFieldTrials) {
883     InstantiateFieldTrialAllocatorIfNeeded();
884     if (global_->readonly_allocator_handle_.IsValid())
885       handles->push_back(global_->readonly_allocator_handle_.GetHandle());
886   }
887 }
888 #elif defined(OS_FUCHSIA)
889 // TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368).
890 #elif defined(OS_POSIX) && !defined(OS_NACL)
891 // static
GetFieldTrialHandle()892 SharedMemoryHandle FieldTrialList::GetFieldTrialHandle() {
893   if (global_ && kUseSharedMemoryForFieldTrials) {
894     InstantiateFieldTrialAllocatorIfNeeded();
895     // We check for an invalid handle where this gets called.
896     return global_->readonly_allocator_handle_;
897   }
898   return SharedMemoryHandle();
899 }
900 #endif
901 
902 // static
CopyFieldTrialStateToFlags(const char * field_trial_handle_switch,const char * enable_features_switch,const char * disable_features_switch,CommandLine * cmd_line)903 void FieldTrialList::CopyFieldTrialStateToFlags(
904     const char* field_trial_handle_switch,
905     const char* enable_features_switch,
906     const char* disable_features_switch,
907     CommandLine* cmd_line) {
908   // TODO(lawrencewu): Ideally, having the global would be guaranteed. However,
909   // content browser tests currently don't create a FieldTrialList because they
910   // don't run ChromeBrowserMainParts code where it's done for Chrome.
911   // Some tests depend on the enable and disable features flag switch, though,
912   // so we can still add those even though AllStatesToString() will be a no-op.
913   if (!global_) {
914     AddFeatureAndFieldTrialFlags(enable_features_switch,
915                                  disable_features_switch, cmd_line);
916     return;
917   }
918 
919   // Use shared memory to pass the state if the feature is enabled, otherwise
920   // fallback to passing it via the command line as a string.
921   if (kUseSharedMemoryForFieldTrials) {
922     InstantiateFieldTrialAllocatorIfNeeded();
923     // If the readonly handle didn't get duplicated properly, then fallback to
924     // original behavior.
925     if (!global_->readonly_allocator_handle_.IsValid()) {
926       AddFeatureAndFieldTrialFlags(enable_features_switch,
927                                    disable_features_switch, cmd_line);
928       return;
929     }
930 
931     global_->field_trial_allocator_->UpdateTrackingHistograms();
932     std::string switch_value = SerializeSharedMemoryHandleMetadata(
933         global_->readonly_allocator_handle_);
934     cmd_line->AppendSwitchASCII(field_trial_handle_switch, switch_value);
935 
936     // Append --enable-features and --disable-features switches corresponding
937     // to the features enabled on the command-line, so that child and browser
938     // process command lines match and clearly show what has been specified
939     // explicitly by the user.
940     std::string enabled_features;
941     std::string disabled_features;
942     FeatureList::GetInstance()->GetCommandLineFeatureOverrides(
943         &enabled_features, &disabled_features);
944 
945     if (!enabled_features.empty())
946       cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features);
947     if (!disabled_features.empty())
948       cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features);
949 
950     return;
951   }
952 
953   AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch,
954                                cmd_line);
955 }
956 
957 // static
CreateFieldTrial(const std::string & name,const std::string & group_name)958 FieldTrial* FieldTrialList::CreateFieldTrial(
959     const std::string& name,
960     const std::string& group_name) {
961   DCHECK(global_);
962   DCHECK_GE(name.size(), 0u);
963   DCHECK_GE(group_name.size(), 0u);
964   if (name.empty() || group_name.empty() || !global_)
965     return nullptr;
966 
967   FieldTrial* field_trial = FieldTrialList::Find(name);
968   if (field_trial) {
969     // In single process mode, or when we force them from the command line,
970     // we may have already created the field trial.
971     if (field_trial->group_name_internal() != group_name)
972       return nullptr;
973     return field_trial;
974   }
975   const int kTotalProbability = 100;
976   field_trial = new FieldTrial(name, kTotalProbability, group_name, 0);
977   FieldTrialList::Register(field_trial);
978   // Force the trial, which will also finalize the group choice.
979   field_trial->SetForced();
980   return field_trial;
981 }
982 
983 // static
AddObserver(Observer * observer)984 bool FieldTrialList::AddObserver(Observer* observer) {
985   if (!global_)
986     return false;
987   global_->observer_list_->AddObserver(observer);
988   return true;
989 }
990 
991 // static
RemoveObserver(Observer * observer)992 void FieldTrialList::RemoveObserver(Observer* observer) {
993   if (!global_)
994     return;
995   global_->observer_list_->RemoveObserver(observer);
996 }
997 
998 // static
SetSynchronousObserver(Observer * observer)999 void FieldTrialList::SetSynchronousObserver(Observer* observer) {
1000   DCHECK(!global_->synchronous_observer_);
1001   global_->synchronous_observer_ = observer;
1002 }
1003 
1004 // static
RemoveSynchronousObserver(Observer * observer)1005 void FieldTrialList::RemoveSynchronousObserver(Observer* observer) {
1006   DCHECK_EQ(global_->synchronous_observer_, observer);
1007   global_->synchronous_observer_ = nullptr;
1008 }
1009 
1010 // static
OnGroupFinalized(bool is_locked,FieldTrial * field_trial)1011 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) {
1012   if (!global_)
1013     return;
1014   if (is_locked) {
1015     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
1016                               field_trial);
1017   } else {
1018     AutoLock auto_lock(global_->lock_);
1019     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
1020                               field_trial);
1021   }
1022 }
1023 
1024 // static
NotifyFieldTrialGroupSelection(FieldTrial * field_trial)1025 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) {
1026   if (!global_)
1027     return;
1028 
1029   {
1030     AutoLock auto_lock(global_->lock_);
1031     if (field_trial->group_reported_)
1032       return;
1033     field_trial->group_reported_ = true;
1034 
1035     if (!field_trial->enable_field_trial_)
1036       return;
1037 
1038     if (kUseSharedMemoryForFieldTrials)
1039       ActivateFieldTrialEntryWhileLocked(field_trial);
1040   }
1041 
1042   // Recording for stability debugging has to be done inline as a task posted
1043   // to an observer may not get executed before a crash.
1044   base::debug::GlobalActivityTracker* tracker =
1045       base::debug::GlobalActivityTracker::Get();
1046   if (tracker) {
1047     tracker->RecordFieldTrial(field_trial->trial_name(),
1048                               field_trial->group_name_internal());
1049   }
1050 
1051   if (global_->synchronous_observer_) {
1052     global_->synchronous_observer_->OnFieldTrialGroupFinalized(
1053         field_trial->trial_name(), field_trial->group_name_internal());
1054   }
1055 
1056   global_->observer_list_->Notify(
1057       FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized,
1058       field_trial->trial_name(), field_trial->group_name_internal());
1059 }
1060 
1061 // static
GetFieldTrialCount()1062 size_t FieldTrialList::GetFieldTrialCount() {
1063   if (!global_)
1064     return 0;
1065   AutoLock auto_lock(global_->lock_);
1066   return global_->registered_.size();
1067 }
1068 
1069 // static
GetParamsFromSharedMemory(FieldTrial * field_trial,std::map<std::string,std::string> * params)1070 bool FieldTrialList::GetParamsFromSharedMemory(
1071     FieldTrial* field_trial,
1072     std::map<std::string, std::string>* params) {
1073   DCHECK(global_);
1074   // If the field trial allocator is not set up yet, then there are several
1075   // cases:
1076   //   - We are in the browser process and the allocator has not been set up
1077   //   yet. If we got here, then we couldn't find the params in
1078   //   FieldTrialParamAssociator, so it's definitely not here. Return false.
1079   //   - Using shared memory for field trials is not enabled. If we got here,
1080   //   then there's nothing in shared memory. Return false.
1081   //   - We are in the child process and the allocator has not been set up yet.
1082   //   If this is the case, then you are calling this too early. The field trial
1083   //   allocator should get set up very early in the lifecycle. Try to see if
1084   //   you can call it after it's been set up.
1085   AutoLock auto_lock(global_->lock_);
1086   if (!global_->field_trial_allocator_)
1087     return false;
1088 
1089   // If ref_ isn't set, then the field trial data can't be in shared memory.
1090   if (!field_trial->ref_)
1091     return false;
1092 
1093   const FieldTrial::FieldTrialEntry* entry =
1094       global_->field_trial_allocator_->GetAsObject<FieldTrial::FieldTrialEntry>(
1095           field_trial->ref_);
1096 
1097   size_t allocated_size =
1098       global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
1099   size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size;
1100   if (allocated_size < actual_size)
1101     return false;
1102 
1103   return entry->GetParams(params);
1104 }
1105 
1106 // static
ClearParamsFromSharedMemoryForTesting()1107 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
1108   if (!global_)
1109     return;
1110 
1111   AutoLock auto_lock(global_->lock_);
1112   if (!global_->field_trial_allocator_)
1113     return;
1114 
1115   // To clear the params, we iterate through every item in the allocator, copy
1116   // just the trial and group name into a newly-allocated segment and then clear
1117   // the existing item.
1118   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1119   FieldTrialAllocator::Iterator mem_iter(allocator);
1120 
1121   // List of refs to eventually be made iterable. We can't make it in the loop,
1122   // since it would go on forever.
1123   std::vector<FieldTrial::FieldTrialRef> new_refs;
1124 
1125   FieldTrial::FieldTrialRef prev_ref;
1126   while ((prev_ref = mem_iter.GetNextOfType<FieldTrial::FieldTrialEntry>()) !=
1127          FieldTrialAllocator::kReferenceNull) {
1128     // Get the existing field trial entry in shared memory.
1129     const FieldTrial::FieldTrialEntry* prev_entry =
1130         allocator->GetAsObject<FieldTrial::FieldTrialEntry>(prev_ref);
1131     StringPiece trial_name;
1132     StringPiece group_name;
1133     if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
1134       continue;
1135 
1136     // Write a new entry, minus the params.
1137     Pickle pickle;
1138     pickle.WriteString(trial_name);
1139     pickle.WriteString(group_name);
1140     size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
1141     FieldTrial::FieldTrialEntry* new_entry =
1142         allocator->New<FieldTrial::FieldTrialEntry>(total_size);
1143     subtle::NoBarrier_Store(&new_entry->activated,
1144                             subtle::NoBarrier_Load(&prev_entry->activated));
1145     new_entry->pickle_size = pickle.size();
1146 
1147     // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
1148     // in memory, so we can avoid this memcpy.
1149     char* dst = reinterpret_cast<char*>(new_entry) +
1150                 sizeof(FieldTrial::FieldTrialEntry);
1151     memcpy(dst, pickle.data(), pickle.size());
1152 
1153     // Update the ref on the field trial and add it to the list to be made
1154     // iterable.
1155     FieldTrial::FieldTrialRef new_ref = allocator->GetAsReference(new_entry);
1156     FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
1157     trial->ref_ = new_ref;
1158     new_refs.push_back(new_ref);
1159 
1160     // Mark the existing entry as unused.
1161     allocator->ChangeType(prev_ref, 0,
1162                           FieldTrial::FieldTrialEntry::kPersistentTypeId,
1163                           /*clear=*/false);
1164   }
1165 
1166   for (const auto& ref : new_refs) {
1167     allocator->MakeIterable(ref);
1168   }
1169 }
1170 
1171 // static
DumpAllFieldTrialsToPersistentAllocator(PersistentMemoryAllocator * allocator)1172 void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(
1173     PersistentMemoryAllocator* allocator) {
1174   if (!global_)
1175     return;
1176   AutoLock auto_lock(global_->lock_);
1177   for (const auto& registered : global_->registered_) {
1178     AddToAllocatorWhileLocked(allocator, registered.second);
1179   }
1180 }
1181 
1182 // static
1183 std::vector<const FieldTrial::FieldTrialEntry*>
GetAllFieldTrialsFromPersistentAllocator(PersistentMemoryAllocator const & allocator)1184 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(
1185     PersistentMemoryAllocator const& allocator) {
1186   std::vector<const FieldTrial::FieldTrialEntry*> entries;
1187   FieldTrialAllocator::Iterator iter(&allocator);
1188   const FieldTrial::FieldTrialEntry* entry;
1189   while ((entry = iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
1190          nullptr) {
1191     entries.push_back(entry);
1192   }
1193   return entries;
1194 }
1195 
1196 // static
IsGlobalSetForTesting()1197 bool FieldTrialList::IsGlobalSetForTesting() {
1198   return global_ != nullptr;
1199 }
1200 
1201 // static
SerializeSharedMemoryHandleMetadata(const SharedMemoryHandle & shm)1202 std::string FieldTrialList::SerializeSharedMemoryHandleMetadata(
1203     const SharedMemoryHandle& shm) {
1204   std::stringstream ss;
1205 #if defined(OS_WIN)
1206   // Tell the child process the name of the inherited HANDLE.
1207   uintptr_t uintptr_handle = reinterpret_cast<uintptr_t>(shm.GetHandle());
1208   ss << uintptr_handle << ",";
1209 #elif defined(OS_FUCHSIA)
1210   ss << shm.GetHandle() << ",";
1211 #elif !defined(OS_POSIX)
1212 #error Unsupported OS
1213 #endif
1214 
1215   base::UnguessableToken guid = shm.GetGUID();
1216   ss << guid.GetHighForSerialization() << "," << guid.GetLowForSerialization();
1217   ss << "," << shm.GetSize();
1218   return ss.str();
1219 }
1220 
1221 #if defined(OS_WIN) || defined(OS_FUCHSIA)
1222 
1223 // static
DeserializeSharedMemoryHandleMetadata(const std::string & switch_value)1224 SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata(
1225     const std::string& switch_value) {
1226   std::vector<base::StringPiece> tokens = base::SplitStringPiece(
1227       switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
1228 
1229   if (tokens.size() != 4)
1230     return SharedMemoryHandle();
1231 
1232   int field_trial_handle = 0;
1233   if (!base::StringToInt(tokens[0], &field_trial_handle))
1234     return SharedMemoryHandle();
1235 #if defined(OS_FUCHSIA)
1236   zx_handle_t handle = static_cast<zx_handle_t>(field_trial_handle);
1237 #elif defined(OS_WIN)
1238   HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
1239   if (base::IsCurrentProcessElevated()) {
1240     // base::LaunchElevatedProcess doesn't have a way to duplicate the handle,
1241     // but this process can since by definition it's not sandboxed.
1242     base::ProcessId parent_pid = base::GetParentProcessId(GetCurrentProcess());
1243     HANDLE parent_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parent_pid);
1244     DuplicateHandle(parent_handle, handle, GetCurrentProcess(), &handle, 0,
1245                     FALSE, DUPLICATE_SAME_ACCESS);
1246     CloseHandle(parent_handle);
1247   }
1248 #endif  // defined(OS_WIN)
1249 
1250   base::UnguessableToken guid;
1251   if (!DeserializeGUIDFromStringPieces(tokens[1], tokens[2], &guid))
1252     return SharedMemoryHandle();
1253 
1254   int size;
1255   if (!base::StringToInt(tokens[3], &size))
1256     return SharedMemoryHandle();
1257 
1258   return SharedMemoryHandle(handle, static_cast<size_t>(size), guid);
1259 }
1260 
1261 #elif defined(OS_POSIX) && !defined(OS_NACL)
1262 
1263 // static
DeserializeSharedMemoryHandleMetadata(int fd,const std::string & switch_value)1264 SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata(
1265     int fd,
1266     const std::string& switch_value) {
1267   std::vector<base::StringPiece> tokens = base::SplitStringPiece(
1268       switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
1269 
1270   if (tokens.size() != 3)
1271     return SharedMemoryHandle();
1272 
1273   base::UnguessableToken guid;
1274   if (!DeserializeGUIDFromStringPieces(tokens[0], tokens[1], &guid))
1275     return SharedMemoryHandle();
1276 
1277   int size;
1278   if (!base::StringToInt(tokens[2], &size))
1279     return SharedMemoryHandle();
1280 
1281   return SharedMemoryHandle(FileDescriptor(fd, true), static_cast<size_t>(size),
1282                             guid);
1283 }
1284 
1285 #endif
1286 
1287 #if defined(OS_WIN) || defined(OS_FUCHSIA)
1288 // static
CreateTrialsFromSwitchValue(const std::string & switch_value)1289 bool FieldTrialList::CreateTrialsFromSwitchValue(
1290     const std::string& switch_value) {
1291   SharedMemoryHandle shm = DeserializeSharedMemoryHandleMetadata(switch_value);
1292   if (!shm.IsValid())
1293     return false;
1294   return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm);
1295 }
1296 #elif defined(OS_POSIX) && !defined(OS_NACL)
1297 // static
CreateTrialsFromDescriptor(int fd_key,const std::string & switch_value)1298 bool FieldTrialList::CreateTrialsFromDescriptor(
1299     int fd_key,
1300     const std::string& switch_value) {
1301   if (!kUseSharedMemoryForFieldTrials)
1302     return false;
1303 
1304   if (fd_key == -1)
1305     return false;
1306 
1307   int fd = GlobalDescriptors::GetInstance()->MaybeGet(fd_key);
1308   if (fd == -1)
1309     return false;
1310 
1311   SharedMemoryHandle shm =
1312       DeserializeSharedMemoryHandleMetadata(fd, switch_value);
1313   if (!shm.IsValid())
1314     return false;
1315 
1316   bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm);
1317   DCHECK(result);
1318   return true;
1319 }
1320 #endif  // defined(OS_POSIX) && !defined(OS_NACL)
1321 
1322 // static
CreateTrialsFromSharedMemoryHandle(SharedMemoryHandle shm_handle)1323 bool FieldTrialList::CreateTrialsFromSharedMemoryHandle(
1324     SharedMemoryHandle shm_handle) {
1325   // shm gets deleted when it gets out of scope, but that's OK because we need
1326   // it only for the duration of this method.
1327   std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
1328   if (!shm.get()->Map(kFieldTrialAllocationSize))
1329     OnOutOfMemory(kFieldTrialAllocationSize);
1330 
1331   return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
1332 }
1333 
1334 // static
CreateTrialsFromSharedMemory(std::unique_ptr<SharedMemory> shm)1335 bool FieldTrialList::CreateTrialsFromSharedMemory(
1336     std::unique_ptr<SharedMemory> shm) {
1337   global_->field_trial_allocator_.reset(
1338       new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true));
1339   FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get();
1340   FieldTrialAllocator::Iterator mem_iter(shalloc);
1341 
1342   const FieldTrial::FieldTrialEntry* entry;
1343   while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
1344          nullptr) {
1345     StringPiece trial_name;
1346     StringPiece group_name;
1347     if (!entry->GetTrialAndGroupName(&trial_name, &group_name))
1348       return false;
1349 
1350     // TODO(lawrencewu): Convert the API for CreateFieldTrial to take
1351     // StringPieces.
1352     FieldTrial* trial =
1353         CreateFieldTrial(trial_name.as_string(), group_name.as_string());
1354 
1355     trial->ref_ = mem_iter.GetAsReference(entry);
1356     if (subtle::NoBarrier_Load(&entry->activated)) {
1357       // Call |group()| to mark the trial as "used" and notify observers, if
1358       // any. This is useful to ensure that field trials created in child
1359       // processes are properly reported in crash reports.
1360       trial->group();
1361     }
1362   }
1363   return true;
1364 }
1365 
1366 // static
InstantiateFieldTrialAllocatorIfNeeded()1367 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() {
1368   if (!global_)
1369     return;
1370   AutoLock auto_lock(global_->lock_);
1371   // Create the allocator if not already created and add all existing trials.
1372   if (global_->field_trial_allocator_ != nullptr)
1373     return;
1374 
1375   SharedMemoryCreateOptions options;
1376   options.size = kFieldTrialAllocationSize;
1377   options.share_read_only = true;
1378 #if defined(OS_MACOSX) && !defined(OS_IOS)
1379   options.type = SharedMemoryHandle::POSIX;
1380 #endif
1381 
1382   std::unique_ptr<SharedMemory> shm(new SharedMemory());
1383   if (!shm->Create(options))
1384     OnOutOfMemory(kFieldTrialAllocationSize);
1385 
1386   if (!shm->Map(kFieldTrialAllocationSize))
1387     OnOutOfMemory(kFieldTrialAllocationSize);
1388 
1389   global_->field_trial_allocator_.reset(
1390       new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false));
1391   global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName);
1392 
1393   // Add all existing field trials.
1394   for (const auto& registered : global_->registered_) {
1395     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
1396                               registered.second);
1397   }
1398 
1399   // Add all existing features.
1400   FeatureList::GetInstance()->AddFeaturesToAllocator(
1401       global_->field_trial_allocator_.get());
1402 
1403 #if !defined(OS_NACL)
1404   global_->readonly_allocator_handle_ = GetSharedMemoryReadOnlyHandle(
1405       global_->field_trial_allocator_->shared_memory());
1406 #endif
1407 }
1408 
1409 // static
AddToAllocatorWhileLocked(PersistentMemoryAllocator * allocator,FieldTrial * field_trial)1410 void FieldTrialList::AddToAllocatorWhileLocked(
1411     PersistentMemoryAllocator* allocator,
1412     FieldTrial* field_trial) {
1413   // Don't do anything if the allocator hasn't been instantiated yet.
1414   if (allocator == nullptr)
1415     return;
1416 
1417   // Or if the allocator is read only, which means we are in a child process and
1418   // shouldn't be writing to it.
1419   if (allocator->IsReadonly())
1420     return;
1421 
1422   FieldTrial::State trial_state;
1423   if (!field_trial->GetStateWhileLocked(&trial_state, false))
1424     return;
1425 
1426   // Or if we've already added it. We must check after GetState since it can
1427   // also add to the allocator.
1428   if (field_trial->ref_)
1429     return;
1430 
1431   Pickle pickle;
1432   PickleFieldTrial(trial_state, &pickle);
1433 
1434   size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
1435   FieldTrial::FieldTrialRef ref = allocator->Allocate(
1436       total_size, FieldTrial::FieldTrialEntry::kPersistentTypeId);
1437   if (ref == FieldTrialAllocator::kReferenceNull) {
1438     NOTREACHED();
1439     return;
1440   }
1441 
1442   FieldTrial::FieldTrialEntry* entry =
1443       allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
1444   subtle::NoBarrier_Store(&entry->activated, trial_state.activated);
1445   entry->pickle_size = pickle.size();
1446 
1447   // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
1448   // memory, so we can avoid this memcpy.
1449   char* dst =
1450       reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry);
1451   memcpy(dst, pickle.data(), pickle.size());
1452 
1453   allocator->MakeIterable(ref);
1454   field_trial->ref_ = ref;
1455 }
1456 
1457 // static
ActivateFieldTrialEntryWhileLocked(FieldTrial * field_trial)1458 void FieldTrialList::ActivateFieldTrialEntryWhileLocked(
1459     FieldTrial* field_trial) {
1460   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1461 
1462   // Check if we're in the child process and return early if so.
1463   if (!allocator || allocator->IsReadonly())
1464     return;
1465 
1466   FieldTrial::FieldTrialRef ref = field_trial->ref_;
1467   if (ref == FieldTrialAllocator::kReferenceNull) {
1468     // It's fine to do this even if the allocator hasn't been instantiated
1469     // yet -- it'll just return early.
1470     AddToAllocatorWhileLocked(allocator, field_trial);
1471   } else {
1472     // It's also okay to do this even though the callee doesn't have a lock --
1473     // the only thing that happens on a stale read here is a slight performance
1474     // hit from the child re-synchronizing activation state.
1475     FieldTrial::FieldTrialEntry* entry =
1476         allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
1477     subtle::NoBarrier_Store(&entry->activated, 1);
1478   }
1479 }
1480 
1481 // static
1482 const FieldTrial::EntropyProvider*
GetEntropyProviderForOneTimeRandomization()1483     FieldTrialList::GetEntropyProviderForOneTimeRandomization() {
1484   if (!global_) {
1485     used_without_global_ = true;
1486     return nullptr;
1487   }
1488 
1489   return global_->entropy_provider_.get();
1490 }
1491 
PreLockedFind(const std::string & name)1492 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
1493   RegistrationMap::iterator it = registered_.find(name);
1494   if (registered_.end() == it)
1495     return nullptr;
1496   return it->second;
1497 }
1498 
1499 // static
Register(FieldTrial * trial)1500 void FieldTrialList::Register(FieldTrial* trial) {
1501   if (!global_) {
1502     used_without_global_ = true;
1503     return;
1504   }
1505   AutoLock auto_lock(global_->lock_);
1506   CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1507   trial->AddRef();
1508   trial->SetTrialRegistered();
1509   global_->registered_[trial->trial_name()] = trial;
1510 }
1511 
1512 // static
GetRegisteredTrials()1513 FieldTrialList::RegistrationMap FieldTrialList::GetRegisteredTrials() {
1514   RegistrationMap output;
1515   if (global_) {
1516     AutoLock auto_lock(global_->lock_);
1517     output = global_->registered_;
1518   }
1519   return output;
1520 }
1521 
1522 }  // namespace base
1523