#include <grpc/support/port_platform.h>
#include "src/core/lib/experiments/config.h"
#include <string.h>
#include <algorithm>
#include <atomic>
#include <string>
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "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"
#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(false);
absl::AnyInvocable<bool(struct ExperimentMetadata)>* g_check_constraints_cb =
nullptr;
GPR_ATTRIBUTE_NOINLINE Experiments LoadExperimentsFromConfigVariable() {
g_loaded.store(true, std::memory_order_relaxed);
Experiments experiments;
for (size_t i = 0; i < kNumExperiments; i++) {
if (!g_forced_experiments[i].forced) {
if (g_check_constraints_cb != nullptr) {
experiments.enabled[i] =
(*g_check_constraints_cb)(g_experiment_metadata[i]);
} else {
experiments.enabled[i] = g_experiment_metadata[i].default_value;
}
} else {
experiments.enabled[i] = g_forced_experiments[i].value;
}
}
for (auto experiment : absl::StrSplit(
absl::string_view(ConfigVars::Get().Experiments()), ',')) {
experiment = absl::StripAsciiWhitespace(experiment);
if (experiment.empty()) continue;
bool enable = true;
if (experiment[0] == '-') {
enable = false;
experiment.remove_prefix(1);
}
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 (!found) {
gpr_log(GPR_ERROR, "Unknown experiment: %s",
std::string(experiment).c_str());
}
}
return experiments;
}
Experiments& ExperimentsSingleton() {
static NoDestruct<Experiments> experiments{
LoadExperimentsFromConfigVariable()};
return *experiments;
}
}
void TestOnlyReloadExperimentsFromConfigVariables() {
ExperimentsSingleton() = LoadExperimentsFromConfigVariable();
PrintExperimentsList();
}
bool IsExperimentEnabled(size_t experiment_id) {
return ExperimentsSingleton().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",
absl::StrCat(
"gRPC EXPERIMENT ", g_experiment_metadata[i].name,
std::string(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
? absl::StrCat(" force:",
g_forced_experiments[i].value ? "ON" : "OFF")
: std::string(),
")")
.c_str());
}
}
void ForceEnableExperiment(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",
std::string(experiment).c_str(), enable ? "enable" : "disable");
}
void RegisterExperimentConstraintsValidator(
absl::AnyInvocable<bool(struct ExperimentMetadata)> check_constraints_cb) {
g_check_constraints_cb =
new absl::AnyInvocable<bool(struct ExperimentMetadata)>(
std::move(check_constraints_cb));
}
} #else
namespace grpc_core {
void PrintExperimentsList() {}
void ForceEnableExperiment(absl::string_view experiment_name, bool) {
Crash(absl::StrCat("ForceEnableExperiment(\"", experiment_name,
"\") called in final build"));
}
void RegisterExperimentConstraintsValidator(
absl::AnyInvocable<
bool(struct ExperimentMetadata)> ) {}
} #endif