diff --git a/BUILD b/BUILD index 7121bc0b7c4..b8e53145408 100644 --- a/BUILD +++ b/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", diff --git a/CMakeLists.txt b/CMakeLists.txt index 00ad1a5ab08..d541b06ff44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Makefile b/Makefile index 802119b0d03..77b17125ace 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/bazel/experiments.bzl b/bazel/experiments.bzl new file mode 100644 index 00000000000..e77ee6a1aad --- /dev/null +++ b/bazel/experiments.bzl @@ -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"]} diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl index 760ece9232d..4c59d5aa4f1 100644 --- a/bazel/grpc_build_system.bzl +++ b/bazel/grpc_build_system.bzl @@ -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"], diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index e242db4f9bf..dd38fab1698 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -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 diff --git a/config.m4 b/config.m4 index 13bc9665481..3994fb80f4b 100644 --- a/config.m4 +++ b/config.m4 @@ -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) diff --git a/config.w32 b/config.w32 index 9b4ae930689..32847fe0417 100644 --- a/config.w32 +++ b/config.w32 @@ -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"); diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index fb683eabd54..1cade5b9012 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -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', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 060736e0482..10c883785ca 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -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', diff --git a/grpc.gemspec b/grpc.gemspec index d30e2621ec1..22583fd3288 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -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 ) diff --git a/grpc.gyp b/grpc.gyp index be5f96a09e6..a173a35b0bd 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -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', diff --git a/package.xml b/package.xml index e2ffa036c0b..4c04a727a28 100644 --- a/package.xml +++ b/package.xml @@ -981,6 +981,8 @@ + + diff --git a/src/core/lib/experiments/experiments.cc b/src/core/lib/experiments/experiments.cc new file mode 100644 index 00000000000..34eb7a2ff42 --- /dev/null +++ b/src/core/lib/experiments/experiments.cc @@ -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 + +#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 diff --git a/src/core/lib/experiments/experiments.h b/src/core/lib/experiments/experiments.h new file mode 100644 index 00000000000..c955ea63f34 --- /dev/null +++ b/src/core/lib/experiments/experiments.h @@ -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 + +#include + +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 diff --git a/src/core/lib/experiments/experiments.yaml b/src/core/lib/experiments/experiments.yaml new file mode 100644 index 00000000000..7c5ec44d1c1 --- /dev/null +++ b/src/core/lib/experiments/experiments.yaml @@ -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"] diff --git a/src/core/lib/iomgr/iomgr.cc b/src/core/lib/iomgr/iomgr.cc index 777c03c319a..8a9eadd18af 100644 --- a/src/core/lib/iomgr/iomgr.cc +++ b/src/core/lib/iomgr/iomgr.cc @@ -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; diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 09c23d9b193..de8bb36b020 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -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 diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 63a873bf72c..8162829ff14 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -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', diff --git a/test/core/end2end/generate_tests.bzl b/test/core/end2end/generate_tests.bzl index 5b491e6f86e..249e98b1859 100755 --- a/test/core/end2end/generate_tests.bzl +++ b/test/core/end2end/generate_tests.bzl @@ -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, diff --git a/test/core/iomgr/BUILD b/test/core/iomgr/BUILD index 05a9dedb43a..7bc51281b09 100644 --- a/test/core/iomgr/BUILD +++ b/test/core/iomgr/BUILD @@ -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", ], diff --git a/test/core/util/test_config.cc b/test/core/util/test_config.cc index 997c5f3b932..f6a5e09b679 100644 --- a/test/core/util/test_config.cc +++ b/test/core/util/test_config.cc @@ -20,13 +20,13 @@ #include #include -#include #include #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 @@ -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; } } diff --git a/tools/codegen/core/gen_experiments.py b/tools/codegen/core/gen_experiments.py new file mode 100755 index 00000000000..e2edb4c6316 --- /dev/null +++ b/tools/codegen/core/gen_experiments.py @@ -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 ", file=H) + print(file=H) + print("#include ", 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 ", 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) diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 142f00f13da..cdc7f4551f6 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -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 \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 4afc526927d..cb15c9afefd 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -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 \ diff --git a/tools/internal_ci/linux/grpc_bazel_rbe.sh b/tools/internal_ci/linux/grpc_bazel_rbe.sh index c7c7f3bac8e..f8f031fa5e0 100755 --- a/tools/internal_ci/linux/grpc_bazel_rbe.sh +++ b/tools/internal_ci/linux/grpc_bazel_rbe.sh @@ -33,4 +33,4 @@ bazel_rbe/bazel_wrapper \ test \ $BAZEL_FLAGS \ "$@" \ - -- //test/... + -- ${BAZEL_TESTS:-//test/...}