Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/feature_list.h" | 5 #include "base/feature_list.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/metrics/field_trial.h" | 14 #include "base/metrics/field_trial.h" |
| 15 #include "base/pickle.h" | |
| 15 #include "base/strings/string_split.h" | 16 #include "base/strings/string_split.h" |
| 16 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
| 17 | 18 |
| 18 namespace base { | 19 namespace base { |
| 19 | 20 |
| 20 namespace { | 21 namespace { |
| 21 | 22 |
| 22 // Pointer to the FeatureList instance singleton that was set via | 23 // Pointer to the FeatureList instance singleton that was set via |
| 23 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to | 24 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to |
| 24 // have more control over initialization timing. Leaky. | 25 // have more control over initialization timing. Leaky. |
| 25 FeatureList* g_instance = nullptr; | 26 FeatureList* g_instance = nullptr; |
| 26 | 27 |
| 27 // Tracks whether the FeatureList instance was initialized via an accessor. | 28 // Tracks whether the FeatureList instance was initialized via an accessor. |
| 28 bool g_initialized_from_accessor = false; | 29 bool g_initialized_from_accessor = false; |
| 29 | 30 |
| 31 const uint32_t kFeatureType = 0x06567CA6 + 1; // SHA1(FeatureEntry) v1 | |
| 32 | |
| 33 // An allocator entry for a feature in shared memory. The FeatureEntry is | |
| 34 // followed by a base::Pickle object that contains the feature and trial name. | |
| 35 // Any changes to this structure requires a bump in kFeatureType defined above. | |
| 36 struct FeatureEntry { | |
| 37 // Expected size for 32/64-bit check. | |
| 38 static constexpr size_t kExpectedInstanceSize = 8; | |
| 39 | |
| 40 // Specifies whether a feature override enables or disables the feature. Same | |
| 41 // values as the OverrideState enum in feature_list.h | |
| 42 uint32_t override_state; | |
|
piman
2016/12/02 22:29:26
Do you intend to modify this field (or other conte
lawrencewu
2016/12/02 22:51:48
I'm pretty sure we don't need to modify any fields
piman
2016/12/02 23:03:24
Either way, please add comments somewhere as to th
| |
| 43 | |
| 44 // Size of the pickled structure, NOT the total size of this entry. | |
| 45 uint32_t pickle_size; | |
| 46 | |
| 47 // Reads the feature and trial name from the pickle. Calling this is only | |
| 48 // valid on an initialized entry that's in shared memory. | |
| 49 bool GetFeatureAndTrialName(StringPiece* feature_name, | |
| 50 StringPiece* trial_name) const { | |
| 51 char* src = reinterpret_cast<char*>(const_cast<FeatureEntry*>(this)) + | |
|
piman
2016/12/02 22:29:26
nit: why const_cast? The memory is read-only, so w
lawrencewu
2016/12/02 22:51:48
Done.
| |
| 52 sizeof(FeatureEntry); | |
| 53 | |
| 54 Pickle pickle(src, pickle_size); | |
| 55 PickleIterator pickle_iter(pickle); | |
| 56 | |
| 57 if (!pickle_iter.ReadStringPiece(feature_name)) | |
| 58 return false; | |
| 59 | |
| 60 // Return true because we are not guaranteed to have a trial name anyways. | |
| 61 auto sink = pickle_iter.ReadStringPiece(trial_name); | |
| 62 (void)sink; | |
|
piman
2016/12/02 22:29:26
nit: ALLOW_UNUSED_LOCAL(sink)
lawrencewu
2016/12/02 22:51:48
Done.
| |
| 63 return true; | |
| 64 } | |
| 65 }; | |
| 66 | |
| 30 // Some characters are not allowed to appear in feature names or the associated | 67 // Some characters are not allowed to appear in feature names or the associated |
| 31 // field trial names, as they are used as special characters for command-line | 68 // field trial names, as they are used as special characters for command-line |
| 32 // serialization. This function checks that the strings are ASCII (since they | 69 // serialization. This function checks that the strings are ASCII (since they |
| 33 // are used in command-line API functions that require ASCII) and whether there | 70 // are used in command-line API functions that require ASCII) and whether there |
| 34 // are any reserved characters present, returning true if the string is valid. | 71 // are any reserved characters present, returning true if the string is valid. |
| 35 // Only called in DCHECKs. | 72 // Only called in DCHECKs. |
| 36 bool IsValidFeatureOrFieldTrialName(const std::string& name) { | 73 bool IsValidFeatureOrFieldTrialName(const std::string& name) { |
| 37 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; | 74 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; |
| 38 } | 75 } |
| 39 | 76 |
| 40 } // namespace | 77 } // namespace |
| 41 | 78 |
| 42 FeatureList::FeatureList() {} | 79 FeatureList::FeatureList() {} |
| 43 | 80 |
| 44 FeatureList::~FeatureList() {} | 81 FeatureList::~FeatureList() {} |
| 45 | 82 |
| 46 void FeatureList::InitializeFromCommandLine( | 83 void FeatureList::InitializeFromCommandLine( |
| 47 const std::string& enable_features, | 84 const std::string& enable_features, |
| 48 const std::string& disable_features) { | 85 const std::string& disable_features) { |
| 49 DCHECK(!initialized_); | 86 DCHECK(!initialized_); |
| 50 | 87 |
| 51 // Process disabled features first, so that disabled ones take precedence over | 88 // Process disabled features first, so that disabled ones take precedence over |
| 52 // enabled ones (since RegisterOverride() uses insert()). | 89 // enabled ones (since RegisterOverride() uses insert()). |
| 53 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); | 90 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); |
| 54 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); | 91 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); |
| 55 | 92 |
| 56 initialized_from_command_line_ = true; | 93 initialized_from_command_line_ = true; |
| 57 } | 94 } |
| 58 | 95 |
| 96 void FeatureList::InitializeFromSharedMemory( | |
| 97 PersistentMemoryAllocator* allocator) { | |
| 98 DCHECK(!initialized_); | |
| 99 | |
| 100 PersistentMemoryAllocator::Iterator iter(allocator); | |
| 101 | |
| 102 PersistentMemoryAllocator::Reference ref; | |
| 103 while ((ref = iter.GetNextOfType(kFeatureType)) != | |
| 104 PersistentMemoryAllocator::kReferenceNull) { | |
| 105 const FeatureEntry* entry = | |
| 106 allocator->GetAsObject<const FeatureEntry>(ref, kFeatureType); | |
| 107 | |
| 108 OverrideState override_state = | |
| 109 static_cast<OverrideState>(entry->override_state); | |
| 110 | |
| 111 StringPiece feature_name; | |
| 112 StringPiece trial_name; | |
| 113 if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name)) | |
| 114 continue; | |
| 115 | |
| 116 FieldTrial* trial = FieldTrialList::Find(trial_name.as_string()); | |
| 117 RegisterOverride(feature_name, override_state, trial); | |
| 118 } | |
| 119 } | |
| 120 | |
| 59 bool FeatureList::IsFeatureOverriddenFromCommandLine( | 121 bool FeatureList::IsFeatureOverriddenFromCommandLine( |
| 60 const std::string& feature_name, | 122 const std::string& feature_name, |
| 61 OverrideState state) const { | 123 OverrideState state) const { |
| 62 auto it = overrides_.find(feature_name); | 124 auto it = overrides_.find(feature_name); |
| 63 return it != overrides_.end() && it->second.overridden_state == state && | 125 return it != overrides_.end() && it->second.overridden_state == state && |
| 64 !it->second.overridden_by_field_trial; | 126 !it->second.overridden_by_field_trial; |
| 65 } | 127 } |
| 66 | 128 |
| 67 void FeatureList::AssociateReportingFieldTrial( | 129 void FeatureList::AssociateReportingFieldTrial( |
| 68 const std::string& feature_name, | 130 const std::string& feature_name, |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 91 DCHECK(!ContainsKey(overrides_, feature_name) || | 153 DCHECK(!ContainsKey(overrides_, feature_name) || |
| 92 !overrides_.find(feature_name)->second.field_trial) | 154 !overrides_.find(feature_name)->second.field_trial) |
| 93 << "Feature " << feature_name | 155 << "Feature " << feature_name |
| 94 << " has conflicting field trial overrides: " | 156 << " has conflicting field trial overrides: " |
| 95 << overrides_.find(feature_name)->second.field_trial->trial_name() | 157 << overrides_.find(feature_name)->second.field_trial->trial_name() |
| 96 << " / " << field_trial->trial_name(); | 158 << " / " << field_trial->trial_name(); |
| 97 | 159 |
| 98 RegisterOverride(feature_name, override_state, field_trial); | 160 RegisterOverride(feature_name, override_state, field_trial); |
| 99 } | 161 } |
| 100 | 162 |
| 163 void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) { | |
| 164 DCHECK(initialized_); | |
| 165 | |
| 166 for (const auto& override : overrides_) { | |
| 167 Pickle pickle; | |
| 168 pickle.WriteString(override.first); | |
| 169 if (override.second.field_trial) | |
| 170 pickle.WriteString(override.second.field_trial->trial_name()); | |
| 171 | |
| 172 size_t total_size = sizeof(FeatureEntry) + pickle.size(); | |
| 173 PersistentMemoryAllocator::Reference ref = | |
| 174 allocator->Allocate(total_size, kFeatureType); | |
| 175 if (!ref) | |
| 176 return; | |
| 177 | |
| 178 FeatureEntry* entry = | |
| 179 allocator->GetAsObject<FeatureEntry>(ref, kFeatureType); | |
| 180 entry->override_state = override.second.overridden_state; | |
| 181 entry->pickle_size = pickle.size(); | |
| 182 | |
| 183 char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry); | |
| 184 memcpy(dst, pickle.data(), pickle.size()); | |
| 185 | |
| 186 allocator->MakeIterable(ref); | |
| 187 } | |
| 188 } | |
| 189 | |
| 101 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, | 190 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, |
| 102 std::string* disable_overrides) { | 191 std::string* disable_overrides) { |
| 103 DCHECK(initialized_); | 192 DCHECK(initialized_); |
| 104 | 193 |
| 105 enable_overrides->clear(); | 194 enable_overrides->clear(); |
| 106 disable_overrides->clear(); | 195 disable_overrides->clear(); |
| 107 | 196 |
| 108 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical | 197 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical |
| 109 // order. This not guaranteed to users of this function, but is useful for | 198 // order. This not guaranteed to users of this function, but is useful for |
| 110 // tests to assume the order. | 199 // tests to assume the order. |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 return it->second == &feature; | 396 return it->second == &feature; |
| 308 } | 397 } |
| 309 | 398 |
| 310 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, | 399 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, |
| 311 FieldTrial* field_trial) | 400 FieldTrial* field_trial) |
| 312 : overridden_state(overridden_state), | 401 : overridden_state(overridden_state), |
| 313 field_trial(field_trial), | 402 field_trial(field_trial), |
| 314 overridden_by_field_trial(field_trial != nullptr) {} | 403 overridden_by_field_trial(field_trial != nullptr) {} |
| 315 | 404 |
| 316 } // namespace base | 405 } // namespace base |
| OLD | NEW |