[experiments] Add experiment framework (#30775)

* [experiments] Add experiment framework

* auto-ci-config

* fix

* fix

* Automated change: Fix sanity tests

* support different configs

* Automated change: Fix sanity tests

* cleaner generated code

* Automated change: Fix sanity tests

* enforce expiry

* make sure expiry isnt far in the future

* fix

* Automated change: Fix sanity tests

* remove specified testing

* cleaner

* fix

* fix

* Automated change: Fix sanity tests

* fix

* fix

* docstring

* clean up code

* Automated change: Fix sanity tests

* review feedback

* build fix

* Automated change: Fix sanity tests

* ownership

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/30807/head
Craig Tiller 2 years ago committed by GitHub
parent 622dd886e6
commit c580d0d9a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      BUILD
  2. 2
      CMakeLists.txt
  3. 2
      Makefile
  4. 18
      bazel/experiments.bzl
  5. 41
      bazel/grpc_build_system.bzl
  6. 4
      build_autogenerated.yaml
  7. 2
      config.m4
  8. 2
      config.w32
  9. 2
      gRPC-C++.podspec
  10. 3
      gRPC-Core.podspec
  11. 2
      grpc.gemspec
  12. 2
      grpc.gyp
  13. 2
      package.xml
  14. 47
      src/core/lib/experiments/experiments.cc
  15. 40
      src/core/lib/experiments/experiments.h
  16. 37
      src/core/lib/experiments/experiments.yaml
  17. 7
      src/core/lib/iomgr/iomgr.cc
  18. 11
      src/core/lib/iomgr/tcp_posix.cc
  19. 1
      src/python/grpcio/grpc_core_dependencies.py
  20. 1
      test/core/end2end/generate_tests.bzl
  21. 7
      test/core/iomgr/BUILD
  22. 17
      test/core/util/test_config.cc
  23. 218
      tools/codegen/core/gen_experiments.py
  24. 2
      tools/doxygen/Doxyfile.c++.internal
  25. 2
      tools/doxygen/Doxyfile.core.internal
  26. 2
      tools/internal_ci/linux/grpc_bazel_rbe.sh

12
BUILD

@ -402,6 +402,17 @@ grpc_cc_library(
deps = ["gpr_platform"],
)
grpc_cc_library(
name = "experiments",
srcs = ["src/core/lib/experiments/experiments.cc"],
hdrs = ["src/core/lib/experiments/experiments.h"],
language = "c++",
deps = [
"gpr",
"gpr_platform",
],
)
grpc_cc_library(
name = "grpc_unsecure",
srcs = [
@ -3185,6 +3196,7 @@ grpc_cc_library(
"error",
"event_engine_common",
"exec_ctx",
"experiments",
"gpr",
"gpr_tls",
"grpc_public_hdrs",

2
CMakeLists.txt generated

@ -2124,6 +2124,7 @@ add_library(grpc
src/core/lib/event_engine/windows/iocp.cc
src/core/lib/event_engine/windows/win_socket.cc
src/core/lib/event_engine/windows/windows_engine.cc
src/core/lib/experiments/experiments.cc
src/core/lib/gprpp/status_helper.cc
src/core/lib/gprpp/time.cc
src/core/lib/gprpp/time_averaged_stats.cc
@ -2758,6 +2759,7 @@ add_library(grpc_unsecure
src/core/lib/event_engine/windows/iocp.cc
src/core/lib/event_engine/windows/win_socket.cc
src/core/lib/event_engine/windows/windows_engine.cc
src/core/lib/experiments/experiments.cc
src/core/lib/gprpp/status_helper.cc
src/core/lib/gprpp/time.cc
src/core/lib/gprpp/time_averaged_stats.cc

2
Makefile generated

@ -1440,6 +1440,7 @@ LIBGRPC_SRC = \
src/core/lib/event_engine/windows/iocp.cc \
src/core/lib/event_engine/windows/win_socket.cc \
src/core/lib/event_engine/windows/windows_engine.cc \
src/core/lib/experiments/experiments.cc \
src/core/lib/gprpp/status_helper.cc \
src/core/lib/gprpp/time.cc \
src/core/lib/gprpp/time_averaged_stats.cc \
@ -1938,6 +1939,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/event_engine/windows/iocp.cc \
src/core/lib/event_engine/windows/win_socket.cc \
src/core/lib/event_engine/windows/windows_engine.cc \
src/core/lib/experiments/experiments.cc \
src/core/lib/gprpp/status_helper.cc \
src/core/lib/gprpp/time.cc \
src/core/lib/gprpp/time_averaged_stats.cc \

@ -0,0 +1,18 @@
# 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.
# Automatically generated by tools/codegen/core/gen_experiments.py
"""Dictionary of tags to experiments so we know when to test different experiments."""
EXPERIMENTS = {"endpoint_test": ["tcp_frame_size_tuning"], "core_end2end_test": ["tcp_frame_size_tuning"]}

@ -29,6 +29,7 @@ Contains macros used throughout the repo.
load("//bazel:cc_grpc_library.bzl", "cc_grpc_library")
load("//bazel:copts.bzl", "GRPC_DEFAULT_COPTS")
load("//bazel:experiments.bzl", "EXPERIMENTS")
load("@upb//bazel:upb_proto_library.bzl", "upb_proto_library", "upb_proto_reflection_library")
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test")
load("@build_bazel_rules_apple//apple/testing/default_runner:ios_test_runner.bzl", "ios_test_runner")
@ -259,8 +260,8 @@ def ios_cc_test(
deps = ios_test_deps,
)
def expand_tests_for_each_poller_and_engine(name, srcs, deps, tags, args, exclude_pollers, uses_event_engine):
"""Common logic used to parameterize tests for every poller and EventEngine.
def expand_tests(name, srcs, deps, tags, args, exclude_pollers, uses_event_engine):
"""Common logic used to parameterize tests for every poller and EventEngine and experiment.
Args:
name: base name of the test
@ -326,7 +327,37 @@ def expand_tests_for_each_poller_and_engine(name, srcs, deps, tags, args, exclud
"tags": test_tags,
"args": test_args,
})
return poller_config
experiments = {}
for tag in tags:
if tag not in EXPERIMENTS:
continue
for experiment in EXPERIMENTS[tag]:
experiments[experiment] = 1
experiments = list(experiments.keys())
experiment_config = list(poller_config)
for experiment in experiments:
for config in poller_config:
config = dict(config)
config["name"] = config["name"] + "@experiment=" + experiment
config["args"] = config["args"] + ["--experiment=" + experiment]
tags = config["tags"]
must_have_tags = [
# We don't run experiments on cmake builds
"bazel_only",
# Nor on windows
"no_windows",
# Nor on mac
"no_mac",
]
for tag in must_have_tags:
if tag not in tags:
tags = tags + [tag]
config["tags"] = tags
experiment_config.append(config)
return experiment_config
def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data = [], uses_polling = True, language = "C++", size = "medium", timeout = None, tags = [], exec_compatible_with = [], exec_properties = {}, shard_count = None, flaky = None, copts = [], linkstatic = None, exclude_pollers = [], uses_event_engine = True):
"""A cc_test target for use in the gRPC repo.
@ -395,7 +426,7 @@ def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data
)
return
for poller_config in expand_tests_for_each_poller_and_engine(name, srcs, core_deps, tags, args, exclude_pollers, uses_event_engine):
for poller_config in expand_tests(name, srcs, core_deps, tags, args, exclude_pollers, uses_event_engine):
native.cc_test(
name = poller_config["name"],
srcs = poller_config["srcs"],
@ -497,7 +528,7 @@ def grpc_sh_test(name, srcs = [], args = [], data = [], uses_polling = True, siz
)
return
for poller_config in expand_tests_for_each_poller_and_engine(name, srcs, [], tags, args, exclude_pollers, uses_event_engine):
for poller_config in expand_tests(name, srcs, [], tags, args, exclude_pollers, uses_event_engine):
native.sh_test(
name = poller_config["name"],
srcs = poller_config["srcs"],

@ -795,6 +795,7 @@ libs:
- src/core/lib/event_engine/windows/iocp.h
- src/core/lib/event_engine/windows/win_socket.h
- src/core/lib/event_engine/windows/windows_engine.h
- src/core/lib/experiments/experiments.h
- src/core/lib/gprpp/atomic_utils.h
- src/core/lib/gprpp/bitset.h
- src/core/lib/gprpp/chunked_vector.h
@ -1496,6 +1497,7 @@ libs:
- src/core/lib/event_engine/windows/iocp.cc
- src/core/lib/event_engine/windows/win_socket.cc
- src/core/lib/event_engine/windows/windows_engine.cc
- src/core/lib/experiments/experiments.cc
- src/core/lib/gprpp/status_helper.cc
- src/core/lib/gprpp/time.cc
- src/core/lib/gprpp/time_averaged_stats.cc
@ -2010,6 +2012,7 @@ libs:
- src/core/lib/event_engine/windows/iocp.h
- src/core/lib/event_engine/windows/win_socket.h
- src/core/lib/event_engine/windows/windows_engine.h
- src/core/lib/experiments/experiments.h
- src/core/lib/gprpp/atomic_utils.h
- src/core/lib/gprpp/bitset.h
- src/core/lib/gprpp/chunked_vector.h
@ -2351,6 +2354,7 @@ libs:
- src/core/lib/event_engine/windows/iocp.cc
- src/core/lib/event_engine/windows/win_socket.cc
- src/core/lib/event_engine/windows/windows_engine.cc
- src/core/lib/experiments/experiments.cc
- src/core/lib/gprpp/status_helper.cc
- src/core/lib/gprpp/time.cc
- src/core/lib/gprpp/time_averaged_stats.cc

2
config.m4 generated

@ -488,6 +488,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/event_engine/windows/iocp.cc \
src/core/lib/event_engine/windows/win_socket.cc \
src/core/lib/event_engine/windows/windows_engine.cc \
src/core/lib/experiments/experiments.cc \
src/core/lib/gpr/alloc.cc \
src/core/lib/gpr/atm.cc \
src/core/lib/gpr/cpu_iphone.cc \
@ -1331,6 +1332,7 @@ if test "$PHP_GRPC" != "no"; then
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/event_engine/executor)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/event_engine/posix_engine)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/event_engine/windows)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/experiments)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/gpr)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/gprpp)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/http)

2
config.w32 generated

@ -454,6 +454,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\event_engine\\windows\\iocp.cc " +
"src\\core\\lib\\event_engine\\windows\\win_socket.cc " +
"src\\core\\lib\\event_engine\\windows\\windows_engine.cc " +
"src\\core\\lib\\experiments\\experiments.cc " +
"src\\core\\lib\\gpr\\alloc.cc " +
"src\\core\\lib\\gpr\\atm.cc " +
"src\\core\\lib\\gpr\\cpu_iphone.cc " +
@ -1453,6 +1454,7 @@ if (PHP_GRPC != "no") {
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\event_engine\\executor");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\event_engine\\posix_engine");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\event_engine\\windows");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\experiments");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\gpr");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\gprpp");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\http");

2
gRPC-C++.podspec generated

@ -700,6 +700,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/windows/iocp.h',
'src/core/lib/event_engine/windows/win_socket.h',
'src/core/lib/event_engine/windows/windows_engine.h',
'src/core/lib/experiments/experiments.h',
'src/core/lib/gpr/alloc.h',
'src/core/lib/gpr/env.h',
'src/core/lib/gpr/murmur_hash.h',
@ -1557,6 +1558,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/windows/iocp.h',
'src/core/lib/event_engine/windows/win_socket.h',
'src/core/lib/event_engine/windows/windows_engine.h',
'src/core/lib/experiments/experiments.h',
'src/core/lib/gpr/alloc.h',
'src/core/lib/gpr/env.h',
'src/core/lib/gpr/murmur_hash.h',

3
gRPC-Core.podspec generated

@ -1086,6 +1086,8 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/windows/win_socket.h',
'src/core/lib/event_engine/windows/windows_engine.cc',
'src/core/lib/event_engine/windows/windows_engine.h',
'src/core/lib/experiments/experiments.cc',
'src/core/lib/experiments/experiments.h',
'src/core/lib/gpr/alloc.cc',
'src/core/lib/gpr/alloc.h',
'src/core/lib/gpr/atm.cc',
@ -2179,6 +2181,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/windows/iocp.h',
'src/core/lib/event_engine/windows/win_socket.h',
'src/core/lib/event_engine/windows/windows_engine.h',
'src/core/lib/experiments/experiments.h',
'src/core/lib/gpr/alloc.h',
'src/core/lib/gpr/env.h',
'src/core/lib/gpr/murmur_hash.h',

2
grpc.gemspec generated

@ -999,6 +999,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/event_engine/windows/win_socket.h )
s.files += %w( src/core/lib/event_engine/windows/windows_engine.cc )
s.files += %w( src/core/lib/event_engine/windows/windows_engine.h )
s.files += %w( src/core/lib/experiments/experiments.cc )
s.files += %w( src/core/lib/experiments/experiments.h )
s.files += %w( src/core/lib/gpr/alloc.cc )
s.files += %w( src/core/lib/gpr/alloc.h )
s.files += %w( src/core/lib/gpr/atm.cc )

2
grpc.gyp generated

@ -819,6 +819,7 @@
'src/core/lib/event_engine/windows/iocp.cc',
'src/core/lib/event_engine/windows/win_socket.cc',
'src/core/lib/event_engine/windows/windows_engine.cc',
'src/core/lib/experiments/experiments.cc',
'src/core/lib/gprpp/status_helper.cc',
'src/core/lib/gprpp/time.cc',
'src/core/lib/gprpp/time_averaged_stats.cc',
@ -1262,6 +1263,7 @@
'src/core/lib/event_engine/windows/iocp.cc',
'src/core/lib/event_engine/windows/win_socket.cc',
'src/core/lib/event_engine/windows/windows_engine.cc',
'src/core/lib/experiments/experiments.cc',
'src/core/lib/gprpp/status_helper.cc',
'src/core/lib/gprpp/time.cc',
'src/core/lib/gprpp/time_averaged_stats.cc',

2
package.xml generated

@ -981,6 +981,8 @@
<file baseinstalldir="/" name="src/core/lib/event_engine/windows/win_socket.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/windows/windows_engine.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/windows/windows_engine.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/experiments/experiments.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/experiments/experiments.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gpr/alloc.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/gpr/alloc.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gpr/atm.cc" role="src" />

@ -0,0 +1,47 @@
// 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.
// Automatically generated by tools/codegen/core/gen_experiments.py
#include <grpc/support/port_platform.h>
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gprpp/global_config.h"
namespace {
const char* const description_tcp_frame_size_tuning =
"If set, enables TCP to use RPC size estimation made by higher layers. TCP "
"would not indicate completion of a read operation until a specified "
"number of bytes have been read over the socket. Buffers are also "
"allocated according to estimated RPC sizes.";
}
GPR_GLOBAL_CONFIG_DEFINE_BOOL(grpc_experimental_enable_tcp_frame_size_tuning,
false, description_tcp_frame_size_tuning);
namespace grpc_core {
bool IsTcpFrameSizeTuningEnabled() {
static const bool enabled =
GPR_GLOBAL_CONFIG_GET(grpc_experimental_enable_tcp_frame_size_tuning);
return enabled;
}
const ExperimentMetadata g_experiment_metadata[] = {
{"tcp_frame_size_tuning", description_tcp_frame_size_tuning, false,
IsTcpFrameSizeTuningEnabled},
};
} // namespace grpc_core

@ -0,0 +1,40 @@
// 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.
// Automatically generated by tools/codegen/core/gen_experiments.py
#ifndef GRPC_CORE_LIB_EXPERIMENTS_EXPERIMENTS_H
#define GRPC_CORE_LIB_EXPERIMENTS_EXPERIMENTS_H
#include <grpc/support/port_platform.h>
#include <stddef.h>
namespace grpc_core {
bool IsTcpFrameSizeTuningEnabled();
struct ExperimentMetadata {
const char* name;
const char* description;
bool default_value;
bool (*is_enabled)();
};
constexpr const size_t kNumExperiments = 1;
extern const ExperimentMetadata g_experiment_metadata[kNumExperiments];
} // namespace grpc_core
#endif // GRPC_CORE_LIB_EXPERIMENTS_EXPERIMENTS_H

@ -0,0 +1,37 @@
# 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.
# Format of each entry:
# name: name of the experiment
# description: description of the experiment
# default: boolean - does this experiment default on
# expiry: when is the next time this experiment *must* be updated
# (date, YYYY/MM/DD)
# test_tags: a set of bazel tags, that if a test declares them signals
# that that test should be run with this experiment enabled in CI
#
# Well known test tags:
# core_end2end_tests: all tests, fixtures in the core end2end suite
# endpoint_test: endpoint related iomgr tests
- name: tcp_frame_size_tuning
description:
If set, enables TCP to use RPC size estimation made by higher layers.
TCP would not indicate completion of a read operation until a specified
number of bytes have been read over the socket.
Buffers are also allocated according to estimated RPC sizes.
default: false
expiry: 2022/09/01
owner: ctiller@google.com
test_tags: ["endpoint_test", "core_end2end_test"]

@ -45,13 +45,6 @@ GPR_GLOBAL_CONFIG_DEFINE_BOOL(grpc_abort_on_leaks, false,
"A debugging aid to cause a call to abort() when "
"gRPC objects are leaked past grpc_shutdown()");
GPR_GLOBAL_CONFIG_DEFINE_BOOL(
grpc_experimental_enable_tcp_frame_size_tuning, false,
"If set, enables TCP to use RPC size estimation made by higher layers. TCP "
"would not indicate completion of a read operation until a specified "
"number of bytes have been read over the socket. Buffers are also "
"allocated according to estimated RPC sizes.");
static gpr_mu g_mu;
static gpr_cv g_rcv;
static int g_shutdown;

@ -50,6 +50,7 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/debug/stats.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/sync.h"
@ -96,8 +97,6 @@ typedef size_t msg_iovlen_type;
extern grpc_core::TraceFlag grpc_tcp_trace;
GPR_GLOBAL_CONFIG_DECLARE_BOOL(grpc_experimental_enable_tcp_frame_size_tuning);
namespace grpc_core {
class TcpZerocopySendRecord {
@ -465,12 +464,6 @@ using grpc_core::TcpZerocopySendRecord;
namespace {
bool ExperimentalTcpFrameSizeTuningEnabled() {
static const bool kEnableTcpFrameSizeTuning =
GPR_GLOBAL_CONFIG_GET(grpc_experimental_enable_tcp_frame_size_tuning);
return kEnableTcpFrameSizeTuning;
}
struct grpc_tcp {
grpc_tcp(int max_sends, size_t send_bytes_threshold)
: tcp_zerocopy_send_ctx(max_sends, send_bytes_threshold) {}
@ -1956,7 +1949,7 @@ grpc_endpoint* grpc_tcp_create(grpc_fd* em_fd,
tcp->socket_ts_enabled = false;
tcp->ts_capable = true;
tcp->outgoing_buffer_arg = nullptr;
tcp->frame_size_tuning_enabled = ExperimentalTcpFrameSizeTuningEnabled();
tcp->frame_size_tuning_enabled = grpc_core::IsTcpFrameSizeTuningEnabled();
tcp->min_progress_size = 1;
if (tcp_tx_zerocopy_enabled && !tcp->tcp_zerocopy_send_ctx.memory_limited()) {
#ifdef GRPC_LINUX_ERRQUEUE

@ -463,6 +463,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/event_engine/windows/iocp.cc',
'src/core/lib/event_engine/windows/win_socket.cc',
'src/core/lib/event_engine/windows/windows_engine.cc',
'src/core/lib/experiments/experiments.cc',
'src/core/lib/gpr/alloc.cc',
'src/core/lib/gpr/atm.cc',
'src/core/lib/gpr/cpu_iphone.cc',

@ -473,6 +473,7 @@ def grpc_end2end_tests():
args = ["$(location %s)" % bin_name, t],
tags = _platform_support_tags(fopt) + fopt.tags + [
"no_test_ios",
"core_end2end_test",
],
flaky = name in FLAKY_TESTS,
exclude_pollers = topt.exclude_pollers,

@ -60,6 +60,7 @@ grpc_cc_test(
srcs = ["endpoint_pair_test.cc"],
external_deps = ["gtest"],
language = "C++",
tags = ["endpoint_test"],
deps = [
":endpoint_tests",
"//:gpr",
@ -241,7 +242,10 @@ grpc_cc_test(
srcs = ["tcp_client_posix_test.cc"],
external_deps = ["gtest"],
language = "C++",
tags = ["no_windows"],
tags = [
"endpoint_test",
"no_windows",
],
deps = [
"//:gpr",
"//:grpc",
@ -255,6 +259,7 @@ grpc_cc_test(
srcs = ["tcp_posix_test.cc"],
language = "C++",
tags = [
"endpoint_test",
"no_mac", # TODO(jtattermusch): Reenable once https://github.com/grpc/grpc/issues/21282 is fixed.
"no_windows",
],

@ -20,13 +20,13 @@
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "absl/debugging/failure_signal_handler.h"
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc.h>
@ -94,8 +94,9 @@ gpr_timespec grpc_timeout_milliseconds_to_deadline(int64_t time_ms) {
namespace {
void RmArg(int i, int* argc, char** argv) {
--(*argc);
if (i < *argc) {
memmove(argv + i, argv + i + 1, *argc - i);
while (i < *argc) {
argv[i] = argv[i + 1];
++i;
}
}
@ -104,6 +105,7 @@ void ParseTestArgs(int* argc, char** argv) {
// flags to look for and consume
const absl::string_view poller_flag{"--poller="};
const absl::string_view engine_flag{"--engine="};
const absl::string_view experiment_flag{"--experiment="};
int i = 1;
while (i < *argc) {
if (absl::StartsWith(argv[i], poller_flag)) {
@ -124,6 +126,15 @@ void ParseTestArgs(int* argc, char** argv) {
RmArg(i, argc, argv);
continue;
}
if (absl::StartsWith(argv[i], experiment_flag)) {
gpr_setenv(
absl::StrCat("GRPC_EXPERIMENT_", argv[i] + experiment_flag.length())
.c_str(),
"true");
// remove the spent argv
RmArg(i, argc, argv);
continue;
}
++i;
}
}

@ -0,0 +1,218 @@
#!/usr/bin/env python3
# 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.
"""
Generate experiment related code artifacts.
Invoke as: tools/codegen/core/gen_experiments.py
Experiment definitions are in src/core/lib/experiments/experiments.yaml
"""
from __future__ import print_function
import collections
import ctypes
import datetime
import json
import math
import os
import re
import sys
import yaml
with open('src/core/lib/experiments/experiments.yaml') as f:
attrs = yaml.load(f.read(), Loader=yaml.FullLoader)
error = False
today = datetime.date.today()
two_quarters_from_now = today + datetime.timedelta(days=180)
for attr in attrs:
if 'name' not in attr:
print("experiment with no name: %r" % attr)
error = True
continue # can't run other diagnostics because we don't know a name
if 'description' not in attr:
print("no description for experiment %s" % attr['name'])
error = True
if 'default' not in attr:
print("no default for experiment %s" % attr['name'])
error = True
if 'expiry' not in attr:
print("no expiry for experiment %s" % attr['name'])
error = True
if 'owner' not in attr:
print("no owner for experiment %s" % attr['name'])
error = True
expiry = datetime.datetime.strptime(attr['expiry'], '%Y/%m/%d').date()
if expiry < today:
print("experiment %s expired on %s" % (attr['name'], attr['expiry']))
error = True
if expiry > two_quarters_from_now:
print("experiment %s expires far in the future on %s" %
(attr['name'], attr['expiry']))
print("expiry should be no more than two quarters from now")
error = True
if error:
sys.exit(1)
def c_str(s, encoding='ascii'):
if isinstance(s, str):
s = s.encode(encoding)
result = ''
for c in s:
c = chr(c) if isinstance(c, int) else c
if not (32 <= ord(c) < 127) or c in ('\\', '"'):
result += '\\%03o' % ord(c)
else:
result += c
return '"' + result + '"'
def snake_to_pascal(s):
return ''.join(x.capitalize() for x in s.split('_'))
# utility: print a big comment block into a set of files
def put_banner(files, banner, prefix):
for f in files:
for line in banner:
print('%s %s' % (prefix, line), file=f)
print(file=f)
def put_copyright(file, prefix):
# copy-paste copyright notice from this file
with open(sys.argv[0]) as my_source:
copyright = []
for line in my_source:
if line[0] != '#':
break
for line in my_source:
if line[0] == '#':
copyright.append(line)
break
for line in my_source:
if line[0] != '#':
break
copyright.append(line)
put_banner([file], [line[2:].rstrip() for line in copyright], prefix)
EXPERIMENT_METADATA = """struct ExperimentMetadata {
const char* name;
const char* description;
bool default_value;
bool (*is_enabled)();
};"""
with open('src/core/lib/experiments/experiments.h', 'w') as H:
put_copyright(H, "//")
put_banner(
[H],
["Automatically generated by tools/codegen/core/gen_experiments.py"],
"//")
print("#ifndef GRPC_CORE_LIB_EXPERIMENTS_EXPERIMENTS_H", file=H)
print("#define GRPC_CORE_LIB_EXPERIMENTS_EXPERIMENTS_H", file=H)
print(file=H)
print("#include <grpc/support/port_platform.h>", file=H)
print(file=H)
print("#include <stddef.h>", file=H)
print(file=H)
print("namespace grpc_core {", file=H)
print(file=H)
for attr in attrs:
print("bool Is%sEnabled();" % snake_to_pascal(attr['name']), file=H)
print(file=H)
print(EXPERIMENT_METADATA, file=H)
print(file=H)
print("constexpr const size_t kNumExperiments = %d;" % len(attrs), file=H)
print(
"extern const ExperimentMetadata g_experiment_metadata[kNumExperiments];",
file=H)
print(file=H)
print("} // namespace grpc_core", file=H)
print(file=H)
print("#endif // GRPC_CORE_LIB_EXPERIMENTS_EXPERIMENTS_H", file=H)
with open('src/core/lib/experiments/experiments.cc', 'w') as C:
put_copyright(C, "//")
put_banner(
[C],
["Automatically generated by tools/codegen/core/gen_experiments.py"],
"//")
print("#include <grpc/support/port_platform.h>", file=C)
print("#include \"src/core/lib/experiments/experiments.h\"", file=C)
print("#include \"src/core/lib/gprpp/global_config.h\"", file=C)
print(file=C)
print("namespace {", file=C)
for attr in attrs:
print("const char* const description_%s = %s;" %
(attr['name'], c_str(attr['description'])),
file=C)
print("}", file=C)
print(file=C)
for attr in attrs:
print(
"GPR_GLOBAL_CONFIG_DEFINE_BOOL(grpc_experimental_enable_%s, %s, description_%s);"
% (attr['name'], 'true' if attr['default'] else 'false',
attr['name']),
file=C)
print(file=C)
print("namespace grpc_core {", file=C)
print(file=C)
for attr in attrs:
print("bool Is%sEnabled() {" % snake_to_pascal(attr['name']), file=C)
print(
" static const bool enabled = GPR_GLOBAL_CONFIG_GET(grpc_experimental_enable_%s);"
% attr['name'],
file=C)
print(" return enabled;", file=C)
print("}", file=C)
print(file=C)
print("const ExperimentMetadata g_experiment_metadata[] = {", file=C)
for attr in attrs:
print(" {%s, description_%s, %s, Is%sEnabled}," %
(c_str(attr['name']), attr['name'], 'true'
if attr['default'] else 'false', snake_to_pascal(attr['name'])),
file=C)
print("};", file=C)
print(file=C)
print("} // namespace grpc_core", file=C)
tags_to_experiments = collections.defaultdict(list)
for attr in attrs:
for tag in attr['test_tags']:
tags_to_experiments[tag].append(attr['name'])
with open('bazel/experiments.bzl', 'w') as B:
put_copyright(B, "#")
put_banner(
[B],
["Automatically generated by tools/codegen/core/gen_experiments.py"],
"#")
print(
"\"\"\"Dictionary of tags to experiments so we know when to test different experiments.\"\"\"",
file=B)
print("EXPERIMENTS=%r" % dict(tags_to_experiments), file=B)

@ -1982,6 +1982,8 @@ src/core/lib/event_engine/windows/win_socket.cc \
src/core/lib/event_engine/windows/win_socket.h \
src/core/lib/event_engine/windows/windows_engine.cc \
src/core/lib/event_engine/windows/windows_engine.h \
src/core/lib/experiments/experiments.cc \
src/core/lib/experiments/experiments.h \
src/core/lib/gpr/alloc.cc \
src/core/lib/gpr/alloc.h \
src/core/lib/gpr/atm.cc \

@ -1772,6 +1772,8 @@ src/core/lib/event_engine/windows/win_socket.cc \
src/core/lib/event_engine/windows/win_socket.h \
src/core/lib/event_engine/windows/windows_engine.cc \
src/core/lib/event_engine/windows/windows_engine.h \
src/core/lib/experiments/experiments.cc \
src/core/lib/experiments/experiments.h \
src/core/lib/gpr/README.md \
src/core/lib/gpr/alloc.cc \
src/core/lib/gpr/alloc.h \

@ -33,4 +33,4 @@ bazel_rbe/bazel_wrapper \
test \
$BAZEL_FLAGS \
"$@" \
-- //test/...
-- ${BAZEL_TESTS:-//test/...}

Loading…
Cancel
Save