blob: 1ed8579a59b60ae97da1d1051f506a1d5f95bc3e [file] [log] [blame]
asvitkinebccbb862015-09-04 17:17:451// 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
avi9b6f42932015-12-26 22:15:147#include <stddef.h>
8
asvitkine8423d172015-09-28 23:23:449#include <utility>
asvitkinebccbb862015-09-04 17:17:4510#include <vector>
11
12#include "base/logging.h"
asvitkine9499b8d2016-08-09 05:37:0713#include "base/memory/ptr_util.h"
asvitkine8423d172015-09-28 23:23:4414#include "base/metrics/field_trial.h"
lawrencewu5e03cd32016-12-05 16:23:2815#include "base/pickle.h"
asvitkinebccbb862015-09-04 17:17:4516#include "base/strings/string_split.h"
asvitkineb2e44d82015-12-01 04:10:2817#include "base/strings/string_util.h"
asvitkinebccbb862015-09-04 17:17:4518
19namespace base {
20
21namespace {
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.
26FeatureList* g_instance = nullptr;
27
joedow958f0472016-07-07 22:08:5528// Tracks whether the FeatureList instance was initialized via an accessor.
29bool g_initialized_from_accessor = false;
30
lawrencewu5e03cd32016-12-05 16:23:2831// 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.
lawrencewu5e03cd32016-12-05 16:23:2833struct FeatureEntry {
bcwhite3f999d32017-01-11 12:42:1334 // SHA1(FeatureEntry): Increment this if structure changes!
35 static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
36
lawrencewu5e03cd32016-12-05 16:23:2837 // 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
asvitkineb2e44d82015-12-01 04:10:2867// 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.
73bool IsValidFeatureOrFieldTrialName(const std::string& name) {
asvitkine6d31c52e2016-03-22 15:37:5274 return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
asvitkineb2e44d82015-12-01 04:10:2875}
76
asvitkinebccbb862015-09-04 17:17:4577} // namespace
78
Sigurdur Asgeirssonad25d872017-09-20 19:10:2979#if DCHECK_IS_ON() && defined(SYZYASAN)
80const Feature kSyzyAsanDCheckIsFatalFeature{"DcheckIsFatal",
81 base::FEATURE_DISABLED_BY_DEFAULT};
82#endif // defined(SYZYASAN)
83
Chris Watkinsbb7211c2017-11-29 07:16:3884FeatureList::FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:4585
Chris Watkinsbb7211c2017-11-29 07:16:3886FeatureList::~FeatureList() = default;
asvitkinebccbb862015-09-04 17:17:4587
88void 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()).
asvitkineb2e44d82015-12-01 04:10:2895 RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
96 RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
changwan5b9da192016-03-31 07:36:1997
98 initialized_from_command_line_ = true;
asvitkinebccbb862015-09-04 17:17:4599}
100
lawrencewu5e03cd32016-12-05 16:23:28101void FeatureList::InitializeFromSharedMemory(
102 PersistentMemoryAllocator* allocator) {
103 DCHECK(!initialized_);
104
105 PersistentMemoryAllocator::Iterator iter(allocator);
bcwhite3f999d32017-01-11 12:42:13106 const FeatureEntry* entry;
107 while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
lawrencewu5e03cd32016-12-05 16:23:28108 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
asvitkine8423d172015-09-28 23:23:44121bool 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
asvitkine8423d172015-09-28 23:23:44129void 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
asvitkine86340192015-12-01 00:45:29149void 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
lawrencewu5e03cd32016-12-05 16:23:28163void 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();
bcwhitecf6a9e82017-02-09 20:44:23173 FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
bcwhite3f999d32017-01-11 12:42:13174 if (!entry)
lawrencewu5e03cd32016-12-05 16:23:28175 return;
176
lawrencewu5e03cd32016-12-05 16:23:28177 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
bcwhite3f999d32017-01-11 12:42:13183 allocator->MakeIterable(entry);
lawrencewu5e03cd32016-12-05 16:23:28184 }
185}
186
asvitkine86340192015-12-01 00:45:29187void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
188 std::string* disable_overrides) {
Alexei Svitkine223d2282018-02-08 00:18:35189 GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
190}
asvitkine86340192015-12-01 00:45:29191
Alexei Svitkine223d2282018-02-08 00:18:35192void FeatureList::GetCommandLineFeatureOverrides(
193 std::string* enable_overrides,
194 std::string* disable_overrides) {
195 GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
asvitkine86340192015-12-01 00:45:29196}
197
asvitkinebccbb862015-09-04 17:17:45198// static
199bool FeatureList::IsEnabled(const Feature& feature) {
joedow958f0472016-07-07 22:08:55200 if (!g_instance) {
201 g_initialized_from_accessor = true;
202 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
203 }
204 return g_instance->IsFeatureEnabled(feature);
asvitkinebccbb862015-09-04 17:17:45205}
206
207// static
jwd07b90382016-05-06 20:39:42208FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
Alexei Svitkinec49d1f402017-09-18 23:00:41209 if (!g_instance) {
210 g_initialized_from_accessor = true;
211 return nullptr;
212 }
213 return g_instance->GetAssociatedFieldTrial(feature);
jwd07b90382016-05-06 20:39:42214}
215
216// static
mgiuca30f75882017-03-28 02:07:19217std::vector<base::StringPiece> FeatureList::SplitFeatureListString(
218 base::StringPiece input) {
219 return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
asvitkine03007d02015-10-21 22:50:06220}
221
222// static
asvitkinee6be55d2016-04-04 23:29:50223bool FeatureList::InitializeInstance(const std::string& enable_features,
changwan5b9da192016-03-31 07:36:19224 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.
joedow958f0472016-07-07 22:08:55233 // 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);
asvitkinee6be55d2016-04-04 23:29:50237 bool instance_existed_before = false;
changwan5b9da192016-03-31 07:36:19238 if (g_instance) {
239 if (g_instance->initialized_from_command_line_)
asvitkinee6be55d2016-04-04 23:29:50240 return false;
changwan5b9da192016-03-31 07:36:19241
242 delete g_instance;
243 g_instance = nullptr;
asvitkinee6be55d2016-04-04 23:29:50244 instance_existed_before = true;
changwan5b9da192016-03-31 07:36:19245 }
246
dcheng093de9b2016-04-04 21:25:51247 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
changwan5b9da192016-03-31 07:36:19248 feature_list->InitializeFromCommandLine(enable_features, disable_features);
249 base::FeatureList::SetInstance(std::move(feature_list));
asvitkinee6be55d2016-04-04 23:29:50250 return !instance_existed_before;
asvitkine9d96abf2015-11-02 21:52:08251}
252
253// static
asvitkinebccbb862015-09-04 17:17:45254FeatureList* FeatureList::GetInstance() {
255 return g_instance;
256}
257
258// static
dcheng093de9b2016-04-04 21:25:51259void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
asvitkinebccbb862015-09-04 17:17:45260 DCHECK(!g_instance);
261 instance->FinalizeInitialization();
262
263 // Note: Intentional leak of global singleton.
264 g_instance = instance.release();
Wez289477f2017-08-24 20:51:30265
266#if DCHECK_IS_ON() && defined(SYZYASAN)
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.
Wez6656c572017-08-29 22:29:58271 if (base::FeatureList::IsEnabled(kSyzyAsanDCheckIsFatalFeature) ||
Wez289477f2017-08-24 20:51:30272 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_ON() && defined(SYZYASAN)
asvitkinebccbb862015-09-04 17:17:45279}
280
281// static
asvitkine9499b8d2016-08-09 05:37:07282std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
283 FeatureList* old_instance = g_instance;
asvitkinebccbb862015-09-04 17:17:45284 g_instance = nullptr;
joedow958f0472016-07-07 22:08:55285 g_initialized_from_accessor = false;
asvitkine9499b8d2016-08-09 05:37:07286 return base::WrapUnique(old_instance);
287}
288
289// static
290void FeatureList::RestoreInstanceForTesting(
291 std::unique_ptr<FeatureList> instance) {
292 DCHECK(!g_instance);
293 // Note: Intentional leak of global singleton.
294 g_instance = instance.release();
asvitkinebccbb862015-09-04 17:17:45295}
296
297void FeatureList::FinalizeInitialization() {
298 DCHECK(!initialized_);
299 initialized_ = true;
300}
301
302bool FeatureList::IsFeatureEnabled(const Feature& feature) {
303 DCHECK(initialized_);
asvitkineb2e44d82015-12-01 04:10:28304 DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
asvitkinebccbb862015-09-04 17:17:45305 DCHECK(CheckFeatureIdentity(feature)) << feature.name;
306
307 auto it = overrides_.find(feature.name);
308 if (it != overrides_.end()) {
309 const OverrideEntry& entry = it->second;
asvitkine8423d172015-09-28 23:23:44310
311 // Activate the corresponding field trial, if necessary.
312 if (entry.field_trial)
313 entry.field_trial->group();
314
asvitkinebccbb862015-09-04 17:17:45315 // TODO(asvitkine) Expand this section as more support is added.
asvitkine64e9e112016-03-17 17:32:00316
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;
asvitkinebccbb862015-09-04 17:17:45320 }
321 // Otherwise, return the default state.
322 return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
323}
324
jwd07b90382016-05-06 20:39:42325FieldTrial* 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
asvitkineb2e44d82015-12-01 04:10:28339void FeatureList::RegisterOverridesFromCommandLine(
340 const std::string& feature_list,
341 OverrideState overridden_state) {
342 for (const auto& value : SplitFeatureListString(feature_list)) {
mgiuca30f75882017-03-28 02:07:19343 StringPiece feature_name = value;
asvitkineb2e44d82015-12-01 04:10:28344 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);
mgiuca30f75882017-03-28 02:07:19351 trial = base::FieldTrialList::Find(value.substr(pos + 1).as_string());
asvitkineb2e44d82015-12-01 04:10:28352 }
353
354 RegisterOverride(feature_name, overridden_state, trial);
355 }
356}
357
358void FeatureList::RegisterOverride(StringPiece feature_name,
asvitkine8423d172015-09-28 23:23:44359 OverrideState overridden_state,
360 FieldTrial* field_trial) {
asvitkinebccbb862015-09-04 17:17:45361 DCHECK(!initialized_);
asvitkineb2e44d82015-12-01 04:10:28362 if (field_trial) {
363 DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
364 << field_trial->trial_name();
365 }
asvitkine6d31c52e2016-03-22 15:37:52366 if (feature_name.starts_with("*")) {
367 feature_name = feature_name.substr(1);
368 overridden_state = OVERRIDE_USE_DEFAULT;
369 }
asvitkineb2e44d82015-12-01 04:10:28370
asvitkine8423d172015-09-28 23:23:44371 // 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(
asvitkineb2e44d82015-12-01 04:10:28375 feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
asvitkinebccbb862015-09-04 17:17:45376}
377
Alexei Svitkine223d2282018-02-08 00:18:35378void 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
asvitkinebccbb862015-09-04 17:17:45419bool 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
asvitkine8423d172015-09-28 23:23:44432FeatureList::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) {}
asvitkinebccbb862015-09-04 17:17:45437
438} // namespace base