field_trial_params.cc 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright 2017 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. #include "base/metrics/field_trial_params.h"
  5. #include <set>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/feature_list.h"
  9. #include "base/metrics/field_trial.h"
  10. #include "base/metrics/field_trial_param_associator.h"
  11. #include "base/notreached.h"
  12. #include "base/strings/escape.h"
  13. #include "base/strings/string_number_conversions.h"
  14. #include "base/strings/string_split.h"
  15. #include "base/strings/stringprintf.h"
  16. #include "base/time/time_delta_from_string.h"
  17. #include "third_party/abseil-cpp/absl/types/optional.h"
  18. namespace base {
  19. std::string UnescapeValue(const std::string& value) {
  20. return UnescapeURLComponent(
  21. value, UnescapeRule::PATH_SEPARATORS |
  22. UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
  23. }
  24. bool AssociateFieldTrialParams(const std::string& trial_name,
  25. const std::string& group_name,
  26. const FieldTrialParams& params) {
  27. return FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
  28. trial_name, group_name, params);
  29. }
  30. bool AssociateFieldTrialParamsFromString(
  31. const std::string& params_string,
  32. FieldTrialParamsDecodeStringFunc decode_data_func) {
  33. // Format: Trial1.Group1:k1/v1/k2/v2,Trial2.Group2:k1/v1/k2/v2
  34. std::set<std::pair<std::string, std::string>> trial_groups;
  35. for (StringPiece experiment_group :
  36. SplitStringPiece(params_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL)) {
  37. std::vector<StringPiece> experiment = SplitStringPiece(
  38. experiment_group, ":", TRIM_WHITESPACE, SPLIT_WANT_ALL);
  39. if (experiment.size() != 2) {
  40. DLOG(ERROR) << "Experiment and params should be separated by ':'";
  41. return false;
  42. }
  43. std::vector<std::string> group_parts =
  44. SplitString(experiment[0], ".", TRIM_WHITESPACE, SPLIT_WANT_ALL);
  45. if (group_parts.size() != 2) {
  46. DLOG(ERROR) << "Trial and group name should be separated by '.'";
  47. return false;
  48. }
  49. std::vector<std::string> key_values =
  50. SplitString(experiment[1], "/", TRIM_WHITESPACE, SPLIT_WANT_ALL);
  51. if (key_values.size() % 2 != 0) {
  52. DLOG(ERROR) << "Param name and param value should be separated by '/'";
  53. return false;
  54. }
  55. std::string trial = decode_data_func(group_parts[0]);
  56. std::string group = decode_data_func(group_parts[1]);
  57. auto trial_group = std::make_pair(trial, group);
  58. if (trial_groups.find(trial_group) != trial_groups.end()) {
  59. DLOG(ERROR) << StringPrintf(
  60. "A (trial, group) pair listed more than once. (%s, %s)",
  61. trial.c_str(), group.c_str());
  62. return false;
  63. }
  64. trial_groups.insert(trial_group);
  65. std::map<std::string, std::string> params;
  66. for (size_t i = 0; i < key_values.size(); i += 2) {
  67. std::string key = decode_data_func(key_values[i]);
  68. std::string value = decode_data_func(key_values[i + 1]);
  69. params[key] = value;
  70. }
  71. bool result = AssociateFieldTrialParams(trial, group, params);
  72. if (!result) {
  73. DLOG(ERROR) << "Failed to associate field trial params for group \""
  74. << group << "\" in trial \"" << trial << "\"";
  75. return false;
  76. }
  77. }
  78. return true;
  79. }
  80. bool GetFieldTrialParams(const std::string& trial_name,
  81. FieldTrialParams* params) {
  82. FieldTrial* trial = FieldTrialList::Find(trial_name);
  83. return FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial,
  84. params);
  85. }
  86. bool GetFieldTrialParamsByFeature(const Feature& feature,
  87. FieldTrialParams* params) {
  88. if (!FeatureList::IsEnabled(feature))
  89. return false;
  90. FieldTrial* trial = FeatureList::GetFieldTrial(feature);
  91. return FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial,
  92. params);
  93. }
  94. std::string GetFieldTrialParamValue(const std::string& trial_name,
  95. const std::string& param_name) {
  96. FieldTrialParams params;
  97. if (GetFieldTrialParams(trial_name, &params)) {
  98. auto it = params.find(param_name);
  99. if (it != params.end())
  100. return it->second;
  101. }
  102. return std::string();
  103. }
  104. std::string GetFieldTrialParamValueByFeature(const Feature& feature,
  105. const std::string& param_name) {
  106. FieldTrialParams params;
  107. if (GetFieldTrialParamsByFeature(feature, &params)) {
  108. auto it = params.find(param_name);
  109. if (it != params.end())
  110. return it->second;
  111. }
  112. return std::string();
  113. }
  114. int GetFieldTrialParamByFeatureAsInt(const Feature& feature,
  115. const std::string& param_name,
  116. int default_value) {
  117. std::string value_as_string =
  118. GetFieldTrialParamValueByFeature(feature, param_name);
  119. int value_as_int = 0;
  120. if (!StringToInt(value_as_string, &value_as_int)) {
  121. if (!value_as_string.empty()) {
  122. NOTREACHED() << "Failed to parse field trial param " << param_name
  123. << " with string value " << value_as_string
  124. << " under feature " << feature.name
  125. << " into an int. Falling back to default value of "
  126. << default_value;
  127. }
  128. value_as_int = default_value;
  129. }
  130. return value_as_int;
  131. }
  132. double GetFieldTrialParamByFeatureAsDouble(const Feature& feature,
  133. const std::string& param_name,
  134. double default_value) {
  135. std::string value_as_string =
  136. GetFieldTrialParamValueByFeature(feature, param_name);
  137. double value_as_double = 0;
  138. if (!StringToDouble(value_as_string, &value_as_double)) {
  139. if (!value_as_string.empty()) {
  140. NOTREACHED() << "Failed to parse field trial param " << param_name
  141. << " with string value " << value_as_string
  142. << " under feature " << feature.name
  143. << " into a double. Falling back to default value of "
  144. << default_value;
  145. }
  146. value_as_double = default_value;
  147. }
  148. return value_as_double;
  149. }
  150. bool GetFieldTrialParamByFeatureAsBool(const Feature& feature,
  151. const std::string& param_name,
  152. bool default_value) {
  153. std::string value_as_string =
  154. GetFieldTrialParamValueByFeature(feature, param_name);
  155. if (value_as_string == "true")
  156. return true;
  157. if (value_as_string == "false")
  158. return false;
  159. if (!value_as_string.empty()) {
  160. NOTREACHED() << "Failed to parse field trial param " << param_name
  161. << " with string value " << value_as_string
  162. << " under feature " << feature.name
  163. << " into a bool. Falling back to default value of "
  164. << default_value;
  165. }
  166. return default_value;
  167. }
  168. base::TimeDelta GetFieldTrialParamByFeatureAsTimeDelta(
  169. const Feature& feature,
  170. const std::string& param_name,
  171. base::TimeDelta default_value) {
  172. std::string value_as_string =
  173. GetFieldTrialParamValueByFeature(feature, param_name);
  174. if (value_as_string.empty())
  175. return default_value;
  176. absl::optional<base::TimeDelta> ret = TimeDeltaFromString(value_as_string);
  177. if (!ret.has_value()) {
  178. NOTREACHED() << "Failed to parse field trial param " << param_name
  179. << " with string value " << value_as_string
  180. << " under feature " << feature.name
  181. << " into a base::TimeDelta. Falling back to default value of "
  182. << default_value;
  183. return default_value;
  184. }
  185. return ret.value();
  186. }
  187. std::string FeatureParam<std::string>::Get() const {
  188. const std::string value = GetFieldTrialParamValueByFeature(*feature, name);
  189. return value.empty() ? default_value : value;
  190. }
  191. double FeatureParam<double>::Get() const {
  192. return GetFieldTrialParamByFeatureAsDouble(*feature, name, default_value);
  193. }
  194. int FeatureParam<int>::Get() const {
  195. return GetFieldTrialParamByFeatureAsInt(*feature, name, default_value);
  196. }
  197. bool FeatureParam<bool>::Get() const {
  198. return GetFieldTrialParamByFeatureAsBool(*feature, name, default_value);
  199. }
  200. base::TimeDelta FeatureParam<base::TimeDelta>::Get() const {
  201. return GetFieldTrialParamByFeatureAsTimeDelta(*feature, name, default_value);
  202. }
  203. void LogInvalidEnumValue(const Feature& feature,
  204. const std::string& param_name,
  205. const std::string& value_as_string,
  206. int default_value_as_int) {
  207. NOTREACHED() << "Failed to parse field trial param " << param_name
  208. << " with string value " << value_as_string << " under feature "
  209. << feature.name
  210. << " into an enum. Falling back to default value of "
  211. << default_value_as_int;
  212. }
  213. } // namespace base