1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
// Copyright 2022 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <grpc/support/port_platform.h>
#include "src/core/lib/experiments/config.h"
#include <string.h>
#include <algorithm>
#include <atomic>
#include <util/generic/string.h>
#include <util/string/cast.h>
#include "y_absl/strings/ascii.h"
#include "y_absl/strings/str_cat.h"
#include "y_absl/strings/str_split.h"
#include "y_absl/strings/string_view.h"
#include <grpc/support/log.h>
#include "src/core/lib/config/config_vars.h"
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gprpp/crash.h" // IWYU pragma: keep
#include "src/core/lib/gprpp/no_destruct.h"
#ifndef GRPC_EXPERIMENTS_ARE_FINAL
namespace grpc_core {
namespace {
struct Experiments {
bool enabled[kNumExperiments];
};
struct ForcedExperiment {
bool forced = false;
bool value;
};
ForcedExperiment g_forced_experiments[kNumExperiments];
std::atomic<bool> g_loaded;
GPR_ATTRIBUTE_NOINLINE Experiments LoadExperimentsFromConfigVariable() {
GPR_ASSERT(g_loaded.exchange(true, std::memory_order_relaxed) == false);
// Set defaults from metadata.
Experiments experiments;
for (size_t i = 0; i < kNumExperiments; i++) {
if (!g_forced_experiments[i].forced) {
experiments.enabled[i] = g_experiment_metadata[i].default_value;
} else {
experiments.enabled[i] = g_forced_experiments[i].value;
}
}
// For each comma-separated experiment in the global config:
for (auto experiment : y_absl::StrSplit(
y_absl::string_view(ConfigVars::Get().Experiments()), ',')) {
// Strip whitespace.
experiment = y_absl::StripAsciiWhitespace(experiment);
// Handle ",," without crashing.
if (experiment.empty()) continue;
// Enable unless prefixed with '-' (=> disable).
bool enable = true;
if (experiment[0] == '-') {
enable = false;
experiment.remove_prefix(1);
}
// See if we can find the experiment in the list in this binary.
bool found = false;
for (size_t i = 0; i < kNumExperiments; i++) {
if (experiment == g_experiment_metadata[i].name) {
experiments.enabled[i] = enable;
found = true;
break;
}
}
// If not found log an error, but don't take any other action.
// Allows us an easy path to disabling experiments.
if (!found) {
gpr_log(GPR_ERROR, "Unknown experiment: %s",
TString(experiment).c_str());
}
}
return experiments;
}
} // namespace
bool IsExperimentEnabled(size_t experiment_id) {
// One time initialization:
static const NoDestruct<Experiments> experiments{
LoadExperimentsFromConfigVariable()};
// Normal path: just return the value;
return experiments->enabled[experiment_id];
}
void PrintExperimentsList() {
size_t max_experiment_length = 0;
for (size_t i = 0; i < kNumExperiments; i++) {
max_experiment_length =
std::max(max_experiment_length, strlen(g_experiment_metadata[i].name));
}
for (size_t i = 0; i < kNumExperiments; i++) {
gpr_log(GPR_DEBUG, "%s",
y_absl::StrCat(
"gRPC EXPERIMENT ", g_experiment_metadata[i].name,
TString(max_experiment_length -
strlen(g_experiment_metadata[i].name) + 1,
' '),
IsExperimentEnabled(i) ? "ON " : "OFF", " (default:",
g_experiment_metadata[i].default_value ? "ON" : "OFF",
g_forced_experiments[i].forced
? y_absl::StrCat(" force:",
g_forced_experiments[i].value ? "ON" : "OFF")
: TString(),
")")
.c_str());
}
}
void ForceEnableExperiment(y_absl::string_view experiment, bool enable) {
GPR_ASSERT(g_loaded.load(std::memory_order_relaxed) == false);
for (size_t i = 0; i < kNumExperiments; i++) {
if (g_experiment_metadata[i].name != experiment) continue;
if (g_forced_experiments[i].forced) {
GPR_ASSERT(g_forced_experiments[i].value == enable);
} else {
g_forced_experiments[i].forced = true;
g_forced_experiments[i].value = enable;
}
return;
}
gpr_log(GPR_INFO, "gRPC EXPERIMENT %s not found to force %s",
TString(experiment).c_str(), enable ? "enable" : "disable");
}
} // namespace grpc_core
#else
namespace grpc_core {
void PrintExperimentsList() {}
void ForceEnableExperiment(y_absl::string_view experiment_name, bool) {
Crash(y_absl::StrCat("ForceEnableExperiment(\"", experiment_name,
"\") called in final build"));
}
} // namespace grpc_core
#endif
|