• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "base/feature_list.h"
6 
7 #include <stddef.h>
8 
9 #include <utility>
10 #include <vector>
11 
12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/pickle.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 
19 namespace base {
20 
21 namespace {
22 
23 // Pointer to the FeatureList instance singleton that was set via
24 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
25 // have more control over initialization timing. Leaky.
26 FeatureList* g_feature_list_instance = nullptr;
27 
28 // Tracks whether the FeatureList instance was initialized via an accessor.
29 bool g_initialized_from_accessor = false;
30 
31 // An allocator entry for a feature in shared memory. The FeatureEntry is
32 // followed by a base::Pickle object that contains the feature and trial name.
33 struct FeatureEntry {
34   // SHA1(FeatureEntry): Increment this if structure changes!
35   static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
36 
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;
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.
GetFeatureAndTrialNamebase::__anon7aaaf6810111::FeatureEntry49   bool GetFeatureAndTrialName(StringPiece* feature_name,
50                               StringPiece* trial_name) const {
51     const char* src =
52         reinterpret_cast<const char*>(this) + 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     ALLOW_UNUSED_LOCAL(sink);
63     return true;
64   }
65 };
66 
67 // Some characters are not allowed to appear in feature names or the associated
68 // field trial names, as they are used as special characters for command-line
69 // serialization. This function checks that the strings are ASCII (since they
70 // are used in command-line API functions that require ASCII) and whether there
71 // are any reserved characters present, returning true if the string is valid.
72 // Only called in DCHECKs.
IsValidFeatureOrFieldTrialName(const std::string & name)73 bool IsValidFeatureOrFieldTrialName(const std::string& name) {
74   return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
75 }
76 
77 }  // namespace
78 
79 #if DCHECK_IS_CONFIGURABLE
80 const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
81                                     base::FEATURE_DISABLED_BY_DEFAULT};
82 #endif  // DCHECK_IS_CONFIGURABLE
83 
84 FeatureList::FeatureList() = default;
85 
86 FeatureList::~FeatureList() = default;
87 
InitializeFromCommandLine(const std::string & enable_features,const std::string & disable_features)88 void FeatureList::InitializeFromCommandLine(
89     const std::string& enable_features,
90     const std::string& disable_features) {
91   DCHECK(!initialized_);
92 
93   // Process disabled features first, so that disabled ones take precedence over
94   // enabled ones (since RegisterOverride() uses insert()).
95   RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
96   RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
97 
98   initialized_from_command_line_ = true;
99 }
100 
InitializeFromSharedMemory(PersistentMemoryAllocator * allocator)101 void FeatureList::InitializeFromSharedMemory(
102     PersistentMemoryAllocator* allocator) {
103   DCHECK(!initialized_);
104 
105   PersistentMemoryAllocator::Iterator iter(allocator);
106   const FeatureEntry* entry;
107   while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
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 
IsFeatureOverriddenFromCommandLine(const std::string & feature_name,OverrideState state) const121 bool FeatureList::IsFeatureOverriddenFromCommandLine(
122     const std::string& feature_name,
123     OverrideState state) const {
124   auto it = overrides_.find(feature_name);
125   return it != overrides_.end() && it->second.overridden_state == state &&
126          !it->second.overridden_by_field_trial;
127 }
128 
AssociateReportingFieldTrial(const std::string & feature_name,OverrideState for_overridden_state,FieldTrial * field_trial)129 void FeatureList::AssociateReportingFieldTrial(
130     const std::string& feature_name,
131     OverrideState for_overridden_state,
132     FieldTrial* field_trial) {
133   DCHECK(
134       IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
135 
136   // Only one associated field trial is supported per feature. This is generally
137   // enforced server-side.
138   OverrideEntry* entry = &overrides_.find(feature_name)->second;
139   if (entry->field_trial) {
140     NOTREACHED() << "Feature " << feature_name
141                  << " already has trial: " << entry->field_trial->trial_name()
142                  << ", associating trial: " << field_trial->trial_name();
143     return;
144   }
145 
146   entry->field_trial = field_trial;
147 }
148 
RegisterFieldTrialOverride(const std::string & feature_name,OverrideState override_state,FieldTrial * field_trial)149 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
150                                              OverrideState override_state,
151                                              FieldTrial* field_trial) {
152   DCHECK(field_trial);
153   DCHECK(!ContainsKey(overrides_, feature_name) ||
154          !overrides_.find(feature_name)->second.field_trial)
155       << "Feature " << feature_name
156       << " has conflicting field trial overrides: "
157       << overrides_.find(feature_name)->second.field_trial->trial_name()
158       << " / " << field_trial->trial_name();
159 
160   RegisterOverride(feature_name, override_state, field_trial);
161 }
162 
AddFeaturesToAllocator(PersistentMemoryAllocator * allocator)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     FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
174     if (!entry)
175       return;
176 
177     entry->override_state = override.second.overridden_state;
178     entry->pickle_size = pickle.size();
179 
180     char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
181     memcpy(dst, pickle.data(), pickle.size());
182 
183     allocator->MakeIterable(entry);
184   }
185 }
186 
GetFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides)187 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
188                                       std::string* disable_overrides) {
189   GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
190 }
191 
GetCommandLineFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides)192 void FeatureList::GetCommandLineFeatureOverrides(
193     std::string* enable_overrides,
194     std::string* disable_overrides) {
195   GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
196 }
197 
198 // static
IsEnabled(const Feature & feature)199 bool FeatureList::IsEnabled(const Feature& feature) {
200   if (!g_feature_list_instance) {
201     g_initialized_from_accessor = true;
202     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
203   }
204   return g_feature_list_instance->IsFeatureEnabled(feature);
205 }
206 
207 // static
GetFieldTrial(const Feature & feature)208 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
209   if (!g_feature_list_instance) {
210     g_initialized_from_accessor = true;
211     return nullptr;
212   }
213   return g_feature_list_instance->GetAssociatedFieldTrial(feature);
214 }
215 
216 // static
SplitFeatureListString(base::StringPiece input)217 std::vector<base::StringPiece> FeatureList::SplitFeatureListString(
218     base::StringPiece input) {
219   return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
220 }
221 
222 // static
InitializeInstance(const std::string & enable_features,const std::string & disable_features)223 bool FeatureList::InitializeInstance(const std::string& enable_features,
224                                      const std::string& disable_features) {
225   // We want to initialize a new instance here to support command-line features
226   // in testing better. For example, we initialize a dummy instance in
227   // base/test/test_suite.cc, and override it in content/browser/
228   // browser_main_loop.cc.
229   // On the other hand, we want to avoid re-initialization from command line.
230   // For example, we initialize an instance in chrome/browser/
231   // chrome_browser_main.cc and do not override it in content/browser/
232   // browser_main_loop.cc.
233   // If the singleton was previously initialized from within an accessor, we
234   // want to prevent callers from reinitializing the singleton and masking the
235   // accessor call(s) which likely returned incorrect information.
236   CHECK(!g_initialized_from_accessor);
237   bool instance_existed_before = false;
238   if (g_feature_list_instance) {
239     if (g_feature_list_instance->initialized_from_command_line_)
240       return false;
241 
242     delete g_feature_list_instance;
243     g_feature_list_instance = nullptr;
244     instance_existed_before = true;
245   }
246 
247   std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
248   feature_list->InitializeFromCommandLine(enable_features, disable_features);
249   base::FeatureList::SetInstance(std::move(feature_list));
250   return !instance_existed_before;
251 }
252 
253 // static
GetInstance()254 FeatureList* FeatureList::GetInstance() {
255   return g_feature_list_instance;
256 }
257 
258 // static
SetInstance(std::unique_ptr<FeatureList> instance)259 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
260   DCHECK(!g_feature_list_instance);
261   instance->FinalizeInitialization();
262 
263   // Note: Intentional leak of global singleton.
264   g_feature_list_instance = instance.release();
265 
266 #if DCHECK_IS_CONFIGURABLE
267   // Update the behaviour of LOG_DCHECK to match the Feature configuration.
268   // DCHECK is also forced to be FATAL if we are running a death-test.
269   // TODO(asvitkine): If we find other use-cases that need integrating here
270   // then define a proper API/hook for the purpose.
271   if (base::FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
272       base::CommandLine::ForCurrentProcess()->HasSwitch(
273           "gtest_internal_run_death_test")) {
274     logging::LOG_DCHECK = logging::LOG_FATAL;
275   } else {
276     logging::LOG_DCHECK = logging::LOG_INFO;
277   }
278 #endif  // DCHECK_IS_CONFIGURABLE
279 }
280 
281 // static
ClearInstanceForTesting()282 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
283   FeatureList* old_instance = g_feature_list_instance;
284   g_feature_list_instance = nullptr;
285   g_initialized_from_accessor = false;
286   return base::WrapUnique(old_instance);
287 }
288 
289 // static
RestoreInstanceForTesting(std::unique_ptr<FeatureList> instance)290 void FeatureList::RestoreInstanceForTesting(
291     std::unique_ptr<FeatureList> instance) {
292   DCHECK(!g_feature_list_instance);
293   // Note: Intentional leak of global singleton.
294   g_feature_list_instance = instance.release();
295 }
296 
FinalizeInitialization()297 void FeatureList::FinalizeInitialization() {
298   DCHECK(!initialized_);
299   initialized_ = true;
300 }
301 
IsFeatureEnabled(const Feature & feature)302 bool FeatureList::IsFeatureEnabled(const Feature& feature) {
303   DCHECK(initialized_);
304   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
305   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
306 
307   auto it = overrides_.find(feature.name);
308   if (it != overrides_.end()) {
309     const OverrideEntry& entry = it->second;
310 
311     // Activate the corresponding field trial, if necessary.
312     if (entry.field_trial)
313       entry.field_trial->group();
314 
315     // TODO(asvitkine) Expand this section as more support is added.
316 
317     // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
318     if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
319       return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
320   }
321   // Otherwise, return the default state.
322   return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
323 }
324 
GetAssociatedFieldTrial(const Feature & feature)325 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
326   DCHECK(initialized_);
327   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
328   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
329 
330   auto it = overrides_.find(feature.name);
331   if (it != overrides_.end()) {
332     const OverrideEntry& entry = it->second;
333     return entry.field_trial;
334   }
335 
336   return nullptr;
337 }
338 
RegisterOverridesFromCommandLine(const std::string & feature_list,OverrideState overridden_state)339 void FeatureList::RegisterOverridesFromCommandLine(
340     const std::string& feature_list,
341     OverrideState overridden_state) {
342   for (const auto& value : SplitFeatureListString(feature_list)) {
343     StringPiece feature_name = value;
344     base::FieldTrial* trial = nullptr;
345 
346     // The entry may be of the form FeatureName<FieldTrialName - in which case,
347     // this splits off the field trial name and associates it with the override.
348     std::string::size_type pos = feature_name.find('<');
349     if (pos != std::string::npos) {
350       feature_name.set(value.data(), pos);
351       trial = base::FieldTrialList::Find(value.substr(pos + 1).as_string());
352     }
353 
354     RegisterOverride(feature_name, overridden_state, trial);
355   }
356 }
357 
RegisterOverride(StringPiece feature_name,OverrideState overridden_state,FieldTrial * field_trial)358 void FeatureList::RegisterOverride(StringPiece feature_name,
359                                    OverrideState overridden_state,
360                                    FieldTrial* field_trial) {
361   DCHECK(!initialized_);
362   if (field_trial) {
363     DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
364         << field_trial->trial_name();
365   }
366   if (feature_name.starts_with("*")) {
367     feature_name = feature_name.substr(1);
368     overridden_state = OVERRIDE_USE_DEFAULT;
369   }
370 
371   // Note: The semantics of insert() is that it does not overwrite the entry if
372   // one already exists for the key. Thus, only the first override for a given
373   // feature name takes effect.
374   overrides_.insert(std::make_pair(
375       feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
376 }
377 
GetFeatureOverridesImpl(std::string * enable_overrides,std::string * disable_overrides,bool command_line_only)378 void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
379                                           std::string* disable_overrides,
380                                           bool command_line_only) {
381   DCHECK(initialized_);
382 
383   enable_overrides->clear();
384   disable_overrides->clear();
385 
386   // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
387   // order. This is not guaranteed to users of this function, but is useful for
388   // tests to assume the order.
389   for (const auto& entry : overrides_) {
390     if (command_line_only &&
391         (entry.second.field_trial != nullptr ||
392          entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
393       continue;
394     }
395 
396     std::string* target_list = nullptr;
397     switch (entry.second.overridden_state) {
398       case OVERRIDE_USE_DEFAULT:
399       case OVERRIDE_ENABLE_FEATURE:
400         target_list = enable_overrides;
401         break;
402       case OVERRIDE_DISABLE_FEATURE:
403         target_list = disable_overrides;
404         break;
405     }
406 
407     if (!target_list->empty())
408       target_list->push_back(',');
409     if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
410       target_list->push_back('*');
411     target_list->append(entry.first);
412     if (entry.second.field_trial) {
413       target_list->push_back('<');
414       target_list->append(entry.second.field_trial->trial_name());
415     }
416   }
417 }
418 
CheckFeatureIdentity(const Feature & feature)419 bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
420   AutoLock auto_lock(feature_identity_tracker_lock_);
421 
422   auto it = feature_identity_tracker_.find(feature.name);
423   if (it == feature_identity_tracker_.end()) {
424     // If it's not tracked yet, register it.
425     feature_identity_tracker_[feature.name] = &feature;
426     return true;
427   }
428   // Compare address of |feature| to the existing tracked entry.
429   return it->second == &feature;
430 }
431 
OverrideEntry(OverrideState overridden_state,FieldTrial * field_trial)432 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
433                                           FieldTrial* field_trial)
434     : overridden_state(overridden_state),
435       field_trial(field_trial),
436       overridden_by_field_trial(field_trial != nullptr) {}
437 
438 }  // namespace base
439