Chromium Code Reviews| 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 PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const { | |
| 253 const char* src = | |
| 254 reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry); | |
| 255 | |
| 256 Pickle pickle(src, pickle_size); | |
| 257 return PickleIterator(pickle); | |
| 258 } | |
| 259 | |
| 260 bool FieldTrial::FieldTrialEntry::ReadStringPair( | |
| 261 PickleIterator* iter, | |
| 262 StringPiece* trial_name, | |
| 263 StringPiece* group_name) const { | |
| 264 if (!iter->ReadStringPiece(trial_name)) | |
| 265 return false; | |
| 266 if (!iter->ReadStringPiece(group_name)) | |
| 267 return false; | |
| 268 return true; | |
| 269 } | |
| 270 | |
| 271 bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName( | |
| 272 StringPiece* trial_name, | |
| 273 StringPiece* group_name) const { | |
| 274 PickleIterator iter = GetPickleIterator(); | |
| 275 return ReadStringPair(&iter, trial_name, group_name); | |
| 276 } | |
| 277 | |
| 278 bool FieldTrial::FieldTrialEntry::GetParams( | |
| 279 std::map<std::string, std::string>* params) const { | |
| 280 PickleIterator iter = GetPickleIterator(); | |
| 281 StringPiece tmp; | |
| 282 if (!ReadStringPair(&iter, &tmp, &tmp)) | |
| 283 return false; | |
| 284 | |
| 285 while (true) { | |
| 286 StringPiece key; | |
| 287 StringPiece value; | |
| 288 if (!ReadStringPair(&iter, &key, &value)) | |
| 289 return key.empty(); // Non-empty is bad: got one of a pair. | |
| 290 (*params)[key.as_string()] = value.as_string(); | |
| 291 } | |
| 292 } | |
| 293 | |
| 317 void FieldTrial::Disable() { | 294 void FieldTrial::Disable() { |
| 318 DCHECK(!group_reported_); | 295 DCHECK(!group_reported_); |
| 319 enable_field_trial_ = false; | 296 enable_field_trial_ = false; |
| 320 | 297 |
| 321 // In case we are disabled after initialization, we need to switch | 298 // In case we are disabled after initialization, we need to switch |
| 322 // the trial to the default group. | 299 // the trial to the default group. |
| 323 if (group_ != kNotFinalized) { | 300 if (group_ != kNotFinalized) { |
| 324 // Only reset when not already the default group, because in case we were | 301 // 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 | 302 // forced to the default group, the group number may not be |
| 326 // kDefaultGroupNumber, so we should keep it as is. | 303 // 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), | 712 command_line.GetSwitchValueASCII(switches::kForceFieldTrials), |
| 736 active_groups); | 713 active_groups); |
| 737 return; | 714 return; |
| 738 } | 715 } |
| 739 | 716 |
| 740 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 717 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); |
| 741 FieldTrialAllocator::Iterator mem_iter(allocator); | 718 FieldTrialAllocator::Iterator mem_iter(allocator); |
| 742 FieldTrial::FieldTrialRef ref; | 719 FieldTrial::FieldTrialRef ref; |
| 743 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 720 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| 744 SharedPersistentMemoryAllocator::kReferenceNull) { | 721 SharedPersistentMemoryAllocator::kReferenceNull) { |
| 745 const FieldTrialEntry* entry = | 722 const FieldTrial::FieldTrialEntry* entry = |
| 746 allocator->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | 723 allocator->GetAsObject<const FieldTrial::FieldTrialEntry>( |
| 724 ref, kFieldTrialType); | |
| 747 | 725 |
| 748 StringPiece trial_name; | 726 StringPiece trial_name; |
| 749 StringPiece group_name; | 727 StringPiece group_name; |
| 750 if (entry->activated && | 728 if (entry->activated && |
| 751 entry->GetTrialAndGroupName(&trial_name, &group_name)) { | 729 entry->GetTrialAndGroupName(&trial_name, &group_name)) { |
| 752 FieldTrial::ActiveGroup group; | 730 FieldTrial::ActiveGroup group; |
| 753 group.trial_name = trial_name.as_string(); | 731 group.trial_name = trial_name.as_string(); |
| 754 group.group_name = group_name.as_string(); | 732 group.group_name = group_name.as_string(); |
| 755 active_groups->push_back(group); | 733 active_groups->push_back(group); |
| 756 } | 734 } |
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 978 if (!global_) | 956 if (!global_) |
| 979 return; | 957 return; |
| 980 global_->observer_list_->RemoveObserver(observer); | 958 global_->observer_list_->RemoveObserver(observer); |
| 981 } | 959 } |
| 982 | 960 |
| 983 // static | 961 // static |
| 984 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { | 962 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { |
| 985 if (!global_) | 963 if (!global_) |
| 986 return; | 964 return; |
| 987 if (is_locked) { | 965 if (is_locked) { |
| 988 AddToAllocatorWhileLocked(field_trial); | 966 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| 967 field_trial); | |
| 989 } else { | 968 } else { |
| 990 AutoLock auto_lock(global_->lock_); | 969 AutoLock auto_lock(global_->lock_); |
| 991 AddToAllocatorWhileLocked(field_trial); | 970 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| 971 field_trial); | |
| 992 } | 972 } |
| 993 } | 973 } |
| 994 | 974 |
| 995 // static | 975 // static |
| 996 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { | 976 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { |
| 997 if (!global_) | 977 if (!global_) |
| 998 return; | 978 return; |
| 999 | 979 |
| 1000 { | 980 { |
| 1001 AutoLock auto_lock(global_->lock_); | 981 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 | 1020 // 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. | 1021 // you can call it after it's been set up. |
| 1042 AutoLock auto_lock(global_->lock_); | 1022 AutoLock auto_lock(global_->lock_); |
| 1043 if (!global_->field_trial_allocator_) | 1023 if (!global_->field_trial_allocator_) |
| 1044 return false; | 1024 return false; |
| 1045 | 1025 |
| 1046 // If ref_ isn't set, then the field trial data can't be in shared memory. | 1026 // If ref_ isn't set, then the field trial data can't be in shared memory. |
| 1047 if (!field_trial->ref_) | 1027 if (!field_trial->ref_) |
| 1048 return false; | 1028 return false; |
| 1049 | 1029 |
| 1050 const FieldTrialEntry* entry = | 1030 const FieldTrial::FieldTrialEntry* entry = |
| 1051 global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>( | 1031 global_->field_trial_allocator_ |
| 1052 field_trial->ref_, kFieldTrialType); | 1032 ->GetAsObject<const FieldTrial::FieldTrialEntry>(field_trial->ref_, |
| 1033 kFieldTrialType); | |
| 1053 | 1034 |
| 1054 size_t allocated_size = | 1035 size_t allocated_size = |
| 1055 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); | 1036 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); |
| 1056 size_t actual_size = sizeof(FieldTrialEntry) + entry->pickle_size; | 1037 size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size; |
| 1057 if (allocated_size < actual_size) | 1038 if (allocated_size < actual_size) |
| 1058 return false; | 1039 return false; |
| 1059 | 1040 |
| 1060 return entry->GetParams(params); | 1041 return entry->GetParams(params); |
| 1061 } | 1042 } |
| 1062 | 1043 |
| 1063 // static | 1044 // static |
| 1064 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { | 1045 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { |
| 1065 if (!global_) | 1046 if (!global_) |
| 1066 return; | 1047 return; |
| 1067 | 1048 |
| 1068 AutoLock auto_lock(global_->lock_); | 1049 AutoLock auto_lock(global_->lock_); |
| 1069 if (!global_->field_trial_allocator_) | 1050 if (!global_->field_trial_allocator_) |
| 1070 return; | 1051 return; |
| 1071 | 1052 |
| 1072 // To clear the params, we iterate through every item in the allocator, copy | 1053 // 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 | 1054 // just the trial and group name into a newly-allocated segment and then clear |
| 1074 // the existing item. | 1055 // the existing item. |
| 1075 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 1056 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); |
| 1076 FieldTrialAllocator::Iterator mem_iter(allocator); | 1057 FieldTrialAllocator::Iterator mem_iter(allocator); |
| 1077 | 1058 |
| 1078 // List of refs to eventually be made iterable. We can't make it in the loop, | 1059 // List of refs to eventually be made iterable. We can't make it in the loop, |
| 1079 // since it would go on forever. | 1060 // since it would go on forever. |
| 1080 std::vector<FieldTrial::FieldTrialRef> new_refs; | 1061 std::vector<FieldTrial::FieldTrialRef> new_refs; |
| 1081 | 1062 |
| 1082 FieldTrial::FieldTrialRef prev_ref; | 1063 FieldTrial::FieldTrialRef prev_ref; |
| 1083 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 1064 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| 1084 FieldTrialAllocator::kReferenceNull) { | 1065 FieldTrialAllocator::kReferenceNull) { |
| 1085 // Get the existing field trial entry in shared memory. | 1066 // Get the existing field trial entry in shared memory. |
| 1086 const FieldTrialEntry* prev_entry = | 1067 const FieldTrial::FieldTrialEntry* prev_entry = |
| 1087 allocator->GetAsObject<const FieldTrialEntry>(prev_ref, | 1068 allocator->GetAsObject<const FieldTrial::FieldTrialEntry>( |
| 1088 kFieldTrialType); | 1069 prev_ref, kFieldTrialType); |
| 1089 StringPiece trial_name; | 1070 StringPiece trial_name; |
| 1090 StringPiece group_name; | 1071 StringPiece group_name; |
| 1091 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) | 1072 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) |
| 1092 continue; | 1073 continue; |
| 1093 | 1074 |
| 1094 // Write a new entry, minus the params. | 1075 // Write a new entry, minus the params. |
| 1095 Pickle pickle; | 1076 Pickle pickle; |
| 1096 pickle.WriteString(trial_name); | 1077 pickle.WriteString(trial_name); |
| 1097 pickle.WriteString(group_name); | 1078 pickle.WriteString(group_name); |
| 1098 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); | 1079 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); |
| 1099 FieldTrial::FieldTrialRef new_ref = | 1080 FieldTrial::FieldTrialRef new_ref = |
| 1100 allocator->Allocate(total_size, kFieldTrialType); | 1081 allocator->Allocate(total_size, kFieldTrialType); |
| 1101 FieldTrialEntry* new_entry = | 1082 FieldTrial::FieldTrialEntry* new_entry = |
| 1102 allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType); | 1083 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(new_ref, |
| 1084 kFieldTrialType); | |
| 1103 new_entry->activated = prev_entry->activated; | 1085 new_entry->activated = prev_entry->activated; |
| 1104 new_entry->pickle_size = pickle.size(); | 1086 new_entry->pickle_size = pickle.size(); |
| 1105 | 1087 |
| 1106 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section | 1088 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section |
| 1107 // in memory, so we can avoid this memcpy. | 1089 // in memory, so we can avoid this memcpy. |
| 1108 char* dst = reinterpret_cast<char*>(new_entry) + sizeof(FieldTrialEntry); | 1090 char* dst = reinterpret_cast<char*>(new_entry) + |
| 1091 sizeof(FieldTrial::FieldTrialEntry); | |
| 1109 memcpy(dst, pickle.data(), pickle.size()); | 1092 memcpy(dst, pickle.data(), pickle.size()); |
| 1110 | 1093 |
| 1111 // Update the ref on the field trial and add it to the list to be made | 1094 // Update the ref on the field trial and add it to the list to be made |
| 1112 // iterable. | 1095 // iterable. |
| 1113 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string()); | 1096 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string()); |
| 1114 trial->ref_ = new_ref; | 1097 trial->ref_ = new_ref; |
| 1115 new_refs.push_back(new_ref); | 1098 new_refs.push_back(new_ref); |
| 1116 | 1099 |
| 1117 // Mark the existing entry as unused. | 1100 // Mark the existing entry as unused. |
| 1118 allocator->ChangeType(prev_ref, 0, kFieldTrialType); | 1101 allocator->ChangeType(prev_ref, 0, kFieldTrialType); |
| 1119 } | 1102 } |
| 1120 | 1103 |
| 1121 for (const auto& ref : new_refs) { | 1104 for (const auto& ref : new_refs) { |
| 1122 allocator->MakeIterable(ref); | 1105 allocator->MakeIterable(ref); |
| 1123 } | 1106 } |
| 1124 } | 1107 } |
| 1125 | 1108 |
| 1109 // static | |
| 1110 void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator( | |
| 1111 PersistentMemoryAllocator* allocator) { | |
| 1112 if (!global_) | |
| 1113 return; | |
| 1114 AutoLock auto_lock(global_->lock_); | |
| 1115 for (const auto& registered : global_->registered_) { | |
| 1116 AddToAllocatorWhileLocked(allocator, registered.second); | |
| 1117 } | |
| 1118 } | |
| 1119 | |
| 1120 // static | |
| 1121 std::vector<FieldTrial::FieldTrialEntry*> | |
| 1122 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator( | |
| 1123 PersistentMemoryAllocator* allocator) { | |
| 1124 std::vector<FieldTrial::FieldTrialEntry*> field_trial_entries; | |
| 1125 FieldTrial::FieldTrialRef ref; | |
| 1126 FieldTrialAllocator::Iterator iter(allocator); | |
| 1127 while ((ref = iter.GetNextOfType(kFieldTrialType)) != | |
| 1128 FieldTrialAllocator::kReferenceNull) { | |
| 1129 FieldTrial::FieldTrialEntry* entry = | |
|
bcwhite
2016/12/09 19:40:28
const
lawrencewu
2016/12/09 19:47:59
Done.
| |
| 1130 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, | |
| 1131 kFieldTrialType); | |
| 1132 field_trial_entries.push_back(entry); | |
| 1133 } | |
| 1134 return field_trial_entries; | |
| 1135 } | |
| 1136 | |
| 1126 #if defined(OS_WIN) | 1137 #if defined(OS_WIN) |
| 1127 // static | 1138 // static |
| 1128 bool FieldTrialList::CreateTrialsFromHandleSwitch( | 1139 bool FieldTrialList::CreateTrialsFromHandleSwitch( |
| 1129 const std::string& handle_switch) { | 1140 const std::string& handle_switch) { |
| 1130 int field_trial_handle = std::stoi(handle_switch); | 1141 int field_trial_handle = std::stoi(handle_switch); |
| 1131 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | 1142 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); |
| 1132 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); | 1143 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); |
| 1133 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); | 1144 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); |
| 1134 } | 1145 } |
| 1135 #endif | 1146 #endif |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 1152 bool FieldTrialList::CreateTrialsFromSharedMemory( | 1163 bool FieldTrialList::CreateTrialsFromSharedMemory( |
| 1153 std::unique_ptr<SharedMemory> shm) { | 1164 std::unique_ptr<SharedMemory> shm) { |
| 1154 global_->field_trial_allocator_.reset( | 1165 global_->field_trial_allocator_.reset( |
| 1155 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); | 1166 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); |
| 1156 FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); | 1167 FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); |
| 1157 FieldTrialAllocator::Iterator mem_iter(shalloc); | 1168 FieldTrialAllocator::Iterator mem_iter(shalloc); |
| 1158 | 1169 |
| 1159 FieldTrial::FieldTrialRef ref; | 1170 FieldTrial::FieldTrialRef ref; |
| 1160 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 1171 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| 1161 FieldTrialAllocator::kReferenceNull) { | 1172 FieldTrialAllocator::kReferenceNull) { |
| 1162 const FieldTrialEntry* entry = | 1173 const FieldTrial::FieldTrialEntry* entry = |
| 1163 shalloc->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | 1174 shalloc->GetAsObject<const FieldTrial::FieldTrialEntry>( |
| 1175 ref, kFieldTrialType); | |
| 1164 | 1176 |
| 1165 StringPiece trial_name; | 1177 StringPiece trial_name; |
| 1166 StringPiece group_name; | 1178 StringPiece group_name; |
| 1167 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) | 1179 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) |
| 1168 return false; | 1180 return false; |
| 1169 | 1181 |
| 1170 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | 1182 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take |
| 1171 // StringPieces. | 1183 // StringPieces. |
| 1172 FieldTrial* trial = | 1184 FieldTrial* trial = |
| 1173 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); | 1185 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 1203 | 1215 |
| 1204 if (!shm->Map(kFieldTrialAllocationSize)) | 1216 if (!shm->Map(kFieldTrialAllocationSize)) |
| 1205 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 1217 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); |
| 1206 | 1218 |
| 1207 global_->field_trial_allocator_.reset( | 1219 global_->field_trial_allocator_.reset( |
| 1208 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); | 1220 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); |
| 1209 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 1221 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); |
| 1210 | 1222 |
| 1211 // Add all existing field trials. | 1223 // Add all existing field trials. |
| 1212 for (const auto& registered : global_->registered_) { | 1224 for (const auto& registered : global_->registered_) { |
| 1213 AddToAllocatorWhileLocked(registered.second); | 1225 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| 1226 registered.second); | |
| 1214 } | 1227 } |
| 1215 | 1228 |
| 1216 // Add all existing features. | 1229 // Add all existing features. |
| 1217 FeatureList::GetInstance()->AddFeaturesToAllocator( | 1230 FeatureList::GetInstance()->AddFeaturesToAllocator( |
| 1218 global_->field_trial_allocator_.get()); | 1231 global_->field_trial_allocator_.get()); |
| 1219 | 1232 |
| 1220 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) | 1233 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) |
| 1221 // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 1234 // Set |readonly_allocator_handle_| so we can pass it to be inherited and |
| 1222 // via the command line. | 1235 // via the command line. |
| 1223 global_->readonly_allocator_handle_ = | 1236 global_->readonly_allocator_handle_ = |
| 1224 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 1237 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
| 1225 #endif | 1238 #endif |
| 1226 } | 1239 } |
| 1227 #endif | 1240 #endif |
| 1228 | 1241 |
| 1229 // static | 1242 // static |
| 1230 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { | 1243 void FieldTrialList::AddToAllocatorWhileLocked( |
| 1231 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 1244 PersistentMemoryAllocator* allocator, |
| 1232 | 1245 FieldTrial* field_trial) { |
| 1233 // Don't do anything if the allocator hasn't been instantiated yet. | 1246 // Don't do anything if the allocator hasn't been instantiated yet. |
| 1234 if (allocator == nullptr) | 1247 if (allocator == nullptr) |
| 1235 return; | 1248 return; |
| 1236 | 1249 |
| 1237 // Or if the allocator is read only, which means we are in a child process and | 1250 // Or if the allocator is read only, which means we are in a child process and |
| 1238 // shouldn't be writing to it. | 1251 // shouldn't be writing to it. |
| 1239 if (allocator->IsReadonly()) | 1252 if (allocator->IsReadonly()) |
| 1240 return; | 1253 return; |
| 1241 | 1254 |
| 1242 FieldTrial::State trial_state; | 1255 FieldTrial::State trial_state; |
| 1243 if (!field_trial->GetStateWhileLocked(&trial_state)) | 1256 if (!field_trial->GetStateWhileLocked(&trial_state)) |
| 1244 return; | 1257 return; |
| 1245 | 1258 |
| 1246 // Or if we've already added it. We must check after GetState since it can | 1259 // Or if we've already added it. We must check after GetState since it can |
| 1247 // also add to the allocator. | 1260 // also add to the allocator. |
| 1248 if (field_trial->ref_) | 1261 if (field_trial->ref_) |
| 1249 return; | 1262 return; |
| 1250 | 1263 |
| 1251 Pickle pickle; | 1264 Pickle pickle; |
| 1252 if (!PickleFieldTrial(trial_state, &pickle)) { | 1265 if (!PickleFieldTrial(trial_state, &pickle)) { |
| 1253 NOTREACHED(); | 1266 NOTREACHED(); |
| 1254 return; | 1267 return; |
| 1255 } | 1268 } |
| 1256 | 1269 |
| 1257 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); | 1270 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); |
| 1258 FieldTrial::FieldTrialRef ref = | 1271 FieldTrial::FieldTrialRef ref = |
| 1259 allocator->Allocate(total_size, kFieldTrialType); | 1272 allocator->Allocate(total_size, kFieldTrialType); |
| 1260 if (ref == FieldTrialAllocator::kReferenceNull) { | 1273 if (ref == FieldTrialAllocator::kReferenceNull) { |
| 1261 NOTREACHED(); | 1274 NOTREACHED(); |
| 1262 return; | 1275 return; |
| 1263 } | 1276 } |
| 1264 | 1277 |
| 1265 FieldTrialEntry* entry = | 1278 FieldTrial::FieldTrialEntry* entry = |
| 1266 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 1279 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, kFieldTrialType); |
| 1267 entry->activated = trial_state.activated; | 1280 entry->activated = trial_state.activated; |
| 1268 entry->pickle_size = pickle.size(); | 1281 entry->pickle_size = pickle.size(); |
| 1269 | 1282 |
| 1270 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in | 1283 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in |
| 1271 // memory, so we can avoid this memcpy. | 1284 // memory, so we can avoid this memcpy. |
| 1272 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); | 1285 char* dst = |
| 1286 reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry); | |
| 1273 memcpy(dst, pickle.data(), pickle.size()); | 1287 memcpy(dst, pickle.data(), pickle.size()); |
| 1274 | 1288 |
| 1275 allocator->MakeIterable(ref); | 1289 allocator->MakeIterable(ref); |
| 1276 field_trial->ref_ = ref; | 1290 field_trial->ref_ = ref; |
| 1277 } | 1291 } |
| 1278 | 1292 |
| 1279 // static | 1293 // static |
| 1280 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( | 1294 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( |
| 1281 FieldTrial* field_trial) { | 1295 FieldTrial* field_trial) { |
| 1282 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 1296 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); |
| 1283 | 1297 |
| 1284 // Check if we're in the child process and return early if so. | 1298 // Check if we're in the child process and return early if so. |
| 1285 if (allocator && allocator->IsReadonly()) | 1299 if (allocator && allocator->IsReadonly()) |
| 1286 return; | 1300 return; |
| 1287 | 1301 |
| 1288 FieldTrial::FieldTrialRef ref = field_trial->ref_; | 1302 FieldTrial::FieldTrialRef ref = field_trial->ref_; |
| 1289 if (ref == FieldTrialAllocator::kReferenceNull) { | 1303 if (ref == FieldTrialAllocator::kReferenceNull) { |
| 1290 // It's fine to do this even if the allocator hasn't been instantiated | 1304 // It's fine to do this even if the allocator hasn't been instantiated |
| 1291 // yet -- it'll just return early. | 1305 // yet -- it'll just return early. |
| 1292 AddToAllocatorWhileLocked(field_trial); | 1306 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| 1307 field_trial); | |
| 1293 } else { | 1308 } else { |
| 1294 // It's also okay to do this even though the callee doesn't have a lock -- | 1309 // 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 | 1310 // the only thing that happens on a stale read here is a slight performance |
| 1296 // hit from the child re-synchronizing activation state. | 1311 // hit from the child re-synchronizing activation state. |
| 1297 FieldTrialEntry* entry = | 1312 FieldTrial::FieldTrialEntry* entry = |
| 1298 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 1313 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, |
| 1314 kFieldTrialType); | |
| 1299 entry->activated = true; | 1315 entry->activated = true; |
| 1300 } | 1316 } |
| 1301 } | 1317 } |
| 1302 | 1318 |
| 1303 // static | 1319 // static |
| 1304 const FieldTrial::EntropyProvider* | 1320 const FieldTrial::EntropyProvider* |
| 1305 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { | 1321 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { |
| 1306 if (!global_) { | 1322 if (!global_) { |
| 1307 used_without_global_ = true; | 1323 used_without_global_ = true; |
| 1308 return NULL; | 1324 return NULL; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 1325 return; | 1341 return; |
| 1326 } | 1342 } |
| 1327 AutoLock auto_lock(global_->lock_); | 1343 AutoLock auto_lock(global_->lock_); |
| 1328 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1344 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 1329 trial->AddRef(); | 1345 trial->AddRef(); |
| 1330 trial->SetTrialRegistered(); | 1346 trial->SetTrialRegistered(); |
| 1331 global_->registered_[trial->trial_name()] = trial; | 1347 global_->registered_[trial->trial_name()] = trial; |
| 1332 } | 1348 } |
| 1333 | 1349 |
| 1334 } // namespace base | 1350 } // namespace base |
| OLD | NEW |