diff options
176 files changed, 4577 insertions, 1811 deletions
@@ -485,10 +485,7 @@ grpc_cc_library( grpc_cc_library( name = "census", srcs = [ - "src/core/ext/census/grpc_context.cc", - ], - external_deps = [ - "nanopb", + "src/cpp/ext/filters/census/grpc_context.cc", ], language = "c++", public_hdrs = [ @@ -1335,6 +1332,7 @@ grpc_cc_library( name = "grpc_resolver_dns_ares", srcs = [ "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc", @@ -1991,4 +1989,40 @@ grpc_cc_library( ], ) +grpc_cc_library( + name = "grpc_opencensus_plugin", + srcs = [ + "src/cpp/ext/filters/census/client_filter.cc", + "src/cpp/ext/filters/census/server_filter.cc", + "src/cpp/ext/filters/census/channel_filter.cc", + "src/cpp/ext/filters/census/context.cc", + "src/cpp/ext/filters/census/grpc_context.cc", + "src/cpp/ext/filters/census/grpc_plugin.cc", + "src/cpp/ext/filters/census/measures.cc", + "src/cpp/ext/filters/census/rpc_encoding.cc", + "src/cpp/ext/filters/census/views.cc", + ], + hdrs = [ + "include/grpcpp/opencensus.h", + "src/cpp/ext/filters/census/client_filter.h", + "src/cpp/ext/filters/census/server_filter.h", + "src/cpp/ext/filters/census/channel_filter.h", + "src/cpp/ext/filters/census/context.h", + "src/cpp/ext/filters/census/grpc_plugin.h", + "src/cpp/ext/filters/census/measures.h", + "src/cpp/ext/filters/census/rpc_encoding.h", + ], + language = "c++", + external_deps = [ + "absl-base", + "absl-time", + "opencensus-trace", + "opencensus-stats", + ], + deps = [ + ":census", + ":grpc++", + ], +) + grpc_generate_one_off_targets() diff --git a/CMakeLists.txt b/CMakeLists.txt index a875b8c73e..9685847351 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -309,6 +309,9 @@ endif() if(_gRPC_PLATFORM_LINUX) add_dependencies(buildtests_c handshake_server_with_readahead_handshaker) endif() +if(_gRPC_PLATFORM_LINUX) +add_dependencies(buildtests_c handshake_verify_peer_options) +endif() add_dependencies(buildtests_c histogram_test) add_dependencies(buildtests_c hpack_parser_test) add_dependencies(buildtests_c hpack_table_test) @@ -1215,6 +1218,7 @@ add_library(grpc src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc @@ -1222,7 +1226,7 @@ add_library(grpc src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc src/core/ext/filters/load_reporting/server_load_reporting_filter.cc src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc - src/core/ext/census/grpc_context.cc + src/cpp/ext/filters/census/grpc_context.cc src/core/ext/filters/max_age/max_age_filter.cc src/core/ext/filters/message_size/message_size_filter.cc src/core/ext/filters/http/client_authority_filter.cc @@ -2509,6 +2513,7 @@ add_library(grpc_unsecure src/core/ext/transport/inproc/inproc_plugin.cc src/core/ext/transport/inproc/inproc_transport.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc @@ -2528,7 +2533,7 @@ add_library(grpc_unsecure third_party/nanopb/pb_encode.c src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc - src/core/ext/census/grpc_context.cc + src/cpp/ext/filters/census/grpc_context.cc src/core/ext/filters/max_age/max_age_filter.cc src/core/ext/filters/message_size/message_size_filter.cc src/core/ext/filters/http/client_authority_filter.cc @@ -3312,10 +3317,7 @@ add_library(grpc++_cronet src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc src/core/ext/transport/chttp2/server/chttp2_server.cc - src/core/ext/census/grpc_context.cc - third_party/nanopb/pb_common.c - third_party/nanopb/pb_decode.c - third_party/nanopb/pb_encode.c + src/cpp/ext/filters/census/grpc_context.cc ) if(WIN32 AND MSVC) @@ -7501,6 +7503,37 @@ target_link_libraries(handshake_server_with_readahead_handshaker endif() endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX) + +add_executable(handshake_verify_peer_options + test/core/handshake/verify_peer_options.cc +) + + +target_include_directories(handshake_verify_peer_options + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${_gRPC_SSL_INCLUDE_DIR} + PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR} + PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR} + PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR} + PRIVATE ${_gRPC_CARES_INCLUDE_DIR} + PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR} + PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} +) + +target_link_libraries(handshake_verify_peer_options + ${_gRPC_SSL_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc + gpr_test_util + gpr +) + +endif() +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) add_executable(histogram_test test/core/util/histogram_test.cc @@ -1012,6 +1012,7 @@ grpc_verify_jwt: $(BINDIR)/$(CONFIG)/grpc_verify_jwt handshake_client: $(BINDIR)/$(CONFIG)/handshake_client handshake_server: $(BINDIR)/$(CONFIG)/handshake_server handshake_server_with_readahead_handshaker: $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker +handshake_verify_peer_options: $(BINDIR)/$(CONFIG)/handshake_verify_peer_options histogram_test: $(BINDIR)/$(CONFIG)/histogram_test hpack_parser_fuzzer_test: $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test hpack_parser_test: $(BINDIR)/$(CONFIG)/hpack_parser_test @@ -1455,6 +1456,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/handshake_client \ $(BINDIR)/$(CONFIG)/handshake_server \ $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker \ + $(BINDIR)/$(CONFIG)/handshake_verify_peer_options \ $(BINDIR)/$(CONFIG)/histogram_test \ $(BINDIR)/$(CONFIG)/hpack_parser_test \ $(BINDIR)/$(CONFIG)/hpack_table_test \ @@ -2012,6 +2014,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/handshake_server || ( echo test handshake_server failed ; exit 1 ) $(E) "[RUN] Testing handshake_server_with_readahead_handshaker" $(Q) $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker || ( echo test handshake_server_with_readahead_handshaker failed ; exit 1 ) + $(E) "[RUN] Testing handshake_verify_peer_options" + $(Q) $(BINDIR)/$(CONFIG)/handshake_verify_peer_options || ( echo test handshake_verify_peer_options failed ; exit 1 ) $(E) "[RUN] Testing histogram_test" $(Q) $(BINDIR)/$(CONFIG)/histogram_test || ( echo test histogram_test failed ; exit 1 ) $(E) "[RUN] Testing hpack_parser_test" @@ -3594,6 +3598,7 @@ LIBGRPC_SRC = \ src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \ @@ -3601,7 +3606,7 @@ LIBGRPC_SRC = \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ - src/core/ext/census/grpc_context.cc \ + src/cpp/ext/filters/census/grpc_context.cc \ src/core/ext/filters/max_age/max_age_filter.cc \ src/core/ext/filters/message_size/message_size_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ @@ -4854,6 +4859,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/transport/inproc/inproc_plugin.cc \ src/core/ext/transport/inproc/inproc_transport.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \ @@ -4873,7 +4879,7 @@ LIBGRPC_UNSECURE_SRC = \ third_party/nanopb/pb_encode.c \ src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ - src/core/ext/census/grpc_context.cc \ + src/cpp/ext/filters/census/grpc_context.cc \ src/core/ext/filters/max_age/max_age_filter.cc \ src/core/ext/filters/message_size/message_size_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ @@ -5645,10 +5651,7 @@ LIBGRPC++_CRONET_SRC = \ src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \ src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \ src/core/ext/transport/chttp2/server/chttp2_server.cc \ - src/core/ext/census/grpc_context.cc \ - third_party/nanopb/pb_common.c \ - third_party/nanopb/pb_decode.c \ - third_party/nanopb/pb_encode.c \ + src/cpp/ext/filters/census/grpc_context.cc \ PUBLIC_HEADERS_CXX += \ include/grpc++/alarm.h \ @@ -12483,6 +12486,38 @@ endif endif +HANDSHAKE_VERIFY_PEER_OPTIONS_SRC = \ + test/core/handshake/verify_peer_options.cc \ + +HANDSHAKE_VERIFY_PEER_OPTIONS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_VERIFY_PEER_OPTIONS_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/handshake_verify_peer_options: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/handshake_verify_peer_options: $(HANDSHAKE_VERIFY_PEER_OPTIONS_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HANDSHAKE_VERIFY_PEER_OPTIONS_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/handshake_verify_peer_options + +endif + +$(OBJDIR)/$(CONFIG)/test/core/handshake/verify_peer_options.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_handshake_verify_peer_options: $(HANDSHAKE_VERIFY_PEER_OPTIONS_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HANDSHAKE_VERIFY_PEER_OPTIONS_OBJS:.o=.dep) +endif +endif + + HISTOGRAM_TEST_SRC = \ test/core/util/histogram_test.cc \ diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl index 0a1edeb31c..73147bf3ac 100644 --- a/bazel/grpc_build_system.bzl +++ b/bazel/grpc_build_system.bzl @@ -60,7 +60,7 @@ def _maybe_update_cc_library_hdrs(hdrs): def grpc_cc_library(name, srcs = [], public_hdrs = [], hdrs = [], external_deps = [], deps = [], standalone = False, language = "C++", testonly = False, visibility = None, - alwayslink = 0): + alwayslink = 0, data = []): copts = [] if language.upper() == "C": copts = if_not_windows(["-std=c99"]) @@ -85,6 +85,7 @@ def grpc_cc_library(name, srcs = [], public_hdrs = [], hdrs = [], "include" ], alwayslink = alwayslink, + data = data, ) def grpc_proto_plugin(name, srcs = [], deps = []): diff --git a/bazel/grpc_deps.bzl b/bazel/grpc_deps.bzl index f7e62f1219..7c349dbcdc 100644 --- a/bazel/grpc_deps.bzl +++ b/bazel/grpc_deps.bzl @@ -9,6 +9,16 @@ def grpc_deps(): ) native.bind( + name = "absl-base", + actual = "@com_google_absl//absl/base", + ) + + native.bind( + name = "absl-time", + actual = "@com_google_absl//absl/time:time", + ) + + native.bind( name = "libssl", actual = "@boringssl//:ssl", ) @@ -73,6 +83,21 @@ def grpc_deps(): actual = "@com_github_grpc_grpc//:grpc++_codegen_proto", ) + native.bind( + name = "opencensus-trace", + actual = "@io_opencensus_cpp//opencensus/trace:trace" + ) + + native.bind( + name = "opencensus-stats", + actual = "@io_opencensus_cpp//opencensus/stats:stats" + ) + + native.bind( + name = "opencensus-stats-test", + actual = "@io_opencensus_cpp//opencensus/stats:test_utils" + ) + if "boringssl" not in native.existing_rules(): native.http_archive( name = "boringssl", @@ -122,8 +147,8 @@ def grpc_deps(): native.new_http_archive( name = "com_github_google_benchmark", build_file = "@com_github_grpc_grpc//third_party:benchmark.BUILD", - strip_prefix = "benchmark-5b7683f49e1e9223cf9927b24f6fd3d6bd82e3f8", - url = "https://github.com/google/benchmark/archive/5b7683f49e1e9223cf9927b24f6fd3d6bd82e3f8.tar.gz", + strip_prefix = "benchmark-9913418d323e64a0111ca0da81388260c2bbe1e9", + url = "https://github.com/google/benchmark/archive/9913418d323e64a0111ca0da81388260c2bbe1e9.tar.gz", ) if "com_github_cares_cares" not in native.existing_rules(): @@ -137,8 +162,8 @@ def grpc_deps(): if "com_google_absl" not in native.existing_rules(): native.http_archive( name = "com_google_absl", - strip_prefix = "abseil-cpp-cc4bed2d74f7c8717e31f9579214ab52a9c9c610", - url = "https://github.com/abseil/abseil-cpp/archive/cc4bed2d74f7c8717e31f9579214ab52a9c9c610.tar.gz", + strip_prefix = "abseil-cpp-cd95e71df6eaf8f2a282b1da556c2cf1c9b09207", + url = "https://github.com/abseil/abseil-cpp/archive/cd95e71df6eaf8f2a282b1da556c2cf1c9b09207.tar.gz", ) if "com_github_bazelbuild_bazeltoolchains" not in native.existing_rules(): @@ -152,6 +177,14 @@ def grpc_deps(): sha256 = "1c4a532b396c698e6467a1548554571cb85fa091e472b05e398ebc836c315d77", ) + if "io_opencensus_cpp" not in native.existing_rules(): + native.http_archive( + name = "io_opencensus_cpp", + strip_prefix = "opencensus-cpp-fdf0f308b1631bb4a942e32ba5d22536a6170274", + url = "https://github.com/census-instrumentation/opencensus-cpp/archive/fdf0f308b1631bb4a942e32ba5d22536a6170274.tar.gz", + ) + + # TODO: move some dependencies from "grpc_deps" here? def grpc_test_only_deps(): """Internal, not intended for use by packages that are consuming grpc. diff --git a/build.yaml b/build.yaml index 2078ef5911..aacb99aaba 100644 --- a/build.yaml +++ b/build.yaml @@ -100,10 +100,9 @@ filegroups: public_headers: - include/grpc/census.h src: - - src/core/ext/census/grpc_context.cc + - src/cpp/ext/filters/census/grpc_context.cc uses: - grpc_base - - nanopb - name: cmdline headers: - test/core/util/cmdline.h @@ -727,6 +726,7 @@ filegroups: - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h src: - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc + - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc @@ -2794,6 +2794,21 @@ targets: platforms: - linux secure: true +- name: handshake_verify_peer_options + build: test + language: c + src: + - test/core/handshake/verify_peer_options.cc + deps: + - grpc_test_util + - grpc + - gpr_test_util + - gpr + exclude_iomgrs: + - uv + platforms: + - linux + secure: true - name: histogram_test build: test language: c @@ -5512,7 +5527,7 @@ configs: compile_the_world: true test_environ: ASAN_OPTIONS: detect_leaks=1:color=always - LSAN_OPTIONS: suppressions=tools/lsan_suppressions.txt:report_objects=1 + LSAN_OPTIONS: suppressions=test/core/util/lsan_suppressions.txt:report_objects=1 asan-noleaks: CC: clang CPPFLAGS: -O0 -fsanitize-coverage=edge,trace-pc-guard -fsanitize=address -fno-omit-frame-pointer @@ -5536,7 +5551,7 @@ configs: compile_the_world: true test_environ: ASAN_OPTIONS: detect_leaks=1:color=always - LSAN_OPTIONS: suppressions=tools/lsan_suppressions.txt:report_objects=1 + LSAN_OPTIONS: suppressions=test/core/util/lsan_suppressions.txt:report_objects=1 basicprof: CPPFLAGS: -O2 -DGRPC_BASIC_PROFILER -DGRPC_TIMERS_RDTSC DEFINES: NDEBUG @@ -5610,7 +5625,7 @@ configs: LDXX: clang++ compile_the_world: true test_environ: - TSAN_OPTIONS: suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1 + TSAN_OPTIONS: suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1 ubsan: CC: clang CPPFLAGS: -O0 -fsanitize-coverage=edge,trace-pc-guard -fsanitize=undefined -fno-omit-frame-pointer @@ -5622,7 +5637,7 @@ configs: LDXX: clang++ compile_the_world: true test_environ: - UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1:suppressions=tools/ubsan_suppressions.txt + UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1:suppressions=test/core/util/ubsan_suppressions.txt defaults: ares: CFLAGS: -Wno-sign-conversion $(if $(subst Darwin,,$(SYSTEM)),,-Wno-shorten-64-to-32) @@ -371,6 +371,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \ @@ -378,7 +379,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ - src/core/ext/census/grpc_context.cc \ + src/cpp/ext/filters/census/grpc_context.cc \ src/core/ext/filters/max_age/max_age_filter.cc \ src/core/ext/filters/message_size/message_size_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ @@ -649,7 +650,6 @@ if test "$PHP_GRPC" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/php/ext/grpc) PHP_ADD_BUILD_DIR($ext_builddir/src/boringssl) - PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/census) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/lb_policy/grpclb) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1) @@ -712,6 +712,7 @@ if test "$PHP_GRPC" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/handshaker) PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/zero_copy_frame_protector) PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/ssl/session_cache) + PHP_ADD_BUILD_DIR($ext_builddir/src/cpp/ext/filters/census) PHP_ADD_BUILD_DIR($ext_builddir/third_party/address_sorting) PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto) PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto/asn1) diff --git a/config.w32 b/config.w32 index b4311cb6d1..e960c283b3 100644 --- a/config.w32 +++ b/config.w32 @@ -347,6 +347,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\client_channel\\lb_policy\\pick_first\\pick_first.cc " + "src\\core\\ext\\filters\\client_channel\\lb_policy\\round_robin\\round_robin.cc " + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\dns_resolver_ares.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver.cc " + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_posix.cc " + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper.cc " + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_fallback.cc " + @@ -354,7 +355,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\client_channel\\resolver\\sockaddr\\sockaddr_resolver.cc " + "src\\core\\ext\\filters\\load_reporting\\server_load_reporting_filter.cc " + "src\\core\\ext\\filters\\load_reporting\\server_load_reporting_plugin.cc " + - "src\\core\\ext\\census\\grpc_context.cc " + + "src\\cpp\\ext\\filters\\census\\grpc_context.cc " + "src\\core\\ext\\filters\\max_age\\max_age_filter.cc " + "src\\core\\ext\\filters\\message_size\\message_size_filter.cc " + "src\\core\\ext\\filters\\http\\client_authority_filter.cc " + @@ -653,7 +654,6 @@ if (PHP_GRPC != "no") { FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\boringssl"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext"); - FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\census"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\client_channel"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\client_channel\\lb_policy"); @@ -729,6 +729,10 @@ if (PHP_GRPC != "no") { FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\zero_copy_frame_protector"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\ssl"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\ssl\\session_cache"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\cpp"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\cpp\\ext"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\cpp\\ext\\filters"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\cpp\\ext\\filters\\census"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php\\ext"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php\\ext\\grpc"); diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 251a7f32ed..89ddf20efb 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -787,6 +787,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', @@ -794,7 +795,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', - 'src/core/ext/census/grpc_context.cc', + 'src/cpp/ext/filters/census/grpc_context.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', diff --git a/grpc.gemspec b/grpc.gemspec index cf0e308145..3b48967cba 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -727,6 +727,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc ) s.files += %w( src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc ) s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc ) s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc ) s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc ) s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc ) @@ -734,7 +735,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc ) s.files += %w( src/core/ext/filters/load_reporting/server_load_reporting_filter.cc ) s.files += %w( src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc ) - s.files += %w( src/core/ext/census/grpc_context.cc ) + s.files += %w( src/cpp/ext/filters/census/grpc_context.cc ) s.files += %w( src/core/ext/filters/max_age/max_age_filter.cc ) s.files += %w( src/core/ext/filters/message_size/message_size_filter.cc ) s.files += %w( src/core/ext/filters/http/client_authority_filter.cc ) @@ -77,6 +77,7 @@ '.', '../..', 'include', + '../../third_party/nanopb', ], 'defines': [ 'GRPC_ARES=0', @@ -537,6 +538,7 @@ 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', @@ -544,7 +546,7 @@ 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', - 'src/core/ext/census/grpc_context.cc', + 'src/cpp/ext/filters/census/grpc_context.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', @@ -1250,6 +1252,7 @@ 'src/core/ext/transport/inproc/inproc_plugin.cc', 'src/core/ext/transport/inproc/inproc_transport.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', @@ -1269,7 +1272,7 @@ 'third_party/nanopb/pb_encode.c', 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', - 'src/core/ext/census/grpc_context.cc', + 'src/cpp/ext/filters/census/grpc_context.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index e1975a8e09..b34fb70898 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -163,6 +163,26 @@ typedef struct { const char* cert_chain; } grpc_ssl_pem_key_cert_pair; +/** Object that holds additional peer-verification options on a secure + channel. */ +typedef struct { + /** If non-NULL this callback will be invoked with the expected + target_name, the peer's certificate (in PEM format), and whatever + userdata pointer is set below. If a non-zero value is returned by this + callback then it is treated as a verification failure. Invocation of + the callback is blocking, so any implementation should be light-weight. + */ + int (*verify_peer_callback)(const char* target_name, const char* peer_pem, + void* userdata); + /** Arbitrary userdata that will be passed as the last argument to + verify_peer_callback. */ + void* verify_peer_callback_userdata; + /** A destruct callback that will be invoked when the channel is being + cleaned up. The userdata argument will be passed to it. The intent is + to perform any cleanup associated with that userdata. */ + void (*verify_peer_destruct)(void* userdata); +} verify_peer_options; + /** Creates an SSL credentials object. - pem_root_certs is the NULL-terminated string containing the PEM encoding of the server root certificates. If this parameter is NULL, the @@ -173,10 +193,17 @@ typedef struct { disk (in the grpc install directory). - pem_key_cert_pair is a pointer on the object containing client's private key and certificate chain. This parameter can be NULL if the client does - not have such a key/cert pair. */ + not have such a key/cert pair. + - verify_options is an optional verify_peer_options object which holds + additional options controlling how peer certificates are verified. For + example, you can supply a callback which receives the peer's certificate + with which you can do additional verification. Can be NULL, in which + case verification will retain default behavior. Any settings in + verify_options are copied during this call, so the verify_options + object can be released afterwards. */ GRPCAPI grpc_channel_credentials* grpc_ssl_credentials_create( const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, - void* reserved); + const verify_peer_options* verify_options, void* reserved); /** --- grpc_call_credentials object. diff --git a/include/grpcpp/opencensus.h b/include/grpcpp/opencensus.h new file mode 100644 index 0000000000..7e5d1dfeb4 --- /dev/null +++ b/include/grpcpp/opencensus.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPCPP_OPENCENSUS_H +#define GRPCPP_OPENCENSUS_H + +namespace grpc { +// These symbols in this file will not be included in the binary unless +// grpc_opencensus_plugin build target was added as a dependency. At the moment +// it is only setup to be built with Bazel. + +// Registers the OpenCensus plugin with gRPC, so that it will be used for future +// RPCs. This must be called before any views are created. +void RegisterOpenCensusPlugin(); + +// RPC stats definitions, defined by +// https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/gRPC.md + +// Registers the cumulative gRPC views so that they will be exported by any +// registered stats exporter. For on-task stats, construct a View using the +// ViewDescriptors below. +void RegisterOpenCensusViewsForExport(); + +} // namespace grpc + +#endif // GRPCPP_OPENCENSUS_H diff --git a/package.xml b/package.xml index 5557fad8e5..29f6f0147a 100644 --- a/package.xml +++ b/package.xml @@ -732,6 +732,7 @@ <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc" role="src" /> + <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc" role="src" /> @@ -739,7 +740,7 @@ <file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/load_reporting/server_load_reporting_filter.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc" role="src" /> - <file baseinstalldir="/" name="src/core/ext/census/grpc_context.cc" role="src" /> + <file baseinstalldir="/" name="src/cpp/ext/filters/census/grpc_context.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/max_age/max_age_filter.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/message_size/message_size_filter.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/filters/http/client_authority_filter.cc" role="src" /> diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index ea6775a8d8..34ea97e23e 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -817,6 +817,7 @@ typedef struct { // For intercepting recv_trailing_metadata. grpc_metadata_batch recv_trailing_metadata; grpc_transport_stream_stats collect_stats; + grpc_closure recv_trailing_metadata_ready; // For intercepting on_complete. grpc_closure on_complete; } subchannel_batch_data; @@ -1192,35 +1193,24 @@ static void pending_batches_fail(grpc_call_element* elem, grpc_error* error, "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s", elem->channel_data, calld, num_batches, grpc_error_string(error)); } - grpc_transport_stream_op_batch* - batches[GPR_ARRAY_SIZE(calld->pending_batches)]; - size_t num_batches = 0; + grpc_core::CallCombinerClosureList closures; for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { pending_batch* pending = &calld->pending_batches[i]; grpc_transport_stream_op_batch* batch = pending->batch; if (batch != nullptr) { - batches[num_batches++] = batch; + batch->handler_private.extra_arg = calld; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, + fail_pending_batch_in_call_combiner, batch, + grpc_schedule_on_exec_ctx); + closures.Add(&batch->handler_private.closure, GRPC_ERROR_REF(error), + "pending_batches_fail"); pending_batch_clear(calld, pending); } } - for (size_t i = yield_call_combiner ? 1 : 0; i < num_batches; ++i) { - grpc_transport_stream_op_batch* batch = batches[i]; - batch->handler_private.extra_arg = calld; - GRPC_CLOSURE_INIT(&batch->handler_private.closure, - fail_pending_batch_in_call_combiner, batch, - grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START(calld->call_combiner, - &batch->handler_private.closure, - GRPC_ERROR_REF(error), "pending_batches_fail"); - } if (yield_call_combiner) { - if (num_batches > 0) { - // Note: This will release the call combiner. - grpc_transport_stream_op_batch_finish_with_failure( - batches[0], GRPC_ERROR_REF(error), calld->call_combiner); - } else { - GRPC_CALL_COMBINER_STOP(calld->call_combiner, "pending_batches_fail"); - } + closures.RunClosures(calld->call_combiner); + } else { + closures.RunClosuresWithoutYielding(calld->call_combiner); } GRPC_ERROR_UNREF(error); } @@ -1255,30 +1245,22 @@ static void pending_batches_resume(grpc_call_element* elem) { " pending batches on subchannel_call=%p", chand, calld, num_batches, calld->subchannel_call); } - grpc_transport_stream_op_batch* - batches[GPR_ARRAY_SIZE(calld->pending_batches)]; - size_t num_batches = 0; + grpc_core::CallCombinerClosureList closures; for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { pending_batch* pending = &calld->pending_batches[i]; grpc_transport_stream_op_batch* batch = pending->batch; if (batch != nullptr) { - batches[num_batches++] = batch; + batch->handler_private.extra_arg = calld->subchannel_call; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, + resume_pending_batch_in_call_combiner, batch, + grpc_schedule_on_exec_ctx); + closures.Add(&batch->handler_private.closure, GRPC_ERROR_NONE, + "pending_batches_resume"); pending_batch_clear(calld, pending); } } - for (size_t i = 1; i < num_batches; ++i) { - grpc_transport_stream_op_batch* batch = batches[i]; - batch->handler_private.extra_arg = calld->subchannel_call; - GRPC_CLOSURE_INIT(&batch->handler_private.closure, - resume_pending_batch_in_call_combiner, batch, - grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START(calld->call_combiner, - &batch->handler_private.closure, GRPC_ERROR_NONE, - "pending_batches_resume"); - } - GPR_ASSERT(num_batches > 0); // Note: This will release the call combiner. - grpc_subchannel_call_process_op(calld->subchannel_call, batches[0]); + closures.RunClosures(calld->call_combiner); } static void maybe_clear_pending_batch(grpc_call_element* elem, @@ -1293,7 +1275,10 @@ static void maybe_clear_pending_batch(grpc_call_element* elem, batch->payload->recv_initial_metadata.recv_initial_metadata_ready == nullptr) && (!batch->recv_message || - batch->payload->recv_message.recv_message_ready == nullptr)) { + batch->payload->recv_message.recv_message_ready == nullptr) && + (!batch->recv_trailing_metadata || + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready == + nullptr)) { if (grpc_client_channel_trace.enabled()) { gpr_log(GPR_INFO, "chand=%p calld=%p: clearing pending batch", chand, calld); @@ -1302,75 +1287,27 @@ static void maybe_clear_pending_batch(grpc_call_element* elem, } } -// Returns true if all ops in the pending batch have been completed. -static bool pending_batch_is_completed( - pending_batch* pending, call_data* calld, - subchannel_call_retry_state* retry_state) { - if (pending->batch == nullptr || pending->batch->on_complete == nullptr) { - return false; - } - if (pending->batch->send_initial_metadata && - !retry_state->completed_send_initial_metadata) { - return false; - } - if (pending->batch->send_message && - retry_state->completed_send_message_count < - calld->send_messages->size()) { - return false; - } - if (pending->batch->send_trailing_metadata && - !retry_state->completed_send_trailing_metadata) { - return false; - } - if (pending->batch->recv_initial_metadata && - !retry_state->completed_recv_initial_metadata) { - return false; - } - if (pending->batch->recv_message && - retry_state->completed_recv_message_count < - retry_state->started_recv_message_count) { - return false; - } - if (pending->batch->recv_trailing_metadata && - !retry_state->completed_recv_trailing_metadata) { - return false; - } - return true; -} - -// Returns true if any op in the batch was not yet started. -static bool pending_batch_is_unstarted( - pending_batch* pending, call_data* calld, - subchannel_call_retry_state* retry_state) { - if (pending->batch == nullptr || pending->batch->on_complete == nullptr) { - return false; - } - if (pending->batch->send_initial_metadata && - !retry_state->started_send_initial_metadata) { - return true; - } - if (pending->batch->send_message && - retry_state->started_send_message_count < calld->send_messages->size()) { - return true; - } - if (pending->batch->send_trailing_metadata && - !retry_state->started_send_trailing_metadata) { - return true; - } - if (pending->batch->recv_initial_metadata && - !retry_state->started_recv_initial_metadata) { - return true; - } - if (pending->batch->recv_message && - retry_state->completed_recv_message_count == - retry_state->started_recv_message_count) { - return true; - } - if (pending->batch->recv_trailing_metadata && - !retry_state->started_recv_trailing_metadata) { - return true; +// Returns a pointer to the first pending batch for which predicate(batch) +// returns true, or null if not found. +template <typename Predicate> +static pending_batch* pending_batch_find(grpc_call_element* elem, + const char* log_message, + Predicate predicate) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch != nullptr && predicate(batch)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: %s pending batch at index %" PRIuPTR, chand, + calld, log_message, i); + } + return pending; + } } - return false; + return nullptr; } // @@ -1557,8 +1494,13 @@ static bool maybe_retry(grpc_call_element* elem, // subchannel_batch_data // +// Creates a subchannel_batch_data object on the call's arena with the +// specified refcount. If set_on_complete is true, the batch's +// on_complete callback will be set to point to on_complete(); +// otherwise, the batch's on_complete callback will be null. static subchannel_batch_data* batch_data_create(grpc_call_element* elem, - int refcount) { + int refcount, + bool set_on_complete) { call_data* calld = static_cast<call_data*>(elem->call_data); subchannel_call_retry_state* retry_state = static_cast<subchannel_call_retry_state*>( @@ -1571,9 +1513,11 @@ static subchannel_batch_data* batch_data_create(grpc_call_element* elem, GRPC_SUBCHANNEL_CALL_REF(calld->subchannel_call, "batch_data_create"); batch_data->batch.payload = &retry_state->batch_payload; gpr_ref_init(&batch_data->refs, refcount); - GRPC_CLOSURE_INIT(&batch_data->on_complete, on_complete, batch_data, - grpc_schedule_on_exec_ctx); - batch_data->batch.on_complete = &batch_data->on_complete; + if (set_on_complete) { + GRPC_CLOSURE_INIT(&batch_data->on_complete, on_complete, batch_data, + grpc_schedule_on_exec_ctx); + batch_data->batch.on_complete = &batch_data->on_complete; + } GRPC_CALL_STACK_REF(calld->owning_call, "batch_data"); return batch_data; } @@ -1606,26 +1550,14 @@ static void batch_data_unref(subchannel_batch_data* batch_data) { static void invoke_recv_initial_metadata_callback(void* arg, grpc_error* error) { subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); - channel_data* chand = - static_cast<channel_data*>(batch_data->elem->channel_data); - call_data* calld = static_cast<call_data*>(batch_data->elem->call_data); // Find pending batch. - pending_batch* pending = nullptr; - for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { - grpc_transport_stream_op_batch* batch = calld->pending_batches[i].batch; - if (batch != nullptr && batch->recv_initial_metadata && - batch->payload->recv_initial_metadata.recv_initial_metadata_ready != - nullptr) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, - "chand=%p calld=%p: invoking recv_initial_metadata_ready for " - "pending batch at index %" PRIuPTR, - chand, calld, i); - } - pending = &calld->pending_batches[i]; - break; - } - } + pending_batch* pending = pending_batch_find( + batch_data->elem, "invoking recv_initial_metadata_ready for", + [](grpc_transport_stream_op_batch* batch) { + return batch->recv_initial_metadata && + batch->payload->recv_initial_metadata + .recv_initial_metadata_ready != nullptr; + }); GPR_ASSERT(pending != nullptr); // Return metadata. grpc_metadata_batch_move( @@ -1661,10 +1593,19 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) { static_cast<subchannel_call_retry_state*>( grpc_connected_subchannel_call_get_parent_data( batch_data->subchannel_call)); + retry_state->completed_recv_initial_metadata = true; + // If a retry was already dispatched, then we're not going to use the + // result of this recv_initial_metadata op, so do nothing. + if (retry_state->retry_dispatched) { + GRPC_CALL_COMBINER_STOP( + calld->call_combiner, + "recv_initial_metadata_ready after retry dispatched"); + return; + } // If we got an error or a Trailers-Only response and have not yet gotten - // the recv_trailing_metadata on_complete callback, then defer - // propagating this callback back to the surface. We can evaluate whether - // to retry when recv_trailing_metadata comes back. + // the recv_trailing_metadata_ready callback, then defer propagating this + // callback back to the surface. We can evaluate whether to retry when + // recv_trailing_metadata comes back. if (GPR_UNLIKELY((batch_data->trailing_metadata_available || error != GRPC_ERROR_NONE) && !retry_state->completed_recv_trailing_metadata)) { @@ -1689,9 +1630,9 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) { } // Received valid initial metadata, so commit the call. retry_commit(elem, retry_state); + // Invoke the callback to return the result to the surface. // Manually invoking a callback function; it does not take ownership of error. invoke_recv_initial_metadata_callback(batch_data, error); - GRPC_ERROR_UNREF(error); } // @@ -1701,25 +1642,13 @@ static void recv_initial_metadata_ready(void* arg, grpc_error* error) { // Invokes recv_message_ready for a subchannel batch. static void invoke_recv_message_callback(void* arg, grpc_error* error) { subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); - channel_data* chand = - static_cast<channel_data*>(batch_data->elem->channel_data); - call_data* calld = static_cast<call_data*>(batch_data->elem->call_data); // Find pending op. - pending_batch* pending = nullptr; - for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { - grpc_transport_stream_op_batch* batch = calld->pending_batches[i].batch; - if (batch != nullptr && batch->recv_message && - batch->payload->recv_message.recv_message_ready != nullptr) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, - "chand=%p calld=%p: invoking recv_message_ready for " - "pending batch at index %" PRIuPTR, - chand, calld, i); - } - pending = &calld->pending_batches[i]; - break; - } - } + pending_batch* pending = pending_batch_find( + batch_data->elem, "invoking recv_message_ready for", + [](grpc_transport_stream_op_batch* batch) { + return batch->recv_message && + batch->payload->recv_message.recv_message_ready != nullptr; + }); GPR_ASSERT(pending != nullptr); // Return payload. *pending->batch->payload->recv_message.recv_message = @@ -1751,10 +1680,18 @@ static void recv_message_ready(void* arg, grpc_error* error) { static_cast<subchannel_call_retry_state*>( grpc_connected_subchannel_call_get_parent_data( batch_data->subchannel_call)); + ++retry_state->completed_recv_message_count; + // If a retry was already dispatched, then we're not going to use the + // result of this recv_message op, so do nothing. + if (retry_state->retry_dispatched) { + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "recv_message_ready after retry dispatched"); + return; + } // If we got an error or the payload was nullptr and we have not yet gotten - // the recv_trailing_metadata on_complete callback, then defer - // propagating this callback back to the surface. We can evaluate whether - // to retry when recv_trailing_metadata comes back. + // the recv_trailing_metadata_ready callback, then defer propagating this + // callback back to the surface. We can evaluate whether to retry when + // recv_trailing_metadata comes back. if (GPR_UNLIKELY( (batch_data->recv_message == nullptr || error != GRPC_ERROR_NONE) && !retry_state->completed_recv_trailing_metadata)) { @@ -1777,133 +1714,268 @@ static void recv_message_ready(void* arg, grpc_error* error) { } // Received a valid message, so commit the call. retry_commit(elem, retry_state); + // Invoke the callback to return the result to the surface. // Manually invoking a callback function; it does not take ownership of error. invoke_recv_message_callback(batch_data, error); - GRPC_ERROR_UNREF(error); } // -// list of closures to execute in call combiner +// recv_trailing_metadata handling // -// Represents a closure that needs to run in the call combiner as part of -// starting or completing a batch. -typedef struct { - grpc_closure* closure; - grpc_error* error; - const char* reason; - bool free_reason = false; -} closure_to_execute; - -static void execute_closures_in_call_combiner(grpc_call_element* elem, - const char* caller, - closure_to_execute* closures, - size_t num_closures) { - channel_data* chand = static_cast<channel_data*>(elem->channel_data); +// Sets *status and *server_pushback_md based on batch_data and error. +static void get_call_status(subchannel_batch_data* batch_data, + grpc_error* error, grpc_status_code* status, + grpc_mdelem** server_pushback_md) { + grpc_call_element* elem = batch_data->elem; call_data* calld = static_cast<call_data*>(elem->call_data); - // Note that the call combiner will be yielded for each closure that - // we schedule. We're already running in the call combiner, so one of - // the closures can be scheduled directly, but the others will - // have to re-enter the call combiner. - if (num_closures > 0) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, "chand=%p calld=%p: %s starting closure: %s", chand, - calld, caller, closures[0].reason); - } - GRPC_CLOSURE_SCHED(closures[0].closure, closures[0].error); - if (closures[0].free_reason) { - gpr_free(const_cast<char*>(closures[0].reason)); - } - for (size_t i = 1; i < num_closures; ++i) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, - "chand=%p calld=%p: %s starting closure in call combiner: %s", - chand, calld, caller, closures[i].reason); - } - GRPC_CALL_COMBINER_START(calld->call_combiner, closures[i].closure, - closures[i].error, closures[i].reason); - if (closures[i].free_reason) { - gpr_free(const_cast<char*>(closures[i].reason)); - } - } + if (error != GRPC_ERROR_NONE) { + grpc_error_get_status(error, calld->deadline, status, nullptr, nullptr, + nullptr); } else { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, "chand=%p calld=%p: no closures to run for %s", chand, - calld, caller); + grpc_metadata_batch* md_batch = + batch_data->batch.payload->recv_trailing_metadata + .recv_trailing_metadata; + GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr); + *status = + grpc_get_status_code_from_metadata(md_batch->idx.named.grpc_status->md); + if (md_batch->idx.named.grpc_retry_pushback_ms != nullptr) { + *server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md; } - GRPC_CALL_COMBINER_STOP(calld->call_combiner, "no closures to run"); } + GRPC_ERROR_UNREF(error); } -// -// on_complete callback handling -// - -// Updates retry_state to reflect the ops completed in batch_data. -static void update_retry_state_for_completed_batch( - subchannel_batch_data* batch_data, - subchannel_call_retry_state* retry_state) { - if (batch_data->batch.send_initial_metadata) { - retry_state->completed_send_initial_metadata = true; - } - if (batch_data->batch.send_message) { - ++retry_state->completed_send_message_count; - } - if (batch_data->batch.send_trailing_metadata) { - retry_state->completed_send_trailing_metadata = true; - } - if (batch_data->batch.recv_initial_metadata) { - retry_state->completed_recv_initial_metadata = true; - } - if (batch_data->batch.recv_message) { - ++retry_state->completed_recv_message_count; - } - if (batch_data->batch.recv_trailing_metadata) { - retry_state->completed_recv_trailing_metadata = true; +// Adds recv_trailing_metadata_ready closure to closures. +static void add_closure_for_recv_trailing_metadata_ready( + grpc_call_element* elem, subchannel_batch_data* batch_data, + grpc_error* error, grpc_core::CallCombinerClosureList* closures) { + // Find pending batch. + pending_batch* pending = pending_batch_find( + elem, "invoking recv_trailing_metadata for", + [](grpc_transport_stream_op_batch* batch) { + return batch->recv_trailing_metadata && + batch->payload->recv_trailing_metadata + .recv_trailing_metadata_ready != nullptr; + }); + // If we generated the recv_trailing_metadata op internally via + // start_internal_recv_trailing_metadata(), then there will be no + // pending batch. + if (pending == nullptr) { + GRPC_ERROR_UNREF(error); + return; } + // Return metadata. + grpc_metadata_batch_move( + &batch_data->recv_trailing_metadata, + pending->batch->payload->recv_trailing_metadata.recv_trailing_metadata); + // Add closure. + closures->Add(pending->batch->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + error, "recv_trailing_metadata_ready for pending batch"); + // Update bookkeeping. + pending->batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + nullptr; + maybe_clear_pending_batch(elem, pending); } // Adds any necessary closures for deferred recv_initial_metadata and -// recv_message callbacks to closures, updating *num_closures as needed. +// recv_message callbacks to closures. static void add_closures_for_deferred_recv_callbacks( subchannel_batch_data* batch_data, subchannel_call_retry_state* retry_state, - closure_to_execute* closures, size_t* num_closures) { + grpc_core::CallCombinerClosureList* closures) { if (batch_data->batch.recv_trailing_metadata) { // Add closure for deferred recv_initial_metadata_ready. if (GPR_UNLIKELY(retry_state->recv_initial_metadata_ready_deferred_batch != nullptr)) { - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = GRPC_CLOSURE_INIT( - &batch_data->recv_initial_metadata_ready, - invoke_recv_initial_metadata_callback, - retry_state->recv_initial_metadata_ready_deferred_batch, - grpc_schedule_on_exec_ctx); - closure->error = retry_state->recv_initial_metadata_error; - closure->reason = "resuming recv_initial_metadata_ready"; + GRPC_CLOSURE_INIT(&batch_data->recv_initial_metadata_ready, + invoke_recv_initial_metadata_callback, + retry_state->recv_initial_metadata_ready_deferred_batch, + grpc_schedule_on_exec_ctx); + closures->Add(&batch_data->recv_initial_metadata_ready, + retry_state->recv_initial_metadata_error, + "resuming recv_initial_metadata_ready"); retry_state->recv_initial_metadata_ready_deferred_batch = nullptr; } // Add closure for deferred recv_message_ready. if (GPR_UNLIKELY(retry_state->recv_message_ready_deferred_batch != nullptr)) { - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = GRPC_CLOSURE_INIT( - &batch_data->recv_message_ready, invoke_recv_message_callback, - retry_state->recv_message_ready_deferred_batch, - grpc_schedule_on_exec_ctx); - closure->error = retry_state->recv_message_error; - closure->reason = "resuming recv_message_ready"; + GRPC_CLOSURE_INIT(&batch_data->recv_message_ready, + invoke_recv_message_callback, + retry_state->recv_message_ready_deferred_batch, + grpc_schedule_on_exec_ctx); + closures->Add(&batch_data->recv_message_ready, + retry_state->recv_message_error, + "resuming recv_message_ready"); retry_state->recv_message_ready_deferred_batch = nullptr; } } } +// Returns true if any op in the batch was not yet started. +// Only looks at send ops, since recv ops are always started immediately. +static bool pending_batch_is_unstarted( + pending_batch* pending, call_data* calld, + subchannel_call_retry_state* retry_state) { + if (pending->batch == nullptr || pending->batch->on_complete == nullptr) { + return false; + } + if (pending->batch->send_initial_metadata && + !retry_state->started_send_initial_metadata) { + return true; + } + if (pending->batch->send_message && + retry_state->started_send_message_count < calld->send_messages->size()) { + return true; + } + if (pending->batch->send_trailing_metadata && + !retry_state->started_send_trailing_metadata) { + return true; + } + return false; +} + +// For any pending batch containing an op that has not yet been started, +// adds the pending batch's completion closures to closures. +static void add_closures_to_fail_unstarted_pending_batches( + grpc_call_element* elem, subchannel_call_retry_state* retry_state, + grpc_error* error, grpc_core::CallCombinerClosureList* closures) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + if (pending_batch_is_unstarted(pending, calld, retry_state)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: failing unstarted pending batch at index " + "%" PRIuPTR, + chand, calld, i); + } + closures->Add(pending->batch->on_complete, GRPC_ERROR_REF(error), + "failing on_complete for pending batch"); + pending->batch->on_complete = nullptr; + maybe_clear_pending_batch(elem, pending); + } + } + GRPC_ERROR_UNREF(error); +} + +// Runs necessary closures upon completion of a call attempt. +static void run_closures_for_completed_call(subchannel_batch_data* batch_data, + grpc_error* error) { + grpc_call_element* elem = batch_data->elem; + call_data* calld = static_cast<call_data*>(elem->call_data); + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + // Construct list of closures to execute. + grpc_core::CallCombinerClosureList closures; + // First, add closure for recv_trailing_metadata_ready. + add_closure_for_recv_trailing_metadata_ready( + elem, batch_data, GRPC_ERROR_REF(error), &closures); + // If there are deferred recv_initial_metadata_ready or recv_message_ready + // callbacks, add them to closures. + add_closures_for_deferred_recv_callbacks(batch_data, retry_state, &closures); + // Add closures to fail any pending batches that have not yet been started. + add_closures_to_fail_unstarted_pending_batches( + elem, retry_state, GRPC_ERROR_REF(error), &closures); + // Don't need batch_data anymore. + batch_data_unref(batch_data); + // Schedule all of the closures identified above. + // Note: This will release the call combiner. + closures.RunClosures(calld->call_combiner); + GRPC_ERROR_UNREF(error); +} + +// Intercepts recv_trailing_metadata_ready callback for retries. +// Commits the call and returns the trailing metadata up the stack. +static void recv_trailing_metadata_ready(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: got recv_trailing_metadata_ready, error=%s", + chand, calld, grpc_error_string(error)); + } + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + retry_state->completed_recv_trailing_metadata = true; + // Get the call's status and check for server pushback metadata. + grpc_status_code status = GRPC_STATUS_OK; + grpc_mdelem* server_pushback_md = nullptr; + get_call_status(batch_data, GRPC_ERROR_REF(error), &status, + &server_pushback_md); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: call finished, status=%s", chand, + calld, grpc_status_code_to_string(status)); + } + // Check if we should retry. + if (maybe_retry(elem, batch_data, status, server_pushback_md)) { + // Unref batch_data for deferred recv_initial_metadata_ready or + // recv_message_ready callbacks, if any. + if (retry_state->recv_initial_metadata_ready_deferred_batch != nullptr) { + batch_data_unref(batch_data); + GRPC_ERROR_UNREF(retry_state->recv_initial_metadata_error); + } + if (retry_state->recv_message_ready_deferred_batch != nullptr) { + batch_data_unref(batch_data); + GRPC_ERROR_UNREF(retry_state->recv_message_error); + } + batch_data_unref(batch_data); + return; + } + // Not retrying, so commit the call. + retry_commit(elem, retry_state); + // Run any necessary closures. + run_closures_for_completed_call(batch_data, GRPC_ERROR_REF(error)); +} + +// +// on_complete callback handling +// + +// Adds the on_complete closure for the pending batch completed in +// batch_data to closures. +static void add_closure_for_completed_pending_batch( + grpc_call_element* elem, subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state, grpc_error* error, + grpc_core::CallCombinerClosureList* closures) { + pending_batch* pending = pending_batch_find( + elem, "completed", [batch_data](grpc_transport_stream_op_batch* batch) { + // Match the pending batch with the same set of send ops as the + // subchannel batch we've just completed. + return batch->on_complete != nullptr && + batch_data->batch.send_initial_metadata == + batch->send_initial_metadata && + batch_data->batch.send_message == batch->send_message && + batch_data->batch.send_trailing_metadata == + batch->send_trailing_metadata; + }); + // If batch_data is a replay batch, then there will be no pending + // batch to complete. + if (pending == nullptr) { + GRPC_ERROR_UNREF(error); + return; + } + // Add closure. + closures->Add(pending->batch->on_complete, error, + "on_complete for pending batch"); + pending->batch->on_complete = nullptr; + maybe_clear_pending_batch(elem, pending); +} + // If there are any cached ops to replay or pending ops to start on the // subchannel call, adds a closure to closures to invoke -// start_retriable_subchannel_batches(), updating *num_closures as needed. +// start_retriable_subchannel_batches(). static void add_closures_for_replay_or_pending_send_ops( grpc_call_element* elem, subchannel_batch_data* batch_data, - subchannel_call_retry_state* retry_state, closure_to_execute* closures, - size_t* num_closures) { + subchannel_call_retry_state* retry_state, + grpc_core::CallCombinerClosureList* closures) { channel_data* chand = static_cast<channel_data*>(elem->channel_data); call_data* calld = static_cast<call_data*>(elem->call_data); bool have_pending_send_message_ops = @@ -1929,93 +2001,12 @@ static void add_closures_for_replay_or_pending_send_ops( "chand=%p calld=%p: starting next batch for pending send op(s)", chand, calld); } - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = GRPC_CLOSURE_INIT( - &batch_data->batch.handler_private.closure, - start_retriable_subchannel_batches, elem, grpc_schedule_on_exec_ctx); - closure->error = GRPC_ERROR_NONE; - closure->reason = "starting next batch for send_* op(s)"; - } -} - -// For any pending batch completed in batch_data, adds the necessary -// completion closures to closures, updating *num_closures as needed. -static void add_closures_for_completed_pending_batches( - grpc_call_element* elem, subchannel_batch_data* batch_data, - subchannel_call_retry_state* retry_state, grpc_error* error, - closure_to_execute* closures, size_t* num_closures) { - channel_data* chand = static_cast<channel_data*>(elem->channel_data); - call_data* calld = static_cast<call_data*>(elem->call_data); - for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { - pending_batch* pending = &calld->pending_batches[i]; - if (pending_batch_is_completed(pending, calld, retry_state)) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, - "chand=%p calld=%p: pending batch completed at index %" PRIuPTR, - chand, calld, i); - } - // Copy the trailing metadata to return it to the surface. - if (batch_data->batch.recv_trailing_metadata) { - grpc_metadata_batch_move(&batch_data->recv_trailing_metadata, - pending->batch->payload->recv_trailing_metadata - .recv_trailing_metadata); - } - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = pending->batch->on_complete; - closure->error = GRPC_ERROR_REF(error); - closure->reason = "on_complete for pending batch"; - pending->batch->on_complete = nullptr; - maybe_clear_pending_batch(elem, pending); - } + GRPC_CLOSURE_INIT(&batch_data->batch.handler_private.closure, + start_retriable_subchannel_batches, elem, + grpc_schedule_on_exec_ctx); + closures->Add(&batch_data->batch.handler_private.closure, GRPC_ERROR_NONE, + "starting next batch for send_* op(s)"); } - GRPC_ERROR_UNREF(error); -} - -// For any pending batch containing an op that has not yet been started, -// adds the pending batch's completion closures to closures, updating -// *num_closures as needed. -static void add_closures_to_fail_unstarted_pending_batches( - grpc_call_element* elem, subchannel_call_retry_state* retry_state, - grpc_error* error, closure_to_execute* closures, size_t* num_closures) { - channel_data* chand = static_cast<channel_data*>(elem->channel_data); - call_data* calld = static_cast<call_data*>(elem->call_data); - for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { - pending_batch* pending = &calld->pending_batches[i]; - if (pending_batch_is_unstarted(pending, calld, retry_state)) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, - "chand=%p calld=%p: failing unstarted pending batch at index " - "%" PRIuPTR, - chand, calld, i); - } - if (pending->batch->recv_initial_metadata) { - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = pending->batch->payload->recv_initial_metadata - .recv_initial_metadata_ready; - closure->error = GRPC_ERROR_REF(error); - closure->reason = - "failing recv_initial_metadata_ready for pending batch"; - pending->batch->payload->recv_initial_metadata - .recv_initial_metadata_ready = nullptr; - } - if (pending->batch->recv_message) { - *pending->batch->payload->recv_message.recv_message = nullptr; - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = - pending->batch->payload->recv_message.recv_message_ready; - closure->error = GRPC_ERROR_REF(error); - closure->reason = "failing recv_message_ready for pending batch"; - pending->batch->payload->recv_message.recv_message_ready = nullptr; - } - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = pending->batch->on_complete; - closure->error = GRPC_ERROR_REF(error); - closure->reason = "failing on_complete for pending batch"; - pending->batch->on_complete = nullptr; - maybe_clear_pending_batch(elem, pending); - } - } - GRPC_ERROR_UNREF(error); } // Callback used to intercept on_complete from subchannel calls. @@ -2035,136 +2026,49 @@ static void on_complete(void* arg, grpc_error* error) { static_cast<subchannel_call_retry_state*>( grpc_connected_subchannel_call_get_parent_data( batch_data->subchannel_call)); - // If we have previously completed recv_trailing_metadata, then the - // call is finished. - bool call_finished = retry_state->completed_recv_trailing_metadata; - // Record whether we were already committed before receiving this callback. - const bool previously_committed = calld->retry_committed; // Update bookkeeping in retry_state. - update_retry_state_for_completed_batch(batch_data, retry_state); - if (call_finished) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, "chand=%p calld=%p: call already finished", chand, - calld); - } - } else { - // Check if this batch finished the call, and if so, get its status. - // The call is finished if either (a) this callback was invoked with - // an error or (b) we receive status. - grpc_status_code status = GRPC_STATUS_OK; - grpc_mdelem* server_pushback_md = nullptr; - if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) { // Case (a). - call_finished = true; - grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr, - nullptr); - } else if (batch_data->batch.recv_trailing_metadata) { // Case (b). - call_finished = true; - grpc_metadata_batch* md_batch = - batch_data->batch.payload->recv_trailing_metadata - .recv_trailing_metadata; - GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr); - status = grpc_get_status_code_from_metadata( - md_batch->idx.named.grpc_status->md); - if (md_batch->idx.named.grpc_retry_pushback_ms != nullptr) { - server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md; - } - } - // If the call just finished, check if we should retry. - if (call_finished) { - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_INFO, "chand=%p calld=%p: call finished, status=%s", chand, - calld, grpc_status_code_to_string(status)); - } - if (maybe_retry(elem, batch_data, status, server_pushback_md)) { - // Unref batch_data for deferred recv_initial_metadata_ready or - // recv_message_ready callbacks, if any. - if (batch_data->batch.recv_trailing_metadata && - retry_state->recv_initial_metadata_ready_deferred_batch != - nullptr) { - batch_data_unref(batch_data); - GRPC_ERROR_UNREF(retry_state->recv_initial_metadata_error); - } - if (batch_data->batch.recv_trailing_metadata && - retry_state->recv_message_ready_deferred_batch != nullptr) { - batch_data_unref(batch_data); - GRPC_ERROR_UNREF(retry_state->recv_message_error); - } - // Track number of pending subchannel send batches and determine if - // this was the last one. - bool last_callback_complete = false; - if (batch_data->batch.send_initial_metadata || - batch_data->batch.send_message || - batch_data->batch.send_trailing_metadata) { - --calld->num_pending_retriable_subchannel_send_batches; - last_callback_complete = - calld->num_pending_retriable_subchannel_send_batches == 0; - } - batch_data_unref(batch_data); - // If we just completed the last subchannel send batch, unref the - // call stack. - if (last_callback_complete) { - GRPC_CALL_STACK_UNREF(calld->owning_call, "subchannel_send_batches"); - } - return; - } - // Not retrying, so commit the call. - retry_commit(elem, retry_state); - } + if (batch_data->batch.send_initial_metadata) { + retry_state->completed_send_initial_metadata = true; + } + if (batch_data->batch.send_message) { + ++retry_state->completed_send_message_count; } - // If we were already committed before receiving this callback, free - // cached data for send ops that we've just completed. (If the call has - // just now finished, the call to retry_commit() above will have freed all - // cached send ops, so we don't need to do it here.) - if (previously_committed) { + if (batch_data->batch.send_trailing_metadata) { + retry_state->completed_send_trailing_metadata = true; + } + // If the call is committed, free cached data for send ops that we've just + // completed. + if (calld->retry_committed) { free_cached_send_op_data_for_completed_batch(elem, batch_data, retry_state); } - // Call not being retried. // Construct list of closures to execute. - // Max number of closures is number of pending batches plus one for - // each of: - // - recv_initial_metadata_ready (either deferred or unstarted) - // - recv_message_ready (either deferred or unstarted) - // - starting a new batch for pending send ops - closure_to_execute closures[GPR_ARRAY_SIZE(calld->pending_batches) + 3]; - size_t num_closures = 0; - // If there are deferred recv_initial_metadata_ready or recv_message_ready - // callbacks, add them to closures. - add_closures_for_deferred_recv_callbacks(batch_data, retry_state, closures, - &num_closures); - // Find pending batches whose ops are now complete and add their - // on_complete callbacks to closures. - add_closures_for_completed_pending_batches(elem, batch_data, retry_state, - GRPC_ERROR_REF(error), closures, - &num_closures); - // Add closures to handle any pending batches that have not yet been started. - // If the call is finished, we fail these batches; otherwise, we add a - // callback to start_retriable_subchannel_batches() to start them on - // the subchannel call. - if (call_finished) { - add_closures_to_fail_unstarted_pending_batches( - elem, retry_state, GRPC_ERROR_REF(error), closures, &num_closures); - } else { - add_closures_for_replay_or_pending_send_ops(elem, batch_data, retry_state, - closures, &num_closures); + grpc_core::CallCombinerClosureList closures; + // If a retry was already dispatched, that means we saw + // recv_trailing_metadata before this, so we do nothing here. + // Otherwise, invoke the callback to return the result to the surface. + if (!retry_state->retry_dispatched) { + // Add closure for the completed pending batch, if any. + add_closure_for_completed_pending_batch(elem, batch_data, retry_state, + GRPC_ERROR_REF(error), &closures); + // If needed, add a callback to start any replay or pending send ops on + // the subchannel call. + if (!retry_state->completed_recv_trailing_metadata) { + add_closures_for_replay_or_pending_send_ops(elem, batch_data, retry_state, + &closures); + } } // Track number of pending subchannel send batches and determine if this // was the last one. - bool last_callback_complete = false; - if (batch_data->batch.send_initial_metadata || - batch_data->batch.send_message || - batch_data->batch.send_trailing_metadata) { - --calld->num_pending_retriable_subchannel_send_batches; - last_callback_complete = - calld->num_pending_retriable_subchannel_send_batches == 0; - } + --calld->num_pending_retriable_subchannel_send_batches; + const bool last_send_batch_complete = + calld->num_pending_retriable_subchannel_send_batches == 0; // Don't need batch_data anymore. batch_data_unref(batch_data); // Schedule all of the closures identified above. // Note: This yeilds the call combiner. - execute_closures_in_call_combiner(elem, "on_complete", closures, - num_closures); - // If we just completed the last subchannel send batch, unref the call stack. - if (last_callback_complete) { + closures.RunClosures(calld->call_combiner); + // If this was the last subchannel send batch, unref the call stack. + if (last_send_batch_complete) { GRPC_CALL_STACK_UNREF(calld->owning_call, "subchannel_send_batches"); } } @@ -2185,27 +2089,22 @@ static void start_batch_in_call_combiner(void* arg, grpc_error* ignored) { // Adds a closure to closures that will execute batch in the call combiner. static void add_closure_for_subchannel_batch( - call_data* calld, grpc_transport_stream_op_batch* batch, - closure_to_execute* closures, size_t* num_closures) { + grpc_call_element* elem, grpc_transport_stream_op_batch* batch, + grpc_core::CallCombinerClosureList* closures) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); batch->handler_private.extra_arg = calld->subchannel_call; GRPC_CLOSURE_INIT(&batch->handler_private.closure, start_batch_in_call_combiner, batch, grpc_schedule_on_exec_ctx); - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = &batch->handler_private.closure; - closure->error = GRPC_ERROR_NONE; - // If the tracer is enabled, we log a more detailed message, which - // requires dynamic allocation. This will be freed in - // start_retriable_subchannel_batches(). if (grpc_client_channel_trace.enabled()) { char* batch_str = grpc_transport_stream_op_batch_string(batch); - gpr_asprintf(const_cast<char**>(&closure->reason), - "starting batch in call combiner: %s", batch_str); + gpr_log(GPR_INFO, "chand=%p calld=%p: starting subchannel batch: %s", chand, + calld, batch_str); gpr_free(batch_str); - closure->free_reason = true; - } else { - closure->reason = "start_subchannel_batch"; } + closures->Add(&batch->handler_private.closure, GRPC_ERROR_NONE, + "start_subchannel_batch"); } // Adds retriable send_initial_metadata op to batch_data. @@ -2341,9 +2240,13 @@ static void add_retriable_recv_trailing_metadata_op( grpc_metadata_batch_init(&batch_data->recv_trailing_metadata); batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata = &batch_data->recv_trailing_metadata; - batch_data->batch.collect_stats = true; - batch_data->batch.payload->collect_stats.collect_stats = + batch_data->batch.payload->recv_trailing_metadata.collect_stats = &batch_data->collect_stats; + GRPC_CLOSURE_INIT(&batch_data->recv_trailing_metadata_ready, + recv_trailing_metadata_ready, batch_data, + grpc_schedule_on_exec_ctx); + batch_data->batch.payload->recv_trailing_metadata + .recv_trailing_metadata_ready = &batch_data->recv_trailing_metadata_ready; } // Helper function used to start a recv_trailing_metadata batch. This @@ -2364,9 +2267,11 @@ static void start_internal_recv_trailing_metadata(grpc_call_element* elem) { grpc_connected_subchannel_call_get_parent_data( calld->subchannel_call)); // Create batch_data with 2 refs, since this batch will be unreffed twice: - // once when the subchannel batch returns, and again when we actually get - // a recv_trailing_metadata op from the surface. - subchannel_batch_data* batch_data = batch_data_create(elem, 2); + // once for the recv_trailing_metadata_ready callback when the subchannel + // batch returns, and again when we actually get a recv_trailing_metadata + // op from the surface. + subchannel_batch_data* batch_data = + batch_data_create(elem, 2, false /* set_on_complete */); add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data); retry_state->recv_trailing_metadata_internal_batch = batch_data; // Note: This will release the call combiner. @@ -2391,7 +2296,7 @@ static subchannel_batch_data* maybe_create_subchannel_batch_for_replay( "send_initial_metadata op", chand, calld); } - replay_batch_data = batch_data_create(elem, 1); + replay_batch_data = batch_data_create(elem, 1, true /* set_on_complete */); add_retriable_send_initial_metadata_op(calld, retry_state, replay_batch_data); } @@ -2408,7 +2313,8 @@ static subchannel_batch_data* maybe_create_subchannel_batch_for_replay( chand, calld); } if (replay_batch_data == nullptr) { - replay_batch_data = batch_data_create(elem, 1); + replay_batch_data = + batch_data_create(elem, 1, true /* set_on_complete */); } add_retriable_send_message_op(elem, retry_state, replay_batch_data); } @@ -2427,7 +2333,8 @@ static subchannel_batch_data* maybe_create_subchannel_batch_for_replay( chand, calld); } if (replay_batch_data == nullptr) { - replay_batch_data = batch_data_create(elem, 1); + replay_batch_data = + batch_data_create(elem, 1, true /* set_on_complete */); } add_retriable_send_trailing_metadata_op(calld, retry_state, replay_batch_data); @@ -2439,7 +2346,7 @@ static subchannel_batch_data* maybe_create_subchannel_batch_for_replay( // *num_batches as needed. static void add_subchannel_batches_for_pending_batches( grpc_call_element* elem, subchannel_call_retry_state* retry_state, - closure_to_execute* closures, size_t* num_closures) { + grpc_core::CallCombinerClosureList* closures) { call_data* calld = static_cast<call_data*>(elem->call_data); for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { pending_batch* pending = &calld->pending_batches[i]; @@ -2495,13 +2402,11 @@ static void add_subchannel_batches_for_pending_batches( if (retry_state->completed_recv_trailing_metadata) { subchannel_batch_data* batch_data = retry_state->recv_trailing_metadata_internal_batch; - closure_to_execute* closure = &closures[(*num_closures)++]; - closure->closure = &batch_data->on_complete; // Batches containing recv_trailing_metadata always succeed. - closure->error = GRPC_ERROR_NONE; - closure->reason = - "re-executing on_complete for recv_trailing_metadata " - "to propagate internally triggered result"; + closures->Add( + &batch_data->recv_trailing_metadata_ready, GRPC_ERROR_NONE, + "re-executing recv_trailing_metadata_ready to propagate " + "internally triggered result"); } else { batch_data_unref(retry_state->recv_trailing_metadata_internal_batch); } @@ -2513,14 +2418,19 @@ static void add_subchannel_batches_for_pending_batches( if (calld->method_params == nullptr || calld->method_params->retry_policy() == nullptr || calld->retry_committed) { - add_closure_for_subchannel_batch(calld, batch, closures, num_closures); + add_closure_for_subchannel_batch(elem, batch, closures); pending_batch_clear(calld, pending); continue; } // Create batch with the right number of callbacks. - const int num_callbacks = - 1 + batch->recv_initial_metadata + batch->recv_message; - subchannel_batch_data* batch_data = batch_data_create(elem, num_callbacks); + const bool has_send_ops = batch->send_initial_metadata || + batch->send_message || + batch->send_trailing_metadata; + const int num_callbacks = has_send_ops + batch->recv_initial_metadata + + batch->recv_message + + batch->recv_trailing_metadata; + subchannel_batch_data* batch_data = batch_data_create( + elem, num_callbacks, has_send_ops /* set_on_complete */); // Cache send ops if needed. maybe_cache_send_ops_for_batch(calld, pending); // send_initial_metadata. @@ -2547,11 +2457,9 @@ static void add_subchannel_batches_for_pending_batches( } // recv_trailing_metadata. if (batch->recv_trailing_metadata) { - GPR_ASSERT(batch->collect_stats); add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data); } - add_closure_for_subchannel_batch(calld, &batch_data->batch, closures, - num_closures); + add_closure_for_subchannel_batch(elem, &batch_data->batch, closures); // Track number of pending subchannel send batches. // If this is the first one, take a ref to the call stack. if (batch->send_initial_metadata || batch->send_message || @@ -2579,15 +2487,13 @@ static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) { grpc_connected_subchannel_call_get_parent_data( calld->subchannel_call)); // Construct list of closures to execute, one for each pending batch. - // We can start up to 6 batches. - closure_to_execute closures[GPR_ARRAY_SIZE(calld->pending_batches)]; - size_t num_closures = 0; + grpc_core::CallCombinerClosureList closures; // Replay previously-returned send_* ops if needed. subchannel_batch_data* replay_batch_data = maybe_create_subchannel_batch_for_replay(elem, retry_state); if (replay_batch_data != nullptr) { - add_closure_for_subchannel_batch(calld, &replay_batch_data->batch, closures, - &num_closures); + add_closure_for_subchannel_batch(elem, &replay_batch_data->batch, + &closures); // Track number of pending subchannel send batches. // If this is the first one, take a ref to the call stack. if (calld->num_pending_retriable_subchannel_send_batches == 0) { @@ -2596,17 +2502,16 @@ static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) { ++calld->num_pending_retriable_subchannel_send_batches; } // Now add pending batches. - add_subchannel_batches_for_pending_batches(elem, retry_state, closures, - &num_closures); + add_subchannel_batches_for_pending_batches(elem, retry_state, &closures); // Start batches on subchannel call. if (grpc_client_channel_trace.enabled()) { gpr_log(GPR_INFO, "chand=%p calld=%p: starting %" PRIuPTR " retriable batches on subchannel_call=%p", - chand, calld, num_closures, calld->subchannel_call); + chand, calld, closures.size(), calld->subchannel_call); } - execute_closures_in_call_combiner(elem, "start_retriable_subchannel_batches", - closures, num_closures); + // Note: This will yield the call combiner. + closures.RunClosures(calld->call_combiner); } // diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc new file mode 100644 index 0000000000..06a6e853f5 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc @@ -0,0 +1,311 @@ +/* + * + * Copyright 2016 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/iomgr/port.h" +#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) + +#include <ares.h> +#include <string.h> +#include <sys/ioctl.h> + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/time.h> +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +typedef struct fd_node { + /** the owner of this fd node */ + grpc_ares_ev_driver* ev_driver; + /** a closure wrapping on_readable_locked, which should be + invoked when the grpc_fd in this node becomes readable. */ + grpc_closure read_closure; + /** a closure wrapping on_writable_locked, which should be + invoked when the grpc_fd in this node becomes writable. */ + grpc_closure write_closure; + /** next fd node in the list */ + struct fd_node* next; + + /** wrapped fd that's polled by grpc's poller for the current platform */ + grpc_core::GrpcPolledFd* grpc_polled_fd; + /** if the readable closure has been registered */ + bool readable_registered; + /** if the writable closure has been registered */ + bool writable_registered; + /** if the fd has been shutdown yet from grpc iomgr perspective */ + bool already_shutdown; +} fd_node; + +struct grpc_ares_ev_driver { + /** the ares_channel owned by this event driver */ + ares_channel channel; + /** pollset set for driving the IO events of the channel */ + grpc_pollset_set* pollset_set; + /** refcount of the event driver */ + gpr_refcount refs; + + /** combiner to synchronize c-ares and I/O callbacks on */ + grpc_combiner* combiner; + /** a list of grpc_fd that this event driver is currently using. */ + fd_node* fds; + /** is this event driver currently working? */ + bool working; + /** is this event driver being shut down */ + bool shutting_down; +}; + +static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver); + +static grpc_ares_ev_driver* grpc_ares_ev_driver_ref( + grpc_ares_ev_driver* ev_driver) { + gpr_log(GPR_DEBUG, "Ref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + gpr_ref(&ev_driver->refs); + return ev_driver; +} + +static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver* ev_driver) { + gpr_log(GPR_DEBUG, "Unref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + if (gpr_unref(&ev_driver->refs)) { + gpr_log(GPR_DEBUG, "destroy ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + GPR_ASSERT(ev_driver->fds == nullptr); + GRPC_COMBINER_UNREF(ev_driver->combiner, "free ares event driver"); + ares_destroy(ev_driver->channel); + gpr_free(ev_driver); + } +} + +static void fd_node_destroy_locked(fd_node* fdn) { + gpr_log(GPR_DEBUG, "delete fd: %s", fdn->grpc_polled_fd->GetName()); + GPR_ASSERT(!fdn->readable_registered); + GPR_ASSERT(!fdn->writable_registered); + GPR_ASSERT(fdn->already_shutdown); + grpc_core::Delete(fdn->grpc_polled_fd); + gpr_free(fdn); +} + +static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) { + if (!fdn->already_shutdown) { + fdn->already_shutdown = true; + fdn->grpc_polled_fd->ShutdownLocked( + GRPC_ERROR_CREATE_FROM_STATIC_STRING(reason)); + } +} + +grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver, + grpc_pollset_set* pollset_set, + grpc_combiner* combiner) { + *ev_driver = static_cast<grpc_ares_ev_driver*>( + gpr_malloc(sizeof(grpc_ares_ev_driver))); + ares_options opts; + memset(&opts, 0, sizeof(opts)); + opts.flags |= ARES_FLAG_STAYOPEN; + int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS); + grpc_core::ConfigureAresChannelLocked(&(*ev_driver)->channel); + gpr_log(GPR_DEBUG, "grpc_ares_ev_driver_create_locked"); + if (status != ARES_SUCCESS) { + char* err_msg; + gpr_asprintf(&err_msg, "Failed to init ares channel. C-ares error: %s", + ares_strerror(status)); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); + gpr_free(err_msg); + gpr_free(*ev_driver); + return err; + } + (*ev_driver)->combiner = GRPC_COMBINER_REF(combiner, "ares event driver"); + gpr_ref_init(&(*ev_driver)->refs, 1); + (*ev_driver)->pollset_set = pollset_set; + (*ev_driver)->fds = nullptr; + (*ev_driver)->working = false; + (*ev_driver)->shutting_down = false; + return GRPC_ERROR_NONE; +} + +void grpc_ares_ev_driver_destroy_locked(grpc_ares_ev_driver* ev_driver) { + // We mark the event driver as being shut down. If the event driver + // is working, grpc_ares_notify_on_event_locked will shut down the + // fds; if it's not working, there are no fds to shut down. + ev_driver->shutting_down = true; + grpc_ares_ev_driver_unref(ev_driver); +} + +void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver) { + ev_driver->shutting_down = true; + fd_node* fn = ev_driver->fds; + while (fn != nullptr) { + fd_node_shutdown_locked(fn, "grpc_ares_ev_driver_shutdown"); + fn = fn->next; + } +} + +// Search fd in the fd_node list head. This is an O(n) search, the max possible +// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests. +static fd_node* pop_fd_node_locked(fd_node** head, ares_socket_t as) { + fd_node dummy_head; + dummy_head.next = *head; + fd_node* node = &dummy_head; + while (node->next != nullptr) { + if (node->next->grpc_polled_fd->GetWrappedAresSocketLocked() == as) { + fd_node* ret = node->next; + node->next = node->next->next; + *head = dummy_head.next; + return ret; + } + node = node->next; + } + return nullptr; +} + +static void on_readable_locked(void* arg, grpc_error* error) { + fd_node* fdn = static_cast<fd_node*>(arg); + grpc_ares_ev_driver* ev_driver = fdn->ev_driver; + const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked(); + fdn->readable_registered = false; + gpr_log(GPR_DEBUG, "readable on %s", fdn->grpc_polled_fd->GetName()); + if (error == GRPC_ERROR_NONE) { + do { + ares_process_fd(ev_driver->channel, as, ARES_SOCKET_BAD); + } while (fdn->grpc_polled_fd->IsFdStillReadableLocked()); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + grpc_ares_notify_on_event_locked(ev_driver); + grpc_ares_ev_driver_unref(ev_driver); +} + +static void on_writable_locked(void* arg, grpc_error* error) { + fd_node* fdn = static_cast<fd_node*>(arg); + grpc_ares_ev_driver* ev_driver = fdn->ev_driver; + const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked(); + fdn->writable_registered = false; + gpr_log(GPR_DEBUG, "writable on %s", fdn->grpc_polled_fd->GetName()); + if (error == GRPC_ERROR_NONE) { + ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, as); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + grpc_ares_notify_on_event_locked(ev_driver); + grpc_ares_ev_driver_unref(ev_driver); +} + +ares_channel* grpc_ares_ev_driver_get_channel_locked( + grpc_ares_ev_driver* ev_driver) { + return &ev_driver->channel; +} + +// Get the file descriptors used by the ev_driver's ares channel, register +// driver_closure with these filedescriptors. +static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver) { + fd_node* new_list = nullptr; + if (!ev_driver->shutting_down) { + ares_socket_t socks[ARES_GETSOCK_MAXNUM]; + int socks_bitmask = + ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM); + for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) { + if (ARES_GETSOCK_READABLE(socks_bitmask, i) || + ARES_GETSOCK_WRITABLE(socks_bitmask, i)) { + fd_node* fdn = pop_fd_node_locked(&ev_driver->fds, socks[i]); + // Create a new fd_node if sock[i] is not in the fd_node list. + if (fdn == nullptr) { + fdn = static_cast<fd_node*>(gpr_malloc(sizeof(fd_node))); + fdn->grpc_polled_fd = grpc_core::NewGrpcPolledFdLocked( + socks[i], ev_driver->pollset_set); + gpr_log(GPR_DEBUG, "new fd: %s", fdn->grpc_polled_fd->GetName()); + fdn->ev_driver = ev_driver; + fdn->readable_registered = false; + fdn->writable_registered = false; + fdn->already_shutdown = false; + GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_locked, fdn, + grpc_combiner_scheduler(ev_driver->combiner)); + GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable_locked, fdn, + grpc_combiner_scheduler(ev_driver->combiner)); + } + fdn->next = new_list; + new_list = fdn; + // Register read_closure if the socket is readable and read_closure has + // not been registered with this socket. + if (ARES_GETSOCK_READABLE(socks_bitmask, i) && + !fdn->readable_registered) { + grpc_ares_ev_driver_ref(ev_driver); + gpr_log(GPR_DEBUG, "notify read on: %s", + fdn->grpc_polled_fd->GetName()); + fdn->grpc_polled_fd->RegisterForOnReadableLocked(&fdn->read_closure); + fdn->readable_registered = true; + } + // Register write_closure if the socket is writable and write_closure + // has not been registered with this socket. + if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) && + !fdn->writable_registered) { + gpr_log(GPR_DEBUG, "notify write on: %s", + fdn->grpc_polled_fd->GetName()); + grpc_ares_ev_driver_ref(ev_driver); + fdn->grpc_polled_fd->RegisterForOnWriteableLocked( + &fdn->write_closure); + fdn->writable_registered = true; + } + } + } + } + // Any remaining fds in ev_driver->fds were not returned by ares_getsock() and + // are therefore no longer in use, so they can be shut down and removed from + // the list. + while (ev_driver->fds != nullptr) { + fd_node* cur = ev_driver->fds; + ev_driver->fds = ev_driver->fds->next; + fd_node_shutdown_locked(cur, "c-ares fd shutdown"); + if (!cur->readable_registered && !cur->writable_registered) { + fd_node_destroy_locked(cur); + } else { + cur->next = new_list; + new_list = cur; + } + } + ev_driver->fds = new_list; + // If the ev driver has no working fd, all the tasks are done. + if (new_list == nullptr) { + ev_driver->working = false; + gpr_log(GPR_DEBUG, "ev driver stop working"); + } +} + +void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) { + if (!ev_driver->working) { + ev_driver->working = true; + grpc_ares_notify_on_event_locked(ev_driver); + } +} + +#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h index 27d1511d94..7002c8f95f 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h @@ -22,6 +22,7 @@ #include <grpc/support/port_platform.h> #include <ares.h> +#include "src/core/lib/gprpp/abstract.h" #include "src/core/lib/iomgr/pollset_set.h" typedef struct grpc_ares_ev_driver grpc_ares_ev_driver; @@ -51,5 +52,40 @@ void grpc_ares_ev_driver_destroy_locked(grpc_ares_ev_driver* ev_driver); /* Shutdown all the grpc_fds used by \a ev_driver */ void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver); +namespace grpc_core { + +/* A wrapped fd that integrates with the grpc iomgr of the current platform. + * A GrpcPolledFd knows how to create grpc platform-specific iomgr endpoints + * from "ares_socket_t" sockets, and then sign up for readability/writeability + * with that poller, and do shutdown and destruction. */ +class GrpcPolledFd { + public: + virtual ~GrpcPolledFd() {} + /* Called when c-ares library is interested and there's no pending callback */ + virtual void RegisterForOnReadableLocked(grpc_closure* read_closure) + GRPC_ABSTRACT; + /* Called when c-ares library is interested and there's no pending callback */ + virtual void RegisterForOnWriteableLocked(grpc_closure* write_closure) + GRPC_ABSTRACT; + /* Indicates if there is data left even after just being read from */ + virtual bool IsFdStillReadableLocked() GRPC_ABSTRACT; + /* Called once and only once. Must cause cancellation of any pending + * read/write callbacks. */ + virtual void ShutdownLocked(grpc_error* error) GRPC_ABSTRACT; + /* Get the underlying ares_socket_t that this was created from */ + virtual ares_socket_t GetWrappedAresSocketLocked() GRPC_ABSTRACT; + /* A unique name, for logging */ + virtual const char* GetName() GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; + +/* Creates a new wrapped fd for the current platform */ +GrpcPolledFd* NewGrpcPolledFdLocked(ares_socket_t as, + grpc_pollset_set* driver_pollset_set); +void ConfigureAresChannelLocked(ares_channel* channel); + +} // namespace grpc_core + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H \ */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc index b73e979e9f..5db832baf8 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc @@ -36,286 +36,60 @@ #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/sockaddr_utils.h" -typedef struct fd_node { - /** the owner of this fd node */ - grpc_ares_ev_driver* ev_driver; - /** a closure wrapping on_readable_locked, which should be - invoked when the grpc_fd in this node becomes readable. */ - grpc_closure read_closure; - /** a closure wrapping on_writable_locked, which should be - invoked when the grpc_fd in this node becomes writable. */ - grpc_closure write_closure; - /** next fd node in the list */ - struct fd_node* next; - - /** the grpc_fd owned by this fd node */ - grpc_fd* fd; - /** if the readable closure has been registered */ - bool readable_registered; - /** if the writable closure has been registered */ - bool writable_registered; - /** if the fd has been shutdown yet from grpc iomgr perspective */ - bool already_shutdown; -} fd_node; - -struct grpc_ares_ev_driver { - /** the ares_channel owned by this event driver */ - ares_channel channel; - /** pollset set for driving the IO events of the channel */ - grpc_pollset_set* pollset_set; - /** refcount of the event driver */ - gpr_refcount refs; - - /** combiner to synchronize c-ares and I/O callbacks on */ - grpc_combiner* combiner; - /** a list of grpc_fd that this event driver is currently using. */ - fd_node* fds; - /** is this event driver currently working? */ - bool working; - /** is this event driver being shut down */ - bool shutting_down; -}; - -static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver); - -static grpc_ares_ev_driver* grpc_ares_ev_driver_ref( - grpc_ares_ev_driver* ev_driver) { - gpr_log(GPR_DEBUG, "Ref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); - gpr_ref(&ev_driver->refs); - return ev_driver; -} - -static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver* ev_driver) { - gpr_log(GPR_DEBUG, "Unref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); - if (gpr_unref(&ev_driver->refs)) { - gpr_log(GPR_DEBUG, "destroy ev_driver %" PRIuPTR, (uintptr_t)ev_driver); - GPR_ASSERT(ev_driver->fds == nullptr); - GRPC_COMBINER_UNREF(ev_driver->combiner, "free ares event driver"); - ares_destroy(ev_driver->channel); - gpr_free(ev_driver); +namespace grpc_core { + +class GrpcPolledFdPosix : public GrpcPolledFd { + public: + GrpcPolledFdPosix(ares_socket_t as, grpc_pollset_set* driver_pollset_set) + : as_(as) { + gpr_asprintf(&name_, "c-ares fd: %d", (int)as); + fd_ = grpc_fd_create((int)as, name_, false); + grpc_pollset_set_add_fd(driver_pollset_set, fd_); } -} - -static void fd_node_destroy_locked(fd_node* fdn) { - gpr_log(GPR_DEBUG, "delete fd: %d", grpc_fd_wrapped_fd(fdn->fd)); - GPR_ASSERT(!fdn->readable_registered); - GPR_ASSERT(!fdn->writable_registered); - GPR_ASSERT(fdn->already_shutdown); - /* c-ares library will close the fd inside grpc_fd. This fd may be picked up - immediately by another thread, and should not be closed by the following - grpc_fd_orphan. */ - int dummy_release_fd; - grpc_fd_orphan(fdn->fd, nullptr, &dummy_release_fd, "c-ares query finished"); - gpr_free(fdn); -} -static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) { - if (!fdn->already_shutdown) { - fdn->already_shutdown = true; - grpc_fd_shutdown(fdn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING(reason)); + ~GrpcPolledFdPosix() { + gpr_free(name_); + /* c-ares library will close the fd inside grpc_fd. This fd may be picked up + immediately by another thread, and should not be closed by the following + grpc_fd_orphan. */ + int dummy_release_fd; + grpc_fd_orphan(fd_, nullptr, &dummy_release_fd, "c-ares query finished"); } -} -grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver, - grpc_pollset_set* pollset_set, - grpc_combiner* combiner) { - *ev_driver = static_cast<grpc_ares_ev_driver*>( - gpr_malloc(sizeof(grpc_ares_ev_driver))); - ares_options opts; - memset(&opts, 0, sizeof(opts)); - opts.flags |= ARES_FLAG_STAYOPEN; - int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS); - gpr_log(GPR_DEBUG, "grpc_ares_ev_driver_create_locked"); - if (status != ARES_SUCCESS) { - char* err_msg; - gpr_asprintf(&err_msg, "Failed to init ares channel. C-ares error: %s", - ares_strerror(status)); - grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); - gpr_free(err_msg); - gpr_free(*ev_driver); - return err; + void RegisterForOnReadableLocked(grpc_closure* read_closure) override { + grpc_fd_notify_on_read(fd_, read_closure); } - (*ev_driver)->combiner = GRPC_COMBINER_REF(combiner, "ares event driver"); - gpr_ref_init(&(*ev_driver)->refs, 1); - (*ev_driver)->pollset_set = pollset_set; - (*ev_driver)->fds = nullptr; - (*ev_driver)->working = false; - (*ev_driver)->shutting_down = false; - return GRPC_ERROR_NONE; -} -void grpc_ares_ev_driver_destroy_locked(grpc_ares_ev_driver* ev_driver) { - // We mark the event driver as being shut down. If the event driver - // is working, grpc_ares_notify_on_event_locked will shut down the - // fds; if it's not working, there are no fds to shut down. - ev_driver->shutting_down = true; - grpc_ares_ev_driver_unref(ev_driver); -} + void RegisterForOnWriteableLocked(grpc_closure* write_closure) override { + grpc_fd_notify_on_write(fd_, write_closure); + } -void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver) { - ev_driver->shutting_down = true; - fd_node* fn = ev_driver->fds; - while (fn != nullptr) { - fd_node_shutdown_locked(fn, "grpc_ares_ev_driver_shutdown"); - fn = fn->next; + bool IsFdStillReadableLocked() override { + size_t bytes_available = 0; + return ioctl(grpc_fd_wrapped_fd(fd_), FIONREAD, &bytes_available) == 0 && + bytes_available > 0; } -} -// Search fd in the fd_node list head. This is an O(n) search, the max possible -// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests. -static fd_node* pop_fd_node_locked(fd_node** head, int fd) { - fd_node dummy_head; - dummy_head.next = *head; - fd_node* node = &dummy_head; - while (node->next != nullptr) { - if (grpc_fd_wrapped_fd(node->next->fd) == fd) { - fd_node* ret = node->next; - node->next = node->next->next; - *head = dummy_head.next; - return ret; - } - node = node->next; + void ShutdownLocked(grpc_error* error) override { + grpc_fd_shutdown(fd_, error); } - return nullptr; -} -/* Check if \a fd is still readable */ -static bool grpc_ares_is_fd_still_readable_locked( - grpc_ares_ev_driver* ev_driver, int fd) { - size_t bytes_available = 0; - return ioctl(fd, FIONREAD, &bytes_available) == 0 && bytes_available > 0; -} + ares_socket_t GetWrappedAresSocketLocked() override { return as_; } -static void on_readable_locked(void* arg, grpc_error* error) { - fd_node* fdn = static_cast<fd_node*>(arg); - grpc_ares_ev_driver* ev_driver = fdn->ev_driver; - const int fd = grpc_fd_wrapped_fd(fdn->fd); - fdn->readable_registered = false; - gpr_log(GPR_DEBUG, "readable on %d", fd); - if (error == GRPC_ERROR_NONE) { - do { - ares_process_fd(ev_driver->channel, fd, ARES_SOCKET_BAD); - } while (grpc_ares_is_fd_still_readable_locked(ev_driver, fd)); - } else { - // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or - // timed out. The pending lookups made on this ev_driver will be cancelled - // by the following ares_cancel() and the on_done callbacks will be invoked - // with a status of ARES_ECANCELLED. The remaining file descriptors in this - // ev_driver will be cleaned up in the follwing - // grpc_ares_notify_on_event_locked(). - ares_cancel(ev_driver->channel); - } - grpc_ares_notify_on_event_locked(ev_driver); - grpc_ares_ev_driver_unref(ev_driver); -} + const char* GetName() override { return name_; } -static void on_writable_locked(void* arg, grpc_error* error) { - fd_node* fdn = static_cast<fd_node*>(arg); - grpc_ares_ev_driver* ev_driver = fdn->ev_driver; - const int fd = grpc_fd_wrapped_fd(fdn->fd); - fdn->writable_registered = false; - gpr_log(GPR_DEBUG, "writable on %d", fd); - if (error == GRPC_ERROR_NONE) { - ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, fd); - } else { - // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or - // timed out. The pending lookups made on this ev_driver will be cancelled - // by the following ares_cancel() and the on_done callbacks will be invoked - // with a status of ARES_ECANCELLED. The remaining file descriptors in this - // ev_driver will be cleaned up in the follwing - // grpc_ares_notify_on_event_locked(). - ares_cancel(ev_driver->channel); - } - grpc_ares_notify_on_event_locked(ev_driver); - grpc_ares_ev_driver_unref(ev_driver); -} + char* name_; + ares_socket_t as_; + grpc_fd* fd_; +}; -ares_channel* grpc_ares_ev_driver_get_channel_locked( - grpc_ares_ev_driver* ev_driver) { - return &ev_driver->channel; +GrpcPolledFd* NewGrpcPolledFdLocked(ares_socket_t as, + grpc_pollset_set* driver_pollset_set) { + return grpc_core::New<GrpcPolledFdPosix>(as, driver_pollset_set); } -// Get the file descriptors used by the ev_driver's ares channel, register -// driver_closure with these filedescriptors. -static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver) { - fd_node* new_list = nullptr; - if (!ev_driver->shutting_down) { - ares_socket_t socks[ARES_GETSOCK_MAXNUM]; - int socks_bitmask = - ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM); - for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) { - if (ARES_GETSOCK_READABLE(socks_bitmask, i) || - ARES_GETSOCK_WRITABLE(socks_bitmask, i)) { - fd_node* fdn = pop_fd_node_locked(&ev_driver->fds, socks[i]); - // Create a new fd_node if sock[i] is not in the fd_node list. - if (fdn == nullptr) { - char* fd_name; - gpr_asprintf(&fd_name, "ares_ev_driver-%" PRIuPTR, i); - fdn = static_cast<fd_node*>(gpr_malloc(sizeof(fd_node))); - gpr_log(GPR_DEBUG, "new fd: %d", socks[i]); - fdn->fd = grpc_fd_create(socks[i], fd_name, false); - fdn->ev_driver = ev_driver; - fdn->readable_registered = false; - fdn->writable_registered = false; - fdn->already_shutdown = false; - GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_locked, fdn, - grpc_combiner_scheduler(ev_driver->combiner)); - GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable_locked, fdn, - grpc_combiner_scheduler(ev_driver->combiner)); - grpc_pollset_set_add_fd(ev_driver->pollset_set, fdn->fd); - gpr_free(fd_name); - } - fdn->next = new_list; - new_list = fdn; - // Register read_closure if the socket is readable and read_closure has - // not been registered with this socket. - if (ARES_GETSOCK_READABLE(socks_bitmask, i) && - !fdn->readable_registered) { - grpc_ares_ev_driver_ref(ev_driver); - gpr_log(GPR_DEBUG, "notify read on: %d", grpc_fd_wrapped_fd(fdn->fd)); - grpc_fd_notify_on_read(fdn->fd, &fdn->read_closure); - fdn->readable_registered = true; - } - // Register write_closure if the socket is writable and write_closure - // has not been registered with this socket. - if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) && - !fdn->writable_registered) { - gpr_log(GPR_DEBUG, "notify write on: %d", - grpc_fd_wrapped_fd(fdn->fd)); - grpc_ares_ev_driver_ref(ev_driver); - grpc_fd_notify_on_write(fdn->fd, &fdn->write_closure); - fdn->writable_registered = true; - } - } - } - } - // Any remaining fds in ev_driver->fds were not returned by ares_getsock() and - // are therefore no longer in use, so they can be shut down and removed from - // the list. - while (ev_driver->fds != nullptr) { - fd_node* cur = ev_driver->fds; - ev_driver->fds = ev_driver->fds->next; - fd_node_shutdown_locked(cur, "c-ares fd shutdown"); - if (!cur->readable_registered && !cur->writable_registered) { - fd_node_destroy_locked(cur); - } else { - cur->next = new_list; - new_list = cur; - } - } - ev_driver->fds = new_list; - // If the ev driver has no working fd, all the tasks are done. - if (new_list == nullptr) { - ev_driver->working = false; - gpr_log(GPR_DEBUG, "ev driver stop working"); - } -} +void ConfigureAresChannelLocked(ares_channel* channel) {} -void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) { - if (!ev_driver->working) { - ev_driver->working = true; - grpc_ares_notify_on_event_locked(ev_driver); - } -} +} // namespace grpc_core #endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */ diff --git a/src/core/ext/filters/deadline/deadline_filter.cc b/src/core/ext/filters/deadline/deadline_filter.cc index e0a41a3637..d23ad67ad5 100644 --- a/src/core/ext/filters/deadline/deadline_filter.cc +++ b/src/core/ext/filters/deadline/deadline_filter.cc @@ -128,21 +128,25 @@ static void cancel_timer_if_needed(grpc_deadline_state* deadline_state) { } } -// Callback run when the call is complete. -static void on_complete(void* arg, grpc_error* error) { +// Callback run when we receive trailing metadata. +static void recv_trailing_metadata_ready(void* arg, grpc_error* error) { grpc_deadline_state* deadline_state = static_cast<grpc_deadline_state*>(arg); cancel_timer_if_needed(deadline_state); - // Invoke the next callback. - GRPC_CLOSURE_RUN(deadline_state->next_on_complete, GRPC_ERROR_REF(error)); + // Invoke the original callback. + GRPC_CLOSURE_RUN(deadline_state->original_recv_trailing_metadata_ready, + GRPC_ERROR_REF(error)); } -// Inject our own on_complete callback into op. -static void inject_on_complete_cb(grpc_deadline_state* deadline_state, - grpc_transport_stream_op_batch* op) { - deadline_state->next_on_complete = op->on_complete; - GRPC_CLOSURE_INIT(&deadline_state->on_complete, on_complete, deadline_state, +// Inject our own recv_trailing_metadata_ready callback into op. +static void inject_recv_trailing_metadata_ready( + grpc_deadline_state* deadline_state, grpc_transport_stream_op_batch* op) { + deadline_state->original_recv_trailing_metadata_ready = + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + GRPC_CLOSURE_INIT(&deadline_state->recv_trailing_metadata_ready, + recv_trailing_metadata_ready, deadline_state, grpc_schedule_on_exec_ctx); - op->on_complete = &deadline_state->on_complete; + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &deadline_state->recv_trailing_metadata_ready; } // Callback and associated state for starting the timer after call stack @@ -226,7 +230,7 @@ void grpc_deadline_state_client_start_transport_stream_op_batch( // Make sure we know when the call is complete, so that we can cancel // the timer. if (op->recv_trailing_metadata) { - inject_on_complete_cb(deadline_state, op); + inject_recv_trailing_metadata_ready(deadline_state, op); } } } @@ -322,7 +326,7 @@ static void server_start_transport_stream_op_batch( // the client never sends trailing metadata, because this is the // hook that tells us when the call is complete on the server side. if (op->recv_trailing_metadata) { - inject_on_complete_cb(&calld->base.deadline_state, op); + inject_recv_trailing_metadata_ready(&calld->base.deadline_state, op); } } // Chain to next filter. diff --git a/src/core/ext/filters/deadline/deadline_filter.h b/src/core/ext/filters/deadline/deadline_filter.h index 13207cbd6f..1d797f445a 100644 --- a/src/core/ext/filters/deadline/deadline_filter.h +++ b/src/core/ext/filters/deadline/deadline_filter.h @@ -37,12 +37,12 @@ typedef struct grpc_deadline_state { grpc_deadline_timer_state timer_state; grpc_timer timer; grpc_closure timer_callback; - // Closure to invoke when the call is complete. + // Closure to invoke when we receive trailing metadata. // We use this to cancel the timer. - grpc_closure on_complete; - // The original on_complete closure, which we chain to after our own - // closure is invoked. - grpc_closure* next_on_complete; + grpc_closure recv_trailing_metadata_ready; + // The original recv_trailing_metadata_ready closure, which we chain to + // after our own closure is invoked. + grpc_closure* original_recv_trailing_metadata_ready; } grpc_deadline_state; // diff --git a/src/core/ext/filters/http/client/http_client_filter.cc b/src/core/ext/filters/http/client/http_client_filter.cc index ae94ce47b9..1678051beb 100644 --- a/src/core/ext/filters/http/client/http_client_filter.cc +++ b/src/core/ext/filters/http/client/http_client_filter.cc @@ -55,8 +55,8 @@ struct call_data { grpc_closure recv_initial_metadata_ready; // State for handling recv_trailing_metadata ops. grpc_metadata_batch* recv_trailing_metadata; - grpc_closure* original_recv_trailing_metadata_on_complete; - grpc_closure recv_trailing_metadata_on_complete; + grpc_closure* original_recv_trailing_metadata_ready; + grpc_closure recv_trailing_metadata_ready; // State for handling send_message ops. grpc_transport_stream_op_batch* send_message_batch; size_t send_message_bytes_read; @@ -153,8 +153,7 @@ static void recv_initial_metadata_ready(void* user_data, grpc_error* error) { GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, error); } -static void recv_trailing_metadata_on_complete(void* user_data, - grpc_error* error) { +static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) { grpc_call_element* elem = static_cast<grpc_call_element*>(user_data); call_data* calld = static_cast<call_data*>(elem->call_data); if (error == GRPC_ERROR_NONE) { @@ -163,7 +162,7 @@ static void recv_trailing_metadata_on_complete(void* user_data, } else { GRPC_ERROR_REF(error); } - GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_on_complete, error); + GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error); } static void send_message_on_complete(void* arg, grpc_error* error) { @@ -312,8 +311,10 @@ static void hc_start_transport_stream_op_batch( /* substitute our callback for the higher callback */ calld->recv_trailing_metadata = batch->payload->recv_trailing_metadata.recv_trailing_metadata; - calld->original_recv_trailing_metadata_on_complete = batch->on_complete; - batch->on_complete = &calld->recv_trailing_metadata_on_complete; + calld->original_recv_trailing_metadata_ready = + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &calld->recv_trailing_metadata_ready; } grpc_error* error = GRPC_ERROR_NONE; @@ -420,8 +421,8 @@ static grpc_error* init_call_elem(grpc_call_element* elem, GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, recv_initial_metadata_ready, elem, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_on_complete, - recv_trailing_metadata_on_complete, elem, + GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready, + recv_trailing_metadata_ready, elem, grpc_schedule_on_exec_ctx); GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete, elem, grpc_schedule_on_exec_ctx); diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index a8090d18a6..0d6b72c66e 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -1149,12 +1149,10 @@ static void maybe_start_some_streams(grpc_chttp2_transport* t) { } } -/* Flag that this closure barrier wants stats to be updated before finishing */ -#define CLOSURE_BARRIER_STATS_BIT (1 << 0) /* Flag that this closure barrier may be covering a write in a pollset, and so we should not complete this closure until we can prove that the write got scheduled */ -#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 1) +#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 0) /* First bit of the reference count, stored in the high order bits (with the low bits being used for flags defined above) */ #define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16) @@ -1206,10 +1204,6 @@ void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t, grpc_error_add_child(closure->error_data.error, error); } if (closure->next_data.scratch < CLOSURE_BARRIER_FIRST_REF_BIT) { - if (closure->next_data.scratch & CLOSURE_BARRIER_STATS_BIT) { - grpc_transport_move_stats(&s->stats, s->collecting_stats); - s->collecting_stats = nullptr; - } if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) || !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) { GRPC_CLOSURE_RUN(closure, closure->error_data.error); @@ -1351,9 +1345,14 @@ static void perform_stream_op_locked(void* stream_op, } grpc_closure* on_complete = op->on_complete; + // TODO(roth): This is a hack needed because we use data inside of the + // closure itself to do the barrier calculation (i.e., to ensure that + // we don't schedule the closure until all ops in the batch have been + // completed). This can go away once we move to a new C++ closure API + // that provides the ability to create a barrier closure. if (on_complete == nullptr) { - on_complete = - GRPC_CLOSURE_CREATE(do_nothing, nullptr, grpc_schedule_on_exec_ctx); + on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing, + nullptr, grpc_schedule_on_exec_ctx); } /* use final_data as a barrier until enqueue time; the inital counter is @@ -1361,12 +1360,6 @@ static void perform_stream_op_locked(void* stream_op, on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT; on_complete->error_data.error = GRPC_ERROR_NONE; - if (op->collect_stats) { - GPR_ASSERT(s->collecting_stats == nullptr); - s->collecting_stats = op_payload->collect_stats.collect_stats; - on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT; - } - if (op->cancel_stream) { GRPC_STATS_INC_HTTP2_OP_CANCEL(); grpc_chttp2_cancel_stream(t, s, op_payload->cancel_stream.cancel_error); @@ -1600,8 +1593,11 @@ static void perform_stream_op_locked(void* stream_op, if (op->recv_trailing_metadata) { GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA(); + GPR_ASSERT(s->collecting_stats == nullptr); + s->collecting_stats = op_payload->recv_trailing_metadata.collect_stats; GPR_ASSERT(s->recv_trailing_metadata_finished == nullptr); - s->recv_trailing_metadata_finished = add_closure_barrier(on_complete); + s->recv_trailing_metadata_finished = + op_payload->recv_trailing_metadata.recv_trailing_metadata_ready; s->recv_trailing_metadata = op_payload->recv_trailing_metadata.recv_trailing_metadata; s->final_metadata_requested = true; @@ -1960,11 +1956,12 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t, } if (s->read_closed && s->frame_storage.length == 0 && !pending_data && s->recv_trailing_metadata_finished != nullptr) { + grpc_transport_move_stats(&s->stats, s->collecting_stats); + s->collecting_stats = nullptr; grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[1], s->recv_trailing_metadata); - grpc_chttp2_complete_closure_step( - t, s, &s->recv_trailing_metadata_finished, GRPC_ERROR_NONE, - "recv_trailing_metadata_finished"); + null_then_run_closure(&s->recv_trailing_metadata_finished, + GRPC_ERROR_NONE); } } } diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.cc b/src/core/ext/transport/cronet/transport/cronet_transport.cc index 420c2d13e1..4a252d972d 100644 --- a/src/core/ext/transport/cronet/transport/cronet_transport.cc +++ b/src/core/ext/transport/cronet/transport/cronet_transport.cc @@ -925,6 +925,10 @@ static bool op_can_be_run(grpc_transport_stream_op_batch* curr_op, result = false; } /* Check if every op that was asked for is done. */ + /* TODO(muxi): We should not consider the recv ops here, since they + * have their own callbacks. We should invoke a batch's on_complete + * as soon as all of the batch's send ops are complete, even if + * there are still recv ops pending. */ else if (curr_op->send_initial_metadata && !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) { CRONET_LOG(GPR_DEBUG, "Because"); @@ -1280,12 +1284,20 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) { op_can_be_run(stream_op, s, &oas->state, OP_RECV_TRAILING_METADATA)) { CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_TRAILING_METADATA", oas); - if (oas->s->state.rs.trailing_metadata_valid) { + grpc_error* error = GRPC_ERROR_NONE; + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + error = GRPC_ERROR_REF(stream_state->cancel_error); + } else if (stream_state->state_op_done[OP_FAILED]) { + error = make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable."); + } else if (oas->s->state.rs.trailing_metadata_valid) { grpc_chttp2_incoming_metadata_buffer_publish( &oas->s->state.rs.trailing_metadata, stream_op->payload->recv_trailing_metadata.recv_trailing_metadata); stream_state->rs.trailing_metadata_valid = false; } + GRPC_CLOSURE_SCHED( + stream_op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + error); stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true; result = ACTION_TAKEN_NO_CALLBACK; } else if (stream_op->cancel_stream && @@ -1398,6 +1410,11 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs, GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready, GRPC_ERROR_CANCELLED); } + if (op->recv_trailing_metadata) { + GRPC_CLOSURE_SCHED( + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + GRPC_ERROR_CANCELLED); + } GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_CANCELLED); return; } diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc index 2c3bff5c1e..b0ca7f8207 100644 --- a/src/core/ext/transport/inproc/inproc_transport.cc +++ b/src/core/ext/transport/inproc/inproc_transport.cc @@ -120,7 +120,6 @@ typedef struct inproc_stream { struct inproc_stream* stream_list_next; } inproc_stream; -static grpc_closure do_nothing_closure; static bool cancel_stream_locked(inproc_stream* s, grpc_error* error); static void op_state_machine(void* arg, grpc_error* error); @@ -373,6 +372,10 @@ static void complete_if_batch_end_locked(inproc_stream* s, grpc_error* error, const char* msg) { int is_sm = static_cast<int>(op == s->send_message_op); int is_stm = static_cast<int>(op == s->send_trailing_md_op); + // TODO(vjpai): We should not consider the recv ops here, since they + // have their own callbacks. We should invoke a batch's on_complete + // as soon as all of the batch's send ops are complete, even if there + // are still recv ops pending. int is_rim = static_cast<int>(op == s->recv_initial_md_op); int is_rm = static_cast<int>(op == s->recv_message_op); int is_rtm = static_cast<int>(op == s->recv_trailing_md_op); @@ -496,6 +499,11 @@ static void fail_helper_locked(inproc_stream* s, grpc_error* error) { s->send_trailing_md_op = nullptr; } if (s->recv_trailing_md_op) { + INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-metadata-ready %p", + s, error); + GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_REF(error)); INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-md-on-complete %p", s, error); complete_if_batch_end_locked( @@ -639,6 +647,12 @@ static void op_state_machine(void* arg, grpc_error* error) { s->trailing_md_sent = true; if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) { INPROC_LOG(GPR_INFO, + "op_state_machine %p scheduling trailing-metadata-ready", s); + GRPC_CLOSURE_SCHED( + s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_NONE); + INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling trailing-md-on-complete", s); GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete, GRPC_ERROR_NONE); @@ -711,6 +725,12 @@ static void op_state_machine(void* arg, grpc_error* error) { } if (s->recv_trailing_md_op && s->t->is_client && other && other->send_message_op) { + INPROC_LOG(GPR_INFO, + "op_state_machine %p scheduling trailing-metadata-ready %p", s, + GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_NONE); maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE); } if (s->to_read_trailing_md_filled) { @@ -766,6 +786,10 @@ static void op_state_machine(void* arg, grpc_error* error) { INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling trailing-md-on-complete %p", s, new_err); + GRPC_CLOSURE_SCHED( + s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_REF(new_err)); GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete, GRPC_ERROR_REF(new_err)); s->recv_trailing_md_op = nullptr; @@ -859,6 +883,9 @@ static bool cancel_stream_locked(inproc_stream* s, grpc_error* error) { // couldn't complete that because we hadn't yet sent out trailing // md, now's the chance if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) { + GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_REF(s->cancel_self_error)); complete_if_batch_end_locked( s, s->cancel_self_error, s->recv_trailing_md_op, "cancel_stream scheduling trailing-md-on-complete"); @@ -873,6 +900,8 @@ static bool cancel_stream_locked(inproc_stream* s, grpc_error* error) { return ret; } +static void do_nothing(void* arg, grpc_error* error) {} + static void perform_stream_op(grpc_transport* gt, grpc_stream* gs, grpc_transport_stream_op_batch* op) { INPROC_LOG(GPR_INFO, "perform_stream_op %p %p %p", gt, gs, op); @@ -892,8 +921,14 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs, } grpc_error* error = GRPC_ERROR_NONE; grpc_closure* on_complete = op->on_complete; + // TODO(roth): This is a hack needed because we use data inside of the + // closure itself to do the barrier calculation (i.e., to ensure that + // we don't schedule the closure until all ops in the batch have been + // completed). This can go away once we move to a new C++ closure API + // that provides the ability to create a barrier closure. if (on_complete == nullptr) { - on_complete = &do_nothing_closure; + on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing, + nullptr, grpc_schedule_on_exec_ctx); } if (op->cancel_stream) { @@ -1026,6 +1061,15 @@ static void perform_stream_op(grpc_transport* gt, grpc_stream* gs, GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready, GRPC_ERROR_REF(error)); } + if (op->recv_trailing_metadata) { + INPROC_LOG( + GPR_INFO, + "perform_stream_op error %p scheduling trailing-metadata-ready %p", + s, error); + GRPC_CLOSURE_SCHED( + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + GRPC_ERROR_REF(error)); + } } INPROC_LOG(GPR_INFO, "perform_stream_op %p scheduling on_complete %p", s, error); @@ -1129,12 +1173,8 @@ static grpc_endpoint* get_endpoint(grpc_transport* t) { return nullptr; } /******************************************************************************* * GLOBAL INIT AND DESTROY */ -static void do_nothing(void* arg, grpc_error* error) {} - void grpc_inproc_transport_init(void) { grpc_core::ExecCtx exec_ctx; - GRPC_CLOSURE_INIT(&do_nothing_closure, do_nothing, nullptr, - grpc_schedule_on_exec_ctx); g_empty_slice = grpc_slice_from_static_buffer(nullptr, 0); grpc_slice key_tmp = grpc_slice_from_static_string(":path"); diff --git a/src/core/lib/channel/connected_channel.cc b/src/core/lib/channel/connected_channel.cc index ddd3029402..e2ea334ded 100644 --- a/src/core/lib/channel/connected_channel.cc +++ b/src/core/lib/channel/connected_channel.cc @@ -51,6 +51,7 @@ typedef struct connected_channel_call_data { callback_state on_complete[6]; // Max number of pending batches. callback_state recv_initial_metadata_ready; callback_state recv_message_ready; + callback_state recv_trailing_metadata_ready; } call_data; static void run_in_call_combiner(void* arg, grpc_error* error) { @@ -111,6 +112,12 @@ static void con_start_transport_stream_op_batch( intercept_callback(calld, state, false, "recv_message_ready", &batch->payload->recv_message.recv_message_ready); } + if (batch->recv_trailing_metadata) { + callback_state* state = &calld->recv_trailing_metadata_ready; + intercept_callback( + calld, state, false, "recv_trailing_metadata_ready", + &batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready); + } if (batch->cancel_stream) { // There can be more than one cancellation batch in flight at any // given time, so we can't just pick out a fixed index into @@ -121,7 +128,7 @@ static void con_start_transport_stream_op_batch( static_cast<callback_state*>(gpr_malloc(sizeof(*state))); intercept_callback(calld, state, true, "on_complete (cancel_stream)", &batch->on_complete); - } else { + } else if (batch->on_complete != nullptr) { callback_state* state = get_state_for_batch(calld, batch); intercept_callback(calld, state, false, "on_complete", &batch->on_complete); } diff --git a/src/core/lib/gprpp/inlined_vector.h b/src/core/lib/gprpp/inlined_vector.h index f36f6cb706..0d2586e507 100644 --- a/src/core/lib/gprpp/inlined_vector.h +++ b/src/core/lib/gprpp/inlined_vector.h @@ -99,6 +99,8 @@ class InlinedVector { void push_back(T&& value) { emplace_back(std::move(value)); } size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + size_t capacity() const { return capacity_; } void clear() { diff --git a/src/core/lib/iomgr/call_combiner.h b/src/core/lib/iomgr/call_combiner.h index 0ccd08ea57..641fa18082 100644 --- a/src/core/lib/iomgr/call_combiner.h +++ b/src/core/lib/iomgr/call_combiner.h @@ -26,6 +26,7 @@ #include <grpc/support/atm.h> #include "src/core/lib/gpr/mpscq.h" +#include "src/core/lib/gprpp/inlined_vector.h" #include "src/core/lib/iomgr/closure.h" // A simple, lock-free mechanism for serializing activity related to a @@ -109,4 +110,83 @@ void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner, void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner, grpc_error* error); +namespace grpc_core { + +// Helper for running a list of closures in a call combiner. +// +// Each callback running in the call combiner will eventually be +// returned to the surface, at which point the surface will yield the +// call combiner. So when we are running in the call combiner and have +// more than one callback to return to the surface, we need to re-enter +// the call combiner for all but one of those callbacks. +class CallCombinerClosureList { + public: + CallCombinerClosureList() {} + + // Adds a closure to the list. The closure must eventually result in + // the call combiner being yielded. + void Add(grpc_closure* closure, grpc_error* error, const char* reason) { + closures_.emplace_back(closure, error, reason); + } + + // Runs all closures in the call combiner and yields the call combiner. + // + // All but one of the closures in the list will be scheduled via + // GRPC_CALL_COMBINER_START(), and the remaining closure will be + // scheduled via GRPC_CLOSURE_SCHED(), which will eventually result in + // yielding the call combiner. If the list is empty, then the call + // combiner will be yielded immediately. + void RunClosures(grpc_call_combiner* call_combiner) { + if (closures_.empty()) { + GRPC_CALL_COMBINER_STOP(call_combiner, "no closures to schedule"); + return; + } + for (size_t i = 1; i < closures_.size(); ++i) { + auto& closure = closures_[i]; + GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error, + closure.reason); + } + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, + "CallCombinerClosureList executing closure while already " + "holding call_combiner %p: closure=%p error=%s reason=%s", + call_combiner, closures_[0].closure, + grpc_error_string(closures_[0].error), closures_[0].reason); + } + // This will release the call combiner. + GRPC_CLOSURE_SCHED(closures_[0].closure, closures_[0].error); + closures_.clear(); + } + + // Runs all closures in the call combiner, but does NOT yield the call + // combiner. All closures will be scheduled via GRPC_CALL_COMBINER_START(). + void RunClosuresWithoutYielding(grpc_call_combiner* call_combiner) { + for (size_t i = 0; i < closures_.size(); ++i) { + auto& closure = closures_[i]; + GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error, + closure.reason); + } + closures_.clear(); + } + + size_t size() const { return closures_.size(); } + + private: + struct CallCombinerClosure { + grpc_closure* closure; + grpc_error* error; + const char* reason; + + CallCombinerClosure(grpc_closure* closure, grpc_error* error, + const char* reason) + : closure(closure), error(error), reason(reason) {} + }; + + // There are generally a maximum of 6 closures to run in the call + // combiner, one for each pending op. + InlinedVector<CallCombinerClosure, 6> closures_; +}; + +} // namespace grpc_core + #endif /* GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H */ diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h index 34a494485d..f14c723844 100644 --- a/src/core/lib/iomgr/closure.h +++ b/src/core/lib/iomgr/closure.h @@ -283,9 +283,10 @@ inline void grpc_closure_sched(grpc_closure* c, grpc_error* error) { if (c->scheduled) { gpr_log(GPR_ERROR, "Closure already scheduled. (closure: %p, created: [%s:%d], " - "previously scheduled at: [%s: %d] run?: %s", + "previously scheduled at: [%s: %d], newly scheduled at [%s: %d], " + "run?: %s", c, c->file_created, c->line_created, c->file_initiated, - c->line_initiated, c->run ? "true" : "false"); + c->line_initiated, file, line, c->run ? "true" : "false"); abort(); } c->scheduled = true; diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/src/core/lib/security/credentials/google_default/google_default_credentials.cc index 38c9175717..c456ffaf5d 100644 --- a/src/core/lib/security/credentials/google_default/google_default_credentials.cc +++ b/src/core/lib/security/credentials/google_default/google_default_credentials.cc @@ -231,7 +231,8 @@ end: creds->base.vtable = &google_default_credentials_vtable; creds->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_GOOGLE_DEFAULT; gpr_ref_init(&creds->base.refcount, 1); - creds->ssl_creds = grpc_ssl_credentials_create(nullptr, nullptr, nullptr); + creds->ssl_creds = + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); GPR_ASSERT(creds->ssl_creds != nullptr); grpc_alts_credentials_options* options = grpc_alts_credentials_client_options_create(); diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/src/core/lib/security/credentials/ssl/ssl_credentials.cc index 2b6377d3ec..3d6f2f200a 100644 --- a/src/core/lib/security/credentials/ssl/ssl_credentials.cc +++ b/src/core/lib/security/credentials/ssl/ssl_credentials.cc @@ -48,6 +48,10 @@ static void ssl_destruct(grpc_channel_credentials* creds) { grpc_ssl_credentials* c = reinterpret_cast<grpc_ssl_credentials*>(creds); gpr_free(c->config.pem_root_certs); grpc_tsi_ssl_pem_key_cert_pairs_destroy(c->config.pem_key_cert_pair, 1); + if (c->config.verify_options.verify_peer_destruct != nullptr) { + c->config.verify_options.verify_peer_destruct( + c->config.verify_options.verify_peer_callback_userdata); + } } static grpc_security_status ssl_create_security_connector( @@ -87,6 +91,7 @@ static grpc_channel_credentials_vtable ssl_vtable = { static void ssl_build_config(const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, + const verify_peer_options* verify_options, grpc_ssl_config* config) { if (pem_root_certs != nullptr) { config->pem_root_certs = gpr_strdup(pem_root_certs); @@ -101,23 +106,32 @@ static void ssl_build_config(const char* pem_root_certs, config->pem_key_cert_pair->private_key = gpr_strdup(pem_key_cert_pair->private_key); } + if (verify_options != nullptr) { + memcpy(&config->verify_options, verify_options, + sizeof(verify_peer_options)); + } else { + // Otherwise set all options to default values + memset(&config->verify_options, 0, sizeof(verify_peer_options)); + } } grpc_channel_credentials* grpc_ssl_credentials_create( const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, - void* reserved) { + const verify_peer_options* verify_options, void* reserved) { grpc_ssl_credentials* c = static_cast<grpc_ssl_credentials*>( gpr_zalloc(sizeof(grpc_ssl_credentials))); GRPC_API_TRACE( "grpc_ssl_credentials_create(pem_root_certs=%s, " "pem_key_cert_pair=%p, " + "verify_options=%p, " "reserved=%p)", - 3, (pem_root_certs, pem_key_cert_pair, reserved)); + 4, (pem_root_certs, pem_key_cert_pair, verify_options, reserved)); GPR_ASSERT(reserved == nullptr); c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL; c->base.vtable = &ssl_vtable; gpr_ref_init(&c->base.refcount, 1); - ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config); + ssl_build_config(pem_root_certs, pem_key_cert_pair, verify_options, + &c->config); return &c->base; } diff --git a/src/core/lib/security/security_connector/security_connector.cc b/src/core/lib/security/security_connector/security_connector.cc index b54a7643e4..cc72bb6164 100644 --- a/src/core/lib/security/security_connector/security_connector.cc +++ b/src/core/lib/security/security_connector/security_connector.cc @@ -620,6 +620,7 @@ typedef struct { tsi_ssl_client_handshaker_factory* client_handshaker_factory; char* target_name; char* overridden_target_name; + const verify_peer_options* verify_options; } grpc_ssl_channel_security_connector; typedef struct { @@ -878,11 +879,34 @@ static void ssl_channel_check_peer(grpc_security_connector* sc, tsi_peer peer, grpc_closure* on_peer_checked) { grpc_ssl_channel_security_connector* c = reinterpret_cast<grpc_ssl_channel_security_connector*>(sc); - grpc_error* error = ssl_check_peer(sc, - c->overridden_target_name != nullptr - ? c->overridden_target_name - : c->target_name, - &peer, auth_context); + const char* target_name = c->overridden_target_name != nullptr + ? c->overridden_target_name + : c->target_name; + grpc_error* error = ssl_check_peer(sc, target_name, &peer, auth_context); + if (error == GRPC_ERROR_NONE && + c->verify_options->verify_peer_callback != nullptr) { + const tsi_peer_property* p = + tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY); + if (p == nullptr) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: missing pem cert property."); + } else { + char* peer_pem = static_cast<char*>(gpr_malloc(p->value.length + 1)); + memcpy(peer_pem, p->value.data, p->value.length); + peer_pem[p->value.length] = '\0'; + int callback_status = c->verify_options->verify_peer_callback( + target_name, peer_pem, + c->verify_options->verify_peer_callback_userdata); + gpr_free(peer_pem); + if (callback_status) { + char* msg; + gpr_asprintf(&msg, "Verify peer callback returned a failure (%d)", + callback_status); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } + } + } GRPC_CLOSURE_SCHED(on_peer_checked, error); tsi_peer_destruct(&peer); } @@ -1047,6 +1071,7 @@ grpc_security_status grpc_ssl_channel_security_connector_create( if (overridden_target_name != nullptr) { c->overridden_target_name = gpr_strdup(overridden_target_name); } + c->verify_options = &config->verify_options; has_key_cert_pair = config->pem_key_cert_pair != nullptr && config->pem_key_cert_pair->private_key != nullptr && diff --git a/src/core/lib/security/security_connector/security_connector.h b/src/core/lib/security/security_connector/security_connector.h index f9723166d0..67a506b576 100644 --- a/src/core/lib/security/security_connector/security_connector.h +++ b/src/core/lib/security/security_connector/security_connector.h @@ -193,6 +193,7 @@ grpc_server_security_connector* grpc_fake_server_security_connector_create( typedef struct { tsi_ssl_pem_key_cert_pair* pem_key_cert_pair; char* pem_root_certs; + verify_peer_options verify_options; } grpc_ssl_config; /* Creates an SSL channel_security_connector. diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 2434c9b952..f94035459f 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -233,6 +233,7 @@ struct grpc_call { grpc_closure receiving_slice_ready; grpc_closure receiving_stream_ready; grpc_closure receiving_initial_metadata_ready; + grpc_closure receiving_trailing_metadata_ready; uint32_t test_only_last_message_flags; grpc_closure release_call; @@ -270,8 +271,17 @@ struct grpc_call { grpc_core::TraceFlag grpc_call_error_trace(false, "call_error"); grpc_core::TraceFlag grpc_compression_trace(false, "compression"); -#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack*)((call) + 1)) -#define CALL_FROM_CALL_STACK(call_stack) (((grpc_call*)(call_stack)) - 1) +/* Given a size, round up to the next multiple of sizeof(void*) */ +#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ + (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) + +#define CALL_STACK_FROM_CALL(call) \ + (grpc_call_stack*)((char*)(call) + \ + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call))) +#define CALL_FROM_CALL_STACK(call_stack) \ + (grpc_call*)(((char*)(call_stack)) - \ + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call))) + #define CALL_ELEM_FROM_CALL(call, idx) \ grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx) #define CALL_FROM_TOP_ELEM(top_elem) \ @@ -342,8 +352,9 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, size_t initial_size = grpc_channel_get_call_size_estimate(args->channel); GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size); gpr_arena* arena = gpr_arena_create(initial_size); - call = static_cast<grpc_call*>(gpr_arena_alloc( - arena, sizeof(grpc_call) + channel_stack->call_stack_size)); + call = static_cast<grpc_call*>( + gpr_arena_alloc(arena, ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) + + channel_stack->call_stack_size)); gpr_ref_init(&call->ext_ref, 1); call->arena = arena; grpc_call_combiner_init(&call->call_combiner); @@ -1211,7 +1222,6 @@ static void post_batch_completion(batch_control* bctl) { if (bctl->op.send_initial_metadata) { grpc_metadata_batch_destroy( - &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]); } if (bctl->op.send_message) { @@ -1219,14 +1229,9 @@ static void post_batch_completion(batch_control* bctl) { } if (bctl->op.send_trailing_metadata) { grpc_metadata_batch_destroy( - &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]); } if (bctl->op.recv_trailing_metadata) { - grpc_metadata_batch* md = - &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - recv_trailing_filter(call, md); - /* propagate cancellation to any interested children */ gpr_atm_rel_store(&call->received_final_op_atm, 1); parent_call* pc = get_parent_call(call); @@ -1248,7 +1253,6 @@ static void post_batch_completion(batch_control* bctl) { } gpr_mu_unlock(&pc->child_list_mu); } - if (call->is_client) { get_final_status(call, set_status_value_directly, call->final_op.client.status, @@ -1258,7 +1262,6 @@ static void post_batch_completion(batch_control* bctl) { get_final_status(call, set_cancelled_value, call->final_op.server.cancelled, nullptr, nullptr); } - grpc_core::channelz::ChannelNode* channelz_channel = grpc_channel_get_channelz_node(call->channel); if (*call->final_op.client.status != GRPC_STATUS_OK) { @@ -1266,7 +1269,6 @@ static void post_batch_completion(batch_control* bctl) { } else { channelz_channel->RecordCallSucceeded(); } - GRPC_ERROR_UNREF(error); error = GRPC_ERROR_NONE; } @@ -1548,6 +1550,17 @@ static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) { finish_batch_step(bctl); } +static void receiving_trailing_metadata_ready(void* bctlp, grpc_error* error) { + batch_control* bctl = static_cast<batch_control*>(bctlp); + grpc_call* call = bctl->call; + GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_trailing_metadata_ready"); + add_batch_error(bctl, GRPC_ERROR_REF(error), false); + grpc_metadata_batch* md = + &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; + recv_trailing_filter(call, md); + finish_batch_step(bctl); +} + static void finish_batch(void* bctlp, grpc_error* error) { batch_control* bctl = static_cast<batch_control*>(bctlp); grpc_call* call = bctl->call; @@ -1568,7 +1581,8 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, size_t i; const grpc_op* op; batch_control* bctl; - int num_completion_callbacks_needed = 1; + bool has_send_ops = false; + int num_recv_ops = 0; grpc_call_error error = GRPC_CALL_OK; grpc_transport_stream_op_batch* stream_op; grpc_transport_stream_op_batch_payload* stream_op_payload; @@ -1674,6 +1688,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, stream_op_payload->send_initial_metadata.peer_string = &call->peer_string; } + has_send_ops = true; break; } case GRPC_OP_SEND_MESSAGE: { @@ -1703,6 +1718,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, &op->data.send_message.send_message->data.raw.slice_buffer, flags); stream_op_payload->send_message.send_message.reset( call->sending_stream.get()); + has_send_ops = true; break; } case GRPC_OP_SEND_CLOSE_FROM_CLIENT: { @@ -1723,6 +1739,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, call->sent_final_op = true; stream_op_payload->send_trailing_metadata.send_trailing_metadata = &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; + has_send_ops = true; break; } case GRPC_OP_SEND_STATUS_FROM_SERVER: { @@ -1787,6 +1804,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, } stream_op_payload->send_trailing_metadata.send_trailing_metadata = &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; + has_send_ops = true; break; } case GRPC_OP_RECV_INITIAL_METADATA: { @@ -1814,7 +1832,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, stream_op_payload->recv_initial_metadata.peer_string = &call->peer_string; } - num_completion_callbacks_needed++; + ++num_recv_ops; break; } case GRPC_OP_RECV_MESSAGE: { @@ -1836,7 +1854,7 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, grpc_schedule_on_exec_ctx); stream_op_payload->recv_message.recv_message_ready = &call->receiving_stream_ready; - num_completion_callbacks_needed++; + ++num_recv_ops; break; } case GRPC_OP_RECV_STATUS_ON_CLIENT: { @@ -1862,11 +1880,16 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, call->final_op.client.error_string = op->data.recv_status_on_client.error_string; stream_op->recv_trailing_metadata = true; - stream_op->collect_stats = true; stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - stream_op_payload->collect_stats.collect_stats = + stream_op_payload->recv_trailing_metadata.collect_stats = &call->final_info.stats.transport_stream_stats; + GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready, + receiving_trailing_metadata_ready, bctl, + grpc_schedule_on_exec_ctx); + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &call->receiving_trailing_metadata_ready; + ++num_recv_ops; break; } case GRPC_OP_RECV_CLOSE_ON_SERVER: { @@ -1887,11 +1910,16 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, call->final_op.server.cancelled = op->data.recv_close_on_server.cancelled; stream_op->recv_trailing_metadata = true; - stream_op->collect_stats = true; stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - stream_op_payload->collect_stats.collect_stats = + stream_op_payload->recv_trailing_metadata.collect_stats = &call->final_info.stats.transport_stream_stats; + GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready, + receiving_trailing_metadata_ready, bctl, + grpc_schedule_on_exec_ctx); + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &call->receiving_trailing_metadata_ready; + ++num_recv_ops; break; } } @@ -1901,13 +1929,15 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, if (!is_notify_tag_closure) { GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag)); } - gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed); + gpr_ref_init(&bctl->steps_to_complete, (has_send_ops ? 1 : 0) + num_recv_ops); - GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl, - grpc_schedule_on_exec_ctx); - stream_op->on_complete = &bctl->finish_batch; - gpr_atm_rel_store(&call->any_ops_sent_atm, 1); + if (has_send_ops) { + GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl, + grpc_schedule_on_exec_ctx); + stream_op->on_complete = &bctl->finish_batch; + } + gpr_atm_rel_store(&call->any_ops_sent_atm, 1); execute_batch(call, stream_op, &bctl->start_batch); done: diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc index 039d603394..cbdb77c844 100644 --- a/src/core/lib/transport/transport.cc +++ b/src/core/lib/transport/transport.cc @@ -212,21 +212,32 @@ void grpc_transport_stream_op_batch_finish_with_failure( if (batch->send_message) { batch->payload->send_message.send_message.reset(); } - if (batch->recv_message) { - GRPC_CALL_COMBINER_START( - call_combiner, batch->payload->recv_message.recv_message_ready, - GRPC_ERROR_REF(error), "failing recv_message_ready"); + if (batch->cancel_stream) { + GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error); } + // Construct a list of closures to execute. + grpc_core::CallCombinerClosureList closures; if (batch->recv_initial_metadata) { - GRPC_CALL_COMBINER_START( - call_combiner, + closures.Add( batch->payload->recv_initial_metadata.recv_initial_metadata_ready, GRPC_ERROR_REF(error), "failing recv_initial_metadata_ready"); } - GRPC_CLOSURE_SCHED(batch->on_complete, error); - if (batch->cancel_stream) { - GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error); + if (batch->recv_message) { + closures.Add(batch->payload->recv_message.recv_message_ready, + GRPC_ERROR_REF(error), "failing recv_message_ready"); + } + if (batch->recv_trailing_metadata) { + closures.Add( + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + GRPC_ERROR_REF(error), "failing recv_trailing_metadata_ready"); + } + if (batch->on_complete != nullptr) { + closures.Add(batch->on_complete, GRPC_ERROR_REF(error), + "failing on_complete"); } + // Execute closures. + closures.RunClosures(call_combiner); + GRPC_ERROR_UNREF(error); } typedef struct { diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h index b2e252d939..585b9dfae9 100644 --- a/src/core/lib/transport/transport.h +++ b/src/core/lib/transport/transport.h @@ -122,9 +122,15 @@ typedef struct grpc_transport_stream_op_batch_payload /* Transport stream op: a set of operations to perform on a transport against a single stream */ typedef struct grpc_transport_stream_op_batch { - /** Should be enqueued when all requested operations (excluding recv_message - and recv_initial_metadata which have their own closures) in a given batch - have been completed. */ + /** Should be scheduled when all of the non-recv operations in the batch + are complete. + + The recv ops (recv_initial_metadata, recv_message, and + recv_trailing_metadata) each have their own callbacks. If a batch + contains both recv ops and non-recv ops, on_complete should be + scheduled as soon as the non-recv ops are complete, regardless of + whether or not the recv ops are complete. If a batch contains + only recv ops, on_complete can be null. */ grpc_closure* on_complete; /** Values for the stream op (fields set are determined by flags above) */ @@ -149,9 +155,6 @@ typedef struct grpc_transport_stream_op_batch { */ bool recv_trailing_metadata : 1; - /** Collect any stats into provided buffer, zero internal stat counters */ - bool collect_stats : 1; - /** Cancel this stream with the provided error */ bool cancel_stream : 1; @@ -219,11 +222,10 @@ struct grpc_transport_stream_op_batch_payload { struct { grpc_metadata_batch* recv_trailing_metadata; - } recv_trailing_metadata; - - struct { grpc_transport_stream_stats* collect_stats; - } collect_stats; + /** Should be enqueued when initial metadata is ready to be processed. */ + grpc_closure* recv_trailing_metadata_ready; + } recv_trailing_metadata; /** Forcefully close this stream. The HTTP2 semantics should be: diff --git a/src/core/lib/transport/transport_op_string.cc b/src/core/lib/transport/transport_op_string.cc index 25ab492f3a..8c7db642a5 100644 --- a/src/core/lib/transport/transport_op_string.cc +++ b/src/core/lib/transport/transport_op_string.cc @@ -120,13 +120,6 @@ char* grpc_transport_stream_op_batch_string( gpr_strvec_add(&b, tmp); } - if (op->collect_stats) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_asprintf(&tmp, "COLLECT_STATS:%p", - op->payload->collect_stats.collect_stats); - gpr_strvec_add(&b, tmp); - } - out = gpr_strvec_flatten(&b, nullptr); gpr_strvec_destroy(&b); diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index 00245b397d..bdb6359632 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -83,7 +83,8 @@ std::shared_ptr<ChannelCredentials> SslCredentials( grpc_channel_credentials* c_creds = grpc_ssl_credentials_create( options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(), - options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr); + options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr, + nullptr); return WrapChannelCredentials(c_creds); } diff --git a/test/core/statistics/small_log_test.cc b/src/cpp/ext/filters/census/channel_filter.cc index fb8dfc9988..4ac684d277 100644 --- a/test/core/statistics/small_log_test.cc +++ b/src/cpp/ext/filters/census/channel_filter.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015 gRPC authors. + * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,15 @@ * */ -#include "test/core/statistics/census_log_tests.h" +#include <grpc/support/port_platform.h> -#include <stdlib.h> +#include "src/cpp/ext/filters/census/channel_filter.h" -#include <grpc/support/time.h> -#include "test/core/util/test_config.h" +namespace grpc { -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - srand(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); - test_small_log(); - return 0; +grpc_error* CensusChannelData::Init(grpc_channel_element* elem, + grpc_channel_element_args* args) { + return GRPC_ERROR_NONE; } + +} // namespace grpc diff --git a/test/core/statistics/multiple_writers_circular_buffer_test.cc b/src/cpp/ext/filters/census/channel_filter.h index 6d3411289e..0b7c682681 100644 --- a/test/core/statistics/multiple_writers_circular_buffer_test.cc +++ b/src/cpp/ext/filters/census/channel_filter.h @@ -1,6 +1,6 @@ /* * - * Copyright 2015 gRPC authors. + * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,21 @@ * */ -#include "test/core/statistics/census_log_tests.h" +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H -#include <stdlib.h> +#include <grpc/support/port_platform.h> -#include <grpc/support/time.h> -#include "test/core/util/test_config.h" +#include "src/cpp/ext/filters/census/context.h" -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - srand(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); - test_multiple_writers_circular_log(); - return 0; -} +namespace grpc { + +class CensusChannelData : public ChannelData { + public: + grpc_error* Init(grpc_channel_element* elem, + grpc_channel_element_args* args) override; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H */ diff --git a/src/cpp/ext/filters/census/client_filter.cc b/src/cpp/ext/filters/census/client_filter.cc new file mode 100644 index 0000000000..293f4b1c25 --- /dev/null +++ b/src/cpp/ext/filters/census/client_filter.cc @@ -0,0 +1,163 @@ +/* + * + * Copyright 2018 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/cpp/ext/filters/census/client_filter.h" + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "opencensus/stats/stats.h" +#include "src/core/lib/surface/call.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" +#include "src/cpp/ext/filters/census/measures.h" + +namespace grpc { + +constexpr uint32_t CensusClientCallData::kMaxTraceContextLen; +constexpr uint32_t CensusClientCallData::kMaxTagsLen; + +namespace { + +void FilterTrailingMetadata(grpc_metadata_batch* b, uint64_t* elapsed_time) { + if (b->idx.named.grpc_server_stats_bin != nullptr) { + ServerStatsDeserialize( + reinterpret_cast<const char*>(GRPC_SLICE_START_PTR( + GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md))), + GRPC_SLICE_LENGTH(GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md)), + elapsed_time); + grpc_metadata_batch_remove(b, b->idx.named.grpc_server_stats_bin); + } +} + +} // namespace + +void CensusClientCallData::OnDoneRecvTrailingMetadataCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data); + CensusClientCallData* calld = + reinterpret_cast<CensusClientCallData*>(elem->call_data); + GPR_ASSERT(calld != nullptr); + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(calld->recv_trailing_metadata_ != nullptr); + FilterTrailingMetadata(calld->recv_trailing_metadata_, + &calld->elapsed_time_); + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_trailing_metadata_, + GRPC_ERROR_REF(error)); +} + +void CensusClientCallData::OnDoneRecvMessageCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data); + CensusClientCallData* calld = + reinterpret_cast<CensusClientCallData*>(elem->call_data); + CensusChannelData* channeld = + reinterpret_cast<CensusChannelData*>(elem->channel_data); + GPR_ASSERT(calld != nullptr); + GPR_ASSERT(channeld != nullptr); + // Stream messages are no longer valid after receiving trailing metadata. + if ((*calld->recv_message_) != nullptr) { + calld->recv_message_count_++; + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_message_, GRPC_ERROR_REF(error)); +} + +void CensusClientCallData::StartTransportStreamOpBatch( + grpc_call_element* elem, TransportStreamOpBatch* op) { + if (op->send_initial_metadata() != nullptr) { + census_context* ctxt = op->get_census_context(); + GenerateClientContext( + qualified_method_, &context_, + (ctxt == nullptr) ? nullptr : reinterpret_cast<CensusContext*>(ctxt)); + size_t tracing_len = TraceContextSerialize(context_.Context(), tracing_buf_, + kMaxTraceContextLen); + if (tracing_len > 0) { + GRPC_LOG_IF_ERROR( + "census grpc_filter", + grpc_metadata_batch_add_tail( + op->send_initial_metadata()->batch(), &tracing_bin_, + grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_TRACE_BIN, + grpc_slice_from_copied_buffer(tracing_buf_, tracing_len)))); + } + grpc_slice tags = grpc_empty_slice(); + // TODO: Add in tagging serialization. + size_t encoded_tags_len = StatsContextSerialize(kMaxTagsLen, &tags); + if (encoded_tags_len > 0) { + GRPC_LOG_IF_ERROR( + "census grpc_filter", + grpc_metadata_batch_add_tail( + op->send_initial_metadata()->batch(), &stats_bin_, + grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_TAGS_BIN, tags))); + } + } + + if (op->send_message() != nullptr) { + ++sent_message_count_; + } + if (op->recv_message() != nullptr) { + recv_message_ = op->op()->payload->recv_message.recv_message; + initial_on_done_recv_message_ = + op->op()->payload->recv_message.recv_message_ready; + op->op()->payload->recv_message.recv_message_ready = &on_done_recv_message_; + } + if (op->recv_trailing_metadata() != nullptr) { + recv_trailing_metadata_ = op->recv_trailing_metadata()->batch(); + initial_on_done_recv_trailing_metadata_ = op->on_complete(); + op->set_on_complete(&on_done_recv_trailing_metadata_); + } + // Call next op. + grpc_call_next_op(elem, op->op()); +} + +grpc_error* CensusClientCallData::Init(grpc_call_element* elem, + const grpc_call_element_args* args) { + path_ = grpc_slice_ref_internal(args->path); + start_time_ = absl::Now(); + method_ = GetMethod(&path_); + qualified_method_ = absl::StrCat("Sent.", method_); + GRPC_CLOSURE_INIT(&on_done_recv_message_, OnDoneRecvMessageCb, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_done_recv_trailing_metadata_, + OnDoneRecvTrailingMetadataCb, elem, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +void CensusClientCallData::Destroy(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) { + const uint64_t request_size = GetOutgoingDataSize(final_info); + const uint64_t response_size = GetIncomingDataSize(final_info); + double latency_ms = absl::ToDoubleMilliseconds(absl::Now() - start_time_); + ::opencensus::stats::Record( + {{RpcClientSentBytesPerRpc(), static_cast<double>(request_size)}, + {RpcClientReceivedBytesPerRpc(), static_cast<double>(response_size)}, + {RpcClientRoundtripLatency(), latency_ms}, + {RpcClientServerLatency(), + ToDoubleMilliseconds(absl::Nanoseconds(elapsed_time_))}, + {RpcClientSentMessagesPerRpc(), sent_message_count_}, + {RpcClientReceivedMessagesPerRpc(), recv_message_count_}}, + {{ClientMethodTagKey(), method_}, + {ClientStatusTagKey(), StatusCodeToString(final_info->final_status)}}); + grpc_slice_unref_internal(path_); + context_.EndSpan(); +} + +} // namespace grpc diff --git a/src/cpp/ext/filters/census/client_filter.h b/src/cpp/ext/filters/census/client_filter.h new file mode 100644 index 0000000000..851022873f --- /dev/null +++ b/src/cpp/ext/filters/census/client_filter.h @@ -0,0 +1,104 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H + +#include <grpc/support/port_platform.h> + +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "src/cpp/ext/filters/census/channel_filter.h" +#include "src/cpp/ext/filters/census/context.h" + +namespace grpc { + +// A CallData class will be created for every grpc call within a channel. It is +// used to store data and methods specific to that call. CensusClientCallData is +// thread-compatible, however typically only 1 thread should be interacting with +// a call at a time. +class CensusClientCallData : public CallData { + public: + // Maximum size of trace context is sent on the wire. + static constexpr uint32_t kMaxTraceContextLen = 64; + // Maximum size of tags that are sent on the wire. + static constexpr uint32_t kMaxTagsLen = 2048; + + CensusClientCallData() + : recv_trailing_metadata_(nullptr), + initial_on_done_recv_trailing_metadata_(nullptr), + initial_on_done_recv_message_(nullptr), + elapsed_time_(0), + recv_message_(nullptr), + recv_message_count_(0), + sent_message_count_(0) { + memset(&stats_bin_, 0, sizeof(grpc_linked_mdelem)); + memset(&tracing_bin_, 0, sizeof(grpc_linked_mdelem)); + memset(&path_, 0, sizeof(grpc_slice)); + memset(&on_done_recv_trailing_metadata_, 0, sizeof(grpc_closure)); + memset(&on_done_recv_message_, 0, sizeof(grpc_closure)); + } + + grpc_error* Init(grpc_call_element* elem, + const grpc_call_element_args* args) override; + + void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) override; + + void StartTransportStreamOpBatch(grpc_call_element* elem, + TransportStreamOpBatch* op) override; + + static void OnDoneRecvTrailingMetadataCb(void* user_data, grpc_error* error); + + static void OnDoneSendInitialMetadataCb(void* user_data, grpc_error* error); + + static void OnDoneRecvMessageCb(void* user_data, grpc_error* error); + + private: + CensusContext context_; + // Metadata elements for tracing and census stats data. + grpc_linked_mdelem stats_bin_; + grpc_linked_mdelem tracing_bin_; + // Client method. + absl::string_view method_; + std::string qualified_method_; + grpc_slice path_; + // The recv trailing metadata callbacks. + grpc_metadata_batch* recv_trailing_metadata_; + grpc_closure* initial_on_done_recv_trailing_metadata_; + grpc_closure on_done_recv_trailing_metadata_; + // recv message + grpc_closure* initial_on_done_recv_message_; + grpc_closure on_done_recv_message_; + // Start time (for measuring latency). + absl::Time start_time_; + // Server elapsed time in nanoseconds. + uint64_t elapsed_time_; + // The received message--may be null. + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message_; + // Number of messages in this RPC. + uint64_t recv_message_count_; + uint64_t sent_message_count_; + // Buffer needed for grpc_slice to reference when adding trace context + // metatdata to outgoing message. + char tracing_buf_[kMaxTraceContextLen]; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H */ diff --git a/src/cpp/ext/filters/census/context.cc b/src/cpp/ext/filters/census/context.cc new file mode 100644 index 0000000000..4b3250236d --- /dev/null +++ b/src/cpp/ext/filters/census/context.cc @@ -0,0 +1,132 @@ +/* + * + * Copyright 2018 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/cpp/ext/filters/census/context.h" + +namespace grpc { + +using ::opencensus::trace::Span; +using ::opencensus::trace::SpanContext; + +void GenerateServerContext(absl::string_view tracing, absl::string_view stats, + absl::string_view primary_role, + absl::string_view method, CensusContext* context) { + GrpcTraceContext trace_ctxt; + TraceContextEncoding::Decode(tracing, &trace_ctxt); + SpanContext parent_ctx = trace_ctxt.ToSpanContext(); + new (context) CensusContext(method, parent_ctx); +} + +void GenerateClientContext(absl::string_view method, CensusContext* ctxt, + CensusContext* parent_ctxt) { + if (parent_ctxt != nullptr) { + SpanContext span_ctxt = parent_ctxt->Context(); + Span span = parent_ctxt->Span(); + if (span_ctxt.IsValid()) { + new (ctxt) CensusContext(method, &span); + return; + } + } + new (ctxt) CensusContext(method); +} + +size_t TraceContextSerialize(const ::opencensus::trace::SpanContext& context, + char* tracing_buf, size_t tracing_buf_size) { + GrpcTraceContext trace_ctxt(context); + return TraceContextEncoding::Encode(trace_ctxt, tracing_buf, + tracing_buf_size); +} + +size_t StatsContextSerialize(size_t max_tags_len, grpc_slice* tags) { + // TODO: Add implementation. Waiting on stats tagging to be added. + return 0; +} + +size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf, + size_t buf_size) { + return RpcServerStatsEncoding::Encode(server_elapsed_time, buf, buf_size); +} + +size_t ServerStatsDeserialize(const char* buf, size_t buf_size, + uint64_t* server_elapsed_time) { + return RpcServerStatsEncoding::Decode(absl::string_view(buf, buf_size), + server_elapsed_time); +} + +uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info) { + return final_info->stats.transport_stream_stats.incoming.data_bytes; +} + +uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info) { + return final_info->stats.transport_stream_stats.outgoing.data_bytes; +} + +SpanContext SpanContextFromCensusContext(const census_context* ctxt) { + return reinterpret_cast<const CensusContext*>(ctxt)->Context(); +} + +Span SpanFromCensusContext(const census_context* ctxt) { + return reinterpret_cast<const CensusContext*>(ctxt)->Span(); +} + +absl::string_view StatusCodeToString(grpc_status_code code) { + switch (code) { + case GRPC_STATUS_OK: + return "OK"; + case GRPC_STATUS_CANCELLED: + return "CANCELLED"; + case GRPC_STATUS_UNKNOWN: + return "UNKNOWN"; + case GRPC_STATUS_INVALID_ARGUMENT: + return "INVALID_ARGUMENT"; + case GRPC_STATUS_DEADLINE_EXCEEDED: + return "DEADLINE_EXCEEDED"; + case GRPC_STATUS_NOT_FOUND: + return "NOT_FOUND"; + case GRPC_STATUS_ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case GRPC_STATUS_PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case GRPC_STATUS_UNAUTHENTICATED: + return "UNAUTHENTICATED"; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return "RESOURCE_EXHAUSTED"; + case GRPC_STATUS_FAILED_PRECONDITION: + return "FAILED_PRECONDITION"; + case GRPC_STATUS_ABORTED: + return "ABORTED"; + case GRPC_STATUS_OUT_OF_RANGE: + return "OUT_OF_RANGE"; + case GRPC_STATUS_UNIMPLEMENTED: + return "UNIMPLEMENTED"; + case GRPC_STATUS_INTERNAL: + return "INTERNAL"; + case GRPC_STATUS_UNAVAILABLE: + return "UNAVAILABLE"; + case GRPC_STATUS_DATA_LOSS: + return "DATA_LOSS"; + default: + // gRPC wants users of this enum to include a default branch so that + // adding values is not a breaking change. + return "UNKNOWN_STATUS"; + } +} + +} // namespace grpc diff --git a/src/cpp/ext/filters/census/context.h b/src/cpp/ext/filters/census/context.h new file mode 100644 index 0000000000..1643fdd11b --- /dev/null +++ b/src/cpp/ext/filters/census/context.h @@ -0,0 +1,126 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H + +#include <grpc/support/port_platform.h> + +#include <grpc/status.h> +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" +#include "opencensus/trace/span.h" +#include "opencensus/trace/span_context.h" +#include "opencensus/trace/trace_params.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/cpp/common/channel_filter.h" +#include "src/cpp/ext/filters/census/rpc_encoding.h" + +// This is needed because grpc has hardcoded CensusContext with a +// forward declaration of 'struct census_context;' +struct census_context; + +namespace grpc { + +// Thread compatible. +class CensusContext { + public: + CensusContext() : span_(::opencensus::trace::Span::BlankSpan()) {} + + explicit CensusContext(absl::string_view name) + : span_(::opencensus::trace::Span::StartSpan(name)) {} + + CensusContext(absl::string_view name, const ::opencensus::trace::Span* parent) + : span_(::opencensus::trace::Span::StartSpan(name, parent)) {} + + CensusContext(absl::string_view name, + const ::opencensus::trace::SpanContext& parent_ctxt) + : span_(::opencensus::trace::Span::StartSpanWithRemoteParent( + name, parent_ctxt)) {} + + ::opencensus::trace::SpanContext Context() const { return span_.context(); } + ::opencensus::trace::Span Span() const { return span_; } + void EndSpan() { span_.End(); } + + private: + ::opencensus::trace::Span span_; +}; + +// Serializes the outgoing trace context. Field IDs are 1 byte followed by +// field data. A 1 byte version ID is always encoded first. +size_t TraceContextSerialize(const ::opencensus::trace::SpanContext& context, + char* tracing_buf, size_t tracing_buf_size); + +// Serializes the outgoing stats context. Field IDs are 1 byte followed by +// field data. A 1 byte version ID is always encoded first. Tags are directly +// serialized into the given grpc_slice. +size_t StatsContextSerialize(size_t max_tags_len, grpc_slice* tags); + +// Serialize outgoing server stats. Returns the number of bytes serialized. +size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf, + size_t buf_size); + +// Deserialize incoming server stats. Returns the number of bytes deserialized. +size_t ServerStatsDeserialize(const char* buf, size_t buf_size, + uint64_t* server_elapsed_time); + +// Deserialize the incoming SpanContext and generate a new server context based +// on that. This new span will never be a root span. This should only be called +// with a blank CensusContext as it overwrites it. +void GenerateServerContext(absl::string_view tracing, absl::string_view stats, + absl::string_view primary_role, + absl::string_view method, CensusContext* context); + +// Creates a new client context that is by default a new root context. +// If the current context is the default context then the newly created +// span automatically becomes a root span. This should only be called with a +// blank CensusContext as it overwrites it. +void GenerateClientContext(absl::string_view method, CensusContext* ctxt, + CensusContext* parent_ctx); + +// Returns the incoming data size from the grpc call final info. +uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info); + +// Returns the outgoing data size from the grpc call final info. +uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info); + +// These helper functions return the SpanContext and Span, respectively +// associated with the census_context* stored by grpc. The user will need to +// call this for manual propagation of tracing data. +::opencensus::trace::SpanContext SpanContextFromCensusContext( + const census_context* ctxt); +::opencensus::trace::Span SpanFromCensusContext(const census_context* ctxt); + +// Returns a string representation of the StatusCode enum. +absl::string_view StatusCodeToString(grpc_status_code code); + +inline absl::string_view GetMethod(const grpc_slice* path) { + if (GRPC_SLICE_IS_EMPTY(*path)) { + return ""; + } + // Check for leading '/' and trim it if present. + return absl::StripPrefix(absl::string_view(reinterpret_cast<const char*>( + GRPC_SLICE_START_PTR(*path)), + GRPC_SLICE_LENGTH(*path)), + "/"); +} + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H */ diff --git a/src/core/ext/census/grpc_context.cc b/src/cpp/ext/filters/census/grpc_context.cc index 599a798dda..599a798dda 100644 --- a/src/core/ext/census/grpc_context.cc +++ b/src/cpp/ext/filters/census/grpc_context.cc diff --git a/src/cpp/ext/filters/census/grpc_plugin.cc b/src/cpp/ext/filters/census/grpc_plugin.cc new file mode 100644 index 0000000000..f978ed3bf5 --- /dev/null +++ b/src/cpp/ext/filters/census/grpc_plugin.cc @@ -0,0 +1,130 @@ +/* + * + * Copyright 2018 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/cpp/ext/filters/census/grpc_plugin.h" + +#include <grpcpp/server_context.h> + +#include "opencensus/trace/span.h" +#include "src/cpp/ext/filters/census/channel_filter.h" +#include "src/cpp/ext/filters/census/client_filter.h" +#include "src/cpp/ext/filters/census/measures.h" +#include "src/cpp/ext/filters/census/server_filter.h" + +namespace grpc { + +void RegisterOpenCensusPlugin() { + RegisterChannelFilter<CensusChannelData, CensusClientCallData>( + "opencensus_client", GRPC_CLIENT_CHANNEL, INT_MAX /* priority */, + nullptr /* condition function */); + RegisterChannelFilter<CensusChannelData, CensusServerCallData>( + "opencensus_server", GRPC_SERVER_CHANNEL, INT_MAX /* priority */, + nullptr /* condition function */); + + // Access measures to ensure they are initialized. Otherwise, creating a view + // before the first RPC would cause an error. + RpcClientSentBytesPerRpc(); + RpcClientReceivedBytesPerRpc(); + RpcClientRoundtripLatency(); + RpcClientServerLatency(); + RpcClientSentMessagesPerRpc(); + RpcClientReceivedMessagesPerRpc(); + + RpcServerSentBytesPerRpc(); + RpcServerReceivedBytesPerRpc(); + RpcServerServerLatency(); + RpcServerSentMessagesPerRpc(); + RpcServerReceivedMessagesPerRpc(); +} + +::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context) { + return reinterpret_cast<const CensusContext*>(context->census_context()) + ->Span(); +} + +// These measure definitions should be kept in sync across opencensus +// implementations--see +// https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java. +::opencensus::stats::TagKey ClientMethodTagKey() { + static const auto method_tag_key = + ::opencensus::stats::TagKey::Register("grpc_client_method"); + return method_tag_key; +} + +::opencensus::stats::TagKey ClientStatusTagKey() { + static const auto status_tag_key = + ::opencensus::stats::TagKey::Register("grpc_client_status"); + return status_tag_key; +} + +::opencensus::stats::TagKey ServerMethodTagKey() { + static const auto method_tag_key = + ::opencensus::stats::TagKey::Register("grpc_server_method"); + return method_tag_key; +} + +::opencensus::stats::TagKey ServerStatusTagKey() { + static const auto status_tag_key = + ::opencensus::stats::TagKey::Register("grpc_server_status"); + return status_tag_key; +} + +// Client +ABSL_CONST_INIT const absl::string_view + kRpcClientSentMessagesPerRpcMeasureName = + "grpc.io/client/sent_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcClientSentBytesPerRpcMeasureName = + "grpc.io/client/sent_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcClientReceivedMessagesPerRpcMeasureName = + "grpc.io/client/received_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcClientReceivedBytesPerRpcMeasureName = + "grpc.io/client/received_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcClientRoundtripLatencyMeasureName = + "grpc.io/client/roundtrip_latency"; + +ABSL_CONST_INIT const absl::string_view kRpcClientServerLatencyMeasureName = + "grpc.io/client/server_latency"; + +// Server +ABSL_CONST_INIT const absl::string_view + kRpcServerSentMessagesPerRpcMeasureName = + "grpc.io/server/sent_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcServerSentBytesPerRpcMeasureName = + "grpc.io/server/sent_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcServerReceivedMessagesPerRpcMeasureName = + "grpc.io/server/received_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcServerReceivedBytesPerRpcMeasureName = + "grpc.io/server/received_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcServerServerLatencyMeasureName = + "grpc.io/server/server_latency"; + +} // namespace grpc diff --git a/src/cpp/ext/filters/census/grpc_plugin.h b/src/cpp/ext/filters/census/grpc_plugin.h new file mode 100644 index 0000000000..7ff2e7a8b8 --- /dev/null +++ b/src/cpp/ext/filters/census/grpc_plugin.h @@ -0,0 +1,111 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H + +#include <grpc/support/port_platform.h> + +#include "absl/strings/string_view.h" +#include "include/grpcpp/opencensus.h" +#include "opencensus/stats/stats.h" +#include "opencensus/trace/span.h" + +namespace grpc { + +class ServerContext; + +// Returns the tracing Span for the current RPC. +::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context); + +// The tag keys set when recording RPC stats. +::opencensus::stats::TagKey ClientMethodTagKey(); +::opencensus::stats::TagKey ClientStatusTagKey(); +::opencensus::stats::TagKey ServerMethodTagKey(); +::opencensus::stats::TagKey ServerStatusTagKey(); + +// Names of measures used by the plugin--users can create views on these +// measures but should not record data for them. +extern const absl::string_view kRpcClientSentMessagesPerRpcMeasureName; +extern const absl::string_view kRpcClientSentBytesPerRpcMeasureName; +extern const absl::string_view kRpcClientReceivedMessagesPerRpcMeasureName; +extern const absl::string_view kRpcClientReceivedBytesPerRpcMeasureName; +extern const absl::string_view kRpcClientRoundtripLatencyMeasureName; +extern const absl::string_view kRpcClientServerLatencyMeasureName; + +extern const absl::string_view kRpcServerSentMessagesPerRpcMeasureName; +extern const absl::string_view kRpcServerSentBytesPerRpcMeasureName; +extern const absl::string_view kRpcServerReceivedMessagesPerRpcMeasureName; +extern const absl::string_view kRpcServerReceivedBytesPerRpcMeasureName; +extern const absl::string_view kRpcServerServerLatencyMeasureName; + +// Canonical gRPC view definitions. +const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ClientReceivedMessagesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ClientReceivedBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientServerLatencyCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsCumulative(); + +const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ServerReceivedBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerServerLatencyCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerStartedCountCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ServerReceivedMessagesPerRpcCumulative(); + +const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyMinute(); +const ::opencensus::stats::ViewDescriptor& ClientServerLatencyMinute(); +const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsMinute(); + +const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerServerLatencyMinute(); +const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsMinute(); + +const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyHour(); +const ::opencensus::stats::ViewDescriptor& ClientServerLatencyHour(); +const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsHour(); + +const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerServerLatencyHour(); +const ::opencensus::stats::ViewDescriptor& ServerStartedCountHour(); +const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsHour(); + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H */ diff --git a/src/cpp/ext/filters/census/measures.cc b/src/cpp/ext/filters/census/measures.cc new file mode 100644 index 0000000000..b522fae09a --- /dev/null +++ b/src/cpp/ext/filters/census/measures.cc @@ -0,0 +1,129 @@ +/* + * + * Copyright 2018 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/cpp/ext/filters/census/measures.h" + +#include "opencensus/stats/stats.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" + +namespace grpc { + +using ::opencensus::stats::MeasureDouble; +using ::opencensus::stats::MeasureInt64; + +// These measure definitions should be kept in sync across opencensus +// implementations--see +// https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java. + +namespace { + +// Unit constants +constexpr char kUnitBytes[] = "By"; +constexpr char kUnitMilliseconds[] = "ms"; +constexpr char kCount[] = "1"; + +} // namespace + +// Client +MeasureDouble RpcClientSentBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcClientSentBytesPerRpcMeasureName, + "Total bytes sent across all request messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcClientReceivedBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcClientReceivedBytesPerRpcMeasureName, + "Total bytes received across all response messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcClientRoundtripLatency() { + static const auto measure = MeasureDouble::Register( + kRpcClientRoundtripLatencyMeasureName, + "Time between first byte of request sent to last byte of response " + "received, or terminal error", + kUnitMilliseconds); + return measure; +} + +MeasureDouble RpcClientServerLatency() { + static const auto measure = MeasureDouble::Register( + kRpcClientServerLatencyMeasureName, + "Time between first byte of request received to last byte of response " + "sent, or terminal error (propagated from the server)", + kUnitMilliseconds); + return measure; +} + +MeasureInt64 RpcClientSentMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcClientSentMessagesPerRpcMeasureName, + "Number of messages sent per RPC", kCount); + return measure; +} + +MeasureInt64 RpcClientReceivedMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcClientReceivedMessagesPerRpcMeasureName, + "Number of messages received per RPC", kCount); + return measure; +} + +// Server +MeasureDouble RpcServerSentBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcServerSentBytesPerRpcMeasureName, + "Total bytes sent across all messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcServerReceivedBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcServerReceivedBytesPerRpcMeasureName, + "Total bytes received across all messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcServerServerLatency() { + static const auto measure = MeasureDouble::Register( + kRpcServerServerLatencyMeasureName, + "Time between first byte of request received to last byte of response " + "sent, or terminal error", + kUnitMilliseconds); + return measure; +} + +MeasureInt64 RpcServerSentMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcServerSentMessagesPerRpcMeasureName, + "Number of messages sent per RPC", kCount); + return measure; +} + +MeasureInt64 RpcServerReceivedMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcServerReceivedMessagesPerRpcMeasureName, + "Number of messages received per RPC", kCount); + return measure; +} + +} // namespace grpc diff --git a/src/cpp/ext/filters/census/measures.h b/src/cpp/ext/filters/census/measures.h new file mode 100644 index 0000000000..8f8e72ace2 --- /dev/null +++ b/src/cpp/ext/filters/census/measures.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H + +#include <grpc/support/port_platform.h> + +#include "opencensus/stats/stats.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" + +namespace grpc { + +::opencensus::stats::MeasureInt64 RpcClientSentMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcClientSentBytesPerRpc(); +::opencensus::stats::MeasureInt64 RpcClientReceivedMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcClientReceivedBytesPerRpc(); +::opencensus::stats::MeasureDouble RpcClientRoundtripLatency(); +::opencensus::stats::MeasureDouble RpcClientServerLatency(); +::opencensus::stats::MeasureInt64 RpcClientCompletedRpcs(); + +::opencensus::stats::MeasureInt64 RpcServerSentMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcServerSentBytesPerRpc(); +::opencensus::stats::MeasureInt64 RpcServerReceivedMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcServerReceivedBytesPerRpc(); +::opencensus::stats::MeasureDouble RpcServerServerLatency(); +::opencensus::stats::MeasureInt64 RpcServerCompletedRpcs(); + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H */ diff --git a/src/cpp/ext/filters/census/rpc_encoding.cc b/src/cpp/ext/filters/census/rpc_encoding.cc new file mode 100644 index 0000000000..45a66d9dc8 --- /dev/null +++ b/src/cpp/ext/filters/census/rpc_encoding.cc @@ -0,0 +1,39 @@ +/* + * + * Copyright 2018 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/cpp/ext/filters/census/rpc_encoding.h" + +namespace grpc { + +constexpr size_t TraceContextEncoding::kGrpcTraceContextSize; +constexpr size_t TraceContextEncoding::kEncodeDecodeFailure; +constexpr size_t TraceContextEncoding::kVersionIdSize; +constexpr size_t TraceContextEncoding::kFieldIdSize; +constexpr size_t TraceContextEncoding::kVersionIdOffset; +constexpr size_t TraceContextEncoding::kVersionId; + +constexpr size_t RpcServerStatsEncoding::kRpcServerStatsSize; +constexpr size_t RpcServerStatsEncoding::kEncodeDecodeFailure; +constexpr size_t RpcServerStatsEncoding::kVersionIdSize; +constexpr size_t RpcServerStatsEncoding::kFieldIdSize; +constexpr size_t RpcServerStatsEncoding::kVersionIdOffset; +constexpr size_t RpcServerStatsEncoding::kVersionId; + +} // namespace grpc diff --git a/src/cpp/ext/filters/census/rpc_encoding.h b/src/cpp/ext/filters/census/rpc_encoding.h new file mode 100644 index 0000000000..ffffa60c46 --- /dev/null +++ b/src/cpp/ext/filters/census/rpc_encoding.h @@ -0,0 +1,284 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H + +#include <grpc/support/port_platform.h> + +#include <string.h> + +#include "absl/base/internal/endian.h" +#include "absl/strings/string_view.h" +#include "opencensus/trace/span_context.h" +#include "opencensus/trace/span_id.h" +#include "opencensus/trace/trace_id.h" + +namespace grpc { + +// TODO: Rename to GrpcTraceContextV0. +struct GrpcTraceContext { + GrpcTraceContext() {} + + explicit GrpcTraceContext(const ::opencensus::trace::SpanContext& ctx) { + ctx.trace_id().CopyTo(trace_id); + ctx.span_id().CopyTo(span_id); + ctx.trace_options().CopyTo(trace_options); + } + + ::opencensus::trace::SpanContext ToSpanContext() const { + return ::opencensus::trace::SpanContext( + ::opencensus::trace::TraceId(trace_id), + ::opencensus::trace::SpanId(span_id), + ::opencensus::trace::TraceOptions(trace_options)); + } + + // TODO: For performance: + // uint8_t version; + // uint8_t trace_id_field_id; + uint8_t trace_id[::opencensus::trace::TraceId::kSize]; + // uint8_t span_id_field_id; + uint8_t span_id[::opencensus::trace::SpanId::kSize]; + // uint8_t trace_options_field_id; + uint8_t trace_options[::opencensus::trace::TraceOptions::kSize]; +}; + +// TraceContextEncoding encapsulates the logic for encoding and decoding of +// trace contexts. +class TraceContextEncoding { + public: + // Size of encoded GrpcTraceContext. (16 + 8 + 1 + 4) + static constexpr size_t kGrpcTraceContextSize = 29; + // Error value. + static constexpr size_t kEncodeDecodeFailure = 0; + + // Deserializes a GrpcTraceContext from the incoming buffer. Returns the + // number of bytes deserialized from the buffer. If the incoming buffer is + // empty or the encoding version is not supported it will return 0 bytes, + // currently only version 0 is supported. If an unknown field ID is + // encountered it will return immediately without parsing the rest of the + // buffer. Inlined for performance reasons. + static size_t Decode(absl::string_view buf, GrpcTraceContext* tc) { + if (buf.empty()) { + return kEncodeDecodeFailure; + } + uint8_t version = buf[kVersionIdOffset]; + // TODO: Support other versions later. Only support version 0 for + // now. + if (version != kVersionId) { + return kEncodeDecodeFailure; + } + + size_t pos = kVersionIdSize; + while (pos < buf.size()) { + size_t bytes_read = + ParseField(absl::string_view(&buf[pos], buf.size() - pos), tc); + if (bytes_read == 0) { + break; + } else { + pos += bytes_read; + } + } + return pos; + } + + // Serializes a GrpcTraceContext into the provided buffer. Returns the number + // of bytes serialized into the buffer. If the buffer is not of sufficient + // size (it must be at least kGrpcTraceContextSize bytes) it will drop + // everything and return 0 bytes serialized. Inlined for performance reasons. + static size_t Encode(const GrpcTraceContext& tc, char* buf, size_t buf_size) { + if (buf_size < kGrpcTraceContextSize) { + return kEncodeDecodeFailure; + } + buf[kVersionIdOffset] = kVersionId; + buf[kTraceIdOffset] = kTraceIdField; + memcpy(&buf[kTraceIdOffset + 1], tc.trace_id, + opencensus::trace::TraceId::kSize); + buf[kSpanIdOffset] = kSpanIdField; + memcpy(&buf[kSpanIdOffset + 1], tc.span_id, + opencensus::trace::SpanId::kSize); + buf[kTraceOptionsOffset] = kTraceOptionsField; + memcpy(&buf[kTraceOptionsOffset + 1], tc.trace_options, + opencensus::trace::TraceOptions::kSize); + return kGrpcTraceContextSize; + } + + private: + // Parses the next field from the incoming buffer and stores the parsed value + // in a GrpcTraceContext struct. If it does not recognize the field ID it + // will return 0, otherwise it returns the number of bytes read. + static size_t ParseField(absl::string_view buf, GrpcTraceContext* tc) { + // TODO: Add support for multi-byte field IDs. + if (buf.empty()) { + return 0; + } + // Field ID is always the first byte in a field. + uint32_t field_id = buf[0]; + size_t bytes_read = kFieldIdSize; + switch (field_id) { + case kTraceIdField: + bytes_read += kTraceIdSize; + if (bytes_read > buf.size()) { + return 0; + } + memcpy(tc->trace_id, &buf[kFieldIdSize], + opencensus::trace::TraceId::kSize); + break; + case kSpanIdField: + bytes_read += kSpanIdSize; + if (bytes_read > buf.size()) { + return 0; + } + memcpy(tc->span_id, &buf[kFieldIdSize], + opencensus::trace::SpanId::kSize); + break; + case kTraceOptionsField: + bytes_read += kTraceOptionsSize; + if (bytes_read > buf.size()) { + return 0; + } + memcpy(tc->trace_options, &buf[kFieldIdSize], + opencensus::trace::TraceOptions::kSize); + break; + default: // Invalid field ID + return 0; + } + + return bytes_read; + } + + // Size of Version ID. + static constexpr size_t kVersionIdSize = 1; + // Size of Field ID. + static constexpr size_t kFieldIdSize = 1; + + // Offset and value for currently supported version ID. + static constexpr size_t kVersionIdOffset = 0; + static constexpr size_t kVersionId = 0; + + // Fixed Field ID values: + enum FieldIdValue { + kTraceIdField = 0, + kSpanIdField = 1, + kTraceOptionsField = 2, + }; + + // Field data sizes in bytes + enum FieldSize { + kTraceIdSize = 16, + kSpanIdSize = 8, + kTraceOptionsSize = 1, + }; + + // Fixed size offsets for field ID start positions during encoding. Field + // data immediately follows. + enum FieldIdOffset { + kTraceIdOffset = kVersionIdSize, + kSpanIdOffset = kTraceIdOffset + kFieldIdSize + kTraceIdSize, + kTraceOptionsOffset = kSpanIdOffset + kFieldIdSize + kSpanIdSize, + }; + + TraceContextEncoding() = delete; + TraceContextEncoding(const TraceContextEncoding&) = delete; + TraceContextEncoding(TraceContextEncoding&&) = delete; + TraceContextEncoding operator=(const TraceContextEncoding&) = delete; + TraceContextEncoding operator=(TraceContextEncoding&&) = delete; +}; + +// TODO: This may not be needed. Check to see if opencensus requires +// a trailing server response. +// RpcServerStatsEncoding encapsulates the logic for encoding and decoding of +// rpc server stats messages. Rpc server stats consists of a uint64_t time +// value (server latency in nanoseconds). +class RpcServerStatsEncoding { + public: + // Size of encoded RPC server stats. + static constexpr size_t kRpcServerStatsSize = 10; + // Error value. + static constexpr size_t kEncodeDecodeFailure = 0; + + // Deserializes rpc server stats from the incoming 'buf' into *time. Returns + // number of bytes decoded. If the buffer is of insufficient size (it must be + // at least kRpcServerStatsSize bytes) or the encoding version or field ID are + // unrecognized, *time will be set to 0 and it will return + // kEncodeDecodeFailure. Inlined for performance reasons. + static size_t Decode(absl::string_view buf, uint64_t* time) { + if (buf.size() < kRpcServerStatsSize) { + *time = 0; + return kEncodeDecodeFailure; + } + + uint8_t version = buf[kVersionIdOffset]; + uint32_t fieldID = buf[kServerElapsedTimeOffset]; + if (version != kVersionId || fieldID != kServerElapsedTimeField) { + *time = 0; + return kEncodeDecodeFailure; + } + *time = absl::little_endian::Load64( + &buf[kServerElapsedTimeOffset + kFieldIdSize]); + return kRpcServerStatsSize; + } + + // Serializes rpc server stats into the provided buffer. It returns the + // number of bytes written to the buffer. If the buffer is smaller than + // kRpcServerStatsSize bytes it will return kEncodeDecodeFailure. Inlined for + // performance reasons. + static size_t Encode(uint64_t time, char* buf, size_t buf_size) { + if (buf_size < kRpcServerStatsSize) { + return kEncodeDecodeFailure; + } + + buf[kVersionIdOffset] = kVersionId; + buf[kServerElapsedTimeOffset] = kServerElapsedTimeField; + absl::little_endian::Store64(&buf[kServerElapsedTimeOffset + kFieldIdSize], + time); + return kRpcServerStatsSize; + } + + private: + // Size of Version ID. + static constexpr size_t kVersionIdSize = 1; + // Size of Field ID. + static constexpr size_t kFieldIdSize = 1; + + // Offset and value for currently supported version ID. + static constexpr size_t kVersionIdOffset = 0; + static constexpr size_t kVersionId = 0; + + enum FieldIdValue { + kServerElapsedTimeField = 0, + }; + + enum FieldSize { + kServerElapsedTimeSize = 8, + }; + + enum FieldIdOffset { + kServerElapsedTimeOffset = kVersionIdSize, + }; + + RpcServerStatsEncoding() = delete; + RpcServerStatsEncoding(const RpcServerStatsEncoding&) = delete; + RpcServerStatsEncoding(RpcServerStatsEncoding&&) = delete; + RpcServerStatsEncoding operator=(const RpcServerStatsEncoding&) = delete; + RpcServerStatsEncoding operator=(RpcServerStatsEncoding&&) = delete; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H */ diff --git a/src/cpp/ext/filters/census/server_filter.cc b/src/cpp/ext/filters/census/server_filter.cc new file mode 100644 index 0000000000..c7c62eefe5 --- /dev/null +++ b/src/cpp/ext/filters/census/server_filter.cc @@ -0,0 +1,198 @@ +/* + * + * Copyright 2018 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/cpp/ext/filters/census/server_filter.h" + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "opencensus/stats/stats.h" +#include "src/core/lib/surface/call.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" +#include "src/cpp/ext/filters/census/measures.h" + +namespace grpc { + +constexpr uint32_t CensusServerCallData::kMaxServerStatsLen; + +namespace { + +// server metadata elements +struct ServerMetadataElements { + grpc_slice path; + grpc_slice tracing_slice; + grpc_slice census_proto; +}; + +void FilterInitialMetadata(grpc_metadata_batch* b, + ServerMetadataElements* sml) { + if (b->idx.named.path != nullptr) { + sml->path = grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.path->md)); + } + if (b->idx.named.grpc_trace_bin != nullptr) { + sml->tracing_slice = + grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_trace_bin->md)); + grpc_metadata_batch_remove(b, b->idx.named.grpc_trace_bin); + } + if (b->idx.named.grpc_tags_bin != nullptr) { + sml->census_proto = + grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_tags_bin->md)); + grpc_metadata_batch_remove(b, b->idx.named.grpc_tags_bin); + } +} + +} // namespace + +void CensusServerCallData::OnDoneRecvMessageCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data); + CensusServerCallData* calld = + reinterpret_cast<CensusServerCallData*>(elem->call_data); + CensusChannelData* channeld = + reinterpret_cast<CensusChannelData*>(elem->channel_data); + GPR_ASSERT(calld != nullptr); + GPR_ASSERT(channeld != nullptr); + // Stream messages are no longer valid after receiving trailing metadata. + if ((*calld->recv_message_) != nullptr) { + ++calld->recv_message_count_; + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_message_, GRPC_ERROR_REF(error)); +} + +void CensusServerCallData::OnDoneRecvInitialMetadataCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data); + CensusServerCallData* calld = + reinterpret_cast<CensusServerCallData*>(elem->call_data); + GPR_ASSERT(calld != nullptr); + if (error == GRPC_ERROR_NONE) { + grpc_metadata_batch* initial_metadata = calld->recv_initial_metadata_; + GPR_ASSERT(initial_metadata != nullptr); + ServerMetadataElements sml; + sml.path = grpc_empty_slice(); + sml.tracing_slice = grpc_empty_slice(); + sml.census_proto = grpc_empty_slice(); + FilterInitialMetadata(initial_metadata, &sml); + calld->path_ = grpc_slice_ref_internal(sml.path); + calld->method_ = GetMethod(&calld->path_); + calld->qualified_method_ = StrCat("Recv.", calld->method_); + const char* tracing_str = + GRPC_SLICE_IS_EMPTY(sml.tracing_slice) + ? "" + : reinterpret_cast<const char*>( + GRPC_SLICE_START_PTR(sml.tracing_slice)); + size_t tracing_str_len = GRPC_SLICE_IS_EMPTY(sml.tracing_slice) + ? 0 + : GRPC_SLICE_LENGTH(sml.tracing_slice); + const char* census_str = GRPC_SLICE_IS_EMPTY(sml.census_proto) + ? "" + : reinterpret_cast<const char*>( + GRPC_SLICE_START_PTR(sml.census_proto)); + size_t census_str_len = GRPC_SLICE_IS_EMPTY(sml.census_proto) + ? 0 + : GRPC_SLICE_LENGTH(sml.census_proto); + + GenerateServerContext(absl::string_view(tracing_str, tracing_str_len), + absl::string_view(census_str, census_str_len), + /*primary_role*/ "", calld->qualified_method_, + &calld->context_); + + grpc_slice_unref_internal(sml.tracing_slice); + grpc_slice_unref_internal(sml.census_proto); + grpc_slice_unref_internal(sml.path); + grpc_census_call_set_context( + calld->gc_, reinterpret_cast<census_context*>(&calld->context_)); + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_initial_metadata_, + GRPC_ERROR_REF(error)); +} + +void CensusServerCallData::StartTransportStreamOpBatch( + grpc_call_element* elem, TransportStreamOpBatch* op) { + if (op->recv_initial_metadata() != nullptr) { + // substitute our callback for the op callback + recv_initial_metadata_ = op->recv_initial_metadata()->batch(); + initial_on_done_recv_initial_metadata_ = op->recv_initial_metadata_ready(); + op->set_recv_initial_metadata_ready(&on_done_recv_initial_metadata_); + } + if (op->send_message() != nullptr) { + ++sent_message_count_; + } + if (op->recv_message() != nullptr) { + recv_message_ = op->op()->payload->recv_message.recv_message; + initial_on_done_recv_message_ = + op->op()->payload->recv_message.recv_message_ready; + op->op()->payload->recv_message.recv_message_ready = &on_done_recv_message_; + } + // We need to record the time when the trailing metadata was sent to mark the + // completeness of the request. + if (op->send_trailing_metadata() != nullptr) { + elapsed_time_ = absl::Now() - start_time_; + size_t len = ServerStatsSerialize(absl::ToInt64Nanoseconds(elapsed_time_), + stats_buf_, kMaxServerStatsLen); + if (len > 0) { + GRPC_LOG_IF_ERROR( + "census grpc_filter", + grpc_metadata_batch_add_tail( + op->send_trailing_metadata()->batch(), &census_bin_, + grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_SERVER_STATS_BIN, + grpc_slice_from_copied_buffer(stats_buf_, len)))); + } + } + // Call next op. + grpc_call_next_op(elem, op->op()); +} + +grpc_error* CensusServerCallData::Init(grpc_call_element* elem, + const grpc_call_element_args* args) { + start_time_ = absl::Now(); + gc_ = + grpc_call_from_top_element(grpc_call_stack_element(args->call_stack, 0)); + GRPC_CLOSURE_INIT(&on_done_recv_initial_metadata_, + OnDoneRecvInitialMetadataCb, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_done_recv_message_, OnDoneRecvMessageCb, elem, + grpc_schedule_on_exec_ctx); + auth_context_ = grpc_call_auth_context(gc_); + return GRPC_ERROR_NONE; +} + +void CensusServerCallData::Destroy(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) { + const uint64_t request_size = GetOutgoingDataSize(final_info); + const uint64_t response_size = GetIncomingDataSize(final_info); + double elapsed_time_ms = absl::ToDoubleMilliseconds(elapsed_time_); + grpc_auth_context_release(auth_context_); + ::opencensus::stats::Record( + {{RpcServerSentBytesPerRpc(), static_cast<double>(response_size)}, + {RpcServerReceivedBytesPerRpc(), static_cast<double>(request_size)}, + {RpcServerServerLatency(), elapsed_time_ms}, + {RpcServerSentMessagesPerRpc(), sent_message_count_}, + {RpcServerReceivedMessagesPerRpc(), recv_message_count_}}, + {{ServerMethodTagKey(), method_}, + {ServerStatusTagKey(), StatusCodeToString(final_info->final_status)}}); + grpc_slice_unref_internal(path_); + context_.EndSpan(); +} + +} // namespace grpc diff --git a/src/cpp/ext/filters/census/server_filter.h b/src/cpp/ext/filters/census/server_filter.h new file mode 100644 index 0000000000..e393ed3283 --- /dev/null +++ b/src/cpp/ext/filters/census/server_filter.h @@ -0,0 +1,101 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H + +#include <grpc/support/port_platform.h> + +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "include/grpc/grpc_security.h" +#include "src/cpp/ext/filters/census/channel_filter.h" +#include "src/cpp/ext/filters/census/context.h" + +namespace grpc { + +// A CallData class will be created for every grpc call within a channel. It is +// used to store data and methods specific to that call. CensusServerCallData is +// thread-compatible, however typically only 1 thread should be interacting with +// a call at a time. +class CensusServerCallData : public CallData { + public: + // Maximum size of server stats that are sent on the wire. + static constexpr uint32_t kMaxServerStatsLen = 16; + + CensusServerCallData() + : gc_(nullptr), + auth_context_(nullptr), + recv_initial_metadata_(nullptr), + initial_on_done_recv_initial_metadata_(nullptr), + initial_on_done_recv_message_(nullptr), + recv_message_(nullptr), + recv_message_count_(0), + sent_message_count_(0) { + memset(&census_bin_, 0, sizeof(grpc_linked_mdelem)); + memset(&path_, 0, sizeof(grpc_slice)); + memset(&on_done_recv_initial_metadata_, 0, sizeof(grpc_closure)); + memset(&on_done_recv_message_, 0, sizeof(grpc_closure)); + } + + grpc_error* Init(grpc_call_element* elem, + const grpc_call_element_args* args) override; + + void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) override; + + void StartTransportStreamOpBatch(grpc_call_element* elem, + TransportStreamOpBatch* op) override; + + static void OnDoneRecvInitialMetadataCb(void* user_data, grpc_error* error); + + static void OnDoneRecvMessageCb(void* user_data, grpc_error* error); + + private: + CensusContext context_; + // server method + absl::string_view method_; + std::string qualified_method_; + grpc_slice path_; + // Pointer to the grpc_call element + grpc_call* gc_; + // Authorization context for the call. + grpc_auth_context* auth_context_; + // Metadata element for census stats. + grpc_linked_mdelem census_bin_; + // recv callback + grpc_metadata_batch* recv_initial_metadata_; + grpc_closure* initial_on_done_recv_initial_metadata_; + grpc_closure on_done_recv_initial_metadata_; + // recv message + grpc_closure* initial_on_done_recv_message_; + grpc_closure on_done_recv_message_; + absl::Time start_time_; + absl::Duration elapsed_time_; + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message_; + uint64_t recv_message_count_; + uint64_t sent_message_count_; + // Buffer needed for grpc_slice to reference it when adding metatdata to + // response. + char stats_buf_[kMaxServerStatsLen]; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H */ diff --git a/src/cpp/ext/filters/census/views.cc b/src/cpp/ext/filters/census/views.cc new file mode 100644 index 0000000000..2c0c5f7295 --- /dev/null +++ b/src/cpp/ext/filters/census/views.cc @@ -0,0 +1,491 @@ +/* + * + * Copyright 2018 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/cpp/ext/filters/census/grpc_plugin.h" + +#include "absl/time/time.h" +#include "opencensus/stats/internal/aggregation_window.h" +#include "opencensus/stats/internal/set_aggregation_window.h" +#include "opencensus/stats/stats.h" + +namespace grpc { + +using ::opencensus::stats::Aggregation; +using ::opencensus::stats::AggregationWindow; +using ::opencensus::stats::BucketBoundaries; +using ::opencensus::stats::ViewDescriptor; + +// These measure definitions should be kept in sync across opencensus +// implementations. + +namespace { + +Aggregation BytesDistributionAggregation() { + return Aggregation::Distribution(BucketBoundaries::Explicit( + {0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, + 67108864, 268435456, 1073741824, 4294967296})); +} + +Aggregation MillisDistributionAggregation() { + return Aggregation::Distribution(BucketBoundaries::Explicit( + {0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1, 2, 3, 4, + 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, + 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, + 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000})); +} + +Aggregation CountDistributionAggregation() { + return Aggregation::Distribution(BucketBoundaries::Exponential(17, 1.0, 2.0)); +} + +ViewDescriptor MinuteDescriptor() { + auto descriptor = ViewDescriptor(); + SetAggregationWindow(AggregationWindow::Interval(absl::Minutes(1)), + &descriptor); + return descriptor; +} + +ViewDescriptor HourDescriptor() { + auto descriptor = ViewDescriptor(); + SetAggregationWindow(AggregationWindow::Interval(absl::Hours(1)), + &descriptor); + return descriptor; +} + +} // namespace + +void RegisterOpenCensusViewsForExport() { + ClientSentMessagesPerRpcCumulative().RegisterForExport(); + ClientSentBytesPerRpcCumulative().RegisterForExport(); + ClientReceivedMessagesPerRpcCumulative().RegisterForExport(); + ClientReceivedBytesPerRpcCumulative().RegisterForExport(); + ClientRoundtripLatencyCumulative().RegisterForExport(); + ClientServerLatencyCumulative().RegisterForExport(); + + ServerSentMessagesPerRpcCumulative().RegisterForExport(); + ServerSentBytesPerRpcCumulative().RegisterForExport(); + ServerReceivedMessagesPerRpcCumulative().RegisterForExport(); + ServerReceivedBytesPerRpcCumulative().RegisterForExport(); + ServerServerLatencyCumulative().RegisterForExport(); +} + +// client cumulative +const ViewDescriptor& ClientSentBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/sent_bytes_per_rpc/cumulative") + .set_measure(kRpcClientSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/received_bytes_per_rpc/cumulative") + .set_measure(kRpcClientReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientRoundtripLatencyCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/roundtrip_latency/cumulative") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientServerLatencyCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/server_latency/cumulative") + .set_measure(kRpcClientServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientCompletedRpcsCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/completed_rpcs/cumulative") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ClientMethodTagKey()) + .add_column(ClientStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientSentMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/received_messages_per_rpc/cumulative") + .set_measure(kRpcClientSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/sent_messages_per_rpc/cumulative") + .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +// server cumulative +const ViewDescriptor& ServerSentBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/received_bytes_per_rpc/cumulative") + .set_measure(kRpcServerSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/sent_bytes_per_rpc/cumulative") + .set_measure(kRpcServerReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerServerLatencyCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/elapsed_time/cumulative") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerCompletedRpcsCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/completed_rpcs/cumulative") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ServerMethodTagKey()) + .add_column(ServerStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerSentMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/received_messages_per_rpc/cumulative") + .set_measure(kRpcServerSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/sent_messages_per_rpc/cumulative") + .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +// client minute +const ViewDescriptor& ClientSentBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/sent_bytes_per_rpc/minute") + .set_measure(kRpcClientSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/received_bytes_per_rpc/minute") + .set_measure(kRpcClientReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientRoundtripLatencyMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/roundtrip_latency/minute") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientServerLatencyMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/server_latency/minute") + .set_measure(kRpcClientServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientCompletedRpcsMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/completed_rpcs/minute") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ClientMethodTagKey()) + .add_column(ClientStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientSentMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/sent_messages_per_rpc/minute") + .set_measure(kRpcClientSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/received_messages_per_rpc/minute") + .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +// server minute +const ViewDescriptor& ServerSentBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/sent_bytes_per_rpc/minute") + .set_measure(kRpcServerSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/received_bytes_per_rpc/minute") + .set_measure(kRpcServerReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerServerLatencyMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/server_latency/minute") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerCompletedRpcsMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/completed_rpcs/minute") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ServerMethodTagKey()) + .add_column(ServerStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerSentMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/sent_messages_per_rpc/minute") + .set_measure(kRpcServerSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/received_messages_per_rpc/minute") + .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +// client hour +const ViewDescriptor& ClientSentBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/sent_bytes_per_rpc/hour") + .set_measure(kRpcClientSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/received_bytes_per_rpc/hour") + .set_measure(kRpcClientReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientRoundtripLatencyHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/roundtrip_latency/hour") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientServerLatencyHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/server_latency/hour") + .set_measure(kRpcClientServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientCompletedRpcsHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/completed_rpcs/hour") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ClientMethodTagKey()) + .add_column(ClientStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientSentMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/sent_messages_per_rpc/hour") + .set_measure(kRpcClientSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/received_messages_per_rpc/hour") + .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +// server hour +const ViewDescriptor& ServerSentBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/sent_bytes_per_rpc/hour") + .set_measure(kRpcServerSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/received_bytes_per_rpc/hour") + .set_measure(kRpcServerReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerServerLatencyHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/server_latency/hour") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerCompletedRpcsHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/completed_rpcs/hour") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ServerMethodTagKey()) + .add_column(ServerStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerSentMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/sent_messages_per_rpc/hour") + .set_measure(kRpcServerSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/received_messages_per_rpc/hour") + .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +} // namespace grpc diff --git a/src/csharp/Grpc.Core/Interceptors/CallInvokerExtensions.cs b/src/csharp/Grpc.Core/Interceptors/CallInvokerExtensions.cs index 421b5d379e..39c24d0157 100644 --- a/src/csharp/Grpc.Core/Interceptors/CallInvokerExtensions.cs +++ b/src/csharp/Grpc.Core/Interceptors/CallInvokerExtensions.cs @@ -24,7 +24,6 @@ namespace Grpc.Core.Interceptors { /// <summary> /// Extends the CallInvoker class to provide the interceptor facility on the client side. - /// This is an EXPERIMENTAL API. /// </summary> public static class CallInvokerExtensions { diff --git a/src/csharp/Grpc.Core/Interceptors/ChannelExtensions.cs b/src/csharp/Grpc.Core/Interceptors/ChannelExtensions.cs index 00b2fa8bec..c7d0c2472a 100644 --- a/src/csharp/Grpc.Core/Interceptors/ChannelExtensions.cs +++ b/src/csharp/Grpc.Core/Interceptors/ChannelExtensions.cs @@ -22,7 +22,6 @@ namespace Grpc.Core.Interceptors { /// <summary> /// Provides extension methods to make it easy to register interceptors on Channel objects. - /// This is an EXPERIMENTAL API. /// </summary> public static class ChannelExtensions { diff --git a/src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs b/src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs index de06a77077..4665fc553b 100644 --- a/src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs +++ b/src/csharp/Grpc.Core/Interceptors/ClientInterceptorContext.cs @@ -25,7 +25,6 @@ namespace Grpc.Core.Interceptors { /// <summary> /// Carries along the context associated with intercepted invocations on the client side. - /// This is an EXPERIMENTAL API. /// </summary> public struct ClientInterceptorContext<TRequest, TResponse> where TRequest : class diff --git a/src/csharp/Grpc.Core/Interceptors/Interceptor.cs b/src/csharp/Grpc.Core/Interceptors/Interceptor.cs index 56a30c34af..ab708034f3 100644 --- a/src/csharp/Grpc.Core/Interceptors/Interceptor.cs +++ b/src/csharp/Grpc.Core/Interceptors/Interceptor.cs @@ -25,7 +25,6 @@ namespace Grpc.Core.Interceptors { /// <summary> /// Serves as the base class for gRPC interceptors. - /// This is an EXPERIMENTAL API. /// </summary> public abstract class Interceptor { diff --git a/src/csharp/Grpc.Core/Interceptors/ServerServiceDefinitionExtensions.cs b/src/csharp/Grpc.Core/Interceptors/ServerServiceDefinitionExtensions.cs index b9b53247ce..8987544f7f 100644 --- a/src/csharp/Grpc.Core/Interceptors/ServerServiceDefinitionExtensions.cs +++ b/src/csharp/Grpc.Core/Interceptors/ServerServiceDefinitionExtensions.cs @@ -24,14 +24,12 @@ namespace Grpc.Core.Interceptors { /// <summary> /// Extends the ServerServiceDefinition class to add methods used to register interceptors on the server side. - /// This is an EXPERIMENTAL API. /// </summary> public static class ServerServiceDefinitionExtensions { /// <summary> /// Returns a <see cref="Grpc.Core.ServerServiceDefinition" /> instance that /// intercepts incoming calls to the underlying service handler through the given interceptor. - /// This is an EXPERIMENTAL API. /// </summary> /// <param name="serverServiceDefinition">The <see cref="Grpc.Core.ServerServiceDefinition" /> instance to register interceptors on.</param> /// <param name="interceptor">The interceptor to intercept the incoming invocations with.</param> @@ -52,7 +50,6 @@ namespace Grpc.Core.Interceptors /// <summary> /// Returns a <see cref="Grpc.Core.ServerServiceDefinition" /> instance that /// intercepts incoming calls to the underlying service handler through the given interceptors. - /// This is an EXPERIMENTAL API. /// </summary> /// <param name="serverServiceDefinition">The <see cref="Grpc.Core.ServerServiceDefinition" /> instance to register interceptors on.</param> /// <param name="interceptors"> diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 60dacbf126..64bb407c57 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -29,7 +29,7 @@ using Grpc.Core.Utils; namespace Grpc.Core { /// <summary> - /// gRPC server. A single server can server arbitrary number of services and can listen on more than one ports. + /// gRPC server. A single server can serve an arbitrary number of services and can listen on more than one port. /// </summary> public class Server { diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 3e6ec474b7..87a2516f8d 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -935,11 +935,12 @@ grpcsharp_ssl_credentials_create(const char* pem_root_certs, if (key_cert_pair_cert_chain || key_cert_pair_private_key) { key_cert_pair.cert_chain = key_cert_pair_cert_chain; key_cert_pair.private_key = key_cert_pair_private_key; - return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, NULL); + return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, NULL, + NULL); } else { GPR_ASSERT(!key_cert_pair_cert_chain); GPR_ASSERT(!key_cert_pair_private_key); - return grpc_ssl_credentials_create(pem_root_certs, NULL, NULL); + return grpc_ssl_credentials_create(pem_root_certs, NULL, NULL, NULL); } } diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m index f4b933751f..348989904a 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.m +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -183,14 +183,14 @@ static NSMutableDictionary *kHostCache; grpc_channel_credentials *creds; if (pemPrivateKey == nil && pemCertChain == nil) { - creds = grpc_ssl_credentials_create(rootsASCII.bytes, NULL, NULL); + creds = grpc_ssl_credentials_create(rootsASCII.bytes, NULL, NULL, NULL); } else { grpc_ssl_pem_key_cert_pair key_cert_pair; NSData *privateKeyASCII = [self nullTerminatedDataWithString:pemPrivateKey]; NSData *certChainASCII = [self nullTerminatedDataWithString:pemCertChain]; key_cert_pair.private_key = privateKeyASCII.bytes; key_cert_pair.cert_chain = certChainASCII.bytes; - creds = grpc_ssl_credentials_create(rootsASCII.bytes, &key_cert_pair, NULL); + creds = grpc_ssl_credentials_create(rootsASCII.bytes, &key_cert_pair, NULL, NULL); } @synchronized(self) { diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c index 10d7380ca1..af1372878d 100644 --- a/src/php/ext/grpc/channel_credentials.c +++ b/src/php/ext/grpc/channel_credentials.c @@ -158,7 +158,7 @@ PHP_METHOD(ChannelCredentials, createSsl) { grpc_channel_credentials *creds = grpc_ssl_credentials_create( pem_root_certs, - pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair, NULL); + pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair, NULL, NULL); zval *creds_object = grpc_php_wrap_channel_credentials(creds, hashstr, false TSRMLS_CC); efree(hashkey); diff --git a/src/proto/census/census.options b/src/proto/census/census.options deleted file mode 100644 index a1f80395c7..0000000000 --- a/src/proto/census/census.options +++ /dev/null @@ -1,3 +0,0 @@ -google.census.Tag.key max_size:255 -google.census.Tag.value max_size:255 -google.census.View.tag_key max_count:15 diff --git a/src/proto/census/census.proto b/src/proto/census/census.proto deleted file mode 100644 index ae7d7763e8..0000000000 --- a/src/proto/census/census.proto +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2016 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. - -syntax = "proto3"; - -package google.census; - -// All the census protos. -// -// Nomenclature note: capitalized names below (like Resource) are protos. -// -// Census lets you define a Resource - something which can be measured, like the -// latency of an RPC, the number of CPU cycles spent on an operation, or -// anything else you care to measure. You can record individual instances of -// measurements (a double value) for every Resource of interest. These -// individual measurements are aggregated together into an Aggregation. There -// are two Aggregation types available: Distribution (describes the -// distribution of all measurements, possibly with a histogram) and -// IntervalStats (the count and mean of measurements across specified time -// periods). An Aggregation is described by an AggregationDescriptor. -// -// You can define how your stats are broken down by Tag values and which -// Aggregations to use through a View. The corresponding combination of -// Resource/View/Aggregation which is available to census clients is called a -// Metric. - - -// The following two types are copied from -// google/protobuf/{duration,timestamp}.proto. Ideally, we would be able to -// import them, but this causes compilation issues on C-based systems -// (e.g. https://koti.kapsi.fi/jpa/nanopb/), which cannot process the C++ -// headers generated from the standard protobuf distribution. See the relevant -// proto files for full documentation of these types. - -message Duration { - // Signed seconds of the span of time. Must be from -315,576,000,000 - // to +315,576,000,000 inclusive. - int64 seconds = 1; - - // Signed fractions of a second at nanosecond resolution of the span - // of time. Durations less than one second are represented with a 0 - // `seconds` field and a positive or negative `nanos` field. For durations - // of one second or more, a non-zero value for the `nanos` field must be - // of the same sign as the `seconds` field. Must be from -999,999,999 - // to +999,999,999 inclusive. - int32 nanos = 2; -} - -message Timestamp { - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. - int64 seconds = 1; - - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - int32 nanos = 2; -} - -// Describes a Resource. -message Resource { - // name of resource, e.g. rpc_latency, cpu. Must be unique. - string name = 1; - - // More detailed description of the resource, used in documentation. - string description = 2; - - // Fundamental units of measurement supported by Census - // TODO(aveitch): expand this to include other S.I. units? - enum BasicUnit { - UNKNOWN = 0; - BITS = 1; - BYTES = 2; - SECS = 3; - CORES = 4; - MAX_UNITS = 5; - } - - // MeasurementUnit lets you build compound units of the form - // 10^n * (A * B * ...) / (X * Y * ...), - // where the elements in the numerator and denominator are all BasicUnits. A - // MeasurementUnit must have at least one BasicUnit in its numerator. - // - // To specify multiplication in the numerator or denominator, simply specify - // multiple numerator or denominator fields. For example: - // - // - byte-seconds (i.e. bytes * seconds): - // numerator: BYTES - // numerator: SECS - // - // - events/sec^2 (i.e. rate of change of events/sec): - // numerator: COUNT - // denominator: SECS - // denominator: SECS - // - // To specify multiples (in power of 10) of units, specify a non-zero prefix - // value, for example: - // - // - MB/s (i.e. megabytes / s): - // prefix: 6 - // numerator: BYTES - // denominator: SECS - // - // - nanoseconds - // prefix: -9 - // numerator: SECS - message MeasurementUnit { - int32 prefix = 1; - repeated BasicUnit numerator = 2; - repeated BasicUnit denominator = 3; - } - - // The units in which Resource values are measured. - MeasurementUnit unit = 3; -} - -// An Aggregation summarizes a series of individual Resource measurements, an -// AggregationDescriptor describes an Aggregation. -message AggregationDescriptor { - enum AggregationType { - // Unspecified. Should not be used. - UNKNOWN = 0; - // A count of measurements made. - COUNT = 1; - // A Distribution. - DISTRIBUTION = 2; - // Counts over fixed time intervals. - INTERVAL = 3; - } - // The type of Aggregation. - AggregationType type = 1; - - // At most one set of options. It is illegal to specifiy an option for - // COUNT Aggregations. interval_boundaries must be set for INTERVAL types. - // bucket_boundaries are optional for DISTRIBUTION types. - oneof options { - // Defines histogram bucket boundaries for Distributions. - BucketBoundaries bucket_boundaries = 2; - // Defines the time windows to record for IntervalStats. - IntervalBoundaries interval_boundaries = 3; - } - - // A Distribution may optionally contain a histogram of the values in the - // population. The bucket boundaries for that histogram are described by - // `bucket_boundaries`. This defines `size(bounds) + 1` (= N) buckets. The - // boundaries for bucket index i are: - // - // [-infinity, bounds[i]) for i == 0 - // [bounds[i-1], bounds[i]) for 0 < i < N-2 - // [bounds[i-1], +infinity) for i == N-1 - // - // i.e. an underflow bucket (number 0), zero or more finite buckets (1 - // through N - 2, and an overflow bucket (N - 1), with inclusive lower - // bounds and exclusive upper bounds. - // - // There must be at least one element in `bounds`. If `bounds` has only one - // element, there are no finite buckets, and that single element is the - // common boundary of the overflow and underflow buckets. - message BucketBoundaries { - // The values must be monotonically increasing. - repeated double bounds = 1; - } - - // For Interval stats, describe the size of each window. - message IntervalBoundaries { - // For each time window, specify a duration in seconds. - repeated double window_size = 1; - } -} - -// Distribution contains summary statistics for a population of values and, -// optionally, a histogram representing the distribution of those values across -// a specified set of histogram buckets, as defined in -// Aggregation.bucket_options. -// -// The summary statistics are the count, mean, minimum, and the maximum of the -// set of population of values. -// -// Although it is not forbidden, it is generally a bad idea to include -// non-finite values (infinities or NaNs) in the population of values, as this -// will render the `mean` field meaningless. -message Distribution { - // The number of values in the population. Must be non-negative. - int64 count = 1; - - // The arithmetic mean of the values in the population. If `count` is zero - // then this field must be zero. - double mean = 2; - - // Describes a range of population values. - message Range { - // The minimum of the population values. - double min = 1; - // The maximum of the population values. - double max = 2; - } - - // The range of the population values. If `count` is zero, this field will not - // be defined. - Range range = 3; - - // A Distribution may optionally contain a histogram of the values in the - // population. The histogram is given in `bucket_count` as counts of values - // that fall into one of a sequence of non-overlapping buckets, as described - // by `AggregationDescriptor.options.bucket_boundaries`. - // The sum of the values in `bucket_counts` must equal the value in `count`. - // - // Bucket counts are given in order under the numbering scheme described - // above (the underflow bucket has number 0; the finite buckets, if any, - // have numbers 1 through N-2; the overflow bucket has number N-1). - // - // The size of `bucket_count` must be no greater than N as defined in - // `bucket_boundaries`. - // - // Any suffix of trailing zero bucket_count fields may be omitted. - repeated int64 bucket_count = 4; -} - -// Record summary stats over various time windows. -message IntervalStats { - // Summary statistic over a single time window. - message Window { - // The window duration. Must be positive. - Duration window_size = 1; - // The number of measurements in this window. - int64 count = 2; - // The arithmetic mean of all measurements in the window. - double mean = 3; - } - - // Full set of windows for this aggregation. - repeated Window window = 1; -} - -// A Tag: key-value pair. -message Tag { - string key = 1; - string value = 2; -} - -// A View specifies an Aggregation and a set of tag keys. The Aggregation will -// be broken down by the unique set of matching tag values for each measurement. -message View { - // Name of view. Must be unique. - string name = 1; - - // More detailed description, for documentation purposes. - string description = 2; - - // Name of Resource to be broken down for this view. - string resource_name = 3; - - // Aggregation type to associate with this View. - AggregationDescriptor aggregation = 4; - - // Tag keys to match with a given Resource measurement. If no keys are - // specified, then all stats are recorded. Keys must be unique. - repeated string tag_key = 5; -} - -// An Aggregation summarizes a series of individual Resource measurements. -message Aggregation { - // Name of this aggregation. - string name = 1; - - // More detailed description, for documentation purposes. - string description = 2; - - // The data for this Aggregation. - oneof data { - uint64 count = 3; - Distribution distribution = 4; - IntervalStats interval_stats = 5; - } - - // Tags associated with this Aggregation. - repeated Tag tag = 6; -} - -// A Metric represents all the Aggregations for a particular view. -message Metric { - // View associated with this Metric. - string view_name = 1; - - // Aggregations - each will have a unique set of tag values for the tag_keys - // associated with the corresponding View. - repeated Aggregation aggregation = 2; - - // Start and end timestamps over which the metric was accumulated. These - // values are not relevant/defined for IntervalStats aggregations, which are - // always accumulated over a fixed time period. - Timestamp start = 3; - Timestamp end = 4; -} diff --git a/src/proto/census/trace_context.options b/src/proto/census/trace_context.options deleted file mode 100644 index e69de29bb2..0000000000 --- a/src/proto/census/trace_context.options +++ /dev/null diff --git a/src/proto/census/trace_context.proto b/src/proto/census/trace_context.proto deleted file mode 100644 index 7e5087dbee..0000000000 --- a/src/proto/census/trace_context.proto +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2016 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. - -syntax = "proto3"; - -package google.trace; - -// Tracing information that is propagated with RPC's. -message TraceContext { - // A TraceId uniquely represents a single Trace. It is a 128-bit nonce. - // The 128-bit ID is split into 2 64-bit chunks. (REQUIRED) - fixed64 trace_id_hi = 1; - fixed64 trace_id_lo = 2; - // ID of parent (client) span. (REQUIRED) - fixed64 span_id = 3; - // Span option flags. First bit is true if this trace is sampled. (OPTIONAL) - fixed32 span_options = 4; -} diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi index f4ccfbc016..d2c0389ca6 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi @@ -142,12 +142,12 @@ cdef class SSLChannelCredentials(ChannelCredentials): c_pem_root_certificates = self._pem_root_certificates if self._private_key is None and self._certificate_chain is None: return grpc_ssl_credentials_create( - c_pem_root_certificates, NULL, NULL) + c_pem_root_certificates, NULL, NULL, NULL) else: c_pem_key_certificate_pair.private_key = self._private_key c_pem_key_certificate_pair.certificate_chain = self._certificate_chain return grpc_ssl_credentials_create( - c_pem_root_certificates, &c_pem_key_certificate_pair, NULL) + c_pem_root_certificates, &c_pem_key_certificate_pair, NULL, NULL) cdef class CompositeChannelCredentials(ChannelCredentials): diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi index cfefeaf938..bcbfec0c9f 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi @@ -453,11 +453,14 @@ cdef extern from "grpc/grpc_security.h": # We don't care about the internals (and in fact don't know them) pass - ctypedef struct grpc_ssl_session_cache: # We don't care about the internals (and in fact don't know them) pass + ctypedef struct verify_peer_options: + # We don't care about the internals (and in fact don't know them) + pass + ctypedef void (*grpc_ssl_roots_override_callback)(char **pem_root_certs) grpc_ssl_session_cache *grpc_ssl_session_cache_create_lru(size_t capacity) @@ -469,7 +472,7 @@ cdef extern from "grpc/grpc_security.h": grpc_channel_credentials *grpc_google_default_credentials_create() nogil grpc_channel_credentials *grpc_ssl_credentials_create( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, - void *reserved) nogil + verify_peer_options *verify_options, void *reserved) nogil grpc_channel_credentials *grpc_composite_channel_credentials_create( grpc_channel_credentials *creds1, grpc_call_credentials *creds2, void *reserved) nogil diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index a64fb42f9b..b20b8155a0 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -346,6 +346,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', @@ -353,7 +354,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', - 'src/core/ext/census/grpc_context.cc', + 'src/cpp/ext/filters/census/grpc_context.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', diff --git a/src/ruby/end2end/package_with_underscore_checker.rb b/src/ruby/end2end/package_with_underscore_checker.rb new file mode 100644 index 0000000000..27ea00ffa7 --- /dev/null +++ b/src/ruby/end2end/package_with_underscore_checker.rb @@ -0,0 +1,54 @@ +# Copyright 2018 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. + +require 'open3' +require 'tmpdir' + +def main + root_dir = File.join(File.dirname(__FILE__), '..', '..', '..') + pb_dir = File.join(root_dir, 'src', 'ruby', 'end2end', 'protos') + + fail 'CONFIG env variable unexpectedly unset' unless ENV['CONFIG'] + bins_sub_dir = ENV['CONFIG'] + bins_dir = File.join(root_dir, 'bins', bins_sub_dir) + + plugin = File.join(bins_dir, 'grpc_ruby_plugin') + protoc = File.join(bins_dir, 'protobuf', 'protoc') + + got = nil + + Dir.mktmpdir do |tmp_dir| + gen_out = File.join(tmp_dir, 'package_with_underscore', 'service_services_pb.rb') + + pid = spawn( + protoc, + "--proto_path=#{pb_dir}", + 'package_with_underscore/service.proto', + "--grpc_out=#{tmp_dir}", + "--plugin=protoc-gen-grpc=#{plugin}" + ) + Process.waitpid2(pid) + File.open(gen_out) { |f| got = f.read } + end + + correct_modularized_rpc = 'rpc :TestOne, ' \ + 'Grpc::Testing::PackageWithUnderscore::Data::Request, ' \ + 'Grpc::Testing::PackageWithUnderscore::Data::Response' + + return if got.include?(correct_modularized_rpc) + + fail 'generated file does not match with correct_modularized_rpc' +end + +main diff --git a/src/ruby/spec/pb/package_with_underscore/data.proto b/src/ruby/end2end/protos/package_with_underscore/data.proto index 2706f1d7be..2706f1d7be 100644 --- a/src/ruby/spec/pb/package_with_underscore/data.proto +++ b/src/ruby/end2end/protos/package_with_underscore/data.proto diff --git a/src/ruby/spec/pb/package_with_underscore/service.proto b/src/ruby/end2end/protos/package_with_underscore/service.proto index 814c7898cd..814c7898cd 100644 --- a/src/ruby/spec/pb/package_with_underscore/service.proto +++ b/src/ruby/end2end/protos/package_with_underscore/service.proto diff --git a/src/ruby/ext/grpc/rb_channel_credentials.c b/src/ruby/ext/grpc/rb_channel_credentials.c index b23a32caf1..178224c6e0 100644 --- a/src/ruby/ext/grpc/rb_channel_credentials.c +++ b/src/ruby/ext/grpc/rb_channel_credentials.c @@ -159,12 +159,12 @@ static VALUE grpc_rb_channel_credentials_init(int argc, VALUE* argv, pem_root_certs_cstr = RSTRING_PTR(pem_root_certs); } if (pem_private_key == Qnil && pem_cert_chain == Qnil) { - creds = grpc_ssl_credentials_create(pem_root_certs_cstr, NULL, NULL); + creds = grpc_ssl_credentials_create(pem_root_certs_cstr, NULL, NULL, NULL); } else { key_cert_pair.private_key = RSTRING_PTR(pem_private_key); key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain); - creds = - grpc_ssl_credentials_create(pem_root_certs_cstr, &key_cert_pair, NULL); + creds = grpc_ssl_credentials_create(pem_root_certs_cstr, &key_cert_pair, + NULL, NULL); } if (creds == NULL) { rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index 3cc6492d04..474405ae3f 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -311,7 +311,7 @@ extern grpc_google_default_credentials_create_type grpc_google_default_credentia typedef void(*grpc_set_ssl_roots_override_callback_type)(grpc_ssl_roots_override_callback cb); extern grpc_set_ssl_roots_override_callback_type grpc_set_ssl_roots_override_callback_import; #define grpc_set_ssl_roots_override_callback grpc_set_ssl_roots_override_callback_import -typedef grpc_channel_credentials*(*grpc_ssl_credentials_create_type)(const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, void* reserved); +typedef grpc_channel_credentials*(*grpc_ssl_credentials_create_type)(const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, const verify_peer_options* verify_options, void* reserved); extern grpc_ssl_credentials_create_type grpc_ssl_credentials_create_import; #define grpc_ssl_credentials_create grpc_ssl_credentials_create_import typedef void(*grpc_call_credentials_release_type)(grpc_call_credentials* creds); diff --git a/src/ruby/spec/call_credentials_spec.rb b/src/ruby/spec/call_credentials_spec.rb index 86f30b46f1..d949ba2235 100644 --- a/src/ruby/spec/call_credentials_spec.rb +++ b/src/ruby/spec/call_credentials_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' describe GRPC::Core::CallCredentials do CallCredentials = GRPC::Core::CallCredentials diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb index 1cc0500242..22d345f658 100644 --- a/src/ruby/spec/call_spec.rb +++ b/src/ruby/spec/call_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' include GRPC::Core::StatusCodes diff --git a/src/ruby/spec/channel_credentials_spec.rb b/src/ruby/spec/channel_credentials_spec.rb index e53f316208..b05e5aebf8 100644 --- a/src/ruby/spec/channel_credentials_spec.rb +++ b/src/ruby/spec/channel_credentials_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' describe GRPC::Core::ChannelCredentials do ChannelCredentials = GRPC::Core::ChannelCredentials diff --git a/src/ruby/spec/channel_spec.rb b/src/ruby/spec/channel_spec.rb index 5b2e355963..3c9eca47ec 100644 --- a/src/ruby/spec/channel_spec.rb +++ b/src/ruby/spec/channel_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') diff --git a/src/ruby/spec/client_auth_spec.rb b/src/ruby/spec/client_auth_spec.rb index b955ad231e..c12ed2e0f4 100644 --- a/src/ruby/spec/client_auth_spec.rb +++ b/src/ruby/spec/client_auth_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' def create_channel_creds test_root = File.join(File.dirname(__FILE__), 'testdata') diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index afbfb0bc43..afde5073a6 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' include GRPC::Core diff --git a/src/ruby/spec/compression_options_spec.rb b/src/ruby/spec/compression_options_spec.rb index 03c3cd9f07..05318e6686 100644 --- a/src/ruby/spec/compression_options_spec.rb +++ b/src/ruby/spec/compression_options_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' describe GRPC::Core::CompressionOptions do # Note these constants should be updated diff --git a/src/ruby/spec/error_sanity_spec.rb b/src/ruby/spec/error_sanity_spec.rb index b8f0638695..c36635ea26 100644 --- a/src/ruby/spec/error_sanity_spec.rb +++ b/src/ruby/spec/error_sanity_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' StatusCodes = GRPC::Core::StatusCodes diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index da50f8d0c9..3f878cc100 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' Thread.abort_on_exception = true diff --git a/src/ruby/spec/generic/rpc_desc_spec.rb b/src/ruby/spec/generic/rpc_desc_spec.rb index 6852b9804f..b4ffe5eb92 100644 --- a/src/ruby/spec/generic/rpc_desc_spec.rb +++ b/src/ruby/spec/generic/rpc_desc_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' require 'grpc/generic/rpc_desc' describe GRPC::RpcDesc do diff --git a/src/ruby/spec/generic/rpc_server_pool_spec.rb b/src/ruby/spec/generic/rpc_server_pool_spec.rb index 27a42c82c1..0f2dac272e 100644 --- a/src/ruby/spec/generic/rpc_server_pool_spec.rb +++ b/src/ruby/spec/generic/rpc_server_pool_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' Thread.abort_on_exception = true diff --git a/src/ruby/spec/generic/service_spec.rb b/src/ruby/spec/generic/service_spec.rb index ad1c268d32..57aec89ce5 100644 --- a/src/ruby/spec/generic/service_spec.rb +++ b/src/ruby/spec/generic/service_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' require 'grpc/generic/rpc_desc' require 'grpc/generic/service' diff --git a/src/ruby/spec/google_rpc_status_utils_spec.rb b/src/ruby/spec/google_rpc_status_utils_spec.rb index 3263589b6a..1f67d3a916 100644 --- a/src/ruby/spec/google_rpc_status_utils_spec.rb +++ b/src/ruby/spec/google_rpc_status_utils_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' require_relative '../lib/grpc/google_rpc_status_utils' require_relative '../pb/src/proto/grpc/testing/messages_pb' require_relative '../pb/src/proto/grpc/testing/messages_pb' diff --git a/src/ruby/spec/pb/duplicate/codegen_spec.rb b/src/ruby/spec/pb/duplicate/codegen_spec.rb index 9f82858d15..fed7c1c8be 100644 --- a/src/ruby/spec/pb/duplicate/codegen_spec.rb +++ b/src/ruby/spec/pb/duplicate/codegen_spec.rb @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'spec_helper' require 'open3' require 'tmpdir' diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb index 58a602327c..84e63c2e54 100644 --- a/src/ruby/spec/pb/health/checker_spec.rb +++ b/src/ruby/spec/pb/health/checker_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' require 'grpc/health/v1/health_pb' require 'grpc/health/checker' require 'open3' diff --git a/src/ruby/spec/pb/package_with_underscore/checker_spec.rb b/src/ruby/spec/pb/package_with_underscore/checker_spec.rb deleted file mode 100644 index dac7c14a9a..0000000000 --- a/src/ruby/spec/pb/package_with_underscore/checker_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2016 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. - -require 'open3' -require 'tmpdir' - -describe 'Package with underscore protobuf code generation' do - it 'should have the same content as created by code generation' do - root_dir = File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..') - pb_dir = File.join(root_dir, 'src', 'ruby', 'spec', 'pb') - - fail 'CONFIG env variable unexpectedly unset' unless ENV['CONFIG'] - bins_sub_dir = ENV['CONFIG'] - bins_dir = File.join(root_dir, 'bins', bins_sub_dir) - - plugin = File.join(bins_dir, 'grpc_ruby_plugin') - protoc = File.join(bins_dir, 'protobuf', 'protoc') - - got = nil - - Dir.mktmpdir do |tmp_dir| - gen_out = File.join(tmp_dir, 'package_with_underscore', 'service_services_pb.rb') - - pid = spawn( - protoc, - '-I.', - 'package_with_underscore/service.proto', - "--grpc_out=#{tmp_dir}", - "--plugin=protoc-gen-grpc=#{plugin}", - chdir: pb_dir) - Process.waitpid2(pid) - File.open(gen_out) { |f| got = f.read } - end - - correct_modularized_rpc = 'rpc :TestOne, ' \ - 'Grpc::Testing::PackageWithUnderscore::Data::Request, ' \ - 'Grpc::Testing::PackageWithUnderscore::Data::Response' - expect(got).to include(correct_modularized_rpc) - end -end diff --git a/src/ruby/spec/server_credentials_spec.rb b/src/ruby/spec/server_credentials_spec.rb index 673d832f62..f773a5f701 100644 --- a/src/ruby/spec/server_credentials_spec.rb +++ b/src/ruby/spec/server_credentials_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') diff --git a/src/ruby/spec/server_spec.rb b/src/ruby/spec/server_spec.rb index 6eaac5ded1..76038d8b8b 100644 --- a/src/ruby/spec/server_spec.rb +++ b/src/ruby/spec/server_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') diff --git a/src/ruby/spec/spec_helper.rb b/src/ruby/spec/spec_helper.rb index 8fe9e6e808..8fe2c16b35 100644 --- a/src/ruby/spec/spec_helper.rb +++ b/src/ruby/spec/spec_helper.rb @@ -31,6 +31,7 @@ end if ENV['COVERAGE_NAME'] require 'rspec' require 'logging' require 'rspec/logging_helper' +require 'grpc' require_relative 'support/services' require_relative 'support/helpers' diff --git a/src/ruby/spec/support/services.rb b/src/ruby/spec/support/services.rb index 27cc8e61ac..27239cd66c 100644 --- a/src/ruby/spec/support/services.rb +++ b/src/ruby/spec/support/services.rb @@ -13,7 +13,7 @@ # limitations under the License. # Test stubs for various scenarios -require 'grpc' +require 'spec_helper' # A test message class EchoMsg diff --git a/src/ruby/spec/time_consts_spec.rb b/src/ruby/spec/time_consts_spec.rb index f7afe6b70a..41655ab106 100644 --- a/src/ruby/spec/time_consts_spec.rb +++ b/src/ruby/spec/time_consts_spec.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'grpc' +require 'spec_helper' TimeConsts = GRPC::Core::TimeConsts diff --git a/templates/grpc.gyp.template b/templates/grpc.gyp.template index 2ea0d06ebd..d71de9f441 100644 --- a/templates/grpc.gyp.template +++ b/templates/grpc.gyp.template @@ -70,6 +70,7 @@ '.', '../..', 'include', + '../../third_party/nanopb', ], 'defines': [ 'GRPC_ARES=0', diff --git a/templates/tools/dockerfile/apt_get_basic.include b/templates/tools/dockerfile/apt_get_basic.include index 2825ad0048..6e19e65157 100644 --- a/templates/tools/dockerfile/apt_get_basic.include +++ b/templates/tools/dockerfile/apt_get_basic.include @@ -23,6 +23,7 @@ RUN apt-get update && apt-get install -y ${'\\'} strace ${'\\'} python-dev ${'\\'} python-setuptools ${'\\'} + python-yaml ${'\\'} telnet ${'\\'} unzip ${'\\'} wget ${'\\'} diff --git a/templates/tools/dockerfile/python_deps.include b/templates/tools/dockerfile/python_deps.include index 4708094d24..c7bf238b5a 100644 --- a/templates/tools/dockerfile/python_deps.include +++ b/templates/tools/dockerfile/python_deps.include @@ -11,4 +11,4 @@ RUN apt-get update && apt-get install -y ${'\\'} # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 diff --git a/test/core/bad_ssl/bad_ssl_test.cc b/test/core/bad_ssl/bad_ssl_test.cc index e2ea7540ef..73d251eff4 100644 --- a/test/core/bad_ssl/bad_ssl_test.cc +++ b/test/core/bad_ssl/bad_ssl_test.cc @@ -37,7 +37,7 @@ static void* tag(intptr_t t) { return (void*)t; } static void run_test(const char* target, size_t nops) { grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(nullptr, nullptr, nullptr); + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); grpc_channel* channel; grpc_call* c; diff --git a/test/core/channel/BUILD b/test/core/channel/BUILD index 81c4353129..da419f00cf 100644 --- a/test/core/channel/BUILD +++ b/test/core/channel/BUILD @@ -122,6 +122,7 @@ grpc_cc_test( language = "C++", deps = [ "//:grpc", + "//test/core/util:gpr_test_util", ], external_deps = [ "gtest", diff --git a/test/core/end2end/fixtures/h2_oauth2.cc b/test/core/end2end/fixtures/h2_oauth2.cc index d44aafd50a..37397d6450 100644 --- a/test/core/end2end/fixtures/h2_oauth2.cc +++ b/test/core/end2end/fixtures/h2_oauth2.cc @@ -146,7 +146,7 @@ static void chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack( grpc_end2end_test_fixture* f, grpc_channel_args* client_args) { grpc_core::ExecCtx exec_ctx; grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(test_root_cert, nullptr, nullptr); + grpc_ssl_credentials_create(test_root_cert, nullptr, nullptr, nullptr); grpc_call_credentials* oauth2_creds = grpc_md_only_test_credentials_create( "authorization", oauth2_md, true /* is_async */); grpc_channel_credentials* ssl_oauth2_creds = diff --git a/test/core/end2end/fixtures/h2_ssl.cc b/test/core/end2end/fixtures/h2_ssl.cc index 999cd4cdfb..4d6c815716 100644 --- a/test/core/end2end/fixtures/h2_ssl.cc +++ b/test/core/end2end/fixtures/h2_ssl.cc @@ -101,7 +101,7 @@ void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture* f) { static void chttp2_init_client_simple_ssl_secure_fullstack( grpc_end2end_test_fixture* f, grpc_channel_args* client_args) { grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(nullptr, nullptr, nullptr); + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); grpc_arg ssl_name_override = { GRPC_ARG_STRING, const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), diff --git a/test/core/end2end/fixtures/h2_ssl_proxy.cc b/test/core/end2end/fixtures/h2_ssl_proxy.cc index 9ab50c6217..09cbf974b6 100644 --- a/test/core/end2end/fixtures/h2_ssl_proxy.cc +++ b/test/core/end2end/fixtures/h2_ssl_proxy.cc @@ -55,7 +55,7 @@ static grpc_channel* create_proxy_client(const char* target, grpc_channel_args* client_args) { grpc_channel* channel; grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(nullptr, nullptr, nullptr); + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); grpc_arg ssl_name_override = { GRPC_ARG_STRING, const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), @@ -138,7 +138,7 @@ void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture* f) { static void chttp2_init_client_simple_ssl_secure_fullstack( grpc_end2end_test_fixture* f, grpc_channel_args* client_args) { grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(nullptr, nullptr, nullptr); + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); grpc_arg ssl_name_override = { GRPC_ARG_STRING, const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), diff --git a/test/core/end2end/fuzzers/api_fuzzer.cc b/test/core/end2end/fuzzers/api_fuzzer.cc index d0d82ea43b..eacfd4a8c3 100644 --- a/test/core/end2end/fuzzers/api_fuzzer.cc +++ b/test/core/end2end/fuzzers/api_fuzzer.cc @@ -222,7 +222,7 @@ static grpc_channel_credentials* read_ssl_channel_creds(input_stream* inp) { grpc_channel_credentials* creds = grpc_ssl_credentials_create( root_certs, private_key != nullptr && certs != nullptr ? &key_cert_pair : nullptr, - nullptr); + nullptr, nullptr); cred_artifact_ctx_finish(&ctx); return creds; } diff --git a/test/core/end2end/h2_ssl_cert_test.cc b/test/core/end2end/h2_ssl_cert_test.cc index 9ed6f23798..2c5ee3b156 100644 --- a/test/core/end2end/h2_ssl_cert_test.cc +++ b/test/core/end2end/h2_ssl_cert_test.cc @@ -169,8 +169,8 @@ typedef enum { NONE, SELF_SIGNED, SIGNED, BAD_CERT_PAIR } certtype; default: \ break; \ } \ - ssl_creds = \ - grpc_ssl_credentials_create(test_root_cert, key_cert_pair, NULL); \ + ssl_creds = grpc_ssl_credentials_create(test_root_cert, key_cert_pair, \ + NULL, NULL); \ grpc_arg ssl_name_override = { \ GRPC_ARG_STRING, \ const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), \ diff --git a/test/core/end2end/h2_ssl_session_reuse_test.cc b/test/core/end2end/h2_ssl_session_reuse_test.cc index d5984be93f..b2f398625a 100644 --- a/test/core/end2end/h2_ssl_session_reuse_test.cc +++ b/test/core/end2end/h2_ssl_session_reuse_test.cc @@ -66,7 +66,7 @@ grpc_channel* client_create(char* server_addr, grpc_ssl_session_cache* cache) { grpc_ssl_pem_key_cert_pair signed_client_key_cert_pair = { test_signed_client_key, test_signed_client_cert}; grpc_channel_credentials* client_creds = grpc_ssl_credentials_create( - test_root_cert, &signed_client_key_cert_pair, nullptr); + test_root_cert, &signed_client_key_cert_pair, nullptr, nullptr); grpc_arg args[] = { grpc_channel_arg_string_create( diff --git a/test/core/gprpp/inlined_vector_test.cc b/test/core/gprpp/inlined_vector_test.cc index ae34947718..41f4338f8a 100644 --- a/test/core/gprpp/inlined_vector_test.cc +++ b/test/core/gprpp/inlined_vector_test.cc @@ -27,10 +27,12 @@ namespace testing { TEST(InlinedVectorTest, CreateAndIterate) { const int kNumElements = 9; InlinedVector<int, 2> v; + EXPECT_TRUE(v.empty()); for (int i = 0; i < kNumElements; ++i) { v.push_back(i); } EXPECT_EQ(static_cast<size_t>(kNumElements), v.size()); + EXPECT_FALSE(v.empty()); for (int i = 0; i < kNumElements; ++i) { EXPECT_EQ(i, v[i]); EXPECT_EQ(i, &v[i] - &v[0]); // Ensure contiguous allocation. diff --git a/test/core/handshake/BUILD b/test/core/handshake/BUILD index a3276b9343..712cd59197 100644 --- a/test/core/handshake/BUILD +++ b/test/core/handshake/BUILD @@ -82,3 +82,21 @@ grpc_cc_test( "//test/core/util:grpc_test_util", ], ) + +grpc_cc_test( + name = "handshake_verify_peer_options", + srcs = ["verify_peer_options.cc"], + language = "C++", + data = [ + "//src/core/tsi/test_creds:ca.pem", + "//src/core/tsi/test_creds:server1.key", + "//src/core/tsi/test_creds:server1.pem", + ], + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + ], +) + diff --git a/test/core/handshake/client_ssl.cc b/test/core/handshake/client_ssl.cc index 8ac763ac4b..467df6e229 100644 --- a/test/core/handshake/client_ssl.cc +++ b/test/core/handshake/client_ssl.cc @@ -251,8 +251,8 @@ static bool client_ssl_test(char* server_alpn_preferred) { reinterpret_cast<const char*> GRPC_SLICE_START_PTR(key_slice); pem_key_cert_pair.cert_chain = reinterpret_cast<const char*> GRPC_SLICE_START_PTR(cert_slice); - grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(ca_cert, &pem_key_cert_pair, nullptr); + grpc_channel_credentials* ssl_creds = grpc_ssl_credentials_create( + ca_cert, &pem_key_cert_pair, nullptr, nullptr); // Establish a channel pointing at the TLS server. Since the gRPC runtime is // lazy, this won't necessarily establish a connection yet. diff --git a/test/core/handshake/verify_peer_options.cc b/test/core/handshake/verify_peer_options.cc new file mode 100644 index 0000000000..86c524b4a5 --- /dev/null +++ b/test/core/handshake/verify_peer_options.cc @@ -0,0 +1,275 @@ +/* + * + * Copyright 2018 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 "src/core/lib/iomgr/port.h" + +// This test won't work except with posix sockets enabled +#ifdef GRPC_POSIX_SOCKET + +#include <arpa/inet.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <grpc/grpc.h> +#include <grpc/grpc_security.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/load_file.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#define SSL_CERT_PATH "src/core/tsi/test_creds/server1.pem" +#define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key" +#define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem" + +// Simple gRPC server. This listens until client_handshake_complete occurs. +static gpr_event client_handshake_complete; + +static void server_thread(void* arg) { + const int port = *static_cast<int*>(arg); + + // Load key pair and establish server SSL credentials. + grpc_ssl_pem_key_cert_pair pem_key_cert_pair; + grpc_slice ca_slice, cert_slice, key_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CA_PATH, 1, &ca_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CERT_PATH, 1, &cert_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_KEY_PATH, 1, &key_slice))); + const char* ca_cert = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(ca_slice); + pem_key_cert_pair.private_key = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(key_slice); + pem_key_cert_pair.cert_chain = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(cert_slice); + grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( + ca_cert, &pem_key_cert_pair, 1, 0, nullptr); + + // Start server listening on local port. + char* addr; + gpr_asprintf(&addr, "127.0.0.1:%d", port); + grpc_server* server = grpc_server_create(nullptr, nullptr); + GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds)); + free(addr); + + grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); + + grpc_server_register_completion_queue(server, cq, nullptr); + grpc_server_start(server); + + // Wait a bounded number of time until client_handshake_complete is set, + // sleeping between polls. The total time spent (deadline * retries) + // should be strictly greater than the client retry limit so that the + // client will always timeout first. + int retries = 60; + while (!gpr_event_get(&client_handshake_complete) && retries-- > 0) { + const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(1); + grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, nullptr); + GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT); + } + + gpr_log(GPR_INFO, "Shutting down server"); + grpc_server_shutdown_and_notify(server, cq, nullptr); + grpc_server_cancel_all_calls(server); + grpc_completion_queue_shutdown(cq); + + const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(60); + grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, nullptr); + GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); + + grpc_server_destroy(server); + grpc_completion_queue_destroy(cq); + grpc_server_credentials_release(ssl_creds); + grpc_slice_unref(cert_slice); + grpc_slice_unref(key_slice); + grpc_slice_unref(ca_slice); +} + +// This test launches a minimal TLS grpc server on a separate thread and then +// establishes a TLS handshake via the core library to the server. The client +// uses the supplied verify options. +static bool verify_peer_options_test(verify_peer_options* verify_options) { + bool success = true; + + grpc_init(); + int port = grpc_pick_unused_port_or_die(); + gpr_event_init(&client_handshake_complete); + + // Launch the gRPC server thread. + bool ok; + grpc_core::Thread thd("grpc_client_ssl_test", server_thread, &port, &ok); + GPR_ASSERT(ok); + thd.Start(); + + // Load key pair and establish client SSL credentials. + grpc_ssl_pem_key_cert_pair pem_key_cert_pair; + grpc_slice ca_slice, cert_slice, key_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CA_PATH, 1, &ca_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CERT_PATH, 1, &cert_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_KEY_PATH, 1, &key_slice))); + const char* ca_cert = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(ca_slice); + pem_key_cert_pair.private_key = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(key_slice); + pem_key_cert_pair.cert_chain = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(cert_slice); + grpc_channel_credentials* ssl_creds = grpc_ssl_credentials_create( + ca_cert, &pem_key_cert_pair, verify_options, nullptr); + + // Establish a channel pointing at the TLS server. Since the gRPC runtime is + // lazy, this won't necessarily establish a connection yet. + char* target; + gpr_asprintf(&target, "127.0.0.1:%d", port); + grpc_arg ssl_name_override = { + GRPC_ARG_STRING, + const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), + {const_cast<char*>("foo.test.google.fr")}}; + grpc_channel_args grpc_args; + grpc_args.num_args = 1; + grpc_args.args = &ssl_name_override; + grpc_channel* channel = + grpc_secure_channel_create(ssl_creds, target, &grpc_args, nullptr); + GPR_ASSERT(channel); + gpr_free(target); + + // Initially the channel will be idle, the + // grpc_channel_check_connectivity_state triggers an attempt to connect. + GPR_ASSERT(grpc_channel_check_connectivity_state( + channel, 1 /* try_to_connect */) == GRPC_CHANNEL_IDLE); + + // Wait a bounded number of times for the channel to be ready. When the + // channel is ready, the initial TLS handshake will have successfully + // completed. The total time spent on the client side (retries * deadline) + // should be greater than the server side time limit. + int retries = 10; + grpc_connectivity_state state = GRPC_CHANNEL_IDLE; + grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); + + while (state != GRPC_CHANNEL_READY && retries-- > 0) { + grpc_channel_watch_connectivity_state( + channel, state, grpc_timeout_seconds_to_deadline(3), cq, nullptr); + gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(5); + grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, nullptr); + GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); + state = + grpc_channel_check_connectivity_state(channel, 0 /* try_to_connect */); + } + grpc_completion_queue_destroy(cq); + if (retries < 0) { + success = false; + } + + grpc_channel_destroy(channel); + grpc_channel_credentials_release(ssl_creds); + grpc_slice_unref(cert_slice); + grpc_slice_unref(key_slice); + grpc_slice_unref(ca_slice); + + // Now that the client is completely cleaned up, trigger the server to + // shutdown + gpr_event_set(&client_handshake_complete, &client_handshake_complete); + // Wait for the server to completely shutdown + thd.Join(); + + grpc_shutdown(); + + return success; +} + +static int callback_return_value = 0; +static char callback_target_host[4096]; +static char callback_target_pem[4096]; +static void* callback_userdata = nullptr; +static void* destruct_userdata = nullptr; + +static int verify_callback(const char* target_host, const char* target_pem, + void* userdata) { + if (target_host != nullptr) { + snprintf(callback_target_host, sizeof(callback_target_host), "%s", + target_host); + } else { + callback_target_host[0] = '\0'; + } + if (target_pem != nullptr) { + snprintf(callback_target_pem, sizeof(callback_target_pem), "%s", + target_pem); + } else { + callback_target_pem[0] = '\0'; + } + callback_userdata = userdata; + return callback_return_value; +} + +static void verify_destruct(void* userdata) { destruct_userdata = userdata; } + +int main(int argc, char* argv[]) { + int userdata = 42; + verify_peer_options verify_options; + + // Load the server's cert so that we can assert it gets passed to the callback + grpc_slice cert_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CERT_PATH, 1, &cert_slice))); + const char* server_cert = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(cert_slice); + + // Running with all-null values should have no effect + verify_options.verify_peer_callback = nullptr; + verify_options.verify_peer_callback_userdata = nullptr; + verify_options.verify_peer_destruct = nullptr; + GPR_ASSERT(verify_peer_options_test(&verify_options)); + GPR_ASSERT(strlen(callback_target_host) == 0); + GPR_ASSERT(strlen(callback_target_pem) == 0); + GPR_ASSERT(callback_userdata == nullptr); + GPR_ASSERT(destruct_userdata == nullptr); + + // Running with the callbacks and verify we get the expected values + verify_options.verify_peer_callback = verify_callback; + verify_options.verify_peer_callback_userdata = static_cast<void*>(&userdata); + verify_options.verify_peer_destruct = verify_destruct; + GPR_ASSERT(verify_peer_options_test(&verify_options)); + GPR_ASSERT(strcmp(callback_target_host, "foo.test.google.fr") == 0); + GPR_ASSERT(strcmp(callback_target_pem, server_cert) == 0); + GPR_ASSERT(callback_userdata == static_cast<void*>(&userdata)); + GPR_ASSERT(destruct_userdata == static_cast<void*>(&userdata)); + + // If the callback returns non-zero, initializing the channel should fail. + callback_return_value = 1; + GPR_ASSERT(!verify_peer_options_test(&verify_options)); + + grpc_slice_unref(cert_slice); + + return 0; +} + +#else /* GRPC_POSIX_SOCKET */ + +int main(int argc, char** argv) { return 1; } + +#endif /* GRPC_POSIX_SOCKET */ diff --git a/test/core/security/BUILD b/test/core/security/BUILD index 70bcc8c9c3..12aa84d93b 100644 --- a/test/core/security/BUILD +++ b/test/core/security/BUILD @@ -183,6 +183,7 @@ grpc_cc_test( "//:gpr", "//:gpr_base", "//:grpc", + "//test/core/util:gpr_test_util", ], ) @@ -195,6 +196,7 @@ grpc_cc_test( "//:gpr", "//:gpr_base", "//:grpc", + "//test/core/util:gpr_test_util", ], ) @@ -206,6 +208,7 @@ grpc_cc_test( "//:alts_util", "//:gpr", "//:grpc", + "//test/core/util:gpr_test_util", ], ) @@ -220,5 +223,6 @@ grpc_cc_test( "//:grpc_secure", "//:tsi", "//:tsi_interface", + "//test/core/util:gpr_test_util", ], ) diff --git a/test/core/statistics/census_log_tests.h b/test/core/statistics/census_log_tests.h deleted file mode 100644 index ed808636e3..0000000000 --- a/test/core/statistics/census_log_tests.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * Copyright 2015 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. - * - */ - -#ifndef GRPC_TEST_CORE_STATISTICS_CENSUS_LOG_TESTS_H -#define GRPC_TEST_CORE_STATISTICS_CENSUS_LOG_TESTS_H - -void test_invalid_record_size(); -void test_end_write_with_different_size(); -void test_read_pending_record(); -void test_read_beyond_pending_record(); -void test_detached_while_reading(); -void test_fill_log_no_fragmentation(); -void test_fill_circular_log_no_fragmentation(); -void test_fill_log_with_straddling_records(); -void test_fill_circular_log_with_straddling_records(); -void test_multiple_writers_circular_log(); -void test_multiple_writers(); -void test_performance(); -void test_small_log(); - -#endif /* GRPC_TEST_CORE_STATISTICS_CENSUS_LOG_TESTS_H */ diff --git a/test/core/statistics/census_stub_test.cc b/test/core/statistics/census_stub_test.cc deleted file mode 100644 index 507ae0a9fa..0000000000 --- a/test/core/statistics/census_stub_test.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * Copyright 2015 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 <stdio.h> -#include <stdlib.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include "src/core/ext/census/census_interface.h" -#include "src/core/ext/census/census_rpc_stats.h" -#include "test/core/util/test_config.h" - -/* Tests census noop stubs in a simulated rpc flow */ -void test_census_stubs(void) { - census_op_id op_id; - census_rpc_stats* stats = census_rpc_stats_create_empty(); - census_aggregated_rpc_stats data_map = {0, NULL}; - - /* Initializes census library at server start up time. */ - census_init(); - /* Starts tracing at the beginning of a rpc. */ - op_id = census_tracing_start_op(); - /* Appends custom annotations on a trace object. */ - census_tracing_print(op_id, "annotation foo"); - census_tracing_print(op_id, "annotation bar"); - /* Appends method tag on the trace object. */ - census_add_method_tag(op_id, "service_foo/method.bar"); - /* Either record client side stats or server side stats associated with the - op_id. Here for testing purpose, we record both. */ - census_record_rpc_client_stats(op_id, stats); - census_record_rpc_server_stats(op_id, stats); - /* Ends a tracing. */ - census_tracing_end_op(op_id); - /* In process stats queries. */ - census_get_server_stats(&data_map); - census_aggregated_rpc_stats_set_empty(&data_map); - census_get_client_stats(&data_map); - census_aggregated_rpc_stats_set_empty(&data_map); - gpr_free(stats); - census_shutdown(); -} - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - test_census_stubs(); - return 0; -} diff --git a/test/core/statistics/multiple_writers_test.cc b/test/core/statistics/multiple_writers_test.cc deleted file mode 100644 index 47410ab105..0000000000 --- a/test/core/statistics/multiple_writers_test.cc +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * Copyright 2015 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 "test/core/statistics/census_log_tests.h" - -#include <stdlib.h> - -#include <grpc/support/time.h> -#include "test/core/util/test_config.h" - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - srand(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); - test_multiple_writers(); - return 0; -} diff --git a/test/core/statistics/performance_test.cc b/test/core/statistics/performance_test.cc deleted file mode 100644 index 9d4fd6ef90..0000000000 --- a/test/core/statistics/performance_test.cc +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * Copyright 2015 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 "test/core/statistics/census_log_tests.h" - -#include <stdlib.h> - -#include <grpc/support/time.h> -#include "test/core/util/test_config.h" - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - srand(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); - test_performance(); - return 0; -} diff --git a/test/core/statistics/quick_test.cc b/test/core/statistics/quick_test.cc deleted file mode 100644 index 91ecdde818..0000000000 --- a/test/core/statistics/quick_test.cc +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015 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 "test/core/statistics/census_log_tests.h" - -#include <stdlib.h> - -#include <grpc/support/time.h> -#include "test/core/util/test_config.h" - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - srand(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); - test_invalid_record_size(); - test_end_write_with_different_size(); - test_read_pending_record(); - test_read_beyond_pending_record(); - test_detached_while_reading(); - test_fill_log_no_fragmentation(); - test_fill_circular_log_no_fragmentation(); - test_fill_log_with_straddling_records(); - test_fill_circular_log_with_straddling_records(); - return 0; -} diff --git a/test/core/statistics/rpc_stats_test.cc b/test/core/statistics/rpc_stats_test.cc deleted file mode 100644 index a2a648e2ad..0000000000 --- a/test/core/statistics/rpc_stats_test.cc +++ /dev/null @@ -1,183 +0,0 @@ -/* - * - * Copyright 2015 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 <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/port_platform.h> -#include <grpc/support/string.h> -#include <grpc/support/time.h> - -#include "src/core/ext/census/census_interface.h" -#include "src/core/ext/census/census_rpc_stats.h" -#include "src/core/ext/census/census_tracing.h" -#include "test/core/util/test_config.h" - -/* Ensure all possible state transitions are called without causing problem */ -static void test_init_shutdown(void) { - census_stats_store_init(); - census_stats_store_init(); - census_stats_store_shutdown(); - census_stats_store_shutdown(); - census_stats_store_init(); -} - -static void test_create_and_destroy(void) { - census_rpc_stats* stats = NULL; - census_aggregated_rpc_stats agg_stats = {0, NULL}; - - stats = census_rpc_stats_create_empty(); - GPR_ASSERT(stats != NULL); - GPR_ASSERT(stats->cnt == 0 && stats->rpc_error_cnt == 0 && - stats->app_error_cnt == 0 && stats->elapsed_time_ms == 0.0 && - stats->api_request_bytes == 0 && stats->wire_request_bytes == 0 && - stats->api_response_bytes == 0 && stats->wire_response_bytes == 0); - gpr_free(stats); - - census_aggregated_rpc_stats_set_empty(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 0); - GPR_ASSERT(agg_stats.stats == NULL); - agg_stats.num_entries = 1; - agg_stats.stats = (census_per_method_rpc_stats*)gpr_malloc( - sizeof(census_per_method_rpc_stats)); - agg_stats.stats[0].method = gpr_strdup("foo"); - census_aggregated_rpc_stats_set_empty(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 0); - GPR_ASSERT(agg_stats.stats == NULL); -} - -#define ASSERT_NEAR(a, b) \ - GPR_ASSERT((a - b) * (a - b) < 1e-24 * (a + b) * (a + b)) - -static void test_record_and_get_stats(void) { - census_rpc_stats stats = {1, 2, 3, 4, 5.1, 6.2, 7.3, 8.4}; - census_op_id id; - census_aggregated_rpc_stats agg_stats = {0, NULL}; - - /* Record client stats twice with the same op_id. */ - census_init(); - id = census_tracing_start_op(); - census_add_method_tag(id, "m1"); - census_record_rpc_client_stats(id, &stats); - census_record_rpc_client_stats(id, &stats); - census_tracing_end_op(id); - /* Server stats expect to be empty */ - census_get_server_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 0); - GPR_ASSERT(agg_stats.stats == NULL); - /* Client stats expect to have one entry */ - census_get_client_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 1); - GPR_ASSERT(agg_stats.stats != NULL); - GPR_ASSERT(strcmp(agg_stats.stats[0].method, "m1") == 0); - GPR_ASSERT(agg_stats.stats[0].minute_stats.cnt == 2 && - agg_stats.stats[0].hour_stats.cnt == 2 && - agg_stats.stats[0].total_stats.cnt == 2); - ASSERT_NEAR(agg_stats.stats[0].minute_stats.wire_response_bytes, 16.8); - ASSERT_NEAR(agg_stats.stats[0].hour_stats.wire_response_bytes, 16.8); - ASSERT_NEAR(agg_stats.stats[0].total_stats.wire_response_bytes, 16.8); - /* Get stats again, results should be the same. */ - census_get_client_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 1); - census_aggregated_rpc_stats_set_empty(&agg_stats); - census_shutdown(); - - /* Record both server (once) and client (twice) stats with different op_ids. - */ - census_init(); - id = census_tracing_start_op(); - census_add_method_tag(id, "m2"); - census_record_rpc_client_stats(id, &stats); - census_tracing_end_op(id); - id = census_tracing_start_op(); - census_add_method_tag(id, "m3"); - census_record_rpc_server_stats(id, &stats); - census_tracing_end_op(id); - id = census_tracing_start_op(); - census_add_method_tag(id, "m4"); - census_record_rpc_client_stats(id, &stats); - census_tracing_end_op(id); - /* Check server stats */ - census_get_server_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 1); - GPR_ASSERT(strcmp(agg_stats.stats[0].method, "m3") == 0); - GPR_ASSERT(agg_stats.stats[0].minute_stats.app_error_cnt == 3 && - agg_stats.stats[0].hour_stats.app_error_cnt == 3 && - agg_stats.stats[0].total_stats.app_error_cnt == 3); - census_aggregated_rpc_stats_set_empty(&agg_stats); - /* Check client stats */ - census_get_client_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 2); - GPR_ASSERT(agg_stats.stats != NULL); - GPR_ASSERT((strcmp(agg_stats.stats[0].method, "m2") == 0 && - strcmp(agg_stats.stats[1].method, "m4") == 0) || - (strcmp(agg_stats.stats[0].method, "m4") == 0 && - strcmp(agg_stats.stats[1].method, "m2") == 0)); - GPR_ASSERT(agg_stats.stats[0].minute_stats.cnt == 1 && - agg_stats.stats[1].minute_stats.cnt == 1); - census_aggregated_rpc_stats_set_empty(&agg_stats); - census_shutdown(); -} - -static void test_record_stats_on_unknown_op_id(void) { - census_op_id unknown_id = {0xDEAD, 0xBEEF}; - census_rpc_stats stats = {1, 2, 3, 4, 5.1, 6.2, 7.3, 8.4}; - census_aggregated_rpc_stats agg_stats = {0, NULL}; - - census_init(); - /* Tests that recording stats against unknown id is noop. */ - census_record_rpc_client_stats(unknown_id, &stats); - census_record_rpc_server_stats(unknown_id, &stats); - census_get_server_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 0); - GPR_ASSERT(agg_stats.stats == NULL); - census_get_client_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 0); - GPR_ASSERT(agg_stats.stats == NULL); - census_aggregated_rpc_stats_set_empty(&agg_stats); - census_shutdown(); -} - -/* Test that record stats is noop when trace store is uninitialized. */ -static void test_record_stats_with_trace_store_uninitialized(void) { - census_rpc_stats stats = {1, 2, 3, 4, 5.1, 6.2, 7.3, 8.4}; - census_op_id id = {0, 0}; - census_aggregated_rpc_stats agg_stats = {0, NULL}; - - census_init(); - id = census_tracing_start_op(); - census_add_method_tag(id, "m"); - census_tracing_end_op(id); - /* shuts down trace store only. */ - census_tracing_shutdown(); - census_record_rpc_client_stats(id, &stats); - census_get_client_stats(&agg_stats); - GPR_ASSERT(agg_stats.num_entries == 0); - census_stats_store_shutdown(); -} - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - test_init_shutdown(); - test_create_and_destroy(); - test_record_and_get_stats(); - test_record_stats_on_unknown_op_id(); - test_record_stats_with_trace_store_uninitialized(); - return 0; -} diff --git a/test/core/surface/num_external_connectivity_watchers_test.cc b/test/core/surface/num_external_connectivity_watchers_test.cc index 467deeeaec..7b7a0b6dfc 100644 --- a/test/core/surface/num_external_connectivity_watchers_test.cc +++ b/test/core/surface/num_external_connectivity_watchers_test.cc @@ -168,7 +168,7 @@ static const test_fixture insecure_test = { static grpc_channel* secure_test_create_channel(const char* addr) { grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(test_root_cert, nullptr, nullptr); + grpc_ssl_credentials_create(test_root_cert, nullptr, nullptr, nullptr); grpc_arg ssl_name_override = { GRPC_ARG_STRING, const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), diff --git a/test/core/surface/sequential_connectivity_test.cc b/test/core/surface/sequential_connectivity_test.cc index 9aba4c499e..10562b3be9 100644 --- a/test/core/surface/sequential_connectivity_test.cc +++ b/test/core/surface/sequential_connectivity_test.cc @@ -144,7 +144,7 @@ static void secure_test_add_port(grpc_server* server, const char* addr) { static grpc_channel* secure_test_create_channel(const char* addr) { grpc_channel_credentials* ssl_creds = - grpc_ssl_credentials_create(test_root_cert, nullptr, nullptr); + grpc_ssl_credentials_create(test_root_cert, nullptr, nullptr, nullptr); grpc_arg ssl_name_override = { GRPC_ARG_STRING, const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), diff --git a/test/core/transport/BUILD b/test/core/transport/BUILD index 84fb3a1421..7ca1c1d943 100644 --- a/test/core/transport/BUILD +++ b/test/core/transport/BUILD @@ -129,6 +129,7 @@ grpc_cc_test( language = "C++", deps = [ "//:grpc", + "//test/core/util:gpr_test_util", ], external_deps = [ "gtest", diff --git a/test/core/tsi/alts/crypt/BUILD b/test/core/tsi/alts/crypt/BUILD index cf9dbca316..abe1e83656 100644 --- a/test/core/tsi/alts/crypt/BUILD +++ b/test/core/tsi/alts/crypt/BUILD @@ -27,6 +27,7 @@ grpc_cc_test( "//:alts_frame_protector", "//:gpr", "//:grpc", + "//test/core/util:gpr_test_util", ], ) diff --git a/test/core/tsi/alts/frame_protector/BUILD b/test/core/tsi/alts/frame_protector/BUILD index dd1966b379..6ff3015f4d 100644 --- a/test/core/tsi/alts/frame_protector/BUILD +++ b/test/core/tsi/alts/frame_protector/BUILD @@ -27,6 +27,7 @@ grpc_cc_test( "//:gpr", "//:grpc", "//test/core/tsi/alts/crypt:alts_crypt_test_util", + "//test/core/util:gpr_test_util", ], ) @@ -39,6 +40,7 @@ grpc_cc_test( "//:gpr", "//:grpc", "//test/core/tsi/alts/crypt:alts_crypt_test_util", + "//test/core/util:gpr_test_util", ], ) @@ -54,6 +56,7 @@ grpc_cc_test( "//:tsi_interface", "//test/core/tsi/alts/crypt:alts_crypt_test_util", "//test/core/tsi:transport_security_test_lib", + "//test/core/util:gpr_test_util", ], ) @@ -67,5 +70,6 @@ grpc_cc_test( "//:gpr_base", "//:grpc", "//test/core/tsi/alts/crypt:alts_crypt_test_util", + "//test/core/util:gpr_test_util", ], ) diff --git a/test/core/tsi/alts/handshaker/BUILD b/test/core/tsi/alts/handshaker/BUILD index 809742744c..3f1a681c1a 100644 --- a/test/core/tsi/alts/handshaker/BUILD +++ b/test/core/tsi/alts/handshaker/BUILD @@ -37,6 +37,7 @@ grpc_cc_test( "//:tsi", "//:tsi_interface", "//:grpc", + "//test/core/util:gpr_test_util", ], ) @@ -47,6 +48,7 @@ grpc_cc_test( deps = [ ":alts_handshaker_service_api_test_lib", "//:grpc", + "//test/core/util:gpr_test_util", ], ) @@ -60,6 +62,7 @@ grpc_cc_test( "//:gpr_base", "//:grpc", "//:tsi", + "//test/core/util:gpr_test_util", ], ) @@ -71,6 +74,7 @@ grpc_cc_test( ":alts_handshaker_service_api_test_lib", "//:grpc", "//:tsi", + "//test/core/util:gpr_test_util", ], ) @@ -81,6 +85,7 @@ grpc_cc_test( deps = [ "//:alts_util", "//:grpc", + "//test/core/util:gpr_test_util", ], ) diff --git a/test/core/tsi/alts/zero_copy_frame_protector/BUILD b/test/core/tsi/alts/zero_copy_frame_protector/BUILD index 2b41dae043..a3b797327e 100644 --- a/test/core/tsi/alts/zero_copy_frame_protector/BUILD +++ b/test/core/tsi/alts/zero_copy_frame_protector/BUILD @@ -28,6 +28,7 @@ grpc_cc_test( "//:grpc", "//:grpc_base_c", "//test/core/tsi/alts/crypt:alts_crypt_test_util", + "//test/core/util:gpr_test_util", ], ) @@ -40,6 +41,7 @@ grpc_cc_test( "//:gpr", "//:grpc", "//test/core/tsi/alts/crypt:alts_crypt_test_util", + "//test/core/util:gpr_test_util", ], ) @@ -53,5 +55,6 @@ grpc_cc_test( "//:grpc", "//:grpc_base_c", "//test/core/tsi/alts/crypt:alts_crypt_test_util", + "//test/core/util:gpr_test_util", ], ) diff --git a/test/core/util/BUILD b/test/core/util/BUILD index f52570cde5..5492dcfa79 100644 --- a/test/core/util/BUILD +++ b/test/core/util/BUILD @@ -32,6 +32,11 @@ grpc_cc_library( "test_config.h", ], deps = ["//:gpr"], + data = [ + "lsan_suppressions.txt", + "tsan_suppressions.txt", + "ubsan_suppressions.txt", + ], ) grpc_cc_library( diff --git a/tools/lsan_suppressions.txt b/test/core/util/lsan_suppressions.txt index 204ddbef5e..204ddbef5e 100644 --- a/tools/lsan_suppressions.txt +++ b/test/core/util/lsan_suppressions.txt diff --git a/tools/tsan_suppressions.txt b/test/core/util/tsan_suppressions.txt index e0c7907228..e0c7907228 100644 --- a/tools/tsan_suppressions.txt +++ b/test/core/util/tsan_suppressions.txt diff --git a/tools/ubsan_suppressions.txt b/test/core/util/ubsan_suppressions.txt index 2268adc169..2268adc169 100644 --- a/tools/ubsan_suppressions.txt +++ b/test/core/util/ubsan_suppressions.txt diff --git a/test/cpp/client/BUILD b/test/cpp/client/BUILD index 12825e88c2..c03ea92d34 100644 --- a/test/cpp/client/BUILD +++ b/test/cpp/client/BUILD @@ -28,6 +28,7 @@ grpc_cc_test( "//:gpr", "//:grpc", "//:grpc++", + "//test/core/util:gpr_test_util", ], ) diff --git a/test/cpp/ext/filters/census/BUILD b/test/cpp/ext/filters/census/BUILD new file mode 100644 index 0000000000..6567dc667a --- /dev/null +++ b/test/cpp/ext/filters/census/BUILD @@ -0,0 +1,42 @@ +# Copyright 2018 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. + +load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package") +load("//bazel:cc_grpc_library.bzl", "cc_grpc_library") + +licenses(["notice"]) # Apache v2 + +grpc_package(name = "test/core/ext/census") + +grpc_cc_test( + name = "grpc_opencensus_plugin_test", + srcs = [ + "stats_plugin_end2end_test.cc", + ], + language = "C++", + external_deps = [ + "gtest", + "gmock", + "opencensus-stats-test", + ], + deps = [ + "//:grpc++", + "//:grpc_opencensus_plugin", + "//src/proto/grpc/testing:echo_proto", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + "//test/cpp/util:test_util", + "//test/cpp/util:test_config", + ], +) diff --git a/test/cpp/ext/filters/census/stats_plugin_end2end_test.cc b/test/cpp/ext/filters/census/stats_plugin_end2end_test.cc new file mode 100644 index 0000000000..664504a090 --- /dev/null +++ b/test/cpp/ext/filters/census/stats_plugin_end2end_test.cc @@ -0,0 +1,376 @@ +/* + * + * Copyright 2018 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 <string> +#include <thread> // NOLINT +#include <vector> + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "include/grpc++/grpc++.h" +#include "opencensus/stats/stats.h" +#include "opencensus/stats/testing/test_utils.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/core/util/test_config.h" + +namespace grpc { +namespace testing { +namespace { + +using ::opencensus::stats::Aggregation; +using ::opencensus::stats::Distribution; +using ::opencensus::stats::View; +using ::opencensus::stats::ViewDescriptor; +using ::opencensus::stats::testing::TestUtils; + +class EchoServer final : public EchoTestService::Service { + ::grpc::Status Echo(::grpc::ServerContext* context, + const EchoRequest* request, + EchoResponse* response) override { + if (request->param().expected_error().code() == 0) { + response->set_message(request->message()); + return ::grpc::Status::OK; + } else { + return ::grpc::Status(static_cast<::grpc::StatusCode>( + request->param().expected_error().code()), + ""); + } + } +}; + +class StatsPluginEnd2EndTest : public ::testing::Test { + protected: + static void SetUpTestCase() { RegisterOpenCensusPlugin(); } + + void SetUp() { + // Set up a synchronous server on a different thread to avoid the asynch + // interface. + ::grpc::ServerBuilder builder; + int port; + // Use IPv4 here because it's less flaky than IPv6 ("[::]:0") on Travis. + builder.AddListeningPort("0.0.0.0:0", ::grpc::InsecureServerCredentials(), + &port); + builder.RegisterService(&service_); + server_ = builder.BuildAndStart(); + ASSERT_NE(nullptr, server_); + ASSERT_NE(0, port); + server_address_ = absl::StrCat("0.0.0.0:", port); + server_thread_ = std::thread(&StatsPluginEnd2EndTest::RunServerLoop, this); + + stub_ = EchoTestService::NewStub(::grpc::CreateChannel( + server_address_, ::grpc::InsecureChannelCredentials())); + } + + void TearDown() { + server_->Shutdown(); + server_thread_.join(); + } + + void RunServerLoop() { server_->Wait(); } + + const std::string client_method_name_ = "grpc.testing.EchoTestService/Echo"; + const std::string server_method_name_ = "grpc.testing.EchoTestService/Echo"; + + std::string server_address_; + EchoServer service_; + std::unique_ptr<grpc::Server> server_; + std::thread server_thread_; + + std::unique_ptr<EchoTestService::Stub> stub_; +}; + +TEST_F(StatsPluginEnd2EndTest, ErrorCount) { + const auto client_method_descriptor = + ViewDescriptor() + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_name("client_method") + .set_aggregation(Aggregation::Count()) + .add_column(ClientMethodTagKey()); + View client_method_view(client_method_descriptor); + const auto server_method_descriptor = + ViewDescriptor() + .set_measure(kRpcServerServerLatencyMeasureName) + .set_name("server_method") + .set_aggregation(Aggregation::Count()) + .add_column(ServerMethodTagKey()); + View server_method_view(server_method_descriptor); + + const auto client_status_descriptor = + ViewDescriptor() + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_name("client_status") + .set_aggregation(Aggregation::Count()) + .add_column(ClientStatusTagKey()); + View client_status_view(client_status_descriptor); + const auto server_status_descriptor = + ViewDescriptor() + .set_measure(kRpcServerServerLatencyMeasureName) + .set_name("server_status") + .set_aggregation(Aggregation::Count()) + .add_column(ServerStatusTagKey()); + View server_status_view(server_status_descriptor); + + // Cover all valid statuses. + for (int i = 0; i <= 16; ++i) { + EchoRequest request; + request.set_message("foo"); + request.mutable_param()->mutable_expected_error()->set_code(i); + EchoResponse response; + ::grpc::ClientContext context; + ::grpc::Status status = stub_->Echo(&context, request, &response); + } + absl::SleepFor(absl::Milliseconds(500)); + TestUtils::Flush(); + + EXPECT_THAT(client_method_view.GetData().int_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_), 17))); + EXPECT_THAT(server_method_view.GetData().int_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(server_method_name_), 17))); + + auto codes = { + ::testing::Pair(::testing::ElementsAre("OK"), 1), + ::testing::Pair(::testing::ElementsAre("CANCELLED"), 1), + ::testing::Pair(::testing::ElementsAre("UNKNOWN"), 1), + ::testing::Pair(::testing::ElementsAre("INVALID_ARGUMENT"), 1), + ::testing::Pair(::testing::ElementsAre("DEADLINE_EXCEEDED"), 1), + ::testing::Pair(::testing::ElementsAre("NOT_FOUND"), 1), + ::testing::Pair(::testing::ElementsAre("ALREADY_EXISTS"), 1), + ::testing::Pair(::testing::ElementsAre("PERMISSION_DENIED"), 1), + ::testing::Pair(::testing::ElementsAre("UNAUTHENTICATED"), 1), + ::testing::Pair(::testing::ElementsAre("RESOURCE_EXHAUSTED"), 1), + ::testing::Pair(::testing::ElementsAre("FAILED_PRECONDITION"), 1), + ::testing::Pair(::testing::ElementsAre("ABORTED"), 1), + ::testing::Pair(::testing::ElementsAre("OUT_OF_RANGE"), 1), + ::testing::Pair(::testing::ElementsAre("UNIMPLEMENTED"), 1), + ::testing::Pair(::testing::ElementsAre("INTERNAL"), 1), + ::testing::Pair(::testing::ElementsAre("UNAVAILABLE"), 1), + ::testing::Pair(::testing::ElementsAre("DATA_LOSS"), 1), + }; + + EXPECT_THAT(client_status_view.GetData().int_data(), + ::testing::UnorderedElementsAreArray(codes)); + EXPECT_THAT(server_status_view.GetData().int_data(), + ::testing::UnorderedElementsAreArray(codes)); +} + +TEST_F(StatsPluginEnd2EndTest, RequestReceivedBytesPerRpc) { + View client_sent_bytes_per_rpc_view(ClientSentBytesPerRpcCumulative()); + View client_received_bytes_per_rpc_view( + ClientReceivedBytesPerRpcCumulative()); + View server_sent_bytes_per_rpc_view(ServerSentBytesPerRpcCumulative()); + View server_received_bytes_per_rpc_view( + ServerReceivedBytesPerRpcCumulative()); + + { + EchoRequest request; + request.set_message("foo"); + EchoResponse response; + ::grpc::ClientContext context; + ::grpc::Status status = stub_->Echo(&context, request, &response); + ASSERT_TRUE(status.ok()); + EXPECT_EQ("foo", response.message()); + } + absl::SleepFor(absl::Milliseconds(500)); + TestUtils::Flush(); + + EXPECT_THAT(client_received_bytes_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, 1), + ::testing::Property(&Distribution::mean, + ::testing::Gt(0.0)))))); + EXPECT_THAT(client_sent_bytes_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, 1), + ::testing::Property(&Distribution::mean, + ::testing::Gt(0.0)))))); + EXPECT_THAT(server_received_bytes_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(server_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, 1), + ::testing::Property(&Distribution::mean, + ::testing::Gt(0.0)))))); + EXPECT_THAT(server_sent_bytes_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(server_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, 1), + ::testing::Property(&Distribution::mean, + ::testing::Gt(0.0)))))); +} + +TEST_F(StatsPluginEnd2EndTest, Latency) { + View client_latency_view(ClientRoundtripLatencyCumulative()); + View client_server_latency_view(ClientServerLatencyCumulative()); + View server_server_latency_view(ServerServerLatencyCumulative()); + + const absl::Time start_time = absl::Now(); + { + EchoRequest request; + request.set_message("foo"); + EchoResponse response; + ::grpc::ClientContext context; + ::grpc::Status status = stub_->Echo(&context, request, &response); + ASSERT_TRUE(status.ok()); + EXPECT_EQ("foo", response.message()); + } + // We do not know exact latency/elapsed time, but we know it is less than the + // entire time spent making the RPC. + const double max_time = absl::ToDoubleMilliseconds(absl::Now() - start_time); + + absl::SleepFor(absl::Milliseconds(500)); + TestUtils::Flush(); + + EXPECT_THAT( + client_latency_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_), + ::testing::AllOf( + ::testing::Property(&Distribution::count, 1), + ::testing::Property(&Distribution::mean, ::testing::Gt(0.0)), + ::testing::Property(&Distribution::mean, + ::testing::Lt(max_time)))))); + + // Elapsed time is a subinterval of total latency. + const auto client_latency = client_latency_view.GetData() + .distribution_data() + .find({client_method_name_}) + ->second.mean(); + EXPECT_THAT( + client_server_latency_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_), + ::testing::AllOf( + ::testing::Property(&Distribution::count, 1), + ::testing::Property(&Distribution::mean, ::testing::Gt(0.0)), + ::testing::Property(&Distribution::mean, + ::testing::Lt(client_latency)))))); + + // client server elapsed time should be the same value propagated to the + // client. + const auto client_elapsed_time = client_server_latency_view.GetData() + .distribution_data() + .find({client_method_name_}) + ->second.mean(); + EXPECT_THAT( + server_server_latency_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(server_method_name_), + ::testing::AllOf( + ::testing::Property(&Distribution::count, 1), + ::testing::Property(&Distribution::mean, + ::testing::DoubleEq(client_elapsed_time)))))); +} + +TEST_F(StatsPluginEnd2EndTest, CompletedRpcs) { + View client_completed_rpcs_view(ClientCompletedRpcsCumulative()); + View server_completed_rpcs_view(ServerCompletedRpcsCumulative()); + + EchoRequest request; + request.set_message("foo"); + EchoResponse response; + const int count = 5; + for (int i = 0; i < count; ++i) { + { + ::grpc::ClientContext context; + ::grpc::Status status = stub_->Echo(&context, request, &response); + ASSERT_TRUE(status.ok()); + EXPECT_EQ("foo", response.message()); + } + absl::SleepFor(absl::Milliseconds(500)); + TestUtils::Flush(); + + EXPECT_THAT(client_completed_rpcs_view.GetData().int_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_, "OK"), i + 1))); + EXPECT_THAT(server_completed_rpcs_view.GetData().int_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(server_method_name_, "OK"), i + 1))); + } +} + +TEST_F(StatsPluginEnd2EndTest, RequestReceivedMessagesPerRpc) { + // TODO: Use streaming RPCs. + View client_received_messages_per_rpc_view( + ClientSentMessagesPerRpcCumulative()); + View client_sent_messages_per_rpc_view( + ClientReceivedMessagesPerRpcCumulative()); + View server_received_messages_per_rpc_view( + ServerSentMessagesPerRpcCumulative()); + View server_sent_messages_per_rpc_view( + ServerReceivedMessagesPerRpcCumulative()); + + EchoRequest request; + request.set_message("foo"); + EchoResponse response; + const int count = 5; + for (int i = 0; i < count; ++i) { + { + ::grpc::ClientContext context; + ::grpc::Status status = stub_->Echo(&context, request, &response); + ASSERT_TRUE(status.ok()); + EXPECT_EQ("foo", response.message()); + } + absl::SleepFor(absl::Milliseconds(500)); + TestUtils::Flush(); + + EXPECT_THAT( + client_received_messages_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, i + 1), + ::testing::Property(&Distribution::mean, + ::testing::DoubleEq(1.0)))))); + EXPECT_THAT( + client_sent_messages_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(client_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, i + 1), + ::testing::Property(&Distribution::mean, + ::testing::DoubleEq(1.0)))))); + EXPECT_THAT( + server_received_messages_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(server_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, i + 1), + ::testing::Property(&Distribution::mean, + ::testing::DoubleEq(1.0)))))); + EXPECT_THAT( + server_sent_messages_per_rpc_view.GetData().distribution_data(), + ::testing::UnorderedElementsAre(::testing::Pair( + ::testing::ElementsAre(server_method_name_), + ::testing::AllOf(::testing::Property(&Distribution::count, i + 1), + ::testing::Property(&Distribution::mean, + ::testing::DoubleEq(1.0)))))); + } +} + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc_test_init(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/cpp/microbenchmarks/BUILD b/test/cpp/microbenchmarks/BUILD index 5dcfd94ed3..7b27aed4c3 100644 --- a/test/cpp/microbenchmarks/BUILD +++ b/test/cpp/microbenchmarks/BUILD @@ -24,6 +24,7 @@ grpc_cc_test( external_deps = [ "benchmark", ], + deps = ["//test/core/util:gpr_test_util",] ) grpc_cc_library( @@ -148,3 +149,15 @@ grpc_cc_binary( srcs = ["bm_chttp2_hpack.cc"], deps = [":helpers"], ) + +grpc_cc_binary( + name = "bm_opencensus_plugin", + testonly = 1, + srcs = ["bm_opencensus_plugin.cc"], + language = "C++", + deps = [ + ":helpers", + "//:grpc_opencensus_plugin", + "//src/proto/grpc/testing:echo_proto", + ], +) diff --git a/test/cpp/microbenchmarks/bm_call_create.cc b/test/cpp/microbenchmarks/bm_call_create.cc index 831b29c506..dd1610dc3d 100644 --- a/test/cpp/microbenchmarks/bm_call_create.cc +++ b/test/cpp/microbenchmarks/bm_call_create.cc @@ -621,18 +621,26 @@ typedef struct { static void StartTransportStreamOp(grpc_call_element* elem, grpc_transport_stream_op_batch* op) { call_data* calld = static_cast<call_data*>(elem->call_data); + // Construct list of closures to return. + grpc_core::CallCombinerClosureList closures; if (op->recv_initial_metadata) { - GRPC_CALL_COMBINER_START( - calld->call_combiner, - op->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_NONE, "recv_initial_metadata"); + closures.Add(op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE, "recv_initial_metadata"); } if (op->recv_message) { - GRPC_CALL_COMBINER_START(calld->call_combiner, - op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE, "recv_message"); + closures.Add(op->payload->recv_message.recv_message_ready, GRPC_ERROR_NONE, + "recv_message"); } - GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_NONE); + if (op->recv_trailing_metadata) { + closures.Add( + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + GRPC_ERROR_NONE, "recv_trailing_metadata"); + } + if (op->on_complete != nullptr) { + closures.Add(op->on_complete, GRPC_ERROR_NONE, "on_complete"); + } + // Execute closures. + closures.RunClosures(calld->call_combiner); } static void StartTransportOp(grpc_channel_element* elem, diff --git a/test/cpp/microbenchmarks/bm_opencensus_plugin.cc b/test/cpp/microbenchmarks/bm_opencensus_plugin.cc new file mode 100644 index 0000000000..9d42eb891d --- /dev/null +++ b/test/cpp/microbenchmarks/bm_opencensus_plugin.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2018 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 <benchmark/benchmark.h> +#include <string> +#include <thread> // NOLINT + +#include "absl/base/call_once.h" +#include "absl/strings/str_cat.h" +#include "include/grpc++/grpc++.h" +#include "opencensus/stats/stats.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/cpp/microbenchmarks/helpers.h" + +absl::once_flag once; +void RegisterOnce() { absl::call_once(once, grpc::RegisterOpenCensusPlugin); } + +class EchoServer final : public grpc::testing::EchoTestService::Service { + grpc::Status Echo(grpc::ServerContext* context, + const grpc::testing::EchoRequest* request, + grpc::testing::EchoResponse* response) override { + if (request->param().expected_error().code() == 0) { + response->set_message(request->message()); + return grpc::Status::OK; + } else { + return grpc::Status(static_cast<grpc::StatusCode>( + request->param().expected_error().code()), + ""); + } + } +}; + +// An EchoServerThread object creates an EchoServer on a separate thread and +// shuts down the server and thread when it goes out of scope. +class EchoServerThread final { + public: + EchoServerThread() { + grpc::ServerBuilder builder; + int port; + builder.AddListeningPort("[::]:0", grpc::InsecureServerCredentials(), + &port); + builder.RegisterService(&service_); + server_ = builder.BuildAndStart(); + if (server_ == nullptr || port == 0) { + std::abort(); + } + server_address_ = absl::StrCat("[::]:", port); + server_thread_ = std::thread(&EchoServerThread::RunServerLoop, this); + } + + ~EchoServerThread() { + server_->Shutdown(); + server_thread_.join(); + } + + const std::string& address() { return server_address_; } + + private: + void RunServerLoop() { server_->Wait(); } + + std::string server_address_; + EchoServer service_; + std::unique_ptr<grpc::Server> server_; + std::thread server_thread_; +}; + +static void BM_E2eLatencyCensusDisabled(benchmark::State& state) { + EchoServerThread server; + std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = + grpc::testing::EchoTestService::NewStub(grpc::CreateChannel( + server.address(), grpc::InsecureChannelCredentials())); + + grpc::testing::EchoResponse response; + for (auto _ : state) { + grpc::testing::EchoRequest request; + grpc::ClientContext context; + grpc::Status status = stub->Echo(&context, request, &response); + } +} +BENCHMARK(BM_E2eLatencyCensusDisabled); + +static void BM_E2eLatencyCensusEnabled(benchmark::State& state) { + RegisterOnce(); + // This we can safely repeat, and doing so clears accumulated data to avoid + // initialization costs varying between runs. + grpc::RegisterOpenCensusViewsForExport(); + + EchoServerThread server; + std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = + grpc::testing::EchoTestService::NewStub(grpc::CreateChannel( + server.address(), grpc::InsecureChannelCredentials())); + + grpc::testing::EchoResponse response; + for (auto _ : state) { + grpc::testing::EchoRequest request; + grpc::ClientContext context; + grpc::Status status = stub->Echo(&context, request, &response); + } +} +BENCHMARK(BM_E2eLatencyCensusEnabled); + +BENCHMARK_MAIN(); diff --git a/test/cpp/util/BUILD b/test/cpp/util/BUILD index b1153d2df3..9b42bb28b1 100644 --- a/test/cpp/util/BUILD +++ b/test/cpp/util/BUILD @@ -217,6 +217,7 @@ grpc_cc_test( ], deps = [ "//:grpc++", + "//test/core/util:gpr_test_util", ], ) @@ -260,6 +261,7 @@ grpc_cc_test( deps = [ "//:grpc++_error_details", "//src/proto/grpc/testing:echo_messages_proto", + "//test/core/util:gpr_test_util", ], ) diff --git a/tools/bazel.rc b/tools/bazel.rc index 19e7921ca9..39f8071535 100644 --- a/tools/bazel.rc +++ b/tools/bazel.rc @@ -11,7 +11,7 @@ build:asan --copt -fno-omit-frame-pointer build:asan --copt -DGPR_NO_DIRECT_SYSCALLS build:asan --linkopt -fsanitize=address build:asan --action_env=ASAN_OPTIONS=detect_leaks=1:color=always -build:asan --action_env=LSAN_OPTIONS=suppressions=lsan_suppressions.txt:report_objects=1 +build:asan --action_env=LSAN_OPTIONS=suppressions=test/core/util/lsan_suppressions.txt:report_objects=1 build:msan --strip=never build:msan --copt -fsanitize-coverage=edge @@ -32,7 +32,7 @@ build:tsan --copt -fno-omit-frame-pointer build:tsan --copt -DGPR_NO_DIRECT_SYSCALLS build:tsan --copt -DGRPC_TSAN build:tsan --linkopt -fsanitize=thread -build:tsan --action_env=TSAN_OPTIONS=suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1 +build:tsan --action_env=TSAN_OPTIONS=suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1 build:ubsan --strip=never build:ubsan --copt -fsanitize-coverage=edge @@ -42,7 +42,7 @@ build:ubsan --copt -DGRPC_UBSAN build:ubsan --copt -DNDEBUG build:ubsan --copt -fno-sanitize=function,vptr build:ubsan --linkopt -fsanitize=undefined -build:ubsan --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1:suppressions=tools/ubsan_suppressions.txt +build:ubsan --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1:suppressions=test/core/util/ubsan_suppressions.txt build:basicprof --strip=never build:basicprof --copt -DNDEBUG diff --git a/tools/dockerfile/grpc_clang_tidy/Dockerfile b/tools/dockerfile/grpc_clang_tidy/Dockerfile index fb177464ca..dec7680fcf 100644 --- a/tools/dockerfile/grpc_clang_tidy/Dockerfile +++ b/tools/dockerfile/grpc_clang_tidy/Dockerfile @@ -35,7 +35,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 ADD clang_tidy_all_the_things.sh / diff --git a/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile index 1bb7ec0f93..511e2932d6 100644 --- a/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -61,7 +62,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================ # C# dependencies diff --git a/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile index 1bb7ec0f93..511e2932d6 100644 --- a/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -61,7 +62,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================ # C# dependencies diff --git a/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile index 88443f2966..9ede89b78d 100644 --- a/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -61,7 +62,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile index 57670ed6eb..b136259ce9 100644 --- a/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile @@ -30,7 +30,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Define the default command. CMD ["bash"] diff --git a/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile index c8c00050e3..d43d0e40f5 100644 --- a/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile @@ -30,7 +30,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Define the default command. CMD ["bash"] diff --git a/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile index 3d81aea38c..17ca6784da 100644 --- a/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile @@ -30,7 +30,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Define the default command. CMD ["bash"] diff --git a/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile index 7bc9f697d8..e7555c95cf 100644 --- a/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile @@ -30,7 +30,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 RUN pip install twisted h2==2.6.1 hyper diff --git a/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile index bf66eb2034..fc29ada0d2 100644 --- a/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile @@ -45,7 +45,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Trigger download of as many Gradle artifacts as possible. diff --git a/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile index bf66eb2034..fc29ada0d2 100644 --- a/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile @@ -45,7 +45,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Trigger download of as many Gradle artifacts as possible. diff --git a/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile index f8f3fb25f0..015641684b 100644 --- a/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -61,7 +62,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================== # Node dependencies diff --git a/tools/dockerfile/interoptest/grpc_interop_nodepurejs/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_nodepurejs/Dockerfile index 21ef101bc7..0006be790e 100644 --- a/tools/dockerfile/interoptest/grpc_interop_nodepurejs/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_nodepurejs/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ diff --git a/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile index e6107f5c6c..e987d4c88e 100644 --- a/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ diff --git a/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile index e5d728b039..dadd856740 100644 --- a/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -61,7 +62,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Install pip and virtualenv for Python 3.4 RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 diff --git a/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile index 8ad6c31c27..97c146bb53 100644 --- a/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -61,7 +62,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================== # Ruby dependencies diff --git a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile index d5536c1762..56bfb89925 100644 --- a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================ # C# dependencies diff --git a/tools/dockerfile/test/cxx_jessie_x64/Dockerfile b/tools/dockerfile/test/cxx_jessie_x64/Dockerfile index fe8ce85928..c4f959fc1d 100644 --- a/tools/dockerfile/test/cxx_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/cxx_jessie_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/dockerfile/test/cxx_jessie_x86/Dockerfile b/tools/dockerfile/test/cxx_jessie_x86/Dockerfile index 7c89036e1a..d07ea9a9b0 100644 --- a/tools/dockerfile/test/cxx_jessie_x86/Dockerfile +++ b/tools/dockerfile/test/cxx_jessie_x86/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/dockerfile/test/cxx_sanitizers_jessie_x64/Dockerfile b/tools/dockerfile/test/cxx_sanitizers_jessie_x64/Dockerfile index 551a284459..f60f67665d 100644 --- a/tools/dockerfile/test/cxx_sanitizers_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/cxx_sanitizers_jessie_x64/Dockerfile @@ -40,6 +40,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -66,7 +67,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies (purposely excluding Clang because it's part of the base image) diff --git a/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile b/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile index 1d61d2f697..b0d9261af2 100644 --- a/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile +++ b/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile b/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile index eff15c629d..439baadc2c 100644 --- a/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile +++ b/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/dockerfile/test/cxx_ubuntu1710_x64/Dockerfile b/tools/dockerfile/test/cxx_ubuntu1710_x64/Dockerfile index 70fa5ba0e1..b80249d2d8 100644 --- a/tools/dockerfile/test/cxx_ubuntu1710_x64/Dockerfile +++ b/tools/dockerfile/test/cxx_ubuntu1710_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/dockerfile/test/fuzzer/Dockerfile b/tools/dockerfile/test/fuzzer/Dockerfile index d808a4f3b4..986cfcbada 100644 --- a/tools/dockerfile/test/fuzzer/Dockerfile +++ b/tools/dockerfile/test/fuzzer/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile index e455443f21..a82e7050fc 100644 --- a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -142,7 +143,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Install pip and virtualenv for Python 3.4 RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 diff --git a/tools/dockerfile/test/node_jessie_x64/Dockerfile b/tools/dockerfile/test/node_jessie_x64/Dockerfile index c81c0060e7..1fe44b6dd7 100644 --- a/tools/dockerfile/test/node_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/node_jessie_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -76,7 +77,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================== # Node dependencies diff --git a/tools/dockerfile/test/php7_jessie_x64/Dockerfile b/tools/dockerfile/test/php7_jessie_x64/Dockerfile index 436a7a77e8..53ef7b3443 100644 --- a/tools/dockerfile/test/php7_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/php7_jessie_x64/Dockerfile @@ -77,7 +77,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 RUN mkdir /var/local/jenkins diff --git a/tools/dockerfile/test/php_jessie_x64/Dockerfile b/tools/dockerfile/test/php_jessie_x64/Dockerfile index 3376f7420c..e884572640 100644 --- a/tools/dockerfile/test/php_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/php_jessie_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # PHP dependencies diff --git a/tools/dockerfile/test/python_jessie_x64/Dockerfile b/tools/dockerfile/test/python_jessie_x64/Dockerfile index 261d6406ae..c2b4c1845b 100644 --- a/tools/dockerfile/test/python_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/python_jessie_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Install pip and virtualenv for Python 3.4 RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 diff --git a/tools/dockerfile/test/python_pyenv_x64/Dockerfile b/tools/dockerfile/test/python_pyenv_x64/Dockerfile index bba0e84fea..c23e67c904 100644 --- a/tools/dockerfile/test/python_pyenv_x64/Dockerfile +++ b/tools/dockerfile/test/python_pyenv_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 # Install dependencies for pyenv RUN apt-get update && apt-get install -y \ diff --git a/tools/dockerfile/test/ruby_jessie_x64/Dockerfile b/tools/dockerfile/test/ruby_jessie_x64/Dockerfile index 883832f916..d6f7459685 100644 --- a/tools/dockerfile/test/ruby_jessie_x64/Dockerfile +++ b/tools/dockerfile/test/ruby_jessie_x64/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================== # Ruby dependencies diff --git a/tools/dockerfile/test/sanity/Dockerfile b/tools/dockerfile/test/sanity/Dockerfile index da405dff29..cb153012ea 100644 --- a/tools/dockerfile/test/sanity/Dockerfile +++ b/tools/dockerfile/test/sanity/Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ strace \ python-dev \ python-setuptools \ + python-yaml \ telnet \ unzip \ wget \ @@ -65,7 +66,7 @@ RUN apt-get update && apt-get install -y \ # Install Python packages from PyPI RUN pip install --upgrade pip==10.0.1 RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 pyyaml==3.12 +RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 #================= # C++ dependencies diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index f95e0f38db..c84dd1c86c 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -864,7 +864,6 @@ include/grpc/support/time.h \ include/grpc/support/workaround_list.h \ src/core/README.md \ src/core/ext/README.md \ -src/core/ext/census/grpc_context.cc \ src/core/ext/filters/client_channel/README.md \ src/core/ext/filters/client_channel/backup_poller.cc \ src/core/ext/filters/client_channel/backup_poller.h \ @@ -913,6 +912,7 @@ src/core/ext/filters/client_channel/resolver.cc \ src/core/ext/filters/client_channel/resolver.h \ src/core/ext/filters/client_channel/resolver/README.md \ src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ +src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ @@ -1501,6 +1501,7 @@ src/core/tsi/transport_security.h \ src/core/tsi/transport_security_grpc.cc \ src/core/tsi/transport_security_grpc.h \ src/core/tsi/transport_security_interface.h \ +src/cpp/ext/filters/census/grpc_context.cc \ third_party/nanopb/pb.h \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_common.h \ diff --git a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh index fafa1ceecb..1396fc4e42 100644 --- a/tools/internal_ci/linux/grpc_tsan_on_foundry.sh +++ b/tools/internal_ci/linux/grpc_tsan_on_foundry.sh @@ -14,5 +14,5 @@ # limitations under the License. export UPLOAD_TEST_RESULTS=true -EXTRA_FLAGS="--copt=-gmlt --strip=never --copt=-fsanitize=thread --linkopt=-fsanitize=thread --test_timeout=3600" +EXTRA_FLAGS="--copt=-gmlt --strip=never --copt=-fsanitize=thread --linkopt=-fsanitize=thread --test_timeout=3600 --action_env=TSAN_OPTIONS=suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1" github/grpc/tools/internal_ci/linux/grpc_bazel_on_foundry_base.sh "${EXTRA_FLAGS}" diff --git a/tools/run_tests/generated/configs.json b/tools/run_tests/generated/configs.json index a14340cb7b..74c6286240 100644 --- a/tools/run_tests/generated/configs.json +++ b/tools/run_tests/generated/configs.json @@ -6,7 +6,7 @@ "config": "asan-trace-cmp", "environ": { "ASAN_OPTIONS": "detect_leaks=1:color=always", - "LSAN_OPTIONS": "suppressions=tools/lsan_suppressions.txt:report_objects=1" + "LSAN_OPTIONS": "suppressions=test/core/util/lsan_suppressions.txt:report_objects=1" } }, { @@ -16,7 +16,7 @@ "config": "asan", "environ": { "ASAN_OPTIONS": "detect_leaks=1:color=always", - "LSAN_OPTIONS": "suppressions=tools/lsan_suppressions.txt:report_objects=1" + "LSAN_OPTIONS": "suppressions=test/core/util/lsan_suppressions.txt:report_objects=1" } }, { @@ -47,13 +47,13 @@ { "config": "ubsan", "environ": { - "UBSAN_OPTIONS": "halt_on_error=1:print_stacktrace=1:suppressions=tools/ubsan_suppressions.txt" + "UBSAN_OPTIONS": "halt_on_error=1:print_stacktrace=1:suppressions=test/core/util/ubsan_suppressions.txt" } }, { "config": "tsan", "environ": { - "TSAN_OPTIONS": "suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1" + "TSAN_OPTIONS": "suppressions=test/core/util/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1" } }, { diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 291cf87244..df1819f46d 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -1177,6 +1177,23 @@ { "deps": [ "gpr", + "gpr_test_util", + "grpc", + "grpc_test_util" + ], + "headers": [], + "is_filegroup": false, + "language": "c", + "name": "handshake_verify_peer_options", + "src": [ + "test/core/handshake/verify_peer_options.cc" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", "grpc_test_util" ], "headers": [], @@ -9072,8 +9089,7 @@ { "deps": [ "gpr", - "grpc_base", - "nanopb" + "grpc_base" ], "headers": [ "include/grpc/census.h" @@ -9083,7 +9099,7 @@ "name": "census", "src": [ "include/grpc/census.h", - "src/core/ext/census/grpc_context.cc" + "src/cpp/ext/filters/census/grpc_context.cc" ], "third_party": false, "type": "filegroup" @@ -10162,6 +10178,7 @@ "name": "grpc_resolver_dns_ares", "src": [ "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 48031c2fd3..afa2e8bda5 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1451,6 +1451,26 @@ "args": [], "benchmark": false, "ci_platforms": [ + "linux" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [ + "uv" + ], + "flaky": false, + "gtest": false, + "language": "c", + "name": "handshake_verify_peer_options", + "platforms": [ + "linux" + ], + "uses_polling": true + }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ "linux", "mac", "posix", diff --git a/tools/run_tests/helper_scripts/build_ruby.sh b/tools/run_tests/helper_scripts/build_ruby.sh index b15a8639d9..38f99d80ab 100755 --- a/tools/run_tests/helper_scripts/build_ruby.sh +++ b/tools/run_tests/helper_scripts/build_ruby.sh @@ -23,3 +23,6 @@ cd "$(dirname "$0")/../../.." rm -rf ./tmp rake compile + +# build grpc_ruby_plugin +make grpc_ruby_plugin -j8 diff --git a/tools/run_tests/helper_scripts/run_ruby.sh b/tools/run_tests/helper_scripts/run_ruby.sh index 03eaeb0ec7..4e9c212877 100755 --- a/tools/run_tests/helper_scripts/run_ruby.sh +++ b/tools/run_tests/helper_scripts/run_ruby.sh @@ -18,7 +18,4 @@ set -ex # change to grpc repo root cd "$(dirname "$0")/../../.." -# build grpc_ruby_plugin -make grpc_ruby_plugin -j8 - rake diff --git a/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh b/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh index 1ae1d598de..7ff877e830 100755 --- a/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh +++ b/tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh @@ -29,4 +29,5 @@ time ruby src/ruby/end2end/grpc_class_init_driver.rb || EXIT_CODE=1 time ruby src/ruby/end2end/multiple_killed_watching_threads_driver.rb || EXIT_CODE=1 time ruby src/ruby/end2end/load_grpc_with_gc_stress_driver.rb || EXIT_CODE=1 time ruby src/ruby/end2end/client_memory_usage_driver.rb || EXIT_CODE=1 +time ruby src/ruby/end2end/package_with_underscore_checker.rb || EXIT_CODE=1 exit $EXIT_CODE diff --git a/tools/run_tests/sanity/check_bazel_workspace.py b/tools/run_tests/sanity/check_bazel_workspace.py index 673feb7558..d562fffc8a 100755 --- a/tools/run_tests/sanity/check_bazel_workspace.py +++ b/tools/run_tests/sanity/check_bazel_workspace.py @@ -51,6 +51,7 @@ _GRPC_DEP_NAMES = [ 'com_github_google_benchmark', 'com_github_cares_cares', 'com_google_absl', + 'io_opencensus_cpp', _BAZEL_TOOLCHAINS_DEP_NAME, _TWISTED_TWISTED_DEP_NAME, _YAML_PYYAML_DEP_NAME, @@ -140,7 +141,6 @@ if len(workspace_git_hashes - git_submodule_hashes) > 0: print( "Found discrepancies between git submodules and Bazel WORKSPACE dependencies" ) - sys.exit(1) # Also check that we can override each dependency for name in _GRPC_DEP_NAMES: |