| OLD | NEW | 
|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "base/metrics/field_trial.h" | 5 #include "base/metrics/field_trial.h" | 
| 6 | 6 | 
| 7 #include <algorithm> | 7 #include <algorithm> | 
| 8 #include <utility> | 8 #include <utility> | 
| 9 | 9 | 
| 10 #include "base/base_switches.h" | 10 #include "base/base_switches.h" | 
| 11 #include "base/build_time.h" | 11 #include "base/build_time.h" | 
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" | 
| 13 #include "base/logging.h" | 13 #include "base/logging.h" | 
| 14 #include "base/metrics/field_trial_param_associator.h" | 14 #include "base/metrics/field_trial_param_associator.h" | 
| 15 #include "base/pickle.h" |  | 
| 16 #include "base/process/memory.h" | 15 #include "base/process/memory.h" | 
| 17 #include "base/rand_util.h" | 16 #include "base/rand_util.h" | 
| 18 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" | 
| 19 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" | 
| 20 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" | 
| 21 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" | 
| 22 | 21 | 
| 23 // On systems that use the zygote process to spawn child processes, we must | 22 // On systems that use the zygote process to spawn child processes, we must | 
| 24 // retrieve the correct fd using the mapping in GlobalDescriptors. | 23 // retrieve the correct fd using the mapping in GlobalDescriptors. | 
| 25 #if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MACOSX) && \ | 24 #if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MACOSX) && \ | 
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 62 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016). | 61 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016). | 
| 63 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped | 62 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped | 
| 64 // to physical memory when they are touched. If the size of the allocated field | 63 // to physical memory when they are touched. If the size of the allocated field | 
| 65 // trials does get larger than 128 KiB, then we will drop some field trials in | 64 // trials does get larger than 128 KiB, then we will drop some field trials in | 
| 66 // child processes, leading to an inconsistent view between browser and child | 65 // child processes, leading to an inconsistent view between browser and child | 
| 67 // processes and possibly causing crashes (see crbug.com/661617). | 66 // processes and possibly causing crashes (see crbug.com/661617). | 
| 68 #if !defined(OS_NACL) | 67 #if !defined(OS_NACL) | 
| 69 const size_t kFieldTrialAllocationSize = 128 << 10;  // 128 KiB | 68 const size_t kFieldTrialAllocationSize = 128 << 10;  // 128 KiB | 
| 70 #endif | 69 #endif | 
| 71 | 70 | 
| 72 // We create one FieldTrialEntry per field trial in shared memory, via |  | 
| 73 // AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a base::Pickle |  | 
| 74 // object that we unpickle and read from. Any changes to this structure requires |  | 
| 75 // a bump in kFieldTrialType id defined above. |  | 
| 76 struct FieldTrialEntry { |  | 
| 77   // Expected size for 32/64-bit check. |  | 
| 78   static constexpr size_t kExpectedInstanceSize = 8; |  | 
| 79 |  | 
| 80   // Whether or not this field trial is activated. This is really just a boolean |  | 
| 81   // but marked as a uint32_t for portability reasons. |  | 
| 82   uint32_t activated; |  | 
| 83 |  | 
| 84   // Size of the pickled structure, NOT the total size of this entry. |  | 
| 85   uint32_t pickle_size; |  | 
| 86 |  | 
| 87   // Returns an iterator over the data containing names and params. |  | 
| 88   PickleIterator GetPickleIterator() const { |  | 
| 89     const char* src = |  | 
| 90         reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry); |  | 
| 91 |  | 
| 92     Pickle pickle(src, pickle_size); |  | 
| 93     return PickleIterator(pickle); |  | 
| 94   } |  | 
| 95 |  | 
| 96   // Takes the iterator and writes out the first two items into |trial_name| and |  | 
| 97   // |group_name|. |  | 
| 98   bool ReadStringPair(PickleIterator* iter, |  | 
| 99                       StringPiece* trial_name, |  | 
| 100                       StringPiece* group_name) const { |  | 
| 101     if (!iter->ReadStringPiece(trial_name)) |  | 
| 102       return false; |  | 
| 103     if (!iter->ReadStringPiece(group_name)) |  | 
| 104       return false; |  | 
| 105     return true; |  | 
| 106   } |  | 
| 107 |  | 
| 108   // Calling this is only valid when the entry is initialized. That is, it |  | 
| 109   // resides in shared memory and has a pickle containing the trial name and |  | 
| 110   // group name following it. |  | 
| 111   bool GetTrialAndGroupName(StringPiece* trial_name, |  | 
| 112                             StringPiece* group_name) const { |  | 
| 113     PickleIterator iter = GetPickleIterator(); |  | 
| 114     return ReadStringPair(&iter, trial_name, group_name); |  | 
| 115   } |  | 
| 116 |  | 
| 117   // Calling this is only valid when the entry is initialized as well. Reads the |  | 
| 118   // parameters following the trial and group name and stores them as key-value |  | 
| 119   // mappings in |params|. |  | 
| 120   bool GetParams(std::map<std::string, std::string>* params) const { |  | 
| 121     PickleIterator iter = GetPickleIterator(); |  | 
| 122     StringPiece tmp; |  | 
| 123     if (!ReadStringPair(&iter, &tmp, &tmp)) |  | 
| 124       return false; |  | 
| 125 |  | 
| 126     while (true) { |  | 
| 127       StringPiece key; |  | 
| 128       StringPiece value; |  | 
| 129       if (!ReadStringPair(&iter, &key, &value)) |  | 
| 130         return key.empty();  // Non-empty is bad: got one of a pair. |  | 
| 131       (*params)[key.as_string()] = value.as_string(); |  | 
| 132     } |  | 
| 133   } |  | 
| 134 }; |  | 
| 135 |  | 
| 136 // Writes out string1 and then string2 to pickle. | 71 // Writes out string1 and then string2 to pickle. | 
| 137 bool WriteStringPair(Pickle* pickle, | 72 bool WriteStringPair(Pickle* pickle, | 
| 138                      const StringPiece& string1, | 73                      const StringPiece& string1, | 
| 139                      const StringPiece& string2) { | 74                      const StringPiece& string2) { | 
| 140   if (!pickle->WriteString(string1)) | 75   if (!pickle->WriteString(string1)) | 
| 141     return false; | 76     return false; | 
| 142   if (!pickle->WriteString(string2)) | 77   if (!pickle->WriteString(string2)) | 
| 143     return false; | 78     return false; | 
| 144   return true; | 79   return true; | 
| 145 } | 80 } | 
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 307 | 242 | 
| 308 FieldTrial::EntropyProvider::~EntropyProvider() { | 243 FieldTrial::EntropyProvider::~EntropyProvider() { | 
| 309 } | 244 } | 
| 310 | 245 | 
| 311 FieldTrial::State::State() : activated(false) {} | 246 FieldTrial::State::State() : activated(false) {} | 
| 312 | 247 | 
| 313 FieldTrial::State::State(const State& other) = default; | 248 FieldTrial::State::State(const State& other) = default; | 
| 314 | 249 | 
| 315 FieldTrial::State::~State() {} | 250 FieldTrial::State::~State() {} | 
| 316 | 251 | 
|  | 252 bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName( | 
|  | 253     StringPiece* trial_name, | 
|  | 254     StringPiece* group_name) const { | 
|  | 255   PickleIterator iter = GetPickleIterator(); | 
|  | 256   return ReadStringPair(&iter, trial_name, group_name); | 
|  | 257 } | 
|  | 258 | 
|  | 259 bool FieldTrial::FieldTrialEntry::GetParams( | 
|  | 260     std::map<std::string, std::string>* params) const { | 
|  | 261   PickleIterator iter = GetPickleIterator(); | 
|  | 262   StringPiece tmp; | 
|  | 263   // Skip reading trial and group name. | 
|  | 264   if (!ReadStringPair(&iter, &tmp, &tmp)) | 
|  | 265     return false; | 
|  | 266 | 
|  | 267   while (true) { | 
|  | 268     StringPiece key; | 
|  | 269     StringPiece value; | 
|  | 270     if (!ReadStringPair(&iter, &key, &value)) | 
|  | 271       return key.empty();  // Non-empty is bad: got one of a pair. | 
|  | 272     (*params)[key.as_string()] = value.as_string(); | 
|  | 273   } | 
|  | 274 } | 
|  | 275 | 
|  | 276 PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const { | 
|  | 277   const char* src = | 
|  | 278       reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry); | 
|  | 279 | 
|  | 280   Pickle pickle(src, pickle_size); | 
|  | 281   return PickleIterator(pickle); | 
|  | 282 } | 
|  | 283 | 
|  | 284 bool FieldTrial::FieldTrialEntry::ReadStringPair( | 
|  | 285     PickleIterator* iter, | 
|  | 286     StringPiece* trial_name, | 
|  | 287     StringPiece* group_name) const { | 
|  | 288   if (!iter->ReadStringPiece(trial_name)) | 
|  | 289     return false; | 
|  | 290   if (!iter->ReadStringPiece(group_name)) | 
|  | 291     return false; | 
|  | 292   return true; | 
|  | 293 } | 
|  | 294 | 
| 317 void FieldTrial::Disable() { | 295 void FieldTrial::Disable() { | 
| 318   DCHECK(!group_reported_); | 296   DCHECK(!group_reported_); | 
| 319   enable_field_trial_ = false; | 297   enable_field_trial_ = false; | 
| 320 | 298 | 
| 321   // In case we are disabled after initialization, we need to switch | 299   // In case we are disabled after initialization, we need to switch | 
| 322   // the trial to the default group. | 300   // the trial to the default group. | 
| 323   if (group_ != kNotFinalized) { | 301   if (group_ != kNotFinalized) { | 
| 324     // Only reset when not already the default group, because in case we were | 302     // Only reset when not already the default group, because in case we were | 
| 325     // forced to the default group, the group number may not be | 303     // forced to the default group, the group number may not be | 
| 326     // kDefaultGroupNumber, so we should keep it as is. | 304     // kDefaultGroupNumber, so we should keep it as is. | 
| (...skipping 408 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 735         command_line.GetSwitchValueASCII(switches::kForceFieldTrials), | 713         command_line.GetSwitchValueASCII(switches::kForceFieldTrials), | 
| 736         active_groups); | 714         active_groups); | 
| 737     return; | 715     return; | 
| 738   } | 716   } | 
| 739 | 717 | 
| 740   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 718   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 
| 741   FieldTrialAllocator::Iterator mem_iter(allocator); | 719   FieldTrialAllocator::Iterator mem_iter(allocator); | 
| 742   FieldTrial::FieldTrialRef ref; | 720   FieldTrial::FieldTrialRef ref; | 
| 743   while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 721   while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 
| 744          SharedPersistentMemoryAllocator::kReferenceNull) { | 722          SharedPersistentMemoryAllocator::kReferenceNull) { | 
| 745     const FieldTrialEntry* entry = | 723     const FieldTrial::FieldTrialEntry* entry = | 
| 746         allocator->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | 724         allocator->GetAsObject<const FieldTrial::FieldTrialEntry>( | 
|  | 725             ref, kFieldTrialType); | 
| 747 | 726 | 
| 748     StringPiece trial_name; | 727     StringPiece trial_name; | 
| 749     StringPiece group_name; | 728     StringPiece group_name; | 
| 750     if (entry->activated && | 729     if (entry->activated && | 
| 751         entry->GetTrialAndGroupName(&trial_name, &group_name)) { | 730         entry->GetTrialAndGroupName(&trial_name, &group_name)) { | 
| 752       FieldTrial::ActiveGroup group; | 731       FieldTrial::ActiveGroup group; | 
| 753       group.trial_name = trial_name.as_string(); | 732       group.trial_name = trial_name.as_string(); | 
| 754       group.group_name = group_name.as_string(); | 733       group.group_name = group_name.as_string(); | 
| 755       active_groups->push_back(group); | 734       active_groups->push_back(group); | 
| 756     } | 735     } | 
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 978   if (!global_) | 957   if (!global_) | 
| 979     return; | 958     return; | 
| 980   global_->observer_list_->RemoveObserver(observer); | 959   global_->observer_list_->RemoveObserver(observer); | 
| 981 } | 960 } | 
| 982 | 961 | 
| 983 // static | 962 // static | 
| 984 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { | 963 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { | 
| 985   if (!global_) | 964   if (!global_) | 
| 986     return; | 965     return; | 
| 987   if (is_locked) { | 966   if (is_locked) { | 
| 988     AddToAllocatorWhileLocked(field_trial); | 967     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), | 
|  | 968                               field_trial); | 
| 989   } else { | 969   } else { | 
| 990     AutoLock auto_lock(global_->lock_); | 970     AutoLock auto_lock(global_->lock_); | 
| 991     AddToAllocatorWhileLocked(field_trial); | 971     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), | 
|  | 972                               field_trial); | 
| 992   } | 973   } | 
| 993 } | 974 } | 
| 994 | 975 | 
| 995 // static | 976 // static | 
| 996 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { | 977 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { | 
| 997   if (!global_) | 978   if (!global_) | 
| 998     return; | 979     return; | 
| 999 | 980 | 
| 1000   { | 981   { | 
| 1001     AutoLock auto_lock(global_->lock_); | 982     AutoLock auto_lock(global_->lock_); | 
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1040   //   allocator should get set up very early in the lifecycle. Try to see if | 1021   //   allocator should get set up very early in the lifecycle. Try to see if | 
| 1041   //   you can call it after it's been set up. | 1022   //   you can call it after it's been set up. | 
| 1042   AutoLock auto_lock(global_->lock_); | 1023   AutoLock auto_lock(global_->lock_); | 
| 1043   if (!global_->field_trial_allocator_) | 1024   if (!global_->field_trial_allocator_) | 
| 1044     return false; | 1025     return false; | 
| 1045 | 1026 | 
| 1046   // If ref_ isn't set, then the field trial data can't be in shared memory. | 1027   // If ref_ isn't set, then the field trial data can't be in shared memory. | 
| 1047   if (!field_trial->ref_) | 1028   if (!field_trial->ref_) | 
| 1048     return false; | 1029     return false; | 
| 1049 | 1030 | 
| 1050   const FieldTrialEntry* entry = | 1031   const FieldTrial::FieldTrialEntry* entry = | 
| 1051       global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>( | 1032       global_->field_trial_allocator_ | 
| 1052           field_trial->ref_, kFieldTrialType); | 1033           ->GetAsObject<const FieldTrial::FieldTrialEntry>(field_trial->ref_, | 
|  | 1034                                                            kFieldTrialType); | 
| 1053 | 1035 | 
| 1054   size_t allocated_size = | 1036   size_t allocated_size = | 
| 1055       global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); | 1037       global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); | 
| 1056   size_t actual_size = sizeof(FieldTrialEntry) + entry->pickle_size; | 1038   size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size; | 
| 1057   if (allocated_size < actual_size) | 1039   if (allocated_size < actual_size) | 
| 1058     return false; | 1040     return false; | 
| 1059 | 1041 | 
| 1060   return entry->GetParams(params); | 1042   return entry->GetParams(params); | 
| 1061 } | 1043 } | 
| 1062 | 1044 | 
| 1063 // static | 1045 // static | 
| 1064 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { | 1046 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { | 
| 1065   if (!global_) | 1047   if (!global_) | 
| 1066     return; | 1048     return; | 
| 1067 | 1049 | 
| 1068   AutoLock auto_lock(global_->lock_); | 1050   AutoLock auto_lock(global_->lock_); | 
| 1069   if (!global_->field_trial_allocator_) | 1051   if (!global_->field_trial_allocator_) | 
| 1070     return; | 1052     return; | 
| 1071 | 1053 | 
| 1072   // To clear the params, we iterate through every item in the allocator, copy | 1054   // To clear the params, we iterate through every item in the allocator, copy | 
| 1073   // just the trial and group name into a newly-allocated segment and then clear | 1055   // just the trial and group name into a newly-allocated segment and then clear | 
| 1074   // the existing item. | 1056   // the existing item. | 
| 1075   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 1057   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 
| 1076   FieldTrialAllocator::Iterator mem_iter(allocator); | 1058   FieldTrialAllocator::Iterator mem_iter(allocator); | 
| 1077 | 1059 | 
| 1078   // List of refs to eventually be made iterable. We can't make it in the loop, | 1060   // List of refs to eventually be made iterable. We can't make it in the loop, | 
| 1079   // since it would go on forever. | 1061   // since it would go on forever. | 
| 1080   std::vector<FieldTrial::FieldTrialRef> new_refs; | 1062   std::vector<FieldTrial::FieldTrialRef> new_refs; | 
| 1081 | 1063 | 
| 1082   FieldTrial::FieldTrialRef prev_ref; | 1064   FieldTrial::FieldTrialRef prev_ref; | 
| 1083   while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 1065   while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 
| 1084          FieldTrialAllocator::kReferenceNull) { | 1066          FieldTrialAllocator::kReferenceNull) { | 
| 1085     // Get the existing field trial entry in shared memory. | 1067     // Get the existing field trial entry in shared memory. | 
| 1086     const FieldTrialEntry* prev_entry = | 1068     const FieldTrial::FieldTrialEntry* prev_entry = | 
| 1087         allocator->GetAsObject<const FieldTrialEntry>(prev_ref, | 1069         allocator->GetAsObject<const FieldTrial::FieldTrialEntry>( | 
| 1088                                                       kFieldTrialType); | 1070             prev_ref, kFieldTrialType); | 
| 1089     StringPiece trial_name; | 1071     StringPiece trial_name; | 
| 1090     StringPiece group_name; | 1072     StringPiece group_name; | 
| 1091     if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) | 1073     if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) | 
| 1092       continue; | 1074       continue; | 
| 1093 | 1075 | 
| 1094     // Write a new entry, minus the params. | 1076     // Write a new entry, minus the params. | 
| 1095     Pickle pickle; | 1077     Pickle pickle; | 
| 1096     pickle.WriteString(trial_name); | 1078     pickle.WriteString(trial_name); | 
| 1097     pickle.WriteString(group_name); | 1079     pickle.WriteString(group_name); | 
| 1098     size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); | 1080     size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); | 
| 1099     FieldTrial::FieldTrialRef new_ref = | 1081     FieldTrial::FieldTrialRef new_ref = | 
| 1100         allocator->Allocate(total_size, kFieldTrialType); | 1082         allocator->Allocate(total_size, kFieldTrialType); | 
| 1101     FieldTrialEntry* new_entry = | 1083     FieldTrial::FieldTrialEntry* new_entry = | 
| 1102         allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType); | 1084         allocator->GetAsObject<FieldTrial::FieldTrialEntry>(new_ref, | 
|  | 1085                                                             kFieldTrialType); | 
| 1103     new_entry->activated = prev_entry->activated; | 1086     new_entry->activated = prev_entry->activated; | 
| 1104     new_entry->pickle_size = pickle.size(); | 1087     new_entry->pickle_size = pickle.size(); | 
| 1105 | 1088 | 
| 1106     // TODO(lawrencewu): Modify base::Pickle to be able to write over a section | 1089     // TODO(lawrencewu): Modify base::Pickle to be able to write over a section | 
| 1107     // in memory, so we can avoid this memcpy. | 1090     // in memory, so we can avoid this memcpy. | 
| 1108     char* dst = reinterpret_cast<char*>(new_entry) + sizeof(FieldTrialEntry); | 1091     char* dst = reinterpret_cast<char*>(new_entry) + | 
|  | 1092                 sizeof(FieldTrial::FieldTrialEntry); | 
| 1109     memcpy(dst, pickle.data(), pickle.size()); | 1093     memcpy(dst, pickle.data(), pickle.size()); | 
| 1110 | 1094 | 
| 1111     // Update the ref on the field trial and add it to the list to be made | 1095     // Update the ref on the field trial and add it to the list to be made | 
| 1112     // iterable. | 1096     // iterable. | 
| 1113     FieldTrial* trial = global_->PreLockedFind(trial_name.as_string()); | 1097     FieldTrial* trial = global_->PreLockedFind(trial_name.as_string()); | 
| 1114     trial->ref_ = new_ref; | 1098     trial->ref_ = new_ref; | 
| 1115     new_refs.push_back(new_ref); | 1099     new_refs.push_back(new_ref); | 
| 1116 | 1100 | 
| 1117     // Mark the existing entry as unused. | 1101     // Mark the existing entry as unused. | 
| 1118     allocator->ChangeType(prev_ref, 0, kFieldTrialType); | 1102     allocator->ChangeType(prev_ref, 0, kFieldTrialType); | 
| 1119   } | 1103   } | 
| 1120 | 1104 | 
| 1121   for (const auto& ref : new_refs) { | 1105   for (const auto& ref : new_refs) { | 
| 1122     allocator->MakeIterable(ref); | 1106     allocator->MakeIterable(ref); | 
| 1123   } | 1107   } | 
| 1124 } | 1108 } | 
| 1125 | 1109 | 
|  | 1110 // static | 
|  | 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*> | 
|  | 1123 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator( | 
|  | 1124     PersistentMemoryAllocator const& allocator) { | 
|  | 1125   std::vector<const FieldTrial::FieldTrialEntry*> entries; | 
|  | 1126   FieldTrial::FieldTrialRef ref; | 
|  | 1127   FieldTrialAllocator::Iterator iter(&allocator); | 
|  | 1128   while ((ref = iter.GetNextOfType(kFieldTrialType)) != | 
|  | 1129          FieldTrialAllocator::kReferenceNull) { | 
|  | 1130     const FieldTrial::FieldTrialEntry* entry = | 
|  | 1131         allocator.GetAsObject<const FieldTrial::FieldTrialEntry>( | 
|  | 1132             ref, kFieldTrialType); | 
|  | 1133     entries.push_back(entry); | 
|  | 1134   } | 
|  | 1135   return entries; | 
|  | 1136 } | 
|  | 1137 | 
| 1126 #if defined(OS_WIN) | 1138 #if defined(OS_WIN) | 
| 1127 // static | 1139 // static | 
| 1128 bool FieldTrialList::CreateTrialsFromHandleSwitch( | 1140 bool FieldTrialList::CreateTrialsFromHandleSwitch( | 
| 1129     const std::string& handle_switch) { | 1141     const std::string& handle_switch) { | 
| 1130   int field_trial_handle = std::stoi(handle_switch); | 1142   int field_trial_handle = std::stoi(handle_switch); | 
| 1131   HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | 1143   HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | 
| 1132   SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); | 1144   SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); | 
| 1133   return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); | 1145   return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); | 
| 1134 } | 1146 } | 
| 1135 #endif | 1147 #endif | 
| (...skipping 16 matching lines...) Expand all  Loading... | 
| 1152 bool FieldTrialList::CreateTrialsFromSharedMemory( | 1164 bool FieldTrialList::CreateTrialsFromSharedMemory( | 
| 1153     std::unique_ptr<SharedMemory> shm) { | 1165     std::unique_ptr<SharedMemory> shm) { | 
| 1154   global_->field_trial_allocator_.reset( | 1166   global_->field_trial_allocator_.reset( | 
| 1155       new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); | 1167       new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); | 
| 1156   FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); | 1168   FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); | 
| 1157   FieldTrialAllocator::Iterator mem_iter(shalloc); | 1169   FieldTrialAllocator::Iterator mem_iter(shalloc); | 
| 1158 | 1170 | 
| 1159   FieldTrial::FieldTrialRef ref; | 1171   FieldTrial::FieldTrialRef ref; | 
| 1160   while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 1172   while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 
| 1161          FieldTrialAllocator::kReferenceNull) { | 1173          FieldTrialAllocator::kReferenceNull) { | 
| 1162     const FieldTrialEntry* entry = | 1174     const FieldTrial::FieldTrialEntry* entry = | 
| 1163         shalloc->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | 1175         shalloc->GetAsObject<const FieldTrial::FieldTrialEntry>( | 
|  | 1176             ref, kFieldTrialType); | 
| 1164 | 1177 | 
| 1165     StringPiece trial_name; | 1178     StringPiece trial_name; | 
| 1166     StringPiece group_name; | 1179     StringPiece group_name; | 
| 1167     if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) | 1180     if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) | 
| 1168       return false; | 1181       return false; | 
| 1169 | 1182 | 
| 1170     // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | 1183     // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | 
| 1171     // StringPieces. | 1184     // StringPieces. | 
| 1172     FieldTrial* trial = | 1185     FieldTrial* trial = | 
| 1173         CreateFieldTrial(trial_name.as_string(), group_name.as_string()); | 1186         CreateFieldTrial(trial_name.as_string(), group_name.as_string()); | 
| (...skipping 29 matching lines...) Expand all  Loading... | 
| 1203 | 1216 | 
| 1204   if (!shm->Map(kFieldTrialAllocationSize)) | 1217   if (!shm->Map(kFieldTrialAllocationSize)) | 
| 1205     TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 1218     TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 
| 1206 | 1219 | 
| 1207   global_->field_trial_allocator_.reset( | 1220   global_->field_trial_allocator_.reset( | 
| 1208       new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); | 1221       new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); | 
| 1209   global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 1222   global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 
| 1210 | 1223 | 
| 1211   // Add all existing field trials. | 1224   // Add all existing field trials. | 
| 1212   for (const auto& registered : global_->registered_) { | 1225   for (const auto& registered : global_->registered_) { | 
| 1213     AddToAllocatorWhileLocked(registered.second); | 1226     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), | 
|  | 1227                               registered.second); | 
| 1214   } | 1228   } | 
| 1215 | 1229 | 
| 1216   // Add all existing features. | 1230   // Add all existing features. | 
| 1217   FeatureList::GetInstance()->AddFeaturesToAllocator( | 1231   FeatureList::GetInstance()->AddFeaturesToAllocator( | 
| 1218       global_->field_trial_allocator_.get()); | 1232       global_->field_trial_allocator_.get()); | 
| 1219 | 1233 | 
| 1220 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) | 1234 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) | 
| 1221   // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 1235   // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 
| 1222   // via the command line. | 1236   // via the command line. | 
| 1223   global_->readonly_allocator_handle_ = | 1237   global_->readonly_allocator_handle_ = | 
| 1224       CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 1238       CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 
| 1225 #endif | 1239 #endif | 
| 1226 } | 1240 } | 
| 1227 #endif | 1241 #endif | 
| 1228 | 1242 | 
| 1229 // static | 1243 // static | 
| 1230 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { | 1244 void FieldTrialList::AddToAllocatorWhileLocked( | 
| 1231   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 1245     PersistentMemoryAllocator* allocator, | 
| 1232 | 1246     FieldTrial* field_trial) { | 
| 1233   // Don't do anything if the allocator hasn't been instantiated yet. | 1247   // Don't do anything if the allocator hasn't been instantiated yet. | 
| 1234   if (allocator == nullptr) | 1248   if (allocator == nullptr) | 
| 1235     return; | 1249     return; | 
| 1236 | 1250 | 
| 1237   // Or if the allocator is read only, which means we are in a child process and | 1251   // Or if the allocator is read only, which means we are in a child process and | 
| 1238   // shouldn't be writing to it. | 1252   // shouldn't be writing to it. | 
| 1239   if (allocator->IsReadonly()) | 1253   if (allocator->IsReadonly()) | 
| 1240     return; | 1254     return; | 
| 1241 | 1255 | 
| 1242   FieldTrial::State trial_state; | 1256   FieldTrial::State trial_state; | 
| 1243   if (!field_trial->GetStateWhileLocked(&trial_state)) | 1257   if (!field_trial->GetStateWhileLocked(&trial_state)) | 
| 1244     return; | 1258     return; | 
| 1245 | 1259 | 
| 1246   // Or if we've already added it. We must check after GetState since it can | 1260   // Or if we've already added it. We must check after GetState since it can | 
| 1247   // also add to the allocator. | 1261   // also add to the allocator. | 
| 1248   if (field_trial->ref_) | 1262   if (field_trial->ref_) | 
| 1249     return; | 1263     return; | 
| 1250 | 1264 | 
| 1251   Pickle pickle; | 1265   Pickle pickle; | 
| 1252   if (!PickleFieldTrial(trial_state, &pickle)) { | 1266   if (!PickleFieldTrial(trial_state, &pickle)) { | 
| 1253     NOTREACHED(); | 1267     NOTREACHED(); | 
| 1254     return; | 1268     return; | 
| 1255   } | 1269   } | 
| 1256 | 1270 | 
| 1257   size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); | 1271   size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); | 
| 1258   FieldTrial::FieldTrialRef ref = | 1272   FieldTrial::FieldTrialRef ref = | 
| 1259       allocator->Allocate(total_size, kFieldTrialType); | 1273       allocator->Allocate(total_size, kFieldTrialType); | 
| 1260   if (ref == FieldTrialAllocator::kReferenceNull) { | 1274   if (ref == FieldTrialAllocator::kReferenceNull) { | 
| 1261     NOTREACHED(); | 1275     NOTREACHED(); | 
| 1262     return; | 1276     return; | 
| 1263   } | 1277   } | 
| 1264 | 1278 | 
| 1265   FieldTrialEntry* entry = | 1279   FieldTrial::FieldTrialEntry* entry = | 
| 1266       allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 1280       allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, kFieldTrialType); | 
| 1267   entry->activated = trial_state.activated; | 1281   entry->activated = trial_state.activated; | 
| 1268   entry->pickle_size = pickle.size(); | 1282   entry->pickle_size = pickle.size(); | 
| 1269 | 1283 | 
| 1270   // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in | 1284   // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in | 
| 1271   // memory, so we can avoid this memcpy. | 1285   // memory, so we can avoid this memcpy. | 
| 1272   char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); | 1286   char* dst = | 
|  | 1287       reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry); | 
| 1273   memcpy(dst, pickle.data(), pickle.size()); | 1288   memcpy(dst, pickle.data(), pickle.size()); | 
| 1274 | 1289 | 
| 1275   allocator->MakeIterable(ref); | 1290   allocator->MakeIterable(ref); | 
| 1276   field_trial->ref_ = ref; | 1291   field_trial->ref_ = ref; | 
| 1277 } | 1292 } | 
| 1278 | 1293 | 
| 1279 // static | 1294 // static | 
| 1280 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( | 1295 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( | 
| 1281     FieldTrial* field_trial) { | 1296     FieldTrial* field_trial) { | 
| 1282   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 1297   FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 
| 1283 | 1298 | 
| 1284   // Check if we're in the child process and return early if so. | 1299   // Check if we're in the child process and return early if so. | 
| 1285   if (allocator && allocator->IsReadonly()) | 1300   if (allocator && allocator->IsReadonly()) | 
| 1286     return; | 1301     return; | 
| 1287 | 1302 | 
| 1288   FieldTrial::FieldTrialRef ref = field_trial->ref_; | 1303   FieldTrial::FieldTrialRef ref = field_trial->ref_; | 
| 1289   if (ref == FieldTrialAllocator::kReferenceNull) { | 1304   if (ref == FieldTrialAllocator::kReferenceNull) { | 
| 1290     // It's fine to do this even if the allocator hasn't been instantiated | 1305     // It's fine to do this even if the allocator hasn't been instantiated | 
| 1291     // yet -- it'll just return early. | 1306     // yet -- it'll just return early. | 
| 1292     AddToAllocatorWhileLocked(field_trial); | 1307     AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), | 
|  | 1308                               field_trial); | 
| 1293   } else { | 1309   } else { | 
| 1294     // It's also okay to do this even though the callee doesn't have a lock -- | 1310     // It's also okay to do this even though the callee doesn't have a lock -- | 
| 1295     // the only thing that happens on a stale read here is a slight performance | 1311     // the only thing that happens on a stale read here is a slight performance | 
| 1296     // hit from the child re-synchronizing activation state. | 1312     // hit from the child re-synchronizing activation state. | 
| 1297     FieldTrialEntry* entry = | 1313     FieldTrial::FieldTrialEntry* entry = | 
| 1298         allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 1314         allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, | 
|  | 1315                                                             kFieldTrialType); | 
| 1299     entry->activated = true; | 1316     entry->activated = true; | 
| 1300   } | 1317   } | 
| 1301 } | 1318 } | 
| 1302 | 1319 | 
| 1303 // static | 1320 // static | 
| 1304 const FieldTrial::EntropyProvider* | 1321 const FieldTrial::EntropyProvider* | 
| 1305     FieldTrialList::GetEntropyProviderForOneTimeRandomization() { | 1322     FieldTrialList::GetEntropyProviderForOneTimeRandomization() { | 
| 1306   if (!global_) { | 1323   if (!global_) { | 
| 1307     used_without_global_ = true; | 1324     used_without_global_ = true; | 
| 1308     return NULL; | 1325     return NULL; | 
| (...skipping 16 matching lines...) Expand all  Loading... | 
| 1325     return; | 1342     return; | 
| 1326   } | 1343   } | 
| 1327   AutoLock auto_lock(global_->lock_); | 1344   AutoLock auto_lock(global_->lock_); | 
| 1328   CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1345   CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 
| 1329   trial->AddRef(); | 1346   trial->AddRef(); | 
| 1330   trial->SetTrialRegistered(); | 1347   trial->SetTrialRegistered(); | 
| 1331   global_->registered_[trial->trial_name()] = trial; | 1348   global_->registered_[trial->trial_name()] = trial; | 
| 1332 } | 1349 } | 
| 1333 | 1350 | 
| 1334 }  // namespace base | 1351 }  // namespace base | 
| OLD | NEW | 
|---|