Home | History | Annotate | Download | only in base
      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.
     49   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.
     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 
     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 
    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 
    121 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 
    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 
    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 
    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 
    187 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
    188                                       std::string* disable_overrides) {
    189   GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
    190 }
    191 
    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
    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
    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
    217 std::vector<base::StringPiece> FeatureList::SplitFeatureListString(
    218     base::StringPiece input) {
    219   return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
    220 }
    221 
    222 // static
    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
    254 FeatureList* FeatureList::GetInstance() {
    255   return g_feature_list_instance;
    256 }
    257 
    258 // static
    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
    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
    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 
    297 void FeatureList::FinalizeInitialization() {
    298   DCHECK(!initialized_);
    299   initialized_ = true;
    300 }
    301 
    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 
    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 
    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 
    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 
    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 
    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 
    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