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