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