blob: 0c5ab654df075d75c17ce0e9cc03103779c1cb5a [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2017 The Chromium Authors
asvitkine79ab08c2017-01-30 23:27:052// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_METRICS_FIELD_TRIAL_PARAMS_H_
6#define BASE_METRICS_FIELD_TRIAL_PARAMS_H_
7
danakje75c6c812024-07-26 20:37:478#include <array>
asvitkine79ab08c2017-01-30 23:27:059#include <map>
10#include <string>
11
12#include "base/base_export.h"
Tom Sepeze6f11a552025-02-18 19:01:5113#include "base/compiler_specific.h"
Anthony Vallee-Dubois9dbbbda32022-08-26 01:25:3114#include "base/feature_list.h"
Keishi Hattori8a7e15d2023-01-19 07:16:2915#include "base/memory/raw_ptr_exclusion.h"
Takashi Toyoshima8a54cdf2024-11-21 02:38:4016#include "base/no_destructor.h"
Hans Wennborgafeb3902020-06-17 14:42:2917#include "base/notreached.h"
Tal Pressman3e91d6fd2020-07-02 04:38:0118#include "base/time/time.h"
asvitkine79ab08c2017-01-30 23:27:0519
20namespace base {
21
Takashi Toyoshima03c3a4d2024-09-06 06:32:0222namespace internal {
23
Takashi Toyoshima5e7b25152024-08-23 04:48:0324BASE_EXPORT bool IsFeatureParamWithCacheEnabled();
Takashi Toyoshima03c3a4d2024-09-06 06:32:0225
26// A traits struct to manage the type for the default value in the following
27// FeatureParam<> template. `std::string` needs to use a string literal instead
28// of `std::string` to realize compile time construction.
29template <typename T>
30struct FeatureParamTraits {
31 using DefaultValueType = T;
Takashi Toyoshima8a54cdf2024-11-21 02:38:4032 using CacheStorageType = T;
Peter Kasting134ef9af2024-12-28 02:30:0933 static CacheStorageType ToCacheStorageType(const T& value) { return value; }
Takashi Toyoshima8a54cdf2024-11-21 02:38:4034 static constexpr T FromCacheStorageType(const CacheStorageType& storage) {
35 return storage;
36 }
Takashi Toyoshima03c3a4d2024-09-06 06:32:0237};
38
39template <>
40struct FeatureParamTraits<std::string> {
41 using DefaultValueType = const char*;
Takashi Toyoshima8a54cdf2024-11-21 02:38:4042 using CacheStorageType = NoDestructor<std::string>;
Peter Kasting134ef9af2024-12-28 02:30:0943 static CacheStorageType ToCacheStorageType(const std::string& value) {
Takashi Toyoshima8a54cdf2024-11-21 02:38:4044 return CacheStorageType(value);
45 }
46 static constexpr std::string FromCacheStorageType(
47 const CacheStorageType& storage) {
48 return *storage;
49 }
Takashi Toyoshima03c3a4d2024-09-06 06:32:0250};
51
52} // namespace internal
Takashi Toyoshima5e7b25152024-08-23 04:48:0353
Miyoung Shinb5ad87f2019-05-13 20:12:4554// Key-value mapping type for field trial parameters.
55typedef std::map<std::string, std::string> FieldTrialParams;
56
Alexei Svitkine8724ea502019-06-14 21:51:4657// Param string decoding function for AssociateFieldTrialParamsFromString().
58typedef std::string (*FieldTrialParamsDecodeStringFunc)(const std::string& str);
59
Peter Kasting18e51f82025-01-14 16:02:3860// Shared declaration for various FeatureParam<T> types.
61//
62// This template is defined for the following types T:
63// bool
64// int
65// size_t
66// double
67// std::string
68// enum types
69// base::TimeDelta
70//
71// Attempting to use it with any other type is a compile error.
72//
73// Getting a param value from a FeatureParam<T> will have the same semantics as
74// GetFieldTrialParamValueByFeature(), see that function's comments for details.
Trevor Perrier3666dee2025-01-16 15:09:0575// `cache_getter` is used to provide a dedicated getter that is used to give a
Peter Kasting18e51f82025-01-14 16:02:3876// local cache to the FeatureParam. Usually, this is automatically generated and
77// provided via BASE_FEATURE_PARAM() or BASE_FEATURE_ENUM_PARAM() macro.
78//
79// Example to declares a double-valued parameter.
80//
81// constexpr FeatureParam<double> kAssistantTriggerThreshold = {
82// &kAssistantFeature, "trigger_threshold", 0.10};
83//
Joe Mason05f944d2025-03-20 14:29:4784// Equivalent using the caching macro (see base/feature_list.h):
85//
86// BASE_FEATURE_PARAM(double, kAssistantTriggerThreshold,
87// &kAssistantFeature, "trigger_threshold", 0.10);
88//
Peter Kasting18e51f82025-01-14 16:02:3889// If the feature is not enabled, the parameter is not set, or set to an invalid
90// value, then Get() will return the default value.
91template <typename T, bool IsEnum = std::is_enum_v<T>>
92struct FeatureParam {
93 using DefaultValueType =
94 typename internal::FeatureParamTraits<T>::DefaultValueType;
95
96 // Prevent use of FeatureParam<> with unsupported types (e.g. void*). Uses T
97 // in its definition so that evaluation is deferred until the template is
98 // instantiated.
99 static_assert(std::is_same_v<bool, T> || std::is_same_v<int, T> ||
100 std::is_same_v<size_t, T> || std::is_same_v<double, T> ||
101 std::is_same_v<std::string, T> ||
102 std::is_same_v<base::TimeDelta, T>,
103 "Unsupported FeatureParam<> type");
104
105 constexpr FeatureParam(const Feature* feature,
106 const char* name,
107 DefaultValueType default_value,
108 T (*cache_getter)(const FeatureParam<T>*) = nullptr)
109 : feature(feature),
110 name(name),
111 default_value(default_value),
112 cache_getter(cache_getter) {}
113
114 // Calling Get() or GetWithoutCache() will activate the field trial associated
115 // with |feature|. See GetFieldTrialParamValueByFeature() for more details.
116 BASE_EXPORT T Get() const {
117 if (internal::IsFeatureParamWithCacheEnabled() && cache_getter) {
118 return cache_getter(this);
119 }
120 return GetWithoutCache();
121 }
122 BASE_EXPORT T GetWithoutCache() const;
123
Jayson Adams6bd00112025-01-15 21:01:41124 // RAW_PTR_EXCLUSION: Using raw_ptr here would make FeatureParam lose its
125 // trivial destructor, causing it to be classified as a non-trivial class
126 // member in contexts where it's included. This can complicate code and
127 // impact performance. base::Features are expected to be globals with
128 // static lifetime, so this is pretty much always safe.
Peter Kasting18e51f82025-01-14 16:02:38129 RAW_PTR_EXCLUSION const Feature* const feature;
130 const char* const name;
131 const DefaultValueType default_value;
132 T (*const cache_getter)(const FeatureParam<T>*);
133};
134
135// Declarations for GetWithoutCache() specializations and explicit
136// instantiations for the FeatureParam<> to ensure instantiating them
137// in the base components to export them.
138template <>
139bool FeatureParam<bool>::GetWithoutCache() const;
140template struct FeatureParam<bool>;
141template struct internal::FeatureParamTraits<bool>;
142
143template <>
144int FeatureParam<int>::GetWithoutCache() const;
145template struct FeatureParam<int>;
146template struct internal::FeatureParamTraits<int>;
147
148template <>
149size_t FeatureParam<size_t>::GetWithoutCache() const;
150template struct FeatureParam<size_t>;
151template struct internal::FeatureParamTraits<size_t>;
152
153template <>
154double FeatureParam<double>::GetWithoutCache() const;
155template struct FeatureParam<double>;
156template struct internal::FeatureParamTraits<double>;
157
158template <>
159std::string FeatureParam<std::string>::GetWithoutCache() const;
160template struct FeatureParam<std::string>;
161
162template <>
163TimeDelta FeatureParam<TimeDelta>::GetWithoutCache() const;
164template struct FeatureParam<TimeDelta>;
165template struct internal::FeatureParamTraits<TimeDelta>;
166
167// Feature param declaration for an enum, with associated options. Example:
168//
169// constexpr FeatureParam<ShapeEnum>::Option kShapeParamOptions[] = {
170// {SHAPE_CIRCLE, "circle"},
171// {SHAPE_CYLINDER, "cylinder"},
172// {SHAPE_PAPERCLIP, "paperclip"}};
173// constexpr FeatureParam<ShapeEnum> kAssistantShapeParam = {
174// &kAssistantFeature, "shape", SHAPE_CIRCLE, &kShapeParamOptions};
175//
176// With this declaration, the parameter may be set to "circle", "cylinder", or
177// "paperclip", and that will be translated to one of the three enum values. By
178// default, or if the param is set to an unknown value, the parameter will be
179// assumed to be SHAPE_CIRCLE.
180template <typename Enum>
181struct FeatureParam<Enum, true> {
182 struct Option {
183 constexpr Option(Enum value, const char* name) : value(value), name(name) {}
184
185 const Enum value;
186 const char* const name;
187 };
188
189 template <size_t option_count>
190 constexpr FeatureParam(
191 const Feature* feature,
192 const char* name,
193 const Enum default_value,
194 const std::array<Option, option_count>& options,
195 Enum (*cache_getter)(const FeatureParam<Enum>*) = nullptr)
196 : feature(feature),
197 name(name),
198 default_value(default_value),
199 options(options.data()),
200 option_count(option_count),
201 cache_getter(cache_getter) {
202 static_assert(option_count >= 1, "FeatureParam<enum> has no options");
203 }
204
205 template <size_t option_count>
206 constexpr FeatureParam(
207 const Feature* feature,
208 const char* name,
209 const Enum default_value,
210 const Option (*options)[option_count],
211 Enum (*cache_getter)(const FeatureParam<Enum>*) = nullptr)
212 : feature(feature),
213 name(name),
214 default_value(default_value),
215 options(*options),
216 option_count(option_count),
217 cache_getter(cache_getter) {
218 static_assert(option_count >= 1, "FeatureParam<enum> has no options");
219 }
220
221 // Calling Get() or GetWithoutCache() will activate the field trial associated
222 // with |feature|. See GetFieldTrialParamValueByFeature() for more details.
223 Enum Get() const {
224 if (internal::IsFeatureParamWithCacheEnabled() && cache_getter) {
225 return cache_getter(this);
226 }
227 return GetWithoutCache();
228 }
229 Enum GetWithoutCache() const {
230 return GetFieldTrialParamByFeatureAsEnum(
Tom Sepeze6f11a552025-02-18 19:01:51231 *feature, name, default_value,
232 UNSAFE_TODO(base::span(&*options, option_count)));
Peter Kasting18e51f82025-01-14 16:02:38233 }
234
235 // Returns the param-string for the given enum value.
236 std::string GetName(Enum value) const {
237 for (size_t i = 0; i < option_count; ++i) {
238 if (value == options[i].value) {
239 return options[i].name;
240 }
241 }
242 NOTREACHED();
243 }
244
245 const raw_ptr<const base::Feature> feature;
246 const char* const name;
247 const Enum default_value;
248 // TODO(crbug.com/40284755): Remove AllowPtrArithmetic if possible after
249 // unsafe buffers have been evaluated.
250 const raw_ptr<const Option, AllowPtrArithmetic> options;
251 const size_t option_count;
252 Enum (*const cache_getter)(const FeatureParam<Enum>*);
253};
254
Weilun Shi1cd8fb92020-07-17 23:31:00255// Unescapes special characters from the given string. Used in
256// AssociateFieldTrialParamsFromString() as one of the feature params decoding
257// functions.
258BASE_EXPORT std::string UnescapeValue(const std::string& value);
259
Peter Kasting18e51f82025-01-14 16:02:38260// Logs a warning that a feature param expected to map to an enum was an unknown
261// non-empty value.
262BASE_EXPORT void LogInvalidEnumValue(const Feature& feature,
263 const std::string& param_name,
264 const std::string& value_as_string,
265 int default_value_as_int);
266
asvitkine79ab08c2017-01-30 23:27:05267// Associates the specified set of key-value |params| with the field trial
268// specified by |trial_name| and |group_name|. Fails and returns false if the
269// specified field trial already has params associated with it or the trial
270// is already active (group() has been called on it). Thread safe.
Miyoung Shinb5ad87f2019-05-13 20:12:45271BASE_EXPORT bool AssociateFieldTrialParams(const std::string& trial_name,
272 const std::string& group_name,
273 const FieldTrialParams& params);
asvitkine79ab08c2017-01-30 23:27:05274
Alexei Svitkine8724ea502019-06-14 21:51:46275// Provides a mechanism to associate multiple set of params to multiple groups
276// with a formatted string as returned by FieldTrialList::AllParamsToString().
277// |decode_data_func| allows specifying a custom decoding function.
278BASE_EXPORT bool AssociateFieldTrialParamsFromString(
279 const std::string& params_string,
280 FieldTrialParamsDecodeStringFunc decode_data_func);
281
asvitkine79ab08c2017-01-30 23:27:05282// Retrieves the set of key-value |params| for the specified field trial, based
283// on its selected group. If the field trial does not exist or its selected
284// group does not have any parameters associated with it, returns false and
285// does not modify |params|. Calling this function will result in the field
286// trial being marked as active if found (i.e. group() will be called on it),
287// if it wasn't already. Thread safe.
Miyoung Shinb5ad87f2019-05-13 20:12:45288BASE_EXPORT bool GetFieldTrialParams(const std::string& trial_name,
289 FieldTrialParams* params);
asvitkine79ab08c2017-01-30 23:27:05290
291// Retrieves the set of key-value |params| for the field trial associated with
292// the specified |feature|. A feature is associated with at most one field
293// trial and selected group. See base/feature_list.h for more information on
294// features. If the feature is not enabled, or if there's no associated params,
295// returns false and does not modify |params|. Calling this function will
296// result in the associated field trial being marked as active if found (i.e.
297// group() will be called on it), if it wasn't already. Thread safe.
Miyoung Shinb5ad87f2019-05-13 20:12:45298BASE_EXPORT bool GetFieldTrialParamsByFeature(const base::Feature& feature,
299 FieldTrialParams* params);
asvitkine79ab08c2017-01-30 23:27:05300
301// Retrieves a specific parameter value corresponding to |param_name| for the
302// specified field trial, based on its selected group. If the field trial does
303// not exist or the specified parameter does not exist, returns an empty
304// string. Calling this function will result in the field trial being marked as
305// active if found (i.e. group() will be called on it), if it wasn't already.
306// Thread safe.
307BASE_EXPORT std::string GetFieldTrialParamValue(const std::string& trial_name,
308 const std::string& param_name);
309
310// Retrieves a specific parameter value corresponding to |param_name| for the
311// field trial associated with the specified |feature|. A feature is associated
312// with at most one field trial and selected group. See base/feature_list.h for
313// more information on features. If the feature is not enabled, or the
314// specified parameter does not exist, returns an empty string. Calling this
315// function will result in the associated field trial being marked as active if
316// found (i.e. group() will be called on it), if it wasn't already. Thread safe.
317BASE_EXPORT std::string GetFieldTrialParamValueByFeature(
318 const base::Feature& feature,
319 const std::string& param_name);
320
Takashi Toyoshima2671988952024-08-26 05:58:34321// Same as GetFieldTrialParamValueByFeature(). But internally relies on
322// GetFieldTrialParamsByFeature to handle empty values in the map, and returns
323// |default_value| only if |param_name| is not found in the map.
324BASE_EXPORT std::string GetFieldTrialParamByFeatureAsString(
325 const base::Feature& feature,
326 const std::string& param_name,
327 const std::string& default_value);
328
asvitkine79ab08c2017-01-30 23:27:05329// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
330// string value into an int using base::StringToInt() and returns it, if
331// successful. Otherwise, it returns |default_value|. If the string value is not
332// empty and the conversion does not succeed, it produces a warning to LOG.
333BASE_EXPORT int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature,
334 const std::string& param_name,
335 int default_value);
336
337// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
338// string value into a double using base::StringToDouble() and returns it, if
339// successful. Otherwise, it returns |default_value|. If the string value is not
340// empty and the conversion does not succeed, it produces a warning to LOG.
341BASE_EXPORT double GetFieldTrialParamByFeatureAsDouble(
342 const base::Feature& feature,
343 const std::string& param_name,
344 double default_value);
345
346// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
347// string value into a boolean and returns it, if successful. Otherwise, it
348// returns |default_value|. The only string representations accepted here are
349// "true" and "false". If the string value is not empty and the conversion does
350// not succeed, it produces a warning to LOG.
351BASE_EXPORT bool GetFieldTrialParamByFeatureAsBool(
352 const base::Feature& feature,
353 const std::string& param_name,
354 bool default_value);
355
Minoru Chikamunefedb8272023-09-27 02:08:24356// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
357// string value into a base::TimeDelta and returns it, if successful. Otherwise,
358// it returns `default_value`. If the string value is not empty and the
359// conversion does not succeed, it produces a warning to LOG.
360BASE_EXPORT base::TimeDelta GetFieldTrialParamByFeatureAsTimeDelta(
361 const Feature& feature,
362 const std::string& param_name,
363 base::TimeDelta default_value);
364
Peter Kasting18e51f82025-01-14 16:02:38365// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
366// string value into an Enum and returns it, if successful. Otherwise, it
367// returns `default_value`. If the string value is not empty and the conversion
368// does not succeed, it produces a warning to LOG.
sfiera01757be62017-09-20 16:36:13369template <typename Enum>
Peter Kasting18e51f82025-01-14 16:02:38370Enum GetFieldTrialParamByFeatureAsEnum(
371 const Feature& feature,
372 const std::string& param_name,
373 Enum default_value,
374 base::span<const typename FeatureParam<Enum, true>::Option> options) {
375 std::string value = GetFieldTrialParamValueByFeature(feature, param_name);
376 if (value.empty()) {
sfiera01757be62017-09-20 16:36:13377 return default_value;
378 }
Peter Kasting18e51f82025-01-14 16:02:38379 for (const auto& option : options) {
380 if (value == option.name) {
381 return option.value;
Fergal Dalybcac47a2020-03-27 01:54:21382 }
Fergal Dalybcac47a2020-03-27 01:54:21383 }
Peter Kasting18e51f82025-01-14 16:02:38384 LogInvalidEnumValue(feature, param_name, value,
385 static_cast<int>(default_value));
386 return default_value;
387}
sfiera01757be62017-09-20 16:36:13388
asvitkine79ab08c2017-01-30 23:27:05389} // namespace base
390
391#endif // BASE_METRICS_FIELD_TRIAL_PARAMS_H_