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 future. Same | |
Alexei Svitkine (slow)
2016/12/01 20:05:52
future -> feature
bcwhite
2016/12/01 20:15:14
future => feature
lawrencewu
2016/12/02 19:24:46
Done.
| |
41 // values as the OverrideState enum in feature_list.h | |
42 uint32_t override_state; | |
43 | |
44 // Size of the pickled structure, NOT the total size of this entry. | |
Alexei Svitkine (slow)
2016/12/01 20:05:52
Nit: Why not name it pickle_size then?
lawrencewu
2016/12/02 19:24:46
Done.
| |
45 uint32_t size; | |
46 | |
47 bool GetFeatureAndTrialName(StringPiece* feature_name, | |
Alexei Svitkine (slow)
2016/12/01 20:05:52
Add a comment and document that it's only valid to
lawrencewu
2016/12/02 19:24:46
Done.
| |
48 StringPiece* trial_name) const { | |
49 char* src = reinterpret_cast<char*>(const_cast<FeatureEntry*>(this)) + | |
50 sizeof(FeatureEntry); | |
51 | |
52 Pickle pickle(src, size); | |
53 PickleIterator pickle_iter(pickle); | |
54 | |
55 if (!pickle_iter.ReadStringPiece(feature_name)) | |
56 return false; | |
57 | |
58 // Return true because we are not guaranteed to have a trial name anyways. | |
59 if (!pickle_iter.ReadStringPiece(trial_name)) | |
bcwhite
2016/12/01 20:15:15
Just remove the if() and keep the comment as to wh
lawrencewu
2016/12/02 19:24:46
The function has warn_unused_result so I think I h
bcwhite
2016/12/02 19:34:57
// Need to return true because... so just ignore t
| |
60 return true; | |
Alexei Svitkine (slow)
2016/12/01 20:05:52
Seems you can just simplify this to not have the i
lawrencewu
2016/12/02 19:24:46
The function has warn_unused_result so I think I h
| |
61 return true; | |
62 } | |
63 }; | |
64 | |
30 // Some characters are not allowed to appear in feature names or the associated | 65 // 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 | 66 // 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 | 67 // 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 | 68 // 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. | 69 // are any reserved characters present, returning true if the string is valid. |
35 // Only called in DCHECKs. | 70 // Only called in DCHECKs. |
36 bool IsValidFeatureOrFieldTrialName(const std::string& name) { | 71 bool IsValidFeatureOrFieldTrialName(const std::string& name) { |
37 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; | 72 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; |
38 } | 73 } |
39 | 74 |
40 } // namespace | 75 } // namespace |
41 | 76 |
42 FeatureList::FeatureList() {} | 77 FeatureList::FeatureList() {} |
43 | 78 |
44 FeatureList::~FeatureList() {} | 79 FeatureList::~FeatureList() {} |
45 | 80 |
46 void FeatureList::InitializeFromCommandLine( | 81 void FeatureList::InitializeFromCommandLine( |
47 const std::string& enable_features, | 82 const std::string& enable_features, |
48 const std::string& disable_features) { | 83 const std::string& disable_features) { |
49 DCHECK(!initialized_); | 84 DCHECK(!initialized_); |
50 | 85 |
51 // Process disabled features first, so that disabled ones take precedence over | 86 // Process disabled features first, so that disabled ones take precedence over |
52 // enabled ones (since RegisterOverride() uses insert()). | 87 // enabled ones (since RegisterOverride() uses insert()). |
53 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); | 88 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); |
54 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); | 89 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); |
55 | 90 |
56 initialized_from_command_line_ = true; | 91 initialized_from_command_line_ = true; |
57 } | 92 } |
58 | 93 |
94 void FeatureList::InitializeFromSharedMemory( | |
95 SharedPersistentMemoryAllocator* allocator) { | |
96 DCHECK(!initialized_); | |
97 | |
98 SharedPersistentMemoryAllocator::Iterator iter(allocator); | |
99 | |
100 SharedPersistentMemoryAllocator::Reference ref; | |
101 while ((ref = iter.GetNextOfType(kFeatureType)) != | |
102 SharedPersistentMemoryAllocator::kReferenceNull) { | |
103 const FeatureEntry* entry = | |
104 allocator->GetAsObject<const FeatureEntry>(ref, kFeatureType); | |
105 | |
106 OverrideState override_state = | |
107 static_cast<OverrideState>(entry->override_state); | |
108 | |
109 StringPiece feature_name; | |
110 StringPiece trial_name; | |
111 entry->GetFeatureAndTrialName(&feature_name, &trial_name); | |
Alexei Svitkine (slow)
2016/12/01 20:05:52
Check return value?
lawrencewu
2016/12/02 19:24:46
Not sure if we should return or continue on failur
| |
112 | |
113 FieldTrial* trial = FieldTrialList::Find(trial_name.as_string()); | |
114 RegisterOverride(feature_name, override_state, trial); | |
115 } | |
116 } | |
117 | |
59 bool FeatureList::IsFeatureOverriddenFromCommandLine( | 118 bool FeatureList::IsFeatureOverriddenFromCommandLine( |
60 const std::string& feature_name, | 119 const std::string& feature_name, |
61 OverrideState state) const { | 120 OverrideState state) const { |
62 auto it = overrides_.find(feature_name); | 121 auto it = overrides_.find(feature_name); |
63 return it != overrides_.end() && it->second.overridden_state == state && | 122 return it != overrides_.end() && it->second.overridden_state == state && |
64 !it->second.overridden_by_field_trial; | 123 !it->second.overridden_by_field_trial; |
65 } | 124 } |
66 | 125 |
67 void FeatureList::AssociateReportingFieldTrial( | 126 void FeatureList::AssociateReportingFieldTrial( |
68 const std::string& feature_name, | 127 const std::string& feature_name, |
(...skipping 22 matching lines...) Expand all Loading... | |
91 DCHECK(!ContainsKey(overrides_, feature_name) || | 150 DCHECK(!ContainsKey(overrides_, feature_name) || |
92 !overrides_.find(feature_name)->second.field_trial) | 151 !overrides_.find(feature_name)->second.field_trial) |
93 << "Feature " << feature_name | 152 << "Feature " << feature_name |
94 << " has conflicting field trial overrides: " | 153 << " has conflicting field trial overrides: " |
95 << overrides_.find(feature_name)->second.field_trial->trial_name() | 154 << overrides_.find(feature_name)->second.field_trial->trial_name() |
96 << " / " << field_trial->trial_name(); | 155 << " / " << field_trial->trial_name(); |
97 | 156 |
98 RegisterOverride(feature_name, override_state, field_trial); | 157 RegisterOverride(feature_name, override_state, field_trial); |
99 } | 158 } |
100 | 159 |
160 void FeatureList::AddFeaturesToAllocator( | |
161 FieldTrialList::FieldTrialAllocator* allocator) { | |
162 DCHECK(initialized_); | |
163 | |
164 for (const auto& override : overrides_) { | |
165 Pickle pickle; | |
166 pickle.WriteString(override.first); | |
167 if (override.second.field_trial) | |
168 pickle.WriteString(override.second.field_trial->trial_name()); | |
169 | |
170 size_t total_size = sizeof(FeatureEntry) + pickle.size(); | |
171 SharedPersistentMemoryAllocator::Reference ref = | |
172 allocator->Allocate(total_size, kFeatureType); | |
173 if (!ref) | |
174 return; | |
Alexei Svitkine (slow)
2016/12/01 20:05:52
Seems like a bad thing to silently fail on.
lawrencewu
2016/12/02 19:24:46
We do the same thing in field_trial.cc. Maybe Term
| |
175 | |
176 FeatureEntry* entry = | |
177 allocator->GetAsObject<FeatureEntry>(ref, kFeatureType); | |
178 entry->override_state = override.second.overridden_state; | |
179 entry->size = pickle.size(); | |
180 | |
181 char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry); | |
182 memcpy(dst, pickle.data(), pickle.size()); | |
183 | |
184 allocator->MakeIterable(ref); | |
185 } | |
186 } | |
187 | |
101 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, | 188 void FeatureList::GetFeatureOverrides(std::string* enable_overrides, |
102 std::string* disable_overrides) { | 189 std::string* disable_overrides) { |
103 DCHECK(initialized_); | 190 DCHECK(initialized_); |
104 | 191 |
105 enable_overrides->clear(); | 192 enable_overrides->clear(); |
106 disable_overrides->clear(); | 193 disable_overrides->clear(); |
107 | 194 |
108 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical | 195 // 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 | 196 // order. This not guaranteed to users of this function, but is useful for |
110 // tests to assume the order. | 197 // tests to assume the order. |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
307 return it->second == &feature; | 394 return it->second == &feature; |
308 } | 395 } |
309 | 396 |
310 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, | 397 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, |
311 FieldTrial* field_trial) | 398 FieldTrial* field_trial) |
312 : overridden_state(overridden_state), | 399 : overridden_state(overridden_state), |
313 field_trial(field_trial), | 400 field_trial(field_trial), |
314 overridden_by_field_trial(field_trial != nullptr) {} | 401 overridden_by_field_trial(field_trial != nullptr) {} |
315 | 402 |
316 } // namespace base | 403 } // namespace base |
OLD | NEW |