blob: a1d21fa3ef01e720565e00f97582b1e9e4ae66df [file] [log] [blame]
andresp@webrtc.orga36ad692014-05-14 12:24:041// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
8//
9
Karl Wiberg79eb1d92017-11-08 11:26:0710#include "system_wrappers/include/field_trial.h"
andresp@webrtc.orga36ad692014-05-14 12:24:0411
Yves Gerey988cc082018-10-23 10:03:0112#include <stddef.h>
Jonas Olsson5b2eda42019-06-11 12:29:4013
14#include <map>
glaznev@webrtc.org669bc7e2015-02-09 18:17:4615#include <string>
Emil Lundmark6bf20cc2022-09-21 13:20:2216#include <utility>
glaznev@webrtc.org669bc7e2015-02-09 18:17:4617
Emil Lundmark6bf20cc2022-09-21 13:20:2218#include "absl/algorithm/container.h"
Jonas Olsson5b2eda42019-06-11 12:29:4019#include "absl/strings/string_view.h"
Emil Lundmark6bf20cc2022-09-21 13:20:2220#include "experiments/registered_field_trials.h"
Jonas Olsson5b2eda42019-06-11 12:29:4021#include "rtc_base/checks.h"
Emil Lundmark6bf20cc2022-09-21 13:20:2222#include "rtc_base/containers/flat_set.h"
Jonas Olsson5b2eda42019-06-11 12:29:4023#include "rtc_base/logging.h"
Konrad Hofbaueree1e0152019-12-05 15:25:4024#include "rtc_base/string_encode.h"
Jonas Olsson5b2eda42019-06-11 12:29:4025
glaznev@webrtc.org669bc7e2015-02-09 18:17:4626// Simple field trial implementation, which allows client to
27// specify desired flags in InitFieldTrialsFromString.
andresp@webrtc.orga36ad692014-05-14 12:24:0428namespace webrtc {
29namespace field_trial {
30
Karl Wiberg79eb1d92017-11-08 11:26:0731static const char* trials_init_string = NULL;
glaznev@webrtc.org669bc7e2015-02-09 18:17:4632
Jonas Olsson5b2eda42019-06-11 12:29:4033namespace {
Emil Lundmark6bf20cc2022-09-21 13:20:2234
Jonas Olsson5b2eda42019-06-11 12:29:4035constexpr char kPersistentStringSeparator = '/';
Emil Lundmark6bf20cc2022-09-21 13:20:2236
37flat_set<std::string>& TestKeys() {
38 static auto* test_keys = new flat_set<std::string>();
39 return *test_keys;
40}
41
Jonas Olsson5b2eda42019-06-11 12:29:4042// Validates the given field trial string.
43// E.g.:
44// "WebRTC-experimentFoo/Enabled/WebRTC-experimentBar/Enabled100kbps/"
45// Assigns the process to group "Enabled" on WebRTCExperimentFoo trial
46// and to group "Enabled100kbps" on WebRTCExperimentBar.
47//
48// E.g. invalid config:
49// "WebRTC-experiment1/Enabled" (note missing / separator at the end).
Konrad Hofbaueree1e0152019-12-05 15:25:4050bool FieldTrialsStringIsValidInternal(const absl::string_view trials) {
Jonas Olsson5b2eda42019-06-11 12:29:4051 if (trials.empty())
52 return true;
53
54 size_t next_item = 0;
55 std::map<absl::string_view, absl::string_view> field_trials;
56 while (next_item < trials.length()) {
57 size_t name_end = trials.find(kPersistentStringSeparator, next_item);
58 if (name_end == trials.npos || next_item == name_end)
59 return false;
60 size_t group_name_end =
61 trials.find(kPersistentStringSeparator, name_end + 1);
62 if (group_name_end == trials.npos || name_end + 1 == group_name_end)
63 return false;
64 absl::string_view name = trials.substr(next_item, name_end - next_item);
65 absl::string_view group_name =
66 trials.substr(name_end + 1, group_name_end - name_end - 1);
67
68 next_item = group_name_end + 1;
69
70 // Fail if duplicate with different group name.
71 if (field_trials.find(name) != field_trials.end() &&
72 field_trials.find(name)->second != group_name) {
73 return false;
74 }
75
76 field_trials[name] = group_name;
77 }
78
79 return true;
80}
Emil Lundmark6bf20cc2022-09-21 13:20:2281
Jonas Olsson5b2eda42019-06-11 12:29:4082} // namespace
83
Ali Tofigh969c13562022-05-13 08:26:5884bool FieldTrialsStringIsValid(absl::string_view trials_string) {
Konrad Hofbaueree1e0152019-12-05 15:25:4085 return FieldTrialsStringIsValidInternal(trials_string);
86}
87
88void InsertOrReplaceFieldTrialStringsInMap(
89 std::map<std::string, std::string>* fieldtrial_map,
90 const absl::string_view trials_string) {
91 if (FieldTrialsStringIsValidInternal(trials_string)) {
Niels Möllerf1d822b2022-06-07 11:58:2792 std::vector<absl::string_view> tokens = rtc::split(trials_string, '/');
Konrad Hofbaueree1e0152019-12-05 15:25:4093 // Skip last token which is empty due to trailing '/'.
94 for (size_t idx = 0; idx < tokens.size() - 1; idx += 2) {
Niels Möllerf1d822b2022-06-07 11:58:2795 (*fieldtrial_map)[std::string(tokens[idx])] =
96 std::string(tokens[idx + 1]);
Konrad Hofbaueree1e0152019-12-05 15:25:4097 }
98 } else {
Artem Titovd3251962021-11-15 15:57:0799 RTC_DCHECK_NOTREACHED() << "Invalid field trials string:" << trials_string;
Konrad Hofbaueree1e0152019-12-05 15:25:40100 }
101}
102
Ali Tofigh969c13562022-05-13 08:26:58103std::string MergeFieldTrialsStrings(absl::string_view first,
104 absl::string_view second) {
Konrad Hofbaueree1e0152019-12-05 15:25:40105 std::map<std::string, std::string> fieldtrial_map;
106 InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, first);
107 InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, second);
108
109 // Merge into fieldtrial string.
110 std::string merged = "";
111 for (auto const& fieldtrial : fieldtrial_map) {
112 merged += fieldtrial.first + '/' + fieldtrial.second + '/';
113 }
114 return merged;
115}
116
Mirko Bonadei83240ac2022-03-14 13:48:17117#ifndef WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT
Ali Tofigh04c88162022-03-21 13:47:35118std::string FindFullName(absl::string_view name) {
Emil Lundmark9109e852023-02-28 12:57:01119#if WEBRTC_STRICT_FIELD_TRIALS == 1
Emil Lundmark6bf20cc2022-09-21 13:20:22120 RTC_DCHECK(absl::c_linear_search(kRegisteredFieldTrials, name) ||
121 TestKeys().contains(name))
Emil Lundmark9109e852023-02-28 12:57:01122 << name << " is not registered, see g3doc/field-trials.md.";
123#elif WEBRTC_STRICT_FIELD_TRIALS == 2
124 RTC_LOG_IF(LS_WARNING,
125 !(absl::c_linear_search(kRegisteredFieldTrials, name) ||
126 TestKeys().contains(name)))
127 << name << " is not registered, see g3doc/field-trials.md.";
Emil Lundmark6bf20cc2022-09-21 13:20:22128#endif
129
glaznev@webrtc.org669bc7e2015-02-09 18:17:46130 if (trials_init_string == NULL)
131 return std::string();
132
Ali Tofigh04c88162022-03-21 13:47:35133 absl::string_view trials_string(trials_init_string);
glaznev@webrtc.org669bc7e2015-02-09 18:17:46134 if (trials_string.empty())
135 return std::string();
136
glaznev@webrtc.org669bc7e2015-02-09 18:17:46137 size_t next_item = 0;
138 while (next_item < trials_string.length()) {
glaznev@webrtc.org669bc7e2015-02-09 18:17:46139 // Find next name/value pair in field trial configuration string.
Karl Wiberg79eb1d92017-11-08 11:26:07140 size_t field_name_end =
141 trials_string.find(kPersistentStringSeparator, next_item);
glaznev@webrtc.org669bc7e2015-02-09 18:17:46142 if (field_name_end == trials_string.npos || field_name_end == next_item)
143 break;
Karl Wiberg79eb1d92017-11-08 11:26:07144 size_t field_value_end =
145 trials_string.find(kPersistentStringSeparator, field_name_end + 1);
glaznev@webrtc.org669bc7e2015-02-09 18:17:46146 if (field_value_end == trials_string.npos ||
147 field_value_end == field_name_end + 1)
148 break;
Ali Tofigh04c88162022-03-21 13:47:35149 absl::string_view field_name =
150 trials_string.substr(next_item, field_name_end - next_item);
151 absl::string_view field_value = trials_string.substr(
152 field_name_end + 1, field_value_end - field_name_end - 1);
glaznev@webrtc.org669bc7e2015-02-09 18:17:46153 next_item = field_value_end + 1;
154
155 if (name == field_name)
Ali Tofigh04c88162022-03-21 13:47:35156 return std::string(field_value);
glaznev@webrtc.org669bc7e2015-02-09 18:17:46157 }
andresp@webrtc.orga36ad692014-05-14 12:24:04158 return std::string();
159}
Mirko Bonadei92e00382018-09-15 08:37:11160#endif // WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT
andresp@webrtc.orga36ad692014-05-14 12:24:04161
glaznev@webrtc.org669bc7e2015-02-09 18:17:46162// Optionally initialize field trial from a string.
163void InitFieldTrialsFromString(const char* trials_string) {
Jonas Olsson5b2eda42019-06-11 12:29:40164 RTC_LOG(LS_INFO) << "Setting field trial string:" << trials_string;
Jonas Olsson5b2eda42019-06-11 12:29:40165 if (trials_string) {
Konrad Hofbaueree1e0152019-12-05 15:25:40166 RTC_DCHECK(FieldTrialsStringIsValidInternal(trials_string))
Jonas Olsson5b2eda42019-06-11 12:29:40167 << "Invalid field trials string:" << trials_string;
168 };
glaznev@webrtc.org669bc7e2015-02-09 18:17:46169 trials_init_string = trials_string;
170}
171
phoglund37ebcf02016-01-08 13:04:57172const char* GetFieldTrialString() {
173 return trials_init_string;
174}
175
Emil Lundmark2d7a3e72022-11-25 09:19:12176FieldTrialsAllowedInScopeForTesting::FieldTrialsAllowedInScopeForTesting(
Emil Lundmark6bf20cc2022-09-21 13:20:22177 flat_set<std::string> keys) {
178 TestKeys() = std::move(keys);
179}
180
Emil Lundmark2d7a3e72022-11-25 09:19:12181FieldTrialsAllowedInScopeForTesting::~FieldTrialsAllowedInScopeForTesting() {
Emil Lundmark6bf20cc2022-09-21 13:20:22182 TestKeys().clear();
183}
184
andresp@webrtc.orga36ad692014-05-14 12:24:04185} // namespace field_trial
186} // namespace webrtc