| 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 |