Merge pull request #11069 from yang-g/bazel_test_filtering

Properly filter the end2end tests
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..60131f2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,31 @@
+Please answer these questions before submitting your issue. 
+ 
+### Should this be an issue in the gRPC issue tracker?
+ 
+Create new issues for bugs and feature requests. An issue needs to be actionable. General gRPC discussions and usage questions belong to:
+- [grpc.io mailing list](https://groups.google.com/forum/#!forum/grpc-io)
+- [StackOverflow, with `grpc` tag](http://stackoverflow.com/questions/tagged/grpc)
+ 
+*Please don't double post your questions in more locations, we are monitoring both channels and the time spent de-duplicating questions can is better spent answering more user questions.*
+ 
+### What version of gRPC and what language are you using?
+ 
+ 
+### What operating system (Linux, Windows, …) and version?
+ 
+ 
+### What runtime / compiler are you using (e.g. python version or version of gcc)
+ 
+ 
+### What did you do?
+If possible, provide a recipe for reproducing the error. Try being specific and include code snippets if helpful.
+ 
+### What did you expect to see?
+ 
+ 
+### What did you see instead?
+ 
+Make sure you include information that can help us debug (full error message, exception listing, stack trace, logs).
+ 
+### Anything else we should know about your project / environment?
+
diff --git a/.gitmodules b/.gitmodules
index 0f00369..144fd08 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -14,9 +14,6 @@
 [submodule "third_party/boringssl"]
 	path = third_party/boringssl
 	url = https://github.com/google/boringssl.git
-[submodule "third_party/thrift"]
-	path = third_party/thrift
-	url = https://github.com/apache/thrift.git
 [submodule "third_party/benchmark"]
 	path = third_party/benchmark
 	url = https://github.com/google/benchmark
diff --git a/BUILD b/BUILD
index 0339c87..62f0e1d 100644
--- a/BUILD
+++ b/BUILD
@@ -829,14 +829,18 @@
 grpc_cc_library(
     name = "grpc_lb_policy_grpclb",
     srcs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
     ],
     hdrs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
     ],
@@ -853,14 +857,18 @@
 grpc_cc_library(
     name = "grpc_lb_policy_grpclb_secure",
     srcs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
     ],
     hdrs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h",
         "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
     ],
@@ -1404,18 +1412,6 @@
 )
 
 grpc_cc_library(
-    name = "thrift_util",
-    language = "c++",
-    public_hdrs = [
-        "include/grpc++/impl/codegen/thrift_serializer.h",
-        "include/grpc++/impl/codegen/thrift_utils.h",
-    ],
-    deps = [
-        "grpc++_codegen_base",
-    ],
-)
-
-grpc_cc_library(
     name = "grpc++_reflection",
     srcs = [
         "src/cpp/ext/proto_server_reflection.cc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a179a0f..f6c5f9e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -676,6 +676,7 @@
 add_dependencies(buildtests_cxx grpc_cli)
 add_dependencies(buildtests_cxx grpc_tool_test)
 add_dependencies(buildtests_cxx grpclb_api_test)
+add_dependencies(buildtests_cxx grpclb_end2end_test)
 add_dependencies(buildtests_cxx grpclb_test)
 add_dependencies(buildtests_cxx health_service_end2end_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@@ -1120,8 +1121,10 @@
   src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
   src/core/ext/transport/chttp2/client/insecure/channel_create.c
   src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
+  src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c
+  src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
   third_party/nanopb/pb_common.c
@@ -1990,8 +1993,10 @@
   src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c
   src/core/ext/filters/load_reporting/load_reporting.c
   src/core/ext/filters/load_reporting/load_reporting_filter.c
+  src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c
+  src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
   src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
   third_party/nanopb/pb_common.c
@@ -3276,8 +3281,6 @@
   include/grpc/impl/codegen/sync_windows.h
   include/grpc++/impl/codegen/proto_utils.h
   include/grpc++/impl/codegen/config_protobuf.h
-  include/grpc++/impl/codegen/thrift_serializer.h
-  include/grpc++/impl/codegen/thrift_utils.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -10748,6 +10751,55 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(grpclb_end2end_test
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.cc
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.h
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.h
+  test/cpp/end2end/grpclb_end2end_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+protobuf_generate_grpc_cpp(
+  src/proto/grpc/lb/v1/load_balancer.proto
+)
+
+target_include_directories(grpclb_end2end_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${BENCHMARK_ROOT_DIR}/include
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+  PRIVATE ${CARES_BUILD_INCLUDE_DIR}
+  PRIVATE ${CARES_INCLUDE_DIR}
+  PRIVATE ${CARES_PLATFORM_INCLUDE_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(grpclb_end2end_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_util
+  grpc_test_util
+  grpc++
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(grpclb_test
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.pb.cc
   ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
@@ -11108,6 +11160,7 @@
 add_executable(memory_test
   test/core/support/memory_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
 )
 
 
@@ -11126,6 +11179,8 @@
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
   PRIVATE third_party/googletest/googletest/include
   PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
   PRIVATE ${_gRPC_PROTO_GENS_DIR}
 )
 
diff --git a/INSTALL.md b/INSTALL.md
index 29f0060..5406fec 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -46,6 +46,20 @@
  $ brew install autoconf automake libtool shtool
 ```
 
+If you plan to build from source and run tests, install the following as well:
+```sh
+ $ brew install gflags
+```
+
+*Tip*: when building, 
+you *may* want to explicitly set the `LIBTOOL` and `LIBTOOLIZE`
+environment variables when running `make` to ensure the version
+installed by `brew` is being used:
+
+```sh
+ $ LIBTOOL=glibtool LIBTOOLIZE=glibtoolize make
+```
+
 ## Protoc
 
 By default gRPC uses [protocol buffers](https://github.com/google/protobuf),
diff --git a/Makefile b/Makefile
index dd35d8c..1a8ee55 100644
--- a/Makefile
+++ b/Makefile
@@ -1154,6 +1154,7 @@
 grpc_ruby_plugin: $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
 grpc_tool_test: $(BINDIR)/$(CONFIG)/grpc_tool_test
 grpclb_api_test: $(BINDIR)/$(CONFIG)/grpclb_api_test
+grpclb_end2end_test: $(BINDIR)/$(CONFIG)/grpclb_end2end_test
 grpclb_test: $(BINDIR)/$(CONFIG)/grpclb_test
 health_service_end2end_test: $(BINDIR)/$(CONFIG)/health_service_end2end_test
 http2_client: $(BINDIR)/$(CONFIG)/http2_client
@@ -1578,6 +1579,7 @@
   $(BINDIR)/$(CONFIG)/grpc_cli \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
+  $(BINDIR)/$(CONFIG)/grpclb_end2end_test \
   $(BINDIR)/$(CONFIG)/grpclb_test \
   $(BINDIR)/$(CONFIG)/health_service_end2end_test \
   $(BINDIR)/$(CONFIG)/http2_client \
@@ -1699,6 +1701,7 @@
   $(BINDIR)/$(CONFIG)/grpc_cli \
   $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
+  $(BINDIR)/$(CONFIG)/grpclb_end2end_test \
   $(BINDIR)/$(CONFIG)/grpclb_test \
   $(BINDIR)/$(CONFIG)/health_service_end2end_test \
   $(BINDIR)/$(CONFIG)/http2_client \
@@ -2081,6 +2084,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/grpc_tool_test || ( echo test grpc_tool_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpclb_api_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpclb_api_test || ( echo test grpclb_api_test failed ; exit 1 )
+	$(E) "[RUN]     Testing grpclb_end2end_test"
+	$(Q) $(BINDIR)/$(CONFIG)/grpclb_end2end_test || ( echo test grpclb_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpclb_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpclb_test || ( echo test grpclb_test failed ; exit 1 )
 	$(E) "[RUN]     Testing health_service_end2end_test"
@@ -3099,8 +3104,10 @@
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+    src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c \
+    src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
     third_party/nanopb/pb_common.c \
@@ -3938,8 +3945,10 @@
     src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c \
     src/core/ext/filters/load_reporting/load_reporting.c \
     src/core/ext/filters/load_reporting/load_reporting_filter.c \
+    src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c \
+    src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
     third_party/nanopb/pb_common.c \
@@ -5157,8 +5166,6 @@
     include/grpc/impl/codegen/sync_windows.h \
     include/grpc++/impl/codegen/proto_utils.h \
     include/grpc++/impl/codegen/config_protobuf.h \
-    include/grpc++/impl/codegen/thrift_serializer.h \
-    include/grpc++/impl/codegen/thrift_utils.h \
 
 LIBGRPC++_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_TEST_UTIL_SRC))))
 
@@ -8272,8 +8279,8 @@
 
 LIBARES_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBARES_SRC))))
 
-$(LIBARES_OBJS): CPPFLAGS += -Ithird_party/cares -Ithird_party/cares/cares $(if $(subst Linux,,$(SYSTEM)),,-Ithird_party/cares/config_linux) $(if $(subst Darwin,,$(SYSTEM)),,-Ithird_party/cares/config_darwin) -fvisibility=hidden -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DHAVE_CONFIG_H
-$(LIBARES_OBJS): CFLAGS += -Wno-sign-conversion -Wno-invalid-source-encoding
+$(LIBARES_OBJS): CPPFLAGS += -Ithird_party/cares -Ithird_party/cares/cares $(if $(subst Linux,,$(SYSTEM)),,-Ithird_party/cares/config_linux) $(if $(subst Darwin,,$(SYSTEM)),,-Ithird_party/cares/config_darwin) -fvisibility=hidden -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX $(if $(subst MINGW32,,$(SYSTEM)),-DHAVE_CONFIG_H,)
+$(LIBARES_OBJS): CFLAGS += -Wno-sign-conversion $(if $(subst MINGW32,,$(SYSTEM)),-Wno-invalid-source-encoding,)
 
 $(LIBDIR)/$(CONFIG)/libares.a: $(ZLIB_DEP)  $(LIBARES_OBJS) 
 	$(E) "[AR]      Creating $@"
@@ -15094,6 +15101,53 @@
 $(OBJDIR)/$(CONFIG)/test/cpp/grpclb/grpclb_api_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
 
 
+GRPCLB_END2END_TEST_SRC = \
+    $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc \
+    test/cpp/end2end/grpclb_end2end_test.cc \
+
+GRPCLB_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPCLB_END2END_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/grpclb_end2end_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/grpclb_end2end_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpclb_end2end_test: $(PROTOBUF_DEP) $(GRPCLB_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(GRPCLB_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpclb_end2end_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/src/proto/grpc/lb/v1/load_balancer.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/grpclb_end2end_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_grpclb_end2end_test: $(GRPCLB_END2END_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GRPCLB_END2END_TEST_OBJS:.o=.dep)
+endif
+endif
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/grpclb_end2end_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
+
+
 GRPCLB_TEST_SRC = \
     $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc \
     test/cpp/grpclb/grpclb_test.cc \
diff --git a/Rakefile b/Rakefile
index cc02aa1..7f8d3a2 100755
--- a/Rakefile
+++ b/Rakefile
@@ -80,7 +80,7 @@
   grpc_config = ENV['GRPC_CONFIG'] || 'opt'
   verbose = ENV['V'] || '0'
 
-  env = 'CPPFLAGS="-D_WIN32_WINNT=0x600 -DUNICODE -D_UNICODE -Wno-unused-variable -Wno-unused-result" '
+  env = 'CPPFLAGS="-D_WIN32_WINNT=0x600 -DUNICODE -D_UNICODE -Wno-unused-variable -Wno-unused-result -DCARES_STATICLIB" '
   env += 'LDFLAGS=-static '
   env += 'SYSTEM=MINGW32 '
   env += 'EMBED_ZLIB=true '
diff --git a/bazel/generate_cc.bzl b/bazel/generate_cc.bzl
index 8f0f94f..d05509f 100644
--- a/bazel/generate_cc.bzl
+++ b/bazel/generate_cc.bzl
@@ -9,18 +9,18 @@
   protos = [f for src in ctx.attr.srcs for f in src.proto.direct_sources]
   includes = [f for src in ctx.attr.srcs for f in src.proto.transitive_imports]
   outs = []
+  # label_len is length of the path from WORKSPACE root to the location of this build file
+  label_len = len(ctx.label.package) + 1
   if ctx.executable.plugin:
-    outs += [proto.basename[:-len(".proto")] + ".grpc.pb.h" for proto in protos]
-    outs += [proto.basename[:-len(".proto")] + ".grpc.pb.cc" for proto in protos]
+    outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.h" for proto in protos]
+    outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.cc" for proto in protos]
     if ctx.attr.generate_mock:
-      outs += [proto.basename[:-len(".proto")] + "_mock.grpc.pb.h" for proto in protos]
+      outs += [proto.path[label_len:-len(".proto")] + "_mock.grpc.pb.h" for proto in protos]
   else:
-    outs += [proto.basename[:-len(".proto")] + ".pb.h" for proto in protos]
-    outs += [proto.basename[:-len(".proto")] + ".pb.cc" for proto in protos]
+    outs += [proto.path[label_len:-len(".proto")] + ".pb.h" for proto in protos]
+    outs += [proto.path[label_len:-len(".proto")] + ".pb.cc" for proto in protos]
   out_files = [ctx.new_file(out) for out in outs]
-  # The following should be replaced with ctx.configuration.buildout
-  # whenever this is added to Skylark.
-  dir_out = out_files[0].dirname[:-len(protos[0].dirname)]
+  dir_out = str(ctx.genfiles_dir.path)
 
   arguments = []
   if ctx.executable.plugin:
diff --git a/binding.gyp b/binding.gyp
index 582c612..c8dd5c1 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -39,15 +39,16 @@
 {
   'variables': {
     'runtime%': 'node',
-    # UV integration in C core is enabled by default. It can be disabled
-    # by setting this argument to anything else.
-    'grpc_uv%': 'true',
     # Some Node installations use the system installation of OpenSSL, and on
     # some systems, the system OpenSSL still does not have ALPN support. This
     # will let users recompile gRPC to work without ALPN.
     'grpc_alpn%': 'true',
     # Indicates that the library should be built with gcov.
-    'grpc_gcov%': 'false'
+    'grpc_gcov%': 'false',
+    # Indicates that the library should be built with compatibility for musl
+    # libc, so that it can run on Alpine Linux. This is only necessary if not
+    # building on Alpine Linux
+    'grpc_alpine%': 'false'
   },
   'target_defaults': {
     'configurations': {
@@ -86,17 +87,11 @@
       'include'
     ],
     'defines': [
-      'GPR_BACKWARDS_COMPATIBILITY_MODE'
+      'GPR_BACKWARDS_COMPATIBILITY_MODE',
+      'GRPC_ARES=0',
+      'GRPC_UV'
     ],
     'conditions': [
-      ['grpc_uv=="true"', {
-        'defines': [
-          'GRPC_ARES=0',
-          # Disabling this while bugs are ironed out. Uncomment this to
-          # re-enable libuv integration in C core.
-          'GRPC_UV'
-        ]
-      }],
       ['grpc_gcov=="true"', {
         'cflags': [
             '-O0',
@@ -115,6 +110,11 @@
             '-rdynamic',
         ],
       }],
+      ['grpc_alpine=="true"', {
+        'defines': [
+          'GPR_MUSL_LIBC_COMPAT'
+        ]
+      }],
       ['OS!="win" and runtime=="electron"', {
         "defines": [
           'OPENSSL_NO_THREADS'
@@ -535,6 +535,10 @@
             }
           ]
         },
+      ]
+    }],
+    ['OS == "win"', {
+      'targets': [
         # Only want to compile zlib under Windows
         {
           'cflags': [
@@ -569,7 +573,6 @@
     }]
   ],
   'targets': [
-
     {
       'cflags': [
         '-std=c99',
@@ -648,7 +651,6 @@
       'type': 'static_library',
       'dependencies': [
         'gpr',
-        'node_modules/cares/deps/cares/cares.gyp:cares',
       ],
       'sources': [
         'src/core/lib/surface/init.c',
@@ -855,8 +857,10 @@
         'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
         'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
         'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
+        'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c',
+        'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c',
         'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
         'third_party/nanopb/pb_common.c',
@@ -940,20 +944,16 @@
         "src/node/ext/call_credentials.cc",
         "src/node/ext/channel.cc",
         "src/node/ext/channel_credentials.cc",
-        "src/node/ext/completion_queue_threadpool.cc",
-        "src/node/ext/completion_queue_uv.cc",
+        "src/node/ext/completion_queue.cc",
         "src/node/ext/node_grpc.cc",
         "src/node/ext/server.cc",
         "src/node/ext/server_credentials.cc",
-        "src/node/ext/server_generic.cc",
-        "src/node/ext/server_uv.cc",
         "src/node/ext/slice.cc",
         "src/node/ext/timeval.cc",
       ],
       "dependencies": [
         "grpc",
         "gpr",
-        "node_modules/cares/deps/cares/cares.gyp:cares",
       ]
     },
     {
diff --git a/build.yaml b/build.yaml
index 7b60612..b293060 100644
--- a/build.yaml
+++ b/build.yaml
@@ -488,13 +488,17 @@
   - grpc_base
 - name: grpc_lb_policy_grpclb
   headers:
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
   src:
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
   plugin: grpc_lb_policy_grpclb
@@ -504,13 +508,17 @@
   - nanopb
 - name: grpc_lb_policy_grpclb_secure
   headers:
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
   - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
   src:
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c
+  - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
   - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
   plugin: grpc_lb_policy_grpclb
@@ -979,13 +987,6 @@
   - include/grpc++/test/server_context_test_spouse.h
   deps:
   - grpc++
-- name: thrift_util
-  language: c++
-  public_headers:
-  - include/grpc++/impl/codegen/thrift_serializer.h
-  - include/grpc++/impl/codegen/thrift_utils.h
-  uses:
-  - grpc++_codegen_base
 libs:
 - name: gpr
   build: all
@@ -1283,7 +1284,6 @@
   - grpc++_codegen_base_src
   - grpc++_codegen_proto
   - grpc++_config_proto
-  - thrift_util
 - name: grpc++_unsecure
   build: all
   language: c++
@@ -3812,6 +3812,20 @@
   - grpc_test_util
   - grpc++
   - grpc
+- name: grpclb_end2end_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - src/proto/grpc/lb/v1/load_balancer.proto
+  - test/cpp/end2end/grpclb_end2end_test.cc
+  deps:
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: grpclb_test
   gtest: false
   build: test
@@ -4498,10 +4512,11 @@
       UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1:suppressions=tools/ubsan_suppressions.txt
 defaults:
   ares:
-    CFLAGS: -Wno-sign-conversion -Wno-invalid-source-encoding
+    CFLAGS: -Wno-sign-conversion $(if $(subst MINGW32,,$(SYSTEM)),-Wno-invalid-source-encoding,)
     CPPFLAGS: -Ithird_party/cares -Ithird_party/cares/cares $(if $(subst Linux,,$(SYSTEM)),,-Ithird_party/cares/config_linux)
       $(if $(subst Darwin,,$(SYSTEM)),,-Ithird_party/cares/config_darwin) -fvisibility=hidden
-      -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DHAVE_CONFIG_H
+      -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -DNOMINMAX $(if $(subst
+      MINGW32,,$(SYSTEM)),-DHAVE_CONFIG_H,)
   benchmark:
     CPPFLAGS: -Ithird_party/benchmark/include -DHAVE_POSIX_REGEX
   boringssl:
@@ -4547,13 +4562,10 @@
   - src/node/ext/call_credentials.cc
   - src/node/ext/channel.cc
   - src/node/ext/channel_credentials.cc
-  - src/node/ext/completion_queue_threadpool.cc
-  - src/node/ext/completion_queue_uv.cc
+  - src/node/ext/completion_queue.cc
   - src/node/ext/node_grpc.cc
   - src/node/ext/server.cc
   - src/node/ext/server_credentials.cc
-  - src/node/ext/server_generic.cc
-  - src/node/ext/server_uv.cc
   - src/node/ext/slice.cc
   - src/node/ext/timeval.cc
 openssl_fallback:
diff --git a/composer.json b/composer.json
index 0cafb94..284b57a 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
   "license": "BSD-3-Clause",
   "require": {
     "php": ">=5.5.0",
-    "google/protobuf": "^v3.1.0"
+    "google/protobuf": "^v3.3.0"
   },
   "require-dev": {
     "google/auth": "v0.9"
diff --git a/config.m4 b/config.m4
index bbd667c..1c0c6d9 100644
--- a/config.m4
+++ b/config.m4
@@ -291,8 +291,10 @@
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+    src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c \
+    src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \
     src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
     third_party/nanopb/pb_common.c \
diff --git a/examples/protos/BUILD b/examples/BUILD
similarity index 79%
rename from examples/protos/BUILD
rename to examples/BUILD
index 2ffdf64..382713e 100644
--- a/examples/protos/BUILD
+++ b/examples/BUILD
@@ -33,20 +33,34 @@
 
 grpc_proto_library(
     name = "auth_sample",
-    srcs = ["auth_sample.proto"],
+    srcs = ["protos/auth_sample.proto"],
 )
 
 grpc_proto_library(
     name = "hellostreamingworld",
-    srcs = ["hellostreamingworld.proto"],
+    srcs = ["protos/hellostreamingworld.proto"],
 )
 
 grpc_proto_library(
     name = "helloworld",
-    srcs = ["helloworld.proto"],
+    srcs = ["protos/helloworld.proto"],
 )
 
 grpc_proto_library(
     name = "route_guide",
-    srcs = ["route_guide.proto"],
+    srcs = ["protos/route_guide.proto"],
+)
+
+cc_binary(
+    name = "greeter_client",
+    srcs = ["cpp/helloworld/greeter_client.cc"],
+    deps = ["helloworld"],
+    defines = ["BAZEL_BUILD"],
+)
+
+cc_binary(
+    name = "greeter_server",
+    srcs = ["cpp/helloworld/greeter_server.cc"],
+    deps = ["helloworld"],
+    defines = ["BAZEL_BUILD"],
 )
diff --git a/examples/cpp/helloworld/BUILD b/examples/cpp/helloworld/BUILD
deleted file mode 100644
index b9c3f5d..0000000
--- a/examples/cpp/helloworld/BUILD
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-cc_binary(
-    name = "greeter_client",
-    srcs = ["greeter_client.cc"],
-    deps = ["//examples/protos:helloworld"],
-    defines = ["BAZEL_BUILD"],
-)
-
-cc_binary(
-    name = "greeter_server",
-    srcs = ["greeter_server.cc"],
-    deps = ["//examples/protos:helloworld"],
-    defines = ["BAZEL_BUILD"],
-)
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 07755ac..241eba0 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -434,8 +434,10 @@
                       'src/core/ext/filters/client_channel/uri_parser.h',
                       'src/core/ext/filters/deadline/deadline_filter.h',
                       'src/core/ext/transport/chttp2/client/chttp2_connector.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
                       'third_party/nanopb/pb.h',
@@ -668,8 +670,10 @@
                       'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
                       'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
                       'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c',
+                      'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c',
                       'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
                       'third_party/nanopb/pb_common.c',
@@ -895,8 +899,10 @@
                               'src/core/ext/filters/client_channel/uri_parser.h',
                               'src/core/ext/filters/deadline/deadline_filter.h',
                               'src/core/ext/transport/chttp2/client/chttp2_connector.h',
+                              'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h',
+                              'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h',
                               'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
                               'third_party/nanopb/pb.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 1cd6d66..b96f3cb 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -350,8 +350,10 @@
   s.files += %w( src/core/ext/filters/client_channel/uri_parser.h )
   s.files += %w( src/core/ext/filters/deadline/deadline_filter.h )
   s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.h )
+  s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h )
+  s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h )
   s.files += %w( third_party/nanopb/pb.h )
@@ -584,8 +586,10 @@
   s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c )
   s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.c )
   s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c )
+  s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c )
+  s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c )
   s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c )
   s.files += %w( third_party/nanopb/pb_common.c )
diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h
index f334ba6..9fe2bbb 100644
--- a/include/grpc++/impl/codegen/call.h
+++ b/include/grpc++/impl/codegen/call.h
@@ -364,28 +364,6 @@
   bool allow_not_getting_message_;
 };
 
-namespace CallOpGenericRecvMessageHelper {
-class DeserializeFunc {
- public:
-  virtual Status Deserialize(grpc_byte_buffer* buf) = 0;
-  virtual ~DeserializeFunc() {}
-};
-
-template <class R>
-class DeserializeFuncType final : public DeserializeFunc {
- public:
-  DeserializeFuncType(R* message) : message_(message) {}
-  Status Deserialize(grpc_byte_buffer* buf) override {
-    return SerializationTraits<R>::Deserialize(buf, message_);
-  }
-
-  ~DeserializeFuncType() override {}
-
- private:
-  R* message_;  // Not a managed pointer because management is external to this
-};
-}  // namespace CallOpGenericRecvMessageHelper
-
 class CallOpGenericRecvMessage {
  public:
   CallOpGenericRecvMessage()
@@ -393,11 +371,9 @@
 
   template <class R>
   void RecvMessage(R* message) {
-    // Use an explicit base class pointer to avoid resolution error in the
-    // following unique_ptr::reset for some old implementations.
-    CallOpGenericRecvMessageHelper::DeserializeFunc* func =
-        new CallOpGenericRecvMessageHelper::DeserializeFuncType<R>(message);
-    deserialize_.reset(func);
+    deserialize_ = [message](grpc_byte_buffer* buf) -> Status {
+      return SerializationTraits<R>::Deserialize(buf, message);
+    };
   }
 
   // Do not change status if no message is received.
@@ -420,7 +396,7 @@
     if (recv_buf_) {
       if (*status) {
         got_message = true;
-        *status = deserialize_->Deserialize(recv_buf_).ok();
+        *status = deserialize_(recv_buf_).ok();
       } else {
         got_message = false;
         g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@@ -431,11 +407,12 @@
         *status = false;
       }
     }
-    deserialize_.reset();
+    deserialize_ = DeserializeFunc();
   }
 
  private:
-  std::unique_ptr<CallOpGenericRecvMessageHelper::DeserializeFunc> deserialize_;
+  typedef std::function<Status(grpc_byte_buffer*)> DeserializeFunc;
+  DeserializeFunc deserialize_;
   grpc_byte_buffer* recv_buf_;
   bool allow_not_getting_message_;
 };
diff --git a/include/grpc++/impl/codegen/config_protobuf.h b/include/grpc++/impl/codegen/config_protobuf.h
index 8620d4b..e66189f 100644
--- a/include/grpc++/impl/codegen/config_protobuf.h
+++ b/include/grpc++/impl/codegen/config_protobuf.h
@@ -34,6 +34,8 @@
 #ifndef GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
 #define GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
 
+#define GRPC_OPEN_SOURCE_PROTO
+
 #ifndef GRPC_CUSTOM_PROTOBUF_INT64
 #include <google/protobuf/stubs/common.h>
 #define GRPC_CUSTOM_PROTOBUF_INT64 ::google::protobuf::int64
diff --git a/include/grpc++/impl/codegen/core_codegen.h b/include/grpc++/impl/codegen/core_codegen.h
index a159372..7a1236a 100644
--- a/include/grpc++/impl/codegen/core_codegen.h
+++ b/include/grpc++/impl/codegen/core_codegen.h
@@ -90,11 +90,15 @@
 
   grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
                                                 size_t nslices) override;
-
+  grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                           void (*destroy)(void*),
+                                           void* user_data) override;
   grpc_slice grpc_empty_slice() override;
   grpc_slice grpc_slice_malloc(size_t length) override;
   void grpc_slice_unref(grpc_slice slice) override;
+  grpc_slice grpc_slice_ref(grpc_slice slice) override;
   grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) override;
+  grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) override;
   void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice slice) override;
   void grpc_slice_buffer_pop(grpc_slice_buffer* sb) override;
   grpc_slice grpc_slice_from_static_buffer(const void* buffer,
diff --git a/include/grpc++/impl/codegen/core_codegen_interface.h b/include/grpc++/impl/codegen/core_codegen_interface.h
index 7cc3a82..4ac37f5 100644
--- a/include/grpc++/impl/codegen/core_codegen_interface.h
+++ b/include/grpc++/impl/codegen/core_codegen_interface.h
@@ -101,15 +101,18 @@
 
   virtual grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
                                                         size_t nslices) = 0;
-
+  virtual grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                                   void (*destroy)(void*),
+                                                   void* user_data) = 0;
   virtual void grpc_call_ref(grpc_call* call) = 0;
   virtual void grpc_call_unref(grpc_call* call) = 0;
   virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) = 0;
-
   virtual grpc_slice grpc_empty_slice() = 0;
   virtual grpc_slice grpc_slice_malloc(size_t length) = 0;
   virtual void grpc_slice_unref(grpc_slice slice) = 0;
+  virtual grpc_slice grpc_slice_ref(grpc_slice slice) = 0;
   virtual grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) = 0;
+  virtual grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) = 0;
   virtual void grpc_slice_buffer_add(grpc_slice_buffer* sb,
                                      grpc_slice slice) = 0;
   virtual void grpc_slice_buffer_pop(grpc_slice_buffer* sb) = 0;
diff --git a/include/grpc++/impl/codegen/proto_utils.h b/include/grpc++/impl/codegen/proto_utils.h
index 8c0c32b..5cdb2a7 100644
--- a/include/grpc++/impl/codegen/proto_utils.h
+++ b/include/grpc++/impl/codegen/proto_utils.h
@@ -54,8 +54,7 @@
 
 const int kGrpcBufferWriterMaxBufferLength = 1024 * 1024;
 
-class GrpcBufferWriter final
-    : public ::grpc::protobuf::io::ZeroCopyOutputStream {
+class GrpcBufferWriter : public ::grpc::protobuf::io::ZeroCopyOutputStream {
  public:
   explicit GrpcBufferWriter(grpc_byte_buffer** bp, int block_size)
       : block_size_(block_size), byte_count_(0), have_backup_(false) {
@@ -103,6 +102,8 @@
 
   grpc::protobuf::int64 ByteCount() const override { return byte_count_; }
 
+  grpc_slice_buffer* SliceBuffer() { return slice_buffer_; }
+
  private:
   friend class GrpcBufferWriterPeer;
   const int block_size_;
@@ -113,8 +114,7 @@
   grpc_slice slice_;
 };
 
-class GrpcBufferReader final
-    : public ::grpc::protobuf::io::ZeroCopyInputStream {
+class GrpcBufferReader : public ::grpc::protobuf::io::ZeroCopyInputStream {
  public:
   explicit GrpcBufferReader(grpc_byte_buffer* buffer)
       : byte_count_(0), backup_count_(0), status_() {
@@ -175,64 +175,91 @@
     return byte_count_ - backup_count_;
   }
 
- private:
+ protected:
   int64_t byte_count_;
   int64_t backup_count_;
   grpc_byte_buffer_reader reader_;
   grpc_slice slice_;
   Status status_;
 };
+
+template <class BufferWriter, class T>
+Status GenericSerialize(const grpc::protobuf::Message& msg,
+                        grpc_byte_buffer** bp, bool* own_buffer) {
+  static_assert(
+      std::is_base_of<protobuf::io::ZeroCopyOutputStream, BufferWriter>::value,
+      "BufferWriter must be a subclass of io::ZeroCopyOutputStream");
+  *own_buffer = true;
+  int byte_size = msg.ByteSize();
+  if (byte_size <= internal::kGrpcBufferWriterMaxBufferLength) {
+    grpc_slice slice = g_core_codegen_interface->grpc_slice_malloc(byte_size);
+    GPR_CODEGEN_ASSERT(
+        GRPC_SLICE_END_PTR(slice) ==
+        msg.SerializeWithCachedSizesToArray(GRPC_SLICE_START_PTR(slice)));
+    *bp = g_core_codegen_interface->grpc_raw_byte_buffer_create(&slice, 1);
+    g_core_codegen_interface->grpc_slice_unref(slice);
+    return g_core_codegen_interface->ok();
+  } else {
+    BufferWriter writer(bp, internal::kGrpcBufferWriterMaxBufferLength);
+    return msg.SerializeToZeroCopyStream(&writer)
+               ? g_core_codegen_interface->ok()
+               : Status(StatusCode::INTERNAL, "Failed to serialize message");
+  }
+}
+
+template <class BufferReader, class T>
+Status GenericDeserialize(grpc_byte_buffer* buffer,
+                          grpc::protobuf::Message* msg) {
+  static_assert(
+      std::is_base_of<protobuf::io::ZeroCopyInputStream, BufferReader>::value,
+      "BufferReader must be a subclass of io::ZeroCopyInputStream");
+  if (buffer == nullptr) {
+    return Status(StatusCode::INTERNAL, "No payload");
+  }
+  Status result = g_core_codegen_interface->ok();
+  {
+    BufferReader reader(buffer);
+    if (!reader.status().ok()) {
+      return reader.status();
+    }
+    ::grpc::protobuf::io::CodedInputStream decoder(&reader);
+    decoder.SetTotalBytesLimit(INT_MAX, INT_MAX);
+    if (!msg->ParseFromCodedStream(&decoder)) {
+      result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
+    }
+    if (!decoder.ConsumedEntireMessage()) {
+      result = Status(StatusCode::INTERNAL, "Did not read entire message");
+    }
+  }
+  g_core_codegen_interface->grpc_byte_buffer_destroy(buffer);
+  return result;
+}
+
 }  // namespace internal
 
+// this is needed so the following class does not conflict with protobuf
+// serializers that utilize internal-only tools.
+#ifdef GRPC_OPEN_SOURCE_PROTO
+// This class provides a protobuf serializer. It translates between protobuf
+// objects and grpc_byte_buffers. More information about SerializationTraits can
+// be found in include/grpc++/impl/codegen/serialization_traits.h.
 template <class T>
 class SerializationTraits<T, typename std::enable_if<std::is_base_of<
                                  grpc::protobuf::Message, T>::value>::type> {
  public:
   static Status Serialize(const grpc::protobuf::Message& msg,
                           grpc_byte_buffer** bp, bool* own_buffer) {
-    *own_buffer = true;
-    int byte_size = msg.ByteSize();
-    if (byte_size <= internal::kGrpcBufferWriterMaxBufferLength) {
-      grpc_slice slice = g_core_codegen_interface->grpc_slice_malloc(byte_size);
-      GPR_CODEGEN_ASSERT(
-          GRPC_SLICE_END_PTR(slice) ==
-          msg.SerializeWithCachedSizesToArray(GRPC_SLICE_START_PTR(slice)));
-      *bp = g_core_codegen_interface->grpc_raw_byte_buffer_create(&slice, 1);
-      g_core_codegen_interface->grpc_slice_unref(slice);
-      return g_core_codegen_interface->ok();
-    } else {
-      internal::GrpcBufferWriter writer(
-          bp, internal::kGrpcBufferWriterMaxBufferLength);
-      return msg.SerializeToZeroCopyStream(&writer)
-                 ? g_core_codegen_interface->ok()
-                 : Status(StatusCode::INTERNAL, "Failed to serialize message");
-    }
+    return internal::GenericSerialize<internal::GrpcBufferWriter, T>(
+        msg, bp, own_buffer);
   }
 
   static Status Deserialize(grpc_byte_buffer* buffer,
                             grpc::protobuf::Message* msg) {
-    if (buffer == nullptr) {
-      return Status(StatusCode::INTERNAL, "No payload");
-    }
-    Status result = g_core_codegen_interface->ok();
-    {
-      internal::GrpcBufferReader reader(buffer);
-      if (!reader.status().ok()) {
-        return reader.status();
-      }
-      ::grpc::protobuf::io::CodedInputStream decoder(&reader);
-      decoder.SetTotalBytesLimit(INT_MAX, INT_MAX);
-      if (!msg->ParseFromCodedStream(&decoder)) {
-        result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
-      }
-      if (!decoder.ConsumedEntireMessage()) {
-        result = Status(StatusCode::INTERNAL, "Did not read entire message");
-      }
-    }
-    g_core_codegen_interface->grpc_byte_buffer_destroy(buffer);
-    return result;
+    return internal::GenericDeserialize<internal::GrpcBufferReader, T>(buffer,
+                                                                       msg);
   }
 };
+#endif
 
 }  // namespace grpc
 
diff --git a/include/grpc++/impl/codegen/thrift_serializer.h b/include/grpc++/impl/codegen/thrift_serializer.h
deleted file mode 100644
index 86bc710..0000000
--- a/include/grpc++/impl/codegen/thrift_serializer.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- *
- * Copyright 2016, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPCXX_IMPL_CODEGEN_THRIFT_SERIALIZER_H
-#define GRPCXX_IMPL_CODEGEN_THRIFT_SERIALIZER_H
-
-#include <grpc/impl/codegen/byte_buffer_reader.h>
-#include <grpc/impl/codegen/slice.h>
-#include <thrift/protocol/TBinaryProtocol.h>
-#include <thrift/protocol/TCompactProtocol.h>
-#include <thrift/protocol/TProtocolException.h>
-#include <thrift/transport/TBufferTransports.h>
-#include <thrift/transport/TTransportUtils.h>
-#include <boost/make_shared.hpp>
-#include <memory>
-#include <stdexcept>
-#include <string>
-
-namespace apache {
-namespace thrift {
-namespace util {
-
-using apache::thrift::protocol::TBinaryProtocolT;
-using apache::thrift::protocol::TCompactProtocolT;
-using apache::thrift::protocol::TMessageType;
-using apache::thrift::protocol::TNetworkBigEndian;
-using apache::thrift::transport::TMemoryBuffer;
-using apache::thrift::transport::TBufferBase;
-using apache::thrift::transport::TTransport;
-
-template <typename Dummy, typename Protocol>
-class ThriftSerializer {
- public:
-  ThriftSerializer()
-      : prepared_(false),
-        last_deserialized_(false),
-        serialize_version_(false) {}
-
-  virtual ~ThriftSerializer() {}
-
-  // Serialize the passed type into the internal buffer
-  // and returns a pointer to internal buffer and its size
-  template <typename T>
-  void Serialize(const T& fields, const uint8_t** serialized_buffer,
-                 size_t* serialized_len) {
-    // prepare or reset buffer
-    if (!prepared_ || last_deserialized_) {
-      prepare();
-    } else {
-      buffer_->resetBuffer();
-    }
-    last_deserialized_ = false;
-
-    // if required serialize protocol version
-    if (serialize_version_) {
-      protocol_->writeMessageBegin("", TMessageType(0), 0);
-    }
-
-    // serialize fields into buffer
-    fields.write(protocol_.get());
-
-    // write the end of message
-    if (serialize_version_) {
-      protocol_->writeMessageEnd();
-    }
-
-    uint8_t* byte_buffer;
-    uint32_t byte_buffer_size;
-    buffer_->getBuffer(&byte_buffer, &byte_buffer_size);
-    *serialized_buffer = byte_buffer;
-    *serialized_len = byte_buffer_size;
-  }
-
-  // Serialize the passed type into the byte buffer
-  template <typename T>
-  void Serialize(const T& fields, grpc_byte_buffer** bp) {
-    const uint8_t* byte_buffer;
-    size_t byte_buffer_size;
-
-    Serialize(fields, &byte_buffer, &byte_buffer_size);
-
-    grpc_slice slice = grpc_slice_from_copied_buffer(
-        reinterpret_cast<const char*>(byte_buffer), byte_buffer_size);
-
-    *bp = grpc_raw_byte_buffer_create(&slice, 1);
-
-    grpc_slice_unref(slice);
-  }
-
-  // Deserialize the passed char array into  the passed type, returns the number
-  // of bytes that have been consumed from the passed string.
-  template <typename T>
-  uint32_t Deserialize(uint8_t* serialized_buffer, size_t length, T* fields) {
-    // prepare buffer if necessary
-    if (!prepared_) {
-      prepare();
-    }
-    last_deserialized_ = true;
-
-    // reset buffer transport
-    buffer_->resetBuffer(serialized_buffer, length);
-
-    // read the protocol version if necessary
-    if (serialize_version_) {
-      std::string name = "";
-      TMessageType mt = static_cast<TMessageType>(0);
-      int32_t seq_id = 0;
-      protocol_->readMessageBegin(name, mt, seq_id);
-    }
-
-    // deserialize buffer into fields
-    uint32_t len = fields->read(protocol_.get());
-
-    // read the end of message
-    if (serialize_version_) {
-      protocol_->readMessageEnd();
-    }
-
-    return len;
-  }
-
-  // Deserialize the passed byte buffer to passed type, returns the number
-  // of bytes consumed from byte buffer
-  template <typename T>
-  uint32_t Deserialize(grpc_byte_buffer* buffer, T* msg) {
-    grpc_byte_buffer_reader reader;
-    grpc_byte_buffer_reader_init(&reader, buffer);
-
-    grpc_slice slice = grpc_byte_buffer_reader_readall(&reader);
-
-    uint32_t len =
-        Deserialize(GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice), msg);
-
-    grpc_slice_unref(slice);
-
-    grpc_byte_buffer_reader_destroy(&reader);
-
-    return len;
-  }
-
-  // set serialization version flag
-  void SetSerializeVersion(bool value) { serialize_version_ = value; }
-
-  // Set the container size limit to deserialize
-  // This function should be called after buffer_ is initialized
-  void SetContainerSizeLimit(int32_t container_limit) {
-    if (!prepared_) {
-      prepare();
-    }
-    protocol_->setContainerSizeLimit(container_limit);
-  }
-
-  // Set the string size limit to deserialize
-  // This function should be called after buffer_ is initialized
-  void SetStringSizeLimit(int32_t string_limit) {
-    if (!prepared_) {
-      prepare();
-    }
-    protocol_->setStringSizeLimit(string_limit);
-  }
-
- private:
-  bool prepared_;
-  bool last_deserialized_;
-  boost::shared_ptr<TMemoryBuffer> buffer_;
-  std::shared_ptr<Protocol> protocol_;
-  bool serialize_version_;
-
-  void prepare() {
-    buffer_ = boost::make_shared<TMemoryBuffer>();
-    // create a protocol for the memory buffer transport
-    protocol_ = std::make_shared<Protocol>(buffer_);
-    prepared_ = true;
-  }
-
-};  // ThriftSerializer
-
-typedef ThriftSerializer<void, TBinaryProtocolT<TBufferBase, TNetworkBigEndian>>
-    ThriftSerializerBinary;
-typedef ThriftSerializer<void, TCompactProtocolT<TBufferBase>>
-    ThriftSerializerCompact;
-
-}  // namespace util
-}  // namespace thrift
-}  // namespace apache
-
-#endif  // GRPCXX_IMPL_CODEGEN_THRIFT_SERIALIZER_H
diff --git a/include/grpc++/impl/codegen/thrift_utils.h b/include/grpc++/impl/codegen/thrift_utils.h
deleted file mode 100644
index 742d739..0000000
--- a/include/grpc++/impl/codegen/thrift_utils.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *
- * Copyright 2016, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPCXX_IMPL_CODEGEN_THRIFT_UTILS_H
-#define GRPCXX_IMPL_CODEGEN_THRIFT_UTILS_H
-
-#include <grpc++/impl/codegen/config.h>
-#include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc++/impl/codegen/serialization_traits.h>
-#include <grpc++/impl/codegen/status.h>
-#include <grpc++/impl/codegen/status_code_enum.h>
-#include <grpc++/impl/codegen/thrift_serializer.h>
-#include <grpc/impl/codegen/byte_buffer_reader.h>
-#include <grpc/impl/codegen/slice.h>
-#include <cstdint>
-#include <cstdlib>
-
-namespace grpc {
-
-using apache::thrift::util::ThriftSerializerCompact;
-
-template <class T>
-class SerializationTraits<T, typename std::enable_if<std::is_base_of<
-                                 apache::thrift::TBase, T>::value>::type> {
- public:
-  static Status Serialize(const T& msg, grpc_byte_buffer** bp,
-                          bool* own_buffer) {
-    *own_buffer = true;
-
-    ThriftSerializerCompact serializer;
-    serializer.Serialize(msg, bp);
-
-    return Status(StatusCode::OK, "ok");
-  }
-
-  static Status Deserialize(grpc_byte_buffer* buffer, T* msg,
-                            int max_receive_message_size) {
-    if (!buffer) {
-      return Status(StatusCode::INTERNAL, "No payload");
-    }
-
-    ThriftSerializerCompact deserializer;
-    deserializer.Deserialize(buffer, msg);
-
-    grpc_byte_buffer_destroy(buffer);
-
-    return Status(StatusCode::OK, "ok");
-  }
-};
-
-}  // namespace grpc
-
-#endif  // GRPCXX_IMPL_CODEGEN_THRIFT_UTILS_H
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index b2c38b2..4738e02 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -293,6 +293,9 @@
   "grpc.experimental.tcp_min_read_chunk_size"
 #define GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE \
   "grpc.experimental.tcp_max_read_chunk_size"
+/* Timeout in milliseconds to use for calls to the grpclb load balancer.
+   If 0 or unset, the balancer calls will have no deadline. */
+#define GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS "grpc.grpclb_timeout_ms"
 /** \} */
 
 /** Result of a grpc call. If the caller satisfies the prerequisites of a
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index e12f6f4..14e348f 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -189,7 +189,7 @@
 #ifdef __GLIBC__
 #define GPR_POSIX_CRASH_HANDLER 1
 #else /* musl libc */
-#define GRPC_MSG_IOVLEN_TYPE int
+#define GPR_MUSL_LIBC_COMPAT 1
 #endif
 #elif defined(__APPLE__)
 #include <Availability.h>
diff --git a/package.json b/package.json
index 6a01ae2..e1499a0 100644
--- a/package.json
+++ b/package.json
@@ -34,8 +34,7 @@
     "lodash": "^4.15.0",
     "nan": "^2.0.0",
     "node-pre-gyp": "^0.6.0",
-    "protobufjs": "^6.7.0",
-    "cares": "^1.1.5"
+    "protobufjs": "^6.7.0"
   },
   "devDependencies": {
     "async": "^2.0.1",
diff --git a/package.xml b/package.xml
index e7d67ec..d47708e 100644
--- a/package.xml
+++ b/package.xml
@@ -359,8 +359,10 @@
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/uri_parser.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb.h" role="src" />
@@ -593,8 +595,10 @@
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb_common.c" role="src" />
diff --git a/setup.py b/setup.py
index 4cbd1a4..cb85273 100644
--- a/setup.py
+++ b/setup.py
@@ -116,7 +116,7 @@
   elif 'win32' in sys.platform:
     EXTRA_ENV_COMPILE_ARGS += ' -D_PYTHON_MSVC'
   elif "linux" in sys.platform:
-    EXTRA_ENV_COMPILE_ARGS += ' -std=c++11 -fvisibility=hidden -fno-wrapv'
+    EXTRA_ENV_COMPILE_ARGS += ' -std=c++11 -std=gnu99 -fvisibility=hidden -fno-wrapv'
   elif "darwin" in sys.platform:
     EXTRA_ENV_COMPILE_ARGS += ' -fvisibility=hidden -fno-wrapv'
 
@@ -144,6 +144,8 @@
 CYTHON_HELPER_C_FILES = ()
 
 CORE_C_FILES = tuple(grpc_core_dependencies.CORE_SOURCE_FILES)
+if "win32" in sys.platform and "64bit" in platform.architecture()[0]:
+  CORE_C_FILES = filter(lambda x: 'third_party/cares' not in x, CORE_C_FILES)
 
 EXTENSION_INCLUDE_DIRECTORIES = (
     (PYTHON_STEM,) + CORE_INCLUDE + BORINGSSL_INCLUDE + ZLIB_INCLUDE +
@@ -163,7 +165,9 @@
 if "win32" in sys.platform:
   DEFINE_MACROS += (('WIN32_LEAN_AND_MEAN', 1), ('CARES_STATICLIB', 1),)
   if '64bit' in platform.architecture()[0]:
-    DEFINE_MACROS += (('MS_WIN64', 1),)
+    # TODO(zyc): Re-enble c-ares on x64 windows after fixing the
+    # ares_library_init compilation issue
+    DEFINE_MACROS += (('MS_WIN64', 1), ('GRPC_ARES', 0),)
   elif sys.version_info >= (3, 5):
     # For some reason, this is needed to get access to inet_pton/inet_ntop
     # on msvc, but only for 32 bits
@@ -233,7 +237,7 @@
     'six>=1.5.2',
     # TODO(atash): eventually split the grpcio package into a metapackage
     # depending on protobuf and the runtime component (independent of protobuf)
-    'protobuf>=3.2.0',
+    'protobuf>=3.3.0',
 )
 
 if not PY3:
diff --git a/src/boringssl/gen_build_yaml.py b/src/boringssl/gen_build_yaml.py
index c53beb0..d01c2b4 100755
--- a/src/boringssl/gen_build_yaml.py
+++ b/src/boringssl/gen_build_yaml.py
@@ -138,7 +138,7 @@
           {
             'name': 'boringssl_%s' % os.path.basename(test[0]),
             'args': [map_testarg(arg) for arg in test[1:]],
-            'exclude_configs': ['asan'],
+            'exclude_configs': ['asan', 'ubsan'],
             'ci_platforms': ['linux', 'mac', 'posix', 'windows'],
             'platforms': ['linux', 'mac', 'posix', 'windows'],
             'flaky': False,
diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc
index d3e7162..a1a0258 100644
--- a/src/compiler/cpp_generator.cc
+++ b/src/compiler/cpp_generator.cc
@@ -804,6 +804,12 @@
                  " public:\n");
   printer->Indent();
 
+  // Service metadata
+  printer->Print(*vars,
+                 "static constexpr char const* service_full_name() {\n"
+                 "  return \"$Package$$Service$\";\n"
+                 "}\n");
+
   // Client side
   printer->Print(
       "class StubInterface {\n"
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
index 50ee54a..278e507 100644
--- a/src/compiler/python_generator.cc
+++ b/src/compiler/python_generator.cc
@@ -622,9 +622,17 @@
 
     for (StringPairSet::iterator it = imports_set.begin();
          it != imports_set.end(); ++it) {
-      var["ModuleName"] = std::get<0>(*it);
+      auto module_name = std::get<0>(*it);
       var["ModuleAlias"] = std::get<1>(*it);
-      out->Print(var, "import $ModuleName$ as $ModuleAlias$\n");
+      const size_t last_dot_pos = module_name.rfind('.');
+      if (last_dot_pos == grpc::string::npos) {
+        var["ImportStatement"] = "import " + module_name;
+      } else {
+        var["ImportStatement"] = "from " + module_name.substr(0, last_dot_pos) +
+                                 " import " +
+                                 module_name.substr(last_dot_pos + 1);
+      }
+      out->Print(var, "$ImportStatement$ as $ModuleAlias$\n");
     }
   }
   return true;
diff --git a/src/core/ext/filters/client_channel/client_channel.c b/src/core/ext/filters/client_channel/client_channel.c
index 0463b25..24843d5 100644
--- a/src/core/ext/filters/client_channel/client_channel.c
+++ b/src/core/ext/filters/client_channel/client_channel.c
@@ -760,12 +760,6 @@
 
 #define CANCELLED_CALL ((grpc_subchannel_call *)1)
 
-typedef enum {
-  /* zero so that it can be default-initialized */
-  GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING = 0,
-  GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL
-} subchannel_creation_phase;
-
 /** Call data.  Holds a pointer to grpc_subchannel_call and the
     associated machinery to create such a pointer.
     Handles queueing of stream ops until a call object is ready, waiting
@@ -793,8 +787,9 @@
   gpr_atm subchannel_call;
   gpr_arena *arena;
 
-  subchannel_creation_phase creation_phase;
+  bool pick_pending;
   grpc_connected_subchannel *connected_subchannel;
+  grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT];
   grpc_polling_entity *pollent;
 
   grpc_transport_stream_op_batch **waiting_ops;
@@ -914,11 +909,10 @@
   grpc_call_element *elem = arg;
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
-  GPR_ASSERT(calld->creation_phase ==
-             GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL);
+  GPR_ASSERT(calld->pick_pending);
+  calld->pick_pending = false;
   grpc_polling_entity_del_from_pollset_set(exec_ctx, calld->pollent,
                                            chand->interested_parties);
-  calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
   if (calld->connected_subchannel == NULL) {
     gpr_atm_no_barrier_store(&calld->subchannel_call, 1);
     fail_locked(exec_ctx, calld,
@@ -944,7 +938,8 @@
         .path = calld->path,
         .start_time = calld->call_start_time,
         .deadline = calld->deadline,
-        .arena = calld->arena};
+        .arena = calld->arena,
+        .context = calld->subchannel_call_context};
     grpc_error *new_error = grpc_connected_subchannel_create_call(
         exec_ctx, calld->connected_subchannel, &call_args, &subchannel_call);
     gpr_atm_rel_store(&calld->subchannel_call,
@@ -973,6 +968,7 @@
   grpc_metadata_batch *initial_metadata;
   uint32_t initial_metadata_flags;
   grpc_connected_subchannel **connected_subchannel;
+  grpc_call_context_element *subchannel_call_context;
   grpc_closure *on_ready;
   grpc_call_element *elem;
   grpc_closure closure;
@@ -984,8 +980,8 @@
 static bool pick_subchannel_locked(
     grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     grpc_metadata_batch *initial_metadata, uint32_t initial_metadata_flags,
-    grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready,
-    grpc_error *error);
+    grpc_connected_subchannel **connected_subchannel,
+    grpc_call_context_element *subchannel_call_context, grpc_closure *on_ready);
 
 static void continue_picking_locked(grpc_exec_ctx *exec_ctx, void *arg,
                                     grpc_error *error) {
@@ -997,49 +993,49 @@
   } else {
     if (pick_subchannel_locked(exec_ctx, cpa->elem, cpa->initial_metadata,
                                cpa->initial_metadata_flags,
-                               cpa->connected_subchannel, cpa->on_ready,
-                               GRPC_ERROR_NONE)) {
+                               cpa->connected_subchannel,
+                               cpa->subchannel_call_context, cpa->on_ready)) {
       grpc_closure_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_NONE);
     }
   }
   gpr_free(cpa);
 }
 
+static void cancel_pick_locked(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+                               grpc_error *error) {
+  channel_data *chand = elem->channel_data;
+  call_data *calld = elem->call_data;
+  if (chand->lb_policy != NULL) {
+    grpc_lb_policy_cancel_pick_locked(exec_ctx, chand->lb_policy,
+                                      &calld->connected_subchannel,
+                                      GRPC_ERROR_REF(error));
+  }
+  for (grpc_closure *closure = chand->waiting_for_config_closures.head;
+       closure != NULL; closure = closure->next_data.next) {
+    continue_picking_args *cpa = closure->cb_arg;
+    if (cpa->connected_subchannel == &calld->connected_subchannel) {
+      cpa->connected_subchannel = NULL;
+      grpc_closure_sched(exec_ctx, cpa->on_ready,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick cancelled", &error, 1));
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
 static bool pick_subchannel_locked(
     grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     grpc_metadata_batch *initial_metadata, uint32_t initial_metadata_flags,
-    grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready,
-    grpc_error *error) {
+    grpc_connected_subchannel **connected_subchannel,
+    grpc_call_context_element *subchannel_call_context,
+    grpc_closure *on_ready) {
   GPR_TIMER_BEGIN("pick_subchannel", 0);
 
   channel_data *chand = elem->channel_data;
   call_data *calld = elem->call_data;
-  continue_picking_args *cpa;
-  grpc_closure *closure;
 
   GPR_ASSERT(connected_subchannel);
 
-  if (initial_metadata == NULL) {
-    if (chand->lb_policy != NULL) {
-      grpc_lb_policy_cancel_pick_locked(exec_ctx, chand->lb_policy,
-                                        connected_subchannel,
-                                        GRPC_ERROR_REF(error));
-    }
-    for (closure = chand->waiting_for_config_closures.head; closure != NULL;
-         closure = closure->next_data.next) {
-      cpa = closure->cb_arg;
-      if (cpa->connected_subchannel == connected_subchannel) {
-        cpa->connected_subchannel = NULL;
-        grpc_closure_sched(exec_ctx, cpa->on_ready,
-                           GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
-                               "Pick cancelled", &error, 1));
-      }
-    }
-    GPR_TIMER_END("pick_subchannel", 0);
-    GRPC_ERROR_UNREF(error);
-    return true;
-  }
-  GPR_ASSERT(error == GRPC_ERROR_NONE);
   if (chand->lb_policy != NULL) {
     apply_final_configuration_locked(exec_ctx, elem);
     grpc_lb_policy *lb_policy = chand->lb_policy;
@@ -1062,8 +1058,7 @@
       }
     }
     const grpc_lb_policy_pick_args inputs = {
-        initial_metadata, initial_metadata_flags, &calld->lb_token_mdelem,
-        gpr_inf_future(GPR_CLOCK_MONOTONIC)};
+        initial_metadata, initial_metadata_flags, &calld->lb_token_mdelem};
 
     // Wrap the user-provided callback in order to hold a strong reference to
     // the LB policy for the duration of the pick.
@@ -1076,8 +1071,8 @@
     GRPC_LB_POLICY_REF(lb_policy, "pick_subchannel_wrapping");
     w_on_pick_arg->lb_policy = lb_policy;
     const bool pick_done = grpc_lb_policy_pick_locked(
-        exec_ctx, lb_policy, &inputs, connected_subchannel, NULL,
-        &w_on_pick_arg->wrapper_closure);
+        exec_ctx, lb_policy, &inputs, connected_subchannel,
+        subchannel_call_context, NULL, &w_on_pick_arg->wrapper_closure);
     if (pick_done) {
       /* synchronous grpc_lb_policy_pick call. Unref the LB policy. */
       GRPC_LB_POLICY_UNREF(exec_ctx, w_on_pick_arg->lb_policy,
@@ -1096,10 +1091,11 @@
                               &chand->on_resolver_result_changed);
   }
   if (chand->resolver != NULL) {
-    cpa = gpr_malloc(sizeof(*cpa));
+    continue_picking_args *cpa = gpr_malloc(sizeof(*cpa));
     cpa->initial_metadata = initial_metadata;
     cpa->initial_metadata_flags = initial_metadata_flags;
     cpa->connected_subchannel = connected_subchannel;
+    cpa->subchannel_call_context = subchannel_call_context;
     cpa->on_ready = on_ready;
     cpa->elem = elem;
     grpc_closure_init(&cpa->closure, continue_picking_locked, cpa,
@@ -1151,16 +1147,13 @@
          error to the caller when the first op does get passed down. */
       calld->cancel_error =
           GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error);
-      switch (calld->creation_phase) {
-        case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
-          fail_locked(exec_ctx, calld,
-                      GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error));
-          break;
-        case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
-          pick_subchannel_locked(
-              exec_ctx, elem, NULL, 0, &calld->connected_subchannel, NULL,
-              GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error));
-          break;
+      if (calld->pick_pending) {
+        cancel_pick_locked(
+            exec_ctx, elem,
+            GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error));
+      } else {
+        fail_locked(exec_ctx, calld,
+                    GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error));
       }
       grpc_transport_stream_op_batch_finish_with_failure(
           exec_ctx, op,
@@ -1170,9 +1163,9 @@
     }
   }
   /* if we don't have a subchannel, try to get one */
-  if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
-      calld->connected_subchannel == NULL && op->send_initial_metadata) {
-    calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL;
+  if (!calld->pick_pending && calld->connected_subchannel == NULL &&
+      op->send_initial_metadata) {
+    calld->pick_pending = true;
     grpc_closure_init(&calld->next_step, subchannel_ready_locked, elem,
                       grpc_combiner_scheduler(chand->combiner, true));
     GRPC_CALL_STACK_REF(calld->owning_call, "pick_subchannel");
@@ -1183,8 +1176,9 @@
             exec_ctx, elem,
             op->payload->send_initial_metadata.send_initial_metadata,
             op->payload->send_initial_metadata.send_initial_metadata_flags,
-            &calld->connected_subchannel, &calld->next_step, GRPC_ERROR_NONE)) {
-      calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+            &calld->connected_subchannel, calld->subchannel_call_context,
+            &calld->next_step)) {
+      calld->pick_pending = false;
       GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
     } else {
       grpc_polling_entity_add_to_pollset_set(exec_ctx, calld->pollent,
@@ -1192,15 +1186,15 @@
     }
   }
   /* if we've got a subchannel, then let's ask it to create a call */
-  if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
-      calld->connected_subchannel != NULL) {
+  if (!calld->pick_pending && calld->connected_subchannel != NULL) {
     grpc_subchannel_call *subchannel_call = NULL;
     const grpc_connected_subchannel_call_args call_args = {
         .pollent = calld->pollent,
         .path = calld->path,
         .start_time = calld->call_start_time,
         .deadline = calld->deadline,
-        .arena = calld->arena};
+        .arena = calld->arena,
+        .context = calld->subchannel_call_context};
     grpc_error *error = grpc_connected_subchannel_create_call(
         exec_ctx, calld->connected_subchannel, &call_args, &subchannel_call);
     gpr_atm_rel_store(&calld->subchannel_call,
@@ -1349,12 +1343,18 @@
     then_schedule_closure = NULL;
     GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "client_channel_destroy_call");
   }
-  GPR_ASSERT(calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING);
+  GPR_ASSERT(!calld->pick_pending);
   GPR_ASSERT(calld->waiting_ops_count == 0);
   if (calld->connected_subchannel != NULL) {
     GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, calld->connected_subchannel,
                                     "picked");
   }
+  for (size_t i = 0; i < GRPC_CONTEXT_COUNT; ++i) {
+    if (calld->subchannel_call_context[i].value != NULL) {
+      calld->subchannel_call_context[i].destroy(
+          calld->subchannel_call_context[i].value);
+    }
+  }
   gpr_free(calld->waiting_ops);
   grpc_closure_sched(exec_ctx, then_schedule_closure, GRPC_ERROR_NONE);
 }
@@ -1450,12 +1450,12 @@
 
 void grpc_client_channel_watch_connectivity_state(
     grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset,
-    grpc_connectivity_state *state, grpc_closure *on_complete) {
+    grpc_connectivity_state *state, grpc_closure *closure) {
   channel_data *chand = elem->channel_data;
   external_connectivity_watcher *w = gpr_malloc(sizeof(*w));
   w->chand = chand;
   w->pollset = pollset;
-  w->on_complete = on_complete;
+  w->on_complete = closure;
   w->state = state;
   grpc_pollset_set_add_pollset(exec_ctx, chand->interested_parties, pollset);
   GRPC_CHANNEL_STACK_REF(w->chand->owning_stack,
diff --git a/src/core/ext/filters/client_channel/lb_policy.c b/src/core/ext/filters/client_channel/lb_policy.c
index 2d31499..112ba40 100644
--- a/src/core/ext/filters/client_channel/lb_policy.c
+++ b/src/core/ext/filters/client_channel/lb_policy.c
@@ -119,9 +119,10 @@
 int grpc_lb_policy_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
                                const grpc_lb_policy_pick_args *pick_args,
                                grpc_connected_subchannel **target,
+                               grpc_call_context_element *context,
                                void **user_data, grpc_closure *on_complete) {
   return policy->vtable->pick_locked(exec_ctx, policy, pick_args, target,
-                                     user_data, on_complete);
+                                     context, user_data, on_complete);
 }
 
 void grpc_lb_policy_cancel_pick_locked(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h
index 2542766..184b2ef 100644
--- a/src/core/ext/filters/client_channel/lb_policy.h
+++ b/src/core/ext/filters/client_channel/lb_policy.h
@@ -43,9 +43,6 @@
 typedef struct grpc_lb_policy grpc_lb_policy;
 typedef struct grpc_lb_policy_vtable grpc_lb_policy_vtable;
 
-typedef void (*grpc_lb_completion)(void *cb_arg, grpc_subchannel *subchannel,
-                                   grpc_status_code status, const char *errmsg);
-
 struct grpc_lb_policy {
   const grpc_lb_policy_vtable *vtable;
   gpr_atm ref_pair;
@@ -65,8 +62,6 @@
   uint32_t initial_metadata_flags;
   /** Storage for LB token in \a initial_metadata, or NULL if not used */
   grpc_linked_mdelem *lb_token_mdelem_storage;
-  /** Deadline for the call to the LB server */
-  gpr_timespec deadline;
 } grpc_lb_policy_pick_args;
 
 struct grpc_lb_policy_vtable {
@@ -76,7 +71,8 @@
   /** \see grpc_lb_policy_pick */
   int (*pick_locked)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
                      const grpc_lb_policy_pick_args *pick_args,
-                     grpc_connected_subchannel **target, void **user_data,
+                     grpc_connected_subchannel **target,
+                     grpc_call_context_element *context, void **user_data,
                      grpc_closure *on_complete);
 
   /** \see grpc_lb_policy_cancel_pick */
@@ -156,6 +152,8 @@
     \a target will be set to the selected subchannel, or NULL on failure.
     Upon success, \a user_data will be set to whatever opaque information
     may need to be propagated from the LB policy, or NULL if not needed.
+    \a context will be populated with context to pass to the subchannel
+    call, if needed.
 
     If the pick succeeds and a result is known immediately, a non-zero
     value will be returned.  Otherwise, \a on_complete will be invoked
@@ -167,6 +165,7 @@
 int grpc_lb_policy_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
                                const grpc_lb_policy_pick_args *pick_args,
                                grpc_connected_subchannel **target,
+                               grpc_call_context_element *context,
                                void **user_data, grpc_closure *on_complete);
 
 /** Perform a connected subchannel ping (see \a grpc_connected_subchannel_ping)
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c
new file mode 100644
index 0000000..67baa46
--- /dev/null
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c
@@ -0,0 +1,153 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h"
+
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/profiling/timers.h"
+
+static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_channel_element *elem,
+                                     grpc_channel_element_args *args) {
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
+                                 grpc_channel_element *elem) {}
+
+typedef struct {
+  // Stats object to update.
+  grpc_grpclb_client_stats *client_stats;
+  // State for intercepting send_initial_metadata.
+  grpc_closure on_complete_for_send;
+  grpc_closure *original_on_complete_for_send;
+  bool send_initial_metadata_succeeded;
+  // State for intercepting recv_initial_metadata.
+  grpc_closure recv_initial_metadata_ready;
+  grpc_closure *original_recv_initial_metadata_ready;
+  bool recv_initial_metadata_succeeded;
+} call_data;
+
+static void on_complete_for_send(grpc_exec_ctx *exec_ctx, void *arg,
+                                 grpc_error *error) {
+  call_data *calld = arg;
+  if (error == GRPC_ERROR_NONE) {
+    calld->send_initial_metadata_succeeded = true;
+  }
+  grpc_closure_run(exec_ctx, calld->original_on_complete_for_send,
+                   GRPC_ERROR_REF(error));
+}
+
+static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, void *arg,
+                                        grpc_error *error) {
+  call_data *calld = arg;
+  if (error == GRPC_ERROR_NONE) {
+    calld->recv_initial_metadata_succeeded = true;
+  }
+  grpc_closure_run(exec_ctx, calld->original_recv_initial_metadata_ready,
+                   GRPC_ERROR_REF(error));
+}
+
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  const grpc_call_element_args *args) {
+  call_data *calld = elem->call_data;
+  // Get stats object from context and take a ref.
+  GPR_ASSERT(args->context != NULL);
+  GPR_ASSERT(args->context[GRPC_GRPCLB_CLIENT_STATS].value != NULL);
+  calld->client_stats = grpc_grpclb_client_stats_ref(
+      args->context[GRPC_GRPCLB_CLIENT_STATS].value);
+  // Record call started.
+  grpc_grpclb_client_stats_add_call_started(calld->client_stats);
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+                              const grpc_call_final_info *final_info,
+                              grpc_closure *ignored) {
+  call_data *calld = elem->call_data;
+  // Record call finished, optionally setting client_failed_to_send and
+  // received.
+  grpc_grpclb_client_stats_add_call_finished(
+      false /* drop_for_rate_limiting */, false /* drop_for_load_balancing */,
+      !calld->send_initial_metadata_succeeded /* client_failed_to_send */,
+      calld->recv_initial_metadata_succeeded /* known_received */,
+      calld->client_stats);
+  // All done, so unref the stats object.
+  grpc_grpclb_client_stats_unref(calld->client_stats);
+}
+
+static void start_transport_stream_op_batch(
+    grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+    grpc_transport_stream_op_batch *batch) {
+  call_data *calld = elem->call_data;
+  GPR_TIMER_BEGIN("clr_start_transport_stream_op_batch", 0);
+  // Intercept send_initial_metadata.
+  if (batch->send_initial_metadata) {
+    calld->original_on_complete_for_send = batch->on_complete;
+    grpc_closure_init(&calld->on_complete_for_send, on_complete_for_send, calld,
+                      grpc_schedule_on_exec_ctx);
+    batch->on_complete = &calld->on_complete_for_send;
+  }
+  // Intercept recv_initial_metadata.
+  if (batch->recv_initial_metadata) {
+    calld->original_recv_initial_metadata_ready =
+        batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
+    grpc_closure_init(&calld->recv_initial_metadata_ready,
+                      recv_initial_metadata_ready, calld,
+                      grpc_schedule_on_exec_ctx);
+    batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
+        &calld->recv_initial_metadata_ready;
+  }
+  // Chain to next filter.
+  grpc_call_next_op(exec_ctx, elem, batch);
+  GPR_TIMER_END("clr_start_transport_stream_op_batch", 0);
+}
+
+const grpc_channel_filter grpc_client_load_reporting_filter = {
+    start_transport_stream_op_batch,
+    grpc_channel_next_op,
+    sizeof(call_data),
+    init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    destroy_call_elem,
+    0,  // sizeof(channel_data)
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_call_next_get_peer,
+    grpc_channel_next_get_info,
+    "client_load_reporting"};
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
new file mode 100644
index 0000000..28b313d
--- /dev/null
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_client_load_reporting_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H \
+          */
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
index ad5f068..695be4f 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
@@ -95,8 +95,7 @@
    headers. Therefore, sockaddr.h must always be included first */
 #include "src/core/lib/iomgr/sockaddr.h"
 
-#include <errno.h>
-
+#include <limits.h>
 #include <string.h>
 
 #include <grpc/byte_buffer_reader.h>
@@ -108,13 +107,16 @@
 
 #include "src/core/ext/filters/client_channel/client_channel.h"
 #include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h"
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 #include "src/core/ext/filters/client_channel/parse_address.h"
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/sockaddr.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
@@ -126,6 +128,7 @@
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/channel_init.h"
 #include "src/core/lib/transport/static_metadata.h"
 
 #define GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS 20
@@ -147,6 +150,10 @@
                                       lb_token_mdelem_storage, lb_token);
 }
 
+static void destroy_client_stats(void *arg) {
+  grpc_grpclb_client_stats_unref(arg);
+}
+
 typedef struct wrapped_rr_closure_arg {
   /* the closure instance using this struct as argument */
   grpc_closure wrapper_closure;
@@ -163,6 +170,13 @@
    * initial metadata */
   grpc_connected_subchannel **target;
 
+  /* the context to be populated for the subchannel call */
+  grpc_call_context_element *context;
+
+  /* Stats for client-side load reporting. Note that this holds a
+   * reference, which must be either passed on via context or unreffed. */
+  grpc_grpclb_client_stats *client_stats;
+
   /* the LB token associated with the pick */
   grpc_mdelem lb_token;
 
@@ -202,6 +216,12 @@
                 (void *)*wc_arg->target, (void *)wc_arg->rr_policy);
         abort();
       }
+      // Pass on client stats via context. Passes ownership of the reference.
+      GPR_ASSERT(wc_arg->client_stats != NULL);
+      wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats;
+      wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats;
+    } else {
+      grpc_grpclb_client_stats_unref(wc_arg->client_stats);
     }
     if (grpc_lb_glb_trace) {
       gpr_log(GPR_INFO, "Unreffing RR %p", (void *)wc_arg->rr_policy);
@@ -237,6 +257,7 @@
 static void add_pending_pick(pending_pick **root,
                              const grpc_lb_policy_pick_args *pick_args,
                              grpc_connected_subchannel **target,
+                             grpc_call_context_element *context,
                              grpc_closure *on_complete) {
   pending_pick *pp = gpr_zalloc(sizeof(*pp));
   pp->next = *root;
@@ -244,6 +265,7 @@
   pp->target = target;
   pp->wrapped_on_complete_arg.wrapped_closure = on_complete;
   pp->wrapped_on_complete_arg.target = target;
+  pp->wrapped_on_complete_arg.context = context;
   pp->wrapped_on_complete_arg.initial_metadata = pick_args->initial_metadata;
   pp->wrapped_on_complete_arg.lb_token_mdelem_storage =
       pick_args->lb_token_mdelem_storage;
@@ -287,8 +309,8 @@
   grpc_client_channel_factory *cc_factory;
   grpc_channel_args *args;
 
-  /** deadline for the LB's call */
-  gpr_timespec deadline;
+  /** timeout in milliseconds for the LB call. 0 means no deadline. */
+  int lb_call_timeout_ms;
 
   /** for communicating with the LB server */
   grpc_channel *lb_channel;
@@ -316,6 +338,10 @@
   /************************************************************/
   /*  client data associated with the LB server communication */
   /************************************************************/
+
+  /* Finished sending initial request. */
+  grpc_closure lb_on_sent_initial_request;
+
   /* Status from the LB server has been received. This signals the end of the LB
    * call. */
   grpc_closure lb_on_server_status_received;
@@ -348,6 +374,23 @@
 
   /** LB call retry timer */
   grpc_timer lb_call_retry_timer;
+
+  bool initial_request_sent;
+  bool seen_initial_response;
+
+  /* Stats for client-side load reporting. Should be unreffed and
+   * recreated whenever lb_call is replaced. */
+  grpc_grpclb_client_stats *client_stats;
+  /* Interval and timer for next client load report. */
+  gpr_timespec client_stats_report_interval;
+  grpc_timer client_load_report_timer;
+  bool client_load_report_timer_pending;
+  bool last_client_load_report_counters_were_zero;
+  /* Closure used for either the load report timer or the callback for
+   * completion of sending the load report. */
+  grpc_closure client_load_report_closure;
+  /* Client load report message payload. */
+  grpc_byte_buffer *client_load_report_payload;
 } glb_lb_policy;
 
 /* Keeps track and reacts to changes in connectivity of the RR instance */
@@ -552,8 +595,8 @@
     grpc_connected_subchannel **target, wrapped_rr_closure_arg *wc_arg) {
   GPR_ASSERT(rr_policy != NULL);
   const bool pick_done = grpc_lb_policy_pick_locked(
-      exec_ctx, rr_policy, pick_args, target, (void **)&wc_arg->lb_token,
-      &wc_arg->wrapper_closure);
+      exec_ctx, rr_policy, pick_args, target, wc_arg->context,
+      (void **)&wc_arg->lb_token, &wc_arg->wrapper_closure);
   if (pick_done) {
     /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */
     if (grpc_lb_glb_trace) {
@@ -567,7 +610,12 @@
                                   pick_args->lb_token_mdelem_storage,
                                   GRPC_MDELEM_REF(wc_arg->lb_token));
 
-    gpr_free(wc_arg);
+    // Pass on client stats via context. Passes ownership of the reference.
+    GPR_ASSERT(wc_arg->client_stats != NULL);
+    wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats;
+    wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats;
+
+    gpr_free(wc_arg->free_when_done);
   }
   /* else, the pending pick will be registered and taken care of by the
    * pending pick list inside the RR policy (glb_policy->rr_policy).
@@ -690,6 +738,8 @@
     glb_policy->pending_picks = pp->next;
     GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_pick");
     pp->wrapped_on_complete_arg.rr_policy = glb_policy->rr_policy;
+    pp->wrapped_on_complete_arg.client_stats =
+        grpc_grpclb_client_stats_ref(glb_policy->client_stats);
     if (grpc_lb_glb_trace) {
       gpr_log(GPR_INFO, "Pending pick about to PICK from 0x%" PRIxPTR "",
               (intptr_t)glb_policy->rr_policy);
@@ -864,9 +914,22 @@
   grpc_uri_destroy(uri);
 
   glb_policy->cc_factory = args->client_channel_factory;
-  glb_policy->args = grpc_channel_args_copy(args->args);
   GPR_ASSERT(glb_policy->cc_factory != NULL);
 
+  arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS);
+  glb_policy->lb_call_timeout_ms =
+      grpc_channel_arg_get_integer(arg, (grpc_integer_options){0, 0, INT_MAX});
+
+  // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args,
+  // since we use this to trigger the client_load_reporting filter.
+  grpc_arg new_arg;
+  new_arg.key = GRPC_ARG_LB_POLICY_NAME;
+  new_arg.type = GRPC_ARG_STRING;
+  new_arg.value.string = "grpclb";
+  static const char *args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME};
+  glb_policy->args = grpc_channel_args_copy_and_add_and_remove(
+      args->args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1);
+
   grpc_slice_hash_table *targets_info = NULL;
   /* Create a client channel over them to communicate with a LB service */
   char *lb_service_target_addresses =
@@ -880,6 +943,8 @@
   grpc_channel_args_destroy(exec_ctx, lb_channel_args);
   gpr_free(lb_service_target_addresses);
   if (glb_policy->lb_channel == NULL) {
+    gpr_free((void *)glb_policy->server_name);
+    grpc_channel_args_destroy(exec_ctx, glb_policy->args);
     gpr_free(glb_policy);
     return NULL;
   }
@@ -895,6 +960,9 @@
   GPR_ASSERT(glb_policy->pending_pings == NULL);
   gpr_free((void *)glb_policy->server_name);
   grpc_channel_args_destroy(exec_ctx, glb_policy->args);
+  if (glb_policy->client_stats != NULL) {
+    grpc_grpclb_client_stats_unref(glb_policy->client_stats);
+  }
   grpc_channel_destroy(glb_policy->lb_channel);
   glb_policy->lb_channel = NULL;
   grpc_connectivity_state_destroy(exec_ctx, &glb_policy->state_tracker);
@@ -1011,7 +1079,8 @@
 
 static int glb_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
                            const grpc_lb_policy_pick_args *pick_args,
-                           grpc_connected_subchannel **target, void **user_data,
+                           grpc_connected_subchannel **target,
+                           grpc_call_context_element *context, void **user_data,
                            grpc_closure *on_complete) {
   if (pick_args->lb_token_mdelem_storage == NULL) {
     *target = NULL;
@@ -1023,7 +1092,6 @@
   }
 
   glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
-  glb_policy->deadline = pick_args->deadline;
   bool pick_done;
 
   if (glb_policy->rr_policy != NULL) {
@@ -1039,6 +1107,10 @@
                       grpc_schedule_on_exec_ctx);
     wc_arg->rr_policy = glb_policy->rr_policy;
     wc_arg->target = target;
+    wc_arg->context = context;
+    GPR_ASSERT(glb_policy->client_stats != NULL);
+    wc_arg->client_stats =
+        grpc_grpclb_client_stats_ref(glb_policy->client_stats);
     wc_arg->wrapped_closure = on_complete;
     wc_arg->lb_token_mdelem_storage = pick_args->lb_token_mdelem_storage;
     wc_arg->initial_metadata = pick_args->initial_metadata;
@@ -1052,7 +1124,7 @@
               "picks",
               (void *)(glb_policy));
     }
-    add_pending_pick(&glb_policy->pending_picks, pick_args, target,
+    add_pending_pick(&glb_policy->pending_picks, pick_args, target, context,
                      on_complete);
 
     if (!glb_policy->started_picking) {
@@ -1093,6 +1165,104 @@
       exec_ctx, &glb_policy->state_tracker, current, notify);
 }
 
+static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                           grpc_error *error);
+
+static void schedule_next_client_load_report(grpc_exec_ctx *exec_ctx,
+                                             glb_lb_policy *glb_policy) {
+  const gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
+  const gpr_timespec next_client_load_report_time =
+      gpr_time_add(now, glb_policy->client_stats_report_interval);
+  grpc_closure_init(&glb_policy->client_load_report_closure,
+                    send_client_load_report_locked, glb_policy,
+                    grpc_combiner_scheduler(glb_policy->base.combiner, false));
+  grpc_timer_init(exec_ctx, &glb_policy->client_load_report_timer,
+                  next_client_load_report_time,
+                  &glb_policy->client_load_report_closure, now);
+}
+
+static void client_load_report_done_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                           grpc_error *error) {
+  glb_lb_policy *glb_policy = arg;
+  grpc_byte_buffer_destroy(glb_policy->client_load_report_payload);
+  glb_policy->client_load_report_payload = NULL;
+  if (error != GRPC_ERROR_NONE || glb_policy->lb_call == NULL) {
+    glb_policy->client_load_report_timer_pending = false;
+    GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
+                              "client_load_report");
+    return;
+  }
+  schedule_next_client_load_report(exec_ctx, glb_policy);
+}
+
+static void do_send_client_load_report_locked(grpc_exec_ctx *exec_ctx,
+                                              glb_lb_policy *glb_policy) {
+  grpc_op op;
+  memset(&op, 0, sizeof(op));
+  op.op = GRPC_OP_SEND_MESSAGE;
+  op.data.send_message.send_message = glb_policy->client_load_report_payload;
+  grpc_closure_init(&glb_policy->client_load_report_closure,
+                    client_load_report_done_locked, glb_policy,
+                    grpc_combiner_scheduler(glb_policy->base.combiner, false));
+  grpc_call_error call_error = grpc_call_start_batch_and_execute(
+      exec_ctx, glb_policy->lb_call, &op, 1,
+      &glb_policy->client_load_report_closure);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+}
+
+static bool load_report_counters_are_zero(grpc_grpclb_request *request) {
+  return request->client_stats.num_calls_started == 0 &&
+         request->client_stats.num_calls_finished == 0 &&
+         request->client_stats.num_calls_finished_with_drop_for_rate_limiting ==
+             0 &&
+         request->client_stats
+                 .num_calls_finished_with_drop_for_load_balancing == 0 &&
+         request->client_stats.num_calls_finished_with_client_failed_to_send ==
+             0 &&
+         request->client_stats.num_calls_finished_known_received == 0;
+}
+
+static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                           grpc_error *error) {
+  glb_lb_policy *glb_policy = arg;
+  if (error == GRPC_ERROR_CANCELLED || glb_policy->lb_call == NULL) {
+    glb_policy->client_load_report_timer_pending = false;
+    GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
+                              "client_load_report");
+    return;
+  }
+  // Construct message payload.
+  GPR_ASSERT(glb_policy->client_load_report_payload == NULL);
+  grpc_grpclb_request *request =
+      grpc_grpclb_load_report_request_create(glb_policy->client_stats);
+  // Skip client load report if the counters were all zero in the last
+  // report and they are still zero in this one.
+  if (load_report_counters_are_zero(request)) {
+    if (glb_policy->last_client_load_report_counters_were_zero) {
+      grpc_grpclb_request_destroy(request);
+      schedule_next_client_load_report(exec_ctx, glb_policy);
+      return;
+    }
+    glb_policy->last_client_load_report_counters_were_zero = true;
+  } else {
+    glb_policy->last_client_load_report_counters_were_zero = false;
+  }
+  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
+  glb_policy->client_load_report_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_slice_unref_internal(exec_ctx, request_payload_slice);
+  grpc_grpclb_request_destroy(request);
+  // If we've already sent the initial request, then we can go ahead and
+  // sent the load report.  Otherwise, we need to wait until the initial
+  // request has been sent to send this
+  // (see lb_on_sent_initial_request_locked() below).
+  if (glb_policy->initial_request_sent) {
+    do_send_client_load_report_locked(exec_ctx, glb_policy);
+  }
+}
+
+static void lb_on_sent_initial_request_locked(grpc_exec_ctx *exec_ctx,
+                                              void *arg, grpc_error *error);
 static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx,
                                                 void *arg, grpc_error *error);
 static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg,
@@ -1107,13 +1277,24 @@
    * glb_policy->base.interested_parties, which is comprised of the polling
    * entities from \a client_channel. */
   grpc_slice host = grpc_slice_from_copied_string(glb_policy->server_name);
+  gpr_timespec deadline =
+      glb_policy->lb_call_timeout_ms == 0
+          ? gpr_inf_future(GPR_CLOCK_MONOTONIC)
+          : gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                         gpr_time_from_millis(glb_policy->lb_call_timeout_ms,
+                                              GPR_TIMESPAN));
   glb_policy->lb_call = grpc_channel_create_pollset_set_call(
       exec_ctx, glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
       glb_policy->base.interested_parties,
       GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD,
-      &host, glb_policy->deadline, NULL);
+      &host, deadline, NULL);
   grpc_slice_unref_internal(exec_ctx, host);
 
+  if (glb_policy->client_stats != NULL) {
+    grpc_grpclb_client_stats_unref(glb_policy->client_stats);
+  }
+  glb_policy->client_stats = grpc_grpclb_client_stats_create();
+
   grpc_metadata_array_init(&glb_policy->lb_initial_metadata_recv);
   grpc_metadata_array_init(&glb_policy->lb_trailing_metadata_recv);
 
@@ -1125,6 +1306,9 @@
   grpc_slice_unref_internal(exec_ctx, request_payload_slice);
   grpc_grpclb_request_destroy(request);
 
+  grpc_closure_init(&glb_policy->lb_on_sent_initial_request,
+                    lb_on_sent_initial_request_locked, glb_policy,
+                    grpc_combiner_scheduler(glb_policy->base.combiner, false));
   grpc_closure_init(&glb_policy->lb_on_server_status_received,
                     lb_on_server_status_received_locked, glb_policy,
                     grpc_combiner_scheduler(glb_policy->base.combiner, false));
@@ -1138,6 +1322,10 @@
                    GRPC_GRPCLB_RECONNECT_JITTER,
                    GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
                    GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
+
+  glb_policy->initial_request_sent = false;
+  glb_policy->seen_initial_response = false;
+  glb_policy->last_client_load_report_counters_were_zero = false;
 }
 
 static void lb_call_destroy_locked(grpc_exec_ctx *exec_ctx,
@@ -1151,6 +1339,10 @@
 
   grpc_byte_buffer_destroy(glb_policy->lb_request_payload);
   grpc_slice_unref_internal(exec_ctx, glb_policy->lb_call_status_details);
+
+  if (!glb_policy->client_load_report_timer_pending) {
+    grpc_timer_cancel(exec_ctx, &glb_policy->client_load_report_timer);
+  }
 }
 
 /*
@@ -1179,21 +1371,27 @@
   op->flags = 0;
   op->reserved = NULL;
   op++;
-
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
   op->data.recv_initial_metadata.recv_initial_metadata =
       &glb_policy->lb_initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
-
   GPR_ASSERT(glb_policy->lb_request_payload != NULL);
   op->op = GRPC_OP_SEND_MESSAGE;
   op->data.send_message.send_message = glb_policy->lb_request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
+  /* take a weak ref (won't prevent calling of \a glb_shutdown if the strong ref
+   * count goes to zero) to be unref'd in lb_on_sent_initial_request_locked() */
+  GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "lb_on_server_status_received");
+  call_error = grpc_call_start_batch_and_execute(
+      exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops),
+      &glb_policy->lb_on_sent_initial_request);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
 
+  op = ops;
   op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
   op->data.recv_status_on_client.trailing_metadata =
       &glb_policy->lb_trailing_metadata_recv;
@@ -1225,6 +1423,19 @@
   GPR_ASSERT(GRPC_CALL_OK == call_error);
 }
 
+static void lb_on_sent_initial_request_locked(grpc_exec_ctx *exec_ctx,
+                                              void *arg, grpc_error *error) {
+  glb_lb_policy *glb_policy = arg;
+  glb_policy->initial_request_sent = true;
+  // If we attempted to send a client load report before the initial
+  // request was sent, send the load report now.
+  if (glb_policy->client_load_report_payload != NULL) {
+    do_send_client_load_report_locked(exec_ctx, glb_policy);
+  }
+  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
+                            "lb_on_response_received_locked");
+}
+
 static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg,
                                            grpc_error *error) {
   glb_lb_policy *glb_policy = arg;
@@ -1240,58 +1451,91 @@
     grpc_byte_buffer_reader_init(&bbr, glb_policy->lb_response_payload);
     grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
     grpc_byte_buffer_destroy(glb_policy->lb_response_payload);
-    grpc_grpclb_serverlist *serverlist =
-        grpc_grpclb_response_parse_serverlist(response_slice);
-    if (serverlist != NULL) {
-      GPR_ASSERT(glb_policy->lb_call != NULL);
-      grpc_slice_unref_internal(exec_ctx, response_slice);
-      if (grpc_lb_glb_trace) {
-        gpr_log(GPR_INFO, "Serverlist with %lu servers received",
-                (unsigned long)serverlist->num_servers);
-        for (size_t i = 0; i < serverlist->num_servers; ++i) {
-          grpc_resolved_address addr;
-          parse_server(serverlist->servers[i], &addr);
-          char *ipport;
-          grpc_sockaddr_to_string(&ipport, &addr, false);
-          gpr_log(GPR_INFO, "Serverlist[%lu]: %s", (unsigned long)i, ipport);
-          gpr_free(ipport);
-        }
-      }
 
-      /* update serverlist */
-      if (serverlist->num_servers > 0) {
-        if (grpc_grpclb_serverlist_equals(glb_policy->serverlist, serverlist)) {
-          if (grpc_lb_glb_trace) {
-            gpr_log(GPR_INFO,
-                    "Incoming server list identical to current, ignoring.");
-          }
-          grpc_grpclb_destroy_serverlist(serverlist);
-        } else { /* new serverlist */
-          if (glb_policy->serverlist != NULL) {
-            /* dispose of the old serverlist */
-            grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
-          }
-          /* and update the copy in the glb_lb_policy instance. This serverlist
-           * instance will be destroyed either upon the next update or in
-           * glb_destroy() */
-          glb_policy->serverlist = serverlist;
-
-          rr_handover_locked(exec_ctx, glb_policy);
-        }
-      } else {
+    grpc_grpclb_initial_response *response = NULL;
+    if (!glb_policy->seen_initial_response &&
+        (response = grpc_grpclb_initial_response_parse(response_slice)) !=
+            NULL) {
+      if (response->has_client_stats_report_interval) {
+        glb_policy->client_stats_report_interval =
+            gpr_time_max(gpr_time_from_seconds(1, GPR_TIMESPAN),
+                         grpc_grpclb_duration_to_timespec(
+                             &response->client_stats_report_interval));
         if (grpc_lb_glb_trace) {
           gpr_log(GPR_INFO,
-                  "Received empty server list. Picks will stay pending until a "
-                  "response with > 0 servers is received");
+                  "received initial LB response message; "
+                  "client load reporting interval = %" PRId64 ".%09d sec",
+                  glb_policy->client_stats_report_interval.tv_sec,
+                  glb_policy->client_stats_report_interval.tv_nsec);
         }
-        grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
+        /* take a weak ref (won't prevent calling of \a glb_shutdown() if the
+         * strong ref count goes to zero) to be unref'd in
+         * send_client_load_report() */
+        glb_policy->client_load_report_timer_pending = true;
+        GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "client_load_report");
+        schedule_next_client_load_report(exec_ctx, glb_policy);
+      } else if (grpc_lb_glb_trace) {
+        gpr_log(GPR_INFO,
+                "received initial LB response message; "
+                "client load reporting NOT enabled");
       }
-    } else { /* serverlist == NULL */
-      gpr_log(GPR_ERROR, "Invalid LB response received: '%s'. Ignoring.",
-              grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX));
-      grpc_slice_unref_internal(exec_ctx, response_slice);
+      grpc_grpclb_initial_response_destroy(response);
+      glb_policy->seen_initial_response = true;
+    } else {
+      grpc_grpclb_serverlist *serverlist =
+          grpc_grpclb_response_parse_serverlist(response_slice);
+      if (serverlist != NULL) {
+        GPR_ASSERT(glb_policy->lb_call != NULL);
+        if (grpc_lb_glb_trace) {
+          gpr_log(GPR_INFO, "Serverlist with %lu servers received",
+                  (unsigned long)serverlist->num_servers);
+          for (size_t i = 0; i < serverlist->num_servers; ++i) {
+            grpc_resolved_address addr;
+            parse_server(serverlist->servers[i], &addr);
+            char *ipport;
+            grpc_sockaddr_to_string(&ipport, &addr, false);
+            gpr_log(GPR_INFO, "Serverlist[%lu]: %s", (unsigned long)i, ipport);
+            gpr_free(ipport);
+          }
+        }
+
+        /* update serverlist */
+        if (serverlist->num_servers > 0) {
+          if (grpc_grpclb_serverlist_equals(glb_policy->serverlist,
+                                            serverlist)) {
+            if (grpc_lb_glb_trace) {
+              gpr_log(GPR_INFO,
+                      "Incoming server list identical to current, ignoring.");
+            }
+            grpc_grpclb_destroy_serverlist(serverlist);
+          } else { /* new serverlist */
+            if (glb_policy->serverlist != NULL) {
+              /* dispose of the old serverlist */
+              grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
+            }
+            /* and update the copy in the glb_lb_policy instance. This
+             * serverlist instance will be destroyed either upon the next
+             * update or in glb_destroy() */
+            glb_policy->serverlist = serverlist;
+
+            rr_handover_locked(exec_ctx, glb_policy);
+          }
+        } else {
+          if (grpc_lb_glb_trace) {
+            gpr_log(GPR_INFO,
+                    "Received empty server list. Picks will stay pending until "
+                    "a response with > 0 servers is received");
+          }
+          grpc_grpclb_destroy_serverlist(serverlist);
+        }
+      } else { /* serverlist == NULL */
+        gpr_log(GPR_ERROR, "Invalid LB response received: '%s'. Ignoring.",
+                grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX));
+      }
     }
 
+    grpc_slice_unref_internal(exec_ctx, response_slice);
+
     if (!glb_policy->shutting_down) {
       /* keep listening for serverlist updates */
       op->op = GRPC_OP_RECV_MESSAGE;
@@ -1403,9 +1647,29 @@
 }
 
 /* Plugin registration */
+
+// Only add client_load_reporting filter if the grpclb LB policy is used.
+static bool maybe_add_client_load_reporting_filter(
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) {
+  const grpc_channel_args *args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  const grpc_arg *channel_arg =
+      grpc_channel_args_find(args, GRPC_ARG_LB_POLICY_NAME);
+  if (channel_arg != NULL && channel_arg->type == GRPC_ARG_STRING &&
+      strcmp(channel_arg->value.string, "grpclb") == 0) {
+    return grpc_channel_stack_builder_append_filter(
+        builder, (const grpc_channel_filter *)arg, NULL, NULL);
+  }
+  return true;
+}
+
 void grpc_lb_policy_grpclb_init() {
   grpc_register_lb_policy(grpc_glb_lb_factory_create());
   grpc_register_tracer("glb", &grpc_lb_glb_trace);
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_client_load_reporting_filter,
+                                   (void *)&grpc_client_load_reporting_filter);
 }
 
 void grpc_lb_policy_grpclb_shutdown() {}
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c
new file mode 100644
index 0000000..444c03b
--- /dev/null
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c
@@ -0,0 +1,133 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/channel/channel_args.h"
+
+#define GRPC_ARG_GRPCLB_CLIENT_STATS "grpc.grpclb_client_stats"
+
+struct grpc_grpclb_client_stats {
+  gpr_refcount refs;
+  gpr_atm num_calls_started;
+  gpr_atm num_calls_finished;
+  gpr_atm num_calls_finished_with_drop_for_rate_limiting;
+  gpr_atm num_calls_finished_with_drop_for_load_balancing;
+  gpr_atm num_calls_finished_with_client_failed_to_send;
+  gpr_atm num_calls_finished_known_received;
+};
+
+grpc_grpclb_client_stats* grpc_grpclb_client_stats_create() {
+  grpc_grpclb_client_stats* client_stats = gpr_zalloc(sizeof(*client_stats));
+  gpr_ref_init(&client_stats->refs, 1);
+  return client_stats;
+}
+
+grpc_grpclb_client_stats* grpc_grpclb_client_stats_ref(
+    grpc_grpclb_client_stats* client_stats) {
+  gpr_ref(&client_stats->refs);
+  return client_stats;
+}
+
+void grpc_grpclb_client_stats_unref(grpc_grpclb_client_stats* client_stats) {
+  if (gpr_unref(&client_stats->refs)) {
+    gpr_free(client_stats);
+  }
+}
+
+void grpc_grpclb_client_stats_add_call_started(
+    grpc_grpclb_client_stats* client_stats) {
+  gpr_atm_full_fetch_add(&client_stats->num_calls_started, (gpr_atm)1);
+}
+
+void grpc_grpclb_client_stats_add_call_finished(
+    bool finished_with_drop_for_rate_limiting,
+    bool finished_with_drop_for_load_balancing,
+    bool finished_with_client_failed_to_send, bool finished_known_received,
+    grpc_grpclb_client_stats* client_stats) {
+  gpr_atm_full_fetch_add(&client_stats->num_calls_finished, (gpr_atm)1);
+  if (finished_with_drop_for_rate_limiting) {
+    gpr_atm_full_fetch_add(
+        &client_stats->num_calls_finished_with_drop_for_rate_limiting,
+        (gpr_atm)1);
+  }
+  if (finished_with_drop_for_load_balancing) {
+    gpr_atm_full_fetch_add(
+        &client_stats->num_calls_finished_with_drop_for_load_balancing,
+        (gpr_atm)1);
+  }
+  if (finished_with_client_failed_to_send) {
+    gpr_atm_full_fetch_add(
+        &client_stats->num_calls_finished_with_client_failed_to_send,
+        (gpr_atm)1);
+  }
+  if (finished_known_received) {
+    gpr_atm_full_fetch_add(&client_stats->num_calls_finished_known_received,
+                           (gpr_atm)1);
+  }
+}
+
+static void atomic_get_and_reset_counter(int64_t* value, gpr_atm* counter) {
+  *value = (int64_t)gpr_atm_acq_load(counter);
+  gpr_atm_full_fetch_add(counter, (gpr_atm)(-*value));
+}
+
+void grpc_grpclb_client_stats_get(
+    grpc_grpclb_client_stats* client_stats, int64_t* num_calls_started,
+    int64_t* num_calls_finished,
+    int64_t* num_calls_finished_with_drop_for_rate_limiting,
+    int64_t* num_calls_finished_with_drop_for_load_balancing,
+    int64_t* num_calls_finished_with_client_failed_to_send,
+    int64_t* num_calls_finished_known_received) {
+  atomic_get_and_reset_counter(num_calls_started,
+                               &client_stats->num_calls_started);
+  atomic_get_and_reset_counter(num_calls_finished,
+                               &client_stats->num_calls_finished);
+  atomic_get_and_reset_counter(
+      num_calls_finished_with_drop_for_rate_limiting,
+      &client_stats->num_calls_finished_with_drop_for_rate_limiting);
+  atomic_get_and_reset_counter(
+      num_calls_finished_with_drop_for_load_balancing,
+      &client_stats->num_calls_finished_with_drop_for_load_balancing);
+  atomic_get_and_reset_counter(
+      num_calls_finished_with_client_failed_to_send,
+      &client_stats->num_calls_finished_with_client_failed_to_send);
+  atomic_get_and_reset_counter(
+      num_calls_finished_known_received,
+      &client_stats->num_calls_finished_known_received);
+}
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
new file mode 100644
index 0000000..0af4a91
--- /dev/null
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H
+
+#include <stdbool.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+typedef struct grpc_grpclb_client_stats grpc_grpclb_client_stats;
+
+grpc_grpclb_client_stats* grpc_grpclb_client_stats_create();
+grpc_grpclb_client_stats* grpc_grpclb_client_stats_ref(
+    grpc_grpclb_client_stats* client_stats);
+void grpc_grpclb_client_stats_unref(grpc_grpclb_client_stats* client_stats);
+
+void grpc_grpclb_client_stats_add_call_started(
+    grpc_grpclb_client_stats* client_stats);
+void grpc_grpclb_client_stats_add_call_finished(
+    bool finished_with_drop_for_rate_limiting,
+    bool finished_with_drop_for_load_balancing,
+    bool finished_with_client_failed_to_send, bool finished_known_received,
+    grpc_grpclb_client_stats* client_stats);
+
+void grpc_grpclb_client_stats_get(
+    grpc_grpclb_client_stats* client_stats, int64_t* num_calls_started,
+    int64_t* num_calls_finished,
+    int64_t* num_calls_finished_with_drop_for_rate_limiting,
+    int64_t* num_calls_finished_with_drop_for_load_balancing,
+    int64_t* num_calls_finished_with_client_failed_to_send,
+    int64_t* num_calls_finished_known_received);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H \
+          */
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
index 87549b7..81b6932 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
@@ -80,15 +80,45 @@
 
 grpc_grpclb_request *grpc_grpclb_request_create(const char *lb_service_name) {
   grpc_grpclb_request *req = gpr_malloc(sizeof(grpc_grpclb_request));
-
-  req->has_client_stats = 0; /* TODO(dgq): add support for stats once defined */
-  req->has_initial_request = 1;
-  req->initial_request.has_name = 1;
+  req->has_client_stats = false;
+  req->has_initial_request = true;
+  req->initial_request.has_name = true;
   strncpy(req->initial_request.name, lb_service_name,
           GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH);
   return req;
 }
 
+static void populate_timestamp(gpr_timespec timestamp,
+                               struct _grpc_lb_v1_Timestamp *timestamp_pb) {
+  timestamp_pb->has_seconds = true;
+  timestamp_pb->seconds = timestamp.tv_sec;
+  timestamp_pb->has_nanos = true;
+  timestamp_pb->nanos = timestamp.tv_nsec;
+}
+
+grpc_grpclb_request *grpc_grpclb_load_report_request_create(
+    grpc_grpclb_client_stats *client_stats) {
+  grpc_grpclb_request *req = gpr_zalloc(sizeof(grpc_grpclb_request));
+  req->has_client_stats = true;
+  req->client_stats.has_timestamp = true;
+  populate_timestamp(gpr_now(GPR_CLOCK_REALTIME), &req->client_stats.timestamp);
+  req->client_stats.has_num_calls_started = true;
+  req->client_stats.has_num_calls_finished = true;
+  req->client_stats.has_num_calls_finished_with_drop_for_rate_limiting = true;
+  req->client_stats.has_num_calls_finished_with_drop_for_load_balancing = true;
+  req->client_stats.has_num_calls_finished_with_client_failed_to_send = true;
+  req->client_stats.has_num_calls_finished_with_client_failed_to_send = true;
+  req->client_stats.has_num_calls_finished_known_received = true;
+  grpc_grpclb_client_stats_get(
+      client_stats, &req->client_stats.num_calls_started,
+      &req->client_stats.num_calls_finished,
+      &req->client_stats.num_calls_finished_with_drop_for_rate_limiting,
+      &req->client_stats.num_calls_finished_with_drop_for_load_balancing,
+      &req->client_stats.num_calls_finished_with_client_failed_to_send,
+      &req->client_stats.num_calls_finished_known_received);
+  return req;
+}
+
 grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request *request) {
   size_t encoded_length;
   pb_ostream_t sizestream;
@@ -122,6 +152,9 @@
     gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
     return NULL;
   }
+
+  if (!res.has_initial_response) return NULL;
+
   grpc_grpclb_initial_response *initial_res =
       gpr_malloc(sizeof(grpc_grpclb_initial_response));
   memcpy(initial_res, &res.initial_response,
@@ -243,6 +276,15 @@
   return 0;
 }
 
+gpr_timespec grpc_grpclb_duration_to_timespec(
+    grpc_grpclb_duration *duration_pb) {
+  gpr_timespec duration;
+  duration.tv_sec = duration_pb->has_seconds ? duration_pb->seconds : 0;
+  duration.tv_nsec = duration_pb->has_nanos ? duration_pb->nanos : 0;
+  duration.clock_type = GPR_TIMESPAN;
+  return duration;
+}
+
 void grpc_grpclb_initial_response_destroy(
     grpc_grpclb_initial_response *response) {
   gpr_free(response);
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
index d014b88..0687382 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
@@ -36,6 +36,7 @@
 
 #include <grpc/slice_buffer.h>
 
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
 #include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h"
 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 
@@ -58,6 +59,8 @@
 
 /** Create a request for a gRPC LB service under \a lb_service_name */
 grpc_grpclb_request *grpc_grpclb_request_create(const char *lb_service_name);
+grpc_grpclb_request *grpc_grpclb_load_report_request_create(
+    grpc_grpclb_client_stats *client_stats);
 
 /** Protocol Buffers v3-encode \a request */
 grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request *request);
@@ -93,6 +96,9 @@
 int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs,
                                  const grpc_grpclb_duration *rhs);
 
+gpr_timespec grpc_grpclb_duration_to_timespec(
+    grpc_grpclb_duration *duration_pb);
+
 /** Destroy \a initial_response */
 void grpc_grpclb_initial_response_destroy(
     grpc_grpclb_initial_response *response);
diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c
index 2b77cd3..b1c5dfc 100644
--- a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c
+++ b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c
@@ -189,7 +189,8 @@
 
 static int pf_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
                           const grpc_lb_policy_pick_args *pick_args,
-                          grpc_connected_subchannel **target, void **user_data,
+                          grpc_connected_subchannel **target,
+                          grpc_call_context_element *context, void **user_data,
                           grpc_closure *on_complete) {
   pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
   pending_pick *pp;
diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c
index ff41e61..4c17f9c 100644
--- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c
+++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c
@@ -414,7 +414,8 @@
 
 static int rr_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
                           const grpc_lb_policy_pick_args *pick_args,
-                          grpc_connected_subchannel **target, void **user_data,
+                          grpc_connected_subchannel **target,
+                          grpc_call_context_element *context, void **user_data,
                           grpc_closure *on_complete) {
   round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
   pending_pick *pp;
diff --git a/src/core/ext/filters/client_channel/subchannel.c b/src/core/ext/filters/client_channel/subchannel.c
index b2de85c..1af3393 100644
--- a/src/core/ext/filters/client_channel/subchannel.c
+++ b/src/core/ext/filters/client_channel/subchannel.c
@@ -781,7 +781,7 @@
   (*call)->connection = GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call");
   const grpc_call_element_args call_args = {.call_stack = callstk,
                                             .server_transport_data = NULL,
-                                            .context = NULL,
+                                            .context = args->context,
                                             .path = args->path,
                                             .start_time = args->start_time,
                                             .deadline = args->deadline,
diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h
index 6473de4..e433c33 100644
--- a/src/core/ext/filters/client_channel/subchannel.h
+++ b/src/core/ext/filters/client_channel/subchannel.h
@@ -119,6 +119,7 @@
   gpr_timespec start_time;
   gpr_timespec deadline;
   gpr_arena *arena;
+  grpc_call_context_element *context;
 } grpc_connected_subchannel_call_args;
 
 grpc_error *grpc_connected_subchannel_create_call(
diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.c b/src/core/ext/filters/http/message_compress/message_compress_filter.c
index 4f5f41e..1da8cf6 100644
--- a/src/core/ext/filters/http/message_compress/message_compress_filter.c
+++ b/src/core/ext/filters/http/message_compress/message_compress_filter.c
@@ -277,13 +277,10 @@
   GPR_TIMER_BEGIN("compress_start_transport_stream_op_batch", 0);
 
   if (op->cancel_stream) {
-    gpr_atm cur;
     GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error);
-    do {
-      cur = gpr_atm_acq_load(&calld->send_initial_metadata_state);
-    } while (!gpr_atm_rel_cas(
-        &calld->send_initial_metadata_state, cur,
-        CANCELLED_BIT | (gpr_atm)op->payload->cancel_stream.cancel_error));
+    gpr_atm cur = gpr_atm_full_xchg(
+        &calld->send_initial_metadata_state,
+        CANCELLED_BIT | (gpr_atm)op->payload->cancel_stream.cancel_error);
     switch (cur) {
       case HAS_COMPRESSION_ALGORITHM:
       case NO_COMPRESSION_ALGORITHM:
@@ -311,13 +308,18 @@
       grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error);
       return;
     }
-    gpr_atm cur = gpr_atm_acq_load(&calld->send_initial_metadata_state);
+    gpr_atm cur;
+  retry_send_im:
+    cur = gpr_atm_acq_load(&calld->send_initial_metadata_state);
     GPR_ASSERT(cur != HAS_COMPRESSION_ALGORITHM &&
                cur != NO_COMPRESSION_ALGORITHM);
     if ((cur & CANCELLED_BIT) == 0) {
-      gpr_atm_rel_store(&calld->send_initial_metadata_state,
-                        has_compression_algorithm ? HAS_COMPRESSION_ALGORITHM
-                                                  : NO_COMPRESSION_ALGORITHM);
+      if (!gpr_atm_rel_cas(&calld->send_initial_metadata_state, cur,
+                           has_compression_algorithm
+                               ? HAS_COMPRESSION_ALGORITHM
+                               : NO_COMPRESSION_ALGORITHM)) {
+        goto retry_send_im;
+      }
       if (cur != INITIAL_METADATA_UNSEEN) {
         grpc_call_next_op(exec_ctx, elem,
                           (grpc_transport_stream_op_batch *)cur);
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
index 9c8505d..ad674b8 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
@@ -101,7 +101,7 @@
                                            void *reserved) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   GRPC_API_TRACE(
-      "grpc_insecure_channel_create(target=%p, args=%p, reserved=%p)", 3,
+      "grpc_insecure_channel_create(target=%s, args=%p, reserved=%p)", 3,
       (target, args, reserved));
   GPR_ASSERT(reserved == NULL);
   // Add channel arg containing the client channel factory.
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index d6b79bd..23a55fa 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -884,14 +884,23 @@
   GPR_TIMER_BEGIN("write_action_begin_locked", 0);
   grpc_chttp2_transport *t = gt;
   GPR_ASSERT(t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE);
-  if (!t->closed && grpc_chttp2_begin_write(exec_ctx, t)) {
-    set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING,
-                    "begin writing");
-    grpc_closure_sched(exec_ctx, &t->write_action, GRPC_ERROR_NONE);
-  } else {
-    set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE,
-                    "begin writing nothing");
-    GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing");
+  switch (t->closed ? GRPC_CHTTP2_NOTHING_TO_WRITE
+                    : grpc_chttp2_begin_write(exec_ctx, t)) {
+    case GRPC_CHTTP2_NOTHING_TO_WRITE:
+      set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE,
+                      "begin writing nothing");
+      GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing");
+      break;
+    case GRPC_CHTTP2_PARTIAL_WRITE:
+      set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE,
+                      "begin writing partial");
+      grpc_closure_sched(exec_ctx, &t->write_action, GRPC_ERROR_NONE);
+      break;
+    case GRPC_CHTTP2_FULL_WRITE:
+      set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING,
+                      "begin writing");
+      grpc_closure_sched(exec_ctx, &t->write_action, GRPC_ERROR_NONE);
+      break;
   }
   GPR_TIMER_END("write_action_begin_locked", 0);
 }
@@ -988,7 +997,7 @@
   t->seen_goaway = 1;
 
   /* When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug
-   * data equal to “too_many_pings”, it should log the occurrence at a log level
+   * data equal to "too_many_pings", it should log the occurrence at a log level
    * that is enabled by default and double the configured KEEPALIVE_TIME used
    * for new connections on that channel. */
   if (t->is_client && goaway_error == GRPC_HTTP2_ENHANCE_YOUR_CALM &&
@@ -2130,27 +2139,29 @@
 
 static void update_bdp(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                        double bdp_dbl) {
-  uint32_t bdp;
-  if (bdp_dbl <= 0) {
-    bdp = 0;
-  } else if (bdp_dbl > UINT32_MAX) {
-    bdp = UINT32_MAX;
+  int32_t bdp;
+  const int32_t kMinBDP = 128;
+  if (bdp_dbl <= kMinBDP) {
+    bdp = kMinBDP;
+  } else if (bdp_dbl > INT32_MAX) {
+    bdp = INT32_MAX;
   } else {
-    bdp = (uint32_t)(bdp_dbl);
+    bdp = (int32_t)(bdp_dbl);
   }
   int64_t delta =
       (int64_t)bdp -
       (int64_t)t->settings[GRPC_LOCAL_SETTINGS]
                           [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
-  if (delta == 0 || (bdp != 0 && delta > -1024 && delta < 1024)) {
+  if (delta == 0 || (delta > -bdp / 10 && delta < bdp / 10)) {
     return;
   }
   if (grpc_bdp_estimator_trace) {
     gpr_log(GPR_DEBUG, "%s: update initial window size to %d", t->peer_string,
             (int)bdp);
   }
-  push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, bdp);
-  push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, bdp);
+  push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
+               (uint32_t)bdp);
+  push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, (uint32_t)bdp);
 }
 
 static grpc_error *try_http_parsing(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 0aaa4ae..1f87206 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -552,9 +552,14 @@
                                 grpc_chttp2_transport *t,
                                 bool covered_by_poller, const char *reason);
 
-/** Someone is unlocking the transport mutex: check to see if writes
-    are required, and frame them if so */
-bool grpc_chttp2_begin_write(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t);
+typedef enum {
+  GRPC_CHTTP2_NOTHING_TO_WRITE,
+  GRPC_CHTTP2_PARTIAL_WRITE,
+  GRPC_CHTTP2_FULL_WRITE,
+} grpc_chttp2_begin_write_result;
+
+grpc_chttp2_begin_write_result grpc_chttp2_begin_write(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t);
 void grpc_chttp2_end_write(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                            grpc_error *error);
 
@@ -815,7 +820,7 @@
 /** Add a new ping strike to ping_recv_state.ping_strikes. If
     ping_recv_state.ping_strikes > ping_policy.max_ping_strikes, it sends GOAWAY
     with error code ENHANCE_YOUR_CALM and additional debug data resembling
-    “too_many_pings” followed by immediately closing the connection. */
+    "too_many_pings" followed by immediately closing the connection. */
 void grpc_chttp2_add_ping_strike(grpc_exec_ctx *exec_ctx,
                                  grpc_chttp2_transport *t);
 
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index 638b137..b0dd685 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -418,11 +418,9 @@
 
     GRPC_CHTTP2_FLOW_DEBIT_STREAM_INCOMING_WINDOW_DELTA("parse", t, s,
                                                         incoming_frame_size);
-    if ((int64_t)t->settings[GRPC_SENT_SETTINGS]
-                            [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] +
-            (int64_t)s->incoming_window_delta - (int64_t)s->announce_window <=
-        (int64_t)t->settings[GRPC_SENT_SETTINGS]
-                            [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] /
+    if ((int64_t)s->incoming_window_delta - (int64_t)s->announce_window <=
+        -(int64_t)t->settings[GRPC_SENT_SETTINGS]
+                             [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] /
             2) {
       grpc_chttp2_become_writable(exec_ctx, t, s,
                                   GRPC_CHTTP2_STREAM_WRITE_INITIATE_UNCOVERED,
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index 069780a..60db567 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -160,19 +160,22 @@
   return true;
 }
 
+/* How many bytes of incoming flow control would we like to advertise */
 uint32_t grpc_chttp2_target_incoming_window(grpc_chttp2_transport *t) {
-  return (uint32_t)GPR_MAX(
+  return (uint32_t)GPR_MIN(
       (int64_t)((1u << 31) - 1),
       t->stream_total_over_incoming_window +
-          (int64_t)GPR_MAX(
-              t->settings[GRPC_SENT_SETTINGS]
-                         [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] -
-                  t->stream_total_under_incoming_window,
-              0));
+          t->settings[GRPC_SENT_SETTINGS]
+                     [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
 }
 
-bool grpc_chttp2_begin_write(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport *t) {
+/* How many bytes would we like to put on the wire during a single syscall */
+static uint32_t target_write_size(grpc_chttp2_transport *t) {
+  return 1024 * 1024;
+}
+
+grpc_chttp2_begin_write_result grpc_chttp2_begin_write(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) {
   grpc_chttp2_stream *s;
 
   GPR_TIMER_BEGIN("grpc_chttp2_begin_write", 0);
@@ -206,9 +209,20 @@
     }
   }
 
+  bool partial_write = false;
+
   /* for each grpc_chttp2_stream that's become writable, frame it's data
      (according to available window sizes) and add to the output buffer */
-  while (grpc_chttp2_list_pop_writable_stream(t, &s)) {
+  while (true) {
+    if (t->outbuf.length > target_write_size(t)) {
+      partial_write = true;
+      break;
+    }
+
+    if (!grpc_chttp2_list_pop_writable_stream(t, &s)) {
+      break;
+    }
+
     bool sent_initial_metadata = s->sent_initial_metadata;
     bool now_writing = false;
 
@@ -395,7 +409,9 @@
 
   GPR_TIMER_END("grpc_chttp2_begin_write", 0);
 
-  return t->outbuf.count > 0;
+  return t->outbuf.count > 0 ? (partial_write ? GRPC_CHTTP2_PARTIAL_WRITE
+                                              : GRPC_CHTTP2_FULL_WRITE)
+                             : GRPC_CHTTP2_NOTHING_TO_WRITE;
 }
 
 void grpc_chttp2_end_write(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c
index d4e89d6..67974b0 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.c
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.c
@@ -886,6 +886,10 @@
                !stream_state->state_op_done[OP_RECV_MESSAGE]) {
       CRONET_LOG(GPR_DEBUG, "Because");
       result = false;
+    } else if (curr_op->cancel_stream &&
+               !stream_state->state_callback_received[OP_CANCELED]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
     } else if (curr_op->recv_trailing_metadata) {
       /* We aren't done with trailing metadata yet */
       if (!stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) {
diff --git a/src/core/lib/channel/context.h b/src/core/lib/channel/context.h
index 6c931ad..f0a2111 100644
--- a/src/core/lib/channel/context.h
+++ b/src/core/lib/channel/context.h
@@ -50,6 +50,9 @@
   /// Reserved for traffic_class_context.
   GRPC_CONTEXT_TRAFFIC,
 
+  /// Value is a \a grpc_grpclb_client_stats.
+  GRPC_GRPCLB_CLIENT_STATS,
+
   GRPC_CONTEXT_COUNT
 } grpc_context_index;
 
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index 453a64b..0ac2c2a 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -105,7 +105,7 @@
                    grpc_error *error) {
   grpc_polling_entity_del_from_pollset_set(exec_ctx, req->pollent,
                                            req->context->pollset_set);
-  grpc_closure_sched(exec_ctx, req->on_done, error);
+  grpc_closure_sched(exec_ctx, req->on_done, GRPC_ERROR_REF(error));
   grpc_http_parser_destroy(&req->parser);
   if (req->addresses != NULL) {
     grpc_resolved_addresses_destroy(req->addresses);
diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h
index 269dc35..2a553f4 100644
--- a/src/core/lib/iomgr/port.h
+++ b/src/core/lib/iomgr/port.h
@@ -88,6 +88,7 @@
 #ifndef __GLIBC__
 #define GRPC_LINUX_EPOLL 1
 #define GRPC_LINUX_EVENTFD 1
+#define GRPC_MSG_IOVLEN_TYPE int
 #endif
 #ifndef GRPC_LINUX_EVENTFD
 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
diff --git a/src/core/lib/support/cpu_linux.c b/src/core/lib/support/cpu_linux.c
index 1e50f59..b826dde 100644
--- a/src/core/lib/support/cpu_linux.c
+++ b/src/core/lib/support/cpu_linux.c
@@ -67,16 +67,16 @@
 }
 
 unsigned gpr_cpu_current_cpu(void) {
-#ifdef __GLIBC__
+#ifdef GPR_MUSL_LIBC_COMPAT
+  // sched_getcpu() is undefined on musl
+  return 0;
+#else
   int cpu = sched_getcpu();
   if (cpu < 0) {
     gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno));
     return 0;
   }
   return (unsigned)cpu;
-#else
-  // sched_getcpu() is undefined on musl
-  return 0;
 #endif
 }
 
diff --git a/src/core/lib/support/wrap_memcpy.c b/src/core/lib/support/wrap_memcpy.c
index 050cc6d..deb8d6b 100644
--- a/src/core/lib/support/wrap_memcpy.c
+++ b/src/core/lib/support/wrap_memcpy.c
@@ -31,6 +31,8 @@
  *
  */
 
+#include <grpc/support/port_platform.h>
+
 #include <string.h>
 
 /* Provide a wrapped memcpy for targets that need to be backwards
@@ -40,7 +42,7 @@
  */
 
 #ifdef __linux__
-#if defined(__x86_64__) && defined(__GNU_LIBRARY__)
+#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT)
 __asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
 void *__wrap_memcpy(void *destination, const void *source, size_t num) {
   return memcpy(destination, source, num);
diff --git a/src/core/lib/transport/bdp_estimator.c b/src/core/lib/transport/bdp_estimator.c
index e148367..5366942 100644
--- a/src/core/lib/transport/bdp_estimator.c
+++ b/src/core/lib/transport/bdp_estimator.c
@@ -44,6 +44,7 @@
   estimator->estimate = 65536;
   estimator->ping_state = GRPC_BDP_PING_UNSCHEDULED;
   estimator->name = name;
+  estimator->bw_est = 0;
 }
 
 bool grpc_bdp_estimator_get_estimate(grpc_bdp_estimator *estimator,
@@ -84,16 +85,26 @@
   GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_SCHEDULED);
   estimator->ping_state = GRPC_BDP_PING_STARTED;
   estimator->accumulator = 0;
+  estimator->ping_start_time = gpr_now(GPR_CLOCK_MONOTONIC);
 }
 
 void grpc_bdp_estimator_complete_ping(grpc_bdp_estimator *estimator) {
+  gpr_timespec dt_ts =
+      gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), estimator->ping_start_time);
+  double dt = (double)dt_ts.tv_sec + 1e-9 * (double)dt_ts.tv_nsec;
+  double bw = dt > 0 ? ((double)estimator->accumulator / dt) : 0;
   if (grpc_bdp_estimator_trace) {
-    gpr_log(GPR_DEBUG, "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64,
-            estimator->name, estimator->accumulator, estimator->estimate);
+    gpr_log(GPR_DEBUG, "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64
+                       " dt=%lf bw=%lfMbs bw_est=%lfMbs",
+            estimator->name, estimator->accumulator, estimator->estimate, dt,
+            bw / 125000.0, estimator->bw_est / 125000.0);
   }
   GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_STARTED);
-  if (estimator->accumulator > 2 * estimator->estimate / 3) {
-    estimator->estimate *= 2;
+  if (estimator->accumulator > 2 * estimator->estimate / 3 &&
+      bw > estimator->bw_est) {
+    estimator->estimate =
+        GPR_MAX(estimator->accumulator, estimator->estimate * 2);
+    estimator->bw_est = bw;
     if (grpc_bdp_estimator_trace) {
       gpr_log(GPR_DEBUG, "bdp[%s]: estimate increased to %" PRId64,
               estimator->name, estimator->estimate);
diff --git a/src/core/lib/transport/bdp_estimator.h b/src/core/lib/transport/bdp_estimator.h
index df8d1f6..752bee1 100644
--- a/src/core/lib/transport/bdp_estimator.h
+++ b/src/core/lib/transport/bdp_estimator.h
@@ -34,6 +34,7 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H
 #define GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H
 
+#include <grpc/support/time.h>
 #include <stdbool.h>
 #include <stdint.h>
 
@@ -52,6 +53,8 @@
   grpc_bdp_estimator_ping_state ping_state;
   int64_t accumulator;
   int64_t estimate;
+  gpr_timespec ping_start_time;
+  double bw_est;
   const char *name;
 } grpc_bdp_estimator;
 
diff --git a/src/cpp/common/core_codegen.cc b/src/cpp/common/core_codegen.cc
index f679a33..2b484c0 100644
--- a/src/cpp/common/core_codegen.cc
+++ b/src/cpp/common/core_codegen.cc
@@ -134,6 +134,12 @@
   return ::grpc_raw_byte_buffer_create(slice, nslices);
 }
 
+grpc_slice CoreCodegen::grpc_slice_new_with_user_data(void* p, size_t len,
+                                                      void (*destroy)(void*),
+                                                      void* user_data) {
+  return ::grpc_slice_new_with_user_data(p, len, destroy, user_data);
+}
+
 grpc_slice CoreCodegen::grpc_empty_slice() { return ::grpc_empty_slice(); }
 
 grpc_slice CoreCodegen::grpc_slice_malloc(size_t length) {
@@ -144,10 +150,18 @@
   ::grpc_slice_unref(slice);
 }
 
+grpc_slice CoreCodegen::grpc_slice_ref(grpc_slice slice) {
+  return ::grpc_slice_ref(slice);
+}
+
 grpc_slice CoreCodegen::grpc_slice_split_tail(grpc_slice* s, size_t split) {
   return ::grpc_slice_split_tail(s, split);
 }
 
+grpc_slice CoreCodegen::grpc_slice_split_head(grpc_slice* s, size_t split) {
+  return ::grpc_slice_split_head(s, split);
+}
+
 grpc_slice CoreCodegen::grpc_slice_from_static_buffer(const void* buffer,
                                                       size_t length) {
   return ::grpc_slice_from_static_buffer(buffer, length);
diff --git a/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs b/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
index b9c0fe6..c9f7c42 100644
--- a/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
@@ -179,6 +179,9 @@
                 statsResetCount.Increment();
             }
 
+            GrpcEnvironment.Logger.Info("[ClientRunnerImpl.GetStats] GC collection counts: gen0 {0}, gen1 {1}, gen2 {2}, gen3 {3} (histogram reset count:{4}, seconds since reset: {5})",
+                GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2), GC.CollectionCount(3), statsResetCount.Count, secondsElapsed);
+
             // TODO: populate user time and system time
             return new ClientStats
             {
diff --git a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
index b17b2c2..486befe 100644
--- a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
+++ b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
@@ -95,10 +95,13 @@
                 Ports = { new ServerPort(host, options.DriverPort, ServerCredentials.Insecure )}
             };
             int boundPort = server.Ports.Single().BoundPort;
-            Console.WriteLine("Running qps worker server on " + string.Format("{0}:{1}", host, boundPort));
+            GrpcEnvironment.Logger.Info("Running qps worker server on {0}:{1}", host, boundPort);
             server.Start();
             await tcs.Task;
             await server.ShutdownAsync();
+
+            GrpcEnvironment.Logger.Info("GC collection counts (after shutdown): gen0 {0}, gen1 {1}, gen2 {2}, gen3 {3}",
+                GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2), GC.CollectionCount(3));
         }
     }
 }
diff --git a/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs b/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs
index 8689d18..7ab7734 100644
--- a/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs
@@ -154,6 +154,9 @@
         {
             var secondsElapsed = wallClockStopwatch.GetElapsedSnapshot(reset).TotalSeconds;
 
+            GrpcEnvironment.Logger.Info("[ServerRunner.GetStats] GC collection counts: gen0 {0}, gen1 {1}, gen2 {2}, gen3 {3} (seconds since last reset {4})",
+                GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2), GC.CollectionCount(3), secondsElapsed);
+
             // TODO: populate user time and system time
             return new ServerStats
             {
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index fe0c80e..49179ab 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -42,7 +42,6 @@
 #include "call_credentials.h"
 #include "channel.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 #include "grpc/grpc.h"
 #include "grpc/grpc_security.h"
 #include "grpc/support/alloc.h"
diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc
index be04cf4..eb6bc0f 100644
--- a/src/node/ext/channel.cc
+++ b/src/node/ext/channel.cc
@@ -41,7 +41,6 @@
 #include "channel.h"
 #include "channel_credentials.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 #include "grpc/grpc.h"
 #include "grpc/grpc_security.h"
 #include "timeval.h"
diff --git a/src/node/ext/completion_queue_uv.cc b/src/node/ext/completion_queue.cc
similarity index 98%
rename from src/node/ext/completion_queue_uv.cc
rename to src/node/ext/completion_queue.cc
index 9b60911..d01abad 100644
--- a/src/node/ext/completion_queue_uv.cc
+++ b/src/node/ext/completion_queue.cc
@@ -31,8 +31,6 @@
  *
  */
 
-#ifdef GRPC_UV
-
 #include <grpc/grpc.h>
 #include <node.h>
 #include <uv.h>
@@ -95,5 +93,3 @@
 
 }  // namespace node
 }  // namespace grpc
-
-#endif /* GRPC_UV */
diff --git a/src/node/ext/completion_queue_async_worker.h b/src/node/ext/completion_queue_async_worker.h
deleted file mode 100644
index 6e54116..0000000
--- a/src/node/ext/completion_queue_async_worker.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_
-#define NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_
-#include <nan.h>
-
-#include "grpc/grpc.h"
-
-namespace grpc {
-namespace node {
-
-/* A worker that asynchronously calls completion_queue_next, and queues onto the
-   node event loop a call to the function stored in the event's tag. */
-class CompletionQueueAsyncWorker : public Nan::AsyncWorker {
- public:
-  CompletionQueueAsyncWorker();
-
-  ~CompletionQueueAsyncWorker();
-  /* Calls completion_queue_next with the provided deadline, and stores the
-     event if there was one or sets an error message if there was not */
-  void Execute();
-
-  /* Returns the completion queue attached to this class */
-  static grpc_completion_queue *GetQueue();
-
-  /* Convenience function to create a worker with the given arguments and queue
-     it to run asynchronously */
-  static void Next();
-
-  /* Initialize the CompletionQueueAsyncWorker class */
-  static void Init(v8::Local<v8::Object> exports);
-
- protected:
-  /* Called when Execute has succeeded (completed without setting an error
-     message). Calls the saved callback with the event that came from
-     completion_queue_next */
-  void HandleOKCallback();
-
-  void HandleErrorCallback();
-
- private:
-  grpc_event result;
-
-  static grpc_completion_queue *queue;
-
-  // Number of grpc_completion_queue_next calls in the thread pool
-  static int current_threads;
-  // Number of grpc_completion_queue_next calls waiting to enter the thread pool
-  static int waiting_next_calls;
-};
-
-}  // namespace node
-}  // namespace grpc
-
-#endif  // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_
diff --git a/src/node/ext/completion_queue_threadpool.cc b/src/node/ext/completion_queue_threadpool.cc
deleted file mode 100644
index 72df5d1..0000000
--- a/src/node/ext/completion_queue_threadpool.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-/* I don't like using #ifndef, but I don't see a better way to do this */
-#ifndef GRPC_UV
-
-#include <nan.h>
-#include <node.h>
-
-#include "call.h"
-#include "completion_queue.h"
-#include "grpc/grpc.h"
-#include "grpc/support/log.h"
-#include "grpc/support/time.h"
-
-namespace grpc {
-namespace node {
-
-namespace {
-
-/* A worker that asynchronously calls completion_queue_next, and queues onto the
-   node event loop a call to the function stored in the event's tag. */
-class CompletionQueueAsyncWorker : public Nan::AsyncWorker {
- public:
-  CompletionQueueAsyncWorker();
-
-  ~CompletionQueueAsyncWorker();
-  /* Calls completion_queue_next with the provided deadline, and stores the
-     event if there was one or sets an error message if there was not */
-  void Execute();
-
-  /* Returns the completion queue attached to this class */
-  static grpc_completion_queue *GetQueue();
-
-  /* Convenience function to create a worker with the given arguments and queue
-     it to run asynchronously */
-  static void Next();
-
-  /* Initialize the CompletionQueueAsyncWorker class */
-  static void Init(v8::Local<v8::Object> exports);
-
- protected:
-  /* Called when Execute has succeeded (completed without setting an error
-     message). Calls the saved callback with the event that came from
-     completion_queue_next */
-  void HandleOKCallback();
-
-  void HandleErrorCallback();
-
- private:
-  static void TryAddWorker();
-
-  grpc_event result;
-
-  static grpc_completion_queue *queue;
-
-  // Number of grpc_completion_queue_next calls in the thread pool
-  static int current_threads;
-  // Number of grpc_completion_queue_next calls waiting to enter the thread pool
-  static int waiting_next_calls;
-};
-
-const int max_queue_threads = 2;
-
-using v8::Function;
-using v8::Local;
-using v8::Object;
-using v8::Value;
-
-grpc_completion_queue *CompletionQueueAsyncWorker::queue;
-
-// Invariants: current_threads <= max_queue_threads
-// (current_threads == max_queue_threads) || (waiting_next_calls == 0)
-
-int CompletionQueueAsyncWorker::current_threads;
-int CompletionQueueAsyncWorker::waiting_next_calls;
-
-CompletionQueueAsyncWorker::CompletionQueueAsyncWorker()
-    : Nan::AsyncWorker(NULL) {}
-
-CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {}
-
-void CompletionQueueAsyncWorker::Execute() {
-  result = grpc_completion_queue_next(queue, gpr_inf_future(GPR_CLOCK_REALTIME),
-                                      NULL);
-  if (!result.success) {
-    SetErrorMessage("The async function encountered an error");
-  }
-}
-
-grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; }
-
-void CompletionQueueAsyncWorker::TryAddWorker() {
-  if (current_threads < max_queue_threads && waiting_next_calls > 0) {
-    current_threads += 1;
-    waiting_next_calls -= 1;
-    CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker();
-    Nan::AsyncQueueWorker(worker);
-  }
-  GPR_ASSERT(current_threads <= max_queue_threads);
-  GPR_ASSERT((current_threads == max_queue_threads) ||
-             (waiting_next_calls == 0));
-}
-
-void CompletionQueueAsyncWorker::Next() {
-  waiting_next_calls += 1;
-  TryAddWorker();
-}
-
-void CompletionQueueAsyncWorker::Init(Local<Object> exports) {
-  Nan::HandleScope scope;
-  current_threads = 0;
-  waiting_next_calls = 0;
-  queue = grpc_completion_queue_create_for_next(NULL);
-}
-
-void CompletionQueueAsyncWorker::HandleOKCallback() {
-  Nan::HandleScope scope;
-  current_threads -= 1;
-  TryAddWorker();
-  CompleteTag(result.tag, NULL);
-
-  DestroyTag(result.tag);
-}
-
-void CompletionQueueAsyncWorker::HandleErrorCallback() {
-  Nan::HandleScope scope;
-  current_threads -= 1;
-  TryAddWorker();
-  CompleteTag(result.tag, ErrorMessage());
-
-  DestroyTag(result.tag);
-}
-
-}  // namespace
-
-grpc_completion_queue *GetCompletionQueue() {
-  return CompletionQueueAsyncWorker::GetQueue();
-}
-
-void CompletionQueueNext() { CompletionQueueAsyncWorker::Next(); }
-
-void CompletionQueueInit(Local<Object> exports) {
-  CompletionQueueAsyncWorker::Init(exports);
-}
-
-}  // namespace node
-}  // namespace grpc
-
-#endif /* GRPC_UV */
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index 076f1ed..e193e82 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -43,18 +43,15 @@
 #include "grpc/support/time.h"
 
 // TODO(murgatroid99): Remove this when the endpoint API becomes public
-#ifdef GRPC_UV
 extern "C" {
 #include "src/core/lib/iomgr/pollset_uv.h"
 }
-#endif
 
 #include "call.h"
 #include "call_credentials.h"
 #include "channel.h"
 #include "channel_credentials.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 #include "server.h"
 #include "server_credentials.h"
 #include "slice.h"
@@ -432,9 +429,7 @@
   InitWriteFlags(exports);
   InitLogConstants(exports);
 
-#ifdef GRPC_UV
   grpc_pollset_work_run_loop = 0;
-#endif
 
   grpc::node::Call::Init(exports);
   grpc::node::CallCredentials::Init(exports);
diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc
index 1871a32..a885a9f 100644
--- a/src/node/ext/server.cc
+++ b/src/node/ext/server.cc
@@ -41,7 +41,6 @@
 #include <vector>
 #include "call.h"
 #include "completion_queue.h"
-#include "completion_queue_async_worker.h"
 #include "grpc/grpc.h"
 #include "grpc/grpc_security.h"
 #include "grpc/support/log.h"
@@ -78,6 +77,30 @@
 Nan::Callback *Server::constructor;
 Persistent<FunctionTemplate> Server::fun_tpl;
 
+static Callback *shutdown_callback = NULL;
+
+class ServerShutdownOp : public Op {
+ public:
+  ServerShutdownOp(grpc_server *server) : server(server) {}
+
+  ~ServerShutdownOp() {}
+
+  Local<Value> GetNodeValue() const { return Nan::Null(); }
+
+  bool ParseOp(Local<Value> value, grpc_op *out) { return true; }
+  bool IsFinalOp() { return false; }
+  void OnComplete(bool success) {
+    /* Because cancel_all_calls was called, we assume that shutdown_and_notify
+       completes successfully */
+    grpc_server_destroy(server);
+  }
+
+  grpc_server *server;
+
+ protected:
+  std::string GetTypeString() const { return "shutdown"; }
+};
+
 class NewCallOp : public Op {
  public:
   NewCallOp() {
@@ -149,6 +172,10 @@
       server_persist;
 };
 
+Server::Server(grpc_server *server) : wrapped_server(server) {}
+
+Server::~Server() { this->ShutdownServer(); }
+
 void Server::Init(Local<Object> exports) {
   HandleScope scope;
   Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
@@ -177,6 +204,36 @@
   }
 }
 
+NAN_METHOD(ServerShutdownCallback) {
+  if (!info[0]->IsNull()) {
+    return Nan::ThrowError("forceShutdown failed somehow");
+  }
+}
+
+void Server::ShutdownServer() {
+  Nan::HandleScope scope;
+  if (this->wrapped_server != NULL) {
+    if (shutdown_callback == NULL) {
+      Local<FunctionTemplate> callback_tpl =
+          Nan::New<FunctionTemplate>(ServerShutdownCallback);
+      shutdown_callback =
+          new Callback(Nan::GetFunction(callback_tpl).ToLocalChecked());
+    }
+
+    ServerShutdownOp *op = new ServerShutdownOp(this->wrapped_server);
+    unique_ptr<OpVec> ops(new OpVec());
+    ops->push_back(unique_ptr<Op>(op));
+
+    grpc_server_shutdown_and_notify(
+        this->wrapped_server, GetCompletionQueue(),
+        new struct tag(new Callback(**shutdown_callback), ops.release(), NULL,
+                       Nan::Null()));
+    grpc_server_cancel_all_calls(this->wrapped_server);
+    CompletionQueueNext();
+    this->wrapped_server = NULL;
+  }
+}
+
 NAN_METHOD(Server::New) {
   /* If this is not a constructor call, make a constructor call and return
      the result */
diff --git a/src/node/ext/server_generic.cc b/src/node/ext/server_generic.cc
deleted file mode 100644
index 088273d..0000000
--- a/src/node/ext/server_generic.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *
- * Copyright 2017, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_UV
-
-#include "server.h"
-
-#include <nan.h>
-#include <node.h>
-#include "grpc/grpc.h"
-#include "grpc/support/time.h"
-
-namespace grpc {
-namespace node {
-
-Server::Server(grpc_server *server) : wrapped_server(server) {
-  grpc_completion_queue_attributes attrs = {
-      GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_NON_LISTENING};
-  shutdown_queue = grpc_completion_queue_create(
-      grpc_completion_queue_factory_lookup(&attrs), &attrs, NULL);
-  grpc_server_register_completion_queue(server, shutdown_queue, NULL);
-}
-
-Server::~Server() {
-  this->ShutdownServer();
-  grpc_completion_queue_shutdown(this->shutdown_queue);
-  grpc_completion_queue_destroy(this->shutdown_queue);
-}
-
-void Server::ShutdownServer() {
-  if (this->wrapped_server != NULL) {
-    grpc_server_shutdown_and_notify(this->wrapped_server, this->shutdown_queue,
-                                    NULL);
-    grpc_server_cancel_all_calls(this->wrapped_server);
-    grpc_completion_queue_pluck(this->shutdown_queue, NULL,
-                                gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-    grpc_server_destroy(this->wrapped_server);
-    this->wrapped_server = NULL;
-  }
-}
-
-}  // namespace grpc
-}  // namespace node
-
-#endif /* GRPC_UV */
diff --git a/src/node/ext/server_uv.cc b/src/node/ext/server_uv.cc
deleted file mode 100644
index 709921b..0000000
--- a/src/node/ext/server_uv.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *
- * Copyright 2017, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifdef GRPC_UV
-
-#include "server.h"
-
-#include <nan.h>
-#include <node.h>
-#include "grpc/grpc.h"
-#include "grpc/support/time.h"
-
-#include "call.h"
-#include "completion_queue.h"
-
-namespace grpc {
-namespace node {
-
-using Nan::Callback;
-using Nan::MaybeLocal;
-
-using v8::External;
-using v8::Function;
-using v8::FunctionTemplate;
-using v8::Local;
-using v8::Object;
-using v8::Value;
-
-static Callback *shutdown_callback = NULL;
-
-class ServerShutdownOp : public Op {
- public:
-  ServerShutdownOp(grpc_server *server) : server(server) {}
-
-  ~ServerShutdownOp() {}
-
-  Local<Value> GetNodeValue() const { return Nan::Null(); }
-
-  bool ParseOp(Local<Value> value, grpc_op *out) { return true; }
-  bool IsFinalOp() { return false; }
-  void OnComplete(bool success) {
-    /* Because cancel_all_calls was called, we assume that shutdown_and_notify
-       completes successfully */
-    grpc_server_destroy(server);
-  }
-
-  grpc_server *server;
-
- protected:
-  std::string GetTypeString() const { return "shutdown"; }
-};
-
-Server::Server(grpc_server *server) : wrapped_server(server) {}
-
-Server::~Server() { this->ShutdownServer(); }
-
-NAN_METHOD(ServerShutdownCallback) {
-  if (!info[0]->IsNull()) {
-    return Nan::ThrowError("forceShutdown failed somehow");
-  }
-}
-
-void Server::ShutdownServer() {
-  Nan::HandleScope scope;
-  if (this->wrapped_server != NULL) {
-    if (shutdown_callback == NULL) {
-      Local<FunctionTemplate> callback_tpl =
-          Nan::New<FunctionTemplate>(ServerShutdownCallback);
-      shutdown_callback =
-          new Callback(Nan::GetFunction(callback_tpl).ToLocalChecked());
-    }
-
-    ServerShutdownOp *op = new ServerShutdownOp(this->wrapped_server);
-    unique_ptr<OpVec> ops(new OpVec());
-    ops->push_back(unique_ptr<Op>(op));
-
-    grpc_server_shutdown_and_notify(
-        this->wrapped_server, GetCompletionQueue(),
-        new struct tag(new Callback(**shutdown_callback), ops.release(), NULL,
-                       Nan::Null()));
-    grpc_server_cancel_all_calls(this->wrapped_server);
-    CompletionQueueNext();
-    this->wrapped_server = NULL;
-  }
-}
-
-}  // namespace grpc
-}  // namespace node
-
-#endif /* GRPC_UV */
diff --git a/src/node/index.js b/src/node/index.js
index 071bfd7..76ab174 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -31,6 +31,10 @@
  *
  */
 
+/**
+ * @module
+ */
+
 'use strict';
 
 var path = require('path');
@@ -256,5 +260,10 @@
 exports.waitForClientReady = client.waitForClientReady;
 
 exports.closeClient = function closeClient(client_obj) {
-  client.getClientChannel(client_obj).close();
+  client.Client.prototype.close.apply(client_obj);
 };
+
+/**
+ * @see module:src/client.Client
+ */
+exports.Client = client.Client;
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 1aaf35c..43502da 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -37,7 +37,7 @@
  * This module contains the factory method for creating Client classes, and the
  * method calling code for all types of methods.
  *
- * For example, to create a client and call a method on it:
+ * @example <caption>Create a client and call a method on it</caption>
  *
  * var proto_obj = grpc.load(proto_file_path);
  * var Client = proto_obj.package.subpackage.ServiceName;
@@ -68,14 +68,33 @@
 var util = require('util');
 var version = require('../../../package.json').version;
 
+util.inherits(ClientUnaryCall, EventEmitter);
+
+/**
+ * An EventEmitter. Used for unary calls
+ * @constructor
+ * @extends external:EventEmitter
+ * @param {grpc.Call} call The call object associated with the request
+ */
+function ClientUnaryCall(call) {
+  EventEmitter.call(this);
+  this.call = call;
+}
+
 util.inherits(ClientWritableStream, Writable);
 
 /**
  * A stream that the client can write to. Used for calls that are streaming from
  * the client side.
  * @constructor
+ * @extends external:Writable
+ * @borrows module:src/client~ClientUnaryCall#cancel as
+ *     module:src/client~ClientWritableStream#cancel
+ * @borrows module:src/client~ClientUnaryCall#getPeer as
+ *     module:src/client~ClientWritableStream#getPeer
  * @param {grpc.Call} call The call object to send data with
- * @param {function(*):Buffer=} serialize Serialization function for writes.
+ * @param {module:src/common~serialize=} [serialize=identity] Serialization
+ *     function for writes.
  */
 function ClientWritableStream(call, serialize) {
   Writable.call(this, {objectMode: true});
@@ -134,8 +153,14 @@
  * A stream that the client can read from. Used for calls that are streaming
  * from the server side.
  * @constructor
+ * @extends external:Readable
+ * @borrows module:src/client~ClientUnaryCall#cancel as
+ *     module:src/client~ClientReadableStream#cancel
+ * @borrows module:src/client~ClientUnaryCall#getPeer as
+ *     module:src/client~ClientReadableStream#getPeer
  * @param {grpc.Call} call The call object to read data with
- * @param {function(Buffer):*=} deserialize Deserialization function for reads
+ * @param {module:src/common~deserialize=} [deserialize=identity]
+ *     Deserialization function for reads
  */
 function ClientReadableStream(call, deserialize) {
   Readable.call(this, {objectMode: true});
@@ -155,6 +180,7 @@
  * parameter indicates that the call should end with that status. status
  * defaults to OK if not provided.
  * @param {Object!} status The status that the call should end with
+ * @access private
  */
 function _readsDone(status) {
   /* jshint validthis: true */
@@ -173,6 +199,7 @@
 
 /**
  * Called to indicate that we have received a status from the server.
+ * @access private
  */
 function _receiveStatus(status) {
   /* jshint validthis: true */
@@ -185,6 +212,7 @@
 /**
  * If we have both processed all incoming messages and received the status from
  * the server, emit the status. Otherwise, do nothing.
+ * @access private
  */
 function _emitStatusIfDone() {
   /* jshint validthis: true */
@@ -270,10 +298,16 @@
  * A stream that the client can read from or write to. Used for calls with
  * duplex streaming.
  * @constructor
+ * @extends external:Duplex
+ * @borrows module:src/client~ClientUnaryCall#cancel as
+ *     module:src/client~ClientDuplexStream#cancel
+ * @borrows module:src/client~ClientUnaryCall#getPeer as
+ *     module:src/client~ClientDuplexStream#getPeer
  * @param {grpc.Call} call Call object to proxy
- * @param {function(*):Buffer=} serialize Serialization function for requests
- * @param {function(Buffer):*=} deserialize Deserialization function for
- *     responses
+ * @param {module:src/common~serialize=} [serialize=identity] Serialization
+ *     function for requests
+ * @param {module:src/common~deserialize=} [deserialize=identity]
+ *     Deserialization function for responses
  */
 function ClientDuplexStream(call, serialize, deserialize) {
   Duplex.call(this, {objectMode: true});
@@ -300,12 +334,14 @@
 
 /**
  * Cancel the ongoing call
+ * @alias module:src/client~ClientUnaryCall#cancel
  */
 function cancel() {
   /* jshint validthis: true */
   this.call.cancel();
 }
 
+ClientUnaryCall.prototype.cancel = cancel;
 ClientReadableStream.prototype.cancel = cancel;
 ClientWritableStream.prototype.cancel = cancel;
 ClientDuplexStream.prototype.cancel = cancel;
@@ -313,21 +349,49 @@
 /**
  * Get the endpoint this call/stream is connected to.
  * @return {string} The URI of the endpoint
+ * @alias module:src/client~ClientUnaryCall#getPeer
  */
 function getPeer() {
   /* jshint validthis: true */
   return this.call.getPeer();
 }
 
+ClientUnaryCall.prototype.getPeer = getPeer;
 ClientReadableStream.prototype.getPeer = getPeer;
 ClientWritableStream.prototype.getPeer = getPeer;
 ClientDuplexStream.prototype.getPeer = getPeer;
 
 /**
+ * Any client call type
+ * @typedef {(ClientUnaryCall|ClientReadableStream|
+ *            ClientWritableStream|ClientDuplexStream)}
+ *     module:src/client~Call
+ */
+
+/**
+ * Options that can be set on a call.
+ * @typedef {Object} module:src/client~CallOptions
+ * @property {(date|number)} deadline The deadline for the entire call to
+ *     complete. A value of Infinity indicates that no deadline should be set.
+ * @property {(string)} host Server hostname to set on the call. Only meaningful
+ *     if different from the server address used to construct the client.
+ * @property {module:src/client~Call} parent Parent call. Used in servers when
+ *     making a call as part of the process of handling a call. Used to
+ *     propagate some information automatically, as specified by
+ *     propagate_flags.
+ * @property {number} propagate_flags Indicates which properties of a parent
+ *     call should propagate to this call. Bitwise combination of flags in
+ *     [grpc.propagate]{@link module:index.propagate}.
+ * @property {module:src/credentials~CallCredentials} credentials The
+ *     credentials that should be used to make this particular call.
+ */
+
+/**
  * Get a call object built with the provided options. Keys for options are
  * 'deadline', which takes a date or number, and 'host', which takes a string
  * and overrides the hostname to connect to.
- * @param {Object} options Options map.
+ * @access private
+ * @param {module:src/client~CallOptions=} options Options object.
  */
 function getCall(channel, method, options) {
   var deadline;
@@ -354,315 +418,380 @@
 }
 
 /**
- * Get a function that can make unary requests to the specified method.
- * @param {string} method The name of the method to request
- * @param {function(*):Buffer} serialize The serialization function for inputs
- * @param {function(Buffer)} deserialize The deserialization function for
- *     outputs
- * @return {Function} makeUnaryRequest
+ * A generic gRPC client. Primarily useful as a base class for generated clients
+ * @alias module:src/client.Client
+ * @constructor
+ * @param {string} address Server address to connect to
+ * @param {module:src/credentials~ChannelCredentials} credentials Credentials to
+ *     use to connect to the server
+ * @param {Object} options Options to apply to channel creation
  */
-function makeUnaryRequestFunction(method, serialize, deserialize) {
-  /**
-   * Make a unary request with this method on the given channel with the given
-   * argument, callback, etc.
-   * @this {Client} Client object. Must have a channel member.
-   * @param {*} argument The argument to the call. Should be serializable with
-   *     serialize
-   * @param {Metadata=} metadata Metadata to add to the call
-   * @param {Object=} options Options map
-   * @param {function(?Error, value=)} callback The callback to for when the
-   *     response is received
-   * @return {EventEmitter} An event emitter for stream related events
+var Client = exports.Client = function Client(address, credentials, options) {
+  if (!options) {
+    options = {};
+  }
+  /* Append the grpc-node user agent string after the application user agent
+   * string, and put the combination at the beginning of the user agent string
    */
-  function makeUnaryRequest(argument, metadata, options, callback) {
-    /* jshint validthis: true */
-    /* While the arguments are listed in the function signature, those variables
-     * are not used directly. Instead, ArgueJS processes the arguments
-     * object. This allows for simple handling of optional arguments in the
-     * middle of the argument list, and also provides type checking. */
-    var args = arguejs({argument: null, metadata: [Metadata, new Metadata()],
-                        options: [Object], callback: Function}, arguments);
-    var emitter = new EventEmitter();
-    var call = getCall(this.$channel, method, args.options);
-    metadata = args.metadata.clone();
-    emitter.cancel = function cancel() {
-      call.cancel();
-    };
-    emitter.getPeer = function getPeer() {
-      return call.getPeer();
-    };
-    var client_batch = {};
-    var message = serialize(args.argument);
-    if (args.options) {
-      message.grpcWriteFlags = args.options.flags;
-    }
+  if (options['grpc.primary_user_agent']) {
+    options['grpc.primary_user_agent'] += ' ';
+  } else {
+    options['grpc.primary_user_agent'] = '';
+  }
+  options['grpc.primary_user_agent'] += 'grpc-node/' + version;
+  /* Private fields use $ as a prefix instead of _ because it is an invalid
+   * prefix of a method name */
+  this.$channel = new grpc.Channel(address, credentials, options);
+};
 
-    client_batch[grpc.opType.SEND_INITIAL_METADATA] =
-        metadata._getCoreRepresentation();
-    client_batch[grpc.opType.SEND_MESSAGE] = message;
-    client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
-    client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
-    client_batch[grpc.opType.RECV_MESSAGE] = true;
-    client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
-    call.startBatch(client_batch, function(err, response) {
-      response.status.metadata = Metadata._fromCoreRepresentation(
-          response.status.metadata);
-      var status = response.status;
-      var error;
-      var deserialized;
-      emitter.emit('metadata', Metadata._fromCoreRepresentation(
-          response.metadata));
-      if (status.code === grpc.status.OK) {
-        if (err) {
-          // Got a batch error, but OK status. Something went wrong
-          args.callback(err);
-          return;
-        } else {
-          try {
-            deserialized = deserialize(response.read);
-          } catch (e) {
-            /* Change status to indicate bad server response. This will result
-             * in passing an error to the callback */
-            status = {
-              code: grpc.status.INTERNAL,
-              details: 'Failed to parse server response'
-            };
-          }
+/**
+ * @typedef {Error} module:src/client.Client~ServiceError
+ * @property {number} code The error code, a key of
+ *     [grpc.status]{@link module:src/client.status}
+ * @property {module:metadata.Metadata} metadata Metadata sent with the status
+ *     by the server, if any
+ */
+
+/**
+ * @callback module:src/client.Client~requestCallback
+ * @param {?module:src/client.Client~ServiceError} error The error, if the call
+ *     failed
+ * @param {*} value The response value, if the call succeeded
+ */
+
+/**
+ * Make a unary request to the given method, using the given serialize
+ * and deserialize functions, with the given argument.
+ * @param {string} method The name of the method to request
+ * @param {module:src/common~serialize} serialize The serialization function for
+ *     inputs
+ * @param {module:src/common~deserialize} deserialize The deserialization
+ *     function for outputs
+ * @param {*} argument The argument to the call. Should be serializable with
+ *     serialize
+ * @param {module:src/metadata.Metadata=} metadata Metadata to add to the call
+ * @param {module:src/client~CallOptions=} options Options map
+ * @param {module:src/client.Client~requestCallback} callback The callback to
+ *     for when the response is received
+ * @return {EventEmitter} An event emitter for stream related events
+ */
+Client.prototype.makeUnaryRequest = function(method, serialize, deserialize,
+                                             argument, metadata, options,
+                                             callback) {
+  /* While the arguments are listed in the function signature, those variables
+   * are not used directly. Instead, ArgueJS processes the arguments
+   * object. This allows for simple handling of optional arguments in the
+   * middle of the argument list, and also provides type checking. */
+  var args = arguejs({method: String, serialize: Function,
+                      deserialize: Function,
+                      argument: null, metadata: [Metadata, new Metadata()],
+                      options: [Object], callback: Function}, arguments);
+  var call = getCall(this.$channel, method, args.options);
+  var emitter = new ClientUnaryCall(call);
+  metadata = args.metadata.clone();
+  var client_batch = {};
+  var message = serialize(args.argument);
+  if (args.options) {
+    message.grpcWriteFlags = args.options.flags;
+  }
+
+  client_batch[grpc.opType.SEND_INITIAL_METADATA] =
+      metadata._getCoreRepresentation();
+  client_batch[grpc.opType.SEND_MESSAGE] = message;
+  client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
+  client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+  client_batch[grpc.opType.RECV_MESSAGE] = true;
+  client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+  call.startBatch(client_batch, function(err, response) {
+    response.status.metadata = Metadata._fromCoreRepresentation(
+        response.status.metadata);
+    var status = response.status;
+    var error;
+    var deserialized;
+    emitter.emit('metadata', Metadata._fromCoreRepresentation(
+        response.metadata));
+    if (status.code === grpc.status.OK) {
+      if (err) {
+        // Got a batch error, but OK status. Something went wrong
+        args.callback(err);
+        return;
+      } else {
+        try {
+          deserialized = deserialize(response.read);
+        } catch (e) {
+          /* Change status to indicate bad server response. This will result
+           * in passing an error to the callback */
+          status = {
+            code: grpc.status.INTERNAL,
+            details: 'Failed to parse server response'
+          };
         }
       }
-      if (status.code !== grpc.status.OK) {
-        error = new Error(status.details);
-        error.code = status.code;
-        error.metadata = status.metadata;
-        args.callback(error);
-      } else {
-        args.callback(null, deserialized);
-      }
-      emitter.emit('status', status);
-    });
-    return emitter;
-  }
-  return makeUnaryRequest;
-}
+    }
+    if (status.code !== grpc.status.OK) {
+      error = new Error(status.details);
+      error.code = status.code;
+      error.metadata = status.metadata;
+      args.callback(error);
+    } else {
+      args.callback(null, deserialized);
+    }
+    emitter.emit('status', status);
+  });
+  return emitter;
+};
 
 /**
- * Get a function that can make client stream requests to the specified method.
+ * Make a client stream request to the given method, using the given serialize
+ * and deserialize functions, with the given argument.
  * @param {string} method The name of the method to request
- * @param {function(*):Buffer} serialize The serialization function for inputs
- * @param {function(Buffer)} deserialize The deserialization function for
- *     outputs
- * @return {Function} makeClientStreamRequest
+ * @param {module:src/common~serialize} serialize The serialization function for
+ *     inputs
+ * @param {module:src/common~deserialize} deserialize The deserialization
+ *     function for outputs
+ * @param {module:src/metadata.Metadata=} metadata Array of metadata key/value
+ *     pairs to add to the call
+ * @param {module:src/client~CallOptions=} options Options map
+ * @param {Client~requestCallback} callback The callback to for when the
+ *     response is received
+ * @return {module:src/client~ClientWritableStream} An event emitter for stream
+ *     related events
  */
-function makeClientStreamRequestFunction(method, serialize, deserialize) {
-  /**
-   * Make a client stream request with this method on the given channel with the
-   * given callback, etc.
-   * @this {Client} Client object. Must have a channel member.
-   * @param {Metadata=} metadata Array of metadata key/value pairs to add to the
-   *     call
-   * @param {Object=} options Options map
-   * @param {function(?Error, value=)} callback The callback to for when the
-   *     response is received
-   * @return {EventEmitter} An event emitter for stream related events
-   */
-  function makeClientStreamRequest(metadata, options, callback) {
-    /* jshint validthis: true */
-    /* While the arguments are listed in the function signature, those variables
-     * are not used directly. Instead, ArgueJS processes the arguments
-     * object. This allows for simple handling of optional arguments in the
-     * middle of the argument list, and also provides type checking. */
-    var args = arguejs({metadata: [Metadata, new Metadata()],
-                        options: [Object], callback: Function}, arguments);
-    var call = getCall(this.$channel, method, args.options);
-    metadata = args.metadata.clone();
-    var stream = new ClientWritableStream(call, serialize);
-    var metadata_batch = {};
-    metadata_batch[grpc.opType.SEND_INITIAL_METADATA] =
-        metadata._getCoreRepresentation();
-    metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
-    call.startBatch(metadata_batch, function(err, response) {
+Client.prototype.makeClientStreamRequest = function(method, serialize,
+                                                      deserialize, metadata,
+                                                      options, callback) {
+  /* While the arguments are listed in the function signature, those variables
+   * are not used directly. Instead, ArgueJS processes the arguments
+   * object. This allows for simple handling of optional arguments in the
+   * middle of the argument list, and also provides type checking. */
+  var args = arguejs({method:String, serialize: Function,
+                      deserialize: Function,
+                      metadata: [Metadata, new Metadata()],
+                      options: [Object], callback: Function}, arguments);
+  var call = getCall(this.$channel, method, args.options);
+  metadata = args.metadata.clone();
+  var stream = new ClientWritableStream(call, serialize);
+  var metadata_batch = {};
+  metadata_batch[grpc.opType.SEND_INITIAL_METADATA] =
+      metadata._getCoreRepresentation();
+  metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+  call.startBatch(metadata_batch, function(err, response) {
+    if (err) {
+      // The call has stopped for some reason. A non-OK status will arrive
+      // in the other batch.
+      return;
+    }
+    stream.emit('metadata', Metadata._fromCoreRepresentation(
+        response.metadata));
+  });
+  var client_batch = {};
+  client_batch[grpc.opType.RECV_MESSAGE] = true;
+  client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+  call.startBatch(client_batch, function(err, response) {
+    response.status.metadata = Metadata._fromCoreRepresentation(
+        response.status.metadata);
+    var status = response.status;
+    var error;
+    var deserialized;
+    if (status.code === grpc.status.OK) {
       if (err) {
-        // The call has stopped for some reason. A non-OK status will arrive
-        // in the other batch.
+        // Got a batch error, but OK status. Something went wrong
+        args.callback(err);
         return;
-      }
-      stream.emit('metadata', Metadata._fromCoreRepresentation(
-          response.metadata));
-    });
-    var client_batch = {};
-    client_batch[grpc.opType.RECV_MESSAGE] = true;
-    client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
-    call.startBatch(client_batch, function(err, response) {
-      response.status.metadata = Metadata._fromCoreRepresentation(
-          response.status.metadata);
-      var status = response.status;
-      var error;
-      var deserialized;
-      if (status.code === grpc.status.OK) {
-        if (err) {
-          // Got a batch error, but OK status. Something went wrong
-          args.callback(err);
-          return;
-        } else {
-          try {
-            deserialized = deserialize(response.read);
-          } catch (e) {
-            /* Change status to indicate bad server response. This will result
-             * in passing an error to the callback */
-            status = {
-              code: grpc.status.INTERNAL,
-              details: 'Failed to parse server response'
-            };
-          }
+      } else {
+        try {
+          deserialized = deserialize(response.read);
+        } catch (e) {
+          /* Change status to indicate bad server response. This will result
+           * in passing an error to the callback */
+          status = {
+            code: grpc.status.INTERNAL,
+            details: 'Failed to parse server response'
+          };
         }
       }
-      if (status.code !== grpc.status.OK) {
-        error = new Error(response.status.details);
-        error.code = status.code;
-        error.metadata = status.metadata;
-        args.callback(error);
-      } else {
-        args.callback(null, deserialized);
-      }
-      stream.emit('status', status);
-    });
-    return stream;
-  }
-  return makeClientStreamRequest;
-}
-
-/**
- * Get a function that can make server stream requests to the specified method.
- * @param {string} method The name of the method to request
- * @param {function(*):Buffer} serialize The serialization function for inputs
- * @param {function(Buffer)} deserialize The deserialization function for
- *     outputs
- * @return {Function} makeServerStreamRequest
- */
-function makeServerStreamRequestFunction(method, serialize, deserialize) {
-  /**
-   * Make a server stream request with this method on the given channel with the
-   * given argument, etc.
-   * @this {SurfaceClient} Client object. Must have a channel member.
-   * @param {*} argument The argument to the call. Should be serializable with
-   *     serialize
-   * @param {Metadata=} metadata Array of metadata key/value pairs to add to the
-   *     call
-   * @param {Object} options Options map
-   * @return {EventEmitter} An event emitter for stream related events
-   */
-  function makeServerStreamRequest(argument, metadata, options) {
-    /* jshint validthis: true */
-    /* While the arguments are listed in the function signature, those variables
-     * are not used directly. Instead, ArgueJS processes the arguments
-     * object. */
-    var args = arguejs({argument: null, metadata: [Metadata, new Metadata()],
-                        options: [Object]}, arguments);
-    var call = getCall(this.$channel, method, args.options);
-    metadata = args.metadata.clone();
-    var stream = new ClientReadableStream(call, deserialize);
-    var start_batch = {};
-    var message = serialize(args.argument);
-    if (args.options) {
-      message.grpcWriteFlags = args.options.flags;
     }
-    start_batch[grpc.opType.SEND_INITIAL_METADATA] =
-        metadata._getCoreRepresentation();
-    start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
-    start_batch[grpc.opType.SEND_MESSAGE] = message;
-    start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
-    call.startBatch(start_batch, function(err, response) {
-      if (err) {
-        // The call has stopped for some reason. A non-OK status will arrive
-        // in the other batch.
-        return;
-      }
-      stream.emit('metadata', Metadata._fromCoreRepresentation(
-          response.metadata));
-    });
-    var status_batch = {};
-    status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
-    call.startBatch(status_batch, function(err, response) {
-      if (err) {
-        stream.emit('error', err);
-        return;
-      }
-      response.status.metadata = Metadata._fromCoreRepresentation(
-          response.status.metadata);
-      stream._receiveStatus(response.status);
-    });
-    return stream;
-  }
-  return makeServerStreamRequest;
-}
+    if (status.code !== grpc.status.OK) {
+      error = new Error(response.status.details);
+      error.code = status.code;
+      error.metadata = status.metadata;
+      args.callback(error);
+    } else {
+      args.callback(null, deserialized);
+    }
+    stream.emit('status', status);
+  });
+  return stream;
+};
 
 /**
- * Get a function that can make bidirectional stream requests to the specified
- * method.
+ * Make a server stream request to the given method, with the given serialize
+ * and deserialize function, using the given argument
  * @param {string} method The name of the method to request
- * @param {function(*):Buffer} serialize The serialization function for inputs
- * @param {function(Buffer)} deserialize The deserialization function for
- *     outputs
- * @return {Function} makeBidiStreamRequest
+ * @param {module:src/common~serialize} serialize The serialization function for
+ *     inputs
+ * @param {module:src/common~deserialize} deserialize The deserialization
+ *     function for outputs
+ * @param {*} argument The argument to the call. Should be serializable with
+ *     serialize
+ * @param {module:src/metadata.Metadata=} metadata Array of metadata key/value
+ *     pairs to add to the call
+ * @param {module:src/client~CallOptions=} options Options map
+ * @return {module:src/client~ClientReadableStream} An event emitter for stream
+ *     related events
  */
-function makeBidiStreamRequestFunction(method, serialize, deserialize) {
-  /**
-   * Make a bidirectional stream request with this method on the given channel.
-   * @this {SurfaceClient} Client object. Must have a channel member.
-   * @param {Metadata=} metadata Array of metadata key/value pairs to add to the
-   *     call
-   * @param {Options} options Options map
-   * @return {EventEmitter} An event emitter for stream related events
-   */
-  function makeBidiStreamRequest(metadata, options) {
-    /* jshint validthis: true */
-    /* While the arguments are listed in the function signature, those variables
-     * are not used directly. Instead, ArgueJS processes the arguments
-     * object. */
-    var args = arguejs({metadata: [Metadata, new Metadata()],
-                        options: [Object]}, arguments);
-    var call = getCall(this.$channel, method, args.options);
-    metadata = args.metadata.clone();
-    var stream = new ClientDuplexStream(call, serialize, deserialize);
-    var start_batch = {};
-    start_batch[grpc.opType.SEND_INITIAL_METADATA] =
-        metadata._getCoreRepresentation();
-    start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
-    call.startBatch(start_batch, function(err, response) {
-      if (err) {
-        // The call has stopped for some reason. A non-OK status will arrive
-        // in the other batch.
-        return;
-      }
-      stream.emit('metadata', Metadata._fromCoreRepresentation(
-          response.metadata));
-    });
-    var status_batch = {};
-    status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
-    call.startBatch(status_batch, function(err, response) {
-      if (err) {
-        stream.emit('error', err);
-        return;
-      }
-      response.status.metadata = Metadata._fromCoreRepresentation(
-          response.status.metadata);
-      stream._receiveStatus(response.status);
-    });
-    return stream;
+Client.prototype.makeServerStreamRequest = function(method, serialize,
+                                                    deserialize, argument,
+                                                    metadata, options) {
+  /* While the arguments are listed in the function signature, those variables
+   * are not used directly. Instead, ArgueJS processes the arguments
+   * object. */
+  var args = arguejs({method:String, serialize: Function,
+                      deserialize: Function,
+                      argument: null, metadata: [Metadata, new Metadata()],
+                      options: [Object]}, arguments);
+  var call = getCall(this.$channel, method, args.options);
+  metadata = args.metadata.clone();
+  var stream = new ClientReadableStream(call, deserialize);
+  var start_batch = {};
+  var message = serialize(args.argument);
+  if (args.options) {
+    message.grpcWriteFlags = args.options.flags;
   }
-  return makeBidiStreamRequest;
-}
+  start_batch[grpc.opType.SEND_INITIAL_METADATA] =
+      metadata._getCoreRepresentation();
+  start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+  start_batch[grpc.opType.SEND_MESSAGE] = message;
+  start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
+  call.startBatch(start_batch, function(err, response) {
+    if (err) {
+      // The call has stopped for some reason. A non-OK status will arrive
+      // in the other batch.
+      return;
+    }
+    stream.emit('metadata', Metadata._fromCoreRepresentation(
+        response.metadata));
+  });
+  var status_batch = {};
+  status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+  call.startBatch(status_batch, function(err, response) {
+    if (err) {
+      stream.emit('error', err);
+      return;
+    }
+    response.status.metadata = Metadata._fromCoreRepresentation(
+        response.status.metadata);
+    stream._receiveStatus(response.status);
+  });
+  return stream;
+};
 
 
 /**
+ * Make a bidirectional stream request with this method on the given channel.
+ * @param {string} method The name of the method to request
+ * @param {module:src/common~serialize} serialize The serialization function for
+ *     inputs
+ * @param {module:src/common~deserialize} deserialize The deserialization
+ *     function for outputs
+ * @param {module:src/metadata.Metadata=} metadata Array of metadata key/value
+ *     pairs to add to the call
+ * @param {module:src/client~CallOptions=} options Options map
+ * @return {module:src/client~ClientDuplexStream} An event emitter for stream
+ *     related events
+ */
+Client.prototype.makeBidiStreamRequest = function(method, serialize,
+                                                  deserialize, metadata,
+                                                  options) {
+  /* While the arguments are listed in the function signature, those variables
+   * are not used directly. Instead, ArgueJS processes the arguments
+   * object. */
+  var args = arguejs({method:String, serialize: Function,
+                      deserialize: Function,
+                      metadata: [Metadata, new Metadata()],
+                      options: [Object]}, arguments);
+  var call = getCall(this.$channel, method, args.options);
+  metadata = args.metadata.clone();
+  var stream = new ClientDuplexStream(call, serialize, deserialize);
+  var start_batch = {};
+  start_batch[grpc.opType.SEND_INITIAL_METADATA] =
+      metadata._getCoreRepresentation();
+  start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+  call.startBatch(start_batch, function(err, response) {
+    if (err) {
+      // The call has stopped for some reason. A non-OK status will arrive
+      // in the other batch.
+      return;
+    }
+    stream.emit('metadata', Metadata._fromCoreRepresentation(
+        response.metadata));
+  });
+  var status_batch = {};
+  status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+  call.startBatch(status_batch, function(err, response) {
+    if (err) {
+      stream.emit('error', err);
+      return;
+    }
+    response.status.metadata = Metadata._fromCoreRepresentation(
+        response.status.metadata);
+    stream._receiveStatus(response.status);
+  });
+  return stream;
+};
+
+Client.prototype.close = function() {
+  this.$channel.close();
+};
+
+/**
+ * Return the underlying channel object for the specified client
+ * @return {Channel} The channel
+ */
+Client.prototype.getChannel = function() {
+  return this.$channel;
+};
+
+/**
+ * Wait for the client to be ready. The callback will be called when the
+ * client has successfully connected to the server, and it will be called
+ * with an error if the attempt to connect to the server has unrecoverablly
+ * failed or if the deadline expires. This function will make the channel
+ * start connecting if it has not already done so.
+ * @param {(Date|Number)} deadline When to stop waiting for a connection. Pass
+ *     Infinity to wait forever.
+ * @param {function(Error)} callback The callback to call when done attempting
+ *     to connect.
+ */
+Client.prototype.waitForReady = function(deadline, callback) {
+  var self = this;
+  var checkState = function(err) {
+    if (err) {
+      callback(new Error('Failed to connect before the deadline'));
+      return;
+    }
+    var new_state = self.$channel.getConnectivityState(true);
+    if (new_state === grpc.connectivityState.READY) {
+      callback();
+    } else if (new_state === grpc.connectivityState.FATAL_FAILURE) {
+      callback(new Error('Failed to connect to server'));
+    } else {
+      self.$channel.watchConnectivityState(new_state, deadline, checkState);
+    }
+  };
+  checkState();
+};
+
+/**
  * Map with short names for each of the requester maker functions. Used in
  * makeClientConstructor
+ * @access private
  */
-var requester_makers = {
-  unary: makeUnaryRequestFunction,
-  server_stream: makeServerStreamRequestFunction,
-  client_stream: makeClientStreamRequestFunction,
-  bidi: makeBidiStreamRequestFunction
+var requester_funcs = {
+  unary: Client.prototype.makeUnaryRequest,
+  server_stream: Client.prototype.makeServerStreamRequest,
+  client_stream: Client.prototype.makeClientStreamRequest,
+  bidi: Client.prototype.makeBidiStreamRequest
 };
 
 function getDefaultValues(metadata, options) {
@@ -675,6 +804,7 @@
 /**
  * Map with wrappers for each type of requester function to make it use the old
  * argument order with optional arguments after the callback.
+ * @access private
  */
 var deprecated_request_wrap = {
   unary: function(makeUnaryRequest) {
@@ -700,55 +830,33 @@
 };
 
 /**
- * Creates a constructor for a client with the given methods. The methods object
- * maps method name to an object with the following keys:
- * path: The path on the server for accessing the method. For example, for
- *     protocol buffers, we use "/service_name/method_name"
- * requestStream: bool indicating whether the client sends a stream
- * resonseStream: bool indicating whether the server sends a stream
- * requestSerialize: function to serialize request objects
- * responseDeserialize: function to deserialize response objects
- * @param {Object} methods An object mapping method names to method attributes
+ * Creates a constructor for a client with the given methods, as specified in
+ * the methods argument.
+ * @param {module:src/common~ServiceDefinition} methods An object mapping
+ *     method names to method attributes
  * @param {string} serviceName The fully qualified name of the service
- * @param {Object} class_options An options object. Currently only uses the key
- *     deprecatedArgumentOrder, a boolean that Indicates that the old argument
- *     order should be used for methods, with optional arguments at the end
- *     instead of the callback at the end. Defaults to false. This option is
- *     only a temporary stopgap measure to smooth an API breakage.
+ * @param {Object} class_options An options object.
+ * @param {boolean=} [class_options.deprecatedArgumentOrder=false] Indicates
+ *     that the old argument order should be used for methods, with optional
+ *     arguments at the end instead of the callback at the end. This option
+ *     is only a temporary stopgap measure to smooth an API breakage.
  *     It is deprecated, and new code should not use it.
- * @return {function(string, Object)} New client constructor
+ * @return {function(string, Object)} New client constructor, which is a
+ *     subclass of [grpc.Client]{@link module:src/client.Client}, and has the
+ *     same arguments as that constructor.
  */
 exports.makeClientConstructor = function(methods, serviceName,
                                          class_options) {
   if (!class_options) {
     class_options = {};
   }
-  /**
-   * Create a client with the given methods
-   * @constructor
-   * @param {string} address The address of the server to connect to
-   * @param {grpc.Credentials} credentials Credentials to use to connect
-   *     to the server
-   * @param {Object} options Options to pass to the underlying channel
-   */
-  function Client(address, credentials, options) {
-    if (!options) {
-      options = {};
-    }
-    /* Append the grpc-node user agent string after the application user agent
-     * string, and put the combination at the beginning of the user agent string
-     */
-    if (options['grpc.primary_user_agent']) {
-      options['grpc.primary_user_agent'] += ' ';
-    } else {
-      options['grpc.primary_user_agent'] = '';
-    }
-    options['grpc.primary_user_agent'] += 'grpc-node/' + version;
-    /* Private fields use $ as a prefix instead of _ because it is an invalid
-     * prefix of a method name */
-    this.$channel = new grpc.Channel(address, credentials, options);
+
+  function ServiceClient(address, credentials, options) {
+    Client.call(this, address, credentials, options);
   }
 
+  util.inherits(ServiceClient, Client);
+
   _.each(methods, function(attrs, name) {
     var method_type;
     if (_.startsWith(name, '$')) {
@@ -769,20 +877,20 @@
     }
     var serialize = attrs.requestSerialize;
     var deserialize = attrs.responseDeserialize;
-    var method_func = requester_makers[method_type](
-        attrs.path, serialize, deserialize);
+    var method_func = _.partial(requester_funcs[method_type], attrs.path,
+                                serialize, deserialize);
     if (class_options.deprecatedArgumentOrder) {
-      Client.prototype[name] = deprecated_request_wrap(method_func);
+      ServiceClient.prototype[name] = deprecated_request_wrap(method_func);
     } else {
-      Client.prototype[name] = method_func;
+      ServiceClient.prototype[name] = method_func;
     }
     // Associate all provided attributes with the method
-    _.assign(Client.prototype[name], attrs);
+    _.assign(ServiceClient.prototype[name], attrs);
   });
 
-  Client.service = methods;
+  ServiceClient.service = methods;
 
-  return Client;
+  return ServiceClient;
 };
 
 /**
@@ -791,7 +899,7 @@
  * @return {Channel} The channel
  */
 exports.getClientChannel = function(client) {
-  return client.$channel;
+  return Client.prototype.getChannel.call(client);
 };
 
 /**
@@ -807,21 +915,7 @@
  *     to connect.
  */
 exports.waitForClientReady = function(client, deadline, callback) {
-  var checkState = function(err) {
-    if (err) {
-      callback(new Error('Failed to connect before the deadline'));
-      return;
-    }
-    var new_state = client.$channel.getConnectivityState(true);
-    if (new_state === grpc.connectivityState.READY) {
-      callback();
-    } else if (new_state === grpc.connectivityState.FATAL_FAILURE) {
-      callback(new Error('Failed to connect to server'));
-    } else {
-      client.$channel.watchConnectivityState(new_state, deadline, checkState);
-    }
-  };
-  checkState();
+  Client.prototype.waitForReady.call(client, deadline, callback);
 };
 
 /**
diff --git a/src/node/src/common.js b/src/node/src/common.js
index 757969d..4dad60e 100644
--- a/src/node/src/common.js
+++ b/src/node/src/common.js
@@ -43,7 +43,8 @@
 
 /**
  * Wrap a function to pass null-like values through without calling it. If no
- * function is given, just uses the identity;
+ * function is given, just uses the identity.
+ * @private
  * @param {?function} func The function to wrap
  * @return {function} The wrapped function
  */
@@ -90,3 +91,67 @@
   enumsAsStrings: true,
   deprecatedArgumentOrder: false
 };
+
+// JSDoc definitions that are used in multiple other modules
+
+/**
+ * The EventEmitter class in the event standard module
+ * @external EventEmitter
+ * @see https://nodejs.org/api/events.html#events_class_eventemitter
+ */
+
+/**
+ * The Readable class in the stream standard module
+ * @external Readable
+ * @see https://nodejs.org/api/stream.html#stream_readable_streams
+ */
+
+/**
+ * The Writable class in the stream standard module
+ * @external Writable
+ * @see https://nodejs.org/api/stream.html#stream_writable_streams
+ */
+
+/**
+ * The Duplex class in the stream standard module
+ * @external Duplex
+ * @see https://nodejs.org/api/stream.html#stream_class_stream_duplex
+ */
+
+/**
+ * A serialization function
+ * @callback module:src/common~serialize
+ * @param {*} value The value to serialize
+ * @return {Buffer} The value serialized as a byte sequence
+ */
+
+/**
+ * A deserialization function
+ * @callback module:src/common~deserialize
+ * @param {Buffer} data The byte sequence to deserialize
+ * @return {*} The data deserialized as a value
+ */
+
+/**
+ * An object that completely defines a service method signature.
+ * @typedef {Object} module:src/common~MethodDefinition
+ * @property {string} path The method's URL path
+ * @property {boolean} requestStream Indicates whether the method accepts
+ *     a stream of requests
+ * @property {boolean} responseStream Indicates whether the method returns
+ *     a stream of responses
+ * @property {module:src/common~serialize} requestSerialize Serialization
+ *     function for request values
+ * @property {module:src/common~serialize} responseSerialize Serialization
+ *     function for response values
+ * @property {module:src/common~deserialize} requestDeserialize Deserialization
+ *     function for request data
+ * @property {module:src/common~deserialize} responseDeserialize Deserialization
+ *     function for repsonse data
+ */
+
+/**
+ * An object that completely defines a service.
+ * @typedef {Object.<string, module:src/common~MethodDefinition>}
+ *     module:src/common~ServiceDefinition
+ */
diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js
index 612361b..92cf239 100644
--- a/src/node/src/metadata.js
+++ b/src/node/src/metadata.js
@@ -35,12 +35,7 @@
  * Metadata module
  *
  * This module defines the Metadata class, which represents header and trailer
- * metadata for gRPC calls. Here is an example of how to use it:
- *
- * var metadata = new metadata_module.Metadata();
- * metadata.set('key1', 'value1');
- * metadata.add('key1', 'value2');
- * metadata.get('key1') // returns ['value1', 'value2']
+ * metadata for gRPC calls.
  *
  * @module
  */
@@ -54,6 +49,12 @@
 /**
  * Class for storing metadata. Keys are normalized to lowercase ASCII.
  * @constructor
+ * @alias module:src/metadata.Metadata
+ * @example
+ * var metadata = new metadata_module.Metadata();
+ * metadata.set('key1', 'value1');
+ * metadata.add('key1', 'value2');
+ * metadata.get('key1') // returns ['value1', 'value2']
  */
 function Metadata() {
   this._internal_repr = {};
diff --git a/src/php/composer.json b/src/php/composer.json
index 24c17c3..a4fba7e 100644
--- a/src/php/composer.json
+++ b/src/php/composer.json
@@ -5,7 +5,7 @@
   "version": "1.4.0",
   "require": {
     "php": ">=5.5.0",
-    "google/protobuf": "^v3.1.0"
+    "google/protobuf": "^v3.3.0"
   },
   "require-dev": {
     "google/auth": "v0.9"
diff --git a/tools/run_tests/stress_test/cleanup_docker_images.sh b/src/proto/grpc/lb/v1/BUILD
old mode 100755
new mode 100644
similarity index 85%
rename from tools/run_tests/stress_test/cleanup_docker_images.sh
rename to src/proto/grpc/lb/v1/BUILD
index e424fcf..46d4f2d
--- a/tools/run_tests/stress_test/cleanup_docker_images.sh
+++ b/src/proto/grpc/lb/v1/BUILD
@@ -1,4 +1,3 @@
-#!/bin/bash
 # Copyright 2017, Google Inc.
 # All rights reserved.
 #
@@ -27,5 +26,14 @@
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-for img in `docker images | grep \<none\> | awk '{print  $3 }'` ; do docker rmi -f $img; done
 
+licenses(["notice"])  # 3-clause BSD
+
+package(default_visibility = ["//visibility:public"])
+
+load("//bazel:grpc_build_system.bzl", "grpc_proto_library")
+
+grpc_proto_library(
+    name = "load_balancer_proto",
+    srcs = ["load_balancer.proto"],
+)
diff --git a/src/proto/grpc/testing/control.proto b/src/proto/grpc/testing/control.proto
index acee866..02b156d 100644
--- a/src/proto/grpc/testing/control.proto
+++ b/src/proto/grpc/testing/control.proto
@@ -52,6 +52,9 @@
 enum RpcType {
   UNARY = 0;
   STREAMING = 1;
+  STREAMING_FROM_CLIENT = 2;
+  STREAMING_FROM_SERVER = 3;
+  STREAMING_BOTH_WAYS = 4;
 }
 
 // Parameters of poisson process distribution, which is a good representation
diff --git a/src/proto/grpc/testing/services.proto b/src/proto/grpc/testing/services.proto
index 969782d..85949ab 100644
--- a/src/proto/grpc/testing/services.proto
+++ b/src/proto/grpc/testing/services.proto
@@ -42,9 +42,22 @@
   // The server returns the client payload as-is.
   rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
 
-  // One request followed by one response.
-  // The server returns the client payload as-is.
+  // Repeated sequence of one request followed by one response.
+  // Should be called streaming ping-pong
+  // The server returns the client payload as-is on each response
   rpc StreamingCall(stream SimpleRequest) returns (stream SimpleResponse);
+
+  // Single-sided unbounded streaming from client to server
+  // The server returns the client payload as-is once the client does WritesDone
+  rpc StreamingFromClient(stream SimpleRequest) returns (SimpleResponse);
+
+  // Single-sided unbounded streaming from server to client
+  // The server repeatedly returns the client payload as-is
+  rpc StreamingFromServer(SimpleRequest) returns (stream SimpleResponse);
+
+  // Two-sided unbounded streaming between server to client
+  // Both sides send the content of their own choice to the other
+  rpc StreamingBothWays(stream SimpleRequest) returns (stream SimpleResponse);
 }
 
 service WorkerService {
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index 4f07280..f4ccb1a 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -284,9 +284,9 @@
                 stderr=subprocess.PIPE)
             make_out, make_err = make_process.communicate()
             if make_out and make_process.returncode != 0:
-                sys.stdout.write(make_out + '\n')
+                sys.stdout.write(str(make_out) + '\n')
             if make_err:
-                sys.stderr.write(make_err + '\n')
+                sys.stderr.write(str(make_err) + '\n')
             if make_process.returncode != 0:
                 raise Exception("make command failed!")
 
diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index 4960df3..2952dcd 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -61,13 +61,13 @@
     This method does not block.
 
     Returns:
-      True if the computation has not yet begun, will not be allowed to take
-        place, and determination of both was possible without blocking. False
-        under all other circumstances including but not limited to the
-        computation's already having begun, the computation's already having
-        finished, and the computation's having been scheduled for execution on a
-        remote system for which a determination of whether or not it commenced
-        before being cancelled cannot be made without blocking.
+        bool:
+        Returns True if the computation was canceled.
+        Returns False under all other circumstances, for example:
+        1. computation has begun and could not be canceled.
+        2. computation has finished
+        3. computation is scheduled for execution and it is impossible to
+           determine its state without blocking.
     """
         raise NotImplementedError()
 
@@ -78,10 +78,12 @@
     This method does not block.
 
     Returns:
-      True if the computation was cancelled any time before its result became
-        immediately available. False under all other circumstances including but
-        not limited to this object's cancel method not having been called and
-        the computation's result having become immediately available.
+        bool:
+        Returns True if the computation was cancelled before its result became
+        available.
+        False under all other circumstances, for example:
+        1. computation was not cancelled.
+        2. computation's result is available.
     """
         raise NotImplementedError()
 
@@ -92,9 +94,10 @@
     This method does not block.
 
     Returns:
-      True if the computation is scheduled to take place in the future or is
-        taking place now, or False if the computation took place in the past or
-        was cancelled.
+        bool:
+        Returns True if the computation is scheduled for execution or currently
+        executing.
+        Returns False if the computation already executed or was cancelled.
     """
         raise NotImplementedError()
 
@@ -105,22 +108,24 @@
     This method does not block.
 
     Returns:
-      True if the computation is known to have either completed or have been
-        unscheduled or interrupted. False if the computation may possibly be
-        executing or scheduled to execute later.
+        bool:
+        Returns True if the computation already executed or was cancelled.
+        Returns False if the computation is scheduled for execution or currently
+        executing.
+        This is exactly opposite of the running() method's result.
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def result(self, timeout=None):
-        """Accesses the outcome of the computation or raises its exception.
+        """Returns the result of the computation or raises its exception.
 
     This method may return immediately or may block.
 
     Args:
       timeout: The length of time in seconds to wait for the computation to
-        finish or be cancelled, or None if this method should block until the
-        computation has finished or is cancelled no matter how long that takes.
+        finish or be cancelled. If None, the call will block until the
+        computations's termination.
 
     Returns:
       The return value of the computation.
@@ -142,12 +147,11 @@
 
     Args:
       timeout: The length of time in seconds to wait for the computation to
-        terminate or be cancelled, or None if this method should block until
-        the computation is terminated or is cancelled no matter how long that
-        takes.
+        terminate or be cancelled. If None, the call will block until the
+        computations's termination.
 
     Returns:
-      The exception raised by the computation, or None if the computation did
+        The exception raised by the computation, or None if the computation did
         not raise an exception.
 
     Raises:
@@ -165,12 +169,11 @@
 
     Args:
       timeout: The length of time in seconds to wait for the computation to
-        terminate or be cancelled, or None if this method should block until
-        the computation is terminated or is cancelled no matter how long that
-        takes.
+        terminate or be cancelled. If None, the call will block until the
+        computations's termination.
 
     Returns:
-      The traceback of the exception raised by the computation, or None if the
+        The traceback of the exception raised by the computation, or None if the
         computation did not raise an exception.
 
     Raises:
@@ -260,7 +263,12 @@
 
     @abc.abstractmethod
     def is_active(self):
-        """Describes whether the RPC is active or has terminated."""
+        """Describes whether the RPC is active or has terminated.
+
+    Returns:
+      bool:
+      True if RPC is active, False otherwise.
+    """
         raise NotImplementedError()
 
     @abc.abstractmethod
@@ -290,8 +298,9 @@
       callback: A no-parameter callable to be called on RPC termination.
 
     Returns:
-      True if the callback was added and will be called later; False if the
-        callback was not added and will not later be called (because the RPC
+      bool:
+        True if the callback was added and will be called later; False if the
+        callback was not added and will not be called (because the RPC
         already terminated or some other reason).
     """
         raise NotImplementedError()
@@ -305,7 +314,7 @@
 
     @abc.abstractmethod
     def initial_metadata(self):
-        """Accesses the initial metadata from the service-side of the RPC.
+        """Accesses the initial metadata sent by the server.
 
     This method blocks until the value is available.
 
@@ -316,7 +325,7 @@
 
     @abc.abstractmethod
     def trailing_metadata(self):
-        """Accesses the trailing metadata from the service-side of the RPC.
+        """Accesses the trailing metadata sent by the server.
 
     This method blocks until the value is available.
 
@@ -327,7 +336,7 @@
 
     @abc.abstractmethod
     def code(self):
-        """Accesses the status code emitted by the service-side of the RPC.
+        """Accesses the status code sent by the server.
 
     This method blocks until the value is available.
 
@@ -338,7 +347,7 @@
 
     @abc.abstractmethod
     def details(self):
-        """Accesses the details value emitted by the service-side of the RPC.
+        """Accesses the details sent by the server.
 
     This method blocks until the value is available.
 
@@ -352,10 +361,12 @@
 
 
 class ChannelCredentials(object):
-    """A value encapsulating the data required to create a secure Channel.
+    """An encapsulation of the data required to create a secure Channel.
 
   This class has no supported interface - it exists to define the type of its
-  instances and its instances exist to be passed to other functions.
+  instances and its instances exist to be passed to other functions. For
+  example, ssl_channel_credentials returns an instance, and secure_channel
+  consumes an instance of this class.
   """
 
     def __init__(self, credentials):
@@ -363,7 +374,8 @@
 
 
 class CallCredentials(object):
-    """A value encapsulating data asserting an identity over a channel.
+    """An encapsulation of the data required to assert an identity over a
+       channel.
 
   A CallCredentials may be composed with ChannelCredentials to always assert
   identity for every call over that Channel.
@@ -389,7 +401,8 @@
     """Callback object received by a metadata plugin."""
 
     def __call__(self, metadata, error):
-        """Inform the gRPC runtime of the metadata to construct a CallCredentials.
+        """Inform the gRPC runtime of the metadata to construct a
+           CallCredentials.
 
     Args:
       metadata: The :term:`metadata` used to construct the CallCredentials.
@@ -416,7 +429,7 @@
 
 
 class ServerCredentials(object):
-    """A value encapsulating the data required to open a secure port on a Server.
+    """An encapsulation of the data required to open a secure port on a Server.
 
   This class has no supported interface - it exists to define the type of its
   instances and its instances exist to be passed to other functions.
@@ -430,7 +443,7 @@
 
 
 class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
-    """Affords invoking a unary-unary RPC."""
+    """Affords invoking a unary-unary RPC from client-side."""
 
     @abc.abstractmethod
     def __call__(self, request, timeout=None, metadata=None, credentials=None):
@@ -486,7 +499,7 @@
       credentials: An optional CallCredentials for the RPC.
 
     Returns:
-      An object that is both a Call for the RPC and a Future. In the event of
+        An object that is both a Call for the RPC and a Future. In the event of
         RPC completion, the return Call-Future's result value will be the
         response message of the RPC. Should the event terminate with non-OK
         status, the returned Call-Future's exception value will be an RpcError.
@@ -495,7 +508,7 @@
 
 
 class UnaryStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
-    """Affords invoking a unary-stream RPC."""
+    """Affords invoking a unary-stream RPC from client-side."""
 
     @abc.abstractmethod
     def __call__(self, request, timeout=None, metadata=None, credentials=None):
@@ -504,12 +517,13 @@
     Args:
       request: The request value for the RPC.
       timeout: An optional duration of time in seconds to allow for the RPC.
+               If None, the timeout is considered infinite.
       metadata: An optional :term:`metadata` to be transmitted to the
         service-side of the RPC.
       credentials: An optional CallCredentials for the RPC.
 
     Returns:
-      An object that is both a Call for the RPC and an iterator of response
+        An object that is both a Call for the RPC and an iterator of response
         values. Drawing response values from the returned Call-iterator may
         raise RpcError indicating termination of the RPC with non-OK status.
     """
@@ -517,7 +531,7 @@
 
 
 class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
-    """Affords invoking a stream-unary RPC in any call style."""
+    """Affords invoking a stream-unary RPC from client-side."""
 
     @abc.abstractmethod
     def __call__(self,
@@ -530,6 +544,7 @@
     Args:
       request_iterator: An iterator that yields request values for the RPC.
       timeout: An optional duration of time in seconds to allow for the RPC.
+               If None, the timeout is considered infinite.
       metadata: Optional :term:`metadata` to be transmitted to the
         service-side of the RPC.
       credentials: An optional CallCredentials for the RPC.
@@ -539,8 +554,8 @@
 
     Raises:
       RpcError: Indicating that the RPC terminated with non-OK status. The
-        raised RpcError will also be a Call for the RPC affording the RPC's
-        metadata, status code, and details.
+        raised RpcError will also implement grpc.Call, affording methods
+        such as metadata, code, and details.
     """
         raise NotImplementedError()
 
@@ -550,17 +565,18 @@
                   timeout=None,
                   metadata=None,
                   credentials=None):
-        """Synchronously invokes the underlying RPC.
+        """Synchronously invokes the underlying RPC on the client.
 
     Args:
       request_iterator: An iterator that yields request values for the RPC.
       timeout: An optional duration of time in seconds to allow for the RPC.
+               If None, the timeout is considered infinite.
       metadata: Optional :term:`metadata` to be transmitted to the
         service-side of the RPC.
       credentials: An optional CallCredentials for the RPC.
 
     Returns:
-      The response value for the RPC and a Call for the RPC.
+      The response value for the RPC and a Call object for the RPC.
 
     Raises:
       RpcError: Indicating that the RPC terminated with non-OK status. The
@@ -575,17 +591,18 @@
                timeout=None,
                metadata=None,
                credentials=None):
-        """Asynchronously invokes the underlying RPC.
+        """Asynchronously invokes the underlying RPC on the client.
 
     Args:
       request_iterator: An iterator that yields request values for the RPC.
       timeout: An optional duration of time in seconds to allow for the RPC.
+               If None, the timeout is considered infinite.
       metadata: Optional :term:`metadata` to be transmitted to the
         service-side of the RPC.
       credentials: An optional CallCredentials for the RPC.
 
     Returns:
-      An object that is both a Call for the RPC and a Future. In the event of
+        An object that is both a Call for the RPC and a Future. In the event of
         RPC completion, the return Call-Future's result value will be the
         response message of the RPC. Should the event terminate with non-OK
         status, the returned Call-Future's exception value will be an RpcError.
@@ -594,7 +611,7 @@
 
 
 class StreamStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
-    """Affords invoking a stream-stream RPC in any call style."""
+    """Affords invoking a stream-stream RPC on client-side."""
 
     @abc.abstractmethod
     def __call__(self,
@@ -602,17 +619,18 @@
                  timeout=None,
                  metadata=None,
                  credentials=None):
-        """Invokes the underlying RPC.
+        """Invokes the underlying RPC on the client.
 
     Args:
       request_iterator: An iterator that yields request values for the RPC.
       timeout: An optional duration of time in seconds to allow for the RPC.
+               if not specified the timeout is considered infinite.
       metadata: Optional :term:`metadata` to be transmitted to the
         service-side of the RPC.
       credentials: An optional CallCredentials for the RPC.
 
     Returns:
-      An object that is both a Call for the RPC and an iterator of response
+        An object that is both a Call for the RPC and an iterator of response
         values. Drawing response values from the returned Call-iterator may
         raise RpcError indicating termination of the RPC with non-OK status.
     """
@@ -623,27 +641,32 @@
 
 
 class Channel(six.with_metaclass(abc.ABCMeta)):
-    """Affords RPC invocation via generic methods."""
+    """Affords RPC invocation via generic methods on client-side."""
 
     @abc.abstractmethod
     def subscribe(self, callback, try_to_connect=False):
-        """Subscribes to this Channel's connectivity.
+        """Subscribe to this Channel's connectivity state machine.
+
+    A Channel may be in any of the states described by ChannelConnectivity.
+    This method allows application to monitor the state transitions.
+    The typical use case is to debug or gain better visibility into gRPC
+    runtime's state.
 
     Args:
-      callback: A callable to be invoked and passed a ChannelConnectivity value
-        describing this Channel's connectivity. The callable will be invoked
-        immediately upon subscription and again for every change to this
-        Channel's connectivity thereafter until it is unsubscribed or this
+      callback: A callable to be invoked with ChannelConnectivity argument.
+        ChannelConnectivity describes current state of the channel.
+        The callable will be invoked immediately upon subscription and again for
+        every change to ChannelConnectivity until it is unsubscribed or this
         Channel object goes out of scope.
       try_to_connect: A boolean indicating whether or not this Channel should
-        attempt to connect if it is not already connected and ready to conduct
-        RPCs.
+        attempt to connect immediately. If set to False, gRPC runtime decides
+        when to connect.
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def unsubscribe(self, callback):
-        """Unsubscribes a callback from this Channel's connectivity.
+        """Unsubscribes a subscribed callback from this Channel's connectivity.
 
     Args:
       callback: A callable previously registered with this Channel from having
@@ -736,7 +759,7 @@
 
     @abc.abstractmethod
     def invocation_metadata(self):
-        """Accesses the metadata from the invocation-side of the RPC.
+        """Accesses the metadata from the sent by the client.
 
     Returns:
       The invocation :term:`metadata`.
@@ -749,15 +772,16 @@
 
     Returns:
       A string identifying the peer that invoked the RPC being serviced.
+      The string format is determined by gRPC runtime.
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def send_initial_metadata(self, initial_metadata):
-        """Sends the initial metadata value to the invocation-side of the RPC.
+        """Sends the initial metadata value to the client.
 
-    This method need not be called by method implementations if they have no
-    service-side initial metadata to transmit.
+    This method need not be called by implementations if they have no
+    metadata to add to what the gRPC runtime will transmit.
 
     Args:
       initial_metadata: The initial :term:`metadata`.
@@ -766,10 +790,10 @@
 
     @abc.abstractmethod
     def set_trailing_metadata(self, trailing_metadata):
-        """Accepts the trailing metadata value of the RPC.
+        """Sends the trailing metadata for the RPC.
 
-    This method need not be called by method implementations if they have no
-    service-side trailing metadata to transmit.
+    This method need not be called by implementations if they have no
+    metadata to add to what the gRPC runtime will transmit.
 
     Args:
       trailing_metadata: The trailing :term:`metadata`.
@@ -778,27 +802,25 @@
 
     @abc.abstractmethod
     def set_code(self, code):
-        """Accepts the status code of the RPC.
+        """Sets the value to be used as status code upon RPC completion.
 
     This method need not be called by method implementations if they wish the
     gRPC runtime to determine the status code of the RPC.
 
     Args:
-      code: A StatusCode value to be transmitted to the invocation side of the
-        RPC as the status code of the RPC.
+      code: A StatusCode object to be sent to the client.
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def set_details(self, details):
-        """Accepts the service-side details of the RPC.
+        """Sets the value to be used as detail string upon RPC completion.
 
     This method need not be called by method implementations if they have no
     details to transmit.
 
     Args:
-      details: A string to be transmitted to the invocation side of the RPC as
-        the status details of the RPC.
+      details: An arbitrary string to be sent to the client upon completion.
     """
         raise NotImplementedError()
 
@@ -845,7 +867,7 @@
     """Describes an RPC that has just arrived for service.
   Attributes:
     method: The method name of the RPC.
-    invocation_metadata: The :term:`metadata` from the invocation side of the RPC.
+    invocation_metadata: The :term:`metadata` sent by the client.
   """
 
 
@@ -854,14 +876,14 @@
 
     @abc.abstractmethod
     def service(self, handler_call_details):
-        """Services an RPC (or not).
+        """Returns the handler for servicing the RPC.
 
     Args:
       handler_call_details: A HandlerCallDetails describing the RPC.
 
     Returns:
-      An RpcMethodHandler with which the RPC may be serviced, or None to
-        indicate that this object will not be servicing the RPC.
+      An RpcMethodHandler with which the RPC may be serviced if the
+      implementation chooses to service this RPC, or None otherwise.
     """
         raise NotImplementedError()
 
@@ -870,15 +892,15 @@
     """An implementation of RPC methods belonging to a service.
 
   A service handles RPC methods with structured names of the form
-  '/Service.Name/Service.MethodX', where 'Service.Name' is the value
-  returned by service_name(), and 'Service.MethodX' is the service method
-  name.  A service can have multiple service methods names, but only a single
+  '/Service.Name/Service.Method', where 'Service.Name' is the value
+  returned by service_name(), and 'Service.Method' is the method
+  name.  A service can have multiple method names, but only a single
   service name.
   """
 
     @abc.abstractmethod
     def service_name(self):
-        """Returns this services name.
+        """Returns this service's name.
 
     Returns:
       The service name.
@@ -900,88 +922,78 @@
 
     Args:
       generic_rpc_handlers: An iterable of GenericRpcHandlers that will be used
-        to service RPCs after this Server is started.
+      to service RPCs.
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def add_insecure_port(self, address):
-        """Reserves a port for insecure RPC service once this Server becomes active.
+        """Opens an insecure port for accepting RPCs.
 
-    This method may only be called before calling this Server's start method is
-    called.
+    This method may only be called before starting the server.
 
     Args:
       address: The address for which to open a port.
+      if the port is 0, or not specified in the address, then gRPC runtime
+      will choose a port.
 
     Returns:
-      An integer port on which RPCs will be serviced after this link has been
-        started. This is typically the same number as the port number contained
-        in the passed address, but will likely be different if the port number
-        contained in the passed address was zero.
+      integer:
+      An integer port on which server will accept RPC requests.
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def add_secure_port(self, address, server_credentials):
-        """Reserves a port for secure RPC service after this Server becomes active.
+        """Opens a secure port for accepting RPCs.
 
-    This method may only be called before calling this Server's start method is
-    called.
+    This method may only be called before starting the server.
 
     Args:
       address: The address for which to open a port.
-      server_credentials: A ServerCredentials.
+        if the port is 0, or not specified in the address, then gRPC runtime
+        will choose a port.
+      server_credentials: A ServerCredentials object.
 
     Returns:
-      An integer port on which RPCs will be serviced after this link has been
-        started. This is typically the same number as the port number contained
-        in the passed address, but will likely be different if the port number
-        contained in the passed address was zero.
+      integer:
+      An integer port on which server will accept RPC requests.
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def start(self):
-        """Starts this Server's service of RPCs.
+        """Starts this Server.
 
-    This method may only be called while the server is not serving RPCs (i.e. it
-    is not idempotent).
+    This method may only be called once. (i.e. it is not idempotent).
     """
         raise NotImplementedError()
 
     @abc.abstractmethod
     def stop(self, grace):
-        """Stops this Server's service of RPCs.
+        """Stops this Server.
 
-    All calls to this method immediately stop service of new RPCs. When existing
-    RPCs are aborted is controlled by the grace period parameter passed to this
-    method.
+    This method immediately stop service of new RPCs in all cases.
+    If a grace period is specified, this method returns immediately
+    and all RPCs active at the end of the grace period are aborted.
 
-    This method may be called at any time and is idempotent. Passing a smaller
-    grace value than has been passed in a previous call will have the effect of
-    stopping the Server sooner. Passing a larger grace value than has been
-    passed in a previous call will not have the effect of stopping the server
-    later.
+    If a grace period is not specified, then all existing RPCs are
+    teriminated immediately and the this method blocks until the last
+    RPC handler terminates.
 
-    This method does not block for any significant length of time. If None is
-    passed as the grace value, existing RPCs are immediately aborted and this
-    method blocks until this Server is completely stopped.
+    This method is idempotent and may be called at any time. Passing a smaller
+    grace value in subsequentcall will have the effect of stopping the Server
+    sooner. Passing a larger grace value in subsequent call *will not* have the
+    effect of stopping the server later (i.e. the most restrictive grace
+    value is used).
 
     Args:
-      grace: A duration of time in seconds or None. If a duration of time in
-        seconds, the time to allow existing RPCs to complete before being
-        aborted by this Server's stopping. If None, all RPCs will be aborted
-        immediately and this method will block until this Server is completely
-        stopped.
+      grace: A duration of time in seconds or None.
 
     Returns:
       A threading.Event that will be set when this Server has completely
-      stopped. The returned event may not be set until after the full grace
-      period (if some ongoing RPC continues for the full length of the period)
-      of it may be set much sooner (such as if this Server had no RPCs underway
-      at the time it was stopped or if all RPCs that it had underway completed
-      very early in the grace period).
+      stopped, i.e. when running RPCs either complete or are aborted and
+      all handlers have terminated.
     """
         raise NotImplementedError()
 
@@ -995,14 +1007,13 @@
     """Creates an RpcMethodHandler for a unary-unary RPC method.
 
   Args:
-    behavior: The implementation of an RPC method as a callable behavior taking
-      a single request value and returning a single response value.
-    request_deserializer: An optional request deserialization behavior.
-    response_serializer: An optional response serialization behavior.
+    behavior: The implementation of an RPC that accepts one request and returns
+    one response.
+    request_deserializer: An optional behavior for request deserialization.
+    response_serializer: An optional behavior for response serialization.
 
   Returns:
-    An RpcMethodHandler for a unary-unary RPC method constructed from the given
-      parameters.
+    An RpcMethodHandler object that is typically used by grpc.Server.
   """
     from grpc import _utilities  # pylint: disable=cyclic-import
     return _utilities.RpcMethodHandler(False, False, request_deserializer,
@@ -1016,14 +1027,13 @@
     """Creates an RpcMethodHandler for a unary-stream RPC method.
 
   Args:
-    behavior: The implementation of an RPC method as a callable behavior taking
-      a single request value and returning an iterator of response values.
-    request_deserializer: An optional request deserialization behavior.
-    response_serializer: An optional response serialization behavior.
+    behavior: The implementation of an RPC that accepts one request and returns
+      an iterator of response values.
+    request_deserializer: An optional behavior for request deserialization.
+    response_serializer: An optional behavior for response serialization.
 
   Returns:
-    An RpcMethodHandler for a unary-stream RPC method constructed from the
-      given parameters.
+    An RpcMethodHandler object that is typically used by grpc.Server.
   """
     from grpc import _utilities  # pylint: disable=cyclic-import
     return _utilities.RpcMethodHandler(False, True, request_deserializer,
@@ -1037,14 +1047,13 @@
     """Creates an RpcMethodHandler for a stream-unary RPC method.
 
   Args:
-    behavior: The implementation of an RPC method as a callable behavior taking
-      an iterator of request values and returning a single response value.
-    request_deserializer: An optional request deserialization behavior.
-    response_serializer: An optional response serialization behavior.
+    behavior: The implementation of an RPC that accepts an iterator of request
+    values and returns a single response value.
+    request_deserializer: An optional behavior for request deserialization.
+    response_serializer: An optional behavior for response serialization.
 
   Returns:
-    An RpcMethodHandler for a stream-unary RPC method constructed from the
-      given parameters.
+    An RpcMethodHandler object that is typically used by grpc.Server.
   """
     from grpc import _utilities  # pylint: disable=cyclic-import
     return _utilities.RpcMethodHandler(True, False, request_deserializer,
@@ -1058,15 +1067,13 @@
     """Creates an RpcMethodHandler for a stream-stream RPC method.
 
   Args:
-    behavior: The implementation of an RPC method as a callable behavior taking
-      an iterator of request values and returning an iterator of response
-      values.
-    request_deserializer: An optional request deserialization behavior.
-    response_serializer: An optional response serialization behavior.
+    behavior: The implementation of an RPC that accepts an iterator of request
+    values and returns an iterator of response values.
+    request_deserializer: An optional behavior for request deserialization.
+    response_serializer: An optional behavior for response serialization.
 
   Returns:
-    An RpcMethodHandler for a stream-stream RPC method constructed from the
-      given parameters.
+    An RpcMethodHandler object that is typically used by grpc.Server.
   """
     from grpc import _utilities  # pylint: disable=cyclic-import
     return _utilities.RpcMethodHandler(True, True, request_deserializer,
@@ -1075,15 +1082,16 @@
 
 
 def method_handlers_generic_handler(service, method_handlers):
-    """Creates a grpc.GenericRpcHandler from RpcMethodHandlers.
+    """Creates a GenericRpcHandler from RpcMethodHandlers.
 
   Args:
-    service: A service name to be used for the given method handlers.
-    method_handlers: A dictionary from method name to RpcMethodHandler
-      implementing the named method.
+    service: The name of the service that is implemented by the method_handlers.
+    method_handlers: A dictionary that maps method names to corresponding
+    RpcMethodHandler.
 
   Returns:
-    A GenericRpcHandler constructed from the given parameters.
+    A GenericRpcHandler. This is typically added to the grpc.Server object
+    with add_generic_rpc_handlers() before starting the server.
   """
     from grpc import _utilities  # pylint: disable=cyclic-import
     return _utilities.DictionaryGenericHandler(service, method_handlers)
@@ -1095,12 +1103,12 @@
     """Creates a ChannelCredentials for use with an SSL-enabled Channel.
 
   Args:
-    root_certificates: The PEM-encoded root certificates or unset to ask for
-      them to be retrieved from a default location.
-    private_key: The PEM-encoded private key to use or unset if no private key
-      should be used.
-    certificate_chain: The PEM-encoded certificate chain to use or unset if no
-      certificate chain should be used.
+    root_certificates: The PEM-encoded root certificates as a byte string,
+    or None to retrieve them from a default location chosen by gRPC runtime.
+    private_key: The PEM-encoded private key as a byte string, or None if no
+    private key should be used.
+    certificate_chain: The PEM-encoded certificate chain as a byte string
+    to use or or None if no certificate chain should be used.
 
   Returns:
     A ChannelCredentials for use with an SSL-enabled Channel.
@@ -1117,9 +1125,8 @@
     """Construct CallCredentials from an AuthMetadataPlugin.
 
   Args:
-    metadata_plugin: An AuthMetadataPlugin to use as the authentication behavior
-      in the created CallCredentials.
-    name: A name for the plugin.
+    metadata_plugin: An AuthMetadataPlugin to use for authentication.
+    name: An optional name for the plugin.
 
   Returns:
     A CallCredentials.
@@ -1142,7 +1149,8 @@
 
   Args:
     access_token: A string to place directly in the http request
-      authorization header, ie "authorization: Bearer <access_token>".
+      authorization header, for example
+      "authorization: Bearer <access_token>".
 
   Returns:
     A CallCredentials.
@@ -1173,12 +1181,12 @@
     """Compose a ChannelCredentials and one or more CallCredentials objects.
 
   Args:
-    channel_credentials: A ChannelCredentials.
+    channel_credentials: A ChannelCredentials object.
     *call_credentials: One or more CallCredentials objects.
 
   Returns:
     A ChannelCredentials composed of the given ChannelCredentials and
-      CallCredentials objects.
+    CallCredentials objects.
   """
     from grpc import _credential_composition  # pylint: disable=cyclic-import
     cygrpc_call_credentials = tuple(
@@ -1195,18 +1203,18 @@
     """Creates a ServerCredentials for use with an SSL-enabled Server.
 
   Args:
-    private_key_certificate_chain_pairs: A nonempty sequence each element of
-      which is a pair the first element of which is a PEM-encoded private key
-      and the second element of which is the corresponding PEM-encoded
-      certificate chain.
-    root_certificates: PEM-encoded client root certificates to be used for
-      verifying authenticated clients. If omitted, require_client_auth must also
-      be omitted or be False.
-    require_client_auth: A boolean indicating whether or not to require clients
-      to be authenticated. May only be True if root_certificates is not None.
+    private_key_certificate_chain_pairs: A list of pairs of the form
+      [PEM-encoded private key, PEM-encoded certificate chain].
+    root_certificates: An optional byte string of PEM-encoded client root
+      certificates that the server will use to verify client authentication.
+      If omitted, require_client_auth must also be False.
+    require_client_auth: A boolean indicating whether or not to require
+      clients to be authenticated. May only be True if root_certificates
+      is not None.
 
   Returns:
-    A ServerCredentials for use with an SSL-enabled Server.
+    A ServerCredentials for use with an SSL-enabled Server. Typically, this
+    object is an argument to add_secure_port() method during server setup.
   """
     if len(private_key_certificate_chain_pairs) == 0:
         raise ValueError(
@@ -1224,18 +1232,17 @@
 
 
 def channel_ready_future(channel):
-    """Creates a Future tracking when a Channel is ready.
+    """Creates a Future that tracks when a Channel is ready.
 
-  Cancelling the returned Future does not tell the given Channel to abandon
-  attempts it may have been making to connect; cancelling merely deactivates the
-  returned Future's subscription to the given Channel's connectivity.
+  Cancelling the Future does not affect the channel's state machine.
+  It merely decouples the Future from channel state machine.
 
   Args:
-    channel: A Channel.
+    channel: A Channel object.
 
   Returns:
-    A Future that matures when the given Channel has connectivity
-      ChannelConnectivity.READY.
+    A Future object that matures when the channel connectivity is
+    ChannelConnectivity.READY.
   """
     from grpc import _utilities  # pylint: disable=cyclic-import
     return _utilities.channel_ready_future(channel)
@@ -1245,12 +1252,12 @@
     """Creates an insecure Channel to a server.
 
   Args:
-    target: The target to which to connect.
-    options: A sequence of string-value pairs according to which to configure
-      the created channel.
+    target: The server address
+    options: An optional list of key-value pairs (channel args in gRPC runtime)
+    to configure the channel.
 
   Returns:
-    A Channel to the target through which RPCs may be conducted.
+    A Channel object.
   """
     from grpc import _channel  # pylint: disable=cyclic-import
     return _channel.Channel(target, () if options is None else options, None)
@@ -1260,13 +1267,13 @@
     """Creates a secure Channel to a server.
 
   Args:
-    target: The target to which to connect.
+    target: The server address.
     credentials: A ChannelCredentials instance.
-    options: A sequence of string-value pairs according to which to configure
-      the created channel.
+    options: An optional list of key-value pairs (channel args in gRPC runtime)
+    to configure the channel.
 
   Returns:
-    A Channel to the target through which RPCs may be conducted.
+    A Channel object.
   """
     from grpc import _channel  # pylint: disable=cyclic-import
     return _channel.Channel(target, () if options is None else options,
@@ -1280,21 +1287,19 @@
     """Creates a Server with which RPCs can be serviced.
 
   Args:
-    thread_pool: A futures.ThreadPoolExecutor to be used by the returned Server
-      to service RPCs.
-    handlers: An optional sequence of GenericRpcHandlers to be used to service
-      RPCs after the returned Server is started. These handlers need not be the
-      only handlers the server will use to service RPCs; other handlers may
-      later be added by calling add_generic_rpc_handlers any time before the
-      returned Server is started.
-    options: A sequence of string-value pairs according to which to configure
-      the created server.
+    thread_pool: A futures.ThreadPoolExecutor to be used by the Server
+      to execute RPC handlers.
+    handlers: An optional list of GenericRpcHandlers used for executing RPCs.
+      More handlers may be added by calling add_generic_rpc_handlers any time
+      before the server is started.
+    options: An optional list of key-value pairs (channel args in gRPC runtime)
+    to configure the channel.
     maximum_concurrent_rpcs: The maximum number of concurrent RPCs this server
-      will service before returning status RESOURCE_EXHAUSTED, or None to
+      will service before returning RESOURCE_EXHAUSTED status, or None to
       indicate no limit.
 
   Returns:
-    A Server with which RPCs can be serviced.
+    A Server object.
   """
     from grpc import _server  # pylint: disable=cyclic-import
     return _server.Server(thread_pool, () if handlers is None else handlers, ()
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
index 34b2623..502b655 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
@@ -37,9 +37,16 @@
 
 cdef class CompletionQueue:
 
-  def __cinit__(self):
+  def __cinit__(self, shutdown_cq=False):
+    cdef grpc_completion_queue_attributes c_attrs
     grpc_init()
-    with nogil:
+    if shutdown_cq:
+      c_attrs.version = 1
+      c_attrs.cq_completion_type = GRPC_CQ_NEXT
+      c_attrs.cq_polling_type = GRPC_CQ_NON_LISTENING
+      self.c_completion_queue = grpc_completion_queue_create(
+          grpc_completion_queue_factory_lookup(&c_attrs), &c_attrs, NULL);
+    else:
       self.c_completion_queue = grpc_completion_queue_create_for_next(NULL)
     self.is_shutting_down = False
     self.is_shutdown = False
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index f66f6e4..1db2056 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -217,6 +217,20 @@
     GRPC_CALL_ERROR_INVALID_FLAGS
     GRPC_CALL_ERROR_INVALID_METADATA
 
+  ctypedef enum grpc_cq_completion_type:
+    GRPC_CQ_NEXT
+    GRPC_CQ_PLUCK
+
+  ctypedef enum grpc_cq_polling_type:
+    GRPC_CQ_DEFAULT_POLLING
+    GRPC_CQ_NON_LISTENING
+    GRPC_CQ_NON_POLLING
+
+  ctypedef struct grpc_completion_queue_attributes:
+    int version
+    grpc_cq_completion_type cq_completion_type
+    grpc_cq_polling_type cq_polling_type
+
   ctypedef enum grpc_connectivity_state:
     GRPC_CHANNEL_IDLE
     GRPC_CHANNEL_CONNECTING
@@ -309,6 +323,14 @@
   void grpc_init() nogil
   void grpc_shutdown() nogil
 
+  ctypedef struct grpc_completion_queue_factory:
+    pass
+
+  grpc_completion_queue_factory *grpc_completion_queue_factory_lookup(
+      const grpc_completion_queue_attributes* attributes) nogil
+  grpc_completion_queue *grpc_completion_queue_create(
+    const grpc_completion_queue_factory* factory,
+    const grpc_completion_queue_attributes* attr, void* reserved) nogil
   grpc_completion_queue *grpc_completion_queue_create_for_next(void *reserved) nogil
 
   grpc_event grpc_completion_queue_next(grpc_completion_queue *cq,
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
index 97192ef..5233edc 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
@@ -85,7 +85,7 @@
   def start(self):
     if self.is_started:
       raise ValueError("the server has already started")
-    self.backup_shutdown_queue = CompletionQueue()
+    self.backup_shutdown_queue = CompletionQueue(shutdown_cq=True)
     self.register_completion_queue(self.backup_shutdown_queue)
     self.is_started = True
     with nogil:
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index d2a570c..42c13b2 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -280,8 +280,10 @@
   'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
   'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
   'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
+  'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c',
   'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c',
   'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c',
+  'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c',
   'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c',
   'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
   'third_party/nanopb/pb_common.c',
diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py
index 17bb3ab..f1c09de 100644
--- a/src/python/grpcio_health_checking/setup.py
+++ b/src/python/grpcio_health_checking/setup.py
@@ -29,7 +29,6 @@
 """Setup module for the GRPC Python package's optional health checking."""
 
 import os
-import sys
 
 import setuptools
 
@@ -47,7 +46,7 @@
 SETUP_REQUIRES = (
     'grpcio-tools>={version}'.format(version=grpc_version.VERSION),)
 
-INSTALL_REQUIRES = ('protobuf>=3.2.0',
+INSTALL_REQUIRES = ('protobuf>=3.3.0',
                     'grpcio>={version}'.format(version=grpc_version.VERSION),)
 
 COMMAND_CLASS = {
diff --git a/src/python/grpcio_reflection/setup.py b/src/python/grpcio_reflection/setup.py
index e6cf547..cb49c3d 100644
--- a/src/python/grpcio_reflection/setup.py
+++ b/src/python/grpcio_reflection/setup.py
@@ -47,7 +47,7 @@
 SETUP_REQUIRES = (
     'grpcio-tools>={version}'.format(version=grpc_version.VERSION),)
 
-INSTALL_REQUIRES = ('protobuf>=3.2.0',
+INSTALL_REQUIRES = ('protobuf>=3.3.0',
                     'grpcio>={version}'.format(version=grpc_version.VERSION),)
 
 COMMAND_CLASS = {
diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py
index b9f0264..7ee5336 100644
--- a/src/python/grpcio_tests/setup.py
+++ b/src/python/grpcio_tests/setup.py
@@ -56,7 +56,7 @@
     'grpcio>={version}'.format(version=grpc_version.VERSION),
     'grpcio-tools>={version}'.format(version=grpc_version.VERSION),
     'grpcio-health-checking>={version}'.format(version=grpc_version.VERSION),
-    'oauth2client>=1.4.7', 'protobuf>=3.2.0', 'six>=1.10',)
+    'oauth2client>=1.4.7', 'protobuf>=3.3.0', 'six>=1.10',)
 
 COMMAND_CLASS = {
     # Run `preprocess` *before* doing any packaging!
diff --git a/templates/binding.gyp.template b/templates/binding.gyp.template
index a2e8c58..933174a 100644
--- a/templates/binding.gyp.template
+++ b/templates/binding.gyp.template
@@ -41,15 +41,16 @@
   {
     'variables': {
       'runtime%': 'node',
-      # UV integration in C core is enabled by default. It can be disabled
-      # by setting this argument to anything else.
-      'grpc_uv%': 'true',
       # Some Node installations use the system installation of OpenSSL, and on
       # some systems, the system OpenSSL still does not have ALPN support. This
       # will let users recompile gRPC to work without ALPN.
       'grpc_alpn%': 'true',
       # Indicates that the library should be built with gcov.
-      'grpc_gcov%': 'false'
+      'grpc_gcov%': 'false',
+      # Indicates that the library should be built with compatibility for musl
+      # libc, so that it can run on Alpine Linux. This is only necessary if not
+      # building on Alpine Linux
+      'grpc_alpine%': 'false'
     },
     'target_defaults': {
       'configurations': {
@@ -83,17 +84,11 @@
         'include'
       ],
       'defines': [
-        'GPR_BACKWARDS_COMPATIBILITY_MODE'
+        'GPR_BACKWARDS_COMPATIBILITY_MODE',
+        'GRPC_ARES=0',
+        'GRPC_UV'
       ],
       'conditions': [
-        ['grpc_uv=="true"', {
-          'defines': [
-            'GRPC_ARES=0',
-            # Disabling this while bugs are ironed out. Uncomment this to
-            # re-enable libuv integration in C core.
-            'GRPC_UV'
-          ]
-        }],
         ['grpc_gcov=="true"', {
           % for arg, prop in [('CPPFLAGS', 'cflags'), ('DEFINES', 'defines'), ('LDFLAGS', 'ldflags')]:
           %  if configs['gcov'].get(arg, None) is not None:
@@ -105,6 +100,11 @@
           %  endif
           % endfor
         }],
+        ['grpc_alpine=="true"', {
+          'defines': [
+            'GPR_MUSL_LIBC_COMPAT'
+          ]
+        }],
         ['OS!="win" and runtime=="electron"', {
           "defines": [
             'OPENSSL_NO_THREADS'
@@ -233,6 +233,10 @@
               }
             ]
           },
+        ]
+      }],
+      ['OS == "win"', {
+        'targets': [
           # Only want to compile zlib under Windows
           % for module in node_modules:
           % for lib in libs:
@@ -264,13 +268,6 @@
       }]
     ],
     'targets': [
-  <%
-      for lib in libs:
-        if 'grpc' in lib.transitive_deps or lib.name == 'grpc':
-          lib.deps.append('node_modules/cares/deps/cares/cares.gyp:cares')
-      for module in node_modules:
-        module.deps.append('node_modules/cares/deps/cares/cares.gyp:cares')
-  %>
       % for module in node_modules:
       % for lib in libs:
       % if lib.name in module.transitive_deps and lib.name not in ('boringssl', 'z'):
diff --git a/templates/composer.json.template b/templates/composer.json.template
index 94f0c23..2d4cb11 100644
--- a/templates/composer.json.template
+++ b/templates/composer.json.template
@@ -9,7 +9,7 @@
     "license": "BSD-3-Clause",
     "require": {
       "php": ">=5.5.0",
-      "google/protobuf": "^v3.1.0"
+      "google/protobuf": "^v3.3.0"
     },
     "require-dev": {
       "google/auth": "v0.9"
diff --git a/templates/package.json.template b/templates/package.json.template
index b69fd28..3bae8fd 100644
--- a/templates/package.json.template
+++ b/templates/package.json.template
@@ -36,8 +36,7 @@
       "lodash": "^4.15.0",
       "nan": "^2.0.0",
       "node-pre-gyp": "^0.6.0",
-      "protobufjs": "^6.7.0",
-      "cares": "^1.1.5"
+      "protobufjs": "^6.7.0"
     },
     "devDependencies": {
       "async": "^2.0.1",
diff --git a/templates/src/php/composer.json.template b/templates/src/php/composer.json.template
index 1887ee3..36932cf 100644
--- a/templates/src/php/composer.json.template
+++ b/templates/src/php/composer.json.template
@@ -7,7 +7,7 @@
     "version": "${settings.php_version.php_composer()}",
     "require": {
       "php": ">=5.5.0",
-      "google/protobuf": "^v3.1.0"
+      "google/protobuf": "^v3.3.0"
     },
     "require-dev": {
       "google/auth": "v0.9"
diff --git a/templates/tools/dockerfile/gcp_api_libraries.include b/templates/tools/dockerfile/gcp_api_libraries.include
index 669b0f8..adecb92 100644
--- a/templates/tools/dockerfile/gcp_api_libraries.include
+++ b/templates/tools/dockerfile/gcp_api_libraries.include
@@ -1,4 +1,3 @@
 # Google Cloud platform API libraries
 RUN apt-get update && apt-get install -y python-pip && apt-get clean
 RUN pip install --upgrade google-api-python-client
-
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile.template
similarity index 93%
rename from templates/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile.template
rename to templates/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile.template
index ad8ad71..b4e618d 100644
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile.template
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile.template
@@ -1,6 +1,6 @@
 %YAML 1.2
 --- |
-  # Copyright 2016, Google Inc.
+  # Copyright 2017, Google Inc.
   # All rights reserved.
   #
   # Redistribution and use in source and binary forms, with or without
@@ -29,10 +29,10 @@
   # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
-  FROM golang:latest
+  FROM golang:1.7
   
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../python_deps.include"/>
   <%include file="../../go_path.include"/>
+  <%include file="../../python_deps.include"/>
   # Define the default command.
   CMD ["bash"]
+  
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile.template
similarity index 93%
copy from templates/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile.template
copy to templates/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile.template
index ad8ad71..437e826 100644
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile.template
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile.template
@@ -1,6 +1,6 @@
 %YAML 1.2
 --- |
-  # Copyright 2016, Google Inc.
+  # Copyright 2017, Google Inc.
   # All rights reserved.
   #
   # Redistribution and use in source and binary forms, with or without
@@ -29,10 +29,10 @@
   # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
-  FROM golang:latest
+  FROM golang:1.8
   
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../python_deps.include"/>
   <%include file="../../go_path.include"/>
+  <%include file="../../python_deps.include"/>
   # Define the default command.
   CMD ["bash"]
+  
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile.template
deleted file mode 100644
index 5d805bb..0000000
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile.template
+++ /dev/null
@@ -1,42 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2015, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  
-  FROM debian:jessie
-  
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../python_deps.include"/>
-  <%include file="../../ccache_setup.include"/>
-  <%include file="../../cxx_deps.include"/>
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../csharp_deps.include"/>
-  # Define the default command.
-  CMD ["bash"]
-  
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_cxx/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_cxx/Dockerfile.template
deleted file mode 100644
index 18f06b7..0000000
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_cxx/Dockerfile.template
+++ /dev/null
@@ -1,41 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2015-2016, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  
-  FROM debian:jessie
-  
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../python_deps.include"/>
-  <%include file="../../ccache_setup.include"/>
-  <%include file="../../cxx_deps.include"/>
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../clang_update.include"/>
-  # Define the default command.
-  CMD ["bash"]
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_java/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_java/Dockerfile.template
deleted file mode 100644
index 2bb2f9b..0000000
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_java/Dockerfile.template
+++ /dev/null
@@ -1,41 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2016, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  
-  FROM debian:jessie
-  
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../python_deps.include"/>
-  <%include file="../../ccache_setup.include"/>
-  <%include file="../../cxx_deps.include"/>
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../java_deps.include"/>
-  # Define the default command.
-  CMD ["bash"]
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_node/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_node/Dockerfile.template
deleted file mode 100644
index d70b751..0000000
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_node/Dockerfile.template
+++ /dev/null
@@ -1,40 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2016, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  
-  FROM debian:jessie
-  
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../python_deps.include"/>
-  <%include file="../../node_deps.include"/>
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../run_tests_addons.include"/>
-  # Define the default command.
-  CMD ["bash"]
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile.template
deleted file mode 100644
index 49ba601..0000000
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile.template
+++ /dev/null
@@ -1,46 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2015, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  
-  FROM debian:jessie
-  
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../python_deps.include"/>
-  <%include file="../../ruby_deps.include"/>
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../php_deps.include"/>
-  <%include file="../../run_tests_addons.include"/>
-  # Install composer
-  RUN curl -sS https://getcomposer.org/installer | php
-  RUN mv composer.phar /usr/local/bin/composer
-  
-  # Define the default command.
-  CMD ["bash"]
-  
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_python/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_python/Dockerfile.template
deleted file mode 100644
index 27e9eee..0000000
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_python/Dockerfile.template
+++ /dev/null
@@ -1,45 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2016, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  
-  FROM debian:jessie
-  
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../ccache_setup.include"/>
-  <%include file="../../cxx_deps.include"/>
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../python_deps.include"/>
-
-  RUN pip install coverage
-  RUN pip install oauth2client
-
-  # Define the default command.
-  CMD ["bash"]
-  
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_ruby/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_ruby/Dockerfile.template
deleted file mode 100644
index 1819977..0000000
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_ruby/Dockerfile.template
+++ /dev/null
@@ -1,42 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2015, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  
-  FROM debian:jessie
-  
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../python_deps.include"/>
-  <%include file="../../ccache_setup.include"/>
-  <%include file="../../cxx_deps.include"/>
-  <%include file="../../gcp_api_libraries.include"/>
-  <%include file="../../ruby_deps.include"/>
-  # Define the default command.
-  CMD ["bash"]
-  
diff --git a/templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template
index 092f04d..f869cf9 100644
--- a/templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../csharp_deps.include"/>
   <%include file="../../csharp_dotnetcli_deps.include"/>
diff --git a/templates/tools/dockerfile/test/cxx_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/cxx_jessie_x64/Dockerfile.template
index 35b0e17..a42215d 100644
--- a/templates/tools/dockerfile/test/cxx_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/cxx_jessie_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
 
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../cxx_deps.include"/>
   <%include file="../../clang_update.include"/>
diff --git a/templates/tools/dockerfile/test/cxx_jessie_x86/Dockerfile.template b/templates/tools/dockerfile/test/cxx_jessie_x86/Dockerfile.template
index 643b5cb..9df8158 100644
--- a/templates/tools/dockerfile/test/cxx_jessie_x86/Dockerfile.template
+++ b/templates/tools/dockerfile/test/cxx_jessie_x86/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM 32bit/debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../cxx_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
diff --git a/templates/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile.template b/templates/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile.template
index 8a95cad..98854ff 100644
--- a/templates/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM ubuntu:14.04
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../cxx_deps.include"/>
   <%include file="../../run_tests_addons_nocache.include"/>
diff --git a/templates/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile.template b/templates/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile.template
index 42ad6c1..3ad65e9 100644
--- a/templates/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM ubuntu:16.04
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../cxx_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
diff --git a/templates/tools/dockerfile/test/cxx_wheezy_x64/Dockerfile.template b/templates/tools/dockerfile/test/cxx_wheezy_x64/Dockerfile.template
deleted file mode 100644
index 5c38e9a..0000000
--- a/templates/tools/dockerfile/test/cxx_wheezy_x64/Dockerfile.template
+++ /dev/null
@@ -1,56 +0,0 @@
-%YAML 1.2
---- |
-  # Copyright 2016, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-  FROM debian:wheezy
-
-  <%include file="../../apt_get_basic.include"/>
-  <%include file="../../python_deps.include"/>
-  <%include file="../../cxx_deps.include"/>
-
-  RUN apt-get update && apt-get install -y ${'\\'}
-    gcc-4.4 ${'\\'}
-    gcc-4.4-multilib ${'\\'}
-    g++-4.4 ${'\\'}
-    g++-4.4-multilib
-
-  # set up backport to allow installation of Git version > 1.7
-  RUN echo "deb http://http.debian.net/debian wheezy-backports main" \
-    >/etc/apt/sources.list.d/wheezy-backports.list
-  RUN apt-get update -qq
-  RUN apt-get -t wheezy-backports install -qq git
-  
-  RUN wget ${openssl_fallback.base_uri + openssl_fallback.tarball}
-
-  ENV POST_GIT_STEP tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh
-
-  <%include file="../../run_tests_addons.include"/>
-  # Define the default command.
-  CMD ["bash"]
diff --git a/templates/tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh.template b/templates/tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh.template
deleted file mode 100644
index b885101..0000000
--- a/templates/tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh.template
+++ /dev/null
@@ -1,37 +0,0 @@
-%YAML 1.2
---- |
-  #!/bin/bash
-  # Copyright 2016, Google Inc.
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #     * Redistributions of source code must retain the above copyright
-  # notice, this list of conditions and the following disclaimer.
-  #     * Redistributions in binary form must reproduce the above
-  # copyright notice, this list of conditions and the following disclaimer
-  # in the documentation and/or other materials provided with the
-  # distribution.
-  #     * Neither the name of Google Inc. nor the names of its
-  # contributors may be used to endorse or promote products derived from
-  # this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-  set -ex
-
-  cd /var/local/git/grpc
-  cp /${openssl_fallback.tarball} third_party
-  ./tools/openssl/use_openssl.sh
diff --git a/templates/tools/dockerfile/test/fuzzer/Dockerfile.template b/templates/tools/dockerfile/test/fuzzer/Dockerfile.template
index 6d7cb72..32a843a 100644
--- a/templates/tools/dockerfile/test/fuzzer/Dockerfile.template
+++ b/templates/tools/dockerfile/test/fuzzer/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
 
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../cxx_deps.include"/>
   <%include file="../../clang_update.include"/>
diff --git a/templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template
index 93d26b5..bf64ba2 100644
--- a/templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../csharp_deps.include"/>
   <%include file="../../cxx_deps.include"/>
   <%include file="../../node_deps.include"/>
diff --git a/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template
index ceaa9aa..103b1ef 100644
--- a/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/node_jessie_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
 
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
 
   # Install Electron apt dependencies
   RUN apt-get update && apt-get install -y ${'\\'}
diff --git a/templates/tools/dockerfile/test/php7_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/php7_jessie_x64/Dockerfile.template
index e6a213d..1581002 100644
--- a/templates/tools/dockerfile/test/php7_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/php7_jessie_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
   
   <%include file="../../php7_deps.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
   # Define the default command.
diff --git a/templates/tools/dockerfile/test/php_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/php_jessie_x64/Dockerfile.template
index 0cfa373..ced93d0 100644
--- a/templates/tools/dockerfile/test/php_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/php_jessie_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../php_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
diff --git a/templates/tools/dockerfile/test/python_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/python_jessie_x64/Dockerfile.template
index 46fb84b..26ff987 100644
--- a/templates/tools/dockerfile/test/python_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/python_jessie_x64/Dockerfile.template
@@ -32,8 +32,8 @@
   FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
   # Define the default command.
   CMD ["bash"]
-  
\ No newline at end of file
diff --git a/templates/tools/dockerfile/test/python_pyenv_x64/Dockerfile.template b/templates/tools/dockerfile/test/python_pyenv_x64/Dockerfile.template
index f9a4dcb..62d9b92 100644
--- a/templates/tools/dockerfile/test/python_pyenv_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/python_pyenv_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../apt_get_pyenv.include"/>
   <%include file="../../run_tests_addons.include"/>
diff --git a/templates/tools/dockerfile/test/ruby_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/ruby_jessie_x64/Dockerfile.template
index 35838bc..d189985 100644
--- a/templates/tools/dockerfile/test/ruby_jessie_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/ruby_jessie_x64/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../ruby_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
diff --git a/templates/tools/dockerfile/test/sanity/Dockerfile.template b/templates/tools/dockerfile/test/sanity/Dockerfile.template
index 8617666..6943134 100644
--- a/templates/tools/dockerfile/test/sanity/Dockerfile.template
+++ b/templates/tools/dockerfile/test/sanity/Dockerfile.template
@@ -32,6 +32,7 @@
   FROM ubuntu:15.10
   
   <%include file="../../apt_get_basic.include"/>
+  <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../python_deps.include"/>
   #========================
   # Sanity test dependencies
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
index 9b0106e..0fafb0c 100644
--- a/test/core/end2end/cq_verifier.c
+++ b/test/core/end2end/cq_verifier.c
@@ -77,7 +77,7 @@
 };
 
 cq_verifier *cq_verifier_create(grpc_completion_queue *cq) {
-  cq_verifier *v = gpr_malloc(sizeof(cq_verifier));
+  cq_verifier *v = (cq_verifier *)gpr_malloc(sizeof(cq_verifier));
   v->cq = cq;
   v->first_expectation = NULL;
   return v;
@@ -189,23 +189,6 @@
   return res;
 }
 
-static void verify_matches(expectation *e, grpc_event *ev) {
-  GPR_ASSERT(e->type == ev->type);
-  switch (e->type) {
-    case GRPC_QUEUE_SHUTDOWN:
-      gpr_log(GPR_ERROR, "premature queue shutdown");
-      abort();
-      break;
-    case GRPC_OP_COMPLETE:
-      GPR_ASSERT(e->success == ev->success);
-      break;
-    case GRPC_QUEUE_TIMEOUT:
-      gpr_log(GPR_ERROR, "not implemented");
-      abort();
-      break;
-  }
-}
-
 static void expectation_to_strvec(gpr_strvec *buf, expectation *e) {
   char *tmp;
 
@@ -214,7 +197,7 @@
 
   switch (e->type) {
     case GRPC_OP_COMPLETE:
-      gpr_asprintf(&tmp, "GRPC_OP_COMPLETE result=%d %s:%d", e->success,
+      gpr_asprintf(&tmp, "GRPC_OP_COMPLETE success=%d %s:%d", e->success,
                    e->file, e->line);
       gpr_strvec_add(buf, tmp);
       break;
@@ -248,6 +231,32 @@
   abort();
 }
 
+static void verify_matches(expectation *e, grpc_event *ev) {
+  GPR_ASSERT(e->type == ev->type);
+  switch (e->type) {
+    case GRPC_OP_COMPLETE:
+      if (e->success != ev->success) {
+        gpr_strvec expected;
+        gpr_strvec_init(&expected);
+        expectation_to_strvec(&expected, e);
+        char *s = gpr_strvec_flatten(&expected, NULL);
+        gpr_strvec_destroy(&expected);
+        gpr_log(GPR_ERROR, "actual success does not match expected: %s", s);
+        gpr_free(s);
+        abort();
+      }
+      break;
+    case GRPC_QUEUE_SHUTDOWN:
+      gpr_log(GPR_ERROR, "premature queue shutdown");
+      abort();
+      break;
+    case GRPC_QUEUE_TIMEOUT:
+      gpr_log(GPR_ERROR, "not implemented");
+      abort();
+      break;
+  }
+}
+
 void cq_verify(cq_verifier *v) {
   const gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10);
   while (v->first_expectation != NULL) {
@@ -305,7 +314,7 @@
 
 static void add(cq_verifier *v, const char *file, int line,
                 grpc_completion_type type, void *tag, bool success) {
-  expectation *e = gpr_malloc(sizeof(expectation));
+  expectation *e = (expectation *)gpr_malloc(sizeof(expectation));
   e->type = type;
   e->file = file;
   e->line = line;
diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c
index 6a71c20..736b224 100644
--- a/test/core/end2end/fake_resolver.c
+++ b/test/core/end2end/fake_resolver.c
@@ -56,9 +56,6 @@
 
 #include "test/core/end2end/fake_resolver.h"
 
-#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \
-  "grpc.fake_resolver.response_generator"
-
 //
 // fake_resolver
 //
@@ -139,7 +136,7 @@
 grpc_fake_resolver_response_generator*
 grpc_fake_resolver_response_generator_create() {
   grpc_fake_resolver_response_generator* generator =
-      gpr_zalloc(sizeof(*generator));
+      (grpc_fake_resolver_response_generator*)gpr_zalloc(sizeof(*generator));
   gpr_ref_init(&generator->refcount, 1);
   return generator;
 }
@@ -160,7 +157,8 @@
 
 static void set_response_cb(grpc_exec_ctx* exec_ctx, void* arg,
                             grpc_error* error) {
-  grpc_fake_resolver_response_generator* generator = arg;
+  grpc_fake_resolver_response_generator* generator =
+      (grpc_fake_resolver_response_generator*)arg;
   fake_resolver* r = generator->resolver;
   if (r->next_results != NULL) {
     grpc_channel_args_destroy(exec_ctx, r->next_results);
@@ -183,11 +181,13 @@
 }
 
 static void* response_generator_arg_copy(void* p) {
-  return grpc_fake_resolver_response_generator_ref(p);
+  return grpc_fake_resolver_response_generator_ref(
+      (grpc_fake_resolver_response_generator*)p);
 }
 
 static void response_generator_arg_destroy(grpc_exec_ctx* exec_ctx, void* p) {
-  grpc_fake_resolver_response_generator_unref(p);
+  grpc_fake_resolver_response_generator_unref(
+      (grpc_fake_resolver_response_generator*)p);
 }
 
 static int response_generator_cmp(void* a, void* b) { return GPR_ICMP(a, b); }
@@ -211,7 +211,7 @@
   const grpc_arg* arg =
       grpc_channel_args_find(args, GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR);
   if (arg == NULL || arg->type != GRPC_ARG_POINTER) return NULL;
-  return arg->value.pointer.p;
+  return (grpc_fake_resolver_response_generator*)arg->value.pointer.p;
 }
 
 //
@@ -225,7 +225,7 @@
 static grpc_resolver* fake_resolver_create(grpc_exec_ctx* exec_ctx,
                                            grpc_resolver_factory* factory,
                                            grpc_resolver_args* args) {
-  fake_resolver* r = gpr_zalloc(sizeof(*r));
+  fake_resolver* r = (fake_resolver*)gpr_zalloc(sizeof(*r));
   r->channel_args = grpc_channel_args_copy(args->args);
   grpc_resolver_init(&r->base, &fake_resolver_vtable, args->combiner);
   grpc_fake_resolver_response_generator* response_generator =
diff --git a/test/core/end2end/fake_resolver.h b/test/core/end2end/fake_resolver.h
index 447289a..d9668d0 100644
--- a/test/core/end2end/fake_resolver.h
+++ b/test/core/end2end/fake_resolver.h
@@ -38,6 +38,9 @@
 
 #include "test/core/util/test_config.h"
 
+#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \
+  "grpc.fake_resolver.response_generator"
+
 void grpc_fake_resolver_init();
 
 // Instances of \a grpc_fake_resolver_response_generator are passed to the
diff --git a/test/core/end2end/fixtures/http_proxy_fixture.c b/test/core/end2end/fixtures/http_proxy_fixture.c
index f0d0948..5df43b9 100644
--- a/test/core/end2end/fixtures/http_proxy_fixture.c
+++ b/test/core/end2end/fixtures/http_proxy_fixture.c
@@ -156,7 +156,7 @@
 // Callback for writing proxy data to the client.
 static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
-  proxy_connection* conn = arg;
+  proxy_connection* conn = (proxy_connection*)arg;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, true /* is_client */,
                             "HTTP proxy client write", error);
@@ -181,7 +181,7 @@
 // Callback for writing proxy data to the backend server.
 static void on_server_write_done(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
-  proxy_connection* conn = arg;
+  proxy_connection* conn = (proxy_connection*)arg;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, false /* is_client */,
                             "HTTP proxy server write", error);
@@ -207,7 +207,7 @@
 // the backend server.
 static void on_client_read_done(grpc_exec_ctx* exec_ctx, void* arg,
                                 grpc_error* error) {
-  proxy_connection* conn = arg;
+  proxy_connection* conn = (proxy_connection*)arg;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, true /* is_client */,
                             "HTTP proxy client read", error);
@@ -239,7 +239,7 @@
 // proxied to the client.
 static void on_server_read_done(grpc_exec_ctx* exec_ctx, void* arg,
                                 grpc_error* error) {
-  proxy_connection* conn = arg;
+  proxy_connection* conn = (proxy_connection*)arg;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, false /* is_client */,
                             "HTTP proxy server read", error);
@@ -270,7 +270,7 @@
 // Callback to write the HTTP response for the CONNECT request.
 static void on_write_response_done(grpc_exec_ctx* exec_ctx, void* arg,
                                    grpc_error* error) {
-  proxy_connection* conn = arg;
+  proxy_connection* conn = (proxy_connection*)arg;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, true /* is_client */,
                             "HTTP proxy write response", error);
@@ -294,7 +294,7 @@
 // CONNECT request.
 static void on_server_connect_done(grpc_exec_ctx* exec_ctx, void* arg,
                                    grpc_error* error) {
-  proxy_connection* conn = arg;
+  proxy_connection* conn = (proxy_connection*)arg;
   if (error != GRPC_ERROR_NONE) {
     // TODO(roth): Technically, in this case, we should handle the error
     // by returning an HTTP response to the client indicating that the
@@ -324,7 +324,7 @@
 // which will cause the client connection to be dropped.
 static void on_read_request_done(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
-  proxy_connection* conn = arg;
+  proxy_connection* conn = (proxy_connection*)arg;
   gpr_log(GPR_DEBUG, "on_read_request_done: %p %s", conn,
           grpc_error_string(error));
   if (error != GRPC_ERROR_NONE) {
@@ -389,9 +389,9 @@
                       grpc_endpoint* endpoint, grpc_pollset* accepting_pollset,
                       grpc_tcp_server_acceptor* acceptor) {
   gpr_free(acceptor);
-  grpc_end2end_http_proxy* proxy = arg;
+  grpc_end2end_http_proxy* proxy = (grpc_end2end_http_proxy*)arg;
   // Instantiate proxy_connection.
-  proxy_connection* conn = gpr_zalloc(sizeof(*conn));
+  proxy_connection* conn = (proxy_connection*)gpr_zalloc(sizeof(*conn));
   gpr_ref(&proxy->users);
   conn->client_endpoint = endpoint;
   conn->proxy = proxy;
@@ -430,7 +430,7 @@
 //
 
 static void thread_main(void* arg) {
-  grpc_end2end_http_proxy* proxy = arg;
+  grpc_end2end_http_proxy* proxy = (grpc_end2end_http_proxy*)arg;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   do {
     gpr_ref(&proxy->users);
@@ -450,7 +450,8 @@
 
 grpc_end2end_http_proxy* grpc_end2end_http_proxy_create(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_end2end_http_proxy* proxy = gpr_malloc(sizeof(*proxy));
+  grpc_end2end_http_proxy* proxy =
+      (grpc_end2end_http_proxy*)gpr_malloc(sizeof(*proxy));
   memset(proxy, 0, sizeof(*proxy));
   gpr_ref_init(&proxy->users, 1);
   // Construct proxy address.
@@ -473,7 +474,7 @@
   GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(port == proxy_port);
   // Start server.
-  proxy->pollset = gpr_zalloc(grpc_pollset_size());
+  proxy->pollset = (grpc_pollset*)gpr_zalloc(grpc_pollset_size());
   grpc_pollset_init(proxy->pollset, &proxy->mu);
   grpc_tcp_server_start(&exec_ctx, proxy->server, &proxy->pollset, 1, on_accept,
                         proxy);
@@ -487,7 +488,7 @@
 
 static void destroy_pollset(grpc_exec_ctx* exec_ctx, void* arg,
                             grpc_error* error) {
-  grpc_pollset* pollset = arg;
+  grpc_pollset* pollset = (grpc_pollset*)arg;
   grpc_pollset_destroy(pollset);
   gpr_free(pollset);
 }
diff --git a/test/core/security/oauth2_utils.c b/test/core/security/oauth2_utils.c
index f0550db..8386257 100644
--- a/test/core/security/oauth2_utils.c
+++ b/test/core/security/oauth2_utils.c
@@ -55,7 +55,7 @@
                                grpc_credentials_md *md_elems, size_t num_md,
                                grpc_credentials_status status,
                                const char *error_details) {
-  oauth2_request *request = user_data;
+  oauth2_request *request = (oauth2_request *)user_data;
   char *token = NULL;
   grpc_slice token_slice;
   if (status == GRPC_CREDENTIALS_ERROR) {
@@ -63,7 +63,7 @@
   } else {
     GPR_ASSERT(num_md == 1);
     token_slice = md_elems[0].value;
-    token = gpr_malloc(GRPC_SLICE_LENGTH(token_slice) + 1);
+    token = (char *)gpr_malloc(GRPC_SLICE_LENGTH(token_slice) + 1);
     memcpy(token, GRPC_SLICE_START_PTR(token_slice),
            GRPC_SLICE_LENGTH(token_slice));
     token[GRPC_SLICE_LENGTH(token_slice)] = '\0';
@@ -87,7 +87,7 @@
   grpc_closure do_nothing_closure;
   grpc_auth_metadata_context null_ctx = {"", "", NULL, NULL};
 
-  grpc_pollset *pollset = gpr_zalloc(grpc_pollset_size());
+  grpc_pollset *pollset = (grpc_pollset *)gpr_zalloc(grpc_pollset_size());
   grpc_pollset_init(pollset, &request.mu);
   request.pops = grpc_polling_entity_create_from_pollset(pollset);
   request.is_done = 0;
diff --git a/test/core/slice/slice_buffer_test.c b/test/core/slice/slice_buffer_test.c
index bf9ae19..41cff3d 100644
--- a/test/core/slice/slice_buffer_test.c
+++ b/test/core/slice/slice_buffer_test.c
@@ -115,8 +115,8 @@
   grpc_slice_buffer_move_first(&src, 2, &dst);
   src_len -= 2;
   dst_len += 2;
-  GPR_ASSERT(src.length == src.length);
-  GPR_ASSERT(dst.length == dst.length);
+  GPR_ASSERT(src.length == src_len);
+  GPR_ASSERT(dst.length == dst_len);
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/transport/bdp_estimator_test.c b/test/core/transport/bdp_estimator_test.c
index f55a3ca..a6f1a55 100644
--- a/test/core/transport/bdp_estimator_test.c
+++ b/test/core/transport/bdp_estimator_test.c
@@ -33,6 +33,7 @@
 
 #include "src/core/lib/transport/bdp_estimator.h"
 
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
@@ -64,6 +65,8 @@
     GPR_ASSERT(grpc_bdp_estimator_add_incoming_bytes(estimator, samples[i]) ==
                false);
   }
+  gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                               gpr_time_from_millis(1, GPR_TIMESPAN)));
   grpc_bdp_estimator_complete_ping(estimator);
 }
 
@@ -123,24 +126,25 @@
   gpr_log(GPR_INFO, "test_get_estimate_random_values(%" PRIdPTR ")", n);
   grpc_bdp_estimator est;
   grpc_bdp_estimator_init(&est, "test");
-  int min = INT_MAX;
-  int max = 65535;  // Windows rand() has limited range, make sure the ASSERT
-                    // passes
+  const int kMaxSample = 65535;
+  int min = kMaxSample;
+  int max = 0;
   for (size_t i = 0; i < n; i++) {
-    int sample = rand();
+    int sample = rand() % (kMaxSample + 1);
     if (sample < min) min = sample;
     if (sample > max) max = sample;
     add_sample(&est, sample);
     if (i >= 3) {
       gpr_log(GPR_DEBUG, "est:%" PRId64 " min:%d max:%d", get_estimate(&est),
               min, max);
-      GPR_ASSERT(get_estimate(&est) <= 2 * next_pow_2(max));
+      GPR_ASSERT(get_estimate(&est) <= GPR_MAX(65536, 2 * next_pow_2(max)));
     }
   }
 }
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_noop();
   test_get_estimate_no_samples();
   test_get_estimate_1_sample();
@@ -149,5 +153,6 @@
   for (size_t i = 3; i < 1000; i = i * 3 / 2) {
     test_get_estimate_random_values(i);
   }
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/util/trickle_endpoint.c b/test/core/util/trickle_endpoint.c
index 58ac597..69386a0 100644
--- a/test/core/util/trickle_endpoint.c
+++ b/test/core/util/trickle_endpoint.c
@@ -44,6 +44,8 @@
 #include <grpc/support/useful.h>
 #include "src/core/lib/slice/slice_internal.h"
 
+#define WRITE_BUFFER_SIZE (2 * 1024 * 1024)
+
 typedef struct {
   grpc_endpoint base;
   double bytes_per_second;
@@ -55,6 +57,7 @@
   grpc_slice_buffer writing_buffer;
   grpc_error *error;
   bool writing;
+  grpc_closure *write_cb;
 } trickle_endpoint;
 
 static void te_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -63,10 +66,20 @@
   grpc_endpoint_read(exec_ctx, te->wrapped, slices, cb);
 }
 
+static void maybe_call_write_cb_locked(grpc_exec_ctx *exec_ctx,
+                                       trickle_endpoint *te) {
+  if (te->write_cb != NULL && (te->error != GRPC_ERROR_NONE ||
+                               te->write_buffer.length <= WRITE_BUFFER_SIZE)) {
+    grpc_closure_sched(exec_ctx, te->write_cb, GRPC_ERROR_REF(te->error));
+    te->write_cb = NULL;
+  }
+}
+
 static void te_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
                      grpc_slice_buffer *slices, grpc_closure *cb) {
   trickle_endpoint *te = (trickle_endpoint *)ep;
   gpr_mu_lock(&te->mu);
+  GPR_ASSERT(te->write_cb == NULL);
   if (te->write_buffer.length == 0) {
     te->last_write = gpr_now(GPR_CLOCK_MONOTONIC);
   }
@@ -74,7 +87,8 @@
     grpc_slice_buffer_add(&te->write_buffer,
                           grpc_slice_copy(slices->slices[i]));
   }
-  grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_REF(te->error));
+  te->write_cb = cb;
+  maybe_call_write_cb_locked(exec_ctx, te);
   gpr_mu_unlock(&te->mu);
 }
 
@@ -102,6 +116,7 @@
   if (te->error == GRPC_ERROR_NONE) {
     te->error = GRPC_ERROR_REF(why);
   }
+  maybe_call_write_cb_locked(exec_ctx, te);
   gpr_mu_unlock(&te->mu);
   grpc_endpoint_shutdown(exec_ctx, te->wrapped, why);
 }
@@ -157,6 +172,7 @@
   te->base.vtable = &vtable;
   te->wrapped = wrap;
   te->bytes_per_second = bytes_per_second;
+  te->write_cb = NULL;
   gpr_mu_init(&te->mu);
   grpc_slice_buffer_init(&te->write_buffer);
   grpc_slice_buffer_init(&te->writing_buffer);
@@ -187,9 +203,18 @@
       grpc_endpoint_write(
           exec_ctx, te->wrapped, &te->writing_buffer,
           grpc_closure_create(te_finish_write, te, grpc_schedule_on_exec_ctx));
+      maybe_call_write_cb_locked(exec_ctx, te);
     }
   }
   size_t backlog = te->write_buffer.length;
   gpr_mu_unlock(&te->mu);
   return backlog;
 }
+
+size_t grpc_trickle_get_backlog(grpc_endpoint *ep) {
+  trickle_endpoint *te = (trickle_endpoint *)ep;
+  gpr_mu_lock(&te->mu);
+  size_t backlog = te->write_buffer.length;
+  gpr_mu_unlock(&te->mu);
+  return backlog;
+}
diff --git a/test/core/util/trickle_endpoint.h b/test/core/util/trickle_endpoint.h
index 7e8d9d9..e513774 100644
--- a/test/core/util/trickle_endpoint.h
+++ b/test/core/util/trickle_endpoint.h
@@ -43,4 +43,6 @@
 size_t grpc_trickle_endpoint_trickle(grpc_exec_ctx *exec_ctx,
                                      grpc_endpoint *endpoint);
 
+size_t grpc_trickle_get_backlog(grpc_endpoint *endpoint);
+
 #endif
diff --git a/test/cpp/client/credentials_test.cc b/test/cpp/client/credentials_test.cc
index 418a544..23b3b2e 100644
--- a/test/cpp/client/credentials_test.cc
+++ b/test/cpp/client/credentials_test.cc
@@ -50,6 +50,10 @@
   EXPECT_EQ(static_cast<CallCredentials*>(nullptr), bad1.get());
 }
 
+TEST_F(CredentialsTest, DefaultCredentials) {
+  auto creds = GoogleDefaultCredentials();
+}
+
 }  // namespace testing
 }  // namespace grpc
 
diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden
index 8e3ae32..eb12d37 100644
--- a/test/cpp/codegen/compiler_test_golden
+++ b/test/cpp/codegen/compiler_test_golden
@@ -69,6 +69,9 @@
 // ServiceA leading comment 1
 class ServiceA final {
  public:
+  static constexpr char const* service_full_name() {
+    return "grpc.testing.ServiceA";
+  }
   class StubInterface {
    public:
     virtual ~StubInterface() {}
@@ -373,6 +376,9 @@
 // ServiceB leading comment 1
 class ServiceB final {
  public:
+  static constexpr char const* service_full_name() {
+    return "grpc.testing.ServiceB";
+  }
   class StubInterface {
    public:
     virtual ~StubInterface() {}
diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD
index f1212e1..e867493 100644
--- a/test/cpp/end2end/BUILD
+++ b/test/cpp/end2end/BUILD
@@ -48,10 +48,10 @@
         "//:grpc",
         "//:grpc++",
         "//external:gtest",
+        "//src/proto/grpc/health/v1:health_proto",
         "//src/proto/grpc/testing:echo_messages_proto",
         "//src/proto/grpc/testing:echo_proto",
         "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
-        "//src/proto/grpc/health/v1:health_proto",
         "//test/core/util:gpr_test_util",
         "//test/core/util:grpc_test_util",
         "//test/cpp/util:test_util",
@@ -198,6 +198,27 @@
     ],
 )
 
+
+cc_test(
+    name = "grpclb_end2end_test",
+    srcs = ["grpclb_end2end_test.cc"],
+    deps = [
+        ":test_service_impl",
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//external:gtest",
+        "//src/proto/grpc/lb/v1:load_balancer_proto",
+        "//src/proto/grpc/testing:echo_messages_proto",
+        "//src/proto/grpc/testing:echo_proto",
+        "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+        "//test/core/end2end:fake_resolver",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:test_util",
+    ],
+)
+
 cc_test(
     name = "proto_server_reflection_test",
     srcs = ["proto_server_reflection_test.cc"],
diff --git a/test/cpp/end2end/grpclb_end2end_test.cc b/test/cpp/end2end/grpclb_end2end_test.cc
new file mode 100644
index 0000000..8417f1a
--- /dev/null
+++ b/test/cpp/end2end/grpclb_end2end_test.cc
@@ -0,0 +1,639 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <thread>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "test/core/end2end/fake_resolver.h"
+}
+
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/end2end/test_service_impl.h"
+
+#include "src/proto/grpc/lb/v1/load_balancer.grpc.pb.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+
+// TODO(dgq): Other scenarios in need of testing:
+// - Send a serverlist with faulty ip:port addresses (port > 2^16, etc).
+// - Test reception of invalid serverlist
+// - Test pinging
+// - Test against a non-LB server.
+// - Random LB server closing the stream unexpectedly.
+// - Test using DNS-resolvable names (localhost?)
+// - Test handling of creation of faulty RR instance by having the LB return a
+//   serverlist with non-existent backends after having initially returned a
+//   valid one.
+//
+// Findings from end to end testing to be covered here:
+// - Handling of LB servers restart, including reconnection after backing-off
+//   retries.
+// - Destruction of load balanced channel (and therefore of grpclb instance)
+//   while:
+//   1) the internal LB call is still active. This should work by virtue
+//   of the weak reference the LB call holds. The call should be terminated as
+//   part of the grpclb shutdown process.
+//   2) the retry timer is active. Again, the weak reference it holds should
+//   prevent a premature call to \a glb_destroy.
+// - Restart of backend servers with no changes to serverlist. This exercises
+//   the RR handover mechanism.
+
+using std::chrono::system_clock;
+
+using grpc::lb::v1::LoadBalanceResponse;
+using grpc::lb::v1::LoadBalanceRequest;
+using grpc::lb::v1::LoadBalancer;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+template <typename ServiceType>
+class CountedService : public ServiceType {
+ public:
+  int request_count() {
+    std::unique_lock<std::mutex> lock(mu_);
+    return request_count_;
+  }
+
+  int response_count() {
+    std::unique_lock<std::mutex> lock(mu_);
+    return response_count_;
+  }
+
+  void IncreaseResponseCount() {
+    std::unique_lock<std::mutex> lock(mu_);
+    ++response_count_;
+  }
+  void IncreaseRequestCount() {
+    std::unique_lock<std::mutex> lock(mu_);
+    ++request_count_;
+  }
+
+ protected:
+  std::mutex mu_;
+
+ private:
+  int request_count_ = 0;
+  int response_count_ = 0;
+};
+
+using BackendService = CountedService<TestServiceImpl>;
+using BalancerService = CountedService<LoadBalancer::Service>;
+
+class BackendServiceImpl : public BackendService {
+ public:
+  BackendServiceImpl() {}
+
+  Status Echo(ServerContext* context, const EchoRequest* request,
+              EchoResponse* response) override {
+    IncreaseRequestCount();
+    const auto status = TestServiceImpl::Echo(context, request, response);
+    IncreaseResponseCount();
+    return status;
+  }
+};
+
+grpc::string Ip4ToPackedString(const char* ip_str) {
+  struct in_addr ip4;
+  GPR_ASSERT(inet_pton(AF_INET, ip_str, &ip4) == 1);
+  return grpc::string(reinterpret_cast<const char*>(&ip4), sizeof(ip4));
+}
+
+struct ClientStats {
+  size_t num_calls_started = 0;
+  size_t num_calls_finished = 0;
+  size_t num_calls_finished_with_drop_for_rate_limiting = 0;
+  size_t num_calls_finished_with_drop_for_load_balancing = 0;
+  size_t num_calls_finished_with_client_failed_to_send = 0;
+  size_t num_calls_finished_known_received = 0;
+
+  ClientStats& operator+=(const ClientStats& other) {
+    num_calls_started += other.num_calls_started;
+    num_calls_finished += other.num_calls_finished;
+    num_calls_finished_with_drop_for_rate_limiting +=
+        other.num_calls_finished_with_drop_for_rate_limiting;
+    num_calls_finished_with_drop_for_load_balancing +=
+        other.num_calls_finished_with_drop_for_load_balancing;
+    num_calls_finished_with_client_failed_to_send +=
+        other.num_calls_finished_with_client_failed_to_send;
+    num_calls_finished_known_received +=
+        other.num_calls_finished_known_received;
+    return *this;
+  }
+};
+
+class BalancerServiceImpl : public BalancerService {
+ public:
+  using Stream = ServerReaderWriter<LoadBalanceResponse, LoadBalanceRequest>;
+  using ResponseDelayPair = std::pair<LoadBalanceResponse, int>;
+
+  explicit BalancerServiceImpl(int client_load_reporting_interval_seconds)
+      : client_load_reporting_interval_seconds_(
+            client_load_reporting_interval_seconds),
+        shutdown_(false) {}
+
+  Status BalanceLoad(ServerContext* context, Stream* stream) override {
+    LoadBalanceRequest request;
+    stream->Read(&request);
+    IncreaseRequestCount();
+    gpr_log(GPR_INFO, "LB: recv msg '%s'", request.DebugString().c_str());
+
+    if (client_load_reporting_interval_seconds_ > 0) {
+      LoadBalanceResponse initial_response;
+      initial_response.mutable_initial_response()
+          ->mutable_client_stats_report_interval()
+          ->set_seconds(client_load_reporting_interval_seconds_);
+      stream->Write(initial_response);
+    }
+
+    std::vector<ResponseDelayPair> responses_and_delays;
+    {
+      std::unique_lock<std::mutex> lock(mu_);
+      responses_and_delays = responses_and_delays_;
+    }
+    for (const auto& response_and_delay : responses_and_delays) {
+      if (shutdown_) break;
+      SendResponse(stream, response_and_delay.first, response_and_delay.second);
+    }
+
+    if (client_load_reporting_interval_seconds_ > 0) {
+      request.Clear();
+      stream->Read(&request);
+      gpr_log(GPR_INFO, "LB: recv client load report msg: '%s'",
+              request.DebugString().c_str());
+      GPR_ASSERT(request.has_client_stats());
+      client_stats_.num_calls_started +=
+          request.client_stats().num_calls_started();
+      client_stats_.num_calls_finished +=
+          request.client_stats().num_calls_finished();
+      client_stats_.num_calls_finished_with_drop_for_rate_limiting +=
+          request.client_stats()
+              .num_calls_finished_with_drop_for_rate_limiting();
+      client_stats_.num_calls_finished_with_drop_for_load_balancing +=
+          request.client_stats()
+              .num_calls_finished_with_drop_for_load_balancing();
+      client_stats_.num_calls_finished_with_client_failed_to_send +=
+          request.client_stats()
+              .num_calls_finished_with_client_failed_to_send();
+      client_stats_.num_calls_finished_known_received +=
+          request.client_stats().num_calls_finished_known_received();
+      std::lock_guard<std::mutex> lock(mu_);
+      cond_.notify_one();
+    }
+
+    return Status::OK;
+  }
+
+  void add_response(const LoadBalanceResponse& response, int send_after_ms) {
+    std::unique_lock<std::mutex> lock(mu_);
+    responses_and_delays_.push_back(std::make_pair(response, send_after_ms));
+  }
+
+  void Shutdown() {
+    std::unique_lock<std::mutex> lock(mu_);
+    shutdown_ = true;
+  }
+
+  static LoadBalanceResponse BuildResponseForBackends(
+      const std::vector<int>& backend_ports) {
+    LoadBalanceResponse response;
+    for (const int backend_port : backend_ports) {
+      auto* server = response.mutable_server_list()->add_servers();
+      server->set_ip_address(Ip4ToPackedString("127.0.0.1"));
+      server->set_port(backend_port);
+    }
+    return response;
+  }
+
+  const ClientStats& WaitForLoadReport() {
+    std::unique_lock<std::mutex> lock(mu_);
+    cond_.wait(lock);
+    return client_stats_;
+  }
+
+ private:
+  void SendResponse(Stream* stream, const LoadBalanceResponse& response,
+                    int delay_ms) {
+    gpr_log(GPR_INFO, "LB: sleeping for %d ms...", delay_ms);
+    gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                                 gpr_time_from_millis(delay_ms, GPR_TIMESPAN)));
+    gpr_log(GPR_INFO, "LB: Woke up! Sending response '%s'",
+            response.DebugString().c_str());
+    stream->Write(response);
+    IncreaseResponseCount();
+  }
+
+  const int client_load_reporting_interval_seconds_;
+  std::vector<ResponseDelayPair> responses_and_delays_;
+  std::mutex mu_;
+  std::condition_variable cond_;
+  ClientStats client_stats_;
+  bool shutdown_;
+};
+
+class GrpclbEnd2endTest : public ::testing::Test {
+ protected:
+  GrpclbEnd2endTest(int num_backends, int num_balancers,
+                    int client_load_reporting_interval_seconds)
+      : server_host_("localhost"),
+        num_backends_(num_backends),
+        num_balancers_(num_balancers),
+        client_load_reporting_interval_seconds_(
+            client_load_reporting_interval_seconds) {}
+
+  void SetUp() override {
+    response_generator_ = grpc_fake_resolver_response_generator_create();
+    // Start the backends.
+    for (size_t i = 0; i < num_backends_; ++i) {
+      backends_.emplace_back(new BackendServiceImpl());
+      backend_servers_.emplace_back(ServerThread<BackendService>(
+          "backend", server_host_, backends_.back().get()));
+    }
+    // Start the load balancers.
+    for (size_t i = 0; i < num_balancers_; ++i) {
+      balancers_.emplace_back(
+          new BalancerServiceImpl(client_load_reporting_interval_seconds_));
+      balancer_servers_.emplace_back(ServerThread<BalancerService>(
+          "balancer", server_host_, balancers_.back().get()));
+    }
+    ResetStub();
+    std::vector<AddressData> addresses;
+    for (size_t i = 0; i < balancer_servers_.size(); ++i) {
+      addresses.emplace_back(AddressData{balancer_servers_[i].port_, true, ""});
+    }
+    SetNextResolution(addresses);
+  }
+
+  void TearDown() override {
+    for (size_t i = 0; i < backends_.size(); ++i) {
+      backend_servers_[i].Shutdown();
+    }
+    for (size_t i = 0; i < balancers_.size(); ++i) {
+      balancers_[i]->Shutdown();
+      balancer_servers_[i].Shutdown();
+    }
+    grpc_fake_resolver_response_generator_unref(response_generator_);
+  }
+
+  void ResetStub() {
+    ChannelArguments args;
+    args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
+                    response_generator_);
+    std::ostringstream uri;
+    uri << "test:///servername_not_used";
+    channel_ =
+        CreateCustomChannel(uri.str(), InsecureChannelCredentials(), args);
+    stub_ = grpc::testing::EchoTestService::NewStub(channel_);
+  }
+
+  ClientStats WaitForLoadReports() {
+    ClientStats client_stats;
+    for (const auto& balancer : balancers_) {
+      client_stats += balancer->WaitForLoadReport();
+    }
+    return client_stats;
+  }
+
+  struct AddressData {
+    int port;
+    bool is_balancer;
+    grpc::string balancer_name;
+  };
+
+  void SetNextResolution(const std::vector<AddressData>& address_data) {
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    grpc_lb_addresses* addresses =
+        grpc_lb_addresses_create(address_data.size(), nullptr);
+    for (size_t i = 0; i < address_data.size(); ++i) {
+      char* lb_uri_str;
+      gpr_asprintf(&lb_uri_str, "ipv4:127.0.0.1:%d", address_data[i].port);
+      grpc_uri* lb_uri = grpc_uri_parse(&exec_ctx, lb_uri_str, true);
+      GPR_ASSERT(lb_uri != nullptr);
+      grpc_lb_addresses_set_address_from_uri(
+          addresses, i, lb_uri, address_data[i].is_balancer,
+          address_data[i].balancer_name.c_str(), nullptr);
+      grpc_uri_destroy(lb_uri);
+      gpr_free(lb_uri_str);
+    }
+    grpc_arg fake_addresses = grpc_lb_addresses_create_channel_arg(addresses);
+    grpc_channel_args fake_result = {1, &fake_addresses};
+    grpc_fake_resolver_response_generator_set_response(
+        &exec_ctx, response_generator_, &fake_result);
+    grpc_lb_addresses_destroy(&exec_ctx, addresses);
+    grpc_exec_ctx_finish(&exec_ctx);
+  }
+
+  const std::vector<int> GetBackendPorts() const {
+    std::vector<int> backend_ports;
+    for (const auto& bs : backend_servers_) {
+      backend_ports.push_back(bs.port_);
+    }
+    return backend_ports;
+  }
+
+  void ScheduleResponseForBalancer(size_t i,
+                                   const LoadBalanceResponse& response,
+                                   int delay_ms) {
+    balancers_.at(i)->add_response(response, delay_ms);
+  }
+
+  std::vector<std::pair<Status, EchoResponse>> SendRpc(const string& message,
+                                                       int num_rpcs,
+                                                       int timeout_ms = 1000) {
+    std::vector<std::pair<Status, EchoResponse>> results;
+    EchoRequest request;
+    EchoResponse response;
+    request.set_message(message);
+    for (int i = 0; i < num_rpcs; i++) {
+      ClientContext context;
+      context.set_deadline(grpc_timeout_milliseconds_to_deadline(timeout_ms));
+      Status status = stub_->Echo(&context, request, &response);
+      results.push_back(std::make_pair(status, response));
+    }
+    return results;
+  }
+
+  template <typename T>
+  struct ServerThread {
+    explicit ServerThread(const grpc::string& type,
+                          const grpc::string& server_host, T* service)
+        : type_(type), service_(service) {
+      port_ = grpc_pick_unused_port_or_die();
+      gpr_log(GPR_INFO, "starting %s server on port %d", type_.c_str(), port_);
+      std::mutex mu;
+      std::condition_variable cond;
+      thread_.reset(new std::thread(
+          std::bind(&ServerThread::Start, this, server_host, &mu, &cond)));
+      std::unique_lock<std::mutex> lock(mu);
+      cond.wait(lock);
+      gpr_log(GPR_INFO, "%s server startup complete", type_.c_str());
+    }
+
+    void Start(const grpc::string& server_host, std::mutex* mu,
+               std::condition_variable* cond) {
+      std::ostringstream server_address;
+      server_address << server_host << ":" << port_;
+      ServerBuilder builder;
+      builder.AddListeningPort(server_address.str(),
+                               InsecureServerCredentials());
+      builder.RegisterService(service_);
+      server_ = builder.BuildAndStart();
+      std::lock_guard<std::mutex> lock(*mu);
+      cond->notify_one();
+    }
+
+    void Shutdown() {
+      gpr_log(GPR_INFO, "%s about to shutdown", type_.c_str());
+      server_->Shutdown();
+      thread_->join();
+      gpr_log(GPR_INFO, "%s shutdown completed", type_.c_str());
+    }
+
+    int port_;
+    grpc::string type_;
+    std::unique_ptr<Server> server_;
+    T* service_;
+    std::unique_ptr<std::thread> thread_;
+  };
+
+  const grpc::string kMessage_ = "Live long and prosper.";
+  const grpc::string server_host_;
+  const size_t num_backends_;
+  const size_t num_balancers_;
+  const int client_load_reporting_interval_seconds_;
+  std::shared_ptr<Channel> channel_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+
+  std::vector<std::unique_ptr<BackendServiceImpl>> backends_;
+  std::vector<std::unique_ptr<BalancerServiceImpl>> balancers_;
+
+  std::vector<ServerThread<BackendService>> backend_servers_;
+  std::vector<ServerThread<BalancerService>> balancer_servers_;
+
+  grpc_fake_resolver_response_generator* response_generator_;
+};
+
+class SingleBalancerTest : public GrpclbEnd2endTest {
+ public:
+  SingleBalancerTest() : GrpclbEnd2endTest(4, 1, 0) {}
+};
+
+TEST_F(SingleBalancerTest, Vanilla) {
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts()), 0);
+  // Make sure that trying to connect works without a call.
+  channel_->GetState(true /* try_to_connect */);
+  // Start servers and send 100 RPCs per server.
+  const auto& statuses_and_responses = SendRpc(kMessage_, 100 * num_backends_);
+
+  for (const auto& status_and_response : statuses_and_responses) {
+    EXPECT_TRUE(status_and_response.first.ok());
+    EXPECT_EQ(status_and_response.second.message(), kMessage_);
+  }
+
+  // Each backend should have gotten 100 requests.
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    EXPECT_EQ(100, backend_servers_[i].service_->request_count());
+  }
+  // The balancer got a single request.
+  EXPECT_EQ(1, balancer_servers_[0].service_->request_count());
+  // and sent a single response.
+  EXPECT_EQ(1, balancer_servers_[0].service_->response_count());
+
+  // Check LB policy name for the channel.
+  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
+}
+
+TEST_F(SingleBalancerTest, InitiallyEmptyServerlist) {
+  const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
+  const int kCallDeadlineMs = 1000 * grpc_test_slowdown_factor();
+
+  // First response is an empty serverlist, sent right away.
+  ScheduleResponseForBalancer(0, LoadBalanceResponse(), 0);
+  // Send non-empty serverlist only after kServerlistDelayMs
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts()),
+      kServerlistDelayMs);
+
+  const auto t0 = system_clock::now();
+  // Client will block: LB will initially send empty serverlist.
+  const auto& statuses_and_responses =
+      SendRpc(kMessage_, num_backends_, kCallDeadlineMs);
+  const auto ellapsed_ms =
+      std::chrono::duration_cast<std::chrono::milliseconds>(
+          system_clock::now() - t0);
+  // but eventually, the LB sends a serverlist update that allows the call to
+  // proceed. The call delay must be larger than the delay in sending the
+  // populated serverlist but under the call's deadline.
+  EXPECT_GT(ellapsed_ms.count(), kServerlistDelayMs);
+  EXPECT_LT(ellapsed_ms.count(), kCallDeadlineMs);
+
+  // Each backend should have gotten 1 request.
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    EXPECT_EQ(1, backend_servers_[i].service_->request_count());
+  }
+  for (const auto& status_and_response : statuses_and_responses) {
+    EXPECT_TRUE(status_and_response.first.ok());
+    EXPECT_EQ(status_and_response.second.message(), kMessage_);
+  }
+
+  // The balancer got a single request.
+  EXPECT_EQ(1, balancer_servers_[0].service_->request_count());
+  // and sent two responses.
+  EXPECT_EQ(2, balancer_servers_[0].service_->response_count());
+
+  // Check LB policy name for the channel.
+  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
+}
+
+TEST_F(SingleBalancerTest, RepeatedServerlist) {
+  constexpr int kServerlistDelayMs = 100;
+
+  // Send a serverlist right away.
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts()), 0);
+  // ... and the same one a bit later.
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts()),
+      kServerlistDelayMs);
+
+  // Send num_backends/2 requests.
+  auto statuses_and_responses = SendRpc(kMessage_, num_backends_ / 2);
+  // only the first half of the backends will receive them.
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    if (i < backends_.size() / 2)
+      EXPECT_EQ(1, backend_servers_[i].service_->request_count());
+    else
+      EXPECT_EQ(0, backend_servers_[i].service_->request_count());
+  }
+  EXPECT_EQ(statuses_and_responses.size(), num_backends_ / 2);
+  for (const auto& status_and_response : statuses_and_responses) {
+    EXPECT_TRUE(status_and_response.first.ok());
+    EXPECT_EQ(status_and_response.second.message(), kMessage_);
+  }
+
+  // Wait for the (duplicated) serverlist update.
+  gpr_sleep_until(gpr_time_add(
+      gpr_now(GPR_CLOCK_REALTIME),
+      gpr_time_from_millis(kServerlistDelayMs * 1.1, GPR_TIMESPAN)));
+
+  // Verify the LB has sent two responses.
+  EXPECT_EQ(2, balancer_servers_[0].service_->response_count());
+
+  // Some more calls to complete the total number of backends.
+  statuses_and_responses = SendRpc(
+      kMessage_,
+      num_backends_ / 2 + (num_backends_ & 0x1) /* extra one if num_bes odd */);
+  // Because a duplicated serverlist should have no effect, all backends must
+  // have been hit once now.
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    EXPECT_EQ(1, backend_servers_[i].service_->request_count());
+  }
+  EXPECT_EQ(statuses_and_responses.size(), num_backends_ / 2);
+  for (const auto& status_and_response : statuses_and_responses) {
+    EXPECT_TRUE(status_and_response.first.ok());
+    EXPECT_EQ(status_and_response.second.message(), kMessage_);
+  }
+
+  // The balancer got a single request.
+  EXPECT_EQ(1, balancer_servers_[0].service_->request_count());
+  // Check LB policy name for the channel.
+  EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
+}
+
+class SingleBalancerWithClientLoadReportingTest : public GrpclbEnd2endTest {
+ public:
+  SingleBalancerWithClientLoadReportingTest() : GrpclbEnd2endTest(4, 1, 2) {}
+};
+
+TEST_F(SingleBalancerWithClientLoadReportingTest, Vanilla) {
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts()), 0);
+  // Start servers and send 100 RPCs per server.
+  const auto& statuses_and_responses = SendRpc(kMessage_, 100 * num_backends_);
+
+  for (const auto& status_and_response : statuses_and_responses) {
+    EXPECT_TRUE(status_and_response.first.ok());
+    EXPECT_EQ(status_and_response.second.message(), kMessage_);
+  }
+
+  // Each backend should have gotten 100 requests.
+  for (size_t i = 0; i < backends_.size(); ++i) {
+    EXPECT_EQ(100, backend_servers_[i].service_->request_count());
+  }
+  // The balancer got a single request.
+  EXPECT_EQ(1, balancer_servers_[0].service_->request_count());
+  // and sent a single response.
+  EXPECT_EQ(1, balancer_servers_[0].service_->response_count());
+
+  const ClientStats client_stats = WaitForLoadReports();
+  EXPECT_EQ(100 * num_backends_, client_stats.num_calls_started);
+  EXPECT_EQ(100 * num_backends_, client_stats.num_calls_finished);
+  EXPECT_EQ(0U, client_stats.num_calls_finished_with_drop_for_rate_limiting);
+  EXPECT_EQ(0U, client_stats.num_calls_finished_with_drop_for_load_balancing);
+  EXPECT_EQ(0U, client_stats.num_calls_finished_with_client_failed_to_send);
+  EXPECT_EQ(100 * num_backends_,
+            client_stats.num_calls_finished_known_received);
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_init();
+  grpc_test_init(argc, argv);
+  grpc_fake_resolver_init();
+  ::testing::InitGoogleTest(&argc, argv);
+  const auto result = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return result;
+}
diff --git a/test/cpp/microbenchmarks/BUILD b/test/cpp/microbenchmarks/BUILD
index cae3fa1..208ac6d 100644
--- a/test/cpp/microbenchmarks/BUILD
+++ b/test/cpp/microbenchmarks/BUILD
@@ -92,7 +92,7 @@
 cc_test(
     name = "bm_fullstack_trickle",
     srcs = ["bm_fullstack_trickle.cc"],
-    deps = [":helpers"],
+    deps = [":helpers", "//external:gflags"],
 )
 
 cc_test(
diff --git a/test/cpp/microbenchmarks/bm_call_create.cc b/test/cpp/microbenchmarks/bm_call_create.cc
index c91219e..67e7c02 100644
--- a/test/cpp/microbenchmarks/bm_call_create.cc
+++ b/test/cpp/microbenchmarks/bm_call_create.cc
@@ -563,7 +563,8 @@
   }
 
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  size_t channel_size = grpc_channel_stack_size(&filters[0], filters.size());
+  size_t channel_size = grpc_channel_stack_size(
+      filters.size() == 0 ? NULL : &filters[0], filters.size());
   grpc_channel_stack *channel_stack =
       static_cast<grpc_channel_stack *>(gpr_zalloc(channel_size));
   GPR_ASSERT(GRPC_LOG_IF_ERROR(
diff --git a/test/cpp/microbenchmarks/bm_fullstack_trickle.cc b/test/cpp/microbenchmarks/bm_fullstack_trickle.cc
index a5cfeb4..6b9fa8b 100644
--- a/test/cpp/microbenchmarks/bm_fullstack_trickle.cc
+++ b/test/cpp/microbenchmarks/bm_fullstack_trickle.cc
@@ -34,6 +34,8 @@
 /* Benchmark gRPC end2end in various configurations */
 
 #include <benchmark/benchmark.h>
+#include <gflags/gflags.h>
+#include <fstream>
 #include "src/core/lib/profiling/timers.h"
 #include "src/cpp/client/create_channel_internal.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
@@ -45,16 +47,58 @@
 #include "test/core/util/trickle_endpoint.h"
 }
 
+DEFINE_bool(log, false, "Log state to CSV files");
+DEFINE_int32(
+    warmup_megabytes, 1,
+    "Number of megabytes to pump before collecting flow control stats");
+DEFINE_int32(
+    warmup_iterations, 100,
+    "Number of iterations to run before collecting flow control stats");
+DEFINE_int32(warmup_max_time_seconds, 10,
+             "Maximum number of seconds to run warmup loop");
+
 namespace grpc {
 namespace testing {
 
 static void* tag(intptr_t x) { return reinterpret_cast<void*>(x); }
 
+template <class A0>
+static void write_csv(std::ostream* out, A0&& a0) {
+  if (!out) return;
+  (*out) << a0 << "\n";
+}
+
+template <class A0, class... Arg>
+static void write_csv(std::ostream* out, A0&& a0, Arg&&... arg) {
+  if (!out) return;
+  (*out) << a0 << ",";
+  write_csv(out, std::forward<Arg>(arg)...);
+}
+
 class TrickledCHTTP2 : public EndpointPairFixture {
  public:
-  TrickledCHTTP2(Service* service, size_t megabits_per_second)
-      : EndpointPairFixture(service, MakeEndpoints(megabits_per_second),
-                            FixtureConfiguration()) {}
+  TrickledCHTTP2(Service* service, bool streaming, size_t req_size,
+                 size_t resp_size, size_t kilobits_per_second)
+      : EndpointPairFixture(service, MakeEndpoints(kilobits_per_second),
+                            FixtureConfiguration()) {
+    if (FLAGS_log) {
+      std::ostringstream fn;
+      fn << "trickle." << (streaming ? "streaming" : "unary") << "." << req_size
+         << "." << resp_size << "." << kilobits_per_second << ".csv";
+      log_.reset(new std::ofstream(fn.str().c_str()));
+      write_csv(log_.get(), "t", "iteration", "client_backlog",
+                "server_backlog", "client_t_stall", "client_s_stall",
+                "server_t_stall", "server_s_stall", "client_t_outgoing",
+                "server_t_outgoing", "client_t_incoming", "server_t_incoming",
+                "client_s_outgoing_delta", "server_s_outgoing_delta",
+                "client_s_incoming_delta", "server_s_incoming_delta",
+                "client_s_announce_window", "server_s_announce_window",
+                "client_peer_iws", "client_local_iws", "client_sent_iws",
+                "client_acked_iws", "server_peer_iws", "server_local_iws",
+                "server_sent_iws", "server_acked_iws", "client_queued_bytes",
+                "server_queued_bytes");
+    }
+  }
 
   void AddToLabel(std::ostream& out, benchmark::State& state) {
     out << " writes/iter:"
@@ -75,7 +119,58 @@
             (double)state.iterations());
   }
 
-  void Step() {
+  void Log(int64_t iteration) {
+    auto now = gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), start_);
+    grpc_chttp2_transport* client =
+        reinterpret_cast<grpc_chttp2_transport*>(client_transport_);
+    grpc_chttp2_transport* server =
+        reinterpret_cast<grpc_chttp2_transport*>(server_transport_);
+    grpc_chttp2_stream* client_stream =
+        client->stream_map.count == 1
+            ? static_cast<grpc_chttp2_stream*>(client->stream_map.values[0])
+            : nullptr;
+    grpc_chttp2_stream* server_stream =
+        server->stream_map.count == 1
+            ? static_cast<grpc_chttp2_stream*>(server->stream_map.values[0])
+            : nullptr;
+    write_csv(
+        log_.get(), static_cast<double>(now.tv_sec) +
+                        1e-9 * static_cast<double>(now.tv_nsec),
+        iteration, grpc_trickle_get_backlog(endpoint_pair_.client),
+        grpc_trickle_get_backlog(endpoint_pair_.server),
+        client->lists[GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT].head != nullptr,
+        client->lists[GRPC_CHTTP2_LIST_STALLED_BY_STREAM].head != nullptr,
+        server->lists[GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT].head != nullptr,
+        server->lists[GRPC_CHTTP2_LIST_STALLED_BY_STREAM].head != nullptr,
+        client->outgoing_window, server->outgoing_window,
+        client->incoming_window, server->incoming_window,
+        client_stream ? client_stream->outgoing_window_delta : -1,
+        server_stream ? server_stream->outgoing_window_delta : -1,
+        client_stream ? client_stream->incoming_window_delta : -1,
+        server_stream ? server_stream->incoming_window_delta : -1,
+        client_stream ? client_stream->announce_window : -1,
+        server_stream ? server_stream->announce_window : -1,
+        client->settings[GRPC_PEER_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        client->settings[GRPC_LOCAL_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        client->settings[GRPC_SENT_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        client->settings[GRPC_ACKED_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        server->settings[GRPC_PEER_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        server->settings[GRPC_LOCAL_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        server->settings[GRPC_SENT_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        server->settings[GRPC_ACKED_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        client_stream ? client_stream->flow_controlled_buffer.length : 0,
+        server_stream ? server_stream->flow_controlled_buffer.length : 0);
+  }
+
+  void Step(bool update_stats) {
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     size_t client_backlog =
         grpc_trickle_endpoint_trickle(&exec_ctx, endpoint_pair_.client);
@@ -83,10 +178,12 @@
         grpc_trickle_endpoint_trickle(&exec_ctx, endpoint_pair_.server);
     grpc_exec_ctx_finish(&exec_ctx);
 
-    UpdateStats((grpc_chttp2_transport*)client_transport_, &client_stats_,
-                client_backlog);
-    UpdateStats((grpc_chttp2_transport*)server_transport_, &server_stats_,
-                server_backlog);
+    if (update_stats) {
+      UpdateStats((grpc_chttp2_transport*)client_transport_, &client_stats_,
+                  client_backlog);
+      UpdateStats((grpc_chttp2_transport*)server_transport_, &server_stats_,
+                  server_backlog);
+    }
   }
 
  private:
@@ -97,6 +194,8 @@
   };
   Stats client_stats_;
   Stats server_stats_;
+  std::unique_ptr<std::ofstream> log_;
+  gpr_timespec start_ = gpr_now(GPR_CLOCK_MONOTONIC);
 
   grpc_endpoint_pair MakeEndpoints(size_t kilobits) {
     grpc_endpoint_pair p;
@@ -123,13 +222,15 @@
 // force library initialization
 auto& force_library_initialization = Library::get();
 
-static void TrickleCQNext(TrickledCHTTP2* fixture, void** t, bool* ok) {
+static void TrickleCQNext(TrickledCHTTP2* fixture, void** t, bool* ok,
+                          int64_t iteration) {
   while (true) {
+    fixture->Log(iteration);
     switch (fixture->cq()->AsyncNext(
         t, ok, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
                             gpr_time_from_micros(100, GPR_TIMESPAN)))) {
       case CompletionQueue::TIMEOUT:
-        fixture->Step();
+        fixture->Step(iteration != -1);
         break;
       case CompletionQueue::SHUTDOWN:
         GPR_ASSERT(false);
@@ -142,8 +243,9 @@
 
 static void BM_PumpStreamServerToClient_Trickle(benchmark::State& state) {
   EchoTestService::AsyncService service;
-  std::unique_ptr<TrickledCHTTP2> fixture(
-      new TrickledCHTTP2(&service, state.range(1)));
+  std::unique_ptr<TrickledCHTTP2> fixture(new TrickledCHTTP2(
+      &service, true, state.range(0) /* req_size */,
+      state.range(0) /* resp_size */, state.range(1) /* bw in kbit/s */));
   {
     EchoResponse send_response;
     EchoResponse recv_response;
@@ -163,18 +265,19 @@
     void* t;
     bool ok;
     while (need_tags) {
-      TrickleCQNext(fixture.get(), &t, &ok);
+      TrickleCQNext(fixture.get(), &t, &ok, -1);
       GPR_ASSERT(ok);
       int i = (int)(intptr_t)t;
       GPR_ASSERT(need_tags & (1 << i));
       need_tags &= ~(1 << i);
     }
     request_rw->Read(&recv_response, tag(0));
-    while (state.KeepRunning()) {
+    auto inner_loop = [&](bool in_warmup) {
       GPR_TIMER_SCOPE("BenchmarkCycle", 0);
       response_rw.Write(send_response, tag(1));
       while (true) {
-        TrickleCQNext(fixture.get(), &t, &ok);
+        TrickleCQNext(fixture.get(), &t, &ok,
+                      in_warmup ? -1 : state.iterations());
         if (t == tag(0)) {
           request_rw->Read(&recv_response, tag(0));
         } else if (t == tag(1)) {
@@ -183,11 +286,26 @@
           GPR_ASSERT(false);
         }
       }
+    };
+    gpr_timespec warmup_start = gpr_now(GPR_CLOCK_MONOTONIC);
+    for (int i = 0;
+         i < GPR_MAX(FLAGS_warmup_iterations, FLAGS_warmup_megabytes * 1024 *
+                                                  1024 / (14 + state.range(0)));
+         i++) {
+      inner_loop(true);
+      if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), warmup_start),
+                       gpr_time_from_seconds(FLAGS_warmup_max_time_seconds,
+                                             GPR_TIMESPAN)) > 0) {
+        break;
+      }
+    }
+    while (state.KeepRunning()) {
+      inner_loop(false);
     }
     response_rw.Finish(Status::OK, tag(1));
     need_tags = (1 << 0) | (1 << 1);
     while (need_tags) {
-      TrickleCQNext(fixture.get(), &t, &ok);
+      TrickleCQNext(fixture.get(), &t, &ok, -1);
       int i = (int)(intptr_t)t;
       GPR_ASSERT(need_tags & (1 << i));
       need_tags &= ~(1 << i);
@@ -198,23 +316,126 @@
   state.SetBytesProcessed(state.range(0) * state.iterations());
 }
 
-/*******************************************************************************
- * CONFIGURATIONS
- */
-
-static void TrickleArgs(benchmark::internal::Benchmark* b) {
+static void StreamingTrickleArgs(benchmark::internal::Benchmark* b) {
   for (int i = 1; i <= 128 * 1024 * 1024; i *= 8) {
-    for (int j = 1; j <= 128 * 1024 * 1024; j *= 8) {
+    for (int j = 64; j <= 128 * 1024 * 1024; j *= 8) {
       double expected_time =
           static_cast<double>(14 + i) / (125.0 * static_cast<double>(j));
-      if (expected_time > 0.01) continue;
+      if (expected_time > 2.0) continue;
       b->Args({i, j});
     }
   }
 }
+BENCHMARK(BM_PumpStreamServerToClient_Trickle)->Apply(StreamingTrickleArgs);
 
-BENCHMARK(BM_PumpStreamServerToClient_Trickle)->Apply(TrickleArgs);
+static void BM_PumpUnbalancedUnary_Trickle(benchmark::State& state) {
+  EchoTestService::AsyncService service;
+  std::unique_ptr<TrickledCHTTP2> fixture(new TrickledCHTTP2(
+      &service, true, state.range(0) /* req_size */,
+      state.range(1) /* resp_size */, state.range(2) /* bw in kbit/s */));
+  EchoRequest send_request;
+  EchoResponse send_response;
+  EchoResponse recv_response;
+  if (state.range(0) > 0) {
+    send_request.set_message(std::string(state.range(0), 'a'));
+  }
+  if (state.range(1) > 0) {
+    send_response.set_message(std::string(state.range(1), 'a'));
+  }
+  Status recv_status;
+  struct ServerEnv {
+    ServerContext ctx;
+    EchoRequest recv_request;
+    grpc::ServerAsyncResponseWriter<EchoResponse> response_writer;
+    ServerEnv() : response_writer(&ctx) {}
+  };
+  uint8_t server_env_buffer[2 * sizeof(ServerEnv)];
+  ServerEnv* server_env[2] = {
+      reinterpret_cast<ServerEnv*>(server_env_buffer),
+      reinterpret_cast<ServerEnv*>(server_env_buffer + sizeof(ServerEnv))};
+  new (server_env[0]) ServerEnv;
+  new (server_env[1]) ServerEnv;
+  service.RequestEcho(&server_env[0]->ctx, &server_env[0]->recv_request,
+                      &server_env[0]->response_writer, fixture->cq(),
+                      fixture->cq(), tag(0));
+  service.RequestEcho(&server_env[1]->ctx, &server_env[1]->recv_request,
+                      &server_env[1]->response_writer, fixture->cq(),
+                      fixture->cq(), tag(1));
+  std::unique_ptr<EchoTestService::Stub> stub(
+      EchoTestService::NewStub(fixture->channel()));
+  auto inner_loop = [&](bool in_warmup) {
+    GPR_TIMER_SCOPE("BenchmarkCycle", 0);
+    recv_response.Clear();
+    ClientContext cli_ctx;
+    std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader(
+        stub->AsyncEcho(&cli_ctx, send_request, fixture->cq()));
+    void* t;
+    bool ok;
+    TrickleCQNext(fixture.get(), &t, &ok, state.iterations());
+    GPR_ASSERT(ok);
+    GPR_ASSERT(t == tag(0) || t == tag(1));
+    intptr_t slot = reinterpret_cast<intptr_t>(t);
+    ServerEnv* senv = server_env[slot];
+    senv->response_writer.Finish(send_response, Status::OK, tag(3));
+    response_reader->Finish(&recv_response, &recv_status, tag(4));
+    for (int i = (1 << 3) | (1 << 4); i != 0;) {
+      TrickleCQNext(fixture.get(), &t, &ok, state.iterations());
+      GPR_ASSERT(ok);
+      int tagnum = (int)reinterpret_cast<intptr_t>(t);
+      GPR_ASSERT(i & (1 << tagnum));
+      i -= 1 << tagnum;
+    }
+    GPR_ASSERT(recv_status.ok());
+
+    senv->~ServerEnv();
+    senv = new (senv) ServerEnv();
+    service.RequestEcho(&senv->ctx, &senv->recv_request, &senv->response_writer,
+                        fixture->cq(), fixture->cq(), tag(slot));
+  };
+  gpr_timespec warmup_start = gpr_now(GPR_CLOCK_MONOTONIC);
+  for (int i = 0;
+       i < GPR_MAX(FLAGS_warmup_iterations, FLAGS_warmup_megabytes * 1024 *
+                                                1024 / (14 + state.range(0)));
+       i++) {
+    inner_loop(true);
+    if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), warmup_start),
+                     gpr_time_from_seconds(FLAGS_warmup_max_time_seconds,
+                                           GPR_TIMESPAN)) > 0) {
+      break;
+    }
+  }
+  while (state.KeepRunning()) {
+    inner_loop(false);
+  }
+  fixture->Finish(state);
+  fixture.reset();
+  server_env[0]->~ServerEnv();
+  server_env[1]->~ServerEnv();
+  state.SetBytesProcessed(state.range(0) * state.iterations() +
+                          state.range(1) * state.iterations());
+}
+
+static void UnaryTrickleArgs(benchmark::internal::Benchmark* b) {
+  const int cli_1024k = 1024 * 1024;
+  const int cli_32M = 32 * 1024 * 1024;
+  const int svr_256k = 256 * 1024;
+  const int svr_4M = 4 * 1024 * 1024;
+  const int svr_64M = 64 * 1024 * 1024;
+  for (int bw = 64; bw <= 128 * 1024 * 1024; bw *= 16) {
+    b->Args({bw, cli_1024k, svr_256k});
+    b->Args({bw, cli_1024k, svr_4M});
+    b->Args({bw, cli_1024k, svr_64M});
+    b->Args({bw, cli_32M, svr_256k});
+    b->Args({bw, cli_32M, svr_4M});
+    b->Args({bw, cli_32M, svr_64M});
+  }
+}
+BENCHMARK(BM_PumpUnbalancedUnary_Trickle)->Apply(UnaryTrickleArgs);
 }
 }
 
-BENCHMARK_MAIN();
+int main(int argc, char** argv) {
+  ::benchmark::Initialize(&argc, argv);
+  ::google::ParseCommandLineFlags(&argc, &argv, false);
+  ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/test/cpp/performance/writes_per_rpc_test.cc b/test/cpp/performance/writes_per_rpc_test.cc
index 7a914c1..12d8268 100644
--- a/test/cpp/performance/writes_per_rpc_test.cc
+++ b/test/cpp/performance/writes_per_rpc_test.cc
@@ -254,8 +254,8 @@
   EXPECT_LT(UnaryPingPong(0, 0), 2.05);
   EXPECT_LT(UnaryPingPong(1, 0), 2.05);
   EXPECT_LT(UnaryPingPong(0, 1), 2.05);
-  EXPECT_LT(UnaryPingPong(4096, 0), 2.2);
-  EXPECT_LT(UnaryPingPong(0, 4096), 2.2);
+  EXPECT_LT(UnaryPingPong(4096, 0), 2.5);
+  EXPECT_LT(UnaryPingPong(0, 4096), 2.5);
 }
 
 }  // namespace testing
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index 25a19a5..c3197eb 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -443,11 +443,8 @@
       create_stub_;
 };
 
-std::unique_ptr<Client> CreateSynchronousUnaryClient(const ClientConfig& args);
-std::unique_ptr<Client> CreateSynchronousStreamingClient(
-    const ClientConfig& args);
-std::unique_ptr<Client> CreateAsyncUnaryClient(const ClientConfig& args);
-std::unique_ptr<Client> CreateAsyncStreamingClient(const ClientConfig& args);
+std::unique_ptr<Client> CreateSynchronousClient(const ClientConfig& args);
+std::unique_ptr<Client> CreateAsyncClient(const ClientConfig& args);
 std::unique_ptr<Client> CreateGenericAsyncStreamingClient(
     const ClientConfig& args);
 
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 29a79e7..01856f7 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -313,9 +313,9 @@
 };
 
 template <class RequestType, class ResponseType>
-class ClientRpcContextStreamingImpl : public ClientRpcContext {
+class ClientRpcContextStreamingPingPongImpl : public ClientRpcContext {
  public:
-  ClientRpcContextStreamingImpl(
+  ClientRpcContextStreamingPingPongImpl(
       BenchmarkService::Stub* stub, const RequestType& req,
       std::function<gpr_timespec()> next_issue,
       std::function<std::unique_ptr<
@@ -333,7 +333,7 @@
         callback_(on_done),
         next_issue_(next_issue),
         start_req_(start_req) {}
-  ~ClientRpcContextStreamingImpl() override {}
+  ~ClientRpcContextStreamingPingPongImpl() override {}
   void Start(CompletionQueue* cq, const ClientConfig& config) override {
     StartInternal(cq, config.messages_per_stream());
   }
@@ -394,8 +394,8 @@
     }
   }
   void StartNewClone(CompletionQueue* cq) override {
-    auto* clone = new ClientRpcContextStreamingImpl(stub_, req_, next_issue_,
-                                                    start_req_, callback_);
+    auto* clone = new ClientRpcContextStreamingPingPongImpl(
+        stub_, req_, next_issue_, start_req_, callback_);
     clone->StartInternal(cq, messages_per_stream_);
   }
 
@@ -434,23 +434,23 @@
 
   void StartInternal(CompletionQueue* cq, int messages_per_stream) {
     cq_ = cq;
-    next_state_ = State::STREAM_IDLE;
-    stream_ = start_req_(stub_, &context_, cq, ClientRpcContext::tag(this));
     messages_per_stream_ = messages_per_stream;
     messages_issued_ = 0;
+    next_state_ = State::STREAM_IDLE;
+    stream_ = start_req_(stub_, &context_, cq, ClientRpcContext::tag(this));
   }
 };
 
-class AsyncStreamingClient final
+class AsyncStreamingPingPongClient final
     : public AsyncClient<BenchmarkService::Stub, SimpleRequest> {
  public:
-  explicit AsyncStreamingClient(const ClientConfig& config)
+  explicit AsyncStreamingPingPongClient(const ClientConfig& config)
       : AsyncClient<BenchmarkService::Stub, SimpleRequest>(
             config, SetupCtx, BenchmarkStubCreator) {
     StartThreads(num_async_threads_);
   }
 
-  ~AsyncStreamingClient() override {}
+  ~AsyncStreamingPingPongClient() override {}
 
  private:
   static void CheckDone(grpc::Status s, SimpleResponse* response) {}
@@ -464,9 +464,250 @@
   static ClientRpcContext* SetupCtx(BenchmarkService::Stub* stub,
                                     std::function<gpr_timespec()> next_issue,
                                     const SimpleRequest& req) {
-    return new ClientRpcContextStreamingImpl<SimpleRequest, SimpleResponse>(
-        stub, req, next_issue, AsyncStreamingClient::StartReq,
-        AsyncStreamingClient::CheckDone);
+    return new ClientRpcContextStreamingPingPongImpl<SimpleRequest,
+                                                     SimpleResponse>(
+        stub, req, next_issue, AsyncStreamingPingPongClient::StartReq,
+        AsyncStreamingPingPongClient::CheckDone);
+  }
+};
+
+template <class RequestType, class ResponseType>
+class ClientRpcContextStreamingFromClientImpl : public ClientRpcContext {
+ public:
+  ClientRpcContextStreamingFromClientImpl(
+      BenchmarkService::Stub* stub, const RequestType& req,
+      std::function<gpr_timespec()> next_issue,
+      std::function<std::unique_ptr<grpc::ClientAsyncWriter<RequestType>>(
+          BenchmarkService::Stub*, grpc::ClientContext*, ResponseType*,
+          CompletionQueue*, void*)>
+          start_req,
+      std::function<void(grpc::Status, ResponseType*)> on_done)
+      : context_(),
+        stub_(stub),
+        cq_(nullptr),
+        req_(req),
+        response_(),
+        next_state_(State::INVALID),
+        callback_(on_done),
+        next_issue_(next_issue),
+        start_req_(start_req) {}
+  ~ClientRpcContextStreamingFromClientImpl() override {}
+  void Start(CompletionQueue* cq, const ClientConfig& config) override {
+    StartInternal(cq);
+  }
+  bool RunNextState(bool ok, HistogramEntry* entry) override {
+    while (true) {
+      switch (next_state_) {
+        case State::STREAM_IDLE:
+          if (!next_issue_) {  // ready to issue
+            next_state_ = State::READY_TO_WRITE;
+          } else {
+            next_state_ = State::WAIT;
+          }
+          break;  // loop around, don't return
+        case State::WAIT:
+          alarm_.reset(
+              new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this)));
+          next_state_ = State::READY_TO_WRITE;
+          return true;
+        case State::READY_TO_WRITE:
+          if (!ok) {
+            return false;
+          }
+          start_ = UsageTimer::Now();
+          next_state_ = State::WRITE_DONE;
+          stream_->Write(req_, ClientRpcContext::tag(this));
+          return true;
+        case State::WRITE_DONE:
+          if (!ok) {
+            return false;
+          }
+          entry->set_value((UsageTimer::Now() - start_) * 1e9);
+          next_state_ = State::STREAM_IDLE;
+          break;  // loop around
+        default:
+          GPR_ASSERT(false);
+          return false;
+      }
+    }
+  }
+  void StartNewClone(CompletionQueue* cq) override {
+    auto* clone = new ClientRpcContextStreamingFromClientImpl(
+        stub_, req_, next_issue_, start_req_, callback_);
+    clone->StartInternal(cq);
+  }
+
+ private:
+  grpc::ClientContext context_;
+  BenchmarkService::Stub* stub_;
+  CompletionQueue* cq_;
+  std::unique_ptr<Alarm> alarm_;
+  RequestType req_;
+  ResponseType response_;
+  enum State {
+    INVALID,
+    STREAM_IDLE,
+    WAIT,
+    READY_TO_WRITE,
+    WRITE_DONE,
+  };
+  State next_state_;
+  std::function<void(grpc::Status, ResponseType*)> callback_;
+  std::function<gpr_timespec()> next_issue_;
+  std::function<std::unique_ptr<grpc::ClientAsyncWriter<RequestType>>(
+      BenchmarkService::Stub*, grpc::ClientContext*, ResponseType*,
+      CompletionQueue*, void*)>
+      start_req_;
+  grpc::Status status_;
+  double start_;
+  std::unique_ptr<grpc::ClientAsyncWriter<RequestType>> stream_;
+
+  void StartInternal(CompletionQueue* cq) {
+    cq_ = cq;
+    stream_ = start_req_(stub_, &context_, &response_, cq,
+                         ClientRpcContext::tag(this));
+    next_state_ = State::STREAM_IDLE;
+  }
+};
+
+class AsyncStreamingFromClientClient final
+    : public AsyncClient<BenchmarkService::Stub, SimpleRequest> {
+ public:
+  explicit AsyncStreamingFromClientClient(const ClientConfig& config)
+      : AsyncClient<BenchmarkService::Stub, SimpleRequest>(
+            config, SetupCtx, BenchmarkStubCreator) {
+    StartThreads(num_async_threads_);
+  }
+
+  ~AsyncStreamingFromClientClient() override {}
+
+ private:
+  static void CheckDone(grpc::Status s, SimpleResponse* response) {}
+  static std::unique_ptr<grpc::ClientAsyncWriter<SimpleRequest>> StartReq(
+      BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
+      SimpleResponse* resp, CompletionQueue* cq, void* tag) {
+    auto stream = stub->AsyncStreamingFromClient(ctx, resp, cq, tag);
+    return stream;
+  };
+  static ClientRpcContext* SetupCtx(BenchmarkService::Stub* stub,
+                                    std::function<gpr_timespec()> next_issue,
+                                    const SimpleRequest& req) {
+    return new ClientRpcContextStreamingFromClientImpl<SimpleRequest,
+                                                       SimpleResponse>(
+        stub, req, next_issue, AsyncStreamingFromClientClient::StartReq,
+        AsyncStreamingFromClientClient::CheckDone);
+  }
+};
+
+template <class RequestType, class ResponseType>
+class ClientRpcContextStreamingFromServerImpl : public ClientRpcContext {
+ public:
+  ClientRpcContextStreamingFromServerImpl(
+      BenchmarkService::Stub* stub, const RequestType& req,
+      std::function<gpr_timespec()> next_issue,
+      std::function<std::unique_ptr<grpc::ClientAsyncReader<ResponseType>>(
+          BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
+          CompletionQueue*, void*)>
+          start_req,
+      std::function<void(grpc::Status, ResponseType*)> on_done)
+      : context_(),
+        stub_(stub),
+        cq_(nullptr),
+        req_(req),
+        response_(),
+        next_state_(State::INVALID),
+        callback_(on_done),
+        next_issue_(next_issue),
+        start_req_(start_req) {}
+  ~ClientRpcContextStreamingFromServerImpl() override {}
+  void Start(CompletionQueue* cq, const ClientConfig& config) override {
+    StartInternal(cq);
+  }
+  bool RunNextState(bool ok, HistogramEntry* entry) override {
+    while (true) {
+      switch (next_state_) {
+        case State::STREAM_IDLE:
+          if (!ok) {
+            return false;
+          }
+          start_ = UsageTimer::Now();
+          next_state_ = State::READ_DONE;
+          stream_->Read(&response_, ClientRpcContext::tag(this));
+          return true;
+        case State::READ_DONE:
+          if (!ok) {
+            return false;
+          }
+          entry->set_value((UsageTimer::Now() - start_) * 1e9);
+          callback_(status_, &response_);
+          next_state_ = State::STREAM_IDLE;
+          break;  // loop around
+        default:
+          GPR_ASSERT(false);
+          return false;
+      }
+    }
+  }
+  void StartNewClone(CompletionQueue* cq) override {
+    auto* clone = new ClientRpcContextStreamingFromServerImpl(
+        stub_, req_, next_issue_, start_req_, callback_);
+    clone->StartInternal(cq);
+  }
+
+ private:
+  grpc::ClientContext context_;
+  BenchmarkService::Stub* stub_;
+  CompletionQueue* cq_;
+  std::unique_ptr<Alarm> alarm_;
+  RequestType req_;
+  ResponseType response_;
+  enum State { INVALID, STREAM_IDLE, READ_DONE };
+  State next_state_;
+  std::function<void(grpc::Status, ResponseType*)> callback_;
+  std::function<gpr_timespec()> next_issue_;
+  std::function<std::unique_ptr<grpc::ClientAsyncReader<ResponseType>>(
+      BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
+      CompletionQueue*, void*)>
+      start_req_;
+  grpc::Status status_;
+  double start_;
+  std::unique_ptr<grpc::ClientAsyncReader<ResponseType>> stream_;
+
+  void StartInternal(CompletionQueue* cq) {
+    // TODO(vjpai): Add support to rate-pace this
+    cq_ = cq;
+    next_state_ = State::STREAM_IDLE;
+    stream_ =
+        start_req_(stub_, &context_, req_, cq, ClientRpcContext::tag(this));
+  }
+};
+
+class AsyncStreamingFromServerClient final
+    : public AsyncClient<BenchmarkService::Stub, SimpleRequest> {
+ public:
+  explicit AsyncStreamingFromServerClient(const ClientConfig& config)
+      : AsyncClient<BenchmarkService::Stub, SimpleRequest>(
+            config, SetupCtx, BenchmarkStubCreator) {
+    StartThreads(num_async_threads_);
+  }
+
+  ~AsyncStreamingFromServerClient() override {}
+
+ private:
+  static void CheckDone(grpc::Status s, SimpleResponse* response) {}
+  static std::unique_ptr<grpc::ClientAsyncReader<SimpleResponse>> StartReq(
+      BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
+      const SimpleRequest& req, CompletionQueue* cq, void* tag) {
+    auto stream = stub->AsyncStreamingFromServer(ctx, req, cq, tag);
+    return stream;
+  };
+  static ClientRpcContext* SetupCtx(BenchmarkService::Stub* stub,
+                                    std::function<gpr_timespec()> next_issue,
+                                    const SimpleRequest& req) {
+    return new ClientRpcContextStreamingFromServerImpl<SimpleRequest,
+                                                       SimpleResponse>(
+        stub, req, next_issue, AsyncStreamingFromServerClient::StartReq,
+        AsyncStreamingFromServerClient::CheckDone);
   }
 };
 
@@ -591,11 +832,11 @@
     cq_ = cq;
     const grpc::string kMethodName(
         "/grpc.testing.BenchmarkService/StreamingCall");
+    messages_per_stream_ = messages_per_stream;
+    messages_issued_ = 0;
     next_state_ = State::STREAM_IDLE;
     stream_ = start_req_(stub_, &context_, kMethodName, cq,
                          ClientRpcContext::tag(this));
-    messages_per_stream_ = messages_per_stream;
-    messages_issued_ = 0;
   }
 };
 
@@ -632,11 +873,26 @@
   }
 };
 
-std::unique_ptr<Client> CreateAsyncUnaryClient(const ClientConfig& args) {
-  return std::unique_ptr<Client>(new AsyncUnaryClient(args));
-}
-std::unique_ptr<Client> CreateAsyncStreamingClient(const ClientConfig& args) {
-  return std::unique_ptr<Client>(new AsyncStreamingClient(args));
+std::unique_ptr<Client> CreateAsyncClient(const ClientConfig& config) {
+  switch (config.rpc_type()) {
+    case UNARY:
+      return std::unique_ptr<Client>(new AsyncUnaryClient(config));
+    case STREAMING:
+      return std::unique_ptr<Client>(new AsyncStreamingPingPongClient(config));
+    case STREAMING_FROM_CLIENT:
+      return std::unique_ptr<Client>(
+          new AsyncStreamingFromClientClient(config));
+    case STREAMING_FROM_SERVER:
+      return std::unique_ptr<Client>(
+          new AsyncStreamingFromServerClient(config));
+    case STREAMING_BOTH_WAYS:
+      // TODO(vjpai): Implement this
+      assert(false);
+      return nullptr;
+    default:
+      assert(false);
+      return nullptr;
+  }
 }
 std::unique_ptr<Client> CreateGenericAsyncStreamingClient(
     const ClientConfig& args) {
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index f8ce2cc..9075033 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -137,7 +137,8 @@
   }
 };
 
-class SynchronousStreamingClient final : public SynchronousClient {
+template <class StreamType>
+class SynchronousStreamingClient : public SynchronousClient {
  public:
   SynchronousStreamingClient(const ClientConfig& config)
       : SynchronousClient(config),
@@ -145,30 +146,69 @@
         stream_(num_threads_),
         messages_per_stream_(config.messages_per_stream()),
         messages_issued_(num_threads_) {
+    StartThreads(num_threads_);
+  }
+  virtual ~SynchronousStreamingClient() {
+    std::vector<std::thread> cleanup_threads;
+    for (size_t i = 0; i < num_threads_; i++) {
+      cleanup_threads.emplace_back([this, i]() {
+        auto stream = &stream_[i];
+        if (*stream) {
+          // forcibly cancel the streams, then finish
+          context_[i].TryCancel();
+          (*stream)->Finish();
+          // don't log any error message on !ok since this was canceled
+        }
+      });
+    }
+    for (auto& th : cleanup_threads) {
+      th.join();
+    }
+  }
+
+ protected:
+  std::vector<grpc::ClientContext> context_;
+  std::vector<std::unique_ptr<StreamType>> stream_;
+  const int messages_per_stream_;
+  std::vector<int> messages_issued_;
+
+  void FinishStream(HistogramEntry* entry, size_t thread_idx) {
+    Status s = stream_[thread_idx]->Finish();
+    // don't set the value since the stream is failed and shouldn't be timed
+    entry->set_status(s.error_code());
+    if (!s.ok()) {
+      gpr_log(GPR_ERROR, "Stream %" PRIuPTR " received an error %s", thread_idx,
+              s.error_message().c_str());
+    }
+    context_[thread_idx].~ClientContext();
+    new (&context_[thread_idx]) ClientContext();
+  }
+};
+
+class SynchronousStreamingPingPongClient final
+    : public SynchronousStreamingClient<
+          grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>> {
+ public:
+  SynchronousStreamingPingPongClient(const ClientConfig& config)
+      : SynchronousStreamingClient(config) {
     for (size_t thread_idx = 0; thread_idx < num_threads_; thread_idx++) {
       auto* stub = channels_[thread_idx % channels_.size()].get_stub();
       stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]);
       messages_issued_[thread_idx] = 0;
     }
-    StartThreads(num_threads_);
   }
-  ~SynchronousStreamingClient() {
+  ~SynchronousStreamingPingPongClient() {
     std::vector<std::thread> cleanup_threads;
     for (size_t i = 0; i < num_threads_; i++) {
       cleanup_threads.emplace_back([this, i]() {
         auto stream = &stream_[i];
         if (*stream) {
           (*stream)->WritesDone();
-          Status s = (*stream)->Finish();
-          if (!s.ok()) {
-            gpr_log(GPR_ERROR, "Stream %" PRIuPTR " received an error %s", i,
-                    s.error_message().c_str());
-          }
         }
       });
     }
-    for (size_t i = 0; i < num_threads_; i++) {
-      cleanup_threads[i].join();
+    for (auto& th : cleanup_threads) {
+      th.join();
     }
   }
 
@@ -176,7 +216,7 @@
     if (!WaitToIssue(thread_idx)) {
       return true;
     }
-    GPR_TIMER_SCOPE("SynchronousStreamingClient::ThreadFunc", 0);
+    GPR_TIMER_SCOPE("SynchronousStreamingPingPongClient::ThreadFunc", 0);
     double start = UsageTimer::Now();
     if (stream_[thread_idx]->Write(request_) &&
         stream_[thread_idx]->Read(&responses_[thread_idx])) {
@@ -192,40 +232,148 @@
       }
     }
     stream_[thread_idx]->WritesDone();
-    Status s = stream_[thread_idx]->Finish();
-    // don't set the value since this is either a failure (shouldn't be timed)
-    // or a stream-end (already has been timed)
-    entry->set_status(s.error_code());
-    if (!s.ok()) {
-      gpr_log(GPR_ERROR, "Stream %" PRIuPTR " received an error %s", thread_idx,
-              s.error_message().c_str());
-    }
+    FinishStream(entry, thread_idx);
     auto* stub = channels_[thread_idx % channels_.size()].get_stub();
-    context_[thread_idx].~ClientContext();
-    new (&context_[thread_idx]) ClientContext();
     stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]);
     messages_issued_[thread_idx] = 0;
     return true;
   }
-
- private:
-  // These are both conceptually std::vector but cannot be for old compilers
-  // that expect contained classes to support copy constructors
-  std::vector<grpc::ClientContext> context_;
-  std::vector<
-      std::unique_ptr<grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>>>
-      stream_;
-  const int messages_per_stream_;
-  std::vector<int> messages_issued_;
 };
 
-std::unique_ptr<Client> CreateSynchronousUnaryClient(
-    const ClientConfig& config) {
-  return std::unique_ptr<Client>(new SynchronousUnaryClient(config));
-}
-std::unique_ptr<Client> CreateSynchronousStreamingClient(
-    const ClientConfig& config) {
-  return std::unique_ptr<Client>(new SynchronousStreamingClient(config));
+class SynchronousStreamingFromClientClient final
+    : public SynchronousStreamingClient<grpc::ClientWriter<SimpleRequest>> {
+ public:
+  SynchronousStreamingFromClientClient(const ClientConfig& config)
+      : SynchronousStreamingClient(config), last_issue_(num_threads_) {
+    for (size_t thread_idx = 0; thread_idx < num_threads_; thread_idx++) {
+      auto* stub = channels_[thread_idx % channels_.size()].get_stub();
+      stream_[thread_idx] = stub->StreamingFromClient(&context_[thread_idx],
+                                                      &responses_[thread_idx]);
+      last_issue_[thread_idx] = UsageTimer::Now();
+    }
+  }
+  ~SynchronousStreamingFromClientClient() {
+    std::vector<std::thread> cleanup_threads;
+    for (size_t i = 0; i < num_threads_; i++) {
+      cleanup_threads.emplace_back([this, i]() {
+        auto stream = &stream_[i];
+        if (*stream) {
+          (*stream)->WritesDone();
+        }
+      });
+    }
+    for (auto& th : cleanup_threads) {
+      th.join();
+    }
+  }
+
+  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+    // Figure out how to make histogram sensible if this is rate-paced
+    if (!WaitToIssue(thread_idx)) {
+      return true;
+    }
+    GPR_TIMER_SCOPE("SynchronousStreamingFromClientClient::ThreadFunc", 0);
+    if (stream_[thread_idx]->Write(request_)) {
+      double now = UsageTimer::Now();
+      entry->set_value((now - last_issue_[thread_idx]) * 1e9);
+      last_issue_[thread_idx] = now;
+      return true;
+    }
+    stream_[thread_idx]->WritesDone();
+    FinishStream(entry, thread_idx);
+    auto* stub = channels_[thread_idx % channels_.size()].get_stub();
+    stream_[thread_idx] = stub->StreamingFromClient(&context_[thread_idx],
+                                                    &responses_[thread_idx]);
+    return true;
+  }
+
+ private:
+  std::vector<double> last_issue_;
+};
+
+class SynchronousStreamingFromServerClient final
+    : public SynchronousStreamingClient<grpc::ClientReader<SimpleResponse>> {
+ public:
+  SynchronousStreamingFromServerClient(const ClientConfig& config)
+      : SynchronousStreamingClient(config), last_recv_(num_threads_) {
+    for (size_t thread_idx = 0; thread_idx < num_threads_; thread_idx++) {
+      auto* stub = channels_[thread_idx % channels_.size()].get_stub();
+      stream_[thread_idx] =
+          stub->StreamingFromServer(&context_[thread_idx], request_);
+      last_recv_[thread_idx] = UsageTimer::Now();
+    }
+  }
+  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+    GPR_TIMER_SCOPE("SynchronousStreamingFromServerClient::ThreadFunc", 0);
+    if (stream_[thread_idx]->Read(&responses_[thread_idx])) {
+      double now = UsageTimer::Now();
+      entry->set_value((now - last_recv_[thread_idx]) * 1e9);
+      last_recv_[thread_idx] = now;
+      return true;
+    }
+    FinishStream(entry, thread_idx);
+    auto* stub = channels_[thread_idx % channels_.size()].get_stub();
+    stream_[thread_idx] =
+        stub->StreamingFromServer(&context_[thread_idx], request_);
+    return true;
+  }
+
+ private:
+  std::vector<double> last_recv_;
+};
+
+class SynchronousStreamingBothWaysClient final
+    : public SynchronousStreamingClient<
+          grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>> {
+ public:
+  SynchronousStreamingBothWaysClient(const ClientConfig& config)
+      : SynchronousStreamingClient(config) {
+    for (size_t thread_idx = 0; thread_idx < num_threads_; thread_idx++) {
+      auto* stub = channels_[thread_idx % channels_.size()].get_stub();
+      stream_[thread_idx] = stub->StreamingBothWays(&context_[thread_idx]);
+    }
+  }
+  ~SynchronousStreamingBothWaysClient() {
+    std::vector<std::thread> cleanup_threads;
+    for (size_t i = 0; i < num_threads_; i++) {
+      cleanup_threads.emplace_back([this, i]() {
+        auto stream = &stream_[i];
+        if (*stream) {
+          (*stream)->WritesDone();
+        }
+      });
+    }
+    for (auto& th : cleanup_threads) {
+      th.join();
+    }
+  }
+
+  bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
+    // TODO (vjpai): Do this
+    return true;
+  }
+};
+
+std::unique_ptr<Client> CreateSynchronousClient(const ClientConfig& config) {
+  switch (config.rpc_type()) {
+    case UNARY:
+      return std::unique_ptr<Client>(new SynchronousUnaryClient(config));
+    case STREAMING:
+      return std::unique_ptr<Client>(
+          new SynchronousStreamingPingPongClient(config));
+    case STREAMING_FROM_CLIENT:
+      return std::unique_ptr<Client>(
+          new SynchronousStreamingFromClientClient(config));
+    case STREAMING_FROM_SERVER:
+      return std::unique_ptr<Client>(
+          new SynchronousStreamingFromServerClient(config));
+    case STREAMING_BOTH_WAYS:
+      return std::unique_ptr<Client>(
+          new SynchronousStreamingBothWaysClient(config));
+    default:
+      assert(false);
+      return nullptr;
+  }
 }
 
 }  // namespace testing
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index d437920..9240897 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -68,15 +68,11 @@
 
   switch (config.client_type()) {
     case ClientType::SYNC_CLIENT:
-      return (config.rpc_type() == RpcType::UNARY)
-                 ? CreateSynchronousUnaryClient(config)
-                 : CreateSynchronousStreamingClient(config);
+      return CreateSynchronousClient(config);
     case ClientType::ASYNC_CLIENT:
-      return (config.rpc_type() == RpcType::UNARY)
-                 ? CreateAsyncUnaryClient(config)
-                 : (config.payload_config().has_bytebuf_params()
-                        ? CreateGenericAsyncStreamingClient(config)
-                        : CreateAsyncStreamingClient(config));
+      return config.payload_config().has_bytebuf_params()
+                 ? CreateGenericAsyncStreamingClient(config)
+                 : CreateAsyncClient(config);
     default:
       abort();
   }
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index b499b82..84f1579 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -71,6 +71,18 @@
                          ServerAsyncReaderWriter<ResponseType, RequestType> *,
                          CompletionQueue *, ServerCompletionQueue *, void *)>
           request_streaming_function,
+      std::function<void(ServiceType *, ServerContextType *,
+                         ServerAsyncReader<ResponseType, RequestType> *,
+                         CompletionQueue *, ServerCompletionQueue *, void *)>
+          request_streaming_from_client_function,
+      std::function<void(ServiceType *, ServerContextType *, RequestType *,
+                         ServerAsyncWriter<ResponseType> *, CompletionQueue *,
+                         ServerCompletionQueue *, void *)>
+          request_streaming_from_server_function,
+      std::function<void(ServiceType *, ServerContextType *,
+                         ServerAsyncReaderWriter<ResponseType, RequestType> *,
+                         CompletionQueue *, ServerCompletionQueue *, void *)>
+          request_streaming_both_ways_function,
       std::function<grpc::Status(const PayloadConfig &, const RequestType *,
                                  ResponseType *)>
           process_rpc)
@@ -107,7 +119,7 @@
         std::bind(process_rpc, config.payload_config(), std::placeholders::_1,
                   std::placeholders::_2);
 
-    for (int i = 0; i < 15000; i++) {
+    for (int i = 0; i < 5000; i++) {
       for (int j = 0; j < num_threads; j++) {
         if (request_unary_function) {
           auto request_unary = std::bind(
@@ -125,6 +137,26 @@
           contexts_.emplace_back(new ServerRpcContextStreamingImpl(
               request_streaming, process_rpc_bound));
         }
+        if (request_streaming_from_client_function) {
+          auto request_streaming_from_client = std::bind(
+              request_streaming_from_client_function, &async_service_,
+              std::placeholders::_1, std::placeholders::_2, srv_cqs_[j].get(),
+              srv_cqs_[j].get(), std::placeholders::_3);
+          contexts_.emplace_back(new ServerRpcContextStreamingFromClientImpl(
+              request_streaming_from_client, process_rpc_bound));
+        }
+        if (request_streaming_from_server_function) {
+          auto request_streaming_from_server =
+              std::bind(request_streaming_from_server_function, &async_service_,
+                        std::placeholders::_1, std::placeholders::_2,
+                        std::placeholders::_3, srv_cqs_[j].get(),
+                        srv_cqs_[j].get(), std::placeholders::_4);
+          contexts_.emplace_back(new ServerRpcContextStreamingFromServerImpl(
+              request_streaming_from_server, process_rpc_bound));
+        }
+        if (request_streaming_both_ways_function) {
+          // TODO(vjpai): Add this code
+        }
       }
     }
 
@@ -289,8 +321,8 @@
       if (!ok) {
         return false;
       }
-      stream_.Read(&req_, AsyncQpsServerTest::tag(this));
       next_state_ = &ServerRpcContextStreamingImpl::read_done;
+      stream_.Read(&req_, AsyncQpsServerTest::tag(this));
       return true;
     }
 
@@ -300,23 +332,23 @@
         // Call the RPC processing function
         grpc::Status status = invoke_method_(&req_, &response_);
         // initiate the write
-        stream_.Write(response_, AsyncQpsServerTest::tag(this));
         next_state_ = &ServerRpcContextStreamingImpl::write_done;
+        stream_.Write(response_, AsyncQpsServerTest::tag(this));
       } else {  // client has sent writes done
         // finish the stream
-        stream_.Finish(Status::OK, AsyncQpsServerTest::tag(this));
         next_state_ = &ServerRpcContextStreamingImpl::finish_done;
+        stream_.Finish(Status::OK, AsyncQpsServerTest::tag(this));
       }
       return true;
     }
     bool write_done(bool ok) {
       // now go back and get another streaming read!
       if (ok) {
-        stream_.Read(&req_, AsyncQpsServerTest::tag(this));
         next_state_ = &ServerRpcContextStreamingImpl::read_done;
+        stream_.Read(&req_, AsyncQpsServerTest::tag(this));
       } else {
-        stream_.Finish(Status::OK, AsyncQpsServerTest::tag(this));
         next_state_ = &ServerRpcContextStreamingImpl::finish_done;
+        stream_.Finish(Status::OK, AsyncQpsServerTest::tag(this));
       }
       return true;
     }
@@ -335,6 +367,146 @@
     grpc::ServerAsyncReaderWriter<ResponseType, RequestType> stream_;
   };
 
+  class ServerRpcContextStreamingFromClientImpl final
+      : public ServerRpcContext {
+   public:
+    ServerRpcContextStreamingFromClientImpl(
+        std::function<void(ServerContextType *,
+                           grpc::ServerAsyncReader<ResponseType, RequestType> *,
+                           void *)>
+            request_method,
+        std::function<grpc::Status(const RequestType *, ResponseType *)>
+            invoke_method)
+        : srv_ctx_(new ServerContextType),
+          next_state_(&ServerRpcContextStreamingFromClientImpl::request_done),
+          request_method_(request_method),
+          invoke_method_(invoke_method),
+          stream_(srv_ctx_.get()) {
+      request_method_(srv_ctx_.get(), &stream_, AsyncQpsServerTest::tag(this));
+    }
+    ~ServerRpcContextStreamingFromClientImpl() override {}
+    bool RunNextState(bool ok) override { return (this->*next_state_)(ok); }
+    void Reset() override {
+      srv_ctx_.reset(new ServerContextType);
+      req_ = RequestType();
+      stream_ =
+          grpc::ServerAsyncReader<ResponseType, RequestType>(srv_ctx_.get());
+
+      // Then request the method
+      next_state_ = &ServerRpcContextStreamingFromClientImpl::request_done;
+      request_method_(srv_ctx_.get(), &stream_, AsyncQpsServerTest::tag(this));
+    }
+
+   private:
+    bool request_done(bool ok) {
+      if (!ok) {
+        return false;
+      }
+      next_state_ = &ServerRpcContextStreamingFromClientImpl::read_done;
+      stream_.Read(&req_, AsyncQpsServerTest::tag(this));
+      return true;
+    }
+
+    bool read_done(bool ok) {
+      if (ok) {
+        // In this case, just do another read
+        // next_state_ is unchanged
+        stream_.Read(&req_, AsyncQpsServerTest::tag(this));
+        return true;
+      } else {  // client has sent writes done
+        // invoke the method
+        // Call the RPC processing function
+        grpc::Status status = invoke_method_(&req_, &response_);
+        // finish the stream
+        next_state_ = &ServerRpcContextStreamingFromClientImpl::finish_done;
+        stream_.Finish(response_, Status::OK, AsyncQpsServerTest::tag(this));
+      }
+      return true;
+    }
+    bool finish_done(bool ok) { return false; /* reset the context */ }
+
+    std::unique_ptr<ServerContextType> srv_ctx_;
+    RequestType req_;
+    ResponseType response_;
+    bool (ServerRpcContextStreamingFromClientImpl::*next_state_)(bool);
+    std::function<void(ServerContextType *,
+                       grpc::ServerAsyncReader<ResponseType, RequestType> *,
+                       void *)>
+        request_method_;
+    std::function<grpc::Status(const RequestType *, ResponseType *)>
+        invoke_method_;
+    grpc::ServerAsyncReader<ResponseType, RequestType> stream_;
+  };
+
+  class ServerRpcContextStreamingFromServerImpl final
+      : public ServerRpcContext {
+   public:
+    ServerRpcContextStreamingFromServerImpl(
+        std::function<void(ServerContextType *, RequestType *,
+                           grpc::ServerAsyncWriter<ResponseType> *, void *)>
+            request_method,
+        std::function<grpc::Status(const RequestType *, ResponseType *)>
+            invoke_method)
+        : srv_ctx_(new ServerContextType),
+          next_state_(&ServerRpcContextStreamingFromServerImpl::request_done),
+          request_method_(request_method),
+          invoke_method_(invoke_method),
+          stream_(srv_ctx_.get()) {
+      request_method_(srv_ctx_.get(), &req_, &stream_,
+                      AsyncQpsServerTest::tag(this));
+    }
+    ~ServerRpcContextStreamingFromServerImpl() override {}
+    bool RunNextState(bool ok) override { return (this->*next_state_)(ok); }
+    void Reset() override {
+      srv_ctx_.reset(new ServerContextType);
+      req_ = RequestType();
+      stream_ = grpc::ServerAsyncWriter<ResponseType>(srv_ctx_.get());
+
+      // Then request the method
+      next_state_ = &ServerRpcContextStreamingFromServerImpl::request_done;
+      request_method_(srv_ctx_.get(), &req_, &stream_,
+                      AsyncQpsServerTest::tag(this));
+    }
+
+   private:
+    bool request_done(bool ok) {
+      if (!ok) {
+        return false;
+      }
+      // invoke the method
+      // Call the RPC processing function
+      grpc::Status status = invoke_method_(&req_, &response_);
+
+      next_state_ = &ServerRpcContextStreamingFromServerImpl::write_done;
+      stream_.Write(response_, AsyncQpsServerTest::tag(this));
+      return true;
+    }
+
+    bool write_done(bool ok) {
+      if (ok) {
+        // Do another write!
+        // next_state_ is unchanged
+        stream_.Write(response_, AsyncQpsServerTest::tag(this));
+      } else {  // must be done so let's finish
+        next_state_ = &ServerRpcContextStreamingFromServerImpl::finish_done;
+        stream_.Finish(Status::OK, AsyncQpsServerTest::tag(this));
+      }
+      return true;
+    }
+    bool finish_done(bool ok) { return false; /* reset the context */ }
+
+    std::unique_ptr<ServerContextType> srv_ctx_;
+    RequestType req_;
+    ResponseType response_;
+    bool (ServerRpcContextStreamingFromServerImpl::*next_state_)(bool);
+    std::function<void(ServerContextType *, RequestType *,
+                       grpc::ServerAsyncWriter<ResponseType> *, void *)>
+        request_method_;
+    std::function<grpc::Status(const RequestType *, ResponseType *)>
+        invoke_method_;
+    grpc::ServerAsyncWriter<ResponseType> stream_;
+  };
+
   std::vector<std::thread> threads_;
   std::unique_ptr<grpc::Server> server_;
   std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_;
@@ -390,6 +562,9 @@
           config, RegisterBenchmarkService,
           &BenchmarkService::AsyncService::RequestUnaryCall,
           &BenchmarkService::AsyncService::RequestStreamingCall,
+          &BenchmarkService::AsyncService::RequestStreamingFromClient,
+          &BenchmarkService::AsyncService::RequestStreamingFromServer,
+          &BenchmarkService::AsyncService::RequestStreamingBothWays,
           ProcessSimpleRPC));
 }
 std::unique_ptr<Server> CreateAsyncGenericServer(const ServerConfig &config) {
@@ -397,7 +572,8 @@
       new AsyncQpsServerTest<ByteBuffer, ByteBuffer, grpc::AsyncGenericService,
                              grpc::GenericServerContext>(
           config, RegisterGenericService, nullptr,
-          &grpc::AsyncGenericService::RequestCall, ProcessGenericRPC));
+          &grpc::AsyncGenericService::RequestCall, nullptr, nullptr, nullptr,
+          ProcessGenericRPC));
 }
 
 }  // namespace testing
diff --git a/test/cpp/qps/server_sync.cc b/test/cpp/qps/server_sync.cc
index f79284d..f04465e 100644
--- a/test/cpp/qps/server_sync.cc
+++ b/test/cpp/qps/server_sync.cc
@@ -31,6 +31,9 @@
  *
  */
 
+#include <atomic>
+#include <thread>
+
 #include <grpc++/resource_quota.h>
 #include <grpc++/security/server_credentials.h>
 #include <grpc++/server.h>
@@ -52,12 +55,9 @@
  public:
   Status UnaryCall(ServerContext* context, const SimpleRequest* request,
                    SimpleResponse* response) override {
-    if (request->response_size() > 0) {
-      if (!Server::SetPayload(request->response_type(),
-                              request->response_size(),
-                              response->mutable_payload())) {
-        return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
-      }
+    auto s = SetResponse(request, response);
+    if (!s.ok()) {
+      return s;
     }
     return Status::OK;
   }
@@ -67,12 +67,9 @@
     SimpleRequest request;
     while (stream->Read(&request)) {
       SimpleResponse response;
-      if (request.response_size() > 0) {
-        if (!Server::SetPayload(request.response_type(),
-                                request.response_size(),
-                                response.mutable_payload())) {
-          return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
-        }
+      auto s = SetResponse(&request, &response);
+      if (!s.ok()) {
+        return s;
       }
       if (!stream->Write(response)) {
         return Status(StatusCode::INTERNAL, "Server couldn't respond");
@@ -80,6 +77,96 @@
     }
     return Status::OK;
   }
+  Status StreamingFromClient(ServerContext* context,
+                             ServerReader<SimpleRequest>* stream,
+                             SimpleResponse* response) override {
+    auto s = ClientPull(context, stream, response);
+    if (!s.ok()) {
+      return s;
+    }
+    return Status::OK;
+  }
+  Status StreamingFromServer(ServerContext* context,
+                             const SimpleRequest* request,
+                             ServerWriter<SimpleResponse>* stream) override {
+    SimpleResponse response;
+    auto s = SetResponse(request, &response);
+    if (!s.ok()) {
+      return s;
+    }
+    return ServerPush(context, stream, response, nullptr);
+  }
+  Status StreamingBothWays(
+      ServerContext* context,
+      ServerReaderWriter<SimpleResponse, SimpleRequest>* stream) override {
+    // Read the first client message to setup server response
+    SimpleRequest request;
+    if (!stream->Read(&request)) {
+      return Status::OK;
+    }
+    SimpleResponse response;
+    auto s = SetResponse(&request, &response);
+    if (!s.ok()) {
+      return s;
+    }
+    std::atomic_bool done;
+    Status sp;
+    std::thread t([context, stream, &response, &done, &sp]() {
+      sp = ServerPush(context, stream, response, [&done]() {
+        return done.load(std::memory_order_relaxed);
+      });
+    });
+    SimpleResponse dummy;
+    auto cp = ClientPull(context, stream, &dummy);
+    done.store(true, std::memory_order_relaxed);  // can be lazy
+    t.join();
+    if (!cp.ok()) {
+      return cp;
+    }
+    if (!sp.ok()) {
+      return sp;
+    }
+    return Status::OK;
+  }
+
+ private:
+  static Status ClientPull(ServerContext* context,
+                           ReaderInterface<SimpleRequest>* stream,
+                           SimpleResponse* response) {
+    SimpleRequest request;
+    while (stream->Read(&request)) {
+    }
+    if (request.response_size() > 0) {
+      if (!Server::SetPayload(request.response_type(), request.response_size(),
+                              response->mutable_payload())) {
+        return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
+      }
+    }
+    return Status::OK;
+  }
+  static Status ServerPush(ServerContext* context,
+                           WriterInterface<SimpleResponse>* stream,
+                           const SimpleResponse& response,
+                           std::function<bool()> done) {
+    while ((done == nullptr) || !done()) {
+      // TODO(vjpai): Add potential for rate-pacing on this
+      if (!stream->Write(response)) {
+        return Status(StatusCode::INTERNAL, "Server couldn't push");
+      }
+    }
+    return Status::OK;
+  }
+  static Status SetResponse(const SimpleRequest* request,
+                            SimpleResponse* response) {
+    if (request->response_size() > 0) {
+      if (!Server::SetPayload(request->response_type(),
+                              request->response_size(),
+                              response->mutable_payload())) {
+        return Status(grpc::StatusCode::INTERNAL, "Error creating payload.");
+      }
+    }
+    return Status::OK;
+  }
 };
 
 class SynchronousServer final : public grpc::testing::Server {
diff --git a/test/cpp/thread_manager/thread_manager_test.cc b/test/cpp/thread_manager/thread_manager_test.cc
index 35c8d5d..e1a0366 100644
--- a/test/cpp/thread_manager/thread_manager_test.cc
+++ b/test/cpp/thread_manager/thread_manager_test.cc
@@ -118,7 +118,7 @@
 
   // The number of times DoWork() was called is equal to the number of times
   // WORK_FOUND was returned
-  gpr_log(GPR_DEBUG, "DoWork() called %ld times",
+  gpr_log(GPR_DEBUG, "DoWork() called %" PRIdPTR " times",
           gpr_atm_no_barrier_load(&num_do_work_));
   GPR_ASSERT(gpr_atm_no_barrier_load(&num_do_work_) ==
              gpr_atm_no_barrier_load(&num_work_found_));
diff --git a/test/cpp/util/BUILD b/test/cpp/util/BUILD
index c83f89e..9dde22b 100644
--- a/test/cpp/util/BUILD
+++ b/test/cpp/util/BUILD
@@ -34,8 +34,8 @@
 cc_binary(
     name = "testso.so",
     srcs = [],
-    deps = ["//:grpc++_unsecure"],
     linkshared = 1,
+    deps = ["//:grpc++_unsecure"],
 )
 
 cc_library(
@@ -104,5 +104,29 @@
     ],
 )
 
-
-
+cc_binary(
+    name = "grpc_cli",
+    srcs = [
+        "cli_call.cc",
+        "cli_call.h",
+        "cli_credentials.cc",
+        "cli_credentials.h",
+        "config_grpc_cli.h",
+        "grpc_cli.cc",
+        "grpc_tool.cc",
+        "grpc_tool.h",
+        "proto_file_parser.cc",
+        "proto_file_parser.h",
+        "proto_reflection_descriptor_database.cc",
+        "proto_reflection_descriptor_database.h",
+        "service_describer.cc",
+        "service_describer.h",
+        "test_config.h",
+        "test_config_cc.cc",
+    ],
+    deps = [
+        "//:grpc++",
+        "//external:gflags",
+        "//src/proto/grpc/reflection/v1alpha:reflection_proto",
+    ],
+)
diff --git a/test/cpp/util/cli_call.cc b/test/cpp/util/cli_call.cc
index 041cc0e..b26beb0 100644
--- a/test/cpp/util/cli_call.cc
+++ b/test/cpp/util/cli_call.cc
@@ -91,7 +91,7 @@
   void* got_tag;
   bool ok;
 
-  grpc_slice s = grpc_slice_from_copied_string(request.c_str());
+  gpr_slice s = gpr_slice_from_copied_buffer(request.data(), request.size());
   grpc::Slice req_slice(s, grpc::Slice::STEAL_REF);
   grpc::ByteBuffer send_buffer(&req_slice, 1);
   call_->Write(send_buffer, tag(2));
diff --git a/third_party/cares/ares_build.h b/third_party/cares/ares_build.h
index 7d69f1e..d6b3d49 100644
--- a/third_party/cares/ares_build.h
+++ b/third_party/cares/ares_build.h
@@ -251,4 +251,14 @@
   typedef CARES_TYPEOF_ARES_SOCKLEN_T ares_socklen_t;
 #endif
 
+/* Undefine UNICODE, as c-ares does not use the ANSI version of functions */
+/* explicitly. */
+#ifdef UNICODE
+#  undef UNICODE
+#endif
+
+#ifdef _UNICODE
+#  undef _UNICODE
+#endif
+
 #endif /* __CARES_BUILD_H */
diff --git a/third_party/protobuf b/third_party/protobuf
index 593e917..a6189ac 160000
--- a/third_party/protobuf
+++ b/third_party/protobuf
@@ -1 +1 @@
-Subproject commit 593e917c176b5bc5aafa57bf9f6030d749d91cd5
+Subproject commit a6189acd18b00611c1dc7042299ad75486f08a1a
diff --git a/third_party/thrift b/third_party/thrift
deleted file mode 160000
index bcad917..0000000
--- a/third_party/thrift
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c
diff --git a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
index c2aa619..8a251f8 100644
--- a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
+++ b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
@@ -29,8 +29,8 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 # AUTO-GENERATED BY make_grpcio_tools.py!
-CC_FILES=['google/protobuf/compiler/zip_writer.cc', 'google/protobuf/compiler/subprocess.cc', 'google/protobuf/compiler/ruby/ruby_generator.cc', 'google/protobuf/compiler/python/python_generator.cc', 'google/protobuf/compiler/plugin.pb.cc', 'google/protobuf/compiler/plugin.cc', 'google/protobuf/compiler/php/php_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_primitive_field.cc', 'google/protobuf/compiler/objectivec/objectivec_oneof.cc', 'google/protobuf/compiler/objectivec/objectivec_message_field.cc', 'google/protobuf/compiler/objectivec/objectivec_message.cc', 'google/protobuf/compiler/objectivec/objectivec_map_field.cc', 'google/protobuf/compiler/objectivec/objectivec_helpers.cc', 'google/protobuf/compiler/objectivec/objectivec_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_file.cc', 'google/protobuf/compiler/objectivec/objectivec_field.cc', 'google/protobuf/compiler/objectivec/objectivec_extension.cc', 'google/protobuf/compiler/objectivec/objectivec_enum_field.cc', 'google/protobuf/compiler/objectivec/objectivec_enum.cc', 'google/protobuf/compiler/js/well_known_types_embed.cc', 'google/protobuf/compiler/js/js_generator.cc', 'google/protobuf/compiler/javanano/javanano_primitive_field.cc', 'google/protobuf/compiler/javanano/javanano_message_field.cc', 'google/protobuf/compiler/javanano/javanano_message.cc', 'google/protobuf/compiler/javanano/javanano_map_field.cc', 'google/protobuf/compiler/javanano/javanano_helpers.cc', 'google/protobuf/compiler/javanano/javanano_generator.cc', 'google/protobuf/compiler/javanano/javanano_file.cc', 'google/protobuf/compiler/javanano/javanano_field.cc', 'google/protobuf/compiler/javanano/javanano_extension.cc', 'google/protobuf/compiler/javanano/javanano_enum_field.cc', 'google/protobuf/compiler/javanano/javanano_enum.cc', 'google/protobuf/compiler/java/java_string_field_lite.cc', 'google/protobuf/compiler/java/java_string_field.cc', 'google/protobuf/compiler/java/java_shared_code_generator.cc', 'google/protobuf/compiler/java/java_service.cc', 'google/protobuf/compiler/java/java_primitive_field_lite.cc', 'google/protobuf/compiler/java/java_primitive_field.cc', 'google/protobuf/compiler/java/java_name_resolver.cc', 'google/protobuf/compiler/java/java_message_lite.cc', 'google/protobuf/compiler/java/java_message_field_lite.cc', 'google/protobuf/compiler/java/java_message_field.cc', 'google/protobuf/compiler/java/java_message_builder_lite.cc', 'google/protobuf/compiler/java/java_message_builder.cc', 'google/protobuf/compiler/java/java_message.cc', 'google/protobuf/compiler/java/java_map_field_lite.cc', 'google/protobuf/compiler/java/java_map_field.cc', 'google/protobuf/compiler/java/java_lazy_message_field_lite.cc', 'google/protobuf/compiler/java/java_lazy_message_field.cc', 'google/protobuf/compiler/java/java_helpers.cc', 'google/protobuf/compiler/java/java_generator_factory.cc', 'google/protobuf/compiler/java/java_generator.cc', 'google/protobuf/compiler/java/java_file.cc', 'google/protobuf/compiler/java/java_field.cc', 'google/protobuf/compiler/java/java_extension_lite.cc', 'google/protobuf/compiler/java/java_extension.cc', 'google/protobuf/compiler/java/java_enum_lite.cc', 'google/protobuf/compiler/java/java_enum_field_lite.cc', 'google/protobuf/compiler/java/java_enum_field.cc', 'google/protobuf/compiler/java/java_enum.cc', 'google/protobuf/compiler/java/java_doc_comment.cc', 'google/protobuf/compiler/java/java_context.cc', 'google/protobuf/compiler/csharp/csharp_wrapper_field.cc', 'google/protobuf/compiler/csharp/csharp_source_generator_base.cc', 'google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_message_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_reflection_class.cc', 'google/protobuf/compiler/csharp/csharp_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_message_field.cc', 'google/protobuf/compiler/csharp/csharp_message.cc', 'google/protobuf/compiler/csharp/csharp_map_field.cc', 'google/protobuf/compiler/csharp/csharp_helpers.cc', 'google/protobuf/compiler/csharp/csharp_generator.cc', 'google/protobuf/compiler/csharp/csharp_field_base.cc', 'google/protobuf/compiler/csharp/csharp_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_enum.cc', 'google/protobuf/compiler/csharp/csharp_doc_comment.cc', 'google/protobuf/compiler/cpp/cpp_string_field.cc', 'google/protobuf/compiler/cpp/cpp_service.cc', 'google/protobuf/compiler/cpp/cpp_primitive_field.cc', 'google/protobuf/compiler/cpp/cpp_message_field.cc', 'google/protobuf/compiler/cpp/cpp_message.cc', 'google/protobuf/compiler/cpp/cpp_map_field.cc', 'google/protobuf/compiler/cpp/cpp_helpers.cc', 'google/protobuf/compiler/cpp/cpp_generator.cc', 'google/protobuf/compiler/cpp/cpp_file.cc', 'google/protobuf/compiler/cpp/cpp_field.cc', 'google/protobuf/compiler/cpp/cpp_extension.cc', 'google/protobuf/compiler/cpp/cpp_enum_field.cc', 'google/protobuf/compiler/cpp/cpp_enum.cc', 'google/protobuf/compiler/command_line_interface.cc', 'google/protobuf/compiler/code_generator.cc', 'google/protobuf/wrappers.pb.cc', 'google/protobuf/wire_format.cc', 'google/protobuf/util/type_resolver_util.cc', 'google/protobuf/util/time_util.cc', 'google/protobuf/util/message_differencer.cc', 'google/protobuf/util/json_util.cc', 'google/protobuf/util/internal/utility.cc', 'google/protobuf/util/internal/type_info_test_helper.cc', 'google/protobuf/util/internal/type_info.cc', 'google/protobuf/util/internal/protostream_objectwriter.cc', 'google/protobuf/util/internal/protostream_objectsource.cc', 'google/protobuf/util/internal/proto_writer.cc', 'google/protobuf/util/internal/object_writer.cc', 'google/protobuf/util/internal/json_stream_parser.cc', 'google/protobuf/util/internal/json_objectwriter.cc', 'google/protobuf/util/internal/json_escaping.cc', 'google/protobuf/util/internal/field_mask_utility.cc', 'google/protobuf/util/internal/error_listener.cc', 'google/protobuf/util/internal/default_value_objectwriter.cc', 'google/protobuf/util/internal/datapiece.cc', 'google/protobuf/util/field_mask_util.cc', 'google/protobuf/util/field_comparator.cc', 'google/protobuf/unknown_field_set.cc', 'google/protobuf/type.pb.cc', 'google/protobuf/timestamp.pb.cc', 'google/protobuf/text_format.cc', 'google/protobuf/stubs/substitute.cc', 'google/protobuf/stubs/mathlimits.cc', 'google/protobuf/struct.pb.cc', 'google/protobuf/source_context.pb.cc', 'google/protobuf/service.cc', 'google/protobuf/reflection_ops.cc', 'google/protobuf/message.cc', 'google/protobuf/map_field.cc', 'google/protobuf/io/zero_copy_stream_impl.cc', 'google/protobuf/io/tokenizer.cc', 'google/protobuf/io/strtod.cc', 'google/protobuf/io/printer.cc', 'google/protobuf/io/gzip_stream.cc', 'google/protobuf/generated_message_reflection.cc', 'google/protobuf/field_mask.pb.cc', 'google/protobuf/extension_set_heavy.cc', 'google/protobuf/empty.pb.cc', 'google/protobuf/dynamic_message.cc', 'google/protobuf/duration.pb.cc', 'google/protobuf/descriptor_database.cc', 'google/protobuf/descriptor.pb.cc', 'google/protobuf/descriptor.cc', 'google/protobuf/compiler/parser.cc', 'google/protobuf/compiler/importer.cc', 'google/protobuf/api.pb.cc', 'google/protobuf/any.pb.cc', 'google/protobuf/any.cc', 'google/protobuf/wire_format_lite.cc', 'google/protobuf/stubs/time.cc', 'google/protobuf/stubs/strutil.cc', 'google/protobuf/stubs/structurally_valid.cc', 'google/protobuf/stubs/stringprintf.cc', 'google/protobuf/stubs/stringpiece.cc', 'google/protobuf/stubs/statusor.cc', 'google/protobuf/stubs/status.cc', 'google/protobuf/stubs/once.cc', 'google/protobuf/stubs/int128.cc', 'google/protobuf/stubs/common.cc', 'google/protobuf/stubs/bytestream.cc', 'google/protobuf/stubs/atomicops_internals_x86_msvc.cc', 'google/protobuf/stubs/atomicops_internals_x86_gcc.cc', 'google/protobuf/repeated_field.cc', 'google/protobuf/message_lite.cc', 'google/protobuf/io/zero_copy_stream_impl_lite.cc', 'google/protobuf/io/zero_copy_stream.cc', 'google/protobuf/io/coded_stream.cc', 'google/protobuf/generated_message_util.cc', 'google/protobuf/extension_set.cc', 'google/protobuf/arenastring.cc', 'google/protobuf/arena.cc', 'google/protobuf/compiler/js/embed.cc']
-PROTO_FILES=['google/protobuf/wrappers.proto', 'google/protobuf/type.proto', 'google/protobuf/timestamp.proto', 'google/protobuf/struct.proto', 'google/protobuf/source_context.proto', 'google/protobuf/field_mask.proto', 'google/protobuf/empty.proto', 'google/protobuf/duration.proto', 'google/protobuf/descriptor.proto', 'google/protobuf/compiler/plugin.proto', 'google/protobuf/api.proto', 'google/protobuf/any.proto']
+CC_FILES=['google/protobuf/compiler/zip_writer.cc', 'google/protobuf/compiler/subprocess.cc', 'google/protobuf/compiler/ruby/ruby_generator.cc', 'google/protobuf/compiler/python/python_generator.cc', 'google/protobuf/compiler/profile.pb.cc', 'google/protobuf/compiler/plugin.pb.cc', 'google/protobuf/compiler/plugin.cc', 'google/protobuf/compiler/php/php_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_primitive_field.cc', 'google/protobuf/compiler/objectivec/objectivec_oneof.cc', 'google/protobuf/compiler/objectivec/objectivec_message_field.cc', 'google/protobuf/compiler/objectivec/objectivec_message.cc', 'google/protobuf/compiler/objectivec/objectivec_map_field.cc', 'google/protobuf/compiler/objectivec/objectivec_helpers.cc', 'google/protobuf/compiler/objectivec/objectivec_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_file.cc', 'google/protobuf/compiler/objectivec/objectivec_field.cc', 'google/protobuf/compiler/objectivec/objectivec_extension.cc', 'google/protobuf/compiler/objectivec/objectivec_enum_field.cc', 'google/protobuf/compiler/objectivec/objectivec_enum.cc', 'google/protobuf/compiler/js/well_known_types_embed.cc', 'google/protobuf/compiler/js/js_generator.cc', 'google/protobuf/compiler/javanano/javanano_primitive_field.cc', 'google/protobuf/compiler/javanano/javanano_message_field.cc', 'google/protobuf/compiler/javanano/javanano_message.cc', 'google/protobuf/compiler/javanano/javanano_map_field.cc', 'google/protobuf/compiler/javanano/javanano_helpers.cc', 'google/protobuf/compiler/javanano/javanano_generator.cc', 'google/protobuf/compiler/javanano/javanano_file.cc', 'google/protobuf/compiler/javanano/javanano_field.cc', 'google/protobuf/compiler/javanano/javanano_extension.cc', 'google/protobuf/compiler/javanano/javanano_enum_field.cc', 'google/protobuf/compiler/javanano/javanano_enum.cc', 'google/protobuf/compiler/java/java_string_field_lite.cc', 'google/protobuf/compiler/java/java_string_field.cc', 'google/protobuf/compiler/java/java_shared_code_generator.cc', 'google/protobuf/compiler/java/java_service.cc', 'google/protobuf/compiler/java/java_primitive_field_lite.cc', 'google/protobuf/compiler/java/java_primitive_field.cc', 'google/protobuf/compiler/java/java_name_resolver.cc', 'google/protobuf/compiler/java/java_message_lite.cc', 'google/protobuf/compiler/java/java_message_field_lite.cc', 'google/protobuf/compiler/java/java_message_field.cc', 'google/protobuf/compiler/java/java_message_builder_lite.cc', 'google/protobuf/compiler/java/java_message_builder.cc', 'google/protobuf/compiler/java/java_message.cc', 'google/protobuf/compiler/java/java_map_field_lite.cc', 'google/protobuf/compiler/java/java_map_field.cc', 'google/protobuf/compiler/java/java_lazy_message_field_lite.cc', 'google/protobuf/compiler/java/java_lazy_message_field.cc', 'google/protobuf/compiler/java/java_helpers.cc', 'google/protobuf/compiler/java/java_generator_factory.cc', 'google/protobuf/compiler/java/java_generator.cc', 'google/protobuf/compiler/java/java_file.cc', 'google/protobuf/compiler/java/java_field.cc', 'google/protobuf/compiler/java/java_extension_lite.cc', 'google/protobuf/compiler/java/java_extension.cc', 'google/protobuf/compiler/java/java_enum_lite.cc', 'google/protobuf/compiler/java/java_enum_field_lite.cc', 'google/protobuf/compiler/java/java_enum_field.cc', 'google/protobuf/compiler/java/java_enum.cc', 'google/protobuf/compiler/java/java_doc_comment.cc', 'google/protobuf/compiler/java/java_context.cc', 'google/protobuf/compiler/csharp/csharp_wrapper_field.cc', 'google/protobuf/compiler/csharp/csharp_source_generator_base.cc', 'google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_message_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_reflection_class.cc', 'google/protobuf/compiler/csharp/csharp_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_message_field.cc', 'google/protobuf/compiler/csharp/csharp_message.cc', 'google/protobuf/compiler/csharp/csharp_map_field.cc', 'google/protobuf/compiler/csharp/csharp_helpers.cc', 'google/protobuf/compiler/csharp/csharp_generator.cc', 'google/protobuf/compiler/csharp/csharp_field_base.cc', 'google/protobuf/compiler/csharp/csharp_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_enum.cc', 'google/protobuf/compiler/csharp/csharp_doc_comment.cc', 'google/protobuf/compiler/cpp/cpp_string_field.cc', 'google/protobuf/compiler/cpp/cpp_service.cc', 'google/protobuf/compiler/cpp/cpp_primitive_field.cc', 'google/protobuf/compiler/cpp/cpp_message_field.cc', 'google/protobuf/compiler/cpp/cpp_message.cc', 'google/protobuf/compiler/cpp/cpp_map_field.cc', 'google/protobuf/compiler/cpp/cpp_helpers.cc', 'google/protobuf/compiler/cpp/cpp_generator.cc', 'google/protobuf/compiler/cpp/cpp_file.cc', 'google/protobuf/compiler/cpp/cpp_field.cc', 'google/protobuf/compiler/cpp/cpp_extension.cc', 'google/protobuf/compiler/cpp/cpp_enum_field.cc', 'google/protobuf/compiler/cpp/cpp_enum.cc', 'google/protobuf/compiler/command_line_interface.cc', 'google/protobuf/compiler/code_generator.cc', 'google/protobuf/wrappers.pb.cc', 'google/protobuf/wire_format.cc', 'google/protobuf/util/type_resolver_util.cc', 'google/protobuf/util/time_util.cc', 'google/protobuf/util/message_differencer.cc', 'google/protobuf/util/json_util.cc', 'google/protobuf/util/internal/utility.cc', 'google/protobuf/util/internal/type_info_test_helper.cc', 'google/protobuf/util/internal/type_info.cc', 'google/protobuf/util/internal/protostream_objectwriter.cc', 'google/protobuf/util/internal/protostream_objectsource.cc', 'google/protobuf/util/internal/proto_writer.cc', 'google/protobuf/util/internal/object_writer.cc', 'google/protobuf/util/internal/json_stream_parser.cc', 'google/protobuf/util/internal/json_objectwriter.cc', 'google/protobuf/util/internal/json_escaping.cc', 'google/protobuf/util/internal/field_mask_utility.cc', 'google/protobuf/util/internal/error_listener.cc', 'google/protobuf/util/internal/default_value_objectwriter.cc', 'google/protobuf/util/internal/datapiece.cc', 'google/protobuf/util/field_mask_util.cc', 'google/protobuf/util/field_comparator.cc', 'google/protobuf/util/delimited_message_util.cc', 'google/protobuf/unknown_field_set.cc', 'google/protobuf/type.pb.cc', 'google/protobuf/timestamp.pb.cc', 'google/protobuf/text_format.cc', 'google/protobuf/stubs/substitute.cc', 'google/protobuf/stubs/mathlimits.cc', 'google/protobuf/struct.pb.cc', 'google/protobuf/source_context.pb.cc', 'google/protobuf/service.cc', 'google/protobuf/reflection_ops.cc', 'google/protobuf/message.cc', 'google/protobuf/map_field.cc', 'google/protobuf/io/zero_copy_stream_impl.cc', 'google/protobuf/io/tokenizer.cc', 'google/protobuf/io/strtod.cc', 'google/protobuf/io/printer.cc', 'google/protobuf/io/gzip_stream.cc', 'google/protobuf/generated_message_reflection.cc', 'google/protobuf/field_mask.pb.cc', 'google/protobuf/extension_set_heavy.cc', 'google/protobuf/empty.pb.cc', 'google/protobuf/dynamic_message.cc', 'google/protobuf/duration.pb.cc', 'google/protobuf/descriptor_database.cc', 'google/protobuf/descriptor.pb.cc', 'google/protobuf/descriptor.cc', 'google/protobuf/compiler/parser.cc', 'google/protobuf/compiler/importer.cc', 'google/protobuf/api.pb.cc', 'google/protobuf/any.pb.cc', 'google/protobuf/any.cc', 'google/protobuf/wire_format_lite.cc', 'google/protobuf/stubs/time.cc', 'google/protobuf/stubs/strutil.cc', 'google/protobuf/stubs/structurally_valid.cc', 'google/protobuf/stubs/stringprintf.cc', 'google/protobuf/stubs/stringpiece.cc', 'google/protobuf/stubs/statusor.cc', 'google/protobuf/stubs/status.cc', 'google/protobuf/stubs/once.cc', 'google/protobuf/stubs/int128.cc', 'google/protobuf/stubs/common.cc', 'google/protobuf/stubs/bytestream.cc', 'google/protobuf/stubs/atomicops_internals_x86_msvc.cc', 'google/protobuf/stubs/atomicops_internals_x86_gcc.cc', 'google/protobuf/repeated_field.cc', 'google/protobuf/message_lite.cc', 'google/protobuf/io/zero_copy_stream_impl_lite.cc', 'google/protobuf/io/zero_copy_stream.cc', 'google/protobuf/io/coded_stream.cc', 'google/protobuf/generated_message_util.cc', 'google/protobuf/extension_set.cc', 'google/protobuf/arenastring.cc', 'google/protobuf/arena.cc', 'google/protobuf/compiler/js/embed.cc']
+PROTO_FILES=['google/protobuf/wrappers.proto', 'google/protobuf/type.proto', 'google/protobuf/timestamp.proto', 'google/protobuf/struct.proto', 'google/protobuf/source_context.proto', 'google/protobuf/field_mask.proto', 'google/protobuf/empty.proto', 'google/protobuf/duration.proto', 'google/protobuf/descriptor.proto', 'google/protobuf/compiler/profile.proto', 'google/protobuf/compiler/plugin.proto', 'google/protobuf/api.proto', 'google/protobuf/any.proto']
 
 CC_INCLUDE='third_party/protobuf/src'
 PROTO_INCLUDE='third_party/protobuf/src'
diff --git a/tools/distrib/python/grpcio_tools/setup.py b/tools/distrib/python/grpcio_tools/setup.py
index 211d442..43b60b1 100644
--- a/tools/distrib/python/grpcio_tools/setup.py
+++ b/tools/distrib/python/grpcio_tools/setup.py
@@ -211,7 +211,7 @@
   ext_modules=extension_modules(),
   packages=setuptools.find_packages('.'),
   install_requires=[
-    'protobuf>=3.2.0',
+    'protobuf>=3.3.0',
     'grpcio>={version}'.format(version=grpc_version.VERSION),
   ],
   package_data=package_data(),
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile
similarity index 90%
rename from tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile
rename to tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile
index c099f33..0a62f1c 100644
--- a/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile
@@ -1,4 +1,4 @@
-# Copyright 2016, Google Inc.
+# Copyright 2017, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,12 +27,10 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-FROM golang:latest
+FROM golang:1.7
 
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
+# Using login shell removes Go from path, so we add it.
+RUN ln -s /usr/local/go/bin/go /usr/local/bin
 
 #====================
 # Python dependencies
@@ -49,8 +47,5 @@
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
 
-# Using login shell removes Go from path, so we add it.
-RUN ln -s /usr/local/go/bin/go /usr/local/bin
-
 # Define the default command.
 CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile
similarity index 90%
copy from tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile
copy to tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile
index c099f33..abf38b8 100644
--- a/tools/dockerfile/stress_test/grpc_interop_stress_go/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile
@@ -1,4 +1,4 @@
-# Copyright 2016, Google Inc.
+# Copyright 2017, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,12 +27,10 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-FROM golang:latest
+FROM golang:1.8
 
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
+# Using login shell removes Go from path, so we add it.
+RUN ln -s /usr/local/go/bin/go /usr/local/bin
 
 #====================
 # Python dependencies
@@ -49,8 +47,5 @@
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
 
-# Using login shell removes Go from path, so we add it.
-RUN ln -s /usr/local/go/bin/go /usr/local/bin
-
 # Define the default command.
 CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile
deleted file mode 100644
index 12d8d09..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:jessie
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-#=================
-# C++ dependencies
-RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
-
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
-
-#================
-# C# dependencies
-
-# Update to a newer version of mono
-RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
-RUN echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list
-RUN echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
-RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
-
-# Install dependencies
-RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y \
-    mono-devel \
-    ca-certificates-mono \
-    nuget \
-    && apt-get clean
-
-RUN nuget update -self
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_csharp/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_csharp/build_interop_stress.sh
deleted file mode 100755
index 3451968..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_csharp/build_interop_stress.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds C# interop server and client in a base image.
-set -e
-
-mkdir -p /var/local/git
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# Copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-cd /var/local/git/grpc
-
-# Build C++ metrics client (to query the metrics from csharp stress client)
-make metrics_client -j
-
-# Build C# interop client & server
-tools/run_tests/run_tests.py -l csharp -c dbg --build_only
-
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_cxx/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_cxx/Dockerfile
deleted file mode 100644
index d0f66d9..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_cxx/Dockerfile
+++ /dev/null
@@ -1,132 +0,0 @@
-# Copyright 2015-2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:jessie
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-#=================
-# C++ dependencies
-RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
-
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
-
-#=================
-# Update clang to a version with improved tsan and fuzzing capabilities
-
-RUN apt-get update && apt-get -y install python cmake && apt-get clean
-
-RUN git clone -n -b release_38 http://llvm.org/git/llvm.git && \
-  cd llvm && git checkout ad57503 && cd ..
-RUN git clone -n -b release_38 http://llvm.org/git/clang.git && \
-  cd clang && git checkout ad2c56e && cd ..
-RUN git clone -n -b release_38 http://llvm.org/git/compiler-rt.git && \
-  cd compiler-rt && git checkout 3176922 && cd ..
-RUN git clone -n -b release_38 \
-  http://llvm.org/git/clang-tools-extra.git && cd clang-tools-extra && \
-  git checkout c288525 && cd ..
-RUN git clone -n -b release_38 http://llvm.org/git/libcxx.git && \
-  cd libcxx && git checkout fda3549  && cd ..
-RUN git clone -n -b release_38 http://llvm.org/git/libcxxabi.git && \
-  cd libcxxabi && git checkout 8d4e51d && cd ..
-
-RUN mv clang llvm/tools
-RUN mv compiler-rt llvm/projects
-RUN mv clang-tools-extra llvm/tools/clang/tools
-RUN mv libcxx llvm/projects
-RUN mv libcxxabi llvm/projects
-
-RUN mkdir llvm-build
-RUN cd llvm-build && cmake \
-  -DCMAKE_BUILD_TYPE:STRING=Release \
-  -DCMAKE_INSTALL_PREFIX:STRING=/usr \
-  -DLLVM_TARGETS_TO_BUILD:STRING=X86 \
-  ../llvm
-RUN make -C llvm-build -j 12 && make -C llvm-build install && rm -rf llvm-build
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_cxx/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_cxx/build_interop_stress.sh
deleted file mode 100755
index 92d1f80..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_cxx/build_interop_stress.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds C++ interop server and client in a base image.
-set -e
-
-mkdir -p /var/local/git
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-cd /var/local/git/grpc
-
-make install-certs
-
-BUILD_TYPE=${BUILD_TYPE:=opt}
-
-# build C++ interop stress client, interop client and server
-make CONFIG=$BUILD_TYPE stress_test metrics_client interop_client interop_server
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_go/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_go/build_interop_stress.sh
deleted file mode 100755
index 9e4769c..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_go/build_interop_stress.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds Go interop server, Stress client and metrics client in a base image.
-set -e
-
-# Clone just the grpc-go source code without any dependencies.
-# We are cloning from a local git repo that contains the right revision
-# to test instead of using "go get" to download from Github directly.
-git clone --recursive /var/local/jenkins/grpc-go src/google.golang.org/grpc
-
-# Clone the 'grpc' repo. We just need this for the wrapper scripts under
-# grpc/tools/gcp/stress_tests
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-# Get dependencies from GitHub
-# NOTE: once grpc-go dependencies change, this needs to be updated manually
-# but we don't expect this to happen any time soon.
-go get github.com/golang/protobuf/proto
-go get golang.org/x/net/context
-go get golang.org/x/net/trace
-go get golang.org/x/oauth2
-go get google.golang.org/cloud
-
-# Build the interop server, stress client and stress metrics client
-(cd src/google.golang.org/grpc/interop/server && go install)
-(cd src/google.golang.org/grpc/stress/client && go install)
-(cd src/google.golang.org/grpc/stress/metrics_client && go install)
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_java/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_java/Dockerfile
deleted file mode 100644
index 229ea46..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_java/Dockerfile
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:jessie
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-#=================
-# C++ dependencies
-RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
-
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
-
-# Install JDK 8 and Git
-#
-RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && \
-  echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list && \
-  echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list && \
-  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
-
-RUN apt-get update && apt-get -y install \
-      git \
-      libapr1 \
-      oracle-java8-installer \
-      && \
-    apt-get clean && rm -r /var/cache/oracle-jdk8-installer/
-
-ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
-ENV PATH $PATH:$JAVA_HOME/bin
-
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_java/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_java/build_interop_stress.sh
deleted file mode 100755
index 0194860..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_java/build_interop_stress.sh
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/bin/bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds C++ interop server and client in a base image.
-set -e
-
-mkdir -p /var/local/git
-# grpc-java repo
-git clone --recursive --depth 1 /var/local/jenkins/grpc-java /var/local/git/grpc-java
-
-# grpc repo (for metrics client and for the stress test wrapper scripts)
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# Copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-# First build the metrics client in grpc repo
-cd /var/local/git/grpc
-make metrics_client
-
-# Build all interop test targets (which includes interop server and stress test
-# client) in grpc-java repo
-cd /var/local/git/grpc-java
-./gradlew :grpc-interop-testing:installDist -PskipCodegen=true
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_node/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_node/Dockerfile
deleted file mode 100644
index 5fd0bc0..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_node/Dockerfile
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:jessie
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-#==================
-# Node dependencies
-
-# Install nvm
-RUN touch .profile
-RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.25.4/install.sh | bash
-# Install all versions of node that we want to test
-RUN /bin/bash -l -c "nvm install 0.12 && npm config set cache /tmp/npm-cache"
-RUN /bin/bash -l -c "nvm install 4 && npm config set cache /tmp/npm-cache"
-RUN /bin/bash -l -c "nvm install 5 && npm config set cache /tmp/npm-cache"
-RUN /bin/bash -l -c "nvm alias default 4"
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-
-RUN mkdir /var/local/jenkins
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_node/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_node/build_interop_stress.sh
deleted file mode 100755
index 4116f84..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_node/build_interop_stress.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds Node interop server and client in a base image.
-set -e
-
-mkdir -p /var/local/git
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-cd /var/local/git/grpc
-
-# build Node interop client & server
-npm install -g node-gyp
-npm install --unsafe-perm --build-from-source
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile
deleted file mode 100644
index b5198b4..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile
+++ /dev/null
@@ -1,125 +0,0 @@
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:jessie
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-#==================
-# Ruby dependencies
-
-# Install rvm
-RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
-RUN \curl -sSL https://get.rvm.io | bash -s stable
-
-# Install Ruby 2.1
-RUN /bin/bash -l -c "rvm install ruby-2.1"
-RUN /bin/bash -l -c "rvm use --default ruby-2.1"
-RUN /bin/bash -l -c "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
-RUN /bin/bash -l -c "echo 'export PATH=/usr/local/rvm/bin:$PATH' >> ~/.bashrc"
-RUN /bin/bash -l -c "echo 'rvm --default use ruby-2.1' >> ~/.bashrc"
-RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"
-
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
-
-#=================
-# PHP dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    git php5 php5-dev phpunit unzip
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-
-RUN mkdir /var/local/jenkins
-
-# Install composer
-RUN curl -sS https://getcomposer.org/installer | php
-RUN mv composer.phar /usr/local/bin/composer
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_php/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_php/build_interop_stress.sh
deleted file mode 100755
index e3cca08..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_php/build_interop_stress.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds PHP interop server and client in a base image.
-set -ex
-
-mkdir -p /var/local/git
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-cd /var/local/git/grpc
-
-make install-certs
-
-# gRPC core and protobuf need to be installed
-make install
-
-(cd src/php/ext/grpc && phpize && ./configure && make)
-
-(cd third_party/protobuf && make install)
-
-(cd src/php && php -d extension=ext/grpc/modules/grpc.so /usr/local/bin/composer install)
-
-(cd src/php && ./bin/generate_proto_php.sh)
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_python/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_python/Dockerfile
deleted file mode 100644
index 8e1de51..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_python/Dockerfile
+++ /dev/null
@@ -1,103 +0,0 @@
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:jessie
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-#=================
-# C++ dependencies
-RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
-
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-
-RUN pip install coverage
-RUN pip install oauth2client
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_python/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_python/build_interop_stress.sh
deleted file mode 100755
index 1c7dc2b..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_python/build_interop_stress.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds Python interop server and client in a base image.
-set -e
-
-mkdir -p /var/local/git
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-cd /var/local/git/grpc
-tools/run_tests/run_tests.py -l python -c opt --build_only
-
-# Build c++ interop client
-make metrics_client -j
-
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_ruby/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_ruby/Dockerfile
deleted file mode 100644
index 9d291aa..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_ruby/Dockerfile
+++ /dev/null
@@ -1,114 +0,0 @@
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:jessie
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-#=================
-# C++ dependencies
-RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
-
-# Google Cloud platform API libraries
-RUN apt-get update && apt-get install -y python-pip && apt-get clean
-RUN pip install --upgrade google-api-python-client
-
-
-#==================
-# Ruby dependencies
-
-# Install rvm
-RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
-RUN \curl -sSL https://get.rvm.io | bash -s stable
-
-# Install Ruby 2.1
-RUN /bin/bash -l -c "rvm install ruby-2.1"
-RUN /bin/bash -l -c "rvm use --default ruby-2.1"
-RUN /bin/bash -l -c "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
-RUN /bin/bash -l -c "echo 'export PATH=/usr/local/rvm/bin:$PATH' >> ~/.bashrc"
-RUN /bin/bash -l -c "echo 'rvm --default use ruby-2.1' >> ~/.bashrc"
-RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_ruby/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_ruby/build_interop_stress.sh
deleted file mode 100755
index 019f0a4..0000000
--- a/tools/dockerfile/stress_test/grpc_interop_stress_ruby/build_interop_stress.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Builds Ruby interop server and client in a base image.
-set -e
-
-mkdir -p /var/local/git
-git clone /var/local/jenkins/grpc /var/local/git/grpc
-# clone gRPC submodules, use data from locally cloned submodules where possible
-(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
-&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
-${name}')
-
-# Copy service account keys if available
-cp -r /var/local/jenkins/service_account $HOME || true
-
-cd /var/local/git/grpc
-rvm --default use ruby-2.1
-
-# Build Ruby interop client and server
-(cd src/ruby && gem update bundler && bundle && rake compile)
-
-# Build c++ metrics client to query the metrics from ruby stress client
-make metrics_client -j
-
diff --git a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
index f9e709d..0b21a22 100644
--- a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/cxx_jessie_x64/Dockerfile b/tools/dockerfile/test/cxx_jessie_x64/Dockerfile
index 4bb97c7..d9dc272 100644
--- a/tools/dockerfile/test/cxx_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/cxx_jessie_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/cxx_jessie_x86/Dockerfile b/tools/dockerfile/test/cxx_jessie_x86/Dockerfile
index c4b710b..11ef52d 100644
--- a/tools/dockerfile/test/cxx_jessie_x86/Dockerfile
+++ b/tools/dockerfile/test/cxx_jessie_x86/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile b/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile
index bd742df..41d3b2b 100644
--- a/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile
+++ b/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile b/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile
index bc46b30..23d6fb8 100644
--- a/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile
+++ b/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/cxx_wheezy_x64/Dockerfile b/tools/dockerfile/test/cxx_wheezy_x64/Dockerfile
deleted file mode 100644
index f7d7f54..0000000
--- a/tools/dockerfile/test/cxx_wheezy_x64/Dockerfile
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FROM debian:wheezy
-
-# Install Git and basic packages.
-RUN apt-get update && apt-get install -y \
-  autoconf \
-  autotools-dev \
-  build-essential \
-  bzip2 \
-  ccache \
-  curl \
-  gcc \
-  gcc-multilib \
-  git \
-  golang \
-  gyp \
-  lcov \
-  libc6 \
-  libc6-dbg \
-  libc6-dev \
-  libgtest-dev \
-  libtool \
-  make \
-  perl \
-  strace \
-  python-dev \
-  python-setuptools \
-  python-yaml \
-  telnet \
-  unzip \
-  wget \
-  zip && apt-get clean
-
-#================
-# Build profiling
-RUN apt-get update && apt-get install -y time && apt-get clean
-
-#====================
-# Python dependencies
-
-# Install dependencies
-
-RUN apt-get update && apt-get install -y \
-    python-all-dev \
-    python3-all-dev \
-    python-pip
-
-# Install Python packages from PyPI
-RUN pip install pip --upgrade
-RUN pip install virtualenv
-RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
-
-#=================
-# C++ dependencies
-RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
-
-
-RUN apt-get update && apt-get install -y \
-  gcc-4.4 \
-  gcc-4.4-multilib \
-  g++-4.4 \
-  g++-4.4-multilib
-
-# set up backport to allow installation of Git version > 1.7
-RUN echo "deb http://http.debian.net/debian wheezy-backports main"   >/etc/apt/sources.list.d/wheezy-backports.list
-RUN apt-get update -qq
-RUN apt-get -t wheezy-backports install -qq git
-
-RUN wget https://openssl.org/source/old/1.0.2/openssl-1.0.2f.tar.gz
-
-ENV POST_GIT_STEP tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh
-
-# Prepare ccache
-RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
-RUN ln -s /usr/bin/ccache /usr/local/bin/g++
-RUN ln -s /usr/bin/ccache /usr/local/bin/cc
-RUN ln -s /usr/bin/ccache /usr/local/bin/c++
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang
-RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
-
-
-RUN mkdir /var/local/jenkins
-
-# Define the default command.
-CMD ["bash"]
diff --git a/tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh b/tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh
deleted file mode 100755
index dfde93b..0000000
--- a/tools/dockerfile/test/cxx_wheezy_x64/post-git-setup.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-cd /var/local/git/grpc
-cp /openssl-1.0.2f.tar.gz third_party
-./tools/openssl/use_openssl.sh
diff --git a/tools/dockerfile/test/fuzzer/Dockerfile b/tools/dockerfile/test/fuzzer/Dockerfile
index b398b70..4200ba0 100644
--- a/tools/dockerfile/test/fuzzer/Dockerfile
+++ b/tools/dockerfile/test/fuzzer/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
index c1cce0a..9b50d85 100644
--- a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #================
 # C# dependencies
 
diff --git a/tools/dockerfile/test/node_jessie_x64/Dockerfile b/tools/dockerfile/test/node_jessie_x64/Dockerfile
index 4595aa6..deef892 100644
--- a/tools/dockerfile/test/node_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/node_jessie_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 
 # Install Electron apt dependencies
 RUN apt-get update && apt-get install -y \
diff --git a/tools/dockerfile/test/php7_jessie_x64/Dockerfile b/tools/dockerfile/test/php7_jessie_x64/Dockerfile
index 0e2c103..6057c2d 100644
--- a/tools/dockerfile/test/php7_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/php7_jessie_x64/Dockerfile
@@ -75,6 +75,10 @@
   && make \
   && make install
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/php_jessie_x64/Dockerfile b/tools/dockerfile/test/php_jessie_x64/Dockerfile
index c6f3dde..1510c36 100644
--- a/tools/dockerfile/test/php_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/php_jessie_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/python_jessie_x64/Dockerfile b/tools/dockerfile/test/python_jessie_x64/Dockerfile
index 94c1707..cc69f4b 100644
--- a/tools/dockerfile/test/python_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/python_jessie_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/python_pyenv_x64/Dockerfile b/tools/dockerfile/test/python_pyenv_x64/Dockerfile
index 435a9fd..a105d33 100644
--- a/tools/dockerfile/test/python_pyenv_x64/Dockerfile
+++ b/tools/dockerfile/test/python_pyenv_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/ruby_jessie_x64/Dockerfile b/tools/dockerfile/test/ruby_jessie_x64/Dockerfile
index 679c8ff..0a5c9a6 100644
--- a/tools/dockerfile/test/ruby_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/ruby_jessie_x64/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/dockerfile/test/sanity/Dockerfile b/tools/dockerfile/test/sanity/Dockerfile
index 0da2a19..7692330 100644
--- a/tools/dockerfile/test/sanity/Dockerfile
+++ b/tools/dockerfile/test/sanity/Dockerfile
@@ -63,6 +63,10 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
+# Google Cloud platform API libraries
+RUN apt-get update && apt-get install -y python-pip && apt-get clean
+RUN pip install --upgrade google-api-python-client
+
 #====================
 # Python dependencies
 
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index f49c2de..51d77c2 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -910,10 +910,14 @@
 src/core/ext/filters/client_channel/http_proxy.h \
 src/core/ext/filters/client_channel/lb_policy.c \
 src/core/ext/filters/client_channel/lb_policy.h \
+src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \
+src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c \
+src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \
+src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \
 src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h \
 src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
diff --git a/tools/gce/linux_worker_init.sh b/tools/gce/linux_worker_init.sh
index d552343..f795980 100755
--- a/tools/gce/linux_worker_init.sh
+++ b/tools/gce/linux_worker_init.sh
@@ -61,6 +61,10 @@
 # see https://github.com/grpc/grpc/issues/4988
 printf "{\n\t\"storage-driver\": \"overlay\"\n}" | sudo tee /etc/docker/daemon.json
 
+# Install pip and Google API library to enable using GCP services
+sudo apt-get install -y python-pip
+sudo pip install google-api-python-client
+
 # Install RVM
 # TODO(jtattermusch): why is RVM needed?
 gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
diff --git a/tools/gcp/stress_test/run_client.py b/tools/gcp/stress_test/run_client.py
deleted file mode 100755
index 51ada68..0000000
--- a/tools/gcp/stress_test/run_client.py
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/usr/bin/env python2.7
-# Copyright 2015-2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import datetime
-import os
-import re
-import resource
-import select
-import subprocess
-import sys
-import time
-
-from stress_test_utils import EventType
-from stress_test_utils import BigQueryHelper
-
-
-# TODO (sree): Write a python grpc client to directly query the metrics instead
-# of calling metrics_client
-def _get_qps(metrics_cmd):
-  qps = 0
-  try:
-    # Note: gpr_log() writes even non-error messages to stderr stream. So it is 
-    # important that we set stderr=subprocess.STDOUT
-    p = subprocess.Popen(args=metrics_cmd,
-                         stdout=subprocess.PIPE,
-                         stderr=subprocess.STDOUT)
-    retcode = p.wait()
-    (out_str, err_str) = p.communicate()
-    if retcode != 0:
-      print 'Error in reading metrics information'
-      print 'Output: ', out_str
-    else:
-      # The overall qps is printed at the end of the line
-      m = re.search('\d+$', out_str)
-      qps = int(m.group()) if m else 0
-  except Exception as ex:
-    print 'Exception while reading metrics information: ' + str(ex)
-  return qps
-
-
-def run_client():
-  """This is a wrapper around the stress test client and performs the following:
-      1) Create the following two tables in Big Query:
-         (i) Summary table: To record events like the test started, completed
-                            successfully or failed
-        (ii) Qps table: To periodically record the QPS sent by this client
-      2) Start the stress test client and add a row in the Big Query summary
-         table
-      3) Once every few seconds (as specificed by the poll_interval_secs) poll
-         the status of the stress test client process and perform the
-         following:
-          3.1) If the process is still running, get the current qps by invoking
-               the metrics client program and add a row in the Big Query
-               Qps table. Sleep for a duration specified by poll_interval_secs
-          3.2) If the process exited successfully, add a row in the Big Query
-               Summary table and exit
-          3.3) If the process failed, add a row in Big Query summary table and
-               wait forever.
-               NOTE: This script typically runs inside a GKE pod which means
-               that the pod gets destroyed when the script exits. However, in
-               case the stress test client fails, we would not want the pod to
-               be destroyed (since we might want to connect to the pod for
-               examining logs). This is the reason why the script waits forever
-               in case of failures
-  """
-  # Set the 'core file' size to 'unlimited' so that 'core' files are generated
-  # if the client crashes (Note: This is not relevant for Java and Go clients)
-  resource.setrlimit(resource.RLIMIT_CORE,
-                     (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
-
-  env = dict(os.environ)
-  image_type = env['STRESS_TEST_IMAGE_TYPE']
-  stress_client_cmd = env['STRESS_TEST_CMD'].split()
-  args_str = env['STRESS_TEST_ARGS_STR']
-  metrics_client_cmd = env['METRICS_CLIENT_CMD'].split()
-  metrics_client_args_str = env['METRICS_CLIENT_ARGS_STR']
-  run_id = env['RUN_ID']
-  pod_name = env['POD_NAME']
-  logfile_name = env.get('LOGFILE_NAME')
-  poll_interval_secs = float(env['POLL_INTERVAL_SECS'])
-  project_id = env['GCP_PROJECT_ID']
-  dataset_id = env['DATASET_ID']
-  summary_table_id = env['SUMMARY_TABLE_ID']
-  qps_table_id = env['QPS_TABLE_ID']
-  # The following parameter is to inform us whether the stress client runs
-  # forever until forcefully stopped or will it naturally stop after sometime.
-  # This way, we know that the stress client process should not terminate (even
-  # if it does with a success exit code) and flag the termination as a failure
-  will_run_forever = env.get('WILL_RUN_FOREVER', '1')
-
-  bq_helper = BigQueryHelper(run_id, image_type, pod_name, project_id,
-                             dataset_id, summary_table_id, qps_table_id)
-  bq_helper.initialize()
-
-  # Create BigQuery Dataset and Tables: Summary Table and Metrics Table
-  if not bq_helper.setup_tables():
-    print 'Error in creating BigQuery tables'
-    return
-
-  start_time = datetime.datetime.now()
-
-  logfile = None
-  details = 'Logging to stdout'
-  if logfile_name is not None:
-    print 'Opening logfile: %s ...' % logfile_name
-    details = 'Logfile: %s' % logfile_name
-    logfile = open(logfile_name, 'w')
-
-  metrics_cmd = metrics_client_cmd + [x
-                                      for x in metrics_client_args_str.split()]
-  stress_cmd = stress_client_cmd + [x for x in args_str.split()]
-
-  details = '%s, Metrics command: %s, Stress client command: %s' % (
-      details, str(metrics_cmd), str(stress_cmd))
-  # Update status that the test is starting (in the status table)
-  bq_helper.insert_summary_row(EventType.STARTING, details)
-
-  print 'Launching process %s ...' % stress_cmd
-  stress_p = subprocess.Popen(args=stress_cmd,
-                              stdout=logfile,
-                              stderr=subprocess.STDOUT)
-
-  qps_history = [1, 1, 1]  # Maintain the last 3 qps readings
-  qps_history_idx = 0  # Index into the qps_history list
-
-  is_running_status_written = False
-  is_error = False
-  while True:
-    # Check if stress_client is still running. If so, collect metrics and upload
-    # to BigQuery status table
-    # If stress_p.poll() is not None, it means that the stress client terminated
-    if stress_p.poll() is not None:
-      end_time = datetime.datetime.now().isoformat()
-      event_type = EventType.SUCCESS
-      details = 'End time: %s' % end_time
-      if will_run_forever == '1' or stress_p.returncode != 0:
-        event_type = EventType.FAILURE
-        details = 'Return code = %d. End time: %s' % (stress_p.returncode,
-                                                      end_time)
-        is_error = True
-      bq_helper.insert_summary_row(event_type, details)
-      print details
-      break
-
-    if not is_running_status_written:
-      bq_helper.insert_summary_row(EventType.RUNNING, '')
-      is_running_status_written = True
-
-    # Stress client still running. Get metrics
-    qps = _get_qps(metrics_cmd)
-    qps_recorded_at = datetime.datetime.now().isoformat()
-    print 'qps: %d at %s' % (qps, qps_recorded_at)
-
-    # If QPS has been zero for the last 3 iterations, flag it as error and exit
-    qps_history[qps_history_idx] = qps
-    qps_history_idx = (qps_history_idx + 1) % len(qps_history)
-    if sum(qps_history) == 0:
-      details = 'QPS has been zero for the last %d seconds - as of : %s' % (
-          poll_interval_secs * 3, qps_recorded_at)
-      is_error = True
-      bq_helper.insert_summary_row(EventType.FAILURE, details)
-      print details
-      break
-
-    # Upload qps metrics to BiqQuery
-    bq_helper.insert_qps_row(qps, qps_recorded_at)
-
-    time.sleep(poll_interval_secs)
-
-  if is_error:
-    print 'Waiting indefinitely..'
-    select.select([], [], [])
-
-  print 'Completed'
-  return
-
-
-if __name__ == '__main__':
-  run_client()
diff --git a/tools/gcp/stress_test/run_node.sh b/tools/gcp/stress_test/run_node.sh
deleted file mode 100755
index 4a4da6f..0000000
--- a/tools/gcp/stress_test/run_node.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-# Copyright 2015-2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# This is a wrapper script that was created to help run_server.py and
-# run_client.py to launch 'node js' stress clients and stress servers
-source ~/.nvm/nvm.sh
-
-set -ex
-
-$@
diff --git a/tools/gcp/stress_test/run_ruby.sh b/tools/gcp/stress_test/run_ruby.sh
deleted file mode 100755
index 80d0567..0000000
--- a/tools/gcp/stress_test/run_ruby.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-# Copyright 2015-2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# This is a wrapper script that was created to help run_server.py and
-# run_client.py to launch 'node js' stress clients and stress servers
-source /etc/profile.d/rvm.sh
-
-set -ex
-
-$@
diff --git a/tools/gcp/stress_test/run_server.py b/tools/gcp/stress_test/run_server.py
deleted file mode 100755
index 8f47e42..0000000
--- a/tools/gcp/stress_test/run_server.py
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/env python2.7
-# Copyright 2015-2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import datetime
-import os
-import resource
-import select
-import subprocess
-import sys
-import time
-
-from stress_test_utils import BigQueryHelper
-from stress_test_utils import EventType
-
-
-def run_server():
-  """This is a wrapper around the interop server and performs the following:
-      1) Create a 'Summary table' in Big Query to record events like the server
-         started, completed successfully or failed. NOTE: This also creates
-         another table called the QPS table which is currently NOT needed on the
-         server (it is needed on the stress test clients)
-      2) Start the server process and add a row in Big Query summary table
-      3) Wait for the server process to terminate. The server process does not
-         terminate unless there is an error.
-         If the server process terminated with a failure, add a row in Big Query
-         and wait forever.
-         NOTE: This script typically runs inside a GKE pod which means that the
-         pod gets destroyed when the script exits. However, in case the server
-         process fails, we would not want the pod to be destroyed (since we
-         might want to connect to the pod for examining logs). This is the
-         reason why the script waits forever in case of failures.
-  """
-  # Set the 'core file' size to 'unlimited' so that 'core' files are generated
-  # if the server crashes (Note: This is not relevant for Java and Go servers)
-  resource.setrlimit(resource.RLIMIT_CORE,
-                     (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
-
-  # Read the parameters from environment variables
-  env = dict(os.environ)
-
-  run_id = env['RUN_ID']  # The unique run id for this test
-  image_type = env['STRESS_TEST_IMAGE_TYPE']
-  stress_server_cmd = env['STRESS_TEST_CMD'].split()
-  args_str = env['STRESS_TEST_ARGS_STR']
-  pod_name = env['POD_NAME']
-  project_id = env['GCP_PROJECT_ID']
-  dataset_id = env['DATASET_ID']
-  summary_table_id = env['SUMMARY_TABLE_ID']
-  qps_table_id = env['QPS_TABLE_ID']
-  # The following parameter is to inform us whether the server runs forever
-  # until forcefully stopped or will it naturally stop after sometime.
-  # This way, we know that the process should not terminate (even if it does
-  # with a success exit code) and flag any termination as a failure.
-  will_run_forever = env.get('WILL_RUN_FOREVER', '1')
-
-  logfile_name = env.get('LOGFILE_NAME')
-
-  print('pod_name: %s, project_id: %s, run_id: %s, dataset_id: %s, '
-        'summary_table_id: %s, qps_table_id: %s') % (pod_name, project_id,
-                                                     run_id, dataset_id,
-                                                     summary_table_id,
-                                                     qps_table_id)
-
-  bq_helper = BigQueryHelper(run_id, image_type, pod_name, project_id,
-                             dataset_id, summary_table_id, qps_table_id)
-  bq_helper.initialize()
-
-  # Create BigQuery Dataset and Tables: Summary Table and Metrics Table
-  if not bq_helper.setup_tables():
-    print 'Error in creating BigQuery tables'
-    return
-
-  start_time = datetime.datetime.now()
-
-  logfile = None
-  details = 'Logging to stdout'
-  if logfile_name is not None:
-    print 'Opening log file: ', logfile_name
-    logfile = open(logfile_name, 'w')
-    details = 'Logfile: %s' % logfile_name
-
-  stress_cmd = stress_server_cmd + [x for x in args_str.split()]
-
-  details = '%s, Stress server command: %s' % (details, str(stress_cmd))
-  # Update status that the test is starting (in the status table)
-  bq_helper.insert_summary_row(EventType.STARTING, details)
-
-  print 'Launching process %s ...' % stress_cmd
-  stress_p = subprocess.Popen(args=stress_cmd,
-                              stdout=logfile,
-                              stderr=subprocess.STDOUT)
-
-  # Update the status to running if subprocess.Popen launched the server
-  if stress_p.poll() is None:
-    bq_helper.insert_summary_row(EventType.RUNNING, '')
-
-  # Wait for the server process to terminate
-  returncode = stress_p.wait()
-
-  if will_run_forever == '1' or returncode != 0:
-    end_time = datetime.datetime.now().isoformat()
-    event_type = EventType.FAILURE
-    details = 'Returncode: %d; End time: %s' % (returncode, end_time)
-    bq_helper.insert_summary_row(event_type, details)
-    print 'Waiting indefinitely..'
-    select.select([], [], [])
-  return returncode
-
-
-if __name__ == '__main__':
-  run_server()
diff --git a/tools/gcp/stress_test/stress_test_utils.py b/tools/gcp/stress_test/stress_test_utils.py
deleted file mode 100755
index be50af3..0000000
--- a/tools/gcp/stress_test/stress_test_utils.py
+++ /dev/null
@@ -1,217 +0,0 @@
-#!/usr/bin/env python2.7
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import datetime
-import json
-import os
-import re
-import select
-import subprocess
-import sys
-import time
-
-# Import big_query_utils module
-bq_utils_dir = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '../utils'))
-sys.path.append(bq_utils_dir)
-import big_query_utils as bq_utils
-
-
-class EventType:
-  STARTING = 'STARTING'
-  RUNNING = 'RUNNING'
-  SUCCESS = 'SUCCESS'
-  FAILURE = 'FAILURE'
-
-
-class BigQueryHelper:
-  """Helper class for the stress test wrappers to interact with BigQuery.
-  """
-
-  def __init__(self, run_id, image_type, pod_name, project_id, dataset_id,
-               summary_table_id, qps_table_id):
-    self.run_id = run_id
-    self.image_type = image_type
-    self.pod_name = pod_name
-    self.project_id = project_id
-    self.dataset_id = dataset_id
-    self.summary_table_id = summary_table_id
-    self.qps_table_id = qps_table_id
-
-  def initialize(self):
-    self.bq = bq_utils.create_big_query()
-
-  def setup_tables(self):
-    return bq_utils.create_dataset(self.bq, self.project_id, self.dataset_id) \
-        and self.__create_summary_table() \
-        and self.__create_qps_table()
-
-  def insert_summary_row(self, event_type, details):
-    row_values_dict = {
-        'run_id': self.run_id,
-        'image_type': self.image_type,
-        'pod_name': self.pod_name,
-        'event_date': datetime.datetime.now().isoformat(),
-        'event_type': event_type,
-        'details': details
-    }
-    # row_unique_id is something that uniquely identifies the row (BigQuery uses
-    # it for duplicate detection).
-    row_unique_id = '%s_%s_%s' % (self.run_id, self.pod_name, event_type)
-    row = bq_utils.make_row(row_unique_id, row_values_dict)
-    return bq_utils.insert_rows(self.bq, self.project_id, self.dataset_id,
-                                self.summary_table_id, [row])
-
-  def insert_qps_row(self, qps, recorded_at):
-    row_values_dict = {
-        'run_id': self.run_id,
-        'pod_name': self.pod_name,
-        'recorded_at': recorded_at,
-        'qps': qps
-    }
-
-    # row_unique_id is something that uniquely identifies the row (BigQuery uses
-    # it for duplicate detection).
-    row_unique_id = '%s_%s_%s' % (self.run_id, self.pod_name, recorded_at)
-    row = bq_utils.make_row(row_unique_id, row_values_dict)
-    return bq_utils.insert_rows(self.bq, self.project_id, self.dataset_id,
-                                self.qps_table_id, [row])
-
-  def check_if_any_tests_failed(self, num_query_retries=3, timeout_msec=30000):
-    query = ('SELECT event_type FROM %s.%s WHERE run_id = \'%s\' AND '
-             'event_type="%s"') % (self.dataset_id, self.summary_table_id,
-                                   self.run_id, EventType.FAILURE)
-    page = None
-    try:
-      query_job = bq_utils.sync_query_job(self.bq, self.project_id, query)
-      job_id = query_job['jobReference']['jobId']
-      project_id = query_job['jobReference']['projectId']
-      page = self.bq.jobs().getQueryResults(
-          projectId=project_id,
-          jobId=job_id,
-          timeoutMs=timeout_msec).execute(num_retries=num_query_retries)
-
-      if not page['jobComplete']:
-        print('TIMEOUT ERROR: The query %s timed out. Current timeout value is'
-              ' %d msec. Returning False (i.e assuming there are no failures)'
-             ) % (query, timeout_msec)
-        return False
-
-      num_failures = int(page['totalRows'])
-      print 'num rows: ', num_failures
-      return num_failures > 0
-    except:
-      print 'Exception in check_if_any_tests_failed(). Info: ', sys.exc_info()
-      print 'Query: ', query
-
-  def print_summary_records(self, num_query_retries=3):
-    line = '-' * 120
-    print line
-    print 'Summary records'
-    print 'Run Id: ', self.run_id
-    print 'Dataset Id: ', self.dataset_id
-    print line
-    query = ('SELECT pod_name, image_type, event_type, event_date, details'
-             ' FROM %s.%s WHERE run_id = \'%s\' ORDER by event_date;') % (
-                 self.dataset_id, self.summary_table_id, self.run_id)
-    query_job = bq_utils.sync_query_job(self.bq, self.project_id, query)
-
-    print '{:<25} {:<12} {:<12} {:<30} {}'.format('Pod name', 'Image type',
-                                                  'Event type', 'Date',
-                                                  'Details')
-    print line
-    page_token = None
-    while True:
-      page = self.bq.jobs().getQueryResults(
-          pageToken=page_token,
-          **query_job['jobReference']).execute(num_retries=num_query_retries)
-      rows = page.get('rows', [])
-      for row in rows:
-        print '{:<25} {:<12} {:<12} {:<30} {}'.format(row['f'][0]['v'],
-                                                      row['f'][1]['v'],
-                                                      row['f'][2]['v'],
-                                                      row['f'][3]['v'],
-                                                      row['f'][4]['v'])
-      page_token = page.get('pageToken')
-      if not page_token:
-        break
-
-  def print_qps_records(self, num_query_retries=3):
-    line = '-' * 80
-    print line
-    print 'QPS Summary'
-    print 'Run Id: ', self.run_id
-    print 'Dataset Id: ', self.dataset_id
-    print line
-    query = (
-        'SELECT pod_name, recorded_at, qps FROM %s.%s WHERE run_id = \'%s\' '
-        'ORDER by recorded_at;') % (self.dataset_id, self.qps_table_id,
-                                    self.run_id)
-    query_job = bq_utils.sync_query_job(self.bq, self.project_id, query)
-    print '{:<25} {:30} {}'.format('Pod name', 'Recorded at', 'Qps')
-    print line
-    page_token = None
-    while True:
-      page = self.bq.jobs().getQueryResults(
-          pageToken=page_token,
-          **query_job['jobReference']).execute(num_retries=num_query_retries)
-      rows = page.get('rows', [])
-      for row in rows:
-        print '{:<25} {:30} {}'.format(row['f'][0]['v'], row['f'][1]['v'],
-                                       row['f'][2]['v'])
-      page_token = page.get('pageToken')
-      if not page_token:
-        break
-
-  def __create_summary_table(self):
-    summary_table_schema = [
-        ('run_id', 'STRING', 'Test run id'),
-        ('image_type', 'STRING', 'Client or Server?'),
-        ('pod_name', 'STRING', 'GKE pod hosting this image'),
-        ('event_date', 'STRING', 'The date of this event'),
-        ('event_type', 'STRING', 'STARTING/RUNNING/SUCCESS/FAILURE'),
-        ('details', 'STRING', 'Any other relevant details')
-    ]
-    desc = ('The table that contains STARTING/RUNNING/SUCCESS/FAILURE events '
-            'for the stress test clients and servers')
-    return bq_utils.create_table(self.bq, self.project_id, self.dataset_id,
-                                 self.summary_table_id, summary_table_schema,
-                                 desc)
-
-  def __create_qps_table(self):
-    qps_table_schema = [
-        ('run_id', 'STRING', 'Test run id'),
-        ('pod_name', 'STRING', 'GKE pod hosting this image'),
-        ('recorded_at', 'STRING', 'Metrics recorded at time'),
-        ('qps', 'INTEGER', 'Queries per second')
-    ]
-    desc = 'The table that cointains the qps recorded at various intervals'
-    return bq_utils.create_table(self.bq, self.project_id, self.dataset_id,
-                                 self.qps_table_id, qps_table_schema, desc)
diff --git a/tools/gcp/utils/kubernetes_api.py b/tools/gcp/utils/kubernetes_api.py
deleted file mode 100755
index a8a4aad..0000000
--- a/tools/gcp/utils/kubernetes_api.py
+++ /dev/null
@@ -1,269 +0,0 @@
-#!/usr/bin/env python2.7
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import requests
-import json
-
-_REQUEST_TIMEOUT_SECS = 10
-
-
-def _make_pod_config(pod_name, image_name, container_port_list, cmd_list,
-                     arg_list, env_dict):
-  """Creates a string containing the Pod defintion as required by the Kubernetes API"""
-  body = {
-      'kind': 'Pod',
-      'apiVersion': 'v1',
-      'metadata': {
-          'name': pod_name,
-          'labels': {'name': pod_name}
-      },
-      'spec': {
-          'containers': [
-              {
-                  'name': pod_name,
-                  'image': image_name,
-                  'ports': [{'containerPort': port,
-                             'protocol': 'TCP'}
-                            for port in container_port_list],
-                  'imagePullPolicy': 'Always'
-              }
-          ]
-      }
-  }
-
-  env_list = [{'name': k, 'value': v} for (k, v) in env_dict.iteritems()]
-  if len(env_list) > 0:
-    body['spec']['containers'][0]['env'] = env_list
-
-  # Add the 'Command' and 'Args' attributes if they are passed.
-  # Note:
-  #  - 'Command' overrides the ENTRYPOINT in the Docker Image
-  #  - 'Args' override the CMD in Docker image (yes, it is confusing!)
-  if len(cmd_list) > 0:
-    body['spec']['containers'][0]['command'] = cmd_list
-  if len(arg_list) > 0:
-    body['spec']['containers'][0]['args'] = arg_list
-  return json.dumps(body)
-
-
-def _make_service_config(service_name, pod_name, service_port_list,
-                         container_port_list, is_headless):
-  """Creates a string containing the Service definition as required by the Kubernetes API.
-
-  NOTE:
-  This creates either a Headless Service or 'LoadBalancer' service depending on
-  the is_headless parameter. For Headless services, there is no 'type' attribute
-  and the 'clusterIP' attribute is set to 'None'. Also, if the service is
-  Headless, Kubernetes creates DNS entries for Pods - i.e creates DNS A-records
-  mapping the service's name to the Pods' IPs
-  """
-  if len(container_port_list) != len(service_port_list):
-    print(
-        'ERROR: container_port_list and service_port_list must be of same size')
-    return ''
-  body = {
-      'kind': 'Service',
-      'apiVersion': 'v1',
-      'metadata': {
-          'name': service_name,
-          'labels': {
-              'name': service_name
-          }
-      },
-      'spec': {
-          'ports': [],
-          'selector': {
-              'name': pod_name
-          }
-      }
-  }
-  # Populate the 'ports' list in the 'spec' section. This maps service ports
-  # (port numbers that are exposed by Kubernetes) to container ports (i.e port
-  # numbers that are exposed by your Docker image)
-  for idx in range(len(container_port_list)):
-    port_entry = {
-        'port': service_port_list[idx],
-        'targetPort': container_port_list[idx],
-        'protocol': 'TCP'
-    }
-    body['spec']['ports'].append(port_entry)
-
-  # Make this either a LoadBalancer service or a headless service depending on
-  # the is_headless parameter
-  if is_headless:
-    body['spec']['clusterIP'] = 'None'
-  else:
-    body['spec']['type'] = 'LoadBalancer'
-  return json.dumps(body)
-
-
-def _print_connection_error(msg):
-  print('ERROR: Connection failed. Did you remember to run Kubenetes proxy on '
-        'localhost (i.e kubectl proxy --port=<proxy_port>) ?. Error: %s' % msg)
-
-
-def _do_post(post_url, api_name, request_body):
-  """Helper to do HTTP POST.
-
-  Note:
-  1) On success, Kubernetes returns a success code of 201(CREATED) not 200(OK)
-  2) A response code of 509(CONFLICT) is interpreted as a success code (since
-  the error is most likely due to the resource already existing). This makes
-  _do_post() idempotent which is semantically desirable.
-  """
-  is_success = True
-  try:
-    r = requests.post(post_url,
-                      data=request_body,
-                      timeout=_REQUEST_TIMEOUT_SECS)
-    if r.status_code == requests.codes.conflict:
-      print('WARN: Looks like the resource already exists. Api: %s, url: %s' %
-            (api_name, post_url))
-    elif r.status_code != requests.codes.created:
-      print('ERROR: %s API returned error. HTTP response: (%d) %s' %
-            (api_name, r.status_code, r.text))
-      is_success = False
-  except (requests.exceptions.Timeout,
-          requests.exceptions.ConnectionError) as e:
-    is_success = False
-    _print_connection_error(str(e))
-  return is_success
-
-
-def _do_delete(del_url, api_name):
-  """Helper to do HTTP DELETE.
-
-  Note: A response code of 404(NOT_FOUND) is treated as success to keep
-  _do_delete() idempotent.
-  """
-  is_success = True
-  try:
-    r = requests.delete(del_url, timeout=_REQUEST_TIMEOUT_SECS)
-    if r.status_code == requests.codes.not_found:
-      print('WARN: The resource does not exist. Api: %s, url: %s' %
-            (api_name, del_url))
-    elif r.status_code != requests.codes.ok:
-      print('ERROR: %s API returned error. HTTP response: %s' %
-            (api_name, r.text))
-      is_success = False
-  except (requests.exceptions.Timeout,
-          requests.exceptions.ConnectionError) as e:
-    is_success = False
-    _print_connection_error(str(e))
-  return is_success
-
-
-def create_service(kube_host, kube_port, namespace, service_name, pod_name,
-                   service_port_list, container_port_list, is_headless):
-  """Creates either a Headless Service or a LoadBalancer Service depending
-  on the is_headless parameter.
-  """
-  post_url = 'http://%s:%d/api/v1/namespaces/%s/services' % (
-      kube_host, kube_port, namespace)
-  request_body = _make_service_config(service_name, pod_name, service_port_list,
-                                      container_port_list, is_headless)
-  return _do_post(post_url, 'Create Service', request_body)
-
-
-def create_pod(kube_host, kube_port, namespace, pod_name, image_name,
-               container_port_list, cmd_list, arg_list, env_dict):
-  """Creates a Kubernetes Pod.
-
-  Note that it is generally NOT considered a good practice to directly create
-  Pods. Typically, the recommendation is to create 'Controllers' to create and
-  manage Pods' lifecycle. Currently Kubernetes only supports 'Replication
-  Controller' which creates a configurable number of 'identical Replicas' of
-  Pods and automatically restarts any Pods in case of failures (for eg: Machine
-  failures in Kubernetes). This makes it less flexible for our test use cases
-  where we might want slightly different set of args to each Pod. Hence we
-  directly create Pods and not care much about Kubernetes failures since those
-  are very rare.
-  """
-  post_url = 'http://%s:%d/api/v1/namespaces/%s/pods' % (kube_host, kube_port,
-                                                         namespace)
-  request_body = _make_pod_config(pod_name, image_name, container_port_list,
-                                  cmd_list, arg_list, env_dict)
-  return _do_post(post_url, 'Create Pod', request_body)
-
-
-def delete_service(kube_host, kube_port, namespace, service_name):
-  del_url = 'http://%s:%d/api/v1/namespaces/%s/services/%s' % (
-      kube_host, kube_port, namespace, service_name)
-  return _do_delete(del_url, 'Delete Service')
-
-
-def delete_pod(kube_host, kube_port, namespace, pod_name):
-  del_url = 'http://%s:%d/api/v1/namespaces/%s/pods/%s' % (kube_host, kube_port,
-                                                           namespace, pod_name)
-  return _do_delete(del_url, 'Delete Pod')
-
-
-def create_pod_and_service(kube_host, kube_port, namespace, pod_name,
-                           image_name, container_port_list, cmd_list, arg_list,
-                           env_dict, is_headless_service):
-  """A helper function that creates a pod and a service (if pod creation was successful)."""
-  is_success = create_pod(kube_host, kube_port, namespace, pod_name, image_name,
-                          container_port_list, cmd_list, arg_list, env_dict)
-  if not is_success:
-    print 'Error in creating Pod'
-    return False
-
-  is_success = create_service(
-      kube_host,
-      kube_port,
-      namespace,
-      pod_name,  # Use pod_name for service
-      pod_name,
-      container_port_list,  # Service port list same as container port list
-      container_port_list,
-      is_headless_service)
-  if not is_success:
-    print 'Error in creating Service'
-    return False
-
-  print 'Successfully created the pod/service %s' % pod_name
-  return True
-
-
-def delete_pod_and_service(kube_host, kube_port, namespace, pod_name):
-  """ A helper function that calls delete_pod and delete_service """
-  is_success = delete_pod(kube_host, kube_port, namespace, pod_name)
-  if not is_success:
-    print 'Error in deleting pod %s' % pod_name
-    return False
-
-  # Note: service name assumed to the the same as pod name
-  is_success = delete_service(kube_host, kube_port, namespace, pod_name)
-  if not is_success:
-    print 'Error in deleting service %s' % pod_name
-    return False
-
-  print 'Successfully deleted the Pod/Service: %s' % pod_name
-  return True
diff --git a/tools/internal_ci/linux/grpc_fuzzer_http_request.sh b/tools/internal_ci/helper_scripts/prepare_build_linux_rc
old mode 100755
new mode 100644
similarity index 89%
rename from tools/internal_ci/linux/grpc_fuzzer_http_request.sh
rename to tools/internal_ci/helper_scripts/prepare_build_linux_rc
index ef975d3..c8cb5a0
--- a/tools/internal_ci/linux/grpc_fuzzer_http_request.sh
+++ b/tools/internal_ci/helper_scripts/prepare_build_linux_rc
@@ -28,14 +28,12 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-set -ex
+# Source this rc script to prepare the environment for linux builds
 
-# change to grpc repo root
-cd $(dirname $0)/../../..
+# Need to increase open files limit for c tests
+ulimit -n 32768
+
+# Download Docker images from DockerHub
+export DOCKERHUB_ORGANIZATION=grpctesting
 
 git submodule update --init
-
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-config=asan-trace-cmp tools/jenkins/run_fuzzer.sh http_request_fuzzer_test
-
diff --git a/tools/internal_ci/linux/grpc_fuzzer_api.cfg b/tools/internal_ci/linux/grpc_fuzzer_api.cfg
deleted file mode 100644
index 5c2592e..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_api.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Config file for the internal CI (in protobuf text format)
-
-# Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_api.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
-action {
-  define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
-  }
-}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_api.sh b/tools/internal_ci/linux/grpc_fuzzer_api.sh
deleted file mode 100755
index edf8843..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_api.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-git submodule update --init
-
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-# runtime 23 * 60 mins
-config=asan-trace-cmp runtime=82800 tools/jenkins/run_fuzzer.sh api_fuzzer
diff --git a/tools/internal_ci/linux/grpc_fuzzer_client.sh b/tools/internal_ci/linux/grpc_fuzzer_client.sh
deleted file mode 100755
index c03f925..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_client.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-git submodule update --init
-
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-# runtime 23 * 60 mins
-config=asan-trace-cmp runtime=82800 tools/jenkins/run_fuzzer.sh client_fuzzer
diff --git a/tools/internal_ci/linux/grpc_fuzzer_hpack_parser.cfg b/tools/internal_ci/linux/grpc_fuzzer_hpack_parser.cfg
deleted file mode 100644
index 72482b6..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_hpack_parser.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Config file for the internal CI (in protobuf text format)
-
-# Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_hpack_parser.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
-action {
-  define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
-  }
-}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_hpack_parser.sh b/tools/internal_ci/linux/grpc_fuzzer_hpack_parser.sh
deleted file mode 100755
index 43933e6..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_hpack_parser.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-git submodule update --init
-
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-config=asan-trace-cmp tools/jenkins/run_fuzzer.sh hpack_parser_fuzzer_test
-
diff --git a/tools/internal_ci/linux/grpc_fuzzer_http_request.cfg b/tools/internal_ci/linux/grpc_fuzzer_http_request.cfg
deleted file mode 100644
index a4a0e89..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_http_request.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Config file for the internal CI (in protobuf text format)
-
-# Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_http_request.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
-action {
-  define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
-  }
-}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_json.cfg b/tools/internal_ci/linux/grpc_fuzzer_json.cfg
deleted file mode 100644
index d22da2d..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_json.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Config file for the internal CI (in protobuf text format)
-
-# Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_json.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
-action {
-  define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
-  }
-}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_json.sh b/tools/internal_ci/linux/grpc_fuzzer_json.sh
deleted file mode 100755
index 1e64a02..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_json.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-git submodule update --init
-
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-config=asan-trace-cmp tools/jenkins/run_fuzzer.sh json_fuzzer_test
-
diff --git a/tools/internal_ci/linux/grpc_fuzzer_nanopb_response.cfg b/tools/internal_ci/linux/grpc_fuzzer_nanopb_response.cfg
deleted file mode 100644
index cbf44ba..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_nanopb_response.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Config file for the internal CI (in protobuf text format)
-
-# Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_nanopb_response.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
-action {
-  define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
-  }
-}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_server.cfg b/tools/internal_ci/linux/grpc_fuzzer_server.cfg
deleted file mode 100644
index 7877d51..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_server.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Config file for the internal CI (in protobuf text format)
-
-# Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_server.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
-action {
-  define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
-  }
-}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_server.sh b/tools/internal_ci/linux/grpc_fuzzer_server.sh
deleted file mode 100755
index 82b24b0..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_server.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-git submodule update --init
-
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-# runtime 23 * 60 mins
-config=asan-trace-cmp runtime=82800 tools/jenkins/run_fuzzer.sh server_fuzzer
diff --git a/tools/internal_ci/linux/grpc_fuzzer_uri.cfg b/tools/internal_ci/linux/grpc_fuzzer_uri.cfg
deleted file mode 100644
index 134b3d0..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_uri.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Config file for the internal CI (in protobuf text format)
-
-# Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_uri.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
-action {
-  define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
-  }
-}
diff --git a/tools/internal_ci/linux/grpc_fuzzer_uri.sh b/tools/internal_ci/linux/grpc_fuzzer_uri.sh
deleted file mode 100755
index 67039f3..0000000
--- a/tools/internal_ci/linux/grpc_fuzzer_uri.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/bash
-# Copyright 2017, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-# change to grpc repo root
-cd $(dirname $0)/../../..
-
-git submodule update --init
-
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-config=asan-trace-cmp tools/jenkins/run_fuzzer.sh uri_fuzzer_test
diff --git a/tools/internal_ci/linux/grpc_interop_badserver_java.sh b/tools/internal_ci/linux/grpc_interop_badserver_java.sh
index c309c62..02d7b9d 100755
--- a/tools/internal_ci/linux/grpc_interop_badserver_java.sh
+++ b/tools/internal_ci/linux/grpc_interop_badserver_java.sh
@@ -35,7 +35,7 @@
 # Enter the gRPC repo root
 cd $(dirname $0)/../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
 tools/run_tests/run_interop_tests.py -l java --use_docker --http2_server_interop $@
 
diff --git a/tools/internal_ci/linux/grpc_interop_badserver_python.sh b/tools/internal_ci/linux/grpc_interop_badserver_python.sh
index c3bb92f..3ceb181 100755
--- a/tools/internal_ci/linux/grpc_interop_badserver_python.sh
+++ b/tools/internal_ci/linux/grpc_interop_badserver_python.sh
@@ -35,7 +35,7 @@
 # Enter the gRPC repo root
 cd $(dirname $0)/../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
 tools/run_tests/run_interop_tests.py -l python --use_docker --http2_server_interop $@
 
diff --git a/tools/internal_ci/linux/grpc_interop_tocloud.sh b/tools/internal_ci/linux/grpc_interop_tocloud.sh
index 572001d..a3067e7 100755
--- a/tools/internal_ci/linux/grpc_interop_tocloud.sh
+++ b/tools/internal_ci/linux/grpc_interop_tocloud.sh
@@ -35,6 +35,6 @@
 # Enter the gRPC repo root
 cd $(dirname $0)/../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
 tools/run_tests/run_interop_tests.py -l all -s all --use_docker --http2_interop -t -j 12 $@
diff --git a/tools/internal_ci/linux/grpc_master.sh b/tools/internal_ci/linux/grpc_master.sh
index 9ecf123..d3c89bf 100755
--- a/tools/internal_ci/linux/grpc_master.sh
+++ b/tools/internal_ci/linux/grpc_master.sh
@@ -33,26 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../..
 
-# TODO(jtattermusch): get rid of the system inspection eventually
-nproc || true
-lsb_release -dc || true
-gcc --version || true
-clang --version || true
-docker --version || true
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# Need to increase open files limit for c tests
-ulimit -n 2000
-
-git submodule update --init
-
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests.py -l c -t -x sponge_log.xml || FAILED="true"
-
-# kill port_server.py to prevent the build from hanging
-ps aux | grep port_server\\.py | awk '{print $2}' | xargs kill -9
-
-if [ "$FAILED" != "" ]
-then
-  exit 1
-fi
+tools/run_tests/run_tests_matrix.py -f basictests linux --inner_jobs 16 -j 1 --internal_ci
diff --git a/tools/internal_ci/linux/grpc_portability.sh b/tools/internal_ci/linux/grpc_portability.sh
index 58d3c58..64959c7 100755
--- a/tools/internal_ci/linux/grpc_portability.sh
+++ b/tools/internal_ci/linux/grpc_portability.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests_matrix.py -f portability linux
+tools/run_tests/run_tests_matrix.py -f portability linux --inner_jobs 16 -j 1 --internal_ci
diff --git a/tools/internal_ci/linux/grpc_portability_build_only.sh b/tools/internal_ci/linux/grpc_portability_build_only.sh
index 80b5c4c..099c3f8 100755
--- a/tools/internal_ci/linux/grpc_portability_build_only.sh
+++ b/tools/internal_ci/linux/grpc_portability_build_only.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests_matrix.py -f portability linux --build_only
+tools/run_tests/run_tests_matrix.py -f portability linux --internal_ci --build_only
diff --git a/tools/internal_ci/linux/grpc_sanity.sh b/tools/internal_ci/linux/grpc_sanity.sh
index fac25c7..7166ce7 100755
--- a/tools/internal_ci/linux/grpc_sanity.sh
+++ b/tools/internal_ci/linux/grpc_sanity.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download base docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
 tools/run_tests/run_tests.py -l sanity -c opt -t -x sponge_log.xml --use_docker --report_suite_name sanity_linux_opt
diff --git a/tools/internal_ci/linux/sanitizer/grpc_c_asan.sh b/tools/internal_ci/linux/sanitizer/grpc_c_asan.sh
index 335d47a..5a61d9d 100755
--- a/tools/internal_ci/linux/sanitizer/grpc_c_asan.sh
+++ b/tools/internal_ci/linux/sanitizer/grpc_c_asan.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests_matrix.py -f c asan
+tools/run_tests/run_tests_matrix.py -f c asan --inner_jobs 16 -j 1 --internal_ci
diff --git a/tools/internal_ci/linux/sanitizer/grpc_c_msan.sh b/tools/internal_ci/linux/sanitizer/grpc_c_msan.sh
index fe9565e..1c3b90d 100755
--- a/tools/internal_ci/linux/sanitizer/grpc_c_msan.sh
+++ b/tools/internal_ci/linux/sanitizer/grpc_c_msan.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests_matrix.py -f c msan
+tools/run_tests/run_tests_matrix.py -f c msan --inner_jobs 16 -j 1 --internal_ci
diff --git a/tools/internal_ci/linux/sanitizer/grpc_c_tsan.sh b/tools/internal_ci/linux/sanitizer/grpc_c_tsan.sh
index 49bbbee..495a004 100755
--- a/tools/internal_ci/linux/sanitizer/grpc_c_tsan.sh
+++ b/tools/internal_ci/linux/sanitizer/grpc_c_tsan.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests_matrix.py -f c tsan
+tools/run_tests/run_tests_matrix.py -f c tsan --inner_jobs 16 -j 1 --internal_ci
diff --git a/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.sh b/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.sh
index 47ccb26..99219e3 100755
--- a/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.sh
+++ b/tools/internal_ci/linux/sanitizer/grpc_cpp_asan.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests_matrix.py -f c++ asan
+tools/run_tests/run_tests_matrix.py -f c++ asan --inner_jobs 16 -j 1 --internal_ci
diff --git a/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.sh b/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.sh
index ee3ec5e..be46af9 100755
--- a/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.sh
+++ b/tools/internal_ci/linux/sanitizer/grpc_cpp_tsan.sh
@@ -33,8 +33,6 @@
 # change to grpc repo root
 cd $(dirname $0)/../../../..
 
-git submodule update --init
+source tools/internal_ci/helper_scripts/prepare_build_linux_rc
 
-# download docker images from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-tools/run_tests/run_tests_matrix.py -f c++ tsan
+tools/run_tests/run_tests_matrix.py -f c++ tsan --inner_jobs 16 -j 1 --internal_ci
diff --git a/tools/internal_ci/linux/grpc_fuzzer_client.cfg b/tools/internal_ci/macos/grpc_master.cfg
similarity index 90%
rename from tools/internal_ci/linux/grpc_fuzzer_client.cfg
rename to tools/internal_ci/macos/grpc_master.cfg
index 1e8f688..039c99e 100644
--- a/tools/internal_ci/linux/grpc_fuzzer_client.cfg
+++ b/tools/internal_ci/macos/grpc_master.cfg
@@ -30,10 +30,10 @@
 # Config file for the internal CI (in protobuf text format)
 
 # Location of the continuous shell script in repository.
-build_file: "grpc/tools/internal_ci/linux/grpc_fuzzer_client.sh"
-timeout_mins: 1440  # 24 hours is the maximum allowed value
+build_file: "grpc/tools/internal_ci/macos/grpc_master.sh"
+timeout_mins: 240
 action {
   define_artifacts {
-    regex: "git/grpc/fuzzer_output/**"
+    regex: "**/*sponge_log.xml"
   }
 }
diff --git a/tools/internal_ci/linux/grpc_fuzzer_nanopb_response.sh b/tools/internal_ci/macos/grpc_master.sh
similarity index 86%
rename from tools/internal_ci/linux/grpc_fuzzer_nanopb_response.sh
rename to tools/internal_ci/macos/grpc_master.sh
index 6e7f4b7..4ce1af7 100755
--- a/tools/internal_ci/linux/grpc_fuzzer_nanopb_response.sh
+++ b/tools/internal_ci/macos/grpc_master.sh
@@ -35,6 +35,12 @@
 
 git submodule update --init
 
-# download fuzzer docker image from dockerhub
-export DOCKERHUB_ORGANIZATION=grpctesting
-config=asan-trace-cmp tools/jenkins/run_fuzzer.sh nanopb_fuzzer_response_test
+tools/run_tests/run_tests_matrix.py -f basictests macos --internal_ci || FAILED="true"
+
+# kill port_server.py to prevent the build from hanging
+ps aux | grep port_server\\.py | awk '{print $2}' | xargs kill -9
+
+if [ "$FAILED" != "" ]
+then
+  exit 1
+fi
diff --git a/tools/internal_ci/windows/grpc_master.bat b/tools/internal_ci/windows/grpc_master.bat
index 8943390..b6f3c87 100644
--- a/tools/internal_ci/windows/grpc_master.bat
+++ b/tools/internal_ci/windows/grpc_master.bat
@@ -36,7 +36,7 @@
 
 git submodule update --init
 
-python tools/run_tests/run_tests_matrix.py -f basictests windows -j 1 --inner_jobs 8 || goto :error
+python tools/run_tests/run_tests_matrix.py -f basictests windows -j 1 --inner_jobs 8 --internal_ci || goto :error
 goto :EOF
 
 :error
diff --git a/tools/internal_ci/windows/grpc_portability_master.bat b/tools/internal_ci/windows/grpc_portability_master.bat
index b98c701..7898086 100644
--- a/tools/internal_ci/windows/grpc_portability_master.bat
+++ b/tools/internal_ci/windows/grpc_portability_master.bat
@@ -36,7 +36,7 @@
 
 git submodule update --init
 
-python tools/run_tests/run_tests_matrix.py -f portability windows -j 1 --inner_jobs 8 || goto :error
+python tools/run_tests/run_tests_matrix.py -f portability windows -j 1 --inner_jobs 8 --internal_ci || goto :error
 goto :EOF
 
 :error
diff --git a/tools/jenkins/run_bazel_basic_in_docker.sh b/tools/jenkins/run_bazel_basic_in_docker.sh
index b1d498a..5013f80 100755
--- a/tools/jenkins/run_bazel_basic_in_docker.sh
+++ b/tools/jenkins/run_bazel_basic_in_docker.sh
@@ -39,4 +39,4 @@
 && git submodule update --init --reference /var/local/jenkins/grpc/${name} \
 ${name}')
 cd /var/local/git/grpc
-bazel build --spawn_strategy=standalone --genrule_strategy=standalone :all test/... examples/cpp/...
+bazel build --spawn_strategy=standalone --genrule_strategy=standalone :all test/... examples/...
diff --git a/tools/jenkins/run_interop_stress.sh b/tools/jenkins/run_interop_stress.sh
deleted file mode 100755
index 22d81db..0000000
--- a/tools/jenkins/run_interop_stress.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# This script is invoked by Jenkins and runs interop test suite.
-set -ex
-
-# Enter the gRPC repo root
-cd $(dirname $0)/../..
-
-tools/run_tests/run_stress_tests.py -l all -s all -j 12 $@ || true
diff --git a/tools/profiling/microbenchmarks/bm_diff.py b/tools/profiling/microbenchmarks/bm_diff.py
index b2d6f46..299abb5 100755
--- a/tools/profiling/microbenchmarks/bm_diff.py
+++ b/tools/profiling/microbenchmarks/bm_diff.py
@@ -56,6 +56,10 @@
   'writes_per_iteration',
   'atm_cas_per_iteration',
   'atm_add_per_iteration',
+  'cli_transport_stalls_per_iteration',
+  'cli_stream_stalls_per_iteration',
+  'svr_transport_stalls_per_iteration',
+  'svr_stream_stalls_per_iteration'
   'nows_per_iteration',
 )
 
diff --git a/tools/run_tests/README.md b/tools/run_tests/README.md
index 05d33fd..60f2074 100644
--- a/tools/run_tests/README.md
+++ b/tools/run_tests/README.md
@@ -33,7 +33,7 @@
 to BigQuery.
 
 ###### Example
-`tools/run_tests/run_peformance_tests.py -l c++ node`
+`tools/run_tests/run_performance_tests.py -l c++ node`
 
 ###### Useful options
 - `--regex` use regex to select particular scenarios to run.
diff --git a/tools/run_tests/artifacts/artifact_targets.py b/tools/run_tests/artifacts/artifact_targets.py
index 04702ba..c90da06 100644
--- a/tools/run_tests/artifacts/artifact_targets.py
+++ b/tools/run_tests/artifacts/artifact_targets.py
@@ -202,6 +202,7 @@
                  'EMBED_OPENSSL': 'true',
                  'EMBED_ZLIB': 'true',
                  'CFLAGS': '-DGPR_BACKWARDS_COMPATIBILITY_MODE',
+                 'CXXFLAGS': '-DGPR_BACKWARDS_COMPATIBILITY_MODE',
                  'LDFLAGS': ''}
       if self.platform == 'linux':
         return create_docker_jobspec(self.name,
@@ -211,6 +212,7 @@
       else:
         archflag = _ARCH_FLAG_MAP[self.arch]
         environ['CFLAGS'] += ' %s %s' % (archflag, _MACOS_COMPAT_FLAG)
+        environ['CXXFLAGS'] += ' %s %s' % (archflag, _MACOS_COMPAT_FLAG)
         environ['LDFLAGS'] += ' %s' % archflag
         return create_jobspec(self.name,
                               ['tools/run_tests/artifacts/build_artifact_csharp.sh'],
diff --git a/tools/run_tests/artifacts/build_artifact_node.sh b/tools/run_tests/artifacts/build_artifact_node.sh
index 2da2ac5..7a74755 100755
--- a/tools/run_tests/artifacts/build_artifact_node.sh
+++ b/tools/run_tests/artifacts/build_artifact_node.sh
@@ -48,7 +48,7 @@
 
 for version in ${node_versions[@]}
 do
-  ./node_modules/.bin/node-pre-gyp configure rebuild package testpackage --target=$version --target_arch=$NODE_TARGET_ARCH
+  ./node_modules/.bin/node-pre-gyp configure rebuild package testpackage --target=$version --target_arch=$NODE_TARGET_ARCH --grpc_alpine=true
   cp -r build/stage/* artifacts/
 done
 
diff --git a/tools/run_tests/dockerize/build_docker_and_run_tests.sh b/tools/run_tests/dockerize/build_docker_and_run_tests.sh
index f10916d..7928745 100755
--- a/tools/run_tests/dockerize/build_docker_and_run_tests.sh
+++ b/tools/run_tests/dockerize/build_docker_and_run_tests.sh
@@ -79,7 +79,11 @@
   -e HOST_GIT_ROOT=$git_root \
   -e LOCAL_GIT_ROOT=$docker_instance_git_root \
   -e "BUILD_ID=$BUILD_ID" \
+  -e "BUILD_URL=$BUILD_URL" \
+  -e "JOB_BASE_NAME=$JOB_BASE_NAME" \
   -i $TTY_FLAG \
+  --sysctl net.ipv6.conf.all.disable_ipv6=0 \
+  -v ~/.config/gcloud:/root/.config/gcloud \
   -v "$git_root:$docker_instance_git_root" \
   -v /tmp/ccache:/tmp/ccache \
   -v /tmp/npm-cache:/tmp/npm-cache \
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 5eba7fb..c962823 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -3291,6 +3291,29 @@
     ], 
     "is_filegroup": false, 
     "language": "c++", 
+    "name": "grpclb_end2end_test", 
+    "src": [
+      "test/cpp/end2end/grpclb_end2end_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [
+      "src/proto/grpc/lb/v1/load_balancer.grpc.pb.h", 
+      "src/proto/grpc/lb/v1/load_balancer.pb.h", 
+      "src/proto/grpc/lb/v1/load_balancer_mock.grpc.pb.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c++", 
     "name": "grpclb_test", 
     "src": [
       "test/cpp/grpclb/grpclb_test.cc"
@@ -6026,8 +6049,7 @@
       "grpc++_codegen_base_src", 
       "grpc++_codegen_proto", 
       "grpc++_config_proto", 
-      "grpc_test_util", 
-      "thrift_util"
+      "grpc_test_util"
     ], 
     "headers": [
       "src/proto/grpc/health/v1/health.grpc.pb.h", 
@@ -8212,8 +8234,10 @@
       "nanopb"
     ], 
     "headers": [
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h"
     ], 
@@ -8221,10 +8245,14 @@
     "language": "c", 
     "name": "grpc_lb_policy_grpclb", 
     "src": [
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", 
@@ -8242,8 +8270,10 @@
       "nanopb"
     ], 
     "headers": [
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h"
     ], 
@@ -8251,10 +8281,14 @@
     "language": "c", 
     "name": "grpc_lb_policy_grpclb_secure", 
     "src": [
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c", 
+      "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", 
       "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", 
@@ -9129,23 +9163,5 @@
     ], 
     "third_party": false, 
     "type": "filegroup"
-  }, 
-  {
-    "deps": [
-      "grpc++_codegen_base"
-    ], 
-    "headers": [
-      "include/grpc++/impl/codegen/thrift_serializer.h", 
-      "include/grpc++/impl/codegen/thrift_utils.h"
-    ], 
-    "is_filegroup": true, 
-    "language": "c++", 
-    "name": "thrift_util", 
-    "src": [
-      "include/grpc++/impl/codegen/thrift_serializer.h", 
-      "include/grpc++/impl/codegen/thrift_utils.h"
-    ], 
-    "third_party": false, 
-    "type": "filegroup"
   }
 ]
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 17f7c4b..c77961c 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -3407,6 +3407,28 @@
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "grpclb_end2end_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
     "gtest": false, 
     "language": "c++", 
     "name": "grpclb_test", 
@@ -4183,7 +4205,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4207,7 +4230,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4231,7 +4255,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4255,7 +4280,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4281,7 +4307,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4305,7 +4332,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4329,7 +4357,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4356,7 +4385,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4383,7 +4413,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4410,7 +4441,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4437,7 +4469,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4464,7 +4497,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4491,7 +4525,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4518,7 +4553,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4545,7 +4581,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4572,7 +4609,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4599,7 +4637,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4626,7 +4665,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4653,7 +4693,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4680,7 +4721,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4707,7 +4749,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4734,7 +4777,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4761,7 +4805,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4788,7 +4833,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4815,7 +4861,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4842,7 +4889,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4869,7 +4917,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4895,7 +4944,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4919,7 +4969,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4943,7 +4994,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4969,7 +5021,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -4993,7 +5046,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5017,7 +5071,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5041,7 +5096,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5065,7 +5121,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5089,7 +5146,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5113,7 +5171,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5137,7 +5196,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5163,7 +5223,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5189,7 +5250,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5213,7 +5275,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5239,7 +5302,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5263,7 +5327,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5287,7 +5352,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5313,7 +5379,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5337,7 +5404,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5361,7 +5429,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5387,7 +5456,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5411,7 +5481,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5435,7 +5506,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5459,7 +5531,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5483,7 +5556,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5509,7 +5583,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5533,7 +5608,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5557,7 +5633,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5581,7 +5658,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5607,7 +5685,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5631,7 +5710,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5655,7 +5735,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5679,7 +5760,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5703,7 +5785,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5727,7 +5810,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5751,7 +5835,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5775,7 +5860,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -5799,7 +5885,8 @@
     "cpu_cost": 1.0, 
     "defaults": "boringssl", 
     "exclude_configs": [
-      "asan"
+      "asan", 
+      "ubsan"
     ], 
     "flaky": false, 
     "language": "c++", 
@@ -41416,7 +41503,7 @@
   {
     "args": [
       "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 13, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
     ], 
     "boringssl": true, 
     "ci_platforms": [
@@ -41845,6 +41932,206 @@
   {
     "args": [
       "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_ping_pong_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1024, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_qps_unconstrained_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_ping_pong_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_qps_unconstrained_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_ping_pong_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1024, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_qps_unconstrained_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_ping_pong_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_qps_unconstrained_secure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
       "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
     ], 
     "boringssl": true, 
@@ -41995,7 +42282,7 @@
   {
     "args": [
       "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 13, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
     ], 
     "boringssl": true, 
     "ci_platforms": [
@@ -42424,6 +42711,206 @@
   {
     "args": [
       "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_ping_pong_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1024, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_ping_pong_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_ping_pong_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1024, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_ping_pong_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "tsan", 
+      "asan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
       "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
     ], 
     "boringssl": true, 
@@ -42652,7 +43139,7 @@
   {
     "args": [
       "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 13, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
     ], 
     "boringssl": true, 
     "ci_platforms": [
@@ -43302,6 +43789,310 @@
   {
     "args": [
       "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_ping_pong_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 64, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_qps_unconstrained_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_ping_pong_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_qps_unconstrained_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_ping_pong_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 64, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_qps_unconstrained_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_ping_pong_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_qps_unconstrained_secure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
       "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
     ], 
     "boringssl": true, 
@@ -43530,7 +44321,7 @@
   {
     "args": [
       "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+      "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_one_server_core_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 13, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
     ], 
     "boringssl": true, 
     "ci_platforms": [
@@ -44179,6 +44970,310 @@
   }, 
   {
     "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_ping_pong_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 64, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_client_qps_unconstrained_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_ping_pong_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_CLIENT\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_client_qps_unconstrained_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_ping_pong_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 64, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_from_server_qps_unconstrained_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 1, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_ping_pong_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"num_clients\": 0, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING_FROM_SERVER\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": "capacity", 
+    "defaults": "boringssl", 
+    "exclude_configs": [
+      "asan-noleaks", 
+      "asan-trace-cmp", 
+      "basicprof", 
+      "c++-compat", 
+      "counters", 
+      "dbg", 
+      "gcov", 
+      "helgrind", 
+      "lto", 
+      "memcheck", 
+      "msan", 
+      "mutrace", 
+      "opt", 
+      "stapprof", 
+      "ubsan"
+    ], 
+    "excluded_poll_engines": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_from_server_qps_unconstrained_insecure_low_thread_count", 
+    "timeout_seconds": 360
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/00.bin"
     ], 
     "ci_platforms": [
diff --git a/tools/run_tests/helper_scripts/build_node.sh b/tools/run_tests/helper_scripts/build_node.sh
index 2c4cf02..07ecf98 100755
--- a/tools/run_tests/helper_scripts/build_node.sh
+++ b/tools/run_tests/helper_scripts/build_node.sh
@@ -46,6 +46,4 @@
   *) config_flag='--release' ;;
 esac
 
-uv_flag=$2
-
-npm install --unsafe-perm --build-from-source $uv_flag $config_flag
+npm install --unsafe-perm --build-from-source $config_flag
diff --git a/tools/run_tests/helper_scripts/pre_build_cmake.bat b/tools/run_tests/helper_scripts/pre_build_cmake.bat
index c937b9e..c721e16 100644
--- a/tools/run_tests/helper_scripts/pre_build_cmake.bat
+++ b/tools/run_tests/helper_scripts/pre_build_cmake.bat
@@ -37,7 +37,10 @@
 cd build
 
 @rem TODO(jtattermusch): Stop hardcoding path to yasm once Jenkins workers can locate yasm correctly
-cmake -G "Visual Studio 14 2015" -DgRPC_BUILD_TESTS=ON -DCMAKE_ASM_NASM_COMPILER="C:/Program Files (x86)/yasm/yasm.exe" ../.. || goto :error
+@rem If yasm is not on the path, use hardcoded path instead.
+yasm --version || set USE_HARDCODED_YASM_PATH_MAYBE=-DCMAKE_ASM_NASM_COMPILER="C:/Program Files (x86)/yasm/yasm.exe"
+
+cmake -G "Visual Studio 14 2015" -DgRPC_BUILD_TESTS=ON %USE_HARDCODED_YASM_PATH_MAYBE% ../.. || goto :error
 
 endlocal
 
diff --git a/tools/run_tests/helper_scripts/pre_build_csharp.bat b/tools/run_tests/helper_scripts/pre_build_csharp.bat
index e59dac4..47ebd8b 100644
--- a/tools/run_tests/helper_scripts/pre_build_csharp.bat
+++ b/tools/run_tests/helper_scripts/pre_build_csharp.bat
@@ -42,8 +42,12 @@
 cd build
 mkdir %ARCHITECTURE%
 cd %ARCHITECTURE%
+
 @rem TODO(jtattermusch): Stop hardcoding path to yasm once Jenkins workers can locate yasm correctly
-cmake -G "Visual Studio 14 2015" -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON -DCMAKE_ASM_NASM_COMPILER="C:/Program Files (x86)/yasm/yasm.exe" ../../.. || goto :error
+@rem If yasm is not on the path, use hardcoded path instead.
+yasm --version || set USE_HARDCODED_YASM_PATH_MAYBE=-DCMAKE_ASM_NASM_COMPILER="C:/Program Files (x86)/yasm/yasm.exe"
+
+cmake -G "Visual Studio 14 2015" -A %ARCHITECTURE% -DgRPC_BUILD_TESTS=OFF -DgRPC_MSVC_STATIC_RUNTIME=ON %USE_HARDCODED_YASM_PATH_MAYBE% ../../.. || goto :error
 
 cd ..\..\..\src\csharp
 
diff --git a/tools/run_tests/performance/build_performance.sh b/tools/run_tests/performance/build_performance.sh
index 3f7c9fe..5f8749d 100755
--- a/tools/run_tests/performance/build_performance.sh
+++ b/tools/run_tests/performance/build_performance.sh
@@ -61,9 +61,6 @@
   "csharp")
     python tools/run_tests/run_tests.py -l $language -c $CONFIG --build_only -j 8 --compiler coreclr
     ;;
-  "node")
-    python tools/run_tests/run_tests.py -l $language -c $CONFIG --build_only -j 8 --iomgr_platform=uv
-    ;;
   *)
     python tools/run_tests/run_tests.py -l $language -c $CONFIG --build_only -j 8
     ;;
diff --git a/tools/run_tests/performance/scenario_config.py b/tools/run_tests/performance/scenario_config.py
index ce08088..8ed675e 100644
--- a/tools/run_tests/performance/scenario_config.py
+++ b/tools/run_tests/performance/scenario_config.py
@@ -53,6 +53,7 @@
 # actual target will be slightly higher)
 OUTSTANDING_REQUESTS={
     'async': 6400,
+    'async-1core': 800,
     'sync': 1000
 }
 
@@ -265,7 +266,7 @@
           rpc_type='STREAMING',
           client_type='ASYNC_CLIENT',
           server_type='ASYNC_GENERIC_SERVER',
-          unconstrained_client='async', use_generic_payload=True,
+          unconstrained_client='async-1core', use_generic_payload=True,
           async_server_threads=1,
           secure=secure)
 
@@ -310,7 +311,7 @@
         secure=secure,
         categories=smoketest_categories + [SCALABLE])
 
-      for rpc_type in ['unary', 'streaming']:
+      for rpc_type in ['unary', 'streaming', 'streaming_from_client', 'streaming_from_server']:
         for synchronicity in ['sync', 'async']:
           yield _ping_pong_scenario(
               'cpp_protobuf_%s_%s_ping_pong_%s' % (synchronicity, rpc_type, secstr),
@@ -410,6 +411,21 @@
         categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
+        'csharp_generic_async_streaming_ping_pong_insecure_1MB', rpc_type='STREAMING',
+        client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
+        req_size=1024*1024, resp_size=1024*1024,
+        use_generic_payload=True,
+        secure=False,
+        categories=[SMOKETEST, SCALABLE])
+
+    yield _ping_pong_scenario(
+        'csharp_generic_async_streaming_qps_unconstrained_insecure', rpc_type='STREAMING',
+        client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
+        unconstrained_client='async', use_generic_payload=True,
+        secure=False,
+        categories=[SMOKETEST, SCALABLE])
+
+    yield _ping_pong_scenario(
         'csharp_protobuf_async_streaming_ping_pong', rpc_type='STREAMING',
         client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER')
 
@@ -469,7 +485,6 @@
         req_size=1024*1024, resp_size=1024*1024,
         categories=[SMOKETEST, SCALABLE])
 
-
   def __str__(self):
     return 'csharp'
 
@@ -738,7 +753,7 @@
       yield _ping_pong_scenario(
           'java_generic_async_streaming_qps_one_server_core_%s' % secstr, rpc_type='STREAMING',
           client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
-          unconstrained_client='async', use_generic_payload=True,
+          unconstrained_client='async-1core', use_generic_payload=True,
           async_server_threads=1,
           secure=secure, warmup_seconds=JAVA_WARMUP_SECONDS)
 
diff --git a/tools/run_tests/python_utils/jobset.py b/tools/run_tests/python_utils/jobset.py
index 460f359..4e97128 100755
--- a/tools/run_tests/python_utils/jobset.py
+++ b/tools/run_tests/python_utils/jobset.py
@@ -208,6 +208,11 @@
   def __repr__(self):
     return 'JobSpec(shortname=%s, cmdline=%s)' % (self.shortname, self.cmdline)
 
+  def __str__(self):
+    return '%s: %s %s' % (self.shortname,
+                          ' '.join('%s=%s' % kv for kv in self.environ.items()),
+                          ' '.join(self.cmdline))
+
 
 class JobResult(object):
   def __init__(self):
@@ -217,7 +222,8 @@
     self.num_failures = 0
     self.retries = 0
     self.message = ''
-
+    self.cpu_estimated = 1
+    self.cpu_measured = 0
 
 class Job(object):
   """Manages one job."""
@@ -307,7 +313,9 @@
           sys = float(m.group(3))
           if real > 0.5:
             cores = (user + sys) / real
-            measurement = '; cpu_cost=%.01f; estimated=%.01f' % (cores, self._spec.cpu_cost)
+            self.result.cpu_measured = float('%.01f' % cores)
+            self.result.cpu_estimated = float('%.01f' % self._spec.cpu_cost)
+            measurement = '; cpu_cost=%.01f; estimated=%.01f' % (self.result.cpu_measured, self.result.cpu_estimated)
         if not self._quiet_success:
           message('PASSED', '%s [time=%.1fsec; retries=%d:%d%s]' % (
               self._spec.shortname, elapsed, self._retries, self._timeout_retries, measurement),
diff --git a/tools/run_tests/python_utils/port_server.py b/tools/run_tests/python_utils/port_server.py
index e96ee0b..9860b92 100755
--- a/tools/run_tests/python_utils/port_server.py
+++ b/tools/run_tests/python_utils/port_server.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -30,8 +30,6 @@
 
 """Manage TCP ports for unit tests; started by run_tests.py"""
 
-from __future__ import print_function
-
 import argparse
 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
 import hashlib
@@ -39,18 +37,20 @@
 import socket
 import sys
 import time
+import random
 from SocketServer import ThreadingMixIn
 import threading
+import platform
 
 
 # increment this number whenever making a change to ensure that
 # the changes are picked up by running CI servers
 # note that all changes must be backwards compatible
-_MY_VERSION = 14
+_MY_VERSION = 20
 
 
 if len(sys.argv) == 2 and sys.argv[1] == 'dump_version':
-  print(_MY_VERSION)
+  print _MY_VERSION
   sys.exit(0)
 
 
@@ -66,16 +66,42 @@
   sys.stderr = open(args.logfile, 'w')
   sys.stdout = sys.stderr
 
-print('port server running on port %d' % args.port)
+print 'port server running on port %d' % args.port
 
 pool = []
 in_use = {}
 mu = threading.Lock()
 
+def can_connect(port):
+  # this test is only really useful on unices where SO_REUSE_PORT is available
+  # so on Windows, where this test is expensive, skip it
+  if platform.system() == 'Windows': return False
+  s = socket.socket()
+  try:
+    s.connect(('localhost', port))
+    return True
+  except socket.error, e:
+    return False
+  finally:
+    s.close()
+
+def can_bind(port, proto):
+  s = socket.socket(proto, socket.SOCK_STREAM)
+  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+  try:
+    s.bind(('localhost', port))
+    return True
+  except socket.error, e:
+    return False
+  finally:
+    s.close()
+
 
 def refill_pool(max_timeout, req):
   """Scan for ports not marked for being in use"""
-  for i in range(1025, 32766):
+  chk = list(range(1025, 32766))
+  random.shuffle(chk)
+  for i in chk:
     if len(pool) > 100: break
     if i in in_use:
       age = time.time() - in_use[i]
@@ -83,16 +109,9 @@
         continue
       req.log_message("kill old request %d" % i)
       del in_use[i]
-    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-    try:
-      s.bind(('localhost', i))
+    if can_bind(i, socket.AF_INET) and can_bind(i, socket.AF_INET6) and not can_connect(i):
       req.log_message("found available port %d" % i)
       pool.append(i)
-    except:
-      pass # we really don't care about failures
-    finally:
-      s.close()
 
 
 def allocate_port(req):
@@ -120,7 +139,7 @@
 
 
 class Handler(BaseHTTPRequestHandler):
-  
+
   def setup(self):
     # If the client is unreachable for 5 seconds, close the connection
     self.timeout = 5
@@ -128,6 +147,7 @@
 
   def do_GET(self):
     global keep_running
+    global mu
     if self.path == '/get':
       # allocate a new port, it will stay bound for ten minutes and until
       # it's unused
@@ -142,12 +162,15 @@
       self.send_header('Content-Type', 'text/plain')
       self.end_headers()
       p = int(self.path[6:])
+      mu.acquire()
       if p in in_use:
         del in_use[p]
         pool.append(p)
-        self.log_message('drop known port %d' % p)
+        k = 'known'
       else:
-        self.log_message('drop unknown port %d' % p)
+        k = 'unknown'
+      mu.release()
+      self.log_message('drop %s port %d' % (k, p))
     elif self.path == '/version_number':
       # fetch a version string and the current process pid
       self.send_response(200)
@@ -161,8 +184,11 @@
       self.send_response(200)
       self.send_header('Content-Type', 'text/plain')
       self.end_headers()
+      mu.acquire()
       now = time.time()
-      self.wfile.write(yaml.dump({'pool': pool, 'in_use': dict((k, now - v) for k, v in in_use.items())}))
+      out = yaml.dump({'pool': pool, 'in_use': dict((k, now - v) for k, v in in_use.items())})
+      mu.release()
+      self.wfile.write(out)
     elif self.path == '/quitquitquit':
       self.send_response(200)
       self.end_headers()
@@ -171,6 +197,4 @@
 class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
   """Handle requests in a separate thread"""
 
-
 ThreadedHTTPServer(('', args.port), Handler).serve_forever()
-
diff --git a/tools/run_tests/python_utils/report_utils.py b/tools/run_tests/python_utils/report_utils.py
index 3b2b4f8..b8dd67e 100644
--- a/tools/run_tests/python_utils/report_utils.py
+++ b/tools/run_tests/python_utils/report_utils.py
@@ -64,22 +64,32 @@
   root = ET.Element('testsuites')
   testsuite = ET.SubElement(root, 'testsuite', id='1', package=suite_package,
                             name=suite_name)
+  failure_count  = 0
+  error_count = 0
   for shortname, results in six.iteritems(resultset):
     for result in results:
       xml_test = ET.SubElement(testsuite, 'testcase', name=shortname)
       if result.elapsed_time:
         xml_test.set('time', str(result.elapsed_time))
-      ET.SubElement(xml_test, 'system-out').text = _filter_msg(result.message,
-                                                               'XML')
+      filtered_msg =  _filter_msg(result.message, 'XML')
       if result.state == 'FAILED':
-        ET.SubElement(xml_test, 'failure', message='Failure')
+        ET.SubElement(xml_test, 'failure', message='Failure').text = filtered_msg
+        failure_count += 1
       elif result.state == 'TIMEOUT':
-        ET.SubElement(xml_test, 'error', message='Timeout')
+        ET.SubElement(xml_test, 'error', message='Timeout').text = filtered_msg
+        error_count += 1
       elif result.state == 'SKIPPED':
         ET.SubElement(xml_test, 'skipped', message='Skipped')
+  testsuite.set('failures', str(failure_count))
+  testsuite.set('errors', str(error_count))
+  # ensure the report directory exists
+  report_dir = os.path.dirname(os.path.abspath(xml_report))
+  if not os.path.exists(report_dir):
+    os.makedirs(report_dir)
   tree = ET.ElementTree(root)
   tree.write(xml_report, encoding='UTF-8')
 
+
 def render_interop_html_report(
   client_langs, server_langs, test_cases, auth_test_cases, http2_cases,
   http2_server_cases, resultset,
diff --git a/tools/run_tests/python_utils/start_port_server.py b/tools/run_tests/python_utils/start_port_server.py
index deb7354..4acc964 100644
--- a/tools/run_tests/python_utils/start_port_server.py
+++ b/tools/run_tests/python_utils/start_port_server.py
@@ -27,9 +27,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from __future__ import print_function
-
-from six.moves import urllib
+import urllib
 import jobset
 import logging
 import os
@@ -50,9 +48,9 @@
     # otherwise, leave it up
     try:
         version = int(
-            urllib.request.urlopen(
-                'http://localhost:%d/version_number' % _PORT_SERVER_PORT,
-                timeout=10).read())
+            urllib.urlopen(
+                'http://localhost:%d/version_number' %
+                _PORT_SERVER_PORT).read())
         logging.info('detected port server running version %d', version)
         running = True
     except Exception as e:
@@ -69,8 +67,8 @@
         running = (version >= current_version)
         if not running:
             logging.info('port_server version mismatch: killing the old one')
-            urllib.request.urlopen('http://localhost:%d/quitquitquit' %
-                                   _PORT_SERVER_PORT).read()
+            urllib.urlopen('http://localhost:%d/quitquitquit' %
+                           _PORT_SERVER_PORT).read()
             time.sleep(1)
     if not running:
         fd, logfile = tempfile.mkstemp()
@@ -109,9 +107,8 @@
                 # try one final time: maybe another build managed to start one
                 time.sleep(1)
                 try:
-                    urllib.request.urlopen(
-                        'http://localhost:%d/get' % _PORT_SERVER_PORT,
-                        timeout=1).read()
+                    urllib.urlopen(
+                        'http://localhost:%d/get' % _PORT_SERVER_PORT).read()
                     logging.info(
                         'last ditch attempt to contact port server succeeded')
                     break
@@ -119,18 +116,18 @@
                     logging.exception(
                         'final attempt to contact port server failed')
                     port_log = open(logfile, 'r').read()
-                    print(port_log)
+                    print port_log
                     sys.exit(1)
             try:
                 port_server_url = 'http://localhost:%d/get' % _PORT_SERVER_PORT
-                urllib.request.urlopen(port_server_url, timeout=1).read()
+                urllib.urlopen(port_server_url).read()
                 logging.info('port server is up and ready')
                 break
             except socket.timeout:
                 logging.exception('while waiting for port_server')
                 time.sleep(1)
                 waits += 1
-            except urllib.error.URLError:
+            except IOError:
                 logging.exception('while waiting for port_server')
                 time.sleep(1)
                 waits += 1
diff --git a/tools/run_tests/python_utils/upload_test_results.py b/tools/run_tests/python_utils/upload_test_results.py
new file mode 100644
index 0000000..d076d1e
--- /dev/null
+++ b/tools/run_tests/python_utils/upload_test_results.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Helper to upload Jenkins test results to BQ"""
+
+from __future__ import print_function
+
+import os
+import six
+import sys
+import time
+import uuid
+
+gcp_utils_dir = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), '../../gcp/utils'))
+sys.path.append(gcp_utils_dir)
+import big_query_utils
+
+_DATASET_ID = 'jenkins_test_results'
+_DESCRIPTION = 'Test results from master job run on Jenkins'
+_PROJECT_ID = 'grpc-testing'
+_RESULTS_SCHEMA = [
+  ('job_name', 'STRING', 'Name of Jenkins job'),
+  ('build_id', 'INTEGER', 'Build ID of Jenkins job'),
+  ('build_url', 'STRING', 'URL of Jenkins job'),
+  ('test_name', 'STRING', 'Individual test name'),
+  ('language', 'STRING', 'Language of test'),
+  ('platform', 'STRING', 'Platform used for test'),
+  ('config', 'STRING', 'Config used for test'),
+  ('compiler', 'STRING', 'Compiler used for test'),
+  ('iomgr_platform', 'STRING', 'Iomgr used for test'),
+  ('result', 'STRING', 'Test result: PASSED, TIMEOUT, FAILED, or SKIPPED'),
+  ('timestamp', 'TIMESTAMP', 'Timestamp of test run'),
+  ('elapsed_time', 'FLOAT', 'How long test took to run'),
+  ('cpu_estimated', 'FLOAT', 'Estimated CPU usage of test'),
+  ('cpu_measured', 'FLOAT', 'Actual CPU usage of test'),
+]
+
+
+def _get_build_metadata(test_results):
+  """Add Jenkins build metadata to test_results based on environment variables set by Jenkins."""
+  build_id = os.getenv('BUILD_ID')
+  build_url = os.getenv('BUILD_URL')
+  job_name = os.getenv('JOB_BASE_NAME')
+
+  if build_id:
+    test_results['build_id'] = build_id
+  if build_url:
+    test_results['build_url'] = build_url
+  if job_name:
+    test_results['job_name'] = job_name
+
+def upload_results_to_bq(resultset, bq_table, args, platform):
+  """Upload test results to a BQ table.
+
+  Args:
+      resultset: dictionary generated by jobset.run
+      bq_table: string name of table to create/upload results to in BQ
+      args: args in run_tests.py, generated by argparse
+      platform: string name of platform tests were run on
+  """
+  bq = big_query_utils.create_big_query()
+  big_query_utils.create_table(bq, _PROJECT_ID, _DATASET_ID, bq_table, _RESULTS_SCHEMA, _DESCRIPTION)
+
+  for shortname, results in six.iteritems(resultset):
+    for result in results:
+      test_results = {}
+      _get_build_metadata(test_results)
+      test_results['compiler'] = args.compiler
+      test_results['config'] = args.config
+      test_results['cpu_estimated'] = result.cpu_estimated
+      test_results['cpu_measured'] = result.cpu_measured
+      test_results['elapsed_time'] = '%.2f' % result.elapsed_time
+      test_results['iomgr_platform'] = args.iomgr_platform
+      # args.language is a list, but will always have one element in the contexts
+      # this function is used.
+      test_results['language'] = args.language[0]
+      test_results['platform'] = platform
+      test_results['result'] = result.state
+      test_results['test_name'] = shortname
+      test_results['timestamp'] = time.strftime('%Y-%m-%d %H:%M:%S')
+
+      row = big_query_utils.make_row(str(uuid.uuid4()), test_results)
+      if not big_query_utils.insert_rows(bq, _PROJECT_ID, _DATASET_ID, bq_table, [row]):
+        print('Error uploading result to bigquery.')
+        sys.exit(1)
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index 44e93eb..867d9e6 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -888,6 +888,10 @@
                   default=False,
                   action='store_const',
                   const=True)
+argp.add_argument('-v', '--verbose',
+                  default=False,
+                  action='store_const',
+                  const=True)
 argp.add_argument('--use_docker',
                   default=False,
                   action='store_const',
@@ -989,6 +993,9 @@
 
   if build_jobs:
     jobset.message('START', 'Building interop docker images.', do_newline=True)
+    if args.verbose:
+      print('Jobs to run: \n%s\n' % '\n'.join(str(j) for j in build_jobs))
+
     num_failures, _ = jobset.run(
         build_jobs, newline_on_success=True, maxjobs=args.jobs)
     if num_failures == 0:
@@ -1164,6 +1171,9 @@
   if args.manual_run:
     print('All tests will skipped --manual_run option is active.')
 
+  if args.verbose:
+    print('Jobs to run: \n%s\n' % '\n'.join(str(job) for job in jobs))
+
   num_failures, resultset = jobset.run(jobs, newline_on_success=True,
                                        maxjobs=args.jobs,
                                        skip_jobs=args.manual_run)
diff --git a/tools/run_tests/run_stress_tests.py b/tools/run_tests/run_stress_tests.py
deleted file mode 100755
index 4eea021..0000000
--- a/tools/run_tests/run_stress_tests.py
+++ /dev/null
@@ -1,331 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Run stress test in C++"""
-
-from __future__ import print_function
-
-import argparse
-import atexit
-import itertools
-import json
-import multiprocessing
-import os
-import re
-import subprocess
-import sys
-import tempfile
-import time
-import uuid
-import six
-
-import python_utils.dockerjob as dockerjob
-import python_utils.jobset as jobset
-
-# Docker doesn't clean up after itself, so we do it on exit.
-atexit.register(lambda: subprocess.call(['stty', 'echo']))
-
-ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
-os.chdir(ROOT)
-
-_DEFAULT_SERVER_PORT = 8080
-_DEFAULT_METRICS_PORT = 8081
-_DEFAULT_TEST_CASES = 'empty_unary:20,large_unary:20,client_streaming:20,server_streaming:20,empty_stream:20'
-_DEFAULT_NUM_CHANNELS_PER_SERVER = 5
-_DEFAULT_NUM_STUBS_PER_CHANNEL = 10
-
-# 15 mins default
-_DEFAULT_TEST_DURATION_SECS = 900
-
-class CXXLanguage:
-
-  def __init__(self):
-    self.client_cwd = None
-    self.server_cwd = None
-    self.safename = 'cxx'
-
-  def client_cmd(self, args):
-    return ['bins/opt/stress_test'] + args
-
-  def server_cmd(self, args):
-    return ['bins/opt/interop_server'] + args
-
-  def global_env(self):
-    return {}
-
-  def __str__(self):
-    return 'c++'
-
-
-_LANGUAGES = {'c++': CXXLanguage(),}
-
-# languages supported as cloud_to_cloud servers
-_SERVERS = ['c++']
-
-DOCKER_WORKDIR_ROOT = '/var/local/git/grpc'
-
-
-def docker_run_cmdline(cmdline, image, docker_args=[], cwd=None, environ=None):
-  """Wraps given cmdline array to create 'docker run' cmdline from it."""
-  docker_cmdline = ['docker', 'run', '-i', '--rm=true']
-
-  # turn environ into -e docker args
-  if environ:
-    for k, v in environ.items():
-      docker_cmdline += ['-e', '%s=%s' % (k, v)]
-
-  # set working directory
-  workdir = DOCKER_WORKDIR_ROOT
-  if cwd:
-    workdir = os.path.join(workdir, cwd)
-  docker_cmdline += ['-w', workdir]
-
-  docker_cmdline += docker_args + [image] + cmdline
-  return docker_cmdline
-
-
-def bash_login_cmdline(cmdline):
-  """Creates bash -l -c cmdline from args list."""
-  # Use login shell:
-  # * rvm and nvm require it
-  # * makes error messages clearer if executables are missing
-  return ['bash', '-l', '-c', ' '.join(cmdline)]
-
-
-def _job_kill_handler(job):
-  if job._spec.container_name:
-    dockerjob.docker_kill(job._spec.container_name)
-    # When the job times out and we decide to kill it,
-    # we need to wait a before restarting the job
-    # to prevent "container name already in use" error.
-    # TODO(jtattermusch): figure out a cleaner way to to this.
-    time.sleep(2)
-
-
-def cloud_to_cloud_jobspec(language,
-                           test_cases,
-                           server_addresses,
-                           test_duration_secs,
-                           num_channels_per_server,
-                           num_stubs_per_channel,
-                           metrics_port,
-                           docker_image=None):
-  """Creates jobspec for cloud-to-cloud interop test"""
-  cmdline = bash_login_cmdline(language.client_cmd([
-      '--test_cases=%s' % test_cases, '--server_addresses=%s' %
-      server_addresses, '--test_duration_secs=%s' % test_duration_secs,
-      '--num_stubs_per_channel=%s' % num_stubs_per_channel,
-      '--num_channels_per_server=%s' % num_channels_per_server,
-      '--metrics_port=%s' % metrics_port
-  ]))
-  print(cmdline)
-  cwd = language.client_cwd
-  environ = language.global_env()
-  if docker_image:
-    container_name = dockerjob.random_name('interop_client_%s' %
-                                           language.safename)
-    cmdline = docker_run_cmdline(
-        cmdline,
-        image=docker_image,
-        environ=environ,
-        cwd=cwd,
-        docker_args=['--net=host', '--name', container_name])
-    cwd = None
-
-  test_job = jobset.JobSpec(cmdline=cmdline,
-                            cwd=cwd,
-                            environ=environ,
-                            shortname='cloud_to_cloud:%s:%s_server:stress_test' % (
-                                language, server_name),
-                            timeout_seconds=test_duration_secs * 2,
-                            flake_retries=0,
-                            timeout_retries=0,
-                            kill_handler=_job_kill_handler)
-  test_job.container_name = container_name
-  return test_job
-
-
-def server_jobspec(language, docker_image, test_duration_secs):
-  """Create jobspec for running a server"""
-  container_name = dockerjob.random_name('interop_server_%s' %
-                                         language.safename)
-  cmdline = bash_login_cmdline(language.server_cmd(['--port=%s' %
-                                                    _DEFAULT_SERVER_PORT]))
-  environ = language.global_env()
-  docker_cmdline = docker_run_cmdline(
-      cmdline,
-      image=docker_image,
-      cwd=language.server_cwd,
-      environ=environ,
-      docker_args=['-p', str(_DEFAULT_SERVER_PORT), '--name', container_name])
-
-  server_job = jobset.JobSpec(cmdline=docker_cmdline,
-                              environ=environ,
-                              shortname='interop_server_%s' % language,
-                              timeout_seconds=test_duration_secs * 3)
-  server_job.container_name = container_name
-  return server_job
-
-
-def build_interop_stress_image_jobspec(language, tag=None):
-  """Creates jobspec for building stress test docker image for a language"""
-  if not tag:
-    tag = 'grpc_interop_stress_%s:%s' % (language.safename, uuid.uuid4())
-  env = {'INTEROP_IMAGE': tag,
-         'BASE_NAME': 'grpc_interop_stress_%s' % language.safename}
-  build_job = jobset.JobSpec(cmdline=['tools/run_tests/dockerize/build_interop_stress_image.sh'],
-                             environ=env,
-                             shortname='build_docker_%s' % (language),
-                             timeout_seconds=30 * 60)
-  build_job.tag = tag
-  return build_job
-
-argp = argparse.ArgumentParser(description='Run stress tests.')
-argp.add_argument('-l',
-                  '--language',
-                  choices=['all'] + sorted(_LANGUAGES),
-                  nargs='+',
-                  default=['all'],
-                  help='Clients to run.')
-argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
-argp.add_argument(
-    '-s',
-    '--server',
-    choices=['all'] + sorted(_SERVERS),
-    action='append',
-    help='Run cloud_to_cloud servers in a separate docker ' + 'image.',
-    default=[])
-argp.add_argument(
-    '--override_server',
-    action='append',
-    type=lambda kv: kv.split('='),
-    help=
-    'Use servername=HOST:PORT to explicitly specify a server. E.g. '
-    'csharp=localhost:50000',
-    default=[])
-argp.add_argument('--test_duration_secs',
-                  help='The duration of the test in seconds',
-                  default=_DEFAULT_TEST_DURATION_SECS)
-
-args = argp.parse_args()
-
-servers = set(
-    s
-    for s in itertools.chain.from_iterable(_SERVERS if x == 'all' else [x]
-                                           for x in args.server))
-
-languages = set(_LANGUAGES[l] for l in itertools.chain.from_iterable(
-  six.iterkeys(_LANGUAGES) if x == 'all' else [x] for x in args.language))
-
-docker_images = {}
-# languages for which to build docker images
-languages_to_build = set(
-    _LANGUAGES[k]
-    for k in set([str(l) for l in languages] + [s for s in servers]))
-build_jobs = []
-for l in languages_to_build:
-  job = build_interop_stress_image_jobspec(l)
-  docker_images[str(l)] = job.tag
-  build_jobs.append(job)
-
-if build_jobs:
-  jobset.message('START', 'Building interop docker images.', do_newline=True)
-  num_failures, _ = jobset.run(build_jobs,
-                               newline_on_success=True,
-                               maxjobs=args.jobs)
-  if num_failures == 0:
-    jobset.message('SUCCESS',
-                   'All docker images built successfully.',
-                   do_newline=True)
-  else:
-    jobset.message('FAILED',
-                   'Failed to build interop docker images.',
-                   do_newline=True)
-    for image in six.itervalues(docker_images):
-      dockerjob.remove_image(image, skip_nonexistent=True)
-    sys.exit(1)
-
-# Start interop servers.
-server_jobs = {}
-server_addresses = {}
-try:
-  for s in servers:
-    lang = str(s)
-    spec = server_jobspec(_LANGUAGES[lang], docker_images.get(lang), args.test_duration_secs)
-    job = dockerjob.DockerJob(spec)
-    server_jobs[lang] = job
-    server_addresses[lang] = ('localhost',
-                              job.mapped_port(_DEFAULT_SERVER_PORT))
-
-  jobs = []
-
-  for server in args.override_server:
-    server_name = server[0]
-    (server_host, server_port) = server[1].split(':')
-    server_addresses[server_name] = (server_host, server_port)
-
-  for server_name, server_address in server_addresses.items():
-    (server_host, server_port) = server_address
-    for language in languages:
-      test_job = cloud_to_cloud_jobspec(
-          language,
-          _DEFAULT_TEST_CASES,
-          ('%s:%s' % (server_host, server_port)),
-          args.test_duration_secs,
-          _DEFAULT_NUM_CHANNELS_PER_SERVER,
-          _DEFAULT_NUM_STUBS_PER_CHANNEL,
-          _DEFAULT_METRICS_PORT,
-          docker_image=docker_images.get(str(language)))
-      jobs.append(test_job)
-
-  if not jobs:
-    print('No jobs to run.')
-    for image in six.itervalues(docker_images):
-      dockerjob.remove_image(image, skip_nonexistent=True)
-    sys.exit(1)
-
-  num_failures, resultset = jobset.run(jobs,
-                                       newline_on_success=True,
-                                       maxjobs=args.jobs)
-  if num_failures:
-    jobset.message('FAILED', 'Some tests failed', do_newline=True)
-  else:
-    jobset.message('SUCCESS', 'All tests passed', do_newline=True)
-
-finally:
-  # Check if servers are still running.
-  for server, job in server_jobs.items():
-    if not job.is_running():
-      print('Server "%s" has exited prematurely.' % server)
-
-  dockerjob.finish_jobs([j for j in six.itervalues(server_jobs)])
-
-  for image in six.itervalues(docker_images):
-    print('Removing docker image %s' % image)
-    dockerjob.remove_image(image)
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 4da2ba4..27e2822 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -60,7 +60,10 @@
 import python_utils.report_utils as report_utils
 import python_utils.watch_dirs as watch_dirs
 import python_utils.start_port_server as start_port_server
-
+try:
+  from python_utils.upload_test_results import upload_results_to_bq
+except (ImportError):
+  pass # It's ok to not import because this is only necessary to upload results to BQ.
 
 _ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
 os.chdir(_ROOT)
@@ -387,10 +390,6 @@
 
     if compiler == 'gcc4.9' or compiler == 'default':
       return ('jessie', [])
-    elif compiler == 'gcc4.4':
-      return ('wheezy', self._gcc_make_options(version_suffix='-4.4'))
-    elif compiler == 'gcc4.6':
-      return ('wheezy', self._gcc_make_options(version_suffix='-4.6'))
     elif compiler == 'gcc4.8':
       return ('jessie', self._gcc_make_options(version_suffix='-4.8'))
     elif compiler == 'gcc5.3':
@@ -430,10 +429,6 @@
     _check_compiler(self.args.compiler, ['default', 'node0.12',
                                          'node4', 'node5', 'node6',
                                          'node7', 'electron1.3', 'electron1.6'])
-    if args.iomgr_platform == "uv":
-      self.use_uv = True
-    else:
-      self.use_uv = False
     if self.args.compiler == 'default':
       self.runtime = 'node'
       self.node_version = '7'
@@ -481,7 +476,6 @@
       else:
         config_flag = '--release'
       return [['tools\\run_tests\\helper_scripts\\build_node.bat',
-               '--grpc_uv={}'.format('true' if self.use_uv else 'false'),
                config_flag]]
     else:
       build_script = 'build_node'
@@ -490,8 +484,7 @@
         # building for electron requires a patch version
         self.node_version += '.0'
       return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script),
-               self.node_version,
-               '--grpc_uv={}'.format('true' if self.use_uv else 'false')]]
+               self.node_version]]
 
   def post_tests_steps(self):
     return []
@@ -1191,7 +1184,7 @@
                   default=False,
                   action='store_const',
                   const=True,
-                  help='Perform all the build steps but dont run any tests.')
+                  help='Perform all the build steps but don\'t run any tests.')
 argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,
                   help='Measure the cpu costs of tests')
 argp.add_argument('--update_submodules', default=[], nargs='*',
@@ -1206,11 +1199,16 @@
                   default=False,
                   action='store_const',
                   const=True,
-                  help='Dont print anything when a test passes. Passing tests also will not be reported in XML report. ' +
+                  help='Don\'t print anything when a test passes. Passing tests also will not be reported in XML report. ' +
                        'Useful when running many iterations of each test (argument -n).')
 argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,
-                  help='Dont try to iterate over many polling strategies when they exist')
+                  help='Don\'t try to iterate over many polling strategies when they exist')
 argp.add_argument('--max_time', default=-1, type=int, help='Maximum test runtime in seconds')
+argp.add_argument('--bq_result_table',
+                  default='',
+                  type=str,
+                  nargs='?',
+                  help='Upload test results to a specified BQ table.')
 args = argp.parse_args()
 
 if args.force_default_poller:
@@ -1509,6 +1507,8 @@
   finally:
     for antagonist in antagonists:
       antagonist.kill()
+    if args.bq_result_table and resultset:
+      upload_results_to_bq(resultset, args.bq_result_table, args, platform_string())
     if xml_report and resultset:
       report_utils.render_junit_xml_report(resultset, xml_report,
                                            suite_name=args.report_suite_name)
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index 02f0ec5..84551d9 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -55,6 +55,16 @@
 _REPORT_SUFFIX = 'sponge_log.xml'
 
 
+def _report_filename(name):
+  """Generates report file name"""
+  return 'report_%s_%s' % (name, _REPORT_SUFFIX)
+
+
+def _report_filename_internal_ci(name):
+  """Generates report file name that leads to better presentation by internal CI"""
+  return '%s/%s' % (name, _REPORT_SUFFIX)
+
+
 def _docker_jobspec(name, runtests_args=[], runtests_envs={},
                     inner_jobs=_DEFAULT_INNER_JOBS):
   """Run a single instance of run_tests.py in a docker container"""
@@ -63,7 +73,7 @@
                    '--use_docker',
                    '-t',
                    '-j', str(inner_jobs),
-                   '-x', 'report_%s_%s' % (name, _REPORT_SUFFIX),
+                   '-x', _report_filename(name),
                    '--report_suite_name', '%s' % name] + runtests_args,
           environ=runtests_envs,
           shortname='run_tests_%s' % name,
@@ -83,7 +93,7 @@
                    'tools/run_tests/helper_scripts/run_tests_in_workspace.sh',
                    '-t',
                    '-j', str(inner_jobs),
-                   '-x', '../report_%s_%s' % (name, _REPORT_SUFFIX),
+                   '-x', '../%s' % _report_filename(name),
                    '--report_suite_name', '%s' % name] + runtests_args,
           environ=env,
           shortname='run_tests_%s' % name,
@@ -159,7 +169,7 @@
 
   # sanitizers
   test_jobs += _generate_jobs(languages=['c'],
-                              configs=['msan', 'asan', 'tsan'],
+                              configs=['msan', 'asan', 'tsan', 'ubsan'],
                               platforms=['linux'],
                               labels=['sanitizers'],
                               extra_args=extra_args,
@@ -187,20 +197,9 @@
                               inner_jobs=inner_jobs)
 
   # portability C and C++ on x64
-  for compiler in ['gcc4.4', 'gcc4.6', 'gcc5.3', 'gcc_musl',
+  for compiler in ['gcc4.8', 'gcc5.3', 'gcc_musl',
                    'clang3.5', 'clang3.6', 'clang3.7']:
-    test_jobs += _generate_jobs(languages=['c'],
-                                configs=['dbg'],
-                                platforms=['linux'],
-                                arch='x64',
-                                compiler=compiler,
-                                labels=['portability'],
-                                extra_args=extra_args,
-                                inner_jobs=inner_jobs)
-
-  for compiler in ['gcc4.8', 'gcc5.3',
-                   'clang3.5', 'clang3.6', 'clang3.7']:
-    test_jobs += _generate_jobs(languages=['c++'],
+    test_jobs += _generate_jobs(languages=['c', 'c++'],
                                 configs=['dbg'],
                                 platforms=['linux'],
                                 arch='x64',
@@ -257,6 +256,15 @@
                               extra_args=extra_args,
                               inner_jobs=inner_jobs)
 
+  test_jobs += _generate_jobs(languages=['python'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              arch='default',
+                              compiler='python_alpine',
+                              labels=['portability'],
+                              extra_args=extra_args,
+                              inner_jobs=inner_jobs)
+
   test_jobs += _generate_jobs(languages=['csharp'],
                               configs=['dbg'],
                               platforms=['linux'],
@@ -279,15 +287,6 @@
                               platforms=['linux'],
                               arch='default',
                               compiler='electron1.6',
-                              iomgr_platform='uv',
-                              labels=['portability'],
-                              extra_args=extra_args,
-                              inner_jobs=inner_jobs)
-
-  test_jobs += _generate_jobs(languages=['node'],
-                              configs=['dbg'],
-                              platforms=['linux'],
-                              iomgr_platform='uv',
                               labels=['portability'],
                               extra_args=extra_args,
                               inner_jobs=inner_jobs)
@@ -380,8 +379,22 @@
   argp.add_argument('--max_time', default=-1, type=int,
                     help='Maximum amount of time to run tests for' +
                          '(other tests will be skipped)')
+  argp.add_argument('--internal_ci',
+                    default=False,
+                    action='store_const',
+                    const=True,
+                    help='Put reports into subdirectories to improve presentation of '
+                    'results by Internal CI.')
+  argp.add_argument('--bq_result_table',
+                    default='',
+                    type=str,
+                    nargs='?',
+                    help='Upload test results to a specified BQ table.')
   args = argp.parse_args()
 
+  if args.internal_ci:
+    _report_filename = _report_filename_internal_ci  # override the function
+
   extra_args = []
   if args.build_only:
     extra_args.append('--build_only')
@@ -393,6 +406,10 @@
     extra_args.append('--quiet_success')
   if args.max_time > 0:
     extra_args.extend(('--max_time', '%d' % args.max_time))
+  if args.bq_result_table:
+    extra_args.append('--bq_result_table')
+    extra_args.append('%s' % args.bq_result_table)
+    extra_args.append('--measure_cpu_costs')
 
   all_jobs = _create_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs) + \
              _create_portability_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs)
@@ -450,7 +467,7 @@
     ignored_num_skipped_failures, skipped_results = jobset.run(
         skipped_jobs, skip_jobs=True)
     resultset.update(skipped_results)
-  report_utils.render_junit_xml_report(resultset, 'report_%s' % _REPORT_SUFFIX,
+  report_utils.render_junit_xml_report(resultset, _report_filename('aggregate_tests'),
                                        suite_name='aggregate_tests')
 
   if num_failures == 0:
diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh
index 0a9c1cc..43d0521 100755
--- a/tools/run_tests/sanity/check_submodules.sh
+++ b/tools/run_tests/sanity/check_submodules.sh
@@ -46,8 +46,7 @@
  886e7d75368e3f4fab3f4d0d3584e4abfc557755 third_party/boringssl-with-bazel (version_for_cocoapods_7.0-857-g886e7d7)
  30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e third_party/gflags (v2.2.0)
  ec44c6c1675c25b9827aacd08c02433cccde7780 third_party/googletest (release-1.8.0)
- 593e917c176b5bc5aafa57bf9f6030d749d91cd5 third_party/protobuf (v3.1.0-alpha-1-326-g593e917)
- bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c third_party/thrift (bcad917)
+ a6189acd18b00611c1dc7042299ad75486f08a1a third_party/protobuf (v3.3.0)
  cacf7f1d4e3d44d871b605da3b647f07d718623f third_party/zlib (v1.2.11)
  7691f773af79bf75a62d1863fd0f13ebf9dc51b1 third_party/cares/cares (1.12.0)
 EOF
diff --git a/tools/run_tests/start_port_server.py b/tools/run_tests/start_port_server.py
index bfd7222..f7c9f43 100755
--- a/tools/run_tests/start_port_server.py
+++ b/tools/run_tests/start_port_server.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7
 
 # Copyright 2017, Google Inc.
 # All rights reserved.
@@ -39,10 +39,8 @@
 an error message to users.
 """
 
-from __future__ import print_function
-
 import python_utils.start_port_server as start_port_server
 
 start_port_server.start_port_server()
 
-print("Port server started successfully")
+print "Port server started successfully"
diff --git a/tools/run_tests/stress_test/README.md b/tools/run_tests/stress_test/README.md
deleted file mode 100644
index cc82b87..0000000
--- a/tools/run_tests/stress_test/README.md
+++ /dev/null
@@ -1,76 +0,0 @@
-Running Stress tests on Google Container Engine

-=======================================

-

-### **Glossary**:

-* GCP: Google Cloud Platform

-* GCE: Google Compute Engine

-* GKE: Google Container Engine

-* GCP console: https://console.cloud.google.com

-

-### **Setup Instructions**

-#### *On GCP:*

-1. Login to GCP with your Google account (for example, your @gmail account) at https://cloud.google.com. If do not have a Google account, you will have to create an account first.

-2. Enable billing on Google cloud platform. Instructions [here](https://cloud.google.com/container-engine/docs/before-you-begin)  (see the '*Enable billing*' section).

-3. Create a Project from the [GCP console](https://console.cloud.google.com).i.e Click on the project dropdown box on the top right (to the right of the search box) and click '*Create a project*' option.

-4. Enable the Container Engine API. Instructions [here](https://cloud.google.com/container-engine/docs/before-you-begin)  (See the '*Enable the Container Engine API*’ section). Alternatively, you can do the following:

-    - Click on the '*Products & Services*' icon on the top left (i.e the icon with three small horizontal bars) and select '*API Manager*'

-    - Select the '*Container Engine API*' under '*Google Cloud APIs*' on the main page. Note that you might have to click on '*More*' under '*Google Cloud APIs*' to see the '*Container Engine API*' link

-    - Click on the '*Enable*' button. If the API is already enabled, the button's label would be '*Disable*' instead (do NOT click the button if its label is '*Disable*')

-5. Create a Cluster from the GCP console.

-    - Go to the Container Engine section from GCP console i.e: Click on the '*Products & Services*' icon on the top left (i.e the icon with three small horizontal bars) and click on '*Container Engine*'

-    - Click '*Create Container Cluster*' and follow the instructions.

-    - The instructions for 'Name/Zone/MachineType' etc are [here](https://cloud.google.com/container-engine/docs/clusters/operations) (**NOTE**: The page also has instructions to setting up default clusters and configuring `kubectl`. We will be doing that later)

-    - For the cluster size, a smaller size of < 10 GCE instances is good enough for our use cases - assuming that we are planning to run a reasonably small number of stress client instances. For the machine type, something like '2 vCPUs 7.5 GB' (available in the drop down box) should be good enough.

-    - **IMPORTANT**: Before hitting the '*Create*' button, click on '*More*' link just above the '*Create*' button and Select '*Enabled*' for BigQuery , '*Enabled*' for Cloud Platform and '*Read/Write*' for Cloud User Accounts.

-    - Create the cluster by clicking '*Create*' button.

-

-#### *On your machine* (or the machine from which stress tests on GKE are launched):

-1. You need a working gRPC repository on your machine. If you do not have it, clone the grpc repository from github (https://github.com/grpc/grpc) and follow the instructions [here](https://github.com/grpc/grpc/blob/master/INSTALL.md)

-2. Install Docker. Instructions [here](https://docs.docker.com/engine/installation/)

-3. Install Google Cloud SDK. Instructions [here](https://cloud.google.com/sdk/). This installs the `gcloud` tool

-4. Install `kubectl`, Kubernetes command line tool using `gcloud`. i.e

-    - `$ gcloud components update kubectl`

-    - NOTE: If you are running this from a GCE instance, the command may fail with the following error:

-    ```

-     You cannot perform this action because this Cloud SDK installation is 

-     managed by an external package manager. If you would like to get the

-     latest version, please see our main download page at:

-

-     https://developers.google.com/cloud/sdk/

-

-     ERROR: (gcloud.components.update) The component manager is disabled for this installation

-    ```

-    -- If so, you will have to manually install Cloud SDK by doing the following

-    ```shell

-      $ # The following installs latest Cloud SDK and updates the PATH

-      $ # (Accept the default values when prompted)

-      $ curl https://sdk.cloud.google.com | bash

-      $ exec -l $SHELL

-      $ # Set the defaults. Pick the default GCE credentials when prompted (The service account

-      $ # name will have a name similar to: "xxx-compute@developer.gserviceaccount.com")

-      $ gcloud init

-    ``` 

-

-5. Install Google python client apis:

-    - `‘$ sudo pip install --upgrade google-api-python-client’`

-    -  **Note**: Do `$ sudo apt-get install python-pip` (or `$ easy_install -U pip`) if you do not have pip

-6. Install the `requests` Python package if you don’t have it already by doing `sudo pip install requests`. More details regarding `requests` package are [here](http://docs.python-requests.org/en/master/user/install/)

-7. Set the `gcloud` defaults: See the instructions [here](https://cloud.google.com/container-engine/docs/before-you-begin) under "*Set gcloud defaults*" section)

-    - Make sure you also fetch the cluster credentials for `kubectl` command to use. I.e `$ gcloud container clusters get-credentials CLUSTER_NAME`

-

-### **Launching Stress tests**

-

-The stress tests are launched by the following script (path is relative to GRPC root directory) :

-`tools/run_tests/stress_test/run_stress_tests_on_gke.py`

-

-You can find out more details by using the `--help` flag.

-  - `<grpc_root_dir>$ tools/run_tests/stress_test/run_on_gke.py --help`

-

-> **Example**

-> ```bash

-> $ # Change to the grpc root directory

-> $ cd $GRPC_ROOT

-> $ tools/run_tests/stress_test/run_on_gke.py --project_id=sree-gce --config_file=tools/run_tests/stress_test/configs/opt.json

-> ```

-

-> The above runs the stress test on GKE under the project `sree-gce` in the default cluster (that you set by `gcloud` command earlier). The test settings (like number of client instances, servers, the parmeters to pass, test cases etc) are all loaded from the config file `$GRPC_ROOT/tools/run_tests/stress_test/opt.json`

diff --git a/tools/run_tests/stress_test/STRESS_CLIENT_SPEC.md b/tools/run_tests/stress_test/STRESS_CLIENT_SPEC.md
deleted file mode 100644
index 9f079be..0000000
--- a/tools/run_tests/stress_test/STRESS_CLIENT_SPEC.md
+++ /dev/null
@@ -1,25 +0,0 @@
-Stress Test client Specification

-=========================

-This document specifies the features a stress test client should implement in order to work with the stress testing framework. The stress test clients are executed against the existing interop test servers.

-

-**Requirements**

---------------

-**1.** A stress test client should be able to repeatedly execute one or more of the existing 'interop test cases'. It may just be a wrapper around the existing interop test client. The exact command line arguments the client should support are listed in _Table 1_ below.

-

-**2.** The stress test client must implement a metrics server defined by _[metrics.proto](https://github.com/grpc/grpc/blob/master/src/proto/grpc/testing/metrics.proto)_ and must expose _qps_ as a `Long`-valued Gauge. The client can track the overall _qps_ in one Gauge or in multiple Gauges (for example: One per Channel or Stub).

- The framework periodically queries the _qps_ by calling the `GetAllGauges()` method (the framework assumes that all the returned Gauges are _qps_ Gauges and adds them up to determine the final qps) and uses this to determine if the stress test client is running or crashed or stalled.

-> *Note:* In this context, the term  _**qps**_  means _interop test cases per second_  (not _messages per second_ or _rpc calls per second_)

-

-

-**Table 1:** Command line arguments that should be supported by the stress test client.

-

->_**Note** The current C++ [stress client](https://github.com/grpc/grpc/blob/master/test/cpp/interop/stress_test.cc) supports more flags than those listed here but those flags will soon be deprecated._

-

-Parameter             |                    Description

-----------------------|---------------------------------

-`--server_addresses`    | The stress client should accept a list of server addresses in the following format:<br> ```<name_1>:<port_1>,<name_2>:<port_2>..<name_N>:<port_N>``` <br> _Note:_ `<name>` can be either server name or IP address.<br><br>_Type:_ string <br>_default:_ ```localhost:8080``` <br>_Example:_ ``foo.foobar.com:8080,bar.foobar.com:8080`` <br><br> Currently, the stress test framework only passes one server address to the client.

-`--test_cases`        |   List of test cases along with the relative weights in the following format:<br> `<testcase_1:w_1>,<testcase_2:w_2>...<testcase_n:w_n>`. <br> The test cases names are the same as those currently used by the interop clients<br><br>_Type:_ string <br>_Example:_ `empty_unary:20,large_unary:10,empty_stream:70` <br>(The stress client would then make `empty_unary` calls 20% of the time, `large_unary` calls 10% of the time and `empty_stream` calls 70% of the time.) <br>_Note:_ The weights need not add up to 100.

-`--test_duration_secs`      | The test duration in seconds. A value of -1 means that the test should run forever until forcefully terminated. <br>_Type:_ int <br>_default:_ -1

-`--num_channels_per_server` | Number of channels (i.e connections) to each server. <br> _Type:_ int <br> _default:_ 1 <br><br> _Note:_ Unfortunately, the term `channel` is used differently in `grpc-java` and `C based grpc`. In this context, this really means "number of connections to the server"

-`--num_stubs_per_channel `  | Number of client stubs per each connection to server.<br>_Type:_ int <br>_default:_ 1

-`--metrics_port`            | The port at which the stress client exposes [QPS metrics](https://github.com/grpc/grpc/blob/master/src/proto/grpc/testing/metrics.proto). <br>_Type:_ int <br>_default:_ 8081

diff --git a/tools/run_tests/stress_test/configs/asan.json b/tools/run_tests/stress_test/configs/asan.json
deleted file mode 100644
index 7ae11cc..0000000
--- a/tools/run_tests/stress_test/configs/asan.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_cxx_asan" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "asan"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 120,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "cxx_client_asan": {
-        "baseTemplate": "default",
-        "stressClientCmd": ["/var/local/git/grpc/bins/asan/stress_test"],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/asan/metrics_client"]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "cxx_server_asan": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/asan/interop_server"]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-asan": {
-        "serverTemplate": "cxx_server_asan",
-        "dockerImage": "grpc_stress_cxx_asan",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "stress-client-asan": {
-        "clientTemplate": "cxx_client_asan",
-        "dockerImage": "grpc_stress_cxx_asan",
-        "numInstances": 5,
-        "serverPodSpec": "stress-server-asan"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8003,
-    "datasetIdNamePrefix": "stress_test_asan",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/csharp.json b/tools/run_tests/stress_test/configs/csharp.json
deleted file mode 100644
index c438e08..0000000
--- a/tools/run_tests/stress_test/configs/csharp.json
+++ /dev/null
@@ -1,91 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_csharp" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_csharp"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 100,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true",
-          "deadline_secs": 60
-        }
-      }
-    },
-    "templates": {
-      "csharp_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "mono",
-          "/var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.StressClient/bin/Debug/Grpc.IntegrationTesting.StressClient.exe"
-		],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/opt/metrics_client"]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "csharp_server": {
-        "baseTemplate": "default",
-        "stressServerCmd": [
-          "mono",
-          "/var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Server/bin/Debug/Grpc.IntegrationTesting.Server.exe"
-		]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-csharp": {
-        "serverTemplate": "csharp_server",
-        "dockerImage": "grpc_stress_csharp",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "stress-client-csharp": {
-        "clientTemplate": "csharp_client",
-        "dockerImage": "grpc_stress_csharp",
-        "numInstances": 10,
-        "serverPodSpec": "stress-server-csharp"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 100,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8009,
-    "datasetIdNamePrefix": "stress_test_csharp",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/go.json b/tools/run_tests/stress_test/configs/go.json
deleted file mode 100644
index f1b2b52..0000000
--- a/tools/run_tests/stress_test/configs/go.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_go" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_go"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "go_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "go",
-          "run",
-          "/go/src/google.golang.org/grpc/stress/client/main.go"
-        ],
-        "metricsClientCmd": [
-          "go",
-          "run",
-          "/go/src/google.golang.org/grpc/stress/metrics_client/main.go"
-        ]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "go_server": {
-        "baseTemplate": "default",
-        "stressServerCmd": [
-          "go",
-          "run",
-          "/go/src/google.golang.org/grpc/interop/server/server.go"
-        ]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "go-stress-server": {
-        "serverTemplate": "go_server",
-        "dockerImage": "grpc_stress_go",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "go-stress-client": {
-        "clientTemplate": "go_client",
-        "dockerImage": "grpc_stress_go",
-        "numInstances": 15,
-        "serverPodSpec": "go-stress-server"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8007,
-    "datasetIdNamePrefix": "stress_test_go",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/java.json b/tools/run_tests/stress_test/configs/java.json
deleted file mode 100644
index 92af63c..0000000
--- a/tools/run_tests/stress_test/configs/java.json
+++ /dev/null
@@ -1,98 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_java" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_java"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 100,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true",
-          "deadline_secs": 60
-        },
-        "env": {
-          "STRESSTEST_CLIENT_OPTS":"-Xmx3g -Xms3g -XX:NewSize=1500m -XX:MaxNewSize=1500m -XX:+UseConcMarkSweepGC"
-        }
-      }
-    },
-    "templates": {
-      "java_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "/var/local/git/grpc-java/interop-testing/build/install/grpc-interop-testing/bin/stresstest-client"
-        ],
-        "metricsClientCmd": [
-          "/var/local/git/grpc/bins/opt/metrics_client"
-        ]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080,
-          "use_tls": "false"
-        },
-        "env": {
-          "TEST_SERVER_OPTS":"-Xmx3g -Xms3g -XX:NewSize=1500m -XX:MaxNewSize=1500m  -XX:+UseConcMarkSweepGC"
-        }
-      }
-    },
-    "templates": {
-      "java_server": {
-        "baseTemplate": "default",
-        "stressServerCmd": [
-          "/var/local/git/grpc-java/interop-testing/build/install/grpc-interop-testing/bin/test-server"
-        ]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "java-stress-server": {
-        "serverTemplate": "java_server",
-        "dockerImage": "grpc_stress_java",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "java-stress-client": {
-        "clientTemplate": "java_client",
-        "dockerImage": "grpc_stress_java",
-        "numInstances": 10,
-        "serverPodSpec": "java-stress-server"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 100,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8008,
-    "datasetIdNamePrefix": "stress_test_java",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/node-cxx.json b/tools/run_tests/stress_test/configs/node-cxx.json
deleted file mode 100644
index 094c123..0000000
--- a/tools/run_tests/stress_test/configs/node-cxx.json
+++ /dev/null
@@ -1,97 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_cxx_opt" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "opt"
-    },
-   "grpc_stress_node": {
-     "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-     "dockerFileDir": "grpc_interop_stress_node"
-   }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "node_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "/var/local/git/grpc/tools/gcp/stress_test/run_node.sh",
-          "node",
-          "/var/local/git/grpc/src/node/stress/stress_client.js"
-        ],
-        "metricsClientCmd": [
-          "/var/local/git/grpc/tools/gcp/stress_test/run_node.sh",
-          "node",
-          "/var/local/git/grpc/src/node/stress/metrics_client.js"
-        ]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "cxx_server_opt": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/opt/interop_server"]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-cxx-opt": {
-        "serverTemplate": "cxx_server_opt",
-        "dockerImage": "grpc_stress_cxx_opt",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "stress-client-node": {
-        "clientTemplate": "node_client",
-        "dockerImage": "grpc_stress_node",
-        "numInstances": 20,
-        "serverPodSpec": "stress-server-cxx-opt"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8006,
-    "datasetIdNamePrefix": "stress_test_node_cxx_opt",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/node.json b/tools/run_tests/stress_test/configs/node.json
deleted file mode 100644
index 85eb9e0..0000000
--- a/tools/run_tests/stress_test/configs/node.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_node" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_node"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "node_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "/var/local/git/grpc/tools/gcp/stress_test/run_node.sh",
-          "node",
-          "/var/local/git/grpc/src/node/stress/stress_client.js"
-        ],
-        "metricsClientCmd": [
-          "/var/local/git/grpc/tools/gcp/stress_test/run_node.sh",
-          "node",
-          "/var/local/git/grpc/src/node/stress/metrics_client.js"
-        ]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "node_server": {
-        "baseTemplate": "default",
-        "stressServerCmd": [
-          "/var/local/git/grpc/tools/gcp/stress_test/run_node.sh",
-          "node",
-          "/var/local/git/grpc/src/node/interop/interop_server.js"
-        ]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "node-stress-server": {
-        "serverTemplate": "node_server",
-        "dockerImage": "grpc_stress_node",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "node-stress-client": {
-        "clientTemplate": "node_client",
-        "dockerImage": "grpc_stress_node",
-        "numInstances": 15,
-        "serverPodSpec": "node-stress-server"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8005,
-    "datasetIdNamePrefix": "stress_test_node",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/opt-tsan-asan.json b/tools/run_tests/stress_test/configs/opt-tsan-asan.json
deleted file mode 100644
index fcb3678..0000000
--- a/tools/run_tests/stress_test/configs/opt-tsan-asan.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_cxx_opt" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "opt"
-    },
-    "grpc_stress_cxx_tsan": {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "tsan"
-    },
-    "grpc_stress_cxx_asan": {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "asan"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "cxx_client_opt": {
-        "baseTemplate": "default",
-        "stressClientCmd": ["/var/local/git/grpc/bins/opt/stress_test"],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/opt/metrics_client"]
-      },
-      "cxx_client_tsan": {
-        "baseTemplate": "default",
-        "stressClientCmd": ["/var/local/git/grpc/bins/tsan/stress_test"],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/tsan/metrics_client"]
-      },
-    "cxx_client_asan": {
-        "baseTemplate": "default",
-        "stressClientCmd": ["/var/local/git/grpc/bins/asan/stress_test"],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/asan/metrics_client"]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "cxx_server_opt": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/opt/interop_server"]
-      },
-      "cxx_server_tsan": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/tsan/interop_server"]
-      },
-    "cxx_server_asan": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/asan/interop_server"]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-opt": {
-        "serverTemplate": "cxx_server_opt",
-        "dockerImage": "grpc_stress_cxx_opt",
-        "numInstances": 1
-      },
-      "stress-server-tsan": {
-        "serverTemplate": "cxx_server_tsan",
-        "dockerImage": "grpc_stress_cxx_tsan",
-        "numInstances": 1
-      },
-      "stress-server-asan": {
-        "serverTemplate": "cxx_server_asan",
-        "dockerImage": "grpc_stress_cxx_asan",
-        "numInstances": 1
-      }
-   },
-
-    "clientPodSpecs": {
-      "stress-client-opt": {
-        "clientTemplate": "cxx_client_opt",
-        "dockerImage": "grpc_stress_cxx_opt",
-        "numInstances": 5,
-        "serverPodSpec": "stress-server-opt"
-      },
-      "stress-client-tsan": {
-        "clientTemplate": "cxx_client_tsan",
-        "dockerImage": "grpc_stress_cxx_tsan",
-        "numInstances": 10,
-        "serverPodSpec": "stress-server-tsan"
-      },
-      "stress-client-asan": {
-        "clientTemplate": "cxx_client_asan",
-        "dockerImage": "grpc_stress_cxx_asan",
-        "numInstances": 10,
-        "serverPodSpec": "stress-server-asan"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8004,
-    "datasetIdNamePrefix": "stress_test_opt_tsan",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
diff --git a/tools/run_tests/stress_test/configs/opt.json b/tools/run_tests/stress_test/configs/opt.json
deleted file mode 100644
index 5e0e930..0000000
--- a/tools/run_tests/stress_test/configs/opt.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_cxx_opt" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "opt"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "cxx_client_opt": {
-        "baseTemplate": "default",
-        "stressClientCmd": ["/var/local/git/grpc/bins/opt/stress_test"],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/opt/metrics_client"]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "cxx_server_opt": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/opt/interop_server"]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-opt": {
-        "serverTemplate": "cxx_server_opt",
-        "dockerImage": "grpc_stress_cxx_opt",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "stress-client-opt": {
-        "clientTemplate": "cxx_client_opt",
-        "dockerImage": "grpc_stress_cxx_opt",
-        "numInstances": 15,
-        "serverPodSpec": "stress-server-opt"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8001,
-    "datasetIdNamePrefix": "stress_test_opt",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/php-cxx.json b/tools/run_tests/stress_test/configs/php-cxx.json
deleted file mode 100644
index 03254b3..0000000
--- a/tools/run_tests/stress_test/configs/php-cxx.json
+++ /dev/null
@@ -1,93 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_cxx_opt" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "opt"
-    },
-   "grpc_stress_php": {
-     "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-     "dockerFileDir": "grpc_interop_stress_php"
-   }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081"
-        }
-      }
-    },
-    "templates": {
-      "php_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "/var/local/git/grpc/src/php/bin/stress_client.sh"
-        ],
-        "metricsClientCmd": [
-          "php",
-          "/var/local/git/grpc/src/php/tests/interop/metrics_client.php"
-        ]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "cxx_server_opt": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/opt/interop_server"]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-cxx-php": {
-        "serverTemplate": "cxx_server_opt",
-        "dockerImage": "grpc_stress_cxx_opt",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "stress-client-php": {
-        "clientTemplate": "php_client",
-        "dockerImage": "grpc_stress_php",
-        "numInstances": 20,
-        "serverPodSpec": "stress-server-cxx-php"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8010,
-    "datasetIdNamePrefix": "stress_test_php_cxx_opt",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/python.json b/tools/run_tests/stress_test/configs/python.json
deleted file mode 100644
index 4f85de1..0000000
--- a/tools/run_tests/stress_test/configs/python.json
+++ /dev/null
@@ -1,98 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_python" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_python"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        },
-		"env": {
-          "PYTHONPATH": "/var/local/git/grpc/src/python/gens:/var/local/git/grpc/src/python/grpcio",
-          "LD_LIBRARY_PATH":"/var/local/git/grpc/libs/opt"
-        }
-      }
-    },
-    "templates": {
-      "python_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "python",
-          "/var/local/git/grpc/src/python/grpcio/tests/stress/client.py"
-        ],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/opt/metrics_client"]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        },
-        "env": {
-          "PYTHONPATH": "/var/local/git/grpc/src/python/gens:/var/local/git/grpc/src/python/grpcio",
-          "LD_LIBRARY_PATH":"/var/local/git/grpc/libs/opt"
-        }
-      }
-    },
-    "templates": {
-      "python_server": {
-        "baseTemplate": "default",
-        "stressServerCmd": [
-          "python",
-          "/var/local/git/grpc/src/python/grpcio/tests/interop/server.py"
-        ]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "python-stress-server": {
-        "serverTemplate": "python_server",
-        "dockerImage": "grpc_stress_python",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "python-stress-client": {
-        "clientTemplate": "python_client",
-        "dockerImage": "grpc_stress_python",
-        "numInstances": 5,
-        "serverPodSpec": "python-stress-server"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8011,
-    "datasetIdNamePrefix": "stress_test_python",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/ruby.json b/tools/run_tests/stress_test/configs/ruby.json
deleted file mode 100644
index 7e2afcb..0000000
--- a/tools/run_tests/stress_test/configs/ruby.json
+++ /dev/null
@@ -1,92 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_ruby" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_ruby"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 60,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "ruby_client": {
-        "baseTemplate": "default",
-        "stressClientCmd": [
-          "/var/local/git/grpc/tools/gcp/stress_test/run_ruby.sh",
-          "ruby",
-          "/var/local/git/grpc/src/ruby/stress/stress_client.rb"
-        ],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/opt/metrics_client"]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "ruby_server": {
-        "baseTemplate": "default",
-        "stressServerCmd": [
-          "/var/local/git/grpc/tools/gcp/stress_test/run_ruby.sh",
-          "ruby",
-          "/var/local/git/grpc/src/ruby/pb/test/server.rb"
-        ]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-ruby": {
-        "serverTemplate": "ruby_server",
-        "dockerImage": "grpc_stress_ruby",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "stress-client-ruby": {
-        "clientTemplate": "ruby_client",
-        "dockerImage": "grpc_stress_ruby",
-        "numInstances": 10,
-        "serverPodSpec": "stress-server-ruby"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8001,
-    "datasetIdNamePrefix": "stress_test_ruby",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/configs/tsan.json b/tools/run_tests/stress_test/configs/tsan.json
deleted file mode 100644
index abc759c..0000000
--- a/tools/run_tests/stress_test/configs/tsan.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  "dockerImages": {
-    "grpc_stress_cxx_tsan" : {
-      "buildScript": "tools/run_tests/dockerize/build_interop_stress_image.sh",
-      "dockerFileDir": "grpc_interop_stress_cxx",
-      "buildType": "tsan"
-    }
-  },
-
-  "clientTemplates": {
-    "baseTemplates": {
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_client.py",
-        "pollIntervalSecs": 120,
-        "clientArgs": {
-          "num_channels_per_server":5,
-          "num_stubs_per_channel":10,
-          "test_cases": "empty_unary:1,large_unary:1,client_streaming:1,server_streaming:1,empty_stream:1",
-          "metrics_port": 8081
-        },
-        "metricsPort": 8081,
-        "metricsArgs": {
-          "metrics_server_address": "localhost:8081",
-          "total_only": "true"
-        }
-      }
-    },
-    "templates": {
-      "cxx_client_tsan": {
-        "baseTemplate": "default",
-        "stressClientCmd": ["/var/local/git/grpc/bins/tsan/stress_test"],
-        "metricsClientCmd": ["/var/local/git/grpc/bins/tsan/metrics_client"]
-      }
-    }
-  },
-
-  "serverTemplates": {
-    "baseTemplates":{
-      "default": {
-        "wrapperScriptPath": "/var/local/git/grpc/tools/gcp/stress_test/run_server.py",
-        "serverPort": 8080,
-        "serverArgs": {
-          "port": 8080
-        }
-      }
-    },
-    "templates": {
-      "cxx_server_tsan": {
-        "baseTemplate": "default",
-        "stressServerCmd": ["/var/local/git/grpc/bins/tsan/interop_server"]
-      }
-    }
-  },
-
-  "testMatrix": {
-    "serverPodSpecs": {
-      "stress-server-tsan": {
-        "serverTemplate": "cxx_server_tsan",
-        "dockerImage": "grpc_stress_cxx_tsan",
-        "numInstances": 1
-      }
-    },
-
-    "clientPodSpecs": {
-      "stress-client-tsan": {
-        "clientTemplate": "cxx_client_tsan",
-        "dockerImage": "grpc_stress_cxx_tsan",
-        "numInstances": 5,
-        "serverPodSpec": "stress-server-tsan"
-      }
-    }
-  },
-
-  "globalSettings": {
-    "buildDockerImages": true,
-    "pollIntervalSecs": 60,
-    "testDurationSecs": 7200,
-    "kubernetesProxyPort": 8002,
-    "datasetIdNamePrefix": "stress_test_tsan",
-    "summaryTableId": "summary",
-    "qpsTableId": "qps",
-    "podWarmupSecs": 60
-  }
-}
-
diff --git a/tools/run_tests/stress_test/print_summary.py b/tools/run_tests/stress_test/print_summary.py
deleted file mode 100755
index 6f4ada2..0000000
--- a/tools/run_tests/stress_test/print_summary.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import argparse
-import os
-import sys
-
-stress_test_utils_dir = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '../../gcp/stress_test'))
-sys.path.append(stress_test_utils_dir)
-from stress_test_utils import BigQueryHelper
-
-argp = argparse.ArgumentParser(
-    description='Print summary tables',
-    formatter_class=argparse.ArgumentDefaultsHelpFormatter)
-argp.add_argument('--gcp_project_id',
-                  required=True,
-                  help='The Google Cloud Platform Project Id')
-argp.add_argument('--dataset_id', type=str, required=True)
-argp.add_argument('--run_id', type=str, required=True)
-argp.add_argument('--summary_table_id', type=str, default='summary')
-argp.add_argument('--qps_table_id', type=str, default='qps')
-argp.add_argument('--summary_only', action='store_true', default=True)
-
-if __name__ == '__main__':
-  args = argp.parse_args()
-  bq_helper = BigQueryHelper(args.run_id, '', '', args.gcp_project_id,
-                             args.dataset_id, args.summary_table_id,
-                             args.qps_table_id)
-  bq_helper.initialize()
-  if not args.summary_only:
-    bq_helper.print_qps_records()
-  bq_helper.print_summary_records()
diff --git a/tools/run_tests/stress_test/run_on_gke.py b/tools/run_tests/stress_test/run_on_gke.py
deleted file mode 100755
index b190ebd..0000000
--- a/tools/run_tests/stress_test/run_on_gke.py
+++ /dev/null
@@ -1,674 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015-2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import print_function
-
-import argparse
-import datetime
-import json
-import os
-import subprocess
-import sys
-import time
-
-stress_test_utils_dir = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '../../gcp/stress_test'))
-sys.path.append(stress_test_utils_dir)
-from stress_test_utils import BigQueryHelper
-
-kubernetes_api_dir = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '../../gcp/utils'))
-sys.path.append(kubernetes_api_dir)
-
-import kubernetes_api
-
-
-class GlobalSettings:
-
-  def __init__(self, gcp_project_id, build_docker_images,
-               test_poll_interval_secs, test_duration_secs,
-               kubernetes_proxy_port, dataset_id_prefix, summary_table_id,
-               qps_table_id, pod_warmup_secs):
-    self.gcp_project_id = gcp_project_id
-    self.build_docker_images = build_docker_images
-    self.test_poll_interval_secs = test_poll_interval_secs
-    self.test_duration_secs = test_duration_secs
-    self.kubernetes_proxy_port = kubernetes_proxy_port
-    self.dataset_id_prefix = dataset_id_prefix
-    self.summary_table_id = summary_table_id
-    self.qps_table_id = qps_table_id
-    self.pod_warmup_secs = pod_warmup_secs
-
-
-class ClientTemplate:
-  """ Contains all the common settings that are used by a stress client """
-
-  def __init__(self, name, stress_client_cmd, metrics_client_cmd, metrics_port,
-               wrapper_script_path, poll_interval_secs, client_args_dict,
-               metrics_args_dict, will_run_forever, env_dict):
-    self.name = name
-    self.stress_client_cmd = stress_client_cmd
-    self.metrics_client_cmd = metrics_client_cmd
-    self.metrics_port = metrics_port
-    self.wrapper_script_path = wrapper_script_path
-    self.poll_interval_secs = poll_interval_secs
-    self.client_args_dict = client_args_dict
-    self.metrics_args_dict = metrics_args_dict
-    self.will_run_forever = will_run_forever
-    self.env_dict = env_dict
-
-
-class ServerTemplate:
-  """ Contains all the common settings used by a stress server """
-
-  def __init__(self, name, server_cmd, wrapper_script_path, server_port,
-               server_args_dict, will_run_forever, env_dict):
-    self.name = name
-    self.server_cmd = server_cmd
-    self.wrapper_script_path = wrapper_script_path
-    self.server_port = server_port
-    self.server_args_dict = server_args_dict
-    self.will_run_forever = will_run_forever
-    self.env_dict = env_dict
-
-
-class DockerImage:
-  """ Represents properties of a Docker image. Provides methods to build the
-  image and push it to GKE registry
-  """
-
-  def __init__(self, gcp_project_id, image_name, build_script_path,
-               dockerfile_dir, build_type):
-    """Args:
-
-      image_name: The docker image name
-      tag_name: The additional tag name. This is the name used when pushing the
-        docker image to GKE registry
-      build_script_path: The path to the build script that builds this docker
-      image
-      dockerfile_dir: The name of the directory under
-      '<grpc_root>/tools/dockerfile' that contains the dockerfile
-    """
-    self.image_name = image_name
-    self.gcp_project_id = gcp_project_id
-    self.build_script_path = build_script_path
-    self.dockerfile_dir = dockerfile_dir
-    self.build_type = build_type
-    self.tag_name = self._make_tag_name(gcp_project_id, image_name)
-
-  def _make_tag_name(self, project_id, image_name):
-    return 'gcr.io/%s/%s' % (project_id, image_name)
-
-  def build_image(self):
-    print('Building docker image: %s (tag: %s)' % (self.image_name,
-                                                   self.tag_name))
-    os.environ['INTEROP_IMAGE'] = self.image_name
-    os.environ['INTEROP_IMAGE_REPOSITORY_TAG'] = self.tag_name
-    os.environ['BASE_NAME'] = self.dockerfile_dir
-    os.environ['BUILD_TYPE'] = self.build_type
-    print('DEBUG: path: ', self.build_script_path)
-    if subprocess.call(args=[self.build_script_path]) != 0:
-      print('Error in building the Docker image')
-      return False
-    return True
-
-  def push_to_gke_registry(self):
-    cmd = ['gcloud', 'docker', 'push', self.tag_name]
-    print('Pushing %s to the GKE registry..' % self.tag_name)
-    if subprocess.call(args=cmd) != 0:
-      print('Error in pushing the image %s to the GKE registry' %
-            self.tag_name)
-      return False
-    return True
-
-
-class ServerPodSpec:
-  """ Contains the information required to launch server pods. """
-
-  def __init__(self, name, server_template, docker_image, num_instances):
-    self.name = name
-    self.template = server_template
-    self.docker_image = docker_image
-    self.num_instances = num_instances
-
-  def pod_names(self):
-    """ Return a list of names of server pods to create. """
-    return ['%s-%d' % (self.name, i) for i in range(1, self.num_instances + 1)]
-
-  def server_addresses(self):
-    """ Return string of server addresses in the following format:
-      '<server_pod_name_1>:<server_port>,<server_pod_name_2>:<server_port>...'
-    """
-    return ','.join(['%s:%d' % (pod_name, self.template.server_port)
-                     for pod_name in self.pod_names()])
-
-
-class ClientPodSpec:
-  """ Contains the information required to launch client pods """
-
-  def __init__(self, name, client_template, docker_image, num_instances,
-               server_addresses):
-    self.name = name
-    self.template = client_template
-    self.docker_image = docker_image
-    self.num_instances = num_instances
-    self.server_addresses = server_addresses
-
-  def pod_names(self):
-    """ Return a list of names of client pods to create """
-    return ['%s-%d' % (self.name, i) for i in range(1, self.num_instances + 1)]
-
-  # The client args in the template do not have server addresses. This function
-  # adds the server addresses and returns the updated client args
-  def get_client_args_dict(self):
-    args_dict = self.template.client_args_dict.copy()
-    args_dict['server_addresses'] = self.server_addresses
-    return args_dict
-
-
-class Gke:
-  """ Class that has helper methods to interact with GKE """
-
-  class KubernetesProxy:
-    """Class to start a proxy on localhost to talk to the Kubernetes API server"""
-
-    def __init__(self, port):
-      cmd = ['kubectl', 'proxy', '--port=%d' % port]
-      self.p = subprocess.Popen(args=cmd)
-      time.sleep(2)
-      print('\nStarted kubernetes proxy on port: %d' % port)
-
-    def __del__(self):
-      if self.p is not None:
-        print('Shutting down Kubernetes proxy..')
-        self.p.kill()
-
-  def __init__(self, project_id, run_id, dataset_id, summary_table_id,
-               qps_table_id, kubernetes_port):
-    self.project_id = project_id
-    self.run_id = run_id
-    self.dataset_id = dataset_id
-    self.summary_table_id = summary_table_id
-    self.qps_table_id = qps_table_id
-
-    # The environment variables we would like to pass to every pod (both client
-    # and server) launched in GKE
-    self.gke_env = {
-        'RUN_ID': self.run_id,
-        'GCP_PROJECT_ID': self.project_id,
-        'DATASET_ID': self.dataset_id,
-        'SUMMARY_TABLE_ID': self.summary_table_id,
-        'QPS_TABLE_ID': self.qps_table_id
-    }
-
-    self.kubernetes_port = kubernetes_port
-    # Start kubernetes proxy
-    self.kubernetes_proxy = Gke.KubernetesProxy(kubernetes_port)
-
-  def _args_dict_to_str(self, args_dict):
-    return ' '.join('--%s=%s' % (k, args_dict[k]) for k in args_dict.keys())
-
-  def launch_servers(self, server_pod_spec):
-    is_success = True
-
-    # The command to run inside the container is the wrapper script (which then
-    # launches the actual server)
-    container_cmd = server_pod_spec.template.wrapper_script_path
-
-    # The parameters to the wrapper script (defined in
-    # server_pod_spec.template.wrapper_script_path) are are injected into the
-    # container via environment variables
-    server_env = self.gke_env.copy()
-    server_env.update(server_pod_spec.template.env_dict)
-    server_env.update({
-        'STRESS_TEST_IMAGE_TYPE': 'SERVER',
-        'STRESS_TEST_CMD': server_pod_spec.template.server_cmd,
-        'STRESS_TEST_ARGS_STR': self._args_dict_to_str(
-            server_pod_spec.template.server_args_dict),
-        'WILL_RUN_FOREVER': str(server_pod_spec.template.will_run_forever)
-    })
-
-    for pod_name in server_pod_spec.pod_names():
-      server_env['POD_NAME'] = pod_name
-      print('Creating server: %s' % pod_name)
-      is_success = kubernetes_api.create_pod_and_service(
-          'localhost',
-          self.kubernetes_port,
-          'default',  # Use 'default' namespace
-          pod_name,
-          server_pod_spec.docker_image.tag_name,
-          [server_pod_spec.template.server_port],  # Ports to expose on the pod
-          [container_cmd],
-          [],  # Args list is empty since we are passing all args via env variables
-          server_env,
-          True  # Headless = True for server to that GKE creates a DNS record for pod_name
-      )
-      if not is_success:
-        print('Error in launching server: %s' % pod_name)
-        break
-
-    if is_success:
-      print('Successfully created server(s)')
-
-    return is_success
-
-  def launch_clients(self, client_pod_spec):
-    is_success = True
-
-    # The command to run inside the container is the wrapper script (which then
-    # launches the actual stress client)
-    container_cmd = client_pod_spec.template.wrapper_script_path
-
-    # The parameters to the wrapper script (defined in
-    # client_pod_spec.template.wrapper_script_path) are are injected into the
-    # container via environment variables
-    client_env = self.gke_env.copy()
-    client_env.update(client_pod_spec.template.env_dict)
-    client_env.update({
-        'STRESS_TEST_IMAGE_TYPE': 'CLIENT',
-        'STRESS_TEST_CMD': client_pod_spec.template.stress_client_cmd,
-        'STRESS_TEST_ARGS_STR': self._args_dict_to_str(
-            client_pod_spec.get_client_args_dict()),
-        'METRICS_CLIENT_CMD': client_pod_spec.template.metrics_client_cmd,
-        'METRICS_CLIENT_ARGS_STR': self._args_dict_to_str(
-            client_pod_spec.template.metrics_args_dict),
-        'POLL_INTERVAL_SECS': str(client_pod_spec.template.poll_interval_secs),
-        'WILL_RUN_FOREVER': str(client_pod_spec.template.will_run_forever)
-    })
-
-    for pod_name in client_pod_spec.pod_names():
-      client_env['POD_NAME'] = pod_name
-      print('Creating client: %s' % pod_name)
-      is_success = kubernetes_api.create_pod_and_service(
-          'localhost',
-          self.kubernetes_port,
-          'default',  # default namespace,
-          pod_name,
-          client_pod_spec.docker_image.tag_name,
-          [client_pod_spec.template.metrics_port],  # Ports to expose on the pod
-          [container_cmd],
-          [],  # Empty args list since all args are passed via env variables
-          client_env,
-          True  # Client is a headless service (no need for an external ip)
-      )
-
-      if not is_success:
-        print('Error in launching client %s' % pod_name)
-        break
-
-    if is_success:
-      print('Successfully created all client(s)')
-
-    return is_success
-
-  def _delete_pods(self, pod_name_list):
-    is_success = True
-    for pod_name in pod_name_list:
-      print('Deleting %s' % pod_name)
-      is_success = kubernetes_api.delete_pod_and_service(
-          'localhost',
-          self.kubernetes_port,
-          'default',  # default namespace
-          pod_name)
-
-      if not is_success:
-        print('Error in deleting pod %s' % pod_name)
-        break
-
-    if is_success:
-      print('Successfully deleted all pods')
-
-    return is_success
-
-  def delete_servers(self, server_pod_spec):
-    return self._delete_pods(server_pod_spec.pod_names())
-
-  def delete_clients(self, client_pod_spec):
-    return self._delete_pods(client_pod_spec.pod_names())
-
-
-class Config:
-
-  def __init__(self, config_filename, gcp_project_id):
-    print('Loading configuration...')
-    config_dict = self._load_config(config_filename)
-
-    self.global_settings = self._parse_global_settings(config_dict,
-                                                       gcp_project_id)
-    self.docker_images_dict = self._parse_docker_images(
-        config_dict, self.global_settings.gcp_project_id)
-    self.client_templates_dict = self._parse_client_templates(config_dict)
-    self.server_templates_dict = self._parse_server_templates(config_dict)
-    self.server_pod_specs_dict = self._parse_server_pod_specs(
-        config_dict, self.docker_images_dict, self.server_templates_dict)
-    self.client_pod_specs_dict = self._parse_client_pod_specs(
-        config_dict, self.docker_images_dict, self.client_templates_dict,
-        self.server_pod_specs_dict)
-    print('Loaded Configuaration.')
-
-  def _parse_global_settings(self, config_dict, gcp_project_id):
-    global_settings_dict = config_dict['globalSettings']
-    return GlobalSettings(gcp_project_id,
-                          global_settings_dict['buildDockerImages'],
-                          global_settings_dict['pollIntervalSecs'],
-                          global_settings_dict['testDurationSecs'],
-                          global_settings_dict['kubernetesProxyPort'],
-                          global_settings_dict['datasetIdNamePrefix'],
-                          global_settings_dict['summaryTableId'],
-                          global_settings_dict['qpsTableId'],
-                          global_settings_dict['podWarmupSecs'])
-
-  def _parse_docker_images(self, config_dict, gcp_project_id):
-    """Parses the 'dockerImages' section of the config file and returns a
-    Dictionary of 'DockerImage' objects keyed by docker image names"""
-    docker_images_dict = {}
-
-    docker_config_dict = config_dict['dockerImages']
-    for image_name in docker_config_dict.keys():
-      build_script_path = docker_config_dict[image_name]['buildScript']
-      dockerfile_dir = docker_config_dict[image_name]['dockerFileDir']
-      build_type = docker_config_dict[image_name].get('buildType', 'opt')
-      docker_images_dict[image_name] = DockerImage(gcp_project_id, image_name,
-                                                   build_script_path,
-                                                   dockerfile_dir, build_type)
-    return docker_images_dict
-
-  def _parse_client_templates(self, config_dict):
-    """Parses the 'clientTemplates' section of the config file and returns a
-    Dictionary of 'ClientTemplate' objects keyed by client template names.
-
-    Note: The 'baseTemplates' sub section of the config file contains templates
-    with default values  and the 'templates' sub section contains the actual
-    client templates (which refer to the base template name to use for default
-    values).
-    """
-    client_templates_dict = {}
-
-    templates_dict = config_dict['clientTemplates']['templates']
-    base_templates_dict = config_dict['clientTemplates'].get('baseTemplates',
-                                                             {})
-    for template_name in templates_dict.keys():
-      # temp_dict is a temporary dictionary that merges base template dictionary
-      # and client template dictionary (with client template dictionary values
-      # overriding base template values)
-      temp_dict = {}
-
-      base_template_name = templates_dict[template_name].get('baseTemplate')
-      if not base_template_name is None:
-        temp_dict = base_templates_dict[base_template_name].copy()
-
-      temp_dict.update(templates_dict[template_name])
-
-      # Create and add ClientTemplate object to the final client_templates_dict
-      stress_client_cmd = ' '.join(temp_dict['stressClientCmd'])
-      metrics_client_cmd = ' '.join(temp_dict['metricsClientCmd'])
-      client_templates_dict[template_name] = ClientTemplate(
-          template_name, stress_client_cmd, metrics_client_cmd,
-          temp_dict['metricsPort'], temp_dict['wrapperScriptPath'],
-          temp_dict['pollIntervalSecs'], temp_dict['clientArgs'].copy(),
-          temp_dict['metricsArgs'].copy(), temp_dict.get('willRunForever', 1),
-          temp_dict.get('env', {}).copy())
-
-    return client_templates_dict
-
-  def _parse_server_templates(self, config_dict):
-    """Parses the 'serverTemplates' section of the config file and returns a
-    Dictionary of 'serverTemplate' objects keyed by server template names.
-
-    Note: The 'baseTemplates' sub section of the config file contains templates
-    with default values  and the 'templates' sub section contains the actual
-    server templates (which refer to the base template name to use for default
-    values).
-    """
-    server_templates_dict = {}
-
-    templates_dict = config_dict['serverTemplates']['templates']
-    base_templates_dict = config_dict['serverTemplates'].get('baseTemplates',
-                                                             {})
-
-    for template_name in templates_dict.keys():
-      # temp_dict is a temporary dictionary that merges base template dictionary
-      # and server template dictionary (with server template dictionary values
-      # overriding base template values)
-      temp_dict = {}
-
-      base_template_name = templates_dict[template_name].get('baseTemplate')
-      if not base_template_name is None:
-        temp_dict = base_templates_dict[base_template_name].copy()
-
-      temp_dict.update(templates_dict[template_name])
-
-      # Create and add ServerTemplate object to the final server_templates_dict
-      stress_server_cmd = ' '.join(temp_dict['stressServerCmd'])
-      server_templates_dict[template_name] = ServerTemplate(
-          template_name, stress_server_cmd, temp_dict['wrapperScriptPath'],
-          temp_dict['serverPort'], temp_dict['serverArgs'].copy(),
-          temp_dict.get('willRunForever', 1), temp_dict.get('env', {}).copy())
-
-    return server_templates_dict
-
-  def _parse_server_pod_specs(self, config_dict, docker_images_dict,
-                              server_templates_dict):
-    """Parses the 'serverPodSpecs' sub-section (under 'testMatrix' section) of
-    the config file and returns a Dictionary of 'ServerPodSpec' objects keyed
-    by server pod spec names"""
-    server_pod_specs_dict = {}
-
-    pod_specs_dict = config_dict['testMatrix'].get('serverPodSpecs', {})
-
-    for pod_name in pod_specs_dict.keys():
-      server_template_name = pod_specs_dict[pod_name]['serverTemplate']
-      docker_image_name = pod_specs_dict[pod_name]['dockerImage']
-      num_instances = pod_specs_dict[pod_name].get('numInstances', 1)
-
-      # Create and add the ServerPodSpec object to the final
-      # server_pod_specs_dict
-      server_pod_specs_dict[pod_name] = ServerPodSpec(
-          pod_name, server_templates_dict[server_template_name],
-          docker_images_dict[docker_image_name], num_instances)
-
-    return server_pod_specs_dict
-
-  def _parse_client_pod_specs(self, config_dict, docker_images_dict,
-                              client_templates_dict, server_pod_specs_dict):
-    """Parses the 'clientPodSpecs' sub-section (under 'testMatrix' section) of
-    the config file and returns a Dictionary of 'ClientPodSpec' objects keyed
-    by client pod spec names"""
-    client_pod_specs_dict = {}
-
-    pod_specs_dict = config_dict['testMatrix'].get('clientPodSpecs', {})
-    for pod_name in pod_specs_dict.keys():
-      client_template_name = pod_specs_dict[pod_name]['clientTemplate']
-      docker_image_name = pod_specs_dict[pod_name]['dockerImage']
-      num_instances = pod_specs_dict[pod_name]['numInstances']
-
-      # Get the server addresses from the server pod spec object
-      server_pod_spec_name = pod_specs_dict[pod_name]['serverPodSpec']
-      server_addresses = server_pod_specs_dict[
-          server_pod_spec_name].server_addresses()
-
-      client_pod_specs_dict[pod_name] = ClientPodSpec(
-          pod_name, client_templates_dict[client_template_name],
-          docker_images_dict[docker_image_name], num_instances,
-          server_addresses)
-
-    return client_pod_specs_dict
-
-  def _load_config(self, config_filename):
-    """Opens the config file and converts the Json text to Dictionary"""
-    if not os.path.isabs(config_filename):
-      raise Exception('Config objects expects an absolute file path. '
-                      'config file name passed: %s' % config_filename)
-    with open(config_filename) as config_file:
-      return json.load(config_file)
-
-
-def run_tests(config):
-  """ The main function that launches the stress tests """
-  # Build docker images and push to GKE registry
-  if config.global_settings.build_docker_images:
-    for name, docker_image in config.docker_images_dict.iteritems():
-      if not (docker_image.build_image() and
-              docker_image.push_to_gke_registry()):
-        return False
-
-  # Create a unique id for this run (Note: Using timestamp instead of UUID to
-  # make it easier to deduce the date/time of the run just by looking at the run
-  # run id. This is useful in debugging when looking at records in Biq query)
-  run_id = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
-  dataset_id = '%s_%s' % (config.global_settings.dataset_id_prefix, run_id)
-  print('Run id:', run_id)
-  print('Dataset id:', dataset_id)
-
-  bq_helper = BigQueryHelper(run_id, '', '',
-                             config.global_settings.gcp_project_id, dataset_id,
-                             config.global_settings.summary_table_id,
-                             config.global_settings.qps_table_id)
-  bq_helper.initialize()
-
-  gke = Gke(config.global_settings.gcp_project_id, run_id, dataset_id,
-            config.global_settings.summary_table_id,
-            config.global_settings.qps_table_id,
-            config.global_settings.kubernetes_proxy_port)
-
-  is_success = True
-
-  try:
-    print('Launching servers..')
-    for name, server_pod_spec in config.server_pod_specs_dict.iteritems():
-      if not gke.launch_servers(server_pod_spec):
-        is_success = False  # is_success is checked in the 'finally' block
-        return False
-
-    print('Launched servers. Waiting for %d seconds for the server pods to be '
-          'fully online') % config.global_settings.pod_warmup_secs
-    time.sleep(config.global_settings.pod_warmup_secs)
-
-    for name, client_pod_spec in config.client_pod_specs_dict.iteritems():
-      if not gke.launch_clients(client_pod_spec):
-        is_success = False  # is_success is checked in the 'finally' block
-        return False
-
-    print('Launched all clients. Waiting for %d seconds for the client pods to '
-          'be fully online') % config.global_settings.pod_warmup_secs
-    time.sleep(config.global_settings.pod_warmup_secs)
-
-    start_time = datetime.datetime.now()
-    end_time = start_time + datetime.timedelta(
-        seconds=config.global_settings.test_duration_secs)
-    print('Running the test until %s' % end_time.isoformat())
-
-    while True:
-      if datetime.datetime.now() > end_time:
-        print('Test was run for %d seconds' %
-              config.global_settings.test_duration_secs)
-        break
-
-      # Check if either stress server or clients have failed (btw, the bq_helper
-      # monitors all the rows in the summary table and checks if any of them
-      # have a failure status)
-      if bq_helper.check_if_any_tests_failed():
-        is_success = False
-        print('Some tests failed.')
-        break  # Don't 'return' here. We still want to call bq_helper to print qps/summary tables
-
-      # Tests running fine. Wait until next poll time to check the status
-      print('Sleeping for %d seconds..' %
-            config.global_settings.test_poll_interval_secs)
-      time.sleep(config.global_settings.test_poll_interval_secs)
-
-    # Print BiqQuery tables
-    bq_helper.print_qps_records()
-    bq_helper.print_summary_records()
-
-  finally:
-    # If there was a test failure, we should not delete the pods since they
-    # would contain useful debug information (logs, core dumps etc)
-    if is_success:
-      for name, server_pod_spec in config.server_pod_specs_dict.iteritems():
-        gke.delete_servers(server_pod_spec)
-      for name, client_pod_spec in config.client_pod_specs_dict.iteritems():
-        gke.delete_clients(client_pod_spec)
-
-  return is_success
-
-
-def tear_down(config):
-  gke = Gke(config.global_settings.gcp_project_id, '', '',
-            config.global_settings.summary_table_id,
-            config.global_settings.qps_table_id,
-            config.global_settings.kubernetes_proxy_port)
-  for name, server_pod_spec in config.server_pod_specs_dict.iteritems():
-    gke.delete_servers(server_pod_spec)
-  for name, client_pod_spec in config.client_pod_specs_dict.iteritems():
-    gke.delete_clients(client_pod_spec)
-
-
-argp = argparse.ArgumentParser(
-    description='Launch stress tests in GKE',
-    formatter_class=argparse.ArgumentDefaultsHelpFormatter)
-argp.add_argument('--gcp_project_id',
-                  required=True,
-                  help='The Google Cloud Platform Project Id')
-argp.add_argument('--config_file',
-                  required=True,
-                  type=str,
-                  help='The test config file')
-argp.add_argument('--tear_down', action='store_true', default=False)
-
-if __name__ == '__main__':
-  args = argp.parse_args()
-
-  config_filename = args.config_file
-
-  # Since we will be changing the current working directory to grpc root in the
-  # next step, we should check if the config filename path is a relative path
-  # (i.e a path relative to the current working directory) and if so, convert it
-  # to abosulte path
-  if not os.path.isabs(config_filename):
-    config_filename = os.path.abspath(config_filename)
-
-  config = Config(config_filename, args.gcp_project_id)
-
-  # Change current working directory to grpc root
-  # (This is important because all relative file paths in the config file are
-  # supposed to interpreted as relative to the GRPC root)
-  grpc_root = os.path.abspath(os.path.join(
-      os.path.dirname(sys.argv[0]), '../../..'))
-  os.chdir(grpc_root)
-
-  # Note that tear_down is only in cases where we want to manually tear down a
-  # test that for some reason run_tests() could not cleanup
-  if args.tear_down:
-    tear_down(config)
-    sys.exit(1)
-
-  if not run_tests(config):
-    sys.exit(1)
diff --git a/tools/ubsan_suppressions.txt b/tools/ubsan_suppressions.txt
index 9869f98..f87ed18 100644
--- a/tools/ubsan_suppressions.txt
+++ b/tools/ubsan_suppressions.txt
@@ -4,4 +4,6 @@
 nonnull-attribute:rsa_blinding_get
 nonnull-attribute:ssl_copy_key_material
 alignment:CRYPTO_cbc128_encrypt
+nonnull-attribute:google::protobuf::DescriptorBuilder::BuildFileImpl
+nonnull-attribute:google::protobuf::TextFormat::Printer::TextGenerator::Write
 
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
index 165ebe6..4958218 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
@@ -197,8 +197,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\proto_utils.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_serializer.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_utils.h" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.h" />
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
index d9aa1e3..1e2a2eb 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
@@ -186,12 +186,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_serializer.h">
-      <Filter>include\grpc++\impl\codegen</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_utils.h">
-      <Filter>include\grpc++\impl\codegen</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.h">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 7152009..86b5856 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -475,8 +475,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\uri_parser.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\deadline\deadline_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb.h" />
@@ -915,10 +917,14 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel_secure.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index de2dfe6..943ad52 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -613,12 +613,18 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
       <Filter>src\core\ext\transport\chttp2\client\insecure</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.c">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.c">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel_secure.c">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.c">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.c">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClCompile>
@@ -1334,12 +1340,18 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\chttp2_connector.h">
       <Filter>src\core\ext\transport\chttp2\client</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.h">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.h">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel.h">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.h">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.h">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index 0bfda72..2db0ffa 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -444,8 +444,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\resolver\dns\c_ares\grpc_ares_wrapper.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\load_reporting\load_reporting.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\load_reporting\load_reporting_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb.h" />
@@ -836,10 +838,14 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\load_reporting\load_reporting_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 63c8d7f..c7d6670 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -547,12 +547,18 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\load_reporting\load_reporting_filter.c">
       <Filter>src\core\ext\filters\load_reporting</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.c">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.c">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel.c">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.c">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.c">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClCompile>
@@ -1181,12 +1187,18 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\load_reporting\load_reporting_filter.h">
       <Filter>src\core\ext\filters\load_reporting</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\client_load_reporting_filter.h">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb.h">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_channel.h">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\grpclb_client_stats.h">
+      <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\filters\client_channel\lb_policy\grpclb\load_balancer_api.h">
       <Filter>src\core\ext\filters\client_channel\lb_policy\grpclb</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/grpclb_end2end_test/grpclb_end2end_test.vcxproj b/vsprojects/vcxproj/test/grpclb_end2end_test/grpclb_end2end_test.vcxproj
new file mode 100644
index 0000000..0a10fc6
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpclb_end2end_test/grpclb_end2end_test.vcxproj
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{15BCAA4C-F569-D5B8-50CF-F442CBC71902}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\cpptest.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\protobuf.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>grpclb_end2end_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>grpclb_end2end_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\grpclb_end2end_test.cc">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
+      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj">
+      <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/grpclb_end2end_test/grpclb_end2end_test.vcxproj.filters b/vsprojects/vcxproj/test/grpclb_end2end_test/grpclb_end2end_test.vcxproj.filters
new file mode 100644
index 0000000..7b1ef95
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpclb_end2end_test/grpclb_end2end_test.vcxproj.filters
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.proto">
+      <Filter>src\proto\grpc\lb\v1</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\grpclb_end2end_test.cc">
+      <Filter>test\cpp\end2end</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="src">
+      <UniqueIdentifier>{5ffc769b-475b-67a1-b131-2af6f6103043}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto">
+      <UniqueIdentifier>{165c6d96-aac0-d0b0-a1b4-9470159d683e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc">
+      <UniqueIdentifier>{0b3a7ccc-ea48-092f-75f1-866995a4ed04}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc\lb">
+      <UniqueIdentifier>{8f4e5440-acec-c6e3-4a3d-c8ff6ed84e11}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc\lb\v1">
+      <UniqueIdentifier>{191ccb8f-33fe-b990-20c1-87c04d15a7c2}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test">
+      <UniqueIdentifier>{f501dace-533d-819c-ca99-9e0359bb67ef}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp">
+      <UniqueIdentifier>{fcbf6f3b-2707-4605-d76e-f32b545c6531}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp\end2end">
+      <UniqueIdentifier>{f70e20f4-442c-b400-758d-f13abf182438}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+