1 // Copyright 2015 The Chromium Authors
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 <string>
8 #include <tuple>
9
10 #include <stddef.h>
11
12 #include "base/base_switches.h"
13 #include "base/debug/crash_logging.h"
14 #include "base/debug/dump_without_crashing.h"
15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/metrics/field_trial.h"
19 #include "base/metrics/field_trial_param_associator.h"
20 #include "base/metrics/field_trial_params.h"
21 #include "base/metrics/persistent_memory_allocator.h"
22 #include "base/no_destructor.h"
23 #include "base/notreached.h"
24 #include "base/pickle.h"
25 #include "base/rand_util.h"
26 #include "base/strings/string_piece.h"
27 #include "base/strings/string_split.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "build/build_config.h"
31
32 namespace base {
33
34 namespace {
35
36 // Pointer to the FeatureList instance singleton that was set via
37 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
38 // have more control over initialization timing. Leaky.
39 FeatureList* g_feature_list_instance = nullptr;
40
41 // Tracks access to Feature state before FeatureList registration.
42 class EarlyFeatureAccessTracker {
43 public:
GetInstance()44 static EarlyFeatureAccessTracker* GetInstance() {
45 static NoDestructor<EarlyFeatureAccessTracker> instance;
46 return instance.get();
47 }
48
49 // Invoked when `feature` is accessed before FeatureList registration.
AccessedFeature(const Feature & feature)50 void AccessedFeature(const Feature& feature) {
51 AutoLock lock(lock_);
52 if (fail_instantly_)
53 Fail(&feature);
54 else if (!feature_)
55 feature_ = &feature;
56 }
57
58 // Asserts that no feature was accessed before FeatureList registration.
AssertNoAccess()59 void AssertNoAccess() {
60 AutoLock lock(lock_);
61 if (feature_)
62 Fail(feature_);
63 }
64
65 // Makes calls to AccessedFeature() fail instantly.
FailOnFeatureAccessWithoutFeatureList()66 void FailOnFeatureAccessWithoutFeatureList() {
67 AutoLock lock(lock_);
68 if (feature_)
69 Fail(feature_);
70 fail_instantly_ = true;
71 }
72
73 // Resets the state of this tracker.
Reset()74 void Reset() {
75 AutoLock lock(lock_);
76 feature_ = nullptr;
77 fail_instantly_ = false;
78 }
79
80 private:
Fail(const Feature * feature)81 void Fail(const Feature* feature) {
82 // TODO(crbug.com/1358639): Enable this check on all platforms.
83 #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
84 #if !BUILDFLAG(IS_NACL)
85 // Create a crash key with the name of the feature accessed too early, to
86 // facilitate crash triage.
87 SCOPED_CRASH_KEY_STRING256("FeatureList", "feature-accessed-too-early",
88 feature->name);
89 #endif // !BUILDFLAG(IS_NACL)
90 // Fail if DCHECKs are enabled.
91 DCHECK(!feature) << "Accessed feature " << feature->name
92 << " before FeatureList registration.";
93 // TODO(crbug.com/1383852): When we believe that all early accesses have
94 // been fixed, remove this base::debug::DumpWithoutCrashing() and change the
95 // above DCHECK to a CHECK.
96 base::debug::DumpWithoutCrashing();
97 #endif // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) &&
98 // !BUILDFLAG(IS_CHROMEOS)
99 }
100
101 friend class NoDestructor<EarlyFeatureAccessTracker>;
102
103 EarlyFeatureAccessTracker() = default;
104 ~EarlyFeatureAccessTracker() = default;
105
106 Lock lock_;
107
108 // First feature to be accessed before FeatureList registration.
109 raw_ptr<const Feature> feature_ GUARDED_BY(lock_) = nullptr;
110
111 // Whether AccessedFeature() should fail instantly.
112 bool fail_instantly_ GUARDED_BY(lock_) = false;
113 };
114
115 #if DCHECK_IS_ON()
116 const char* g_reason_overrides_disallowed = nullptr;
117
DCheckOverridesAllowed()118 void DCheckOverridesAllowed() {
119 const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
120 DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
121 }
122 #else
DCheckOverridesAllowed()123 void DCheckOverridesAllowed() {}
124 #endif
125
126 // An allocator entry for a feature in shared memory. The FeatureEntry is
127 // followed by a base::Pickle object that contains the feature and trial name.
128 struct FeatureEntry {
129 // SHA1(FeatureEntry): Increment this if structure changes!
130 static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 2;
131
132 // Expected size for 32/64-bit check.
133 static constexpr size_t kExpectedInstanceSize = 16;
134
135 // Specifies whether a feature override enables or disables the feature. Same
136 // values as the OverrideState enum in feature_list.h
137 uint32_t override_state;
138
139 // On e.g. x86, alignof(uint64_t) is 4. Ensure consistent size and alignment
140 // of `pickle_size` across platforms.
141 uint32_t padding;
142
143 // Size of the pickled structure, NOT the total size of this entry.
144 uint64_t pickle_size;
145
146 // Reads the feature and trial name from the pickle. Calling this is only
147 // valid on an initialized entry that's in shared memory.
GetFeatureAndTrialNamebase::__anon796c87f50111::FeatureEntry148 bool GetFeatureAndTrialName(StringPiece* feature_name,
149 StringPiece* trial_name) const {
150 const char* src =
151 reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
152
153 Pickle pickle(src, checked_cast<size_t>(pickle_size));
154 PickleIterator pickle_iter(pickle);
155
156 if (!pickle_iter.ReadStringPiece(feature_name))
157 return false;
158
159 // Return true because we are not guaranteed to have a trial name anyways.
160 std::ignore = pickle_iter.ReadStringPiece(trial_name);
161 return true;
162 }
163 };
164
165 // Splits |text| into two parts by the |separator| where the first part will be
166 // returned updated in |first| and the second part will be returned as |second|.
167 // This function returns false if there is more than one |separator| in |first|.
168 // If there is no |separator| presented in |first|, this function will not
169 // modify |first| and |second|. It's used for splitting the |enable_features|
170 // flag into feature name, field trial name and feature parameters.
SplitIntoTwo(StringPiece text,StringPiece separator,StringPiece * first,std::string * second)171 bool SplitIntoTwo(StringPiece text,
172 StringPiece separator,
173 StringPiece* first,
174 std::string* second) {
175 std::vector<StringPiece> parts =
176 SplitStringPiece(text, separator, TRIM_WHITESPACE, SPLIT_WANT_ALL);
177 if (parts.size() == 2) {
178 *second = std::string(parts[1]);
179 } else if (parts.size() > 2) {
180 DLOG(ERROR) << "Only one '" << separator
181 << "' is allowed but got: " << *first;
182 return false;
183 }
184 *first = parts[0];
185 return true;
186 }
187
188 // Checks and parses the |enable_features| flag and sets
189 // |parsed_enable_features| to be a comma-separated list of features,
190 // |force_fieldtrials| to be a comma-separated list of field trials that each
191 // feature want to associate with and |force_fieldtrial_params| to be the field
192 // trial parameters for each field trial.
193 // Returns true if |enable_features| is parsable, otherwise false.
ParseEnableFeatures(const std::string & enable_features,std::string * parsed_enable_features,std::string * force_fieldtrials,std::string * force_fieldtrial_params)194 bool ParseEnableFeatures(const std::string& enable_features,
195 std::string* parsed_enable_features,
196 std::string* force_fieldtrials,
197 std::string* force_fieldtrial_params) {
198 std::vector<std::string> enable_features_list;
199 std::vector<std::string> force_fieldtrials_list;
200 std::vector<std::string> force_fieldtrial_params_list;
201 for (const auto& enable_feature :
202 FeatureList::SplitFeatureListString(enable_features)) {
203 std::string feature_name;
204 std::string study;
205 std::string group;
206 std::string feature_params;
207 if (!FeatureList::ParseEnableFeatureString(
208 enable_feature, &feature_name, &study, &group, &feature_params)) {
209 return false;
210 }
211
212 // If feature params were set but group and study weren't, associate the
213 // feature and its feature params to a synthetic field trial as the
214 // feature params only make sense when it's combined with a field trial.
215 if (!feature_params.empty()) {
216 force_fieldtrials_list.push_back(study + "/" + group);
217 force_fieldtrial_params_list.push_back(study + "." + group + ":" +
218 feature_params);
219 }
220 enable_features_list.push_back(
221 study.empty() ? feature_name : (feature_name + "<" + study));
222 }
223
224 *parsed_enable_features = JoinString(enable_features_list, ",");
225 // Field trial separator is currently a slash. See
226 // |kPersistentStringSeparator| in base/metrics/field_trial.cc.
227 *force_fieldtrials = JoinString(force_fieldtrials_list, "/");
228 *force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
229 return true;
230 }
231
UnpackFeatureCache(uint32_t packed_cache_value)232 std::pair<FeatureList::OverrideState, uint16_t> UnpackFeatureCache(
233 uint32_t packed_cache_value) {
234 return std::make_pair(
235 static_cast<FeatureList::OverrideState>(packed_cache_value >> 24),
236 packed_cache_value & 0xFFFF);
237 }
238
PackFeatureCache(FeatureList::OverrideState override_state,uint32_t caching_context)239 uint32_t PackFeatureCache(FeatureList::OverrideState override_state,
240 uint32_t caching_context) {
241 return (static_cast<uint32_t>(override_state) << 24) |
242 (caching_context & 0xFFFF);
243 }
244
245 } // namespace
246
247 #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
248 BASE_FEATURE(kDCheckIsFatalFeature,
249 "DcheckIsFatal",
250 FEATURE_DISABLED_BY_DEFAULT);
251 #endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
252
253 FeatureList::FeatureList() = default;
254
255 FeatureList::~FeatureList() = default;
256
ScopedDisallowOverrides(const char * reason)257 FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
258 const char* reason)
259 #if DCHECK_IS_ON()
260 : previous_reason_(g_reason_overrides_disallowed) {
261 g_reason_overrides_disallowed = reason;
262 }
263 #else
264 {
265 }
266 #endif
267
~ScopedDisallowOverrides()268 FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
269 #if DCHECK_IS_ON()
270 g_reason_overrides_disallowed = previous_reason_;
271 #endif
272 }
273
InitializeFromCommandLine(const std::string & enable_features,const std::string & disable_features)274 void FeatureList::InitializeFromCommandLine(
275 const std::string& enable_features,
276 const std::string& disable_features) {
277 DCHECK(!initialized_);
278
279 std::string parsed_enable_features;
280 std::string force_fieldtrials;
281 std::string force_fieldtrial_params;
282 bool parse_enable_features_result =
283 ParseEnableFeatures(enable_features, &parsed_enable_features,
284 &force_fieldtrials, &force_fieldtrial_params);
285 DCHECK(parse_enable_features_result) << StringPrintf(
286 "The --%s list is unparsable or invalid, please check the format.",
287 ::switches::kEnableFeatures);
288
289 // Only create field trials when field_trial_list is available. Some tests
290 // don't have field trial list available.
291 if (FieldTrialList::GetInstance()) {
292 bool associate_params_result = AssociateFieldTrialParamsFromString(
293 force_fieldtrial_params, &UnescapeValue);
294 DCHECK(associate_params_result) << StringPrintf(
295 "The field trial parameters part of the --%s list is invalid. Make "
296 "sure "
297 "you %%-encode the following characters in param values: %%:/.,",
298 ::switches::kEnableFeatures);
299
300 bool create_trials_result =
301 FieldTrialList::CreateTrialsFromString(force_fieldtrials);
302 DCHECK(create_trials_result)
303 << StringPrintf("Invalid field trials are specified in --%s.",
304 ::switches::kEnableFeatures);
305 }
306
307 // Process disabled features first, so that disabled ones take precedence over
308 // enabled ones (since RegisterOverride() uses insert()).
309 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
310 RegisterOverridesFromCommandLine(parsed_enable_features,
311 OVERRIDE_ENABLE_FEATURE);
312
313 initialized_from_command_line_ = true;
314 }
315
InitializeFromSharedMemory(PersistentMemoryAllocator * allocator)316 void FeatureList::InitializeFromSharedMemory(
317 PersistentMemoryAllocator* allocator) {
318 DCHECK(!initialized_);
319
320 PersistentMemoryAllocator::Iterator iter(allocator);
321 const FeatureEntry* entry;
322 while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
323 OverrideState override_state =
324 static_cast<OverrideState>(entry->override_state);
325
326 StringPiece feature_name;
327 StringPiece trial_name;
328 if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
329 continue;
330
331 FieldTrial* trial = FieldTrialList::Find(trial_name);
332 RegisterOverride(feature_name, override_state, trial);
333 }
334 }
335
IsFeatureOverridden(const std::string & feature_name) const336 bool FeatureList::IsFeatureOverridden(const std::string& feature_name) const {
337 return overrides_.count(feature_name);
338 }
339
IsFeatureOverriddenFromCommandLine(const std::string & feature_name) const340 bool FeatureList::IsFeatureOverriddenFromCommandLine(
341 const std::string& feature_name) const {
342 auto it = overrides_.find(feature_name);
343 return it != overrides_.end() && !it->second.overridden_by_field_trial;
344 }
345
IsFeatureOverriddenFromCommandLine(const std::string & feature_name,OverrideState state) const346 bool FeatureList::IsFeatureOverriddenFromCommandLine(
347 const std::string& feature_name,
348 OverrideState state) const {
349 auto it = overrides_.find(feature_name);
350 return it != overrides_.end() && !it->second.overridden_by_field_trial &&
351 it->second.overridden_state == state;
352 }
353
AssociateReportingFieldTrial(const std::string & feature_name,OverrideState for_overridden_state,FieldTrial * field_trial)354 void FeatureList::AssociateReportingFieldTrial(
355 const std::string& feature_name,
356 OverrideState for_overridden_state,
357 FieldTrial* field_trial) {
358 DCHECK(
359 IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
360
361 // Only one associated field trial is supported per feature. This is generally
362 // enforced server-side.
363 OverrideEntry* entry = &overrides_.find(feature_name)->second;
364 if (entry->field_trial) {
365 NOTREACHED() << "Feature " << feature_name
366 << " already has trial: " << entry->field_trial->trial_name()
367 << ", associating trial: " << field_trial->trial_name();
368 return;
369 }
370
371 entry->field_trial = field_trial;
372 }
373
RegisterFieldTrialOverride(const std::string & feature_name,OverrideState override_state,FieldTrial * field_trial)374 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
375 OverrideState override_state,
376 FieldTrial* field_trial) {
377 DCHECK(field_trial);
378 DCHECK(!HasAssociatedFieldTrialByFeatureName(feature_name))
379 << "Feature " << feature_name << " is overriden multiple times in these "
380 << "trials: "
381 << overrides_.find(feature_name)->second.field_trial->trial_name()
382 << " and " << field_trial->trial_name() << ". "
383 << "Check the trial (study) in (1) the server config, "
384 << "(2) fieldtrial_testing_config.json, (3) about_flags.cc, and "
385 << "(4) client-side field trials.";
386
387 RegisterOverride(feature_name, override_state, field_trial);
388 }
389
RegisterExtraFeatureOverrides(const std::vector<FeatureOverrideInfo> & extra_overrides)390 void FeatureList::RegisterExtraFeatureOverrides(
391 const std::vector<FeatureOverrideInfo>& extra_overrides) {
392 for (const FeatureOverrideInfo& override_info : extra_overrides) {
393 RegisterOverride(override_info.first.get().name, override_info.second,
394 /* field_trial = */ nullptr);
395 }
396 }
397
AddFeaturesToAllocator(PersistentMemoryAllocator * allocator)398 void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
399 DCHECK(initialized_);
400
401 for (const auto& override : overrides_) {
402 Pickle pickle;
403 pickle.WriteString(override.first);
404 if (override.second.field_trial)
405 pickle.WriteString(override.second.field_trial->trial_name());
406
407 size_t total_size = sizeof(FeatureEntry) + pickle.size();
408 FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
409 if (!entry)
410 return;
411
412 entry->override_state = override.second.overridden_state;
413 entry->pickle_size = pickle.size();
414
415 char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
416 memcpy(dst, pickle.data(), pickle.size());
417
418 allocator->MakeIterable(entry);
419 }
420 }
421
GetFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides,bool include_group_name) const422 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
423 std::string* disable_overrides,
424 bool include_group_name) const {
425 GetFeatureOverridesImpl(enable_overrides, disable_overrides, false,
426 include_group_name);
427 }
428
GetCommandLineFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides) const429 void FeatureList::GetCommandLineFeatureOverrides(
430 std::string* enable_overrides,
431 std::string* disable_overrides) const {
432 GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
433 }
434
435 // static
IsEnabled(const Feature & feature)436 bool FeatureList::IsEnabled(const Feature& feature) {
437 if (!g_feature_list_instance) {
438 EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
439 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
440 }
441 return g_feature_list_instance->IsFeatureEnabled(feature);
442 }
443
444 // static
IsValidFeatureOrFieldTrialName(StringPiece name)445 bool FeatureList::IsValidFeatureOrFieldTrialName(StringPiece name) {
446 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
447 }
448
449 // static
GetStateIfOverridden(const Feature & feature)450 absl::optional<bool> FeatureList::GetStateIfOverridden(const Feature& feature) {
451 if (!g_feature_list_instance) {
452 EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
453 // If there is no feature list, there can be no overrides.
454 return absl::nullopt;
455 }
456 return g_feature_list_instance->IsFeatureEnabledIfOverridden(feature);
457 }
458
459 // static
GetFieldTrial(const Feature & feature)460 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
461 if (!g_feature_list_instance) {
462 EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(feature);
463 return nullptr;
464 }
465 return g_feature_list_instance->GetAssociatedFieldTrial(feature);
466 }
467
468 // static
SplitFeatureListString(StringPiece input)469 std::vector<StringPiece> FeatureList::SplitFeatureListString(
470 StringPiece input) {
471 return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
472 }
473
474 // static
ParseEnableFeatureString(StringPiece enable_feature,std::string * feature_name,std::string * study_name,std::string * group_name,std::string * params)475 bool FeatureList::ParseEnableFeatureString(StringPiece enable_feature,
476 std::string* feature_name,
477 std::string* study_name,
478 std::string* group_name,
479 std::string* params) {
480 StringPiece first;
481 // First, check whether ":" is present. If true, feature parameters were
482 // set for this feature.
483 std::string feature_params;
484 if (!SplitIntoTwo(enable_feature, ":", &first, &feature_params))
485 return false;
486 // Then, check whether "." is present. If true, a group was specified for
487 // this feature.
488 std::string group;
489 if (!SplitIntoTwo(first, ".", &first, &group))
490 return false;
491 // Finally, check whether "<" is present. If true, a study was specified for
492 // this feature.
493 std::string study;
494 if (!SplitIntoTwo(first, "<", &first, &study))
495 return false;
496
497 std::string enable_feature_name(first);
498 // If feature params were set but group and study weren't, associate the
499 // feature and its feature params to a synthetic field trial as the
500 // feature params only make sense when it's combined with a field trial.
501 if (!feature_params.empty()) {
502 study = study.empty() ? "Study" + enable_feature_name : study;
503 group = group.empty() ? "Group" + enable_feature_name : group;
504 }
505
506 feature_name->swap(enable_feature_name);
507 study_name->swap(study);
508 group_name->swap(group);
509 params->swap(feature_params);
510 return true;
511 }
512
513 // static
InitializeInstance(const std::string & enable_features,const std::string & disable_features)514 bool FeatureList::InitializeInstance(const std::string& enable_features,
515 const std::string& disable_features) {
516 return InitializeInstance(enable_features, disable_features,
517 std::vector<FeatureOverrideInfo>());
518 }
519
520 // static
InitializeInstance(const std::string & enable_features,const std::string & disable_features,const std::vector<FeatureOverrideInfo> & extra_overrides)521 bool FeatureList::InitializeInstance(
522 const std::string& enable_features,
523 const std::string& disable_features,
524 const std::vector<FeatureOverrideInfo>& extra_overrides) {
525 // We want to initialize a new instance here to support command-line features
526 // in testing better. For example, we initialize a dummy instance in
527 // base/test/test_suite.cc, and override it in content/browser/
528 // browser_main_loop.cc.
529 // On the other hand, we want to avoid re-initialization from command line.
530 // For example, we initialize an instance in chrome/browser/
531 // chrome_browser_main.cc and do not override it in content/browser/
532 // browser_main_loop.cc.
533 // If the singleton was previously initialized from within an accessor, we
534 // want to prevent callers from reinitializing the singleton and masking the
535 // accessor call(s) which likely returned incorrect information.
536 EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
537 bool instance_existed_before = false;
538 if (g_feature_list_instance) {
539 if (g_feature_list_instance->initialized_from_command_line_)
540 return false;
541
542 delete g_feature_list_instance;
543 g_feature_list_instance = nullptr;
544 instance_existed_before = true;
545 }
546
547 std::unique_ptr<FeatureList> feature_list(new FeatureList);
548 feature_list->InitializeFromCommandLine(enable_features, disable_features);
549 feature_list->RegisterExtraFeatureOverrides(extra_overrides);
550 FeatureList::SetInstance(std::move(feature_list));
551 return !instance_existed_before;
552 }
553
554 // static
GetInstance()555 FeatureList* FeatureList::GetInstance() {
556 return g_feature_list_instance;
557 }
558
559 // static
SetInstance(std::unique_ptr<FeatureList> instance)560 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
561 DCHECK(!g_feature_list_instance);
562 instance->FinalizeInitialization();
563
564 // Note: Intentional leak of global singleton.
565 g_feature_list_instance = instance.release();
566
567 EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
568
569 #if !BUILDFLAG(IS_NACL)
570 // Configured first because it takes precedence over the getrandom() trial.
571 internal::ConfigureBoringSSLBackedRandBytesFieldTrial();
572 #endif
573
574 #if BUILDFLAG(IS_ANDROID)
575 internal::ConfigureRandBytesFieldTrial();
576 #endif
577
578 #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
579 // Update the behaviour of LOGGING_DCHECK to match the Feature configuration.
580 // DCHECK is also forced to be FATAL if we are running a death-test.
581 // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
582 // currently run through this codepath, mitigated in
583 // base::TestSuite::Initialize() for now.
584 // TODO(asvitkine): If we find other use-cases that need integrating here
585 // then define a proper API/hook for the purpose.
586 if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
587 CommandLine::ForCurrentProcess()->HasSwitch(
588 "gtest_internal_run_death_test")) {
589 logging::LOGGING_DCHECK = logging::LOG_FATAL;
590 } else {
591 logging::LOGGING_DCHECK = logging::LOG_INFO;
592 }
593 #endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
594 }
595
596 // static
ClearInstanceForTesting()597 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
598 FeatureList* old_instance = g_feature_list_instance;
599 g_feature_list_instance = nullptr;
600 EarlyFeatureAccessTracker::GetInstance()->Reset();
601 return WrapUnique(old_instance);
602 }
603
604 // static
RestoreInstanceForTesting(std::unique_ptr<FeatureList> instance)605 void FeatureList::RestoreInstanceForTesting(
606 std::unique_ptr<FeatureList> instance) {
607 DCHECK(!g_feature_list_instance);
608 // Note: Intentional leak of global singleton.
609 g_feature_list_instance = instance.release();
610 }
611
612 // static
FailOnFeatureAccessWithoutFeatureList()613 void FeatureList::FailOnFeatureAccessWithoutFeatureList() {
614 EarlyFeatureAccessTracker::GetInstance()
615 ->FailOnFeatureAccessWithoutFeatureList();
616 }
617
SetCachingContextForTesting(uint16_t caching_context)618 void FeatureList::SetCachingContextForTesting(uint16_t caching_context) {
619 caching_context_ = caching_context;
620 }
621
FinalizeInitialization()622 void FeatureList::FinalizeInitialization() {
623 DCHECK(!initialized_);
624 // Store the field trial list pointer for DCHECKing.
625 field_trial_list_ = FieldTrialList::GetInstance();
626 initialized_ = true;
627 }
628
IsFeatureEnabled(const Feature & feature) const629 bool FeatureList::IsFeatureEnabled(const Feature& feature) const {
630 OverrideState overridden_state = GetOverrideState(feature);
631
632 // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
633 if (overridden_state != OVERRIDE_USE_DEFAULT)
634 return overridden_state == OVERRIDE_ENABLE_FEATURE;
635
636 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
637 }
638
IsFeatureEnabledIfOverridden(const Feature & feature) const639 absl::optional<bool> FeatureList::IsFeatureEnabledIfOverridden(
640 const Feature& feature) const {
641 OverrideState overridden_state = GetOverrideState(feature);
642
643 // If marked as OVERRIDE_USE_DEFAULT, fall through to returning empty.
644 if (overridden_state != OVERRIDE_USE_DEFAULT)
645 return overridden_state == OVERRIDE_ENABLE_FEATURE;
646
647 return absl::nullopt;
648 }
649
GetOverrideState(const Feature & feature) const650 FeatureList::OverrideState FeatureList::GetOverrideState(
651 const Feature& feature) const {
652 DCHECK(initialized_);
653 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
654 DCHECK(CheckFeatureIdentity(feature))
655 << feature.name
656 << " has multiple definitions. Either it is defined more than once in "
657 "code or (for component builds) the code is built into multiple "
658 "components (shared libraries) without a corresponding export "
659 "statement";
660
661 uint32_t current_cache_value =
662 feature.cached_value.load(std::memory_order_relaxed);
663
664 auto unpacked = UnpackFeatureCache(current_cache_value);
665
666 if (unpacked.second == caching_context_)
667 return unpacked.first;
668
669 OverrideState state = GetOverrideStateByFeatureName(feature.name);
670 uint32_t new_cache_value = PackFeatureCache(state, caching_context_);
671
672 // Update the cache with the new value.
673 // In non-test code, this value can be in one of 2 states: either it's unset,
674 // or another thread has updated it to the same value we're about to write.
675 // Because of this, a plain `store` yields the correct result in all cases.
676 // In test code, it's possible for a different thread to have installed a new
677 // `ScopedFeatureList` and written a value that's different than the one we're
678 // about to write, although that would be a thread safety violation already
679 // and such tests should be fixed.
680 feature.cached_value.store(new_cache_value, std::memory_order_relaxed);
681
682 return state;
683 }
684
GetOverrideStateByFeatureName(StringPiece feature_name) const685 FeatureList::OverrideState FeatureList::GetOverrideStateByFeatureName(
686 StringPiece feature_name) const {
687 DCHECK(initialized_);
688 DCHECK(IsValidFeatureOrFieldTrialName(feature_name)) << feature_name;
689
690 auto it = overrides_.find(feature_name);
691 if (it != overrides_.end()) {
692 const OverrideEntry& entry = it->second;
693
694 // Activate the corresponding field trial, if necessary.
695 if (entry.field_trial)
696 entry.field_trial->Activate();
697
698 // TODO(asvitkine) Expand this section as more support is added.
699
700 return entry.overridden_state;
701 }
702 // Otherwise, report that we want to use the default state.
703 return OVERRIDE_USE_DEFAULT;
704 }
705
GetAssociatedFieldTrial(const Feature & feature) const706 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) const {
707 DCHECK(initialized_);
708 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
709
710 return GetAssociatedFieldTrialByFeatureName(feature.name);
711 }
712
713 const base::FeatureList::OverrideEntry*
GetOverrideEntryByFeatureName(StringPiece name) const714 FeatureList::GetOverrideEntryByFeatureName(StringPiece name) const {
715 DCHECK(initialized_);
716 DCHECK(IsValidFeatureOrFieldTrialName(name)) << name;
717
718 auto it = overrides_.find(name);
719 if (it != overrides_.end()) {
720 const OverrideEntry& entry = it->second;
721 return &entry;
722 }
723 return nullptr;
724 }
725
GetAssociatedFieldTrialByFeatureName(StringPiece name) const726 FieldTrial* FeatureList::GetAssociatedFieldTrialByFeatureName(
727 StringPiece name) const {
728 DCHECK(initialized_);
729
730 const base::FeatureList::OverrideEntry* entry =
731 GetOverrideEntryByFeatureName(name);
732 if (entry) {
733 return entry->field_trial;
734 }
735 return nullptr;
736 }
737
HasAssociatedFieldTrialByFeatureName(StringPiece name) const738 bool FeatureList::HasAssociatedFieldTrialByFeatureName(StringPiece name) const {
739 DCHECK(!initialized_);
740 auto entry = overrides_.find(name);
741 return entry != overrides_.end() && entry->second.field_trial != nullptr;
742 }
743
GetEnabledFieldTrialByFeatureName(StringPiece name) const744 FieldTrial* FeatureList::GetEnabledFieldTrialByFeatureName(
745 StringPiece name) const {
746 DCHECK(initialized_);
747
748 const base::FeatureList::OverrideEntry* entry =
749 GetOverrideEntryByFeatureName(name);
750 if (entry &&
751 entry->overridden_state == base::FeatureList::OVERRIDE_ENABLE_FEATURE) {
752 return entry->field_trial;
753 }
754 return nullptr;
755 }
756
ConstructAccessor()757 std::unique_ptr<FeatureList::Accessor> FeatureList::ConstructAccessor() {
758 if (initialized_) {
759 // This function shouldn't be called after initialization.
760 NOTREACHED();
761 return nullptr;
762 }
763 // Use new and WrapUnique because we want to restrict access to the Accessor's
764 // constructor.
765 return base::WrapUnique(new Accessor(this));
766 }
767
RegisterOverridesFromCommandLine(const std::string & feature_list,OverrideState overridden_state)768 void FeatureList::RegisterOverridesFromCommandLine(
769 const std::string& feature_list,
770 OverrideState overridden_state) {
771 for (const auto& value : SplitFeatureListString(feature_list)) {
772 StringPiece feature_name = value;
773 FieldTrial* trial = nullptr;
774
775 // The entry may be of the form FeatureName<FieldTrialName - in which case,
776 // this splits off the field trial name and associates it with the override.
777 std::string::size_type pos = feature_name.find('<');
778 if (pos != std::string::npos) {
779 feature_name = StringPiece(value.data(), pos);
780 trial = FieldTrialList::Find(value.substr(pos + 1));
781 #if !BUILDFLAG(IS_NACL)
782 // If the below DCHECK fires, it means a non-existent trial name was
783 // specified via the "Feature<Trial" command-line syntax.
784 DCHECK(trial) << "trial='" << value.substr(pos + 1) << "' does not exist";
785 #endif // !BUILDFLAG(IS_NACL)
786 }
787
788 RegisterOverride(feature_name, overridden_state, trial);
789 }
790 }
791
RegisterOverride(StringPiece feature_name,OverrideState overridden_state,FieldTrial * field_trial)792 void FeatureList::RegisterOverride(StringPiece feature_name,
793 OverrideState overridden_state,
794 FieldTrial* field_trial) {
795 DCHECK(!initialized_);
796 DCheckOverridesAllowed();
797 if (field_trial) {
798 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
799 << field_trial->trial_name();
800 }
801 if (StartsWith(feature_name, "*")) {
802 feature_name = feature_name.substr(1);
803 overridden_state = OVERRIDE_USE_DEFAULT;
804 }
805
806 // Note: The semantics of emplace() is that it does not overwrite the entry if
807 // one already exists for the key. Thus, only the first override for a given
808 // feature name takes effect.
809 overrides_.emplace(std::string(feature_name),
810 OverrideEntry(overridden_state, field_trial));
811 }
812
GetFeatureOverridesImpl(std::string * enable_overrides,std::string * disable_overrides,bool command_line_only,bool include_group_name) const813 void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
814 std::string* disable_overrides,
815 bool command_line_only,
816 bool include_group_name) const {
817 DCHECK(initialized_);
818
819 // Check that the FieldTrialList this is associated with, if any, is the
820 // active one. If not, it likely indicates that this FeatureList has override
821 // entries from a freed FieldTrial, which may be caused by an incorrect test
822 // set up.
823 if (field_trial_list_)
824 DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
825
826 enable_overrides->clear();
827 disable_overrides->clear();
828
829 // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
830 // order. This is not guaranteed to users of this function, but is useful for
831 // tests to assume the order.
832 for (const auto& entry : overrides_) {
833 if (command_line_only &&
834 (entry.second.field_trial != nullptr ||
835 entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
836 continue;
837 }
838
839 std::string* target_list = nullptr;
840 switch (entry.second.overridden_state) {
841 case OVERRIDE_USE_DEFAULT:
842 case OVERRIDE_ENABLE_FEATURE:
843 target_list = enable_overrides;
844 break;
845 case OVERRIDE_DISABLE_FEATURE:
846 target_list = disable_overrides;
847 break;
848 }
849
850 if (!target_list->empty())
851 target_list->push_back(',');
852 if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
853 target_list->push_back('*');
854 target_list->append(entry.first);
855 if (entry.second.field_trial) {
856 auto* const field_trial = entry.second.field_trial.get();
857 target_list->push_back('<');
858 target_list->append(field_trial->trial_name());
859 if (include_group_name) {
860 target_list->push_back('.');
861 target_list->append(field_trial->GetGroupNameWithoutActivation());
862 }
863 }
864 }
865 }
866
CheckFeatureIdentity(const Feature & feature) const867 bool FeatureList::CheckFeatureIdentity(const Feature& feature) const {
868 AutoLock auto_lock(feature_identity_tracker_lock_);
869
870 auto it = feature_identity_tracker_.find(feature.name);
871 if (it == feature_identity_tracker_.end()) {
872 // If it's not tracked yet, register it.
873 feature_identity_tracker_[feature.name] = &feature;
874 return true;
875 }
876 // Compare address of |feature| to the existing tracked entry.
877 return it->second == &feature;
878 }
879
OverrideEntry(OverrideState overridden_state,FieldTrial * field_trial)880 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
881 FieldTrial* field_trial)
882 : overridden_state(overridden_state),
883 field_trial(field_trial),
884 overridden_by_field_trial(field_trial != nullptr) {}
885
Accessor(FeatureList * feature_list)886 FeatureList::Accessor::Accessor(FeatureList* feature_list)
887 : feature_list_(feature_list) {}
888
GetOverrideStateByFeatureName(StringPiece feature_name)889 FeatureList::OverrideState FeatureList::Accessor::GetOverrideStateByFeatureName(
890 StringPiece feature_name) {
891 return feature_list_->GetOverrideStateByFeatureName(feature_name);
892 }
893
GetParamsByFeatureName(StringPiece feature_name,std::map<std::string,std::string> * params)894 bool FeatureList::Accessor::GetParamsByFeatureName(
895 StringPiece feature_name,
896 std::map<std::string, std::string>* params) {
897 base::FieldTrial* trial =
898 feature_list_->GetAssociatedFieldTrialByFeatureName(feature_name);
899 return FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial,
900 params);
901 }
902
903 } // namespace base
904