1 // Copyright 2017 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 #ifndef BASE_METRICS_FIELD_TRIAL_PARAMS_H_ 6 #define BASE_METRICS_FIELD_TRIAL_PARAMS_H_ 7 8 #include <map> 9 #include <string> 10 11 #include "base/base_export.h" 12 #include "base/feature_list.h" 13 #include "base/logging.h" 14 #include "base/memory/raw_ptr_exclusion.h" 15 #include "base/notreached.h" 16 #include "base/time/time.h" 17 18 namespace base { 19 20 // Key-value mapping type for field trial parameters. 21 typedef std::map<std::string, std::string> FieldTrialParams; 22 23 // Param string decoding function for AssociateFieldTrialParamsFromString(). 24 typedef std::string (*FieldTrialParamsDecodeStringFunc)(const std::string& str); 25 26 // Unescapes special characters from the given string. Used in 27 // AssociateFieldTrialParamsFromString() as one of the feature params decoding 28 // functions. 29 BASE_EXPORT std::string UnescapeValue(const std::string& value); 30 31 // Associates the specified set of key-value |params| with the field trial 32 // specified by |trial_name| and |group_name|. Fails and returns false if the 33 // specified field trial already has params associated with it or the trial 34 // is already active (group() has been called on it). Thread safe. 35 BASE_EXPORT bool AssociateFieldTrialParams(const std::string& trial_name, 36 const std::string& group_name, 37 const FieldTrialParams& params); 38 39 // Provides a mechanism to associate multiple set of params to multiple groups 40 // with a formatted string as returned by FieldTrialList::AllParamsToString(). 41 // |decode_data_func| allows specifying a custom decoding function. 42 BASE_EXPORT bool AssociateFieldTrialParamsFromString( 43 const std::string& params_string, 44 FieldTrialParamsDecodeStringFunc decode_data_func); 45 46 // Retrieves the set of key-value |params| for the specified field trial, based 47 // on its selected group. If the field trial does not exist or its selected 48 // group does not have any parameters associated with it, returns false and 49 // does not modify |params|. Calling this function will result in the field 50 // trial being marked as active if found (i.e. group() will be called on it), 51 // if it wasn't already. Thread safe. 52 BASE_EXPORT bool GetFieldTrialParams(const std::string& trial_name, 53 FieldTrialParams* params); 54 55 // Retrieves the set of key-value |params| for the field trial associated with 56 // the specified |feature|. A feature is associated with at most one field 57 // trial and selected group. See base/feature_list.h for more information on 58 // features. If the feature is not enabled, or if there's no associated params, 59 // returns false and does not modify |params|. Calling this function will 60 // result in the associated field trial being marked as active if found (i.e. 61 // group() will be called on it), if it wasn't already. Thread safe. 62 BASE_EXPORT bool GetFieldTrialParamsByFeature(const base::Feature& feature, 63 FieldTrialParams* params); 64 65 // Retrieves a specific parameter value corresponding to |param_name| for the 66 // specified field trial, based on its selected group. If the field trial does 67 // not exist or the specified parameter does not exist, returns an empty 68 // string. Calling this function will result in the field trial being marked as 69 // active if found (i.e. group() will be called on it), if it wasn't already. 70 // Thread safe. 71 BASE_EXPORT std::string GetFieldTrialParamValue(const std::string& trial_name, 72 const std::string& param_name); 73 74 // Retrieves a specific parameter value corresponding to |param_name| for the 75 // field trial associated with the specified |feature|. A feature is associated 76 // with at most one field trial and selected group. See base/feature_list.h for 77 // more information on features. If the feature is not enabled, or the 78 // specified parameter does not exist, returns an empty string. Calling this 79 // function will result in the associated field trial being marked as active if 80 // found (i.e. group() will be called on it), if it wasn't already. Thread safe. 81 BASE_EXPORT std::string GetFieldTrialParamValueByFeature( 82 const base::Feature& feature, 83 const std::string& param_name); 84 85 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the 86 // string value into an int using base::StringToInt() and returns it, if 87 // successful. Otherwise, it returns |default_value|. If the string value is not 88 // empty and the conversion does not succeed, it produces a warning to LOG. 89 BASE_EXPORT int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature, 90 const std::string& param_name, 91 int default_value); 92 93 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the 94 // string value into a double using base::StringToDouble() and returns it, if 95 // successful. Otherwise, it returns |default_value|. If the string value is not 96 // empty and the conversion does not succeed, it produces a warning to LOG. 97 BASE_EXPORT double GetFieldTrialParamByFeatureAsDouble( 98 const base::Feature& feature, 99 const std::string& param_name, 100 double default_value); 101 102 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the 103 // string value into a boolean and returns it, if successful. Otherwise, it 104 // returns |default_value|. The only string representations accepted here are 105 // "true" and "false". If the string value is not empty and the conversion does 106 // not succeed, it produces a warning to LOG. 107 BASE_EXPORT bool GetFieldTrialParamByFeatureAsBool( 108 const base::Feature& feature, 109 const std::string& param_name, 110 bool default_value); 111 112 // Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the 113 // string value into a base::TimeDelta and returns it, if successful. Otherwise, 114 // it returns `default_value`. If the string value is not empty and the 115 // conversion does not succeed, it produces a warning to LOG. 116 BASE_EXPORT base::TimeDelta GetFieldTrialParamByFeatureAsTimeDelta( 117 const Feature& feature, 118 const std::string& param_name, 119 base::TimeDelta default_value); 120 121 // Shared declaration for various FeatureParam<T> types. 122 // 123 // This template is defined for the following types T: 124 // bool 125 // int 126 // double 127 // std::string 128 // enum types 129 // base::TimeDelta 130 // 131 // See the individual definitions below for the appropriate interfaces. 132 // Attempting to use it with any other type is a compile error. 133 // 134 // Getting a param value from a FeatureParam<T> will have the same semantics as 135 // GetFieldTrialParamValueByFeature(), see that function's comments for details. 136 template <typename T, bool IsEnum = std::is_enum_v<T>> 137 struct FeatureParam { 138 // Prevent use of FeatureParam<> with unsupported types (e.g. void*). Uses T 139 // in its definition so that evaluation is deferred until the template is 140 // instantiated. 141 static_assert(!std::is_same_v<T, T>, "unsupported FeatureParam<> type"); 142 }; 143 144 // Declares a string-valued parameter. Example: 145 // 146 // constexpr FeatureParam<string> kAssistantName{ 147 // &kAssistantFeature, "assistant_name", "HAL"}; 148 // 149 // If the feature is not enabled, the parameter is not set, or set to the empty 150 // string, then Get() will return the default value. 151 template <> 152 struct FeatureParam<std::string> { 153 constexpr FeatureParam(const Feature* feature, 154 const char* name, 155 const char* default_value) 156 : feature(feature), name(name), default_value(default_value) {} 157 158 // Calling Get() will activate the field trial associated with |feature|. See 159 // GetFieldTrialParamValueByFeature() for more details. 160 BASE_EXPORT std::string Get() const; 161 162 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 163 // #global-scope, #constexpr-ctor-field-initializer 164 RAW_PTR_EXCLUSION const Feature* const feature; 165 const char* const name; 166 const char* const default_value; 167 }; 168 169 // Declares a double-valued parameter. Example: 170 // 171 // constexpr FeatureParam<double> kAssistantTriggerThreshold{ 172 // &kAssistantFeature, "trigger_threshold", 0.10}; 173 // 174 // If the feature is not enabled, the parameter is not set, or set to an invalid 175 // double value, then Get() will return the default value. 176 template <> 177 struct FeatureParam<double> { 178 constexpr FeatureParam(const Feature* feature, 179 const char* name, 180 double default_value) 181 : feature(feature), name(name), default_value(default_value) {} 182 183 // Calling Get() will activate the field trial associated with |feature|. See 184 // GetFieldTrialParamValueByFeature() for more details. 185 BASE_EXPORT double Get() const; 186 187 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 188 // #global-scope, #constexpr-ctor-field-initializer 189 RAW_PTR_EXCLUSION const Feature* const feature; 190 const char* const name; 191 const double default_value; 192 }; 193 194 // Declares an int-valued parameter. Example: 195 // 196 // constexpr FeatureParam<int> kAssistantParallelism{ 197 // &kAssistantFeature, "parallelism", 4}; 198 // 199 // If the feature is not enabled, the parameter is not set, or set to an invalid 200 // int value, then Get() will return the default value. 201 template <> 202 struct FeatureParam<int> { 203 constexpr FeatureParam(const Feature* feature, 204 const char* name, 205 int default_value) 206 : feature(feature), name(name), default_value(default_value) {} 207 208 // Calling Get() will activate the field trial associated with |feature|. See 209 // GetFieldTrialParamValueByFeature() for more details. 210 BASE_EXPORT int Get() const; 211 212 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 213 // #global-scope, #constexpr-ctor-field-initializer 214 RAW_PTR_EXCLUSION const Feature* const feature; 215 const char* const name; 216 const int default_value; 217 }; 218 219 // Declares a bool-valued parameter. Example: 220 // 221 // constexpr FeatureParam<int> kAssistantIsHelpful{ 222 // &kAssistantFeature, "is_helpful", true}; 223 // 224 // If the feature is not enabled, the parameter is not set, or set to value 225 // other than "true" or "false", then Get() will return the default value. 226 template <> 227 struct FeatureParam<bool> { 228 constexpr FeatureParam(const Feature* feature, 229 const char* name, 230 bool default_value) 231 : feature(feature), name(name), default_value(default_value) {} 232 233 // Calling Get() will activate the field trial associated with |feature|. See 234 // GetFieldTrialParamValueByFeature() for more details. 235 BASE_EXPORT bool Get() const; 236 237 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 238 // #global-scope, #constexpr-ctor-field-initializer 239 RAW_PTR_EXCLUSION const Feature* const feature; 240 const char* const name; 241 const bool default_value; 242 }; 243 244 // Declares an TimeDelta-valued parameter. Example: 245 // 246 // constexpr base::FeatureParam<base::TimeDelta> kPerAgentDelay{ 247 // &kPerAgentSchedulingExperiments, "delay", base::TimeDelta()}; 248 // 249 // If the feature is not enabled, the parameter is not set, or set to an 250 // invalid value (as defined by base::TimeDeltaFromString()), then Get() will 251 // return the default value. 252 template <> 253 struct FeatureParam<base::TimeDelta> { 254 constexpr FeatureParam(const Feature* feature, 255 const char* name, 256 base::TimeDelta default_value) 257 : feature(feature), name(name), default_value(default_value) {} 258 259 // Calling Get() will activate the field trial associated with |feature|. See 260 // GetFieldTrialParamValueByFeature() for more details. 261 BASE_EXPORT base::TimeDelta Get() const; 262 263 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 264 // #global-scope, #constexpr-ctor-field-initializer 265 RAW_PTR_EXCLUSION const Feature* const feature; 266 const char* const name; 267 const base::TimeDelta default_value; 268 }; 269 270 BASE_EXPORT void LogInvalidEnumValue(const Feature& feature, 271 const std::string& param_name, 272 const std::string& value_as_string, 273 int default_value_as_int); 274 275 // Feature param declaration for an enum, with associated options. Example: 276 // 277 // constexpr FeatureParam<ShapeEnum>::Option kShapeParamOptions[] = { 278 // {SHAPE_CIRCLE, "circle"}, 279 // {SHAPE_CYLINDER, "cylinder"}, 280 // {SHAPE_PAPERCLIP, "paperclip"}}; 281 // constexpr FeatureParam<ShapeEnum> kAssistantShapeParam{ 282 // &kAssistantFeature, "shape", SHAPE_CIRCLE, &kShapeParamOptions}; 283 // 284 // With this declaration, the parameter may be set to "circle", "cylinder", or 285 // "paperclip", and that will be translated to one of the three enum values. By 286 // default, or if the param is set to an unknown value, the parameter will be 287 // assumed to be SHAPE_CIRCLE. 288 template <typename Enum> 289 struct FeatureParam<Enum, true> { 290 struct Option { 291 constexpr Option(Enum value, const char* name) : value(value), name(name) {} 292 293 const Enum value; 294 const char* const name; 295 }; 296 297 template <size_t option_count> 298 constexpr FeatureParam(const Feature* feature, 299 const char* name, 300 const Enum default_value, 301 const Option (*options)[option_count]) 302 : feature(feature), 303 name(name), 304 default_value(default_value), 305 options(*options), 306 option_count(option_count) { 307 static_assert(option_count >= 1, "FeatureParam<enum> has no options"); 308 } 309 310 // Calling Get() will activate the field trial associated with |feature|. See 311 // GetFieldTrialParamValueByFeature() for more details. 312 Enum Get() const { 313 std::string value = GetFieldTrialParamValueByFeature(*feature, name); 314 if (value.empty()) 315 return default_value; 316 for (size_t i = 0; i < option_count; ++i) { 317 if (value == options[i].name) 318 return options[i].value; 319 } 320 LogInvalidEnumValue(*feature, name, value, static_cast<int>(default_value)); 321 return default_value; 322 } 323 324 // Returns the param-string for the given enum value. 325 std::string GetName(Enum value) const { 326 for (size_t i = 0; i < option_count; ++i) { 327 if (value == options[i].value) 328 return options[i].name; 329 } 330 NOTREACHED(); 331 return ""; 332 } 333 334 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 335 // #global-scope, #constexpr-ctor-field-initializer 336 RAW_PTR_EXCLUSION const base::Feature* const feature; 337 const char* const name; 338 const Enum default_value; 339 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 340 // #global-scope, #constexpr-ctor-field-initializer 341 RAW_PTR_EXCLUSION const Option* const options; 342 const size_t option_count; 343 }; 344 345 } // namespace base 346 347 #endif // BASE_METRICS_FIELD_TRIAL_PARAMS_H_ 348