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