Merge pull request #13406 from nathanielmanistaatgoogle/interop_required_args

Require port and server_port interop flags.
diff --git a/BUILD b/BUILD
index 6a51449..4e04b8c 100644
--- a/BUILD
+++ b/BUILD
@@ -558,7 +558,6 @@
         "src/core/lib/http/httpcli.cc",
         "src/core/lib/http/parser.cc",
         "src/core/lib/iomgr/call_combiner.cc",
-        "src/core/lib/iomgr/closure.cc",
         "src/core/lib/iomgr/combiner.cc",
         "src/core/lib/iomgr/endpoint.cc",
         "src/core/lib/iomgr/endpoint_pair_posix.cc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 153e3a0..11fc050 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -978,7 +978,6 @@
   src/core/lib/http/httpcli.cc
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/call_combiner.cc
-  src/core/lib/iomgr/closure.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -1319,7 +1318,6 @@
   src/core/lib/http/httpcli.cc
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/call_combiner.cc
-  src/core/lib/iomgr/closure.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -1621,6 +1619,7 @@
   test/core/util/port.cc
   test/core/util/port_server_client.cc
   test/core/util/slice_splitter.cc
+  test/core/util/tracer_util.cc
   test/core/util/trickle_endpoint.cc
   src/core/lib/backoff/backoff.cc
   src/core/lib/channel/channel_args.cc
@@ -1641,7 +1640,6 @@
   src/core/lib/http/httpcli.cc
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/call_combiner.cc
-  src/core/lib/iomgr/closure.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -1887,6 +1885,7 @@
   test/core/util/port.cc
   test/core/util/port_server_client.cc
   test/core/util/slice_splitter.cc
+  test/core/util/tracer_util.cc
   test/core/util/trickle_endpoint.cc
   src/core/lib/backoff/backoff.cc
   src/core/lib/channel/channel_args.cc
@@ -1907,7 +1906,6 @@
   src/core/lib/http/httpcli.cc
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/call_combiner.cc
-  src/core/lib/iomgr/closure.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -2159,7 +2157,6 @@
   src/core/lib/http/httpcli.cc
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/call_combiner.cc
-  src/core/lib/iomgr/closure.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -2910,7 +2907,6 @@
   src/core/lib/http/httpcli.cc
   src/core/lib/http/parser.cc
   src/core/lib/iomgr/call_combiner.cc
-  src/core/lib/iomgr/closure.cc
   src/core/lib/iomgr/combiner.cc
   src/core/lib/iomgr/endpoint.cc
   src/core/lib/iomgr/endpoint_pair_posix.cc
diff --git a/Makefile b/Makefile
index 0dacde8..fd8b35e 100644
--- a/Makefile
+++ b/Makefile
@@ -2968,7 +2968,6 @@
     src/core/lib/http/httpcli.cc \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/call_combiner.cc \
-    src/core/lib/iomgr/closure.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
@@ -3308,7 +3307,6 @@
     src/core/lib/http/httpcli.cc \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/call_combiner.cc \
-    src/core/lib/iomgr/closure.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
@@ -3608,6 +3606,7 @@
     test/core/util/port.cc \
     test/core/util/port_server_client.cc \
     test/core/util/slice_splitter.cc \
+    test/core/util/tracer_util.cc \
     test/core/util/trickle_endpoint.cc \
     src/core/lib/backoff/backoff.cc \
     src/core/lib/channel/channel_args.cc \
@@ -3628,7 +3627,6 @@
     src/core/lib/http/httpcli.cc \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/call_combiner.cc \
-    src/core/lib/iomgr/closure.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
@@ -3864,6 +3862,7 @@
     test/core/util/port.cc \
     test/core/util/port_server_client.cc \
     test/core/util/slice_splitter.cc \
+    test/core/util/tracer_util.cc \
     test/core/util/trickle_endpoint.cc \
     src/core/lib/backoff/backoff.cc \
     src/core/lib/channel/channel_args.cc \
@@ -3884,7 +3883,6 @@
     src/core/lib/http/httpcli.cc \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/call_combiner.cc \
-    src/core/lib/iomgr/closure.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
@@ -4113,7 +4111,6 @@
     src/core/lib/http/httpcli.cc \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/call_combiner.cc \
-    src/core/lib/iomgr/closure.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
@@ -4842,7 +4839,6 @@
     src/core/lib/http/httpcli.cc \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/call_combiner.cc \
-    src/core/lib/iomgr/closure.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
diff --git a/build.yaml b/build.yaml
index 18f9449..9018e51 100644
--- a/build.yaml
+++ b/build.yaml
@@ -168,7 +168,6 @@
   - src/core/lib/http/httpcli.cc
   - src/core/lib/http/parser.cc
   - src/core/lib/iomgr/call_combiner.cc
-  - src/core/lib/iomgr/closure.cc
   - src/core/lib/iomgr/combiner.cc
   - src/core/lib/iomgr/endpoint.cc
   - src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -714,6 +713,7 @@
   - test/core/util/port.h
   - test/core/util/port_server_client.h
   - test/core/util/slice_splitter.h
+  - test/core/util/tracer_util.h
   - test/core/util/trickle_endpoint.h
   src:
   - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
@@ -730,6 +730,7 @@
   - test/core/util/port.cc
   - test/core/util/port_server_client.cc
   - test/core/util/slice_splitter.cc
+  - test/core/util/tracer_util.cc
   - test/core/util/trickle_endpoint.cc
   deps:
   - gpr_test_util
diff --git a/config.m4 b/config.m4
index 5cb46cf..d2f2520 100644
--- a/config.m4
+++ b/config.m4
@@ -104,7 +104,6 @@
     src/core/lib/http/httpcli.cc \
     src/core/lib/http/parser.cc \
     src/core/lib/iomgr/call_combiner.cc \
-    src/core/lib/iomgr/closure.cc \
     src/core/lib/iomgr/combiner.cc \
     src/core/lib/iomgr/endpoint.cc \
     src/core/lib/iomgr/endpoint_pair_posix.cc \
diff --git a/config.w32 b/config.w32
index 0fc5066..8a71375 100644
--- a/config.w32
+++ b/config.w32
@@ -81,7 +81,6 @@
     "src\\core\\lib\\http\\httpcli.cc " +
     "src\\core\\lib\\http\\parser.cc " +
     "src\\core\\lib\\iomgr\\call_combiner.cc " +
-    "src\\core\\lib\\iomgr\\closure.cc " +
     "src\\core\\lib\\iomgr\\combiner.cc " +
     "src\\core\\lib\\iomgr\\endpoint.cc " +
     "src\\core\\lib\\iomgr\\endpoint_pair_posix.cc " +
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 01b8d65..02c6a64 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -478,7 +478,6 @@
                       'src/core/lib/http/httpcli.cc',
                       'src/core/lib/http/parser.cc',
                       'src/core/lib/iomgr/call_combiner.cc',
-                      'src/core/lib/iomgr/closure.cc',
                       'src/core/lib/iomgr/combiner.cc',
                       'src/core/lib/iomgr/endpoint.cc',
                       'src/core/lib/iomgr/endpoint_pair_posix.cc',
diff --git a/grpc.gemspec b/grpc.gemspec
index 2fe2536..d674c09 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -413,7 +413,6 @@
   s.files += %w( src/core/lib/http/httpcli.cc )
   s.files += %w( src/core/lib/http/parser.cc )
   s.files += %w( src/core/lib/iomgr/call_combiner.cc )
-  s.files += %w( src/core/lib/iomgr/closure.cc )
   s.files += %w( src/core/lib/iomgr/combiner.cc )
   s.files += %w( src/core/lib/iomgr/endpoint.cc )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_posix.cc )
diff --git a/grpc.gyp b/grpc.gyp
index 7075aa9..fb15391 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -245,7 +245,6 @@
         'src/core/lib/http/httpcli.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/call_combiner.cc',
-        'src/core/lib/iomgr/closure.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
@@ -515,6 +514,7 @@
         'test/core/util/port.cc',
         'test/core/util/port_server_client.cc',
         'test/core/util/slice_splitter.cc',
+        'test/core/util/tracer_util.cc',
         'test/core/util/trickle_endpoint.cc',
         'src/core/lib/backoff/backoff.cc',
         'src/core/lib/channel/channel_args.cc',
@@ -535,7 +535,6 @@
         'src/core/lib/http/httpcli.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/call_combiner.cc',
-        'src/core/lib/iomgr/closure.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
@@ -723,6 +722,7 @@
         'test/core/util/port.cc',
         'test/core/util/port_server_client.cc',
         'test/core/util/slice_splitter.cc',
+        'test/core/util/tracer_util.cc',
         'test/core/util/trickle_endpoint.cc',
         'src/core/lib/backoff/backoff.cc',
         'src/core/lib/channel/channel_args.cc',
@@ -743,7 +743,6 @@
         'src/core/lib/http/httpcli.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/call_combiner.cc',
-        'src/core/lib/iomgr/closure.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
@@ -936,7 +935,6 @@
         'src/core/lib/http/httpcli.cc',
         'src/core/lib/http/parser.cc',
         'src/core/lib/iomgr/call_combiner.cc',
-        'src/core/lib/iomgr/closure.cc',
         'src/core/lib/iomgr/combiner.cc',
         'src/core/lib/iomgr/endpoint.cc',
         'src/core/lib/iomgr/endpoint_pair_posix.cc',
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index fb4bfc3..1906886 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -297,6 +297,27 @@
 #endif
 #endif /* GPR_NO_AUTODETECT_PLATFORM */
 
+/*
+ *  There are platforms for which TLS should not be used even though the
+ * compiler makes it seem like it's supported (Android NDK < r12b for example).
+ * This is primarily because of linker problems and toolchain misconfiguration:
+ * TLS isn't supported until NDK r12b per
+ * https://developer.android.com/ndk/downloads/revision_history.html
+ * Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in
+ * <android/ndk-version.h>. For NDK < r16, users should define these macros,
+ * e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. */
+#if defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS)
+#if __has_include(<android/ndk-version.h>)
+#include <android/ndk-version.h>
+#endif /* __has_include(<android/ndk-version.h>) */
+#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
+    defined(__NDK_MINOR__) &&                                               \
+    ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
+#undef GPR_GCC_TLS
+#define GPR_PTHREAD_TLS 1
+#endif
+#endif /*defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS) */
+
 #if defined(__has_include)
 #if __has_include(<atomic>)
 #define GRPC_HAS_CXX11_ATOMIC
diff --git a/package.xml b/package.xml
index 9dee62f..3356c27 100644
--- a/package.xml
+++ b/package.xml
@@ -425,7 +425,6 @@
     <file baseinstalldir="/" name="src/core/lib/http/httpcli.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/parser.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/call_combiner.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/iomgr/closure.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/combiner.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_posix.cc" role="src" />
diff --git a/src/core/ext/filters/client_channel/channel_connectivity.cc b/src/core/ext/filters/client_channel/channel_connectivity.cc
index d3627a2..7eaf5d9 100644
--- a/src/core/ext/filters/client_channel/channel_connectivity.cc
+++ b/src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -122,7 +122,7 @@
   gpr_mu_lock(&w->mu);
 
   if (due_to_completion) {
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures)) {
+    if (grpc_trace_operation_failures.enabled()) {
       GRPC_LOG_IF_ERROR("watch_completion_error", GRPC_ERROR_REF(error));
     }
     GRPC_ERROR_UNREF(error);
diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc
index 8b8d512..03c1b6f 100644
--- a/src/core/ext/filters/client_channel/client_channel.cc
+++ b/src/core/ext/filters/client_channel/client_channel.cc
@@ -56,8 +56,7 @@
 
 /* Client channel implementation */
 
-grpc_tracer_flag grpc_client_channel_trace =
-    GRPC_TRACER_INITIALIZER(false, "client_channel");
+grpc_core::TraceFlag grpc_client_channel_trace(false, "client_channel");
 
 /*************************************************************************
  * METHOD-CONFIG TABLE
@@ -248,7 +247,7 @@
                                          GRPC_ERROR_REF(error));
     }
   }
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p: setting connectivity state to %s", chand,
             grpc_connectivity_state_name(state));
   }
@@ -262,7 +261,7 @@
   grpc_connectivity_state publish_state = w->state;
   /* check if the notification is for the latest policy */
   if (w->lb_policy == w->chand->lb_policy) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p: lb_policy=%p state changed to %s", w->chand,
               w->lb_policy, grpc_connectivity_state_name(w->state));
     }
@@ -300,7 +299,7 @@
 
 static void start_resolving_locked(grpc_exec_ctx* exec_ctx,
                                    channel_data* chand) {
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p: starting name resolution", chand);
   }
   GPR_ASSERT(!chand->started_resolving);
@@ -373,7 +372,7 @@
 static void on_resolver_result_changed_locked(grpc_exec_ctx* exec_ctx,
                                               void* arg, grpc_error* error) {
   channel_data* chand = (channel_data*)arg;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p: got resolver result: error=%s", chand,
             grpc_error_string(error));
   }
@@ -483,7 +482,7 @@
     grpc_channel_args_destroy(exec_ctx, chand->resolver_result);
     chand->resolver_result = nullptr;
   }
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p: resolver result: lb_policy_name=\"%s\"%s, "
             "service_config=\"%s\"",
@@ -524,7 +523,7 @@
   if (new_lb_policy != nullptr || error != GRPC_ERROR_NONE ||
       chand->resolver == nullptr) {
     if (chand->lb_policy != nullptr) {
-      if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+      if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: unreffing lb_policy=%p", chand,
                 chand->lb_policy);
       }
@@ -538,11 +537,11 @@
   // Now that we've swapped out the relevant fields of chand, check for
   // error or shutdown.
   if (error != GRPC_ERROR_NONE || chand->resolver == nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p: shutting down", chand);
     }
     if (chand->resolver != nullptr) {
-      if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+      if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: shutting down resolver", chand);
       }
       grpc_resolver_shutdown_locked(exec_ctx, chand->resolver);
@@ -565,7 +564,7 @@
     grpc_error* state_error =
         GRPC_ERROR_CREATE_FROM_STATIC_STRING("No load balancing policy");
     if (new_lb_policy != nullptr) {
-      if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+      if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: initializing new LB policy", chand);
       }
       GRPC_ERROR_UNREF(state_error);
@@ -899,7 +898,7 @@
                                           grpc_call_element* elem,
                                           grpc_error* error) {
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s",
             elem->channel_data, calld, calld->waiting_for_pick_batches_count,
@@ -942,7 +941,7 @@
                                             grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: sending %" PRIuPTR
             " pending batches to subchannel_call=%p",
@@ -969,7 +968,7 @@
                                                 grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call",
             chand, calld);
   }
@@ -1015,7 +1014,7 @@
   grpc_error* new_error = grpc_connected_subchannel_create_call(
       exec_ctx, calld->connected_subchannel, &call_args,
       &calld->subchannel_call);
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: create subchannel_call=%p: error=%s",
             chand, calld, calld->subchannel_call, grpc_error_string(new_error));
   }
@@ -1041,7 +1040,7 @@
                              "Call dropped by load balancing policy")
                        : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
                              "Failed to create subchannel", &error, 1);
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: failed to create subchannel: error=%s", chand,
               calld, grpc_error_string(calld->error));
@@ -1075,7 +1074,7 @@
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
   if (calld->lb_policy != nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: cancelling pick from LB policy %p",
               chand, calld, calld->lb_policy);
     }
@@ -1093,7 +1092,7 @@
   grpc_call_element* elem = (grpc_call_element*)arg;
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed asynchronously",
             chand, calld);
   }
@@ -1110,7 +1109,7 @@
                                        grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p",
             chand, calld, chand->lb_policy);
   }
@@ -1148,7 +1147,7 @@
       calld->subchannel_call_context, nullptr, &calld->lb_pick_closure);
   if (pick_done) {
     /* synchronous grpc_lb_policy_pick call. Unref the LB policy. */
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed synchronously",
               chand, calld);
     }
@@ -1193,7 +1192,7 @@
   grpc_call_element* elem = args->elem;
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: cancelling pick waiting for resolver result",
             chand, calld);
@@ -1217,7 +1216,7 @@
   pick_after_resolver_result_args* args = (pick_after_resolver_result_args*)arg;
   if (args->finished) {
     /* cancelled, do nothing */
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "call cancelled before resolver result");
     }
     gpr_free(args);
@@ -1228,13 +1227,13 @@
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
   if (error != GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver failed to return data",
               chand, calld);
     }
     async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error));
   } else if (chand->lb_policy != nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver returned, doing pick",
               chand, calld);
     }
@@ -1256,7 +1255,7 @@
   // right way to deal with it.
   else if (chand->resolver != nullptr) {
     // No LB policy, so try again.
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: resolver returned but no LB policy, "
               "trying again",
@@ -1264,7 +1263,7 @@
     }
     pick_after_resolver_result_start_locked(exec_ctx, elem);
   } else {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver disconnected", chand,
               calld);
     }
@@ -1277,7 +1276,7 @@
                                                     grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: deferring pick pending resolver result", chand,
             calld);
@@ -1362,7 +1361,7 @@
   GPR_TIMER_BEGIN("cc_start_transport_stream_op_batch", 0);
   // If we've previously been cancelled, immediately fail any new batches.
   if (calld->error != GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: failing batch with error: %s",
               chand, calld, grpc_error_string(calld->error));
     }
@@ -1378,7 +1377,7 @@
     // error to the caller when the first batch does get passed down.
     GRPC_ERROR_UNREF(calld->error);
     calld->error = GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: recording cancel_error=%s", chand,
               calld, grpc_error_string(calld->error));
     }
@@ -1407,7 +1406,7 @@
   // the channel combiner, which is more efficient (especially for
   // streaming calls).
   if (calld->subchannel_call != nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: sending batch to subchannel_call=%p", chand,
               calld, calld->subchannel_call);
@@ -1421,7 +1420,7 @@
   // For batches containing a send_initial_metadata op, enter the channel
   // combiner to start a pick.
   if (batch->send_initial_metadata) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: entering client_channel combiner",
               chand, calld);
     }
@@ -1432,7 +1431,7 @@
         GRPC_ERROR_NONE);
   } else {
     // For all other batches, release the call combiner.
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: saved batch, yeilding call combiner", chand,
               calld);
diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h
index 27862cf..f58a8c1 100644
--- a/src/core/ext/filters/client_channel/client_channel.h
+++ b/src/core/ext/filters/client_channel/client_channel.h
@@ -23,7 +23,7 @@
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/lib/channel/channel_stack.h"
 
-extern grpc_tracer_flag grpc_client_channel_trace;
+extern grpc_core::TraceFlag grpc_client_channel_trace;
 
 // Channel arg key for server URI string.
 #define GRPC_ARG_SERVER_URI "grpc.server_uri"
diff --git a/src/core/ext/filters/client_channel/client_channel_plugin.cc b/src/core/ext/filters/client_channel/client_channel_plugin.cc
index eebef68..c1b57d0 100644
--- a/src/core/ext/filters/client_channel/client_channel_plugin.cc
+++ b/src/core/ext/filters/client_channel/client_channel_plugin.cc
@@ -78,10 +78,6 @@
       GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, append_filter,
       (void*)&grpc_client_channel_filter);
   grpc_http_connect_register_handshaker_factory();
-  grpc_register_tracer(&grpc_client_channel_trace);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_resolver_refcount);
-#endif
 }
 
 extern "C" void grpc_client_channel_shutdown(void) {
diff --git a/src/core/ext/filters/client_channel/lb_policy.cc b/src/core/ext/filters/client_channel/lb_policy.cc
index 387c26e..6276c3e 100644
--- a/src/core/ext/filters/client_channel/lb_policy.cc
+++ b/src/core/ext/filters/client_channel/lb_policy.cc
@@ -21,10 +21,8 @@
 
 #define WEAK_REF_BITS 16
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_lb_policy_refcount =
-    GRPC_TRACER_INITIALIZER(false, "lb_policy_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount(
+    false, "lb_policy_refcount");
 
 void grpc_lb_policy_init(grpc_lb_policy* policy,
                          const grpc_lb_policy_vtable* vtable,
@@ -52,7 +50,7 @@
   gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta)
                             : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_lb_policy_refcount)) {
+  if (grpc_trace_lb_policy_refcount.enabled()) {
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "LB_POLICY: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c,
             purpose, old_val, old_val + delta, reason);
diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h
index 590094e..cd40b4d 100644
--- a/src/core/ext/filters/client_channel/lb_policy.h
+++ b/src/core/ext/filters/client_channel/lb_policy.h
@@ -33,9 +33,7 @@
 typedef struct grpc_lb_policy_vtable grpc_lb_policy_vtable;
 typedef struct grpc_lb_policy_args grpc_lb_policy_args;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_lb_policy_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount;
 
 struct grpc_lb_policy {
   const grpc_lb_policy_vtable* vtable;
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
index 040941a..5fb502e 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
@@ -126,7 +126,7 @@
 #define GRPC_GRPCLB_RECONNECT_JITTER 0.2
 #define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000
 
-grpc_tracer_flag grpc_lb_glb_trace = GRPC_TRACER_INITIALIZER(false, "glb");
+grpc_core::TraceFlag grpc_lb_glb_trace(false, "glb");
 
 /* add lb_token of selected subchannel (address) to the call's initial
  * metadata */
@@ -217,7 +217,7 @@
     } else {
       grpc_grpclb_client_stats_unref(wc_arg->client_stats);
     }
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Unreffing RR %p", wc_arg->glb_policy,
               wc_arg->rr_policy);
     }
@@ -623,7 +623,7 @@
       GPR_ASSERT(rr_state_error == GRPC_ERROR_NONE);
   }
 
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     gpr_log(
         GPR_INFO,
         "[grpclb %p] Setting grpclb's state to %s from new RR policy %p state.",
@@ -654,7 +654,7 @@
     }
     if (server->drop) {
       // Not using the RR policy, so unref it.
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO, "[grpclb %p] Unreffing RR %p for drop", glb_policy,
                 wc_arg->rr_policy);
       }
@@ -684,7 +684,7 @@
       (void**)&wc_arg->lb_token, &wc_arg->wrapper_closure);
   if (pick_done) {
     /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Unreffing RR %p", glb_policy,
               wc_arg->rr_policy);
     }
@@ -806,7 +806,7 @@
     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_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO,
               "[grpclb %p] Pending pick about to (async) PICK from RR %p",
               glb_policy, glb_policy->rr_policy);
@@ -821,7 +821,7 @@
     glb_policy->pending_pings = pping->next;
     GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_ping");
     pping->wrapped_notify_arg.rr_policy = glb_policy->rr_policy;
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Pending ping about to PING from RR %p",
               glb_policy, glb_policy->rr_policy);
     }
@@ -837,14 +837,14 @@
   grpc_lb_policy_args* args = lb_policy_args_create(exec_ctx, glb_policy);
   GPR_ASSERT(args != nullptr);
   if (glb_policy->rr_policy != nullptr) {
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG, "[grpclb %p] Updating RR policy %p", glb_policy,
               glb_policy->rr_policy);
     }
     grpc_lb_policy_update_locked(exec_ctx, glb_policy->rr_policy, args);
   } else {
     create_rr_locked(exec_ctx, glb_policy, args);
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG, "[grpclb %p] Created new RR policy %p", glb_policy,
               glb_policy->rr_policy);
     }
@@ -1186,7 +1186,7 @@
     // need to make sure we aren't trying to pick from a RR policy instance
     // that's in shutdown.
     if (rr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) {
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] NOT picking from from RR %p: RR conn state=%s",
                 glb_policy, glb_policy->rr_policy,
@@ -1196,7 +1196,7 @@
                        on_complete);
       pick_done = false;
     } else {  // RR not in shutdown
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO, "[grpclb %p] about to PICK from RR %p", glb_policy,
                 glb_policy->rr_policy);
       }
@@ -1221,7 +1221,7 @@
                                        false /* force_async */, target, wc_arg);
     }
   } else {  // glb_policy->rr_policy == NULL
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "[grpclb %p] No RR policy. Adding to grpclb's pending picks",
               glb_policy);
@@ -1272,7 +1272,7 @@
   glb_policy->retry_timer_active = false;
   if (!glb_policy->shutting_down && glb_policy->lb_call == nullptr &&
       error == GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Restarting call to LB server", glb_policy);
     }
     query_for_backends_locked(exec_ctx, glb_policy);
@@ -1293,7 +1293,7 @@
     grpc_millis next_try =
         grpc_backoff_step(exec_ctx, &glb_policy->lb_call_backoff_state)
             .next_attempt_start_time;
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG, "[grpclb %p] Connection to LB server lost...",
               glb_policy);
       grpc_millis timeout = next_try - grpc_exec_ctx_now(exec_ctx);
@@ -1342,6 +1342,9 @@
     glb_policy->client_load_report_timer_pending = false;
     GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
                               "client_load_report");
+    if (glb_policy->lb_call == nullptr) {
+      maybe_restart_lb_call(exec_ctx, glb_policy);
+    }
     return;
   }
   schedule_next_client_load_report(exec_ctx, glb_policy);
@@ -1496,7 +1499,7 @@
 
   lb_call_init_locked(exec_ctx, glb_policy);
 
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     gpr_log(GPR_INFO,
             "[grpclb %p] Query for backends (lb_channel: %p, lb_call: %p)",
             glb_policy, glb_policy->lb_channel, glb_policy->lb_call);
@@ -1587,7 +1590,7 @@
         glb_policy->client_stats_report_interval = GPR_MAX(
             GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis(
                                 &response->client_stats_report_interval));
-        if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+        if (grpc_lb_glb_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "[grpclb %p] Received initial LB response message; "
                   "client load reporting interval = %" PRIdPTR " milliseconds",
@@ -1599,7 +1602,7 @@
         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_TRACER_ON(grpc_lb_glb_trace)) {
+      } else if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] Received initial LB response message; client load "
                 "reporting NOT enabled",
@@ -1612,7 +1615,7 @@
           grpc_grpclb_response_parse_serverlist(response_slice);
       if (serverlist != nullptr) {
         GPR_ASSERT(glb_policy->lb_call != nullptr);
-        if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+        if (grpc_lb_glb_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "[grpclb %p] Serverlist with %" PRIuPTR " servers received",
                   glb_policy, serverlist->num_servers);
@@ -1630,7 +1633,7 @@
         if (serverlist->num_servers > 0) {
           if (grpc_grpclb_serverlist_equals(glb_policy->serverlist,
                                             serverlist)) {
-            if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+            if (grpc_lb_glb_trace.enabled()) {
               gpr_log(GPR_INFO,
                       "[grpclb %p] Incoming server list identical to current, "
                       "ignoring.",
@@ -1659,7 +1662,7 @@
             rr_handover_locked(exec_ctx, glb_policy);
           }
         } else {
-          if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+          if (grpc_lb_glb_trace.enabled()) {
             gpr_log(GPR_INFO,
                     "[grpclb %p] Received empty server list, ignoring.",
                     glb_policy);
@@ -1707,7 +1710,7 @@
    * actually runs, don't fall back. */
   if (glb_policy->serverlist == nullptr) {
     if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) {
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] Falling back to use backends from resolver",
                 glb_policy);
@@ -1724,7 +1727,7 @@
                                                 void* arg, grpc_error* error) {
   glb_lb_policy* glb_policy = (glb_lb_policy*)arg;
   GPR_ASSERT(glb_policy->lb_call != nullptr);
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     char* status_details =
         grpc_slice_to_c_string(glb_policy->lb_call_status_details);
     gpr_log(GPR_INFO,
@@ -1906,7 +1909,7 @@
   GPR_ASSERT(uri->path[0] != '\0');
   glb_policy->server_name =
       gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     gpr_log(GPR_INFO,
             "[grpclb %p] Will use '%s' as the server name for LB request.",
             glb_policy, glb_policy->server_name);
@@ -2000,10 +2003,6 @@
 
 extern "C" void grpc_lb_policy_grpclb_init() {
   grpc_register_lb_policy(grpc_glb_lb_factory_create());
-  grpc_register_tracer(&grpc_lb_glb_trace);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_lb_policy_refcount);
-#endif
   grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    maybe_add_client_load_reporting_filter,
diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
index caa6aee..b15ca82 100644
--- a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
@@ -29,8 +29,7 @@
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
-grpc_tracer_flag grpc_lb_pick_first_trace =
-    GRPC_TRACER_INITIALIZER(false, "pick_first");
+grpc_core::TraceFlag grpc_lb_pick_first_trace(false, "pick_first");
 
 typedef struct pending_pick {
   struct pending_pick* next;
@@ -66,14 +65,14 @@
   grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker);
   gpr_free(p);
   grpc_subchannel_index_unref();
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Pick First %p destroyed.", (void*)p);
   }
 }
 
 static void shutdown_locked(grpc_exec_ctx* exec_ctx, pick_first_lb_policy* p,
                             grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Pick First %p Shutting down", p);
   }
   p->shutdown = true;
@@ -261,7 +260,7 @@
   }
   const grpc_lb_addresses* addresses =
       (const grpc_lb_addresses*)arg->value.pointer.p;
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_INFO, "Pick First %p received update with %lu addresses",
             (void*)p, (unsigned long)addresses->num_addresses);
   }
@@ -298,27 +297,27 @@
       grpc_lb_subchannel_data* sd = &subchannel_list->subchannels[i];
       if (sd->subchannel == p->selected->subchannel) {
         // The currently selected subchannel is in the update: we are done.
-        if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+        if (grpc_lb_pick_first_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "Pick First %p found already selected subchannel %p "
                   "at update index %" PRIuPTR " of %" PRIuPTR "; update done",
                   p, p->selected->subchannel, i,
                   subchannel_list->num_subchannels);
         }
-        grpc_lb_subchannel_list_ref_for_connectivity_watch(
-            subchannel_list, "connectivity_watch+replace_selected");
-        grpc_lb_subchannel_data_start_connectivity_watch(exec_ctx, sd);
-        if (p->subchannel_list != nullptr) {
-          grpc_lb_subchannel_list_shutdown_and_unref(
-              exec_ctx, p->subchannel_list, "pf_update_includes_selected");
-        }
-        p->subchannel_list = subchannel_list;
         if (p->selected->connected_subchannel != nullptr) {
           sd->connected_subchannel = GRPC_CONNECTED_SUBCHANNEL_REF(
               p->selected->connected_subchannel, "pf_update_includes_selected");
         }
         p->selected = sd;
+        if (p->subchannel_list != nullptr) {
+          grpc_lb_subchannel_list_shutdown_and_unref(
+              exec_ctx, p->subchannel_list, "pf_update_includes_selected");
+        }
+        p->subchannel_list = subchannel_list;
         destroy_unselected_subchannels_locked(exec_ctx, p);
+        grpc_lb_subchannel_list_ref_for_connectivity_watch(
+            subchannel_list, "connectivity_watch+replace_selected");
+        grpc_lb_subchannel_data_start_connectivity_watch(exec_ctx, sd);
         // If there was a previously pending update (which may or may
         // not have contained the currently selected subchannel), drop
         // it, so that it doesn't override what we've done here.
@@ -336,7 +335,7 @@
     // for it to report READY before swapping it into the current
     // subchannel list.
     if (p->latest_pending_subchannel_list != nullptr) {
-      if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+      if (grpc_lb_pick_first_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "Pick First %p Shutting down latest pending subchannel list "
                 "%p, about to be replaced by newer latest %p",
@@ -363,7 +362,7 @@
                                            grpc_error* error) {
   grpc_lb_subchannel_data* sd = (grpc_lb_subchannel_data*)arg;
   pick_first_lb_policy* p = (pick_first_lb_policy*)sd->subchannel_list->policy;
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "Pick First %p connectivity changed for subchannel %p (%" PRIuPTR
             " of %" PRIuPTR
@@ -460,7 +459,7 @@
           grpc_subchannel_get_connected_subchannel(sd->subchannel),
           "connected");
       p->selected = sd;
-      if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+      if (grpc_lb_pick_first_trace.enabled()) {
         gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", (void*)p,
                 (void*)sd->subchannel);
       }
@@ -472,7 +471,7 @@
         p->pending_picks = pp->next;
         *pp->target = GRPC_CONNECTED_SUBCHANNEL_REF(
             p->selected->connected_subchannel, "picked");
-        if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+        if (grpc_lb_pick_first_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "Servicing pending pick with selected subchannel %p",
                   (void*)p->selected);
@@ -571,7 +570,7 @@
                                          grpc_lb_policy_args* args) {
   GPR_ASSERT(args->client_channel_factory != nullptr);
   pick_first_lb_policy* p = (pick_first_lb_policy*)gpr_zalloc(sizeof(*p));
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Pick First %p created.", (void*)p);
   }
   pf_update_locked(exec_ctx, &p->base, args);
@@ -595,7 +594,6 @@
 
 extern "C" void grpc_lb_policy_pick_first_init() {
   grpc_register_lb_policy(pick_first_lb_factory_create());
-  grpc_register_tracer(&grpc_lb_pick_first_trace);
 }
 
 extern "C" void grpc_lb_policy_pick_first_shutdown() {}
diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
index 6ea1f02..5e54d1f 100644
--- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
@@ -39,8 +39,7 @@
 #include "src/core/lib/transport/connectivity_state.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-grpc_tracer_flag grpc_lb_round_robin_trace =
-    GRPC_TRACER_INITIALIZER(false, "round_robin");
+grpc_core::TraceFlag grpc_lb_round_robin_trace(false, "round_robin");
 
 /** List of entities waiting for a pick.
  *
@@ -101,7 +100,7 @@
 static size_t get_next_ready_subchannel_index_locked(
     const round_robin_lb_policy* p) {
   GPR_ASSERT(p->subchannel_list != nullptr);
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_INFO,
             "[RR %p] getting next ready subchannel (out of %lu), "
             "last_ready_subchannel_index=%lu",
@@ -111,7 +110,7 @@
   for (size_t i = 0; i < p->subchannel_list->num_subchannels; ++i) {
     const size_t index = (i + p->last_ready_subchannel_index + 1) %
                          p->subchannel_list->num_subchannels;
-    if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+    if (grpc_lb_round_robin_trace.enabled()) {
       gpr_log(
           GPR_DEBUG,
           "[RR %p] checking subchannel %p, subchannel_list %p, index %lu: "
@@ -123,7 +122,7 @@
     }
     if (p->subchannel_list->subchannels[index].curr_connectivity_state ==
         GRPC_CHANNEL_READY) {
-      if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+      if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "[RR %p] found next ready subchannel (%p) at index %lu of "
                 "subchannel_list %p",
@@ -134,7 +133,7 @@
       return index;
     }
   }
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] no subchannels in ready state", (void*)p);
   }
   return p->subchannel_list->num_subchannels;
@@ -145,7 +144,7 @@
                                                       size_t last_ready_index) {
   GPR_ASSERT(last_ready_index < p->subchannel_list->num_subchannels);
   p->last_ready_subchannel_index = last_ready_index;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "[RR %p] setting last_ready_subchannel_index=%lu (SC %p, CSC %p)",
             (void*)p, (unsigned long)last_ready_index,
@@ -157,7 +156,7 @@
 
 static void rr_destroy(grpc_exec_ctx* exec_ctx, grpc_lb_policy* pol) {
   round_robin_lb_policy* p = (round_robin_lb_policy*)pol;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] Destroying Round Robin policy at %p",
             (void*)pol, (void*)pol);
   }
@@ -170,7 +169,7 @@
 
 static void shutdown_locked(grpc_exec_ctx* exec_ctx, round_robin_lb_policy* p,
                             grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] Shutting down", p);
   }
   p->shutdown = true;
@@ -276,7 +275,7 @@
                           grpc_call_context_element* context, void** user_data,
                           grpc_closure* on_complete) {
   round_robin_lb_policy* p = (round_robin_lb_policy*)pol;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_INFO, "[RR %p] Trying to pick (shutdown: %d)", (void*)pol,
             p->shutdown);
   }
@@ -292,7 +291,7 @@
       if (user_data != nullptr) {
         *user_data = sd->user_data;
       }
-      if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+      if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(
             GPR_DEBUG,
             "[RR %p] Picked target <-- Subchannel %p (connected %p) (sl %p, "
@@ -393,7 +392,7 @@
                                 "rr_shutdown");
     p->shutdown = true;
     new_state = GRPC_CHANNEL_SHUTDOWN;
-    if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+    if (grpc_lb_round_robin_trace.enabled()) {
       gpr_log(GPR_INFO,
               "[RR %p] Shutting down: all subchannels have gone into shutdown",
               (void*)p);
@@ -419,7 +418,7 @@
   grpc_lb_subchannel_data* sd = (grpc_lb_subchannel_data*)arg;
   round_robin_lb_policy* p =
       (round_robin_lb_policy*)sd->subchannel_list->policy;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(
         GPR_DEBUG,
         "[RR %p] connectivity changed for subchannel %p, subchannel_list %p: "
@@ -484,7 +483,7 @@
         // for sds belonging to outdated subchannel lists.
         GPR_ASSERT(sd->subchannel_list == p->latest_pending_subchannel_list);
         GPR_ASSERT(!sd->subchannel_list->shutting_down);
-        if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+        if (grpc_lb_round_robin_trace.enabled()) {
           const unsigned long num_subchannels =
               p->subchannel_list != nullptr
                   ? (unsigned long)p->subchannel_list->num_subchannels
@@ -523,7 +522,7 @@
         if (pp->user_data != nullptr) {
           *pp->user_data = selected->user_data;
         }
-        if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+        if (grpc_lb_round_robin_trace.enabled()) {
           gpr_log(GPR_DEBUG,
                   "[RR %p] Fulfilling pending pick. Target <-- subchannel %p "
                   "(subchannel_list %p, index %lu)",
@@ -590,7 +589,7 @@
     return;
   }
   grpc_lb_addresses* addresses = (grpc_lb_addresses*)arg->value.pointer.p;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] received update with %" PRIuPTR " addresses", p,
             addresses->num_addresses);
   }
@@ -611,7 +610,7 @@
   }
   if (p->started_picking) {
     if (p->latest_pending_subchannel_list != nullptr) {
-      if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+      if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "[RR %p] Shutting down latest pending subchannel list %p, "
                 "about to be replaced by newer latest %p",
@@ -669,7 +668,7 @@
   grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE,
                                "round_robin");
   rr_update_locked(exec_ctx, &p->base, args);
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] Created with %lu subchannels", (void*)p,
             (unsigned long)p->subchannel_list->num_subchannels);
   }
@@ -691,7 +690,6 @@
 
 extern "C" void grpc_lb_policy_round_robin_init() {
   grpc_register_lb_policy(round_robin_lb_factory_create());
-  grpc_register_tracer(&grpc_lb_round_robin_trace);
 }
 
 extern "C" void grpc_lb_policy_round_robin_shutdown() {}
diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
index 27d9598..b6fce4d 100644
--- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
@@ -32,11 +32,11 @@
                                               grpc_lb_subchannel_data* sd,
                                               const char* reason) {
   if (sd->subchannel != nullptr) {
-    if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+    if (sd->subchannel_list->tracer->enabled()) {
       gpr_log(GPR_DEBUG,
               "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
               " (subchannel %p): unreffing subchannel",
-              sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+              sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
               sd->subchannel_list,
               (size_t)(sd - sd->subchannel_list->subchannels),
               sd->subchannel_list->num_subchannels, sd->subchannel);
@@ -58,11 +58,11 @@
 
 void grpc_lb_subchannel_data_start_connectivity_watch(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_data* sd) {
-  if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+  if (sd->subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): requesting connectivity change notification",
-            sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+            sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
             sd->subchannel_list,
             (size_t)(sd - sd->subchannel_list->subchannels),
             sd->subchannel_list->num_subchannels, sd->subchannel);
@@ -76,11 +76,11 @@
 
 void grpc_lb_subchannel_data_stop_connectivity_watch(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_data* sd) {
-  if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+  if (sd->subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): stopping connectivity watch",
-            sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+            sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
             sd->subchannel_list,
             (size_t)(sd - sd->subchannel_list->subchannels),
             sd->subchannel_list->num_subchannels, sd->subchannel);
@@ -90,15 +90,15 @@
 }
 
 grpc_lb_subchannel_list* grpc_lb_subchannel_list_create(
-    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_tracer_flag* tracer,
+    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_core::TraceFlag* tracer,
     const grpc_lb_addresses* addresses, const grpc_lb_policy_args* args,
     grpc_iomgr_cb_func connectivity_changed_cb) {
   grpc_lb_subchannel_list* subchannel_list =
       (grpc_lb_subchannel_list*)gpr_zalloc(sizeof(*subchannel_list));
-  if (GRPC_TRACER_ON(*tracer)) {
+  if (tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] Creating subchannel list %p for %" PRIuPTR " subchannels",
-            tracer->name, p, subchannel_list, addresses->num_addresses);
+            tracer->name(), p, subchannel_list, addresses->num_addresses);
   }
   subchannel_list->policy = p;
   subchannel_list->tracer = tracer;
@@ -128,24 +128,24 @@
     grpc_channel_args_destroy(exec_ctx, new_args);
     if (subchannel == nullptr) {
       // Subchannel could not be created.
-      if (GRPC_TRACER_ON(*tracer)) {
+      if (tracer->enabled()) {
         char* address_uri =
             grpc_sockaddr_to_uri(&addresses->addresses[i].address);
         gpr_log(GPR_DEBUG,
                 "[%s %p] could not create subchannel for address uri %s, "
                 "ignoring",
-                tracer->name, subchannel_list->policy, address_uri);
+                tracer->name(), subchannel_list->policy, address_uri);
         gpr_free(address_uri);
       }
       continue;
     }
-    if (GRPC_TRACER_ON(*tracer)) {
+    if (tracer->enabled()) {
       char* address_uri =
           grpc_sockaddr_to_uri(&addresses->addresses[i].address);
       gpr_log(GPR_DEBUG,
               "[%s %p] subchannel list %p index %" PRIuPTR
               ": Created subchannel %p for address uri %s",
-              tracer->name, p, subchannel_list, subchannel_index, subchannel,
+              tracer->name(), p, subchannel_list, subchannel_index, subchannel,
               address_uri);
       gpr_free(address_uri);
     }
@@ -174,9 +174,9 @@
 
 static void subchannel_list_destroy(grpc_exec_ctx* exec_ctx,
                                     grpc_lb_subchannel_list* subchannel_list) {
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG, "[%s %p] Destroying subchannel_list %p",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list);
   }
   for (size_t i = 0; i < subchannel_list->num_subchannels; i++) {
@@ -191,10 +191,10 @@
 void grpc_lb_subchannel_list_ref(grpc_lb_subchannel_list* subchannel_list,
                                  const char* reason) {
   gpr_ref_non_zero(&subchannel_list->refcount);
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count);
     gpr_log(GPR_DEBUG, "[%s %p] subchannel_list %p REF %lu->%lu (%s)",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list, (unsigned long)(count - 1), (unsigned long)count,
             reason);
   }
@@ -204,10 +204,10 @@
                                    grpc_lb_subchannel_list* subchannel_list,
                                    const char* reason) {
   const bool done = gpr_unref(&subchannel_list->refcount);
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count);
     gpr_log(GPR_DEBUG, "[%s %p] subchannel_list %p UNREF %lu->%lu (%s)",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list, (unsigned long)(count + 1), (unsigned long)count,
             reason);
   }
@@ -231,11 +231,11 @@
 
 static void subchannel_data_cancel_connectivity_watch(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_data* sd, const char* reason) {
-  if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+  if (sd->subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): canceling connectivity watch (%s)",
-            sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+            sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
             sd->subchannel_list,
             (size_t)(sd - sd->subchannel_list->subchannels),
             sd->subchannel_list->num_subchannels, sd->subchannel, reason);
@@ -248,9 +248,9 @@
 void grpc_lb_subchannel_list_shutdown_and_unref(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_list* subchannel_list,
     const char* reason) {
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG, "[%s %p] Shutting down subchannel_list %p (%s)",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list, reason);
   }
   GPR_ASSERT(!subchannel_list->shutting_down);
diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
index e18ad49..6538bd0 100644
--- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
+++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
@@ -88,7 +88,7 @@
   /** backpointer to owning policy */
   grpc_lb_policy* policy;
 
-  grpc_tracer_flag* tracer;
+  grpc_core::TraceFlag* tracer;
 
   /** all our subchannels */
   size_t num_subchannels;
@@ -121,7 +121,7 @@
 };
 
 grpc_lb_subchannel_list* grpc_lb_subchannel_list_create(
-    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_tracer_flag* tracer,
+    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_core::TraceFlag* tracer,
     const grpc_lb_addresses* addresses, const grpc_lb_policy_args* args,
     grpc_iomgr_cb_func connectivity_changed_cb);
 
diff --git a/src/core/ext/filters/client_channel/resolver.cc b/src/core/ext/filters/client_channel/resolver.cc
index 7e84b98..c16b151 100644
--- a/src/core/ext/filters/client_channel/resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver.cc
@@ -19,10 +19,8 @@
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/lib/iomgr/combiner.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_resolver_refcount =
-    GRPC_TRACER_INITIALIZER(false, "resolver_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount(false,
+                                                           "resolver_refcount");
 
 void grpc_resolver_init(grpc_resolver* resolver,
                         const grpc_resolver_vtable* vtable,
@@ -35,7 +33,7 @@
 #ifndef NDEBUG
 void grpc_resolver_ref(grpc_resolver* resolver, const char* file, int line,
                        const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) {
+  if (grpc_trace_resolver_refcount.enabled()) {
     gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "RESOLVER:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", resolver,
@@ -50,7 +48,7 @@
 #ifndef NDEBUG
 void grpc_resolver_unref(grpc_exec_ctx* exec_ctx, grpc_resolver* resolver,
                          const char* file, int line, const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) {
+  if (grpc_trace_resolver_refcount.enabled()) {
     gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "RESOLVER:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", resolver,
diff --git a/src/core/ext/filters/client_channel/resolver.h b/src/core/ext/filters/client_channel/resolver.h
index a0eb0bc..b5806ad 100644
--- a/src/core/ext/filters/client_channel/resolver.h
+++ b/src/core/ext/filters/client_channel/resolver.h
@@ -29,9 +29,7 @@
 typedef struct grpc_resolver grpc_resolver;
 typedef struct grpc_resolver_vtable grpc_resolver_vtable;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_resolver_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount;
 
 /** \a grpc_resolver provides \a grpc_channel_args objects to its caller */
 struct grpc_resolver {
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
index 7271559..7846576 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
@@ -431,7 +431,10 @@
   }
   if (service_config_json != nullptr) {
     grpc_ares_request_ref(r);
-    ares_search(*channel, hr->host, ns_c_in, ns_t_txt, on_txt_done_cb, r);
+    char* config_name;
+    gpr_asprintf(&config_name, "_grpc_config.%s", host);
+    ares_search(*channel, config_name, ns_c_in, ns_t_txt, on_txt_done_cb, r);
+    gpr_free(config_name);
   }
   /* TODO(zyc): Handle CNAME records here. */
   grpc_ares_ev_driver_start(exec_ctx, r->ev_driver);
diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc
index 2720e68..58e294d 100644
--- a/src/core/ext/filters/client_channel/subchannel.cc
+++ b/src/core/ext/filters/client_channel/subchannel.cc
@@ -199,7 +199,7 @@
   gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta)
                             : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) {
+  if (grpc_trace_stream_refcount.enabled()) {
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SUBCHANNEL: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c,
             purpose, old_val, old_val + delta, reason);
diff --git a/src/core/ext/filters/http/http_filters_plugin.cc b/src/core/ext/filters/http/http_filters_plugin.cc
index 69dbde4..ac31ace 100644
--- a/src/core/ext/filters/http/http_filters_plugin.cc
+++ b/src/core/ext/filters/http/http_filters_plugin.cc
@@ -65,7 +65,6 @@
 }
 
 extern "C" void grpc_http_filters_init(void) {
-  grpc_register_tracer(&grpc_compression_trace);
   grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    maybe_add_optional_filter, &compress_filter);
diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
index 8d95133..d070b56 100644
--- a/src/core/ext/filters/http/message_compress/message_compress_filter.cc
+++ b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
@@ -243,7 +243,7 @@
   bool did_compress = grpc_msg_compress(exec_ctx, calld->compression_algorithm,
                                         &calld->slices, &tmp);
   if (did_compress) {
-    if (GRPC_TRACER_ON(grpc_compression_trace)) {
+    if (grpc_compression_trace.enabled()) {
       const char* algo_name;
       const size_t before_size = calld->slices.length;
       const size_t after_size = tmp.length;
@@ -258,7 +258,7 @@
     grpc_slice_buffer_swap(&calld->slices, &tmp);
     send_flags |= GRPC_WRITE_INTERNAL_COMPRESS;
   } else {
-    if (GRPC_TRACER_ON(grpc_compression_trace)) {
+    if (grpc_compression_trace.enabled()) {
       const char* algo_name;
       GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm,
                                                  &algo_name));
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
index ac9ae5c..2569347 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
@@ -20,13 +20,6 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/transport/metadata.h"
 
-extern "C" void grpc_chttp2_plugin_init(void) {
-  grpc_register_tracer(&grpc_http_trace);
-  grpc_register_tracer(&grpc_flowctl_trace);
-  grpc_register_tracer(&grpc_trace_http2_stream_state);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_chttp2_refcount);
-#endif
-}
+extern "C" void grpc_chttp2_plugin_init(void) {}
 
 extern "C" void grpc_chttp2_plugin_shutdown(void) {}
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index 43788bf..5bd6478 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -90,13 +90,9 @@
 static int g_default_max_ping_strikes = DEFAULT_MAX_PING_STRIKES;
 
 #define MAX_CLIENT_STREAM_ID 0x7fffffffu
-grpc_tracer_flag grpc_http_trace = GRPC_TRACER_INITIALIZER(false, "http");
-grpc_tracer_flag grpc_flowctl_trace = GRPC_TRACER_INITIALIZER(false, "flowctl");
-
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_chttp2_refcount =
-    GRPC_TRACER_INITIALIZER(false, "chttp2_refcount");
-#endif
+grpc_core::TraceFlag grpc_http_trace(false, "http");
+grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount(false,
+                                                         "chttp2_refcount");
 
 /* forward declarations of various callbacks that we'll build closures around */
 static void write_action_begin_locked(grpc_exec_ctx* exec_ctx, void* t,
@@ -235,7 +231,7 @@
 void grpc_chttp2_unref_transport(grpc_exec_ctx* exec_ctx,
                                  grpc_chttp2_transport* t, const char* reason,
                                  const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) {
+  if (grpc_trace_chttp2_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count);
     gpr_log(GPR_DEBUG, "chttp2:unref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]",
             t, val, val - 1, reason, file, line);
@@ -246,7 +242,7 @@
 
 void grpc_chttp2_ref_transport(grpc_chttp2_transport* t, const char* reason,
                                const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) {
+  if (grpc_trace_chttp2_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count);
     gpr_log(GPR_DEBUG, "chttp2:  ref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]",
             t, val, val + 1, reason, file, line);
@@ -1237,7 +1233,7 @@
     return;
   }
   closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     const char* errstr = grpc_error_string(error);
     gpr_log(
         GPR_DEBUG,
@@ -1396,7 +1392,7 @@
 
   GRPC_STATS_INC_HTTP2_OP_BATCHES(exec_ctx);
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* str = grpc_transport_stream_op_batch_string(op);
     gpr_log(GPR_DEBUG, "perform_stream_op_locked: %s; on_complete = %p", str,
             op->on_complete);
@@ -1686,7 +1682,7 @@
     }
   }
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* str = grpc_transport_stream_op_batch_string(op);
     gpr_log(GPR_DEBUG, "perform_stream_op[s=%p]: %s", s, str);
     gpr_free(str);
@@ -2594,7 +2590,7 @@
 static void start_bdp_ping_locked(grpc_exec_ctx* exec_ctx, void* tp,
                                   grpc_error* error) {
   grpc_chttp2_transport* t = (grpc_chttp2_transport*)tp;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "%s: Start BDP ping err=%s", t->peer_string,
             grpc_error_string(error));
   }
@@ -2608,7 +2604,7 @@
 static void finish_bdp_ping_locked(grpc_exec_ctx* exec_ctx, void* tp,
                                    grpc_error* error) {
   grpc_chttp2_transport* t = (grpc_chttp2_transport*)tp;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "%s: Complete BDP ping err=%s", t->peer_string,
             grpc_error_string(error));
   }
@@ -3109,7 +3105,7 @@
       grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
     /* Channel with no active streams: send a goaway to try and make it
      * disconnect cleanly */
-    if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+    if (grpc_resource_quota_trace.enabled()) {
       gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory",
               t->peer_string);
     }
@@ -3117,8 +3113,7 @@
                 grpc_error_set_int(
                     GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"),
                     GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM));
-  } else if (error == GRPC_ERROR_NONE &&
-             GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  } else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
             " streams",
@@ -3140,7 +3135,7 @@
   if (error == GRPC_ERROR_NONE && n > 0) {
     grpc_chttp2_stream* s =
         (grpc_chttp2_stream*)grpc_chttp2_stream_map_rand(&t->stream_map);
-    if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+    if (grpc_resource_quota_trace.enabled()) {
       gpr_log(GPR_DEBUG, "HTTP2: %s - abandon stream id %d", t->peer_string,
               s->id);
     }
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
index 972104f..54abbe6 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
@@ -23,18 +23,14 @@
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/transport/transport.h"
 
+extern grpc_core::TraceFlag grpc_http_trace;
+extern grpc_core::TraceFlag grpc_trace_http2_stream_state;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-extern grpc_tracer_flag grpc_http_trace;
-extern grpc_tracer_flag grpc_flowctl_trace;
-extern grpc_tracer_flag grpc_trace_http2_stream_state;
-
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_chttp2_refcount;
-#endif
-
 grpc_transport* grpc_create_chttp2_transport(
     grpc_exec_ctx* exec_ctx, const grpc_channel_args* channel_args,
     grpc_endpoint* ep, int is_client);
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc
index 64f6b3c..8a057bd 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.cc
+++ b/src/core/ext/transport/chttp2/transport/flow_control.cc
@@ -31,6 +31,8 @@
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 #include "src/core/lib/support/string.h"
 
+grpc_core::TraceFlag grpc_flowctl_trace(false, "flowctl");
+
 namespace grpc_core {
 namespace chttp2 {
 
@@ -312,7 +314,9 @@
   double bdp_error = value - pid_controller_.last_control_value();
   const double dt = (double)(now - last_pid_update_) * 1e-3;
   last_pid_update_ = now;
-  return pid_controller_.Update(bdp_error, dt);
+  // Limit dt to 100ms
+  const double kMaxDt = 0.1;
+  return pid_controller_.Update(bdp_error, dt > kMaxDt ? kMaxDt : dt);
 }
 
 FlowControlAction::Urgency TransportFlowControl::DeltaUrgency(
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.h b/src/core/ext/transport/chttp2/transport/flow_control.h
index 7dd348e..bb710fe 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.h
+++ b/src/core/ext/transport/chttp2/transport/flow_control.h
@@ -30,7 +30,7 @@
 struct grpc_chttp2_transport;
 struct grpc_chttp2_stream;
 
-extern "C" grpc_tracer_flag grpc_flowctl_trace;
+extern grpc_core::TraceFlag grpc_flowctl_trace;
 
 namespace grpc {
 namespace testing {
@@ -118,7 +118,7 @@
             StreamFlowControl* sfc);
   void Finish();
 
-  const bool enabled_ = GRPC_TRACER_ON(grpc_flowctl_trace);
+  const bool enabled_ = grpc_flowctl_trace.enabled();
 
   TransportFlowControl* tfc_;
   StreamFlowControl* sfc_;
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.cc b/src/core/ext/transport/chttp2/transport/frame_settings.cc
index d33da72..75bb78d 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.cc
@@ -204,20 +204,19 @@
               parser->incoming_settings[id] != parser->value) {
             t->initial_window_update +=
                 (int64_t)parser->value - parser->incoming_settings[id];
-            if (GRPC_TRACER_ON(grpc_http_trace) ||
-                GRPC_TRACER_ON(grpc_flowctl_trace)) {
+            if (grpc_http_trace.enabled() || grpc_flowctl_trace.enabled()) {
               gpr_log(GPR_DEBUG, "%p[%s] adding %d for initial_window change",
                       t, t->is_client ? "cli" : "svr",
                       (int)t->initial_window_update);
             }
           }
           parser->incoming_settings[id] = parser->value;
-          if (GRPC_TRACER_ON(grpc_http_trace)) {
+          if (grpc_http_trace.enabled()) {
             gpr_log(GPR_DEBUG, "CHTTP2:%s:%s: got setting %s = %d",
                     t->is_client ? "CLI" : "SVR", t->peer_string, sp->name,
                     parser->value);
           }
-        } else if (GRPC_TRACER_ON(grpc_http_trace)) {
+        } else if (grpc_http_trace.enabled()) {
           gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)",
                   parser->id, parser->value);
         }
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
index e6e4ff2..e76d92e 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
@@ -57,8 +57,6 @@
     {{nullptr, 0}}            /* data.refcounted */
 };
 
-extern "C" grpc_tracer_flag grpc_http_trace;
-
 typedef struct {
   int is_first_frame;
   /* number of bytes in 'output' when we started the frame - used to calculate
@@ -475,7 +473,7 @@
         "Reserved header (colon-prefixed) happening after regular ones.");
   }
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* k = grpc_slice_to_c_string(GRPC_MDKEY(elem));
     char* v = nullptr;
     if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
@@ -669,7 +667,7 @@
     }
   }
   c->advertise_table_size_change = 1;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "set max table size from encoder to %d", max_table_size);
   }
 }
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
index fd01d16..96d8e99 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -34,6 +34,8 @@
 /* maximum table size we'll actually use */
 #define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024)
 
+extern grpc_core::TraceFlag grpc_http_trace;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
index 960610e..18cb27f 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
@@ -651,7 +651,7 @@
 /* emission helpers */
 static grpc_error* on_hdr(grpc_exec_ctx* exec_ctx, grpc_chttp2_hpack_parser* p,
                           grpc_mdelem md, int add_to_table) {
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* k = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* v = nullptr;
     if (grpc_is_binary_header(GRPC_MDKEY(md))) {
@@ -1047,7 +1047,7 @@
 static grpc_error* finish_max_tbl_size(grpc_exec_ctx* exec_ctx,
                                        grpc_chttp2_hpack_parser* p,
                                        const uint8_t* cur, const uint8_t* end) {
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
   }
   grpc_error* err =
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.cc b/src/core/ext/transport/chttp2/transport/hpack_table.cc
index 7970d2a..75b83b8 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.cc
@@ -28,7 +28,7 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/support/murmur_hash.h"
 
-extern "C" grpc_tracer_flag grpc_http_trace;
+extern grpc_core::TraceFlag grpc_http_trace;
 
 static struct {
   const char* key;
@@ -246,7 +246,7 @@
   if (tbl->max_bytes == max_bytes) {
     return;
   }
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes);
   }
   while (tbl->mem_used > max_bytes) {
@@ -270,7 +270,7 @@
     gpr_free(msg);
     return err;
   }
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes);
   }
   while (tbl->mem_used > bytes) {
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 60cc280..4555b7b 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -674,13 +674,13 @@
 #define GRPC_CHTTP2_CLIENT_CONNECT_STRLEN \
   (sizeof(GRPC_CHTTP2_CLIENT_CONNECT_STRING) - 1)
 
-extern grpc_tracer_flag grpc_http_trace;
-extern grpc_tracer_flag grpc_flowctl_trace;
+// extern grpc_core::TraceFlag grpc_http_trace;
+// extern grpc_core::TraceFlag grpc_flowctl_trace;
 
-#define GRPC_CHTTP2_IF_TRACING(stmt)      \
-  if (!(GRPC_TRACER_ON(grpc_http_trace))) \
-    ;                                     \
-  else                                    \
+#define GRPC_CHTTP2_IF_TRACING(stmt) \
+  if (!(grpc_http_trace.enabled()))  \
+    ;                                \
+  else                               \
     stmt
 
 void grpc_chttp2_fake_status(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t,
diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc
index 5f07dec..46ec3fb 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.cc
+++ b/src/core/ext/transport/chttp2/transport/parsing.cc
@@ -312,7 +312,7 @@
     case GRPC_CHTTP2_FRAME_GOAWAY:
       return init_goaway_parser(exec_ctx, t);
     default:
-      if (GRPC_TRACER_ON(grpc_http_trace)) {
+      if (grpc_http_trace.enabled()) {
         gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type);
       }
       return init_skip_frame_parser(exec_ctx, t, 0);
@@ -417,7 +417,7 @@
 
   GPR_ASSERT(s != nullptr);
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* value =
         grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -501,7 +501,7 @@
 
   GPR_ASSERT(s != nullptr);
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* value =
         grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -758,7 +758,7 @@
   if (err == GRPC_ERROR_NONE) {
     return err;
   } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, nullptr)) {
-    if (GRPC_TRACER_ON(grpc_http_trace)) {
+    if (grpc_http_trace.enabled()) {
       const char* msg = grpc_error_string(err);
       gpr_log(GPR_ERROR, "%s", msg);
     }
diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.cc b/src/core/ext/transport/chttp2/transport/stream_lists.cc
index d92527f..c95d025 100644
--- a/src/core/ext/transport/chttp2/transport/stream_lists.cc
+++ b/src/core/ext/transport/chttp2/transport/stream_lists.cc
@@ -39,8 +39,7 @@
   GPR_UNREACHABLE_CODE(return "unknown");
 }
 
-grpc_tracer_flag grpc_trace_http2_stream_state =
-    GRPC_TRACER_INITIALIZER(false, "http2_stream_state");
+grpc_core::TraceFlag grpc_trace_http2_stream_state(false, "http2_stream_state");
 
 /* core list management */
 
@@ -66,7 +65,7 @@
     s->included[id] = 0;
   }
   *stream = s;
-  if (s && GRPC_TRACER_ON(grpc_trace_http2_stream_state)) {
+  if (s && grpc_trace_http2_stream_state.enabled()) {
     gpr_log(GPR_DEBUG, "%p[%d][%s]: pop from %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
@@ -88,7 +87,7 @@
   } else {
     t->lists[id].tail = s->links[id].prev;
   }
-  if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) {
+  if (grpc_trace_http2_stream_state.enabled()) {
     gpr_log(GPR_DEBUG, "%p[%d][%s]: remove from %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
@@ -120,7 +119,7 @@
   }
   t->lists[id].tail = s;
   s->included[id] = 1;
-  if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) {
+  if (grpc_trace_http2_stream_state.enabled()) {
     gpr_log(GPR_DEBUG, "%p[%d][%s]: add to %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc
index 61a2598..15869b8 100644
--- a/src/core/ext/transport/chttp2/transport/writing.cc
+++ b/src/core/ext/transport/chttp2/transport/writing.cc
@@ -51,8 +51,7 @@
   }
   if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) {
     /* ping already in-flight: wait */
-    if (GRPC_TRACER_ON(grpc_http_trace) ||
-        GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "%s: Ping delayed [%p]: already pinging",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string);
     }
@@ -61,8 +60,7 @@
   if (t->ping_state.pings_before_data_required == 0 &&
       t->ping_policy.max_pings_without_data != 0) {
     /* need to receive something of substance before sending a ping again */
-    if (GRPC_TRACER_ON(grpc_http_trace) ||
-        GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "%s: Ping delayed [%p]: too many recent pings: %d/%d",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string,
               t->ping_state.pings_before_data_required,
@@ -81,8 +79,7 @@
   }
   if (next_allowed_ping > now) {
     /* not enough elapsed time between successive pings */
-    if (GRPC_TRACER_ON(grpc_http_trace) ||
-        GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "%s: Ping delayed [%p]: not enough time elapsed since last ping",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string);
@@ -103,8 +100,7 @@
                         grpc_chttp2_ping_create(false, pq->inflight_id));
   GRPC_STATS_INC_HTTP2_PINGS_SENT(exec_ctx);
   t->ping_state.last_ping_sent_time = now;
-  if (GRPC_TRACER_ON(grpc_http_trace) ||
-      GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+  if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
     gpr_log(GPR_DEBUG, "%s: Ping sent [%p]: %d/%d",
             t->is_client ? "CLIENT" : "SERVER", t->peer_string,
             t->ping_state.pings_before_data_required,
@@ -322,7 +318,7 @@
         GPR_MIN(stream_remote_window(), t_->flow_control->remote_window()));
   }
 
-  bool AnyOutgoing() const { return max_outgoing() != 0; }
+  bool AnyOutgoing() const { return max_outgoing() > 0; }
 
   void FlushCompressedBytes() {
     uint32_t send_bytes =
diff --git a/src/core/ext/transport/inproc/inproc_plugin.cc b/src/core/ext/transport/inproc/inproc_plugin.cc
index 5d8a1c7..2526dbf 100644
--- a/src/core/ext/transport/inproc/inproc_plugin.cc
+++ b/src/core/ext/transport/inproc/inproc_plugin.cc
@@ -19,12 +19,9 @@
 #include "src/core/ext/transport/inproc/inproc_transport.h"
 #include "src/core/lib/debug/trace.h"
 
-grpc_tracer_flag grpc_inproc_trace = GRPC_TRACER_INITIALIZER(false, "inproc");
+grpc_core::TraceFlag grpc_inproc_trace(false, "inproc");
 
-extern "C" void grpc_inproc_plugin_init(void) {
-  grpc_register_tracer(&grpc_inproc_trace);
-  grpc_inproc_transport_init();
-}
+extern "C" void grpc_inproc_plugin_init(void) { grpc_inproc_transport_init(); }
 
 extern "C" void grpc_inproc_plugin_shutdown(void) {
   grpc_inproc_transport_shutdown();
diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc
index 7ea08b0..2579060 100644
--- a/src/core/ext/transport/inproc/inproc_transport.cc
+++ b/src/core/ext/transport/inproc/inproc_transport.cc
@@ -32,9 +32,9 @@
 #include "src/core/lib/transport/error_utils.h"
 #include "src/core/lib/transport/transport_impl.h"
 
-#define INPROC_LOG(...)                                          \
-  do {                                                           \
-    if (GRPC_TRACER_ON(grpc_inproc_trace)) gpr_log(__VA_ARGS__); \
+#define INPROC_LOG(...)                                    \
+  do {                                                     \
+    if (grpc_inproc_trace.enabled()) gpr_log(__VA_ARGS__); \
   } while (0)
 
 static grpc_slice g_empty_slice;
@@ -199,7 +199,7 @@
                                     const grpc_metadata_batch* metadata,
                                     uint32_t flags, grpc_metadata_batch* out_md,
                                     uint32_t* outflags, bool* markfilled) {
-  if (GRPC_TRACER_ON(grpc_inproc_trace)) {
+  if (grpc_inproc_trace.enabled()) {
     log_metadata(metadata, s->t->is_client, outflags != nullptr);
   }
 
@@ -873,7 +873,7 @@
   gpr_mu* mu = &s->t->mu->mu;  // save aside in case s gets closed
   gpr_mu_lock(mu);
 
-  if (GRPC_TRACER_ON(grpc_inproc_trace)) {
+  if (grpc_inproc_trace.enabled()) {
     if (op->send_initial_metadata) {
       log_metadata(op->payload->send_initial_metadata.send_initial_metadata,
                    s->t->is_client, true);
diff --git a/src/core/ext/transport/inproc/inproc_transport.h b/src/core/ext/transport/inproc/inproc_transport.h
index 6e83af3..f27789a 100644
--- a/src/core/ext/transport/inproc/inproc_transport.h
+++ b/src/core/ext/transport/inproc/inproc_transport.h
@@ -29,7 +29,7 @@
                                          grpc_channel_args* args,
                                          void* reserved);
 
-extern grpc_tracer_flag grpc_inproc_trace;
+extern grpc_core::TraceFlag grpc_inproc_trace;
 
 void grpc_inproc_transport_init(void);
 void grpc_inproc_transport_shutdown(void);
diff --git a/src/core/lib/channel/channel_stack.cc b/src/core/lib/channel/channel_stack.cc
index 6e50809..7629d18 100644
--- a/src/core/lib/channel/channel_stack.cc
+++ b/src/core/lib/channel/channel_stack.cc
@@ -23,7 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-grpc_tracer_flag grpc_trace_channel = GRPC_TRACER_INITIALIZER(false, "channel");
+grpc_core::TraceFlag grpc_trace_channel(false, "channel");
 
 /* Memory layouts.
 
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index aa99311..6b41ad1 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -285,10 +285,10 @@
                       grpc_call_element* elem,
                       grpc_transport_stream_op_batch* op);
 
-extern grpc_tracer_flag grpc_trace_channel;
+extern grpc_core::TraceFlag grpc_trace_channel;
 
 #define GRPC_CALL_LOG_OP(sev, elem, op) \
-  if (GRPC_TRACER_ON(grpc_trace_channel)) grpc_call_log_op(sev, elem, op)
+  if (grpc_trace_channel.enabled()) grpc_call_log_op(sev, elem, op)
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/channel/channel_stack_builder.cc b/src/core/lib/channel/channel_stack_builder.cc
index 1e553bf..77b7854 100644
--- a/src/core/lib/channel/channel_stack_builder.cc
+++ b/src/core/lib/channel/channel_stack_builder.cc
@@ -23,8 +23,8 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/string_util.h>
 
-grpc_tracer_flag grpc_trace_channel_stack_builder =
-    GRPC_TRACER_INITIALIZER(false, "channel_stack_builder");
+grpc_core::TraceFlag grpc_trace_channel_stack_builder(false,
+                                                      "channel_stack_builder");
 
 typedef struct filter_node {
   struct filter_node* next;
diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h
index 23134b7..8e3ec2e 100644
--- a/src/core/lib/channel/channel_stack_builder.h
+++ b/src/core/lib/channel/channel_stack_builder.h
@@ -160,7 +160,7 @@
 void grpc_channel_stack_builder_destroy(grpc_exec_ctx* exec_ctx,
                                         grpc_channel_stack_builder* builder);
 
-extern grpc_tracer_flag grpc_trace_channel_stack_builder;
+extern grpc_core::TraceFlag grpc_trace_channel_stack_builder;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/debug/trace.cc b/src/core/lib/debug/trace.cc
index 9c75ef1..4c63983 100644
--- a/src/core/lib/debug/trace.cc
+++ b/src/core/lib/debug/trace.cc
@@ -27,26 +27,61 @@
 
 int grpc_tracer_set_enabled(const char* name, int enabled);
 
-typedef struct tracer {
-  grpc_tracer_flag* flag;
-  struct tracer* next;
-} tracer;
-static tracer* tracers;
+namespace grpc_core {
 
-#ifdef GRPC_THREADSAFE_TRACER
-#define TRACER_SET(flag, on) gpr_atm_no_barrier_store(&(flag).value, (on))
-#else
-#define TRACER_SET(flag, on) (flag).value = (on)
-#endif
+TraceFlag* TraceFlagList::root_tracer_ = nullptr;
 
-void grpc_register_tracer(grpc_tracer_flag* flag) {
-  tracer* t = (tracer*)gpr_malloc(sizeof(*t));
-  t->flag = flag;
-  t->next = tracers;
-  TRACER_SET(*flag, false);
-  tracers = t;
+bool TraceFlagList::Set(const char* name, bool enabled) {
+  TraceFlag* t;
+  if (0 == strcmp(name, "all")) {
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      t->set_enabled(enabled);
+    }
+  } else if (0 == strcmp(name, "list_tracers")) {
+    LogAllTracers();
+  } else if (0 == strcmp(name, "refcount")) {
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      if (strstr(t->name_, "refcount") != nullptr) {
+        t->set_enabled(enabled);
+      }
+    }
+  } else {
+    bool found = false;
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      if (0 == strcmp(name, t->name_)) {
+        t->set_enabled(enabled);
+        found = true;
+      }
+    }
+    if (!found) {
+      gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name);
+      return false; /* early return */
+    }
+  }
+  return true;
 }
 
+void TraceFlagList::Add(TraceFlag* flag) {
+  flag->next_tracer_ = root_tracer_;
+  root_tracer_ = flag;
+}
+
+void TraceFlagList::LogAllTracers() {
+  gpr_log(GPR_DEBUG, "available tracers:");
+  TraceFlag* t;
+  for (t = root_tracer_; t != nullptr; t = t->next_tracer_) {
+    gpr_log(GPR_DEBUG, "\t%s", t->name_);
+  }
+}
+
+// Flags register themselves on the list during construction
+TraceFlag::TraceFlag(bool default_enabled, const char* name)
+    : name_(name), value_(default_enabled) {
+  TraceFlagList::Add(this);
+}
+
+}  // namespace grpc_core
+
 static void add(const char* beg, const char* end, char*** ss, size_t* ns) {
   size_t n = *ns;
   size_t np = n + 1;
@@ -80,9 +115,9 @@
 
   for (i = 0; i < nstrings; i++) {
     if (strings[i][0] == '-') {
-      grpc_tracer_set_enabled(strings[i] + 1, 0);
+      grpc_core::TraceFlagList::Set(strings[i] + 1, false);
     } else {
-      grpc_tracer_set_enabled(strings[i], 1);
+      grpc_core::TraceFlagList::Set(strings[i], true);
     }
   }
 
@@ -92,14 +127,6 @@
   gpr_free(strings);
 }
 
-static void list_tracers() {
-  gpr_log(GPR_DEBUG, "available tracers:");
-  tracer* t;
-  for (t = tracers; t; t = t->next) {
-    gpr_log(GPR_DEBUG, "\t%s", t->flag->name);
-  }
-}
-
 void grpc_tracer_init(const char* env_var) {
   char* e = gpr_getenv(env_var);
   if (e != nullptr) {
@@ -108,40 +135,8 @@
   }
 }
 
-void grpc_tracer_shutdown(void) {
-  while (tracers) {
-    tracer* t = tracers;
-    tracers = t->next;
-    gpr_free(t);
-  }
-}
+void grpc_tracer_shutdown(void) {}
 
 int grpc_tracer_set_enabled(const char* name, int enabled) {
-  tracer* t;
-  if (0 == strcmp(name, "all")) {
-    for (t = tracers; t; t = t->next) {
-      TRACER_SET(*t->flag, enabled);
-    }
-  } else if (0 == strcmp(name, "list_tracers")) {
-    list_tracers();
-  } else if (0 == strcmp(name, "refcount")) {
-    for (t = tracers; t; t = t->next) {
-      if (strstr(t->flag->name, "refcount") != nullptr) {
-        TRACER_SET(*t->flag, enabled);
-      }
-    }
-  } else {
-    int found = 0;
-    for (t = tracers; t; t = t->next) {
-      if (0 == strcmp(name, t->flag->name)) {
-        TRACER_SET(*t->flag, enabled);
-        found = 1;
-      }
-    }
-    if (!found) {
-      gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name);
-      return 0; /* early return */
-    }
-  }
-  return 1;
+  return grpc_core::TraceFlagList::Set(name, enabled != 0);
 }
diff --git a/src/core/lib/debug/trace.h b/src/core/lib/debug/trace.h
index 7447d5d..b58c16f 100644
--- a/src/core/lib/debug/trace.h
+++ b/src/core/lib/debug/trace.h
@@ -27,32 +27,6 @@
 extern "C" {
 #endif
 
-#if defined(__has_feature)
-#if __has_feature(thread_sanitizer)
-#define GRPC_THREADSAFE_TRACER
-#endif
-#endif
-
-typedef struct {
-#ifdef GRPC_THREADSAFE_TRACER
-  gpr_atm value;
-#else
-  bool value;
-#endif
-  const char* name;
-} grpc_tracer_flag;
-
-#ifdef GRPC_THREADSAFE_TRACER
-#define GRPC_TRACER_ON(flag) (gpr_atm_no_barrier_load(&(flag).value) != 0)
-#define GRPC_TRACER_INITIALIZER(on, name) \
-  { (gpr_atm)(on), (name) }
-#else
-#define GRPC_TRACER_ON(flag) ((flag).value)
-#define GRPC_TRACER_INITIALIZER(on, name) \
-  { (on), (name) }
-#endif
-
-void grpc_register_tracer(grpc_tracer_flag* flag);
 void grpc_tracer_init(const char* env_var_name);
 void grpc_tracer_shutdown(void);
 
@@ -60,4 +34,82 @@
 }
 #endif
 
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#define GRPC_THREADSAFE_TRACER
+#endif
+#endif
+
+#ifdef __cplusplus
+
+namespace grpc_core {
+
+class TraceFlag;
+class TraceFlagList {
+ public:
+  static bool Set(const char* name, bool enabled);
+  static void Add(TraceFlag* flag);
+
+ private:
+  static void LogAllTracers();
+  static TraceFlag* root_tracer_;
+};
+
+namespace testing {
+void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag);
+}
+
+class TraceFlag {
+ public:
+  TraceFlag(bool default_enabled, const char* name);
+  ~TraceFlag() {}
+
+  const char* name() const { return name_; }
+
+  bool enabled() {
+#ifdef GRPC_THREADSAFE_TRACER
+    return gpr_atm_no_barrier_load(&value_) != 0;
+#else
+    return value_;
+#endif
+  }
+
+ private:
+  friend void grpc_core::testing::grpc_tracer_enable_flag(TraceFlag* flag);
+  friend class TraceFlagList;
+
+  void set_enabled(bool enabled) {
+#ifdef GRPC_THREADSAFE_TRACER
+    gpr_atm_no_barrier_store(&value_, enabled);
+#else
+    value_ = enabled;
+#endif
+  }
+
+  TraceFlag* next_tracer_;
+  const char* const name_;
+#ifdef GRPC_THREADSAFE_TRACER
+  gpr_atm value_;
+#else
+  bool value_;
+#endif
+};
+
+#ifndef NDEBUG
+typedef TraceFlag DebugOnlyTraceFlag;
+#else
+class DebugOnlyTraceFlag {
+ public:
+  DebugOnlyTraceFlag(bool default_enabled, const char* name) {}
+  bool enabled() { return false; }
+
+ private:
+  void set_enabled(bool enabled) {}
+};
+#endif
+
+}  // namespace grpc_core
+
+#endif  // __cplusplus
+
 #endif /* GRPC_CORE_LIB_DEBUG_TRACE_H */
diff --git a/src/core/lib/http/parser.cc b/src/core/lib/http/parser.cc
index 9134b0c..fb4eb23 100644
--- a/src/core/lib/http/parser.cc
+++ b/src/core/lib/http/parser.cc
@@ -25,7 +25,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
-grpc_tracer_flag grpc_http1_trace = GRPC_TRACER_INITIALIZER(false, "http1");
+grpc_core::TraceFlag grpc_http1_trace(false, "http1");
 
 static char* buf2str(void* buffer, size_t length) {
   char* out = (char*)gpr_malloc(length + 1);
@@ -294,7 +294,7 @@
     case GRPC_HTTP_FIRST_LINE:
     case GRPC_HTTP_HEADERS:
       if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) {
-        if (GRPC_TRACER_ON(grpc_http1_trace))
+        if (grpc_http1_trace.enabled())
           gpr_log(GPR_ERROR, "HTTP header max line length (%d) exceeded",
                   GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
         return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
index 3d28481..391bd35 100644
--- a/src/core/lib/http/parser.h
+++ b/src/core/lib/http/parser.h
@@ -111,7 +111,7 @@
 void grpc_http_request_destroy(grpc_http_request* request);
 void grpc_http_response_destroy(grpc_http_response* response);
 
-extern grpc_tracer_flag grpc_http1_trace;
+extern grpc_core::TraceFlag grpc_http1_trace;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/iomgr/call_combiner.cc b/src/core/lib/iomgr/call_combiner.cc
index 74b077d..b5910b4 100644
--- a/src/core/lib/iomgr/call_combiner.cc
+++ b/src/core/lib/iomgr/call_combiner.cc
@@ -24,8 +24,7 @@
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/profiling/timers.h"
 
-grpc_tracer_flag grpc_call_combiner_trace =
-    GRPC_TRACER_INITIALIZER(false, "call_combiner");
+grpc_core::TraceFlag grpc_call_combiner_trace(false, "call_combiner");
 
 static grpc_error* decode_cancel_state_error(gpr_atm cancel_state) {
   if (cancel_state & 1) {
@@ -63,7 +62,7 @@
                               grpc_error* error DEBUG_ARGS,
                               const char* reason) {
   GPR_TIMER_BEGIN("call_combiner_start", 0);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "==> grpc_call_combiner_start() [%p] closure=%p [" DEBUG_FMT_STR
             "%s] error=%s",
@@ -72,7 +71,7 @@
   }
   size_t prev_size =
       (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)1);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
             prev_size + 1);
   }
@@ -80,13 +79,13 @@
   if (prev_size == 0) {
     GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED(exec_ctx);
     GPR_TIMER_MARK("call_combiner_initiate", 0);
-    if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+    if (grpc_call_combiner_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  EXECUTING IMMEDIATELY");
     }
     // Queue was empty, so execute this closure immediately.
     GRPC_CLOSURE_SCHED(exec_ctx, closure, error);
   } else {
-    if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+    if (grpc_call_combiner_trace.enabled()) {
       gpr_log(GPR_INFO, "  QUEUING");
     }
     // Queue was not empty, so add closure to queue.
@@ -100,21 +99,21 @@
                              grpc_call_combiner* call_combiner DEBUG_ARGS,
                              const char* reason) {
   GPR_TIMER_BEGIN("call_combiner_stop", 0);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "==> grpc_call_combiner_stop() [%p] [" DEBUG_FMT_STR "%s]",
             call_combiner DEBUG_FMT_ARGS, reason);
   }
   size_t prev_size =
       (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)-1);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
             prev_size - 1);
   }
   GPR_ASSERT(prev_size >= 1);
   if (prev_size > 1) {
     while (true) {
-      if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+      if (grpc_call_combiner_trace.enabled()) {
         gpr_log(GPR_DEBUG, "  checking queue");
       }
       bool empty;
@@ -123,19 +122,19 @@
       if (closure == nullptr) {
         // This can happen either due to a race condition within the mpscq
         // code or because of a race with grpc_call_combiner_start().
-        if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+        if (grpc_call_combiner_trace.enabled()) {
           gpr_log(GPR_DEBUG, "  queue returned no result; checking again");
         }
         continue;
       }
-      if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+      if (grpc_call_combiner_trace.enabled()) {
         gpr_log(GPR_DEBUG, "  EXECUTING FROM QUEUE: closure=%p error=%s",
                 closure, grpc_error_string(closure->error_data.error));
       }
       GRPC_CLOSURE_SCHED(exec_ctx, closure, closure->error_data.error);
       break;
     }
-  } else if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  } else if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  queue empty");
   }
   GPR_TIMER_END("call_combiner_stop", 0);
@@ -152,7 +151,7 @@
     // If error is set, invoke the cancellation closure immediately.
     // Otherwise, store the new closure.
     if (original_error != GRPC_ERROR_NONE) {
-      if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+      if (grpc_call_combiner_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "call_combiner=%p: scheduling notify_on_cancel callback=%p "
                 "for pre-existing cancellation",
@@ -163,7 +162,7 @@
     } else {
       if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state,
                            (gpr_atm)closure)) {
-        if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+        if (grpc_call_combiner_trace.enabled()) {
           gpr_log(GPR_DEBUG, "call_combiner=%p: setting notify_on_cancel=%p",
                   call_combiner, closure);
         }
@@ -172,7 +171,7 @@
         // up any resources they may be holding for the callback.
         if (original_state != 0) {
           closure = (grpc_closure*)original_state;
-          if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+          if (grpc_call_combiner_trace.enabled()) {
             gpr_log(GPR_DEBUG,
                     "call_combiner=%p: scheduling old cancel callback=%p",
                     call_combiner, closure);
@@ -201,7 +200,7 @@
                          encode_cancel_state_error(error))) {
       if (original_state != 0) {
         grpc_closure* notify_on_cancel = (grpc_closure*)original_state;
-        if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+        if (grpc_call_combiner_trace.enabled()) {
           gpr_log(GPR_DEBUG,
                   "call_combiner=%p: scheduling notify_on_cancel callback=%p",
                   call_combiner, notify_on_cancel);
diff --git a/src/core/lib/iomgr/call_combiner.h b/src/core/lib/iomgr/call_combiner.h
index 527f84f..77420fa 100644
--- a/src/core/lib/iomgr/call_combiner.h
+++ b/src/core/lib/iomgr/call_combiner.h
@@ -40,7 +40,7 @@
 // when it is done with the action that was kicked off by the original
 // callback.
 
-extern grpc_tracer_flag grpc_call_combiner_trace;
+extern grpc_core::TraceFlag grpc_call_combiner_trace;
 
 typedef struct {
   gpr_atm size;  // size_t, num closures in queue or currently executing
diff --git a/src/core/lib/iomgr/closure.cc b/src/core/lib/iomgr/closure.cc
deleted file mode 100644
index 09257d2..0000000
--- a/src/core/lib/iomgr/closure.cc
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- *
- * Copyright 2015 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include "src/core/lib/iomgr/closure.h"
-
-#include <assert.h>
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-
-#include "src/core/lib/profiling/timers.h"
-
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_closure = GRPC_TRACER_INITIALIZER(false, "closure");
-#endif
-
-#ifndef NDEBUG
-grpc_closure* grpc_closure_init(const char* file, int line,
-                                grpc_closure* closure, grpc_iomgr_cb_func cb,
-                                void* cb_arg,
-                                grpc_closure_scheduler* scheduler) {
-#else
-grpc_closure* grpc_closure_init(grpc_closure* closure, grpc_iomgr_cb_func cb,
-                                void* cb_arg,
-                                grpc_closure_scheduler* scheduler) {
-#endif
-  closure->cb = cb;
-  closure->cb_arg = cb_arg;
-  closure->scheduler = scheduler;
-#ifndef NDEBUG
-  closure->scheduled = false;
-  closure->file_initiated = nullptr;
-  closure->line_initiated = 0;
-  closure->run = false;
-  closure->file_created = file;
-  closure->line_created = line;
-#endif
-  return closure;
-}
-
-void grpc_closure_list_init(grpc_closure_list* closure_list) {
-  closure_list->head = closure_list->tail = nullptr;
-}
-
-bool grpc_closure_list_append(grpc_closure_list* closure_list,
-                              grpc_closure* closure, grpc_error* error) {
-  if (closure == nullptr) {
-    GRPC_ERROR_UNREF(error);
-    return false;
-  }
-  closure->error_data.error = error;
-  closure->next_data.next = nullptr;
-  bool was_empty = (closure_list->head == nullptr);
-  if (was_empty) {
-    closure_list->head = closure;
-  } else {
-    closure_list->tail->next_data.next = closure;
-  }
-  closure_list->tail = closure;
-  return was_empty;
-}
-
-void grpc_closure_list_fail_all(grpc_closure_list* list,
-                                grpc_error* forced_failure) {
-  for (grpc_closure* c = list->head; c != nullptr; c = c->next_data.next) {
-    if (c->error_data.error == GRPC_ERROR_NONE) {
-      c->error_data.error = GRPC_ERROR_REF(forced_failure);
-    }
-  }
-  GRPC_ERROR_UNREF(forced_failure);
-}
-
-bool grpc_closure_list_empty(grpc_closure_list closure_list) {
-  return closure_list.head == nullptr;
-}
-
-void grpc_closure_list_move(grpc_closure_list* src, grpc_closure_list* dst) {
-  if (src->head == nullptr) {
-    return;
-  }
-  if (dst->head == nullptr) {
-    *dst = *src;
-  } else {
-    dst->tail->next_data.next = src->head;
-    dst->tail = src->tail;
-  }
-  src->head = src->tail = nullptr;
-}
-
-typedef struct {
-  grpc_iomgr_cb_func cb;
-  void* cb_arg;
-  grpc_closure wrapper;
-} wrapped_closure;
-
-static void closure_wrapper(grpc_exec_ctx* exec_ctx, void* arg,
-                            grpc_error* error) {
-  wrapped_closure* wc = (wrapped_closure*)arg;
-  grpc_iomgr_cb_func cb = wc->cb;
-  void* cb_arg = wc->cb_arg;
-  gpr_free(wc);
-  cb(exec_ctx, cb_arg, error);
-}
-
-#ifndef NDEBUG
-grpc_closure* grpc_closure_create(const char* file, int line,
-                                  grpc_iomgr_cb_func cb, void* cb_arg,
-                                  grpc_closure_scheduler* scheduler) {
-#else
-grpc_closure* grpc_closure_create(grpc_iomgr_cb_func cb, void* cb_arg,
-                                  grpc_closure_scheduler* scheduler) {
-#endif
-  wrapped_closure* wc = (wrapped_closure*)gpr_malloc(sizeof(*wc));
-  wc->cb = cb;
-  wc->cb_arg = cb_arg;
-#ifndef NDEBUG
-  grpc_closure_init(file, line, &wc->wrapper, closure_wrapper, wc, scheduler);
-#else
-  grpc_closure_init(&wc->wrapper, closure_wrapper, wc, scheduler);
-#endif
-  return &wc->wrapper;
-}
-
-#ifndef NDEBUG
-void grpc_closure_run(const char* file, int line, grpc_exec_ctx* exec_ctx,
-                      grpc_closure* c, grpc_error* error) {
-#else
-void grpc_closure_run(grpc_exec_ctx* exec_ctx, grpc_closure* c,
-                      grpc_error* error) {
-#endif
-  GPR_TIMER_BEGIN("grpc_closure_run", 0);
-  if (c != nullptr) {
-#ifndef NDEBUG
-    c->file_initiated = file;
-    c->line_initiated = line;
-    c->run = true;
-#endif
-    assert(c->cb);
-    c->scheduler->vtable->run(exec_ctx, c, error);
-  } else {
-    GRPC_ERROR_UNREF(error);
-  }
-  GPR_TIMER_END("grpc_closure_run", 0);
-}
-
-#ifndef NDEBUG
-void grpc_closure_sched(const char* file, int line, grpc_exec_ctx* exec_ctx,
-                        grpc_closure* c, grpc_error* error) {
-#else
-void grpc_closure_sched(grpc_exec_ctx* exec_ctx, grpc_closure* c,
-                        grpc_error* error) {
-#endif
-  GPR_TIMER_BEGIN("grpc_closure_sched", 0);
-  if (c != nullptr) {
-#ifndef NDEBUG
-    if (c->scheduled) {
-      gpr_log(GPR_ERROR,
-              "Closure already scheduled. (closure: %p, created: [%s:%d], "
-              "previously scheduled at: [%s: %d] run?: %s",
-              c, c->file_created, c->line_created, c->file_initiated,
-              c->line_initiated, c->run ? "true" : "false");
-      abort();
-    }
-    c->scheduled = true;
-    c->file_initiated = file;
-    c->line_initiated = line;
-    c->run = false;
-#endif
-    assert(c->cb);
-    c->scheduler->vtable->sched(exec_ctx, c, error);
-  } else {
-    GRPC_ERROR_UNREF(error);
-  }
-  GPR_TIMER_END("grpc_closure_sched", 0);
-}
-
-#ifndef NDEBUG
-void grpc_closure_list_sched(const char* file, int line,
-                             grpc_exec_ctx* exec_ctx, grpc_closure_list* list) {
-#else
-void grpc_closure_list_sched(grpc_exec_ctx* exec_ctx, grpc_closure_list* list) {
-#endif
-  grpc_closure* c = list->head;
-  while (c != nullptr) {
-    grpc_closure* next = c->next_data.next;
-#ifndef NDEBUG
-    if (c->scheduled) {
-      gpr_log(GPR_ERROR,
-              "Closure already scheduled. (closure: %p, created: [%s:%d], "
-              "previously scheduled at: [%s: %d] run?: %s",
-              c, c->file_created, c->line_created, c->file_initiated,
-              c->line_initiated, c->run ? "true" : "false");
-      abort();
-    }
-    c->scheduled = true;
-    c->file_initiated = file;
-    c->line_initiated = line;
-    c->run = false;
-#endif
-    assert(c->cb);
-    c->scheduler->vtable->sched(exec_ctx, c, c->error_data.error);
-    c = next;
-  }
-  list->head = list->tail = nullptr;
-}
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index 8b1188e..46793dd 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -21,21 +21,19 @@
 
 #include <grpc/support/port_platform.h>
 
+#include <assert.h>
 #include <grpc/impl/codegen/exec_ctx_fwd.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
 #include <stdbool.h>
 #include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/support/mpscq.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 struct grpc_closure;
 typedef struct grpc_closure grpc_closure;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_closure;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_closure;
 
 typedef struct grpc_closure_list {
   grpc_closure* head;
@@ -85,8 +83,8 @@
   /** Arguments to be passed to "cb". */
   void* cb_arg;
 
-  /** Scheduler to schedule against: NULL to schedule against current execution
-      context */
+  /** Scheduler to schedule against: nullptr to schedule against current
+     execution context */
   grpc_closure_scheduler* scheduler;
 
   /** Once queued, the result of the closure. Before then: scratch space */
@@ -107,102 +105,262 @@
 #endif
 };
 
+#ifndef NDEBUG
+inline grpc_closure* grpc_closure_init(const char* file, int line,
+                                       grpc_closure* closure,
+                                       grpc_iomgr_cb_func cb, void* cb_arg,
+                                       grpc_closure_scheduler* scheduler) {
+#else
+inline grpc_closure* grpc_closure_init(grpc_closure* closure,
+                                       grpc_iomgr_cb_func cb, void* cb_arg,
+                                       grpc_closure_scheduler* scheduler) {
+#endif
+  closure->cb = cb;
+  closure->cb_arg = cb_arg;
+  closure->scheduler = scheduler;
+#ifndef NDEBUG
+  closure->scheduled = false;
+  closure->file_initiated = nullptr;
+  closure->line_initiated = 0;
+  closure->run = false;
+  closure->file_created = file;
+  closure->line_created = line;
+#endif
+  return closure;
+}
+
 /** Initializes \a closure with \a cb and \a cb_arg. Returns \a closure. */
 #ifndef NDEBUG
-grpc_closure* grpc_closure_init(const char* file, int line,
-                                grpc_closure* closure, grpc_iomgr_cb_func cb,
-                                void* cb_arg,
-                                grpc_closure_scheduler* scheduler);
 #define GRPC_CLOSURE_INIT(closure, cb, cb_arg, scheduler) \
   grpc_closure_init(__FILE__, __LINE__, closure, cb, cb_arg, scheduler)
 #else
-grpc_closure* grpc_closure_init(grpc_closure* closure, grpc_iomgr_cb_func cb,
-                                void* cb_arg,
-                                grpc_closure_scheduler* scheduler);
 #define GRPC_CLOSURE_INIT(closure, cb, cb_arg, scheduler) \
   grpc_closure_init(closure, cb, cb_arg, scheduler)
 #endif
 
+namespace closure_impl {
+
+typedef struct {
+  grpc_iomgr_cb_func cb;
+  void* cb_arg;
+  grpc_closure wrapper;
+} wrapped_closure;
+
+inline void closure_wrapper(grpc_exec_ctx* exec_ctx, void* arg,
+                            grpc_error* error) {
+  wrapped_closure* wc = (wrapped_closure*)arg;
+  grpc_iomgr_cb_func cb = wc->cb;
+  void* cb_arg = wc->cb_arg;
+  gpr_free(wc);
+  cb(exec_ctx, cb_arg, error);
+}
+
+}  // namespace closure_impl
+
+#ifndef NDEBUG
+inline grpc_closure* grpc_closure_create(const char* file, int line,
+                                         grpc_iomgr_cb_func cb, void* cb_arg,
+                                         grpc_closure_scheduler* scheduler) {
+#else
+inline grpc_closure* grpc_closure_create(grpc_iomgr_cb_func cb, void* cb_arg,
+                                         grpc_closure_scheduler* scheduler) {
+#endif
+  closure_impl::wrapped_closure* wc =
+      (closure_impl::wrapped_closure*)gpr_malloc(sizeof(*wc));
+  wc->cb = cb;
+  wc->cb_arg = cb_arg;
+#ifndef NDEBUG
+  grpc_closure_init(file, line, &wc->wrapper, closure_impl::closure_wrapper, wc,
+                    scheduler);
+#else
+  grpc_closure_init(&wc->wrapper, closure_impl::closure_wrapper, wc, scheduler);
+#endif
+  return &wc->wrapper;
+}
+
 /* Create a heap allocated closure: try to avoid except for very rare events */
 #ifndef NDEBUG
-grpc_closure* grpc_closure_create(const char* file, int line,
-                                  grpc_iomgr_cb_func cb, void* cb_arg,
-                                  grpc_closure_scheduler* scheduler);
 #define GRPC_CLOSURE_CREATE(cb, cb_arg, scheduler) \
   grpc_closure_create(__FILE__, __LINE__, cb, cb_arg, scheduler)
 #else
-grpc_closure* grpc_closure_create(grpc_iomgr_cb_func cb, void* cb_arg,
-                                  grpc_closure_scheduler* scheduler);
 #define GRPC_CLOSURE_CREATE(cb, cb_arg, scheduler) \
   grpc_closure_create(cb, cb_arg, scheduler)
 #endif
 
 #define GRPC_CLOSURE_LIST_INIT \
-  { NULL, NULL }
+  { nullptr, nullptr }
 
-void grpc_closure_list_init(grpc_closure_list* list);
+inline void grpc_closure_list_init(grpc_closure_list* closure_list) {
+  closure_list->head = closure_list->tail = nullptr;
+}
 
 /** add \a closure to the end of \a list
     and set \a closure's result to \a error
     Returns true if \a list becomes non-empty */
-bool grpc_closure_list_append(grpc_closure_list* list, grpc_closure* closure,
-                              grpc_error* error);
+inline bool grpc_closure_list_append(grpc_closure_list* closure_list,
+                                     grpc_closure* closure, grpc_error* error) {
+  if (closure == nullptr) {
+    GRPC_ERROR_UNREF(error);
+    return false;
+  }
+  closure->error_data.error = error;
+  closure->next_data.next = nullptr;
+  bool was_empty = (closure_list->head == nullptr);
+  if (was_empty) {
+    closure_list->head = closure;
+  } else {
+    closure_list->tail->next_data.next = closure;
+  }
+  closure_list->tail = closure;
+  return was_empty;
+}
 
 /** force all success bits in \a list to false */
-void grpc_closure_list_fail_all(grpc_closure_list* list,
-                                grpc_error* forced_failure);
+inline void grpc_closure_list_fail_all(grpc_closure_list* list,
+                                       grpc_error* forced_failure) {
+  for (grpc_closure* c = list->head; c != nullptr; c = c->next_data.next) {
+    if (c->error_data.error == GRPC_ERROR_NONE) {
+      c->error_data.error = GRPC_ERROR_REF(forced_failure);
+    }
+  }
+  GRPC_ERROR_UNREF(forced_failure);
+}
 
 /** append all closures from \a src to \a dst and empty \a src. */
-void grpc_closure_list_move(grpc_closure_list* src, grpc_closure_list* dst);
+inline void grpc_closure_list_move(grpc_closure_list* src,
+                                   grpc_closure_list* dst) {
+  if (src->head == nullptr) {
+    return;
+  }
+  if (dst->head == nullptr) {
+    *dst = *src;
+  } else {
+    dst->tail->next_data.next = src->head;
+    dst->tail = src->tail;
+  }
+  src->head = src->tail = nullptr;
+}
 
 /** return whether \a list is empty. */
-bool grpc_closure_list_empty(grpc_closure_list list);
+inline bool grpc_closure_list_empty(grpc_closure_list closure_list) {
+  return closure_list.head == nullptr;
+}
+
+#ifndef NDEBUG
+inline void grpc_closure_run(const char* file, int line,
+                             grpc_exec_ctx* exec_ctx, grpc_closure* c,
+                             grpc_error* error) {
+#else
+inline void grpc_closure_run(grpc_exec_ctx* exec_ctx, grpc_closure* c,
+                             grpc_error* error) {
+#endif
+  GPR_TIMER_BEGIN("grpc_closure_run", 0);
+  if (c != nullptr) {
+#ifndef NDEBUG
+    c->file_initiated = file;
+    c->line_initiated = line;
+    c->run = true;
+#endif
+    assert(c->cb);
+    c->scheduler->vtable->run(exec_ctx, c, error);
+  } else {
+    GRPC_ERROR_UNREF(error);
+  }
+  GPR_TIMER_END("grpc_closure_run", 0);
+}
 
 /** Run a closure directly. Caller ensures that no locks are being held above.
  *  Note that calling this at the end of a closure callback function itself is
  *  by definition safe. */
 #ifndef NDEBUG
-void grpc_closure_run(const char* file, int line, grpc_exec_ctx* exec_ctx,
-                      grpc_closure* closure, grpc_error* error);
 #define GRPC_CLOSURE_RUN(exec_ctx, closure, error) \
   grpc_closure_run(__FILE__, __LINE__, exec_ctx, closure, error)
 #else
-void grpc_closure_run(grpc_exec_ctx* exec_ctx, grpc_closure* closure,
-                      grpc_error* error);
 #define GRPC_CLOSURE_RUN(exec_ctx, closure, error) \
   grpc_closure_run(exec_ctx, closure, error)
 #endif
 
+#ifndef NDEBUG
+inline void grpc_closure_sched(const char* file, int line,
+                               grpc_exec_ctx* exec_ctx, grpc_closure* c,
+                               grpc_error* error) {
+#else
+inline void grpc_closure_sched(grpc_exec_ctx* exec_ctx, grpc_closure* c,
+                               grpc_error* error) {
+#endif
+  GPR_TIMER_BEGIN("grpc_closure_sched", 0);
+  if (c != nullptr) {
+#ifndef NDEBUG
+    if (c->scheduled) {
+      gpr_log(GPR_ERROR,
+              "Closure already scheduled. (closure: %p, created: [%s:%d], "
+              "previously scheduled at: [%s: %d] run?: %s",
+              c, c->file_created, c->line_created, c->file_initiated,
+              c->line_initiated, c->run ? "true" : "false");
+      abort();
+    }
+    c->scheduled = true;
+    c->file_initiated = file;
+    c->line_initiated = line;
+    c->run = false;
+#endif
+    assert(c->cb);
+    c->scheduler->vtable->sched(exec_ctx, c, error);
+  } else {
+    GRPC_ERROR_UNREF(error);
+  }
+  GPR_TIMER_END("grpc_closure_sched", 0);
+}
+
 /** Schedule a closure to be run. Does not need to be run from a safe point. */
 #ifndef NDEBUG
-void grpc_closure_sched(const char* file, int line, grpc_exec_ctx* exec_ctx,
-                        grpc_closure* closure, grpc_error* error);
 #define GRPC_CLOSURE_SCHED(exec_ctx, closure, error) \
   grpc_closure_sched(__FILE__, __LINE__, exec_ctx, closure, error)
 #else
-void grpc_closure_sched(grpc_exec_ctx* exec_ctx, grpc_closure* closure,
-                        grpc_error* error);
 #define GRPC_CLOSURE_SCHED(exec_ctx, closure, error) \
   grpc_closure_sched(exec_ctx, closure, error)
 #endif
 
+#ifndef NDEBUG
+inline void grpc_closure_list_sched(const char* file, int line,
+                                    grpc_exec_ctx* exec_ctx,
+                                    grpc_closure_list* list) {
+#else
+inline void grpc_closure_list_sched(grpc_exec_ctx* exec_ctx,
+                                    grpc_closure_list* list) {
+#endif
+  grpc_closure* c = list->head;
+  while (c != nullptr) {
+    grpc_closure* next = c->next_data.next;
+#ifndef NDEBUG
+    if (c->scheduled) {
+      gpr_log(GPR_ERROR,
+              "Closure already scheduled. (closure: %p, created: [%s:%d], "
+              "previously scheduled at: [%s: %d] run?: %s",
+              c, c->file_created, c->line_created, c->file_initiated,
+              c->line_initiated, c->run ? "true" : "false");
+      abort();
+    }
+    c->scheduled = true;
+    c->file_initiated = file;
+    c->line_initiated = line;
+    c->run = false;
+#endif
+    assert(c->cb);
+    c->scheduler->vtable->sched(exec_ctx, c, c->error_data.error);
+    c = next;
+  }
+  list->head = list->tail = nullptr;
+}
+
 /** Schedule all closures in a list to be run. Does not need to be run from a
  * safe point. */
 #ifndef NDEBUG
-void grpc_closure_list_sched(const char* file, int line,
-                             grpc_exec_ctx* exec_ctx,
-                             grpc_closure_list* closure_list);
 #define GRPC_CLOSURE_LIST_SCHED(exec_ctx, closure_list) \
   grpc_closure_list_sched(__FILE__, __LINE__, exec_ctx, closure_list)
 #else
-void grpc_closure_list_sched(grpc_exec_ctx* exec_ctx,
-                             grpc_closure_list* closure_list);
 #define GRPC_CLOSURE_LIST_SCHED(exec_ctx, closure_list) \
   grpc_closure_list_sched(exec_ctx, closure_list)
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* GRPC_CORE_LIB_IOMGR_CLOSURE_H */
diff --git a/src/core/lib/iomgr/combiner.cc b/src/core/lib/iomgr/combiner.cc
index b28ca34..15c009d 100644
--- a/src/core/lib/iomgr/combiner.cc
+++ b/src/core/lib/iomgr/combiner.cc
@@ -29,14 +29,13 @@
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/profiling/timers.h"
 
-grpc_tracer_flag grpc_combiner_trace =
-    GRPC_TRACER_INITIALIZER(false, "combiner");
+grpc_core::TraceFlag grpc_combiner_trace(false, "combiner");
 
-#define GRPC_COMBINER_TRACE(fn)                \
-  do {                                         \
-    if (GRPC_TRACER_ON(grpc_combiner_trace)) { \
-      fn;                                      \
-    }                                          \
+#define GRPC_COMBINER_TRACE(fn)          \
+  do {                                   \
+    if (grpc_combiner_trace.enabled()) { \
+      fn;                                \
+    }                                    \
   } while (0)
 
 #define STATE_UNORPHANED 1
@@ -106,7 +105,7 @@
 
 #ifndef NDEBUG
 #define GRPC_COMBINER_DEBUG_SPAM(op, delta)                                \
-  if (GRPC_TRACER_ON(grpc_combiner_trace)) {                               \
+  if (grpc_combiner_trace.enabled()) {                                     \
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,                            \
             "C:%p %s %" PRIdPTR " --> %" PRIdPTR " %s", lock, (op),        \
             gpr_atm_no_barrier_load(&lock->refs.count),                    \
diff --git a/src/core/lib/iomgr/combiner.h b/src/core/lib/iomgr/combiner.h
index f8a8b9d..e99b063 100644
--- a/src/core/lib/iomgr/combiner.h
+++ b/src/core/lib/iomgr/combiner.h
@@ -65,7 +65,7 @@
 
 bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx* exec_ctx);
 
-extern grpc_tracer_flag grpc_combiner_trace;
+extern grpc_core::TraceFlag grpc_combiner_trace;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc
index 581b903..e6d640c 100644
--- a/src/core/lib/iomgr/error.cc
+++ b/src/core/lib/iomgr/error.cc
@@ -37,10 +37,9 @@
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_error_refcount =
-    GRPC_TRACER_INITIALIZER(false, "error_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount(false,
+                                                        "error_refcount");
+grpc_core::DebugOnlyTraceFlag grpc_trace_closure(false, "closure");
 
 static const char* error_int_name(grpc_error_ints key) {
   switch (key) {
@@ -130,7 +129,7 @@
 #ifndef NDEBUG
 grpc_error* grpc_error_ref(grpc_error* err, const char* file, int line) {
   if (grpc_error_is_special(err)) return err;
-  if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+  if (grpc_trace_error_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err,
             gpr_atm_no_barrier_load(&err->atomics.refs.count),
             gpr_atm_no_barrier_load(&err->atomics.refs.count) + 1, file, line);
@@ -183,7 +182,7 @@
 #ifndef NDEBUG
 void grpc_error_unref(grpc_error* err, const char* file, int line) {
   if (grpc_error_is_special(err)) return;
-  if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+  if (grpc_trace_error_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err,
             gpr_atm_no_barrier_load(&err->atomics.refs.count),
             gpr_atm_no_barrier_load(&err->atomics.refs.count) - 1, file, line);
@@ -216,7 +215,7 @@
     *err = (grpc_error*)gpr_realloc(
         *err, sizeof(grpc_error) + (*err)->arena_capacity * sizeof(intptr_t));
 #ifndef NDEBUG
-    if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+    if (grpc_trace_error_refcount.enabled()) {
       if (*err != orig) {
         gpr_log(GPR_DEBUG, "realloc %p -> %p", orig, *err);
       }
@@ -329,7 +328,7 @@
     return GRPC_ERROR_OOM;
   }
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+  if (grpc_trace_error_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line);
   }
 #endif
@@ -411,7 +410,7 @@
     out = (grpc_error*)gpr_malloc(sizeof(*in) +
                                   new_arena_capacity * sizeof(intptr_t));
 #ifndef NDEBUG
-    if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+    if (grpc_trace_error_refcount.enabled()) {
       gpr_log(GPR_DEBUG, "%p create copying %p", out, in);
     }
 #endif
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
index 8d7aea4..d10bf0b 100644
--- a/src/core/lib/iomgr/error.h
+++ b/src/core/lib/iomgr/error.h
@@ -39,9 +39,7 @@
 
 typedef struct grpc_error grpc_error;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_error_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount;
 
 typedef enum {
   /// 'errno' from the operating system
diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc
index 918bc6f..0dda1d9 100644
--- a/src/core/lib/iomgr/ev_epoll1_linux.cc
+++ b/src/core/lib/iomgr/ev_epoll1_linux.cc
@@ -263,11 +263,13 @@
 
   if (new_fd == nullptr) {
     new_fd = (grpc_fd*)gpr_malloc(sizeof(grpc_fd));
+    new_fd->read_closure.Init();
+    new_fd->write_closure.Init();
   }
 
   new_fd->fd = fd;
-  new_fd->read_closure.Init();
-  new_fd->write_closure.Init();
+  new_fd->read_closure->InitEvent();
+  new_fd->write_closure->InitEvent();
   gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL);
 
   new_fd->freelist_next = nullptr;
@@ -276,7 +278,7 @@
   gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
   grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name);
   }
 #endif
@@ -336,8 +338,8 @@
   GRPC_CLOSURE_SCHED(exec_ctx, on_done, GRPC_ERROR_REF(error));
 
   grpc_iomgr_unregister_object(&fd->iomgr_object);
-  fd->read_closure.Destroy();
-  fd->write_closure.Destroy();
+  fd->read_closure->DestroyEvent();
+  fd->write_closure->DestroyEvent();
 
   gpr_mu_lock(&fd_freelist_mu);
   fd->freelist_next = fd_freelist;
@@ -651,7 +653,7 @@
 
   GRPC_STATS_INC_POLL_EVENTS_RETURNED(exec_ctx, r);
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "ps: %p poll got %d events", ps, r);
   }
 
@@ -673,7 +675,7 @@
   worker->schedule_on_end_work = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT;
   pollset->begin_refs++;
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_ERROR, "PS:%p BEGIN_STARTS:%p", pollset, worker);
   }
 
@@ -692,7 +694,7 @@
   retry_lock_neighborhood:
     gpr_mu_lock(&neighborhood->mu);
     gpr_mu_lock(&pollset->mu);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "PS:%p BEGIN_REORG:%p kick_state=%s is_reassigning=%d",
               pollset, worker, kick_state_string(worker->state),
               is_reassigning);
@@ -744,7 +746,7 @@
     worker->initialized_cv = true;
     gpr_cv_init(&worker->cv);
     while (worker->state == UNKICKED && !pollset->shutting_down) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_ERROR, "PS:%p BEGIN_WAIT:%p kick_state=%s shutdown=%d",
                 pollset, worker, kick_state_string(worker->state),
                 pollset->shutting_down);
@@ -761,7 +763,7 @@
     grpc_exec_ctx_invalidate_now(exec_ctx);
   }
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_ERROR,
             "PS:%p BEGIN_DONE:%p kick_state=%s shutdown=%d "
             "kicked_without_poller: %d",
@@ -806,7 +808,7 @@
           case UNKICKED:
             if (gpr_atm_no_barrier_cas(&g_active_poller, 0,
                                        (gpr_atm)inspect_worker)) {
-              if (GRPC_TRACER_ON(grpc_polling_trace)) {
+              if (grpc_polling_trace.enabled()) {
                 gpr_log(GPR_DEBUG, " .. choose next poller to be %p",
                         inspect_worker);
               }
@@ -817,7 +819,7 @@
                 gpr_cv_signal(&inspect_worker->cv);
               }
             } else {
-              if (GRPC_TRACER_ON(grpc_polling_trace)) {
+              if (grpc_polling_trace.enabled()) {
                 gpr_log(GPR_DEBUG, " .. beaten to choose next poller");
               }
             }
@@ -835,7 +837,7 @@
       } while (!found_worker && inspect_worker != inspect->root_worker);
     }
     if (!found_worker) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, " .. mark pollset %p inactive", inspect);
       }
       inspect->seen_inactive = true;
@@ -857,7 +859,7 @@
                        grpc_pollset_worker* worker,
                        grpc_pollset_worker** worker_hdl) {
   GPR_TIMER_BEGIN("end_worker", 0);
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PS:%p END_WORKER:%p", pollset, worker);
   }
   if (worker_hdl != nullptr) *worker_hdl = nullptr;
@@ -867,7 +869,7 @@
                          &exec_ctx->closure_list);
   if (gpr_atm_no_barrier_load(&g_active_poller) == (gpr_atm)worker) {
     if (worker->next != worker && worker->next->state == UNKICKED) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, " .. choose next poller to be peer %p", worker);
       }
       GPR_ASSERT(worker->next->initialized_cv);
@@ -921,7 +923,7 @@
   if (worker->initialized_cv) {
     gpr_cv_destroy(&worker->cv);
   }
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, " .. remove worker");
   }
   if (EMPTIED == worker_remove(pollset, worker)) {
@@ -993,7 +995,7 @@
   GPR_TIMER_BEGIN("pollset_kick", 0);
   GRPC_STATS_INC_POLLSET_KICK(exec_ctx);
   grpc_error* ret_err = GRPC_ERROR_NONE;
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_strvec log;
     gpr_strvec_init(&log);
     char* tmp;
@@ -1026,7 +1028,7 @@
       if (root_worker == nullptr) {
         GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(exec_ctx);
         pollset->kicked_without_poller = true;
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. kicked_without_poller");
         }
         goto done;
@@ -1034,14 +1036,14 @@
       grpc_pollset_worker* next_worker = root_worker->next;
       if (root_worker->state == KICKED) {
         GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. already kicked %p", root_worker);
         }
         SET_KICK_STATE(root_worker, KICKED);
         goto done;
       } else if (next_worker->state == KICKED) {
         GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. already kicked %p", next_worker);
         }
         SET_KICK_STATE(next_worker, KICKED);
@@ -1052,7 +1054,7 @@
                  root_worker == (grpc_pollset_worker*)gpr_atm_no_barrier_load(
                                     &g_active_poller)) {
         GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. kicked %p", root_worker);
         }
         SET_KICK_STATE(root_worker, KICKED);
@@ -1060,7 +1062,7 @@
         goto done;
       } else if (next_worker->state == UNKICKED) {
         GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. kicked %p", next_worker);
         }
         GPR_ASSERT(next_worker->initialized_cv);
@@ -1069,7 +1071,7 @@
         goto done;
       } else if (next_worker->state == DESIGNATED_POLLER) {
         if (root_worker->state != DESIGNATED_POLLER) {
-          if (GRPC_TRACER_ON(grpc_polling_trace)) {
+          if (grpc_polling_trace.enabled()) {
             gpr_log(
                 GPR_ERROR,
                 " .. kicked root non-poller %p (initialized_cv=%d) (poller=%p)",
@@ -1083,7 +1085,7 @@
           goto done;
         } else {
           GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-          if (GRPC_TRACER_ON(grpc_polling_trace)) {
+          if (grpc_polling_trace.enabled()) {
             gpr_log(GPR_ERROR, " .. non-root poller %p (root=%p)", next_worker,
                     root_worker);
           }
@@ -1099,7 +1101,7 @@
       }
     } else {
       GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_ERROR, " .. kicked while waking up");
       }
       goto done;
@@ -1109,14 +1111,14 @@
   }
 
   if (specific_worker->state == KICKED) {
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. specific worker already kicked");
     }
     goto done;
   } else if (gpr_tls_get(&g_current_thread_worker) ==
              (intptr_t)specific_worker) {
     GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. mark %p kicked", specific_worker);
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1124,7 +1126,7 @@
   } else if (specific_worker ==
              (grpc_pollset_worker*)gpr_atm_no_barrier_load(&g_active_poller)) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. kick active poller");
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1132,7 +1134,7 @@
     goto done;
   } else if (specific_worker->initialized_cv) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. kick waiting worker");
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1140,7 +1142,7 @@
     goto done;
   } else {
     GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. kick non-waiting worker");
     }
     SET_KICK_STATE(specific_worker, KICKED);
diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc
index bfd2ac4..62643df 100644
--- a/src/core/lib/iomgr/ev_epollex_linux.cc
+++ b/src/core/lib/iomgr/ev_epollex_linux.cc
@@ -59,10 +59,8 @@
 #define MAX_EPOLL_EVENTS 100
 #define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 5
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_pollable_refcount =
-    GRPC_TRACER_INITIALIZER(false, "pollable_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_pollable_refcount(false,
+                                                           "pollable_refcount");
 
 /*******************************************************************************
  * pollable Declarations
@@ -263,7 +261,7 @@
   unref_by(ec, fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -288,8 +286,8 @@
   fd->freelist_next = fd_freelist;
   fd_freelist = fd;
 
-  fd->read_closure.Destroy();
-  fd->write_closure.Destroy();
+  fd->read_closure->DestroyEvent();
+  fd->write_closure->DestroyEvent();
 
   gpr_mu_unlock(&fd_freelist_mu);
 }
@@ -297,7 +295,7 @@
 #ifndef NDEBUG
 static void unref_by(grpc_exec_ctx* exec_ctx, grpc_fd* fd, int n,
                      const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -342,6 +340,8 @@
 
   if (new_fd == nullptr) {
     new_fd = (grpc_fd*)gpr_malloc(sizeof(grpc_fd));
+    new_fd->read_closure.Init();
+    new_fd->write_closure.Init();
   }
 
   gpr_mu_init(&new_fd->pollable_mu);
@@ -349,8 +349,8 @@
   new_fd->pollable_obj = nullptr;
   gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1);
   new_fd->fd = fd;
-  new_fd->read_closure.Init();
-  new_fd->write_closure.Init();
+  new_fd->read_closure->InitEvent();
+  new_fd->write_closure->InitEvent();
   gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL);
 
   new_fd->freelist_next = nullptr;
@@ -360,7 +360,7 @@
   gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
   grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name);
   }
 #endif
@@ -483,7 +483,7 @@
 static pollable* pollable_ref(pollable* p) {
 #else
 static pollable* pollable_ref(pollable* p, int line, const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_pollable_refcount)) {
+  if (grpc_trace_pollable_refcount.enabled()) {
     int r = (int)gpr_atm_no_barrier_load(&p->refs.count);
     gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
             "POLLABLE:%p   ref %d->%d %s", p, r, r + 1, reason);
@@ -498,7 +498,7 @@
 #else
 static void pollable_unref(pollable* p, int line, const char* reason) {
   if (p == nullptr) return;
-  if (GRPC_TRACER_ON(grpc_trace_pollable_refcount)) {
+  if (grpc_trace_pollable_refcount.enabled()) {
     int r = (int)gpr_atm_no_barrier_load(&p->refs.count);
     gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
             "POLLABLE:%p unref %d->%d %s", p, r, r - 1, reason);
@@ -516,7 +516,7 @@
   static const char* err_desc = "pollable_add_fd";
   const int epfd = p->epfd;
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "add fd %p (%d) to pollable %p", fd, fd->fd, p);
   }
 
@@ -558,7 +558,7 @@
 /* pollset->mu must be held while calling this function */
 static void pollset_maybe_finish_shutdown(grpc_exec_ctx* exec_ctx,
                                           grpc_pollset* pollset) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p (pollable:%p) maybe_finish_shutdown sc=%p (target:!NULL) "
             "rw=%p (target:NULL) cpsc=%d (target:0)",
@@ -581,14 +581,14 @@
   grpc_core::mu_guard lock(&p->mu);
   GPR_ASSERT(specific_worker != nullptr);
   if (specific_worker->kicked) {
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_already_kicked", p);
     }
     GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
     return GRPC_ERROR_NONE;
   }
   if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) {
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_awake", p);
     }
     GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
@@ -597,7 +597,7 @@
   }
   if (specific_worker == p->root_worker) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_wakeup_fd", p);
     }
     specific_worker->kicked = true;
@@ -606,7 +606,7 @@
   }
   if (specific_worker->initialized_cv) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_cv", p);
     }
     specific_worker->kicked = true;
@@ -621,7 +621,7 @@
 static grpc_error* pollset_kick(grpc_exec_ctx* exec_ctx, grpc_pollset* pollset,
                                 grpc_pollset_worker* specific_worker) {
   GRPC_STATS_INC_POLLSET_KICK(exec_ctx);
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p kick %p tls_pollset=%p tls_worker=%p pollset.root_worker=%p",
             pollset, specific_worker,
@@ -631,7 +631,7 @@
   if (specific_worker == nullptr) {
     if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) {
       if (pollset->root_worker == nullptr) {
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_DEBUG, "PS:%p kicked_any_without_poller", pollset);
         }
         GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(exec_ctx);
@@ -657,7 +657,7 @@
             exec_ctx, pollset->root_worker->links[PWLINK_POLLSET].next);
       }
     } else {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, "PS:%p kicked_any_but_awake", pollset);
       }
       GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
@@ -765,7 +765,7 @@
     struct epoll_event* ev = &pollable_obj->events[n];
     void* data_ptr = ev->data.ptr;
     if (1 & (intptr_t)data_ptr) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, "PS:%p got pollset_wakeup %p", pollset, data_ptr);
       }
       append_error(&error,
@@ -777,7 +777,7 @@
       bool cancel = (ev->events & (EPOLLERR | EPOLLHUP)) != 0;
       bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0;
       bool write_ev = (ev->events & EPOLLOUT) != 0;
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "PS:%p got fd %p: cancel=%d read=%d "
                 "write=%d",
@@ -805,7 +805,7 @@
                                   grpc_millis deadline) {
   int timeout = poll_deadline_to_millis_timeout(exec_ctx, deadline);
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     char* desc = pollable_desc(p);
     gpr_log(GPR_DEBUG, "POLLABLE:%p[%s] poll for %dms", p, desc, timeout);
     gpr_free(desc);
@@ -825,7 +825,7 @@
 
   if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait");
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "POLLABLE:%p got %d events", p, r);
   }
 
@@ -893,7 +893,7 @@
     worker->initialized_cv = true;
     gpr_cv_init(&worker->cv);
     gpr_mu_unlock(&pollset->mu);
-    if (GRPC_TRACER_ON(grpc_polling_trace) &&
+    if (grpc_polling_trace.enabled() &&
         worker->pollable_obj->root_worker != worker) {
       gpr_log(GPR_DEBUG, "PS:%p wait %p w=%p for %dms", pollset,
               worker->pollable_obj, worker,
@@ -902,18 +902,18 @@
     while (do_poll && worker->pollable_obj->root_worker != worker) {
       if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->mu,
                       grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_DEBUG, "PS:%p timeout_wait %p w=%p", pollset,
                   worker->pollable_obj, worker);
         }
         do_poll = false;
       } else if (worker->kicked) {
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_DEBUG, "PS:%p wakeup %p w=%p", pollset,
                   worker->pollable_obj, worker);
         }
         do_poll = false;
-      } else if (GRPC_TRACER_ON(grpc_polling_trace) &&
+      } else if (grpc_polling_trace.enabled() &&
                  worker->pollable_obj->root_worker != worker) {
         gpr_log(GPR_DEBUG, "PS:%p spurious_wakeup %p w=%p", pollset,
                 worker->pollable_obj, worker);
@@ -984,7 +984,7 @@
 #ifndef NDEBUG
   WORKER_PTR->originator = gettid();
 #endif
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p work hdl=%p worker=%p now=%" PRIdPTR " deadline=%" PRIdPTR
             " kwp=%d pollable=%p",
@@ -1027,7 +1027,7 @@
     grpc_exec_ctx* exec_ctx, grpc_pollset* pollset, grpc_fd* fd) {
   static const char* err_desc = "pollset_transition_pollable_from_empty_to_fd";
   grpc_error* error = GRPC_ERROR_NONE;
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p add fd %p (%d); transition pollable from empty to fd",
             pollset, fd, fd->fd);
@@ -1043,7 +1043,7 @@
     grpc_exec_ctx* exec_ctx, grpc_pollset* pollset, grpc_fd* and_add_fd) {
   static const char* err_desc = "pollset_transition_pollable_from_fd_to_multi";
   grpc_error* error = GRPC_ERROR_NONE;
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(
         GPR_DEBUG,
         "PS:%p add fd %p (%d); transition pollable from fd %p to multipoller",
@@ -1193,7 +1193,7 @@
 
 static void pollset_set_add_fd(grpc_exec_ctx* exec_ctx, grpc_pollset_set* pss,
                                grpc_fd* fd) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: add fd %p (%d)", pss, fd, fd->fd);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1217,7 +1217,7 @@
 
 static void pollset_set_del_fd(grpc_exec_ctx* exec_ctx, grpc_pollset_set* pss,
                                grpc_fd* fd) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: del fd %p", pss, fd);
   }
   pss = pss_lock_adam(pss);
@@ -1238,7 +1238,7 @@
 
 static void pollset_set_del_pollset(grpc_exec_ctx* exec_ctx,
                                     grpc_pollset_set* pss, grpc_pollset* ps) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: del pollset %p", pss, ps);
   }
   pss = pss_lock_adam(pss);
@@ -1289,7 +1289,7 @@
 
 static void pollset_set_add_pollset(grpc_exec_ctx* exec_ctx,
                                     grpc_pollset_set* pss, grpc_pollset* ps) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: add pollset %p", pss, ps);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1326,7 +1326,7 @@
 static void pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx,
                                         grpc_pollset_set* a,
                                         grpc_pollset_set* b) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS: merge (%p, %p)", a, b);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1360,7 +1360,7 @@
   if (b_size > a_size) {
     GPR_SWAP(grpc_pollset_set*, a, b);
   }
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS: parent %p to %p", b, a);
   }
   gpr_ref(&a->refs);
@@ -1461,10 +1461,6 @@
     return nullptr;
   }
 
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_pollable_refcount);
-#endif
-
   fd_global_init();
 
   if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc
index 5b4a7ba..12c8483 100644
--- a/src/core/lib/iomgr/ev_epollsig_linux.cc
+++ b/src/core/lib/iomgr/ev_epollsig_linux.cc
@@ -54,9 +54,9 @@
 
 #define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1)
 
-#define GRPC_POLLING_TRACE(...)             \
-  if (GRPC_TRACER_ON(grpc_polling_trace)) { \
-    gpr_log(GPR_INFO, __VA_ARGS__);         \
+#define GRPC_POLLING_TRACE(...)       \
+  if (grpc_polling_trace.enabled()) { \
+    gpr_log(GPR_INFO, __VA_ARGS__);   \
   }
 
 static int grpc_wakeup_signal = -1;
@@ -289,7 +289,7 @@
 #ifndef NDEBUG
 static void pi_add_ref_dbg(polling_island* pi, const char* reason,
                            const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count);
     gpr_log(GPR_DEBUG,
             "Add ref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR
@@ -301,7 +301,7 @@
 
 static void pi_unref_dbg(grpc_exec_ctx* exec_ctx, polling_island* pi,
                          const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count);
     gpr_log(GPR_DEBUG,
             "Unref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR
@@ -733,7 +733,7 @@
 #define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -750,7 +750,7 @@
 #ifndef NDEBUG
 static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                      int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -767,8 +767,8 @@
     fd_freelist = fd;
     grpc_iomgr_unregister_object(&fd->iomgr_object);
 
-    fd->read_closure.Destroy();
-    fd->write_closure.Destroy();
+    fd->read_closure->DestroyEvent();
+    fd->write_closure->DestroyEvent();
 
     gpr_mu_unlock(&fd_freelist_mu);
   } else {
@@ -819,6 +819,8 @@
   if (new_fd == nullptr) {
     new_fd = (grpc_fd*)gpr_malloc(sizeof(grpc_fd));
     gpr_mu_init(&new_fd->po.mu);
+    new_fd->read_closure.Init();
+    new_fd->write_closure.Init();
   }
 
   /* Note: It is not really needed to get the new_fd->po.mu lock here. If this
@@ -833,8 +835,8 @@
   gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1);
   new_fd->fd = fd;
   new_fd->orphaned = false;
-  new_fd->read_closure.Init();
-  new_fd->write_closure.Init();
+  new_fd->read_closure->InitEvent();
+  new_fd->write_closure->InitEvent();
   gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL);
 
   new_fd->freelist_next = nullptr;
diff --git a/src/core/lib/iomgr/ev_poll_posix.cc b/src/core/lib/iomgr/ev_poll_posix.cc
index f8b9629..8659559 100644
--- a/src/core/lib/iomgr/ev_poll_posix.cc
+++ b/src/core/lib/iomgr/ev_poll_posix.cc
@@ -288,7 +288,7 @@
 #define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -305,7 +305,7 @@
 #ifndef NDEBUG
 static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                      int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -992,7 +992,7 @@
       r = grpc_poll_function(pfds, pfd_count, timeout);
       GRPC_SCHEDULING_END_BLOCKING_REGION_WITH_EXEC_CTX(exec_ctx);
 
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, "%p poll=%d", pollset, r);
       }
 
@@ -1016,7 +1016,7 @@
         }
       } else {
         if (pfds[0].revents & POLLIN_CHECK) {
-          if (GRPC_TRACER_ON(grpc_polling_trace)) {
+          if (grpc_polling_trace.enabled()) {
             gpr_log(GPR_DEBUG, "%p: got_wakeup", pollset);
           }
           work_combine_error(
@@ -1026,7 +1026,7 @@
           if (watchers[i].fd == nullptr) {
             fd_end_poll(exec_ctx, &watchers[i], 0, 0, nullptr);
           } else {
-            if (GRPC_TRACER_ON(grpc_polling_trace)) {
+            if (grpc_polling_trace.enabled()) {
               gpr_log(GPR_DEBUG, "%p got_event: %d r:%d w:%d [%d]", pollset,
                       pfds[i].fd, (pfds[i].revents & POLLIN_CHECK) != 0,
                       (pfds[i].revents & POLLOUT_CHECK) != 0, pfds[i].revents);
diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc
index 076d2e6..80dde6d 100644
--- a/src/core/lib/iomgr/ev_posix.cc
+++ b/src/core/lib/iomgr/ev_posix.cc
@@ -36,13 +36,9 @@
 #include "src/core/lib/iomgr/ev_poll_posix.h"
 #include "src/core/lib/support/env.h"
 
-grpc_tracer_flag grpc_polling_trace =
-    GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */
-
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_fd_refcount =
-    GRPC_TRACER_INITIALIZER(false, "fd_refcount");
-#endif
+grpc_core::TraceFlag grpc_polling_trace(false,
+                                        "polling"); /* Disabled by default */
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 
 /** Default poll() function - a pointer so that it can be overridden by some
  *  tests */
@@ -153,8 +149,6 @@
 const char* grpc_get_poll_strategy_name() { return g_poll_strategy_name; }
 
 void grpc_event_engine_init(void) {
-  grpc_register_tracer(&grpc_polling_trace);
-
   char* s = gpr_getenv("GRPC_POLL_STRATEGY");
   if (s == nullptr) {
     s = gpr_strdup("all");
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index d719b8f..8f45d2e 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -31,7 +31,7 @@
 extern "C" {
 #endif
 
-extern grpc_tracer_flag grpc_polling_trace; /* Disabled by default */
+extern grpc_core::TraceFlag grpc_polling_trace; /* Disabled by default */
 
 typedef struct grpc_fd grpc_fd;
 
diff --git a/src/core/lib/iomgr/ev_windows.cc b/src/core/lib/iomgr/ev_windows.cc
index c24dfae..697697d 100644
--- a/src/core/lib/iomgr/ev_windows.cc
+++ b/src/core/lib/iomgr/ev_windows.cc
@@ -22,7 +22,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-grpc_tracer_flag grpc_polling_trace =
-    GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */
+grpc_core::TraceFlag grpc_polling_trace(false,
+                                        "polling"); /* Disabled by default */
 
 #endif  // GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/exec_ctx.cc b/src/core/lib/iomgr/exec_ctx.cc
index 27c769a..1777456 100644
--- a/src/core/lib/iomgr/exec_ctx.cc
+++ b/src/core/lib/iomgr/exec_ctx.cc
@@ -25,9 +25,6 @@
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/profiling/timers.h"
 
-#define GRPC_START_TIME_UPDATE_INTERVAL 10000
-extern "C" grpc_tracer_flag grpc_timer_check_trace;
-
 bool grpc_exec_ctx_ready_to_finish(grpc_exec_ctx* exec_ctx) {
   if ((exec_ctx->flags & GRPC_EXEC_CTX_FLAG_IS_FINISHED) == 0) {
     if (exec_ctx->check_ready_to_finish(exec_ctx,
@@ -63,7 +60,7 @@
                          grpc_error* error) {
 #ifndef NDEBUG
   closure->scheduled = false;
-  if (GRPC_TRACER_ON(grpc_trace_closure)) {
+  if (grpc_trace_closure.enabled()) {
     gpr_log(GPR_DEBUG, "running closure %p: created [%s:%d]: %s [%s:%d]",
             closure, closure->file_created, closure->line_created,
             closure->run ? "run" : "scheduled", closure->file_initiated,
@@ -72,7 +69,7 @@
 #endif
   closure->cb(exec_ctx, closure->cb_arg, error);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_closure)) {
+  if (grpc_trace_closure.enabled()) {
     gpr_log(GPR_DEBUG, "closure %p finished", closure);
   }
 #endif
@@ -107,49 +104,16 @@
   grpc_closure_list_append(&exec_ctx->closure_list, closure, error);
 }
 
-/* This time pair is not entirely thread-safe as store/load of tv_sec and
- * tv_nsec are performed separately. However g_start_time do not need to have
- * sub-second precision, so it is ok if the value of tv_nsec is off in this
- * case. */
-typedef struct time_atm_pair {
-  gpr_atm tv_sec;
-  gpr_atm tv_nsec;
-} time_atm_pair;
-
-static time_atm_pair
-    g_start_time[GPR_TIMESPAN + 1];  // assumes GPR_TIMESPAN is the
-                                     // last enum value in
-                                     // gpr_clock_type
-static grpc_millis g_last_start_time_update;
-
-static gpr_timespec timespec_from_time_atm_pair(const time_atm_pair* src,
-                                                gpr_clock_type clock_type) {
-  gpr_timespec time;
-  time.tv_nsec = (int32_t)gpr_atm_no_barrier_load(&src->tv_nsec);
-  time.tv_sec = (int64_t)gpr_atm_no_barrier_load(&src->tv_sec);
-  time.clock_type = clock_type;
-  return time;
-}
-
-static void time_atm_pair_store(time_atm_pair* dst, const gpr_timespec src) {
-  gpr_atm_no_barrier_store(&dst->tv_sec, src.tv_sec);
-  gpr_atm_no_barrier_store(&dst->tv_nsec, src.tv_nsec);
-}
+static gpr_timespec g_start_time;
 
 void grpc_exec_ctx_global_init(void) {
-  for (int i = 0; i < GPR_TIMESPAN; i++) {
-    time_atm_pair_store(&g_start_time[i], gpr_now((gpr_clock_type)i));
-  }
-  // allows uniform treatment in conversion functions
-  time_atm_pair_store(&g_start_time[GPR_TIMESPAN], gpr_time_0(GPR_TIMESPAN));
+  g_start_time = gpr_now(GPR_CLOCK_MONOTONIC);
 }
 
 void grpc_exec_ctx_global_shutdown(void) {}
 
 static gpr_atm timespec_to_atm_round_down(gpr_timespec ts) {
-  gpr_timespec start_time =
-      timespec_from_time_atm_pair(&g_start_time[ts.clock_type], ts.clock_type);
-  ts = gpr_time_sub(ts, start_time);
+  ts = gpr_time_sub(ts, g_start_time);
   double x =
       GPR_MS_PER_SEC * (double)ts.tv_sec + (double)ts.tv_nsec / GPR_NS_PER_MS;
   if (x < 0) return 0;
@@ -158,9 +122,7 @@
 }
 
 static gpr_atm timespec_to_atm_round_up(gpr_timespec ts) {
-  gpr_timespec start_time =
-      timespec_from_time_atm_pair(&g_start_time[ts.clock_type], ts.clock_type);
-  ts = gpr_time_sub(ts, start_time);
+  ts = gpr_time_sub(ts, g_start_time);
   double x = GPR_MS_PER_SEC * (double)ts.tv_sec +
              (double)ts.tv_nsec / GPR_NS_PER_MS +
              (double)(GPR_NS_PER_SEC - 1) / (double)GPR_NS_PER_SEC;
@@ -195,41 +157,18 @@
   if (clock_type == GPR_TIMESPAN) {
     return gpr_time_from_millis(millis, GPR_TIMESPAN);
   }
-  gpr_timespec start_time =
-      timespec_from_time_atm_pair(&g_start_time[clock_type], clock_type);
-  return gpr_time_add(start_time, gpr_time_from_millis(millis, GPR_TIMESPAN));
+  return gpr_time_add(gpr_convert_clock_type(g_start_time, clock_type),
+                      gpr_time_from_millis(millis, GPR_TIMESPAN));
 }
 
 grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec ts) {
-  return timespec_to_atm_round_down(ts);
+  return timespec_to_atm_round_down(
+      gpr_convert_clock_type(ts, g_start_time.clock_type));
 }
 
 grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec ts) {
-  return timespec_to_atm_round_up(ts);
-}
-
-void grpc_exec_ctx_maybe_update_start_time(grpc_exec_ctx* exec_ctx) {
-  grpc_millis now = grpc_exec_ctx_now(exec_ctx);
-  grpc_millis last_start_time_update =
-      gpr_atm_no_barrier_load(&g_last_start_time_update);
-
-  if (now > last_start_time_update &&
-      now - last_start_time_update > GRPC_START_TIME_UPDATE_INTERVAL) {
-    /* Get the current system time and subtract \a now from it, where \a now is
-     * the relative time from grpc_init() from monotonic clock. This calibrates
-     * the time when grpc_exec_ctx_global_init was called based on current
-     * system clock. */
-    gpr_atm_no_barrier_store(&g_last_start_time_update, now);
-    gpr_timespec real_now = gpr_now(GPR_CLOCK_REALTIME);
-    gpr_timespec real_start_time =
-        gpr_time_sub(real_now, gpr_time_from_millis(now, GPR_TIMESPAN));
-    time_atm_pair_store(&g_start_time[GPR_CLOCK_REALTIME], real_start_time);
-
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
-      gpr_log(GPR_DEBUG, "Update realtime clock start time: %" PRId64 "s %dns",
-              real_start_time.tv_sec, real_start_time.tv_nsec);
-    }
-  }
+  return timespec_to_atm_round_up(
+      gpr_convert_clock_type(ts, g_start_time.clock_type));
 }
 
 static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = {
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index 6035e08..bd27506 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -124,8 +124,6 @@
 grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec timespec);
 grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec timespec);
 
-void grpc_exec_ctx_maybe_update_start_time(grpc_exec_ctx* exec_ctx);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc
index 6097d66..d8a195f 100644
--- a/src/core/lib/iomgr/executor.cc
+++ b/src/core/lib/iomgr/executor.cc
@@ -51,8 +51,7 @@
 
 GPR_TLS_DECL(g_this_thread_state);
 
-static grpc_tracer_flag executor_trace =
-    GRPC_TRACER_INITIALIZER(false, "executor");
+grpc_core::TraceFlag executor_trace(false, "executor");
 
 static void executor_thread(void* arg);
 
@@ -63,7 +62,7 @@
   while (c != nullptr) {
     grpc_closure* next = c->next_data.next;
     grpc_error* error = c->error_data.error;
-    if (GRPC_TRACER_ON(executor_trace)) {
+    if (executor_trace.enabled()) {
 #ifndef NDEBUG
       gpr_log(GPR_DEBUG, "EXECUTOR: run %p [created by %s:%d]", c,
               c->file_created, c->line_created);
@@ -134,7 +133,6 @@
 }
 
 void grpc_executor_init(grpc_exec_ctx* exec_ctx) {
-  grpc_register_tracer(&executor_trace);
   gpr_atm_no_barrier_store(&g_cur_threads, 0);
   grpc_executor_set_threading(exec_ctx, true);
 }
@@ -152,7 +150,7 @@
 
   size_t subtract_depth = 0;
   for (;;) {
-    if (GRPC_TRACER_ON(executor_trace)) {
+    if (executor_trace.enabled()) {
       gpr_log(GPR_DEBUG, "EXECUTOR[%d]: step (sub_depth=%" PRIdPTR ")",
               (int)(ts - g_thread_state), subtract_depth);
     }
@@ -163,7 +161,7 @@
       gpr_cv_wait(&ts->cv, &ts->mu, gpr_inf_future(GPR_CLOCK_REALTIME));
     }
     if (ts->shutdown) {
-      if (GRPC_TRACER_ON(executor_trace)) {
+      if (executor_trace.enabled()) {
         gpr_log(GPR_DEBUG, "EXECUTOR[%d]: shutdown",
                 (int)(ts - g_thread_state));
       }
@@ -174,7 +172,7 @@
     grpc_closure_list exec = ts->elems;
     ts->elems = GRPC_CLOSURE_LIST_INIT;
     gpr_mu_unlock(&ts->mu);
-    if (GRPC_TRACER_ON(executor_trace)) {
+    if (executor_trace.enabled()) {
       gpr_log(GPR_DEBUG, "EXECUTOR[%d]: execute", (int)(ts - g_thread_state));
     }
 
@@ -196,7 +194,7 @@
     retry_push = false;
     size_t cur_thread_count = (size_t)gpr_atm_no_barrier_load(&g_cur_threads);
     if (cur_thread_count == 0) {
-      if (GRPC_TRACER_ON(executor_trace)) {
+      if (executor_trace.enabled()) {
 #ifndef NDEBUG
         gpr_log(GPR_DEBUG, "EXECUTOR: schedule %p (created %s:%d) inline",
                 closure, closure->file_created, closure->line_created);
@@ -217,7 +215,7 @@
 
     bool try_new_thread;
     for (;;) {
-      if (GRPC_TRACER_ON(executor_trace)) {
+      if (executor_trace.enabled()) {
 #ifndef NDEBUG
         gpr_log(
             GPR_DEBUG,
diff --git a/src/core/lib/iomgr/iomgr_posix.cc b/src/core/lib/iomgr/iomgr_posix.cc
index f5875a2..f8f6fe2 100644
--- a/src/core/lib/iomgr/iomgr_posix.cc
+++ b/src/core/lib/iomgr/iomgr_posix.cc
@@ -28,7 +28,6 @@
 void grpc_iomgr_platform_init(void) {
   grpc_wakeup_fd_global_init();
   grpc_event_engine_init();
-  grpc_register_tracer(&grpc_tcp_trace);
 }
 
 void grpc_iomgr_platform_flush(void) {}
diff --git a/src/core/lib/iomgr/iomgr_uv.cc b/src/core/lib/iomgr/iomgr_uv.cc
index df5d23a..b8a10f2 100644
--- a/src/core/lib/iomgr/iomgr_uv.cc
+++ b/src/core/lib/iomgr/iomgr_uv.cc
@@ -31,7 +31,7 @@
 void grpc_iomgr_platform_init(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_pollset_global_init();
-  grpc_register_tracer(&grpc_tcp_trace);
+
   grpc_executor_set_threading(&exec_ctx, false);
   g_init_thread = gpr_thd_currentid();
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/src/core/lib/iomgr/lockfree_event.cc b/src/core/lib/iomgr/lockfree_event.cc
index 40e2ed6..f0e798e 100644
--- a/src/core/lib/iomgr/lockfree_event.cc
+++ b/src/core/lib/iomgr/lockfree_event.cc
@@ -22,7 +22,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-extern grpc_tracer_flag grpc_polling_trace;
+extern grpc_core::TraceFlag grpc_polling_trace;
 
 /* 'state' holds the to call when the fd is readable or writable respectively.
    It can contain one of the following values:
@@ -57,7 +57,9 @@
 
 namespace grpc_core {
 
-LockfreeEvent::LockfreeEvent() {
+LockfreeEvent::LockfreeEvent() { InitEvent(); }
+
+void LockfreeEvent::InitEvent() {
   /* Perform an atomic store to start the state machine.
 
      Note carefully that LockfreeEvent *MAY* be used whilst in a destroyed
@@ -67,7 +69,7 @@
   gpr_atm_no_barrier_store(&state_, kClosureNotReady);
 }
 
-LockfreeEvent::~LockfreeEvent() {
+void LockfreeEvent::DestroyEvent() {
   gpr_atm curr;
   do {
     curr = gpr_atm_no_barrier_load(&state_);
@@ -86,7 +88,7 @@
 void LockfreeEvent::NotifyOn(grpc_exec_ctx* exec_ctx, grpc_closure* closure) {
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "LockfreeEvent::NotifyOn: %p curr=%p closure=%p", this,
               (void*)curr, closure);
     }
@@ -153,7 +155,7 @@
 
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "LockfreeEvent::SetShutdown: %p curr=%p err=%s",
               &state_, (void*)curr, grpc_error_string(shutdown_err));
     }
@@ -202,7 +204,7 @@
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
 
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "LockfreeEvent::SetReady: %p curr=%p", &state_,
               (void*)curr);
     }
diff --git a/src/core/lib/iomgr/lockfree_event.h b/src/core/lib/iomgr/lockfree_event.h
index c667dcd..aec67a3 100644
--- a/src/core/lib/iomgr/lockfree_event.h
+++ b/src/core/lib/iomgr/lockfree_event.h
@@ -30,11 +30,16 @@
 class LockfreeEvent {
  public:
   LockfreeEvent();
-  ~LockfreeEvent();
 
   LockfreeEvent(const LockfreeEvent&) = delete;
   LockfreeEvent& operator=(const LockfreeEvent&) = delete;
 
+  // These methods are used to initialize and destroy the internal state. These
+  // cannot be done in constructor and destructor because SetReady may be called
+  // when the event is destroyed and put in a freelist.
+  void InitEvent();
+  void DestroyEvent();
+
   bool IsShutdown() const {
     return (gpr_atm_no_barrier_load(&state_) & kShutdownBit) != 0;
   }
diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h
index c99b930..6911a8e 100644
--- a/src/core/lib/iomgr/pollset.h
+++ b/src/core/lib/iomgr/pollset.h
@@ -29,9 +29,7 @@
 extern "C" {
 #endif
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_fd_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount;
 
 /* A grpc_pollset is a set of file descriptors that a higher level item is
    interested in. For example:
diff --git a/src/core/lib/iomgr/pollset_uv.cc b/src/core/lib/iomgr/pollset_uv.cc
index 1d54942..16132f3 100644
--- a/src/core/lib/iomgr/pollset_uv.cc
+++ b/src/core/lib/iomgr/pollset_uv.cc
@@ -34,10 +34,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_fd_refcount =
-    GRPC_TRACER_INITIALIZER(false, "fd_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 
 struct grpc_pollset {
   uv_timer_t* timer;
diff --git a/src/core/lib/iomgr/pollset_windows.cc b/src/core/lib/iomgr/pollset_windows.cc
index 5998b3f..95dd7d7 100644
--- a/src/core/lib/iomgr/pollset_windows.cc
+++ b/src/core/lib/iomgr/pollset_windows.cc
@@ -30,10 +30,7 @@
 
 #define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1)
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_fd_refcount =
-    GRPC_TRACER_INITIALIZER(false, "fd_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 
 gpr_mu grpc_polling_mu;
 static grpc_pollset_worker* g_active_poller;
diff --git a/src/core/lib/iomgr/resource_quota.cc b/src/core/lib/iomgr/resource_quota.cc
index 9a44fa2..ccd8d9f 100644
--- a/src/core/lib/iomgr/resource_quota.cc
+++ b/src/core/lib/iomgr/resource_quota.cc
@@ -31,8 +31,7 @@
 
 #include "src/core/lib/iomgr/combiner.h"
 
-grpc_tracer_flag grpc_resource_quota_trace =
-    GRPC_TRACER_INITIALIZER(false, "resource_quota");
+grpc_core::TraceFlag grpc_resource_quota_trace(false, "resource_quota");
 
 #define MEMORY_USAGE_ESTIMATION_MAX 65536
 
@@ -293,7 +292,7 @@
   while ((resource_user = rulist_pop_head(resource_quota,
                                           GRPC_RULIST_AWAITING_ALLOCATION))) {
     gpr_mu_lock(&resource_user->mu);
-    if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+    if (grpc_resource_quota_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "RQ: check allocation for user %p shutdown=%" PRIdPTR
               " free_pool=%" PRId64,
@@ -319,14 +318,14 @@
       resource_user->free_pool = 0;
       resource_quota->free_pool -= amt;
       rq_update_estimate(resource_quota);
-      if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+      if (grpc_resource_quota_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "RQ %s %s: grant alloc %" PRId64
                 " bytes; rq_free_pool -> %" PRId64,
                 resource_quota->name, resource_user->name, amt,
                 resource_quota->free_pool);
       }
-    } else if (GRPC_TRACER_ON(grpc_resource_quota_trace) &&
+    } else if (grpc_resource_quota_trace.enabled() &&
                resource_user->free_pool >= 0) {
       gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request",
               resource_quota->name, resource_user->name);
@@ -357,7 +356,7 @@
       resource_user->free_pool = 0;
       resource_quota->free_pool += amt;
       rq_update_estimate(resource_quota);
-      if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+      if (grpc_resource_quota_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
                 " bytes; rq_free_pool -> %" PRId64,
@@ -381,7 +380,7 @@
                                  : GRPC_RULIST_RECLAIMER_BENIGN;
   grpc_resource_user* resource_user = rulist_pop_head(resource_quota, list);
   if (resource_user == nullptr) return false;
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation",
             resource_quota->name, resource_user->name,
             destructive ? "destructive" : "benign");
@@ -515,7 +514,7 @@
 }
 
 static void ru_shutdown(grpc_exec_ctx* exec_ctx, void* ru, grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RU shutdown %p", ru);
   }
   grpc_resource_user* resource_user = (grpc_resource_user*)ru;
@@ -813,7 +812,7 @@
   ru_ref_by(resource_user, (gpr_atm)size);
   resource_user->free_pool -= (int64_t)size;
   resource_user->outstanding_allocations += (int64_t)size;
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
             resource_user->resource_quota->name, resource_user->name, size,
             resource_user->free_pool);
@@ -838,7 +837,7 @@
   gpr_mu_lock(&resource_user->mu);
   bool was_zero_or_negative = resource_user->free_pool <= 0;
   resource_user->free_pool += (int64_t)size;
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64,
             resource_user->resource_quota->name, resource_user->name, size,
             resource_user->free_pool);
@@ -867,7 +866,7 @@
 
 void grpc_resource_user_finish_reclamation(grpc_exec_ctx* exec_ctx,
                                            grpc_resource_user* resource_user) {
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
             resource_user->resource_quota->name, resource_user->name);
   }
@@ -896,10 +895,3 @@
   grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user,
                            count * length, &slice_allocator->on_allocated);
 }
-
-grpc_slice grpc_resource_user_slice_malloc(grpc_exec_ctx* exec_ctx,
-                                           grpc_resource_user* resource_user,
-                                           size_t size) {
-  grpc_resource_user_alloc(exec_ctx, resource_user, size, nullptr);
-  return ru_slice_create(resource_user, size);
-}
diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h
index fcdf9c2..3af93a8 100644
--- a/src/core/lib/iomgr/resource_quota.h
+++ b/src/core/lib/iomgr/resource_quota.h
@@ -65,7 +65,7 @@
     maintain lists of users (which users arrange to leave before they are
     destroyed) */
 
-extern grpc_tracer_flag grpc_resource_quota_trace;
+extern grpc_core::TraceFlag grpc_resource_quota_trace;
 
 grpc_resource_quota* grpc_resource_quota_ref_internal(
     grpc_resource_quota* resource_quota);
diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc
index cb0f627..8a6262b 100644
--- a/src/core/lib/iomgr/tcp_client_posix.cc
+++ b/src/core/lib/iomgr/tcp_client_posix.cc
@@ -43,7 +43,7 @@
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
 #include "src/core/lib/support/string.h"
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 typedef struct {
   gpr_mu mu;
@@ -99,7 +99,7 @@
 static void tc_on_alarm(grpc_exec_ctx* exec_ctx, void* acp, grpc_error* error) {
   int done;
   async_connect* ac = (async_connect*)acp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str,
             str);
@@ -138,7 +138,7 @@
 
   GRPC_ERROR_REF(error);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s",
             ac->addr_str, str);
@@ -317,7 +317,7 @@
                     grpc_schedule_on_exec_ctx);
   ac->channel_args = grpc_channel_args_copy(channel_args);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting fd %p",
             ac->addr_str, fdobj);
   }
diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc
index 15345c8..7a5727e 100644
--- a/src/core/lib/iomgr/tcp_client_uv.cc
+++ b/src/core/lib/iomgr/tcp_client_uv.cc
@@ -32,7 +32,7 @@
 #include "src/core/lib/iomgr/tcp_uv.h"
 #include "src/core/lib/iomgr/timer.h"
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 typedef struct grpc_uv_tcp_connect {
   uv_connect_t connect_req;
@@ -59,7 +59,7 @@
                            grpc_error* error) {
   int done;
   grpc_uv_tcp_connect* connect = (grpc_uv_tcp_connect*)acp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s",
             connect->addr_name, str);
@@ -147,7 +147,7 @@
   connect->connect_req.data = connect;
   connect->refs = 2;  // One for the connect operation, one for the timer.
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting",
             connect->addr_name);
   }
diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc
index cb90933..d09cfca 100644
--- a/src/core/lib/iomgr/tcp_posix.cc
+++ b/src/core/lib/iomgr/tcp_posix.cc
@@ -61,7 +61,7 @@
 typedef size_t msg_iovlen_type;
 #endif
 
-grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp");
+grpc_core::TraceFlag grpc_tcp_trace(false, "tcp");
 
 typedef struct {
   grpc_endpoint base;
@@ -81,9 +81,7 @@
 
   grpc_slice_buffer* incoming_buffer;
   grpc_slice_buffer* outgoing_buffer;
-  /** slice within outgoing_buffer to write next */
-  size_t outgoing_slice_idx;
-  /** byte within outgoing_buffer->slices[outgoing_slice_idx] to write next */
+  /** byte within outgoing_buffer->slices[0] to write next */
   size_t outgoing_byte_idx;
 
   grpc_closure* read_cb;
@@ -121,7 +119,7 @@
 static void done_poller(grpc_exec_ctx* exec_ctx, void* bp,
                         grpc_error* error_ignored) {
   backup_poller* p = (backup_poller*)bp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p destroy", p);
   }
   grpc_pollset_destroy(exec_ctx, BACKUP_POLLER_POLLSET(p));
@@ -131,7 +129,7 @@
 static void run_poller(grpc_exec_ctx* exec_ctx, void* bp,
                        grpc_error* error_ignored) {
   backup_poller* p = (backup_poller*)bp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p run", p);
   }
   gpr_mu_lock(p->pollset_mu);
@@ -147,18 +145,18 @@
       gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) {
     gpr_mu_lock(p->pollset_mu);
     bool cas_ok = gpr_atm_full_cas(&g_backup_poller, (gpr_atm)p, 0);
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok);
     }
     gpr_mu_unlock(p->pollset_mu);
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p shutdown", p);
     }
     grpc_pollset_shutdown(exec_ctx, BACKUP_POLLER_POLLSET(p),
                           GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p,
                                             grpc_schedule_on_exec_ctx));
   } else {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p reschedule", p);
     }
     GRPC_CLOSURE_SCHED(exec_ctx, &p->run_poller, GRPC_ERROR_NONE);
@@ -169,7 +167,7 @@
   backup_poller* p = (backup_poller*)gpr_atm_acq_load(&g_backup_poller);
   gpr_atm old_count =
       gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, -1);
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p uncover cnt %d->%d", p, (int)old_count,
             (int)old_count - 1);
   }
@@ -180,14 +178,14 @@
   backup_poller* p;
   gpr_atm old_count =
       gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2);
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER: cover cnt %d->%d", (int)old_count,
             2 + (int)old_count);
   }
   if (old_count == 0) {
     GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED(exec_ctx);
     p = (backup_poller*)gpr_zalloc(sizeof(*p) + grpc_pollset_size());
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p create", p);
     }
     grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu);
@@ -203,7 +201,7 @@
       // spin waiting for backup poller
     }
   }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p add %p", p, tcp);
   }
   grpc_pollset_add_fd(exec_ctx, BACKUP_POLLER_POLLSET(p), tcp->em_fd);
@@ -213,7 +211,7 @@
 }
 
 static void notify_on_read(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p notify_on_read", tcp);
   }
   GRPC_CLOSURE_INIT(&tcp->read_done_closure, tcp_handle_read, tcp,
@@ -222,7 +220,7 @@
 }
 
 static void notify_on_write(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p notify_on_write", tcp);
   }
   cover_self(exec_ctx, tcp);
@@ -234,7 +232,7 @@
 
 static void tcp_drop_uncovered_then_handle_write(grpc_exec_ctx* exec_ctx,
                                                  void* arg, grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p got_write: %s", arg, grpc_error_string(error));
   }
   drop_uncovered(exec_ctx, (grpc_tcp*)arg);
@@ -311,7 +309,7 @@
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
                       const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -324,7 +322,7 @@
 
 static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
                     int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -355,7 +353,7 @@
                          grpc_error* error) {
   grpc_closure* cb = tcp->read_cb;
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg);
     size_t i;
     const char* str = grpc_error_string(error);
@@ -451,7 +449,7 @@
 static void tcp_read_allocation_done(grpc_exec_ctx* exec_ctx, void* tcpp,
                                      grpc_error* error) {
   grpc_tcp* tcp = (grpc_tcp*)tcpp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp,
             grpc_error_string(error));
   }
@@ -470,13 +468,13 @@
   size_t target_read_size = get_target_read_size(tcp);
   if (tcp->incoming_buffer->length < target_read_size &&
       tcp->incoming_buffer->count < MAX_READ_IOVEC) {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "TCP:%p alloc_slices", tcp);
     }
     grpc_resource_user_alloc_slices(exec_ctx, &tcp->slice_allocator,
                                     target_read_size, 1, tcp->incoming_buffer);
   } else {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "TCP:%p do_read", tcp);
     }
     tcp_do_read(exec_ctx, tcp);
@@ -487,7 +485,7 @@
                             grpc_error* error) {
   grpc_tcp* tcp = (grpc_tcp*)arg;
   GPR_ASSERT(!tcp->finished_edge);
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p got_read: %s", tcp, grpc_error_string(error));
   }
 
@@ -532,23 +530,26 @@
   size_t unwind_slice_idx;
   size_t unwind_byte_idx;
 
+  // We always start at zero, because we eagerly unref and trim the slice
+  // buffer as we write
+  size_t outgoing_slice_idx = 0;
+
   for (;;) {
     sending_length = 0;
-    unwind_slice_idx = tcp->outgoing_slice_idx;
+    unwind_slice_idx = outgoing_slice_idx;
     unwind_byte_idx = tcp->outgoing_byte_idx;
-    for (iov_size = 0; tcp->outgoing_slice_idx != tcp->outgoing_buffer->count &&
+    for (iov_size = 0; outgoing_slice_idx != tcp->outgoing_buffer->count &&
                        iov_size != MAX_WRITE_IOVEC;
          iov_size++) {
       iov[iov_size].iov_base =
           GRPC_SLICE_START_PTR(
-              tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) +
+              tcp->outgoing_buffer->slices[outgoing_slice_idx]) +
           tcp->outgoing_byte_idx;
       iov[iov_size].iov_len =
-          GRPC_SLICE_LENGTH(
-              tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) -
+          GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]) -
           tcp->outgoing_byte_idx;
       sending_length += iov[iov_size].iov_len;
-      tcp->outgoing_slice_idx++;
+      outgoing_slice_idx++;
       tcp->outgoing_byte_idx = 0;
     }
     GPR_ASSERT(iov_size > 0);
@@ -574,16 +575,25 @@
 
     if (sent_length < 0) {
       if (errno == EAGAIN) {
-        tcp->outgoing_slice_idx = unwind_slice_idx;
         tcp->outgoing_byte_idx = unwind_byte_idx;
+        // unref all and forget about all slices that have been written to this
+        // point
+        for (size_t idx = 0; idx < unwind_slice_idx; ++idx) {
+          grpc_slice_unref_internal(
+              exec_ctx, grpc_slice_buffer_take_first(tcp->outgoing_buffer));
+        }
         return false;
       } else if (errno == EPIPE) {
         *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"),
                                     GRPC_ERROR_INT_GRPC_STATUS,
                                     GRPC_STATUS_UNAVAILABLE);
+        grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                                   tcp->outgoing_buffer);
         return true;
       } else {
         *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp);
+        grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                                   tcp->outgoing_buffer);
         return true;
       }
     }
@@ -593,9 +603,9 @@
     while (trailing > 0) {
       size_t slice_length;
 
-      tcp->outgoing_slice_idx--;
-      slice_length = GRPC_SLICE_LENGTH(
-          tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]);
+      outgoing_slice_idx--;
+      slice_length =
+          GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]);
       if (slice_length > trailing) {
         tcp->outgoing_byte_idx = slice_length - trailing;
         break;
@@ -604,11 +614,13 @@
       }
     }
 
-    if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) {
+    if (outgoing_slice_idx == tcp->outgoing_buffer->count) {
       *error = GRPC_ERROR_NONE;
+      grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                                 tcp->outgoing_buffer);
       return true;
     }
-  };
+  }
 }
 
 static void tcp_handle_write(grpc_exec_ctx* exec_ctx, void* arg /* grpc_tcp */,
@@ -625,14 +637,14 @@
   }
 
   if (!tcp_flush(exec_ctx, tcp, &error)) {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "write: delayed");
     }
     notify_on_write(exec_ctx, tcp);
   } else {
     cb = tcp->write_cb;
     tcp->write_cb = nullptr;
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       const char* str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "write: %s", str);
     }
@@ -647,7 +659,7 @@
   grpc_tcp* tcp = (grpc_tcp*)ep;
   grpc_error* error = GRPC_ERROR_NONE;
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     size_t i;
 
     for (i = 0; i < buf->count; i++) {
@@ -672,18 +684,17 @@
     return;
   }
   tcp->outgoing_buffer = buf;
-  tcp->outgoing_slice_idx = 0;
   tcp->outgoing_byte_idx = 0;
 
   if (!tcp_flush(exec_ctx, tcp, &error)) {
     TCP_REF(tcp, "write");
     tcp->write_cb = cb;
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "write: delayed");
     }
     notify_on_write(exec_ctx, tcp);
   } else {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       const char* str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "write: %s", str);
     }
diff --git a/src/core/lib/iomgr/tcp_posix.h b/src/core/lib/iomgr/tcp_posix.h
index ff1060b..ba85146 100644
--- a/src/core/lib/iomgr/tcp_posix.h
+++ b/src/core/lib/iomgr/tcp_posix.h
@@ -37,7 +37,7 @@
 extern "C" {
 #endif
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 /* Create a tcp endpoint given a file desciptor and a read slice size.
    Takes ownership of fd. */
diff --git a/src/core/lib/iomgr/tcp_server_posix.cc b/src/core/lib/iomgr/tcp_server_posix.cc
index f84fa97..6fed13c 100644
--- a/src/core/lib/iomgr/tcp_server_posix.cc
+++ b/src/core/lib/iomgr/tcp_server_posix.cc
@@ -243,7 +243,7 @@
     addr_str = grpc_sockaddr_to_uri(&addr);
     gpr_asprintf(&name, "tcp-server-connection:%s", addr_str);
 
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming connection: %s", addr_str);
     }
 
diff --git a/src/core/lib/iomgr/tcp_server_uv.cc b/src/core/lib/iomgr/tcp_server_uv.cc
index 0eed4d4..2c76fae 100644
--- a/src/core/lib/iomgr/tcp_server_uv.cc
+++ b/src/core/lib/iomgr/tcp_server_uv.cc
@@ -213,7 +213,7 @@
   } else {
     gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(err));
   }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     if (peer_name_string) {
       gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s",
               sp->server, peer_name_string);
@@ -247,7 +247,7 @@
 
   GPR_ASSERT(!sp->has_pending_connection);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p incoming connection", sp->server);
   }
 
@@ -403,7 +403,7 @@
 
   gpr_free(allocated_addr);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     char* port_string;
     grpc_sockaddr_to_string(&port_string, addr, 0);
     const char* str = grpc_error_string(error);
@@ -435,7 +435,7 @@
   (void)pollsets;
   (void)pollset_count;
   GRPC_UV_ASSERT_SAME_THREAD();
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "SERVER_START %p", server);
   }
   GPR_ASSERT(on_accept_cb);
diff --git a/src/core/lib/iomgr/tcp_uv.cc b/src/core/lib/iomgr/tcp_uv.cc
index ac9ca4e..40f4006 100644
--- a/src/core/lib/iomgr/tcp_uv.cc
+++ b/src/core/lib/iomgr/tcp_uv.cc
@@ -38,7 +38,7 @@
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
-grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp");
+grpc_core::TraceFlag grpc_tcp_trace(false, "tcp");
 
 typedef struct {
   grpc_endpoint base;
@@ -52,12 +52,12 @@
   grpc_closure* read_cb;
   grpc_closure* write_cb;
 
-  grpc_slice read_slice;
   grpc_slice_buffer* read_slices;
   grpc_slice_buffer* write_slices;
   uv_buf_t* write_buffers;
 
   grpc_resource_user* resource_user;
+  grpc_resource_user_slice_allocator slice_allocator;
 
   bool shutting_down;
 
@@ -66,7 +66,6 @@
 } grpc_tcp;
 
 static void tcp_free(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp) {
-  grpc_slice_unref_internal(exec_ctx, tcp->read_slice);
   grpc_resource_user_unref(exec_ctx, tcp->resource_user);
   gpr_free(tcp->handle);
   gpr_free(tcp->peer_string);
@@ -79,7 +78,7 @@
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
                       const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -92,7 +91,7 @@
 
 static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
                     int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -119,91 +118,117 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static grpc_slice alloc_read_slice(grpc_exec_ctx* exec_ctx,
-                                   grpc_resource_user* resource_user) {
-  return grpc_resource_user_slice_malloc(exec_ctx, resource_user,
-                                         GRPC_TCP_DEFAULT_READ_SLICE_SIZE);
-}
-
 static void alloc_uv_buf(uv_handle_t* handle, size_t suggested_size,
                          uv_buf_t* buf) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_tcp* tcp = (grpc_tcp*)handle->data;
   (void)suggested_size;
-  buf->base = (char*)GRPC_SLICE_START_PTR(tcp->read_slice);
-  buf->len = GRPC_SLICE_LENGTH(tcp->read_slice);
+  /* Before calling uv_read_start, we allocate a buffer with exactly one slice
+   * to tcp->read_slices and wait for the callback indicating that the
+   * allocation was successful. So slices[0] should always exist here */
+  buf->base = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[0]);
+  buf->len = GRPC_SLICE_LENGTH(tcp->read_slices->slices[0]);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
+static void call_read_cb(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
+                         grpc_error* error) {
+  grpc_closure* cb = tcp->read_cb;
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg);
+    size_t i;
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "read: error=%s", str);
+
+    for (i = 0; i < tcp->read_slices->count; i++) {
+      char* dump = grpc_dump_slice(tcp->read_slices->slices[i],
+                                   GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump);
+      gpr_free(dump);
+    }
+  }
+  tcp->read_slices = NULL;
+  tcp->read_cb = NULL;
+  GRPC_CLOSURE_RUN(exec_ctx, cb, error);
+}
+
 static void read_callback(uv_stream_t* stream, ssize_t nread,
                           const uv_buf_t* buf) {
-  grpc_slice sub;
   grpc_error* error;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_tcp* tcp = (grpc_tcp*)stream->data;
-  grpc_closure* cb = tcp->read_cb;
+  grpc_slice_buffer garbage;
   if (nread == 0) {
     // Nothing happened. Wait for the next callback
     return;
   }
   TCP_UNREF(&exec_ctx, tcp, "read");
-  tcp->read_cb = NULL;
   // TODO(murgatroid99): figure out what the return value here means
   uv_read_stop(stream);
   if (nread == UV_EOF) {
     error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF");
+    grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, tcp->read_slices);
   } else if (nread > 0) {
     // Successful read
-    sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, (size_t)nread);
-    grpc_slice_buffer_add(tcp->read_slices, sub);
-    tcp->read_slice = alloc_read_slice(&exec_ctx, tcp->resource_user);
     error = GRPC_ERROR_NONE;
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
-      size_t i;
-      const char* str = grpc_error_string(error);
-      gpr_log(GPR_DEBUG, "read: error=%s", str);
-
-      for (i = 0; i < tcp->read_slices->count; i++) {
-        char* dump = grpc_dump_slice(tcp->read_slices->slices[i],
-                                     GPR_DUMP_HEX | GPR_DUMP_ASCII);
-        gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string,
-                dump);
-        gpr_free(dump);
-      }
+    if ((size_t)nread < tcp->read_slices->length) {
+      /* TODO(murgatroid99): Instead of discarding the unused part of the read
+       * buffer, reuse it as the next read buffer. */
+      grpc_slice_buffer_init(&garbage);
+      grpc_slice_buffer_trim_end(
+          tcp->read_slices, tcp->read_slices->length - (size_t)nread, &garbage);
+      grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &garbage);
     }
   } else {
     // nread < 0: Error
     error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed");
+    grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, tcp->read_slices);
   }
-  GRPC_CLOSURE_SCHED(&exec_ctx, cb, error);
+  call_read_cb(&exec_ctx, tcp, error);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
+static void tcp_read_allocation_done(grpc_exec_ctx* exec_ctx, void* tcpp,
+                                     grpc_error* error) {
+  int status;
+  grpc_tcp* tcp = (grpc_tcp*)tcpp;
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp,
+            grpc_error_string(error));
+  }
+  if (error == GRPC_ERROR_NONE) {
+    status =
+        uv_read_start((uv_stream_t*)tcp->handle, alloc_uv_buf, read_callback);
+    if (status != 0) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed at start");
+      error = grpc_error_set_str(
+          error, GRPC_ERROR_STR_OS_ERROR,
+          grpc_slice_from_static_string(uv_strerror(status)));
+    }
+  }
+  if (error != GRPC_ERROR_NONE) {
+    grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->read_slices);
+    call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
+    TCP_UNREF(exec_ctx, tcp, "read");
+  }
+  if (grpc_tcp_trace.enabled()) {
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str);
+  }
+}
+
 static void uv_endpoint_read(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep,
                              grpc_slice_buffer* read_slices, grpc_closure* cb) {
   grpc_tcp* tcp = (grpc_tcp*)ep;
-  int status;
-  grpc_error* error = GRPC_ERROR_NONE;
   GRPC_UV_ASSERT_SAME_THREAD();
   GPR_ASSERT(tcp->read_cb == NULL);
   tcp->read_cb = cb;
   tcp->read_slices = read_slices;
   grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices);
   TCP_REF(tcp, "read");
-  // TODO(murgatroid99): figure out what the return value here means
-  status =
-      uv_read_start((uv_stream_t*)tcp->handle, alloc_uv_buf, read_callback);
-  if (status != 0) {
-    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed at start");
-    error =
-        grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR,
-                           grpc_slice_from_static_string(uv_strerror(status)));
-    GRPC_CLOSURE_SCHED(exec_ctx, cb, error);
-  }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
-    const char* str = grpc_error_string(error);
-    gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str);
-  }
+  grpc_resource_user_alloc_slices(exec_ctx, &tcp->slice_allocator,
+                                  GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1,
+                                  tcp->read_slices);
 }
 
 static void write_callback(uv_write_t* req, int status) {
@@ -218,13 +243,11 @@
   } else {
     error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Write failed");
   }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp, str);
   }
   gpr_free(tcp->write_buffers);
-  grpc_resource_user_free(&exec_ctx, tcp->resource_user,
-                          sizeof(uv_buf_t) * tcp->write_slices->count);
   GRPC_CLOSURE_SCHED(&exec_ctx, cb, error);
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -240,7 +263,7 @@
   uv_write_t* write_req;
   GRPC_UV_ASSERT_SAME_THREAD();
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     size_t j;
 
     for (j = 0; j < write_slices->count; j++) {
@@ -271,8 +294,6 @@
   tcp->write_cb = cb;
   buffer_count = (unsigned int)tcp->write_slices->count;
   buffers = (uv_buf_t*)gpr_malloc(sizeof(uv_buf_t) * buffer_count);
-  grpc_resource_user_alloc(exec_ctx, tcp->resource_user,
-                           sizeof(uv_buf_t) * buffer_count, NULL);
   for (i = 0; i < buffer_count; i++) {
     slice = &tcp->write_slices->slices[i];
     buffers[i].base = (char*)GRPC_SLICE_START_PTR(*slice);
@@ -320,7 +341,7 @@
                                  grpc_error* why) {
   grpc_tcp* tcp = (grpc_tcp*)ep;
   if (!tcp->shutting_down) {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       const char* str = grpc_error_string(why);
       gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->handle, str);
     }
@@ -367,7 +388,7 @@
   grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp);
   }
 
@@ -381,8 +402,10 @@
   gpr_ref_init(&tcp->refcount, 1);
   tcp->peer_string = gpr_strdup(peer_string);
   tcp->shutting_down = false;
+  tcp->read_slices = NULL;
   tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
-  tcp->read_slice = alloc_read_slice(&exec_ctx, tcp->resource_user);
+  grpc_resource_user_slice_allocator_init(
+      &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp);
   /* Tell network status tracking code about the new endpoint */
   grpc_network_status_register_endpoint(&tcp->base);
 
diff --git a/src/core/lib/iomgr/tcp_uv.h b/src/core/lib/iomgr/tcp_uv.h
index 708e846..4b4da36 100644
--- a/src/core/lib/iomgr/tcp_uv.h
+++ b/src/core/lib/iomgr/tcp_uv.h
@@ -38,7 +38,7 @@
 
 #include <uv.h>
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 #define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192
 
diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc
index 04922b4..33868cd 100644
--- a/src/core/lib/iomgr/tcp_windows.cc
+++ b/src/core/lib/iomgr/tcp_windows.cc
@@ -49,7 +49,7 @@
 #define GRPC_FIONBIO FIONBIO
 #endif
 
-grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp");
+grpc_core::TraceFlag grpc_tcp_trace(false, "tcp");
 
 static grpc_error* set_non_block(SOCKET sock) {
   int status;
@@ -124,7 +124,7 @@
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
                       const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -137,7 +137,7 @@
 
 static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
                     int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc
index a4bfbcb..fa95c43 100644
--- a/src/core/lib/iomgr/timer_generic.cc
+++ b/src/core/lib/iomgr/timer_generic.cc
@@ -42,11 +42,8 @@
 #define MIN_QUEUE_WINDOW_DURATION 0.01
 #define MAX_QUEUE_WINDOW_DURATION 1
 
-extern "C" {
-grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer");
-grpc_tracer_flag grpc_timer_check_trace =
-    GRPC_TRACER_INITIALIZER(false, "timer_check");
-}
+grpc_core::TraceFlag grpc_timer_trace(false, "timer");
+grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check");
 
 /* A "timer shard". Contains a 'heap' and a 'list' of timers. All timers with
  * deadlines earlier than 'queue_deadline" cap are maintained in the heap and
@@ -253,8 +250,6 @@
   g_shared_mutables.min_timer = grpc_exec_ctx_now(exec_ctx);
   gpr_tls_init(&g_last_seen_min_timer);
   gpr_tls_set(&g_last_seen_min_timer, 0);
-  grpc_register_tracer(&grpc_timer_trace);
-  grpc_register_tracer(&grpc_timer_check_trace);
 
   for (i = 0; i < g_num_shards; i++) {
     timer_shard* shard = &g_shards[i];
@@ -339,7 +334,7 @@
   timer->hash_table_next = nullptr;
 #endif
 
-  if (GRPC_TRACER_ON(grpc_timer_trace)) {
+  if (grpc_timer_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "TIMER %p: SET %" PRIdPTR " now %" PRIdPTR " call %p[%p]", timer,
             deadline, grpc_exec_ctx_now(exec_ctx), closure, closure->cb);
@@ -375,7 +370,7 @@
     timer->heap_index = INVALID_HEAP_INDEX;
     list_join(&shard->list, timer);
   }
-  if (GRPC_TRACER_ON(grpc_timer_trace)) {
+  if (grpc_timer_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "  .. add to shard %d with queue_deadline_cap=%" PRIdPTR
             " => is_first_timer=%s",
@@ -397,7 +392,7 @@
      grpc_timer_check. */
   if (is_first_timer) {
     gpr_mu_lock(&g_shared_mutables.mu);
-    if (GRPC_TRACER_ON(grpc_timer_trace)) {
+    if (grpc_timer_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  .. old shard min_deadline=%" PRIdPTR,
               shard->min_deadline);
     }
@@ -427,7 +422,7 @@
 
   timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)];
   gpr_mu_lock(&shard->mu);
-  if (GRPC_TRACER_ON(grpc_timer_trace)) {
+  if (grpc_timer_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TIMER %p: CANCEL pending=%s", timer,
             timer->pending ? "true" : "false");
   }
@@ -468,7 +463,7 @@
       saturating_add(GPR_MAX(now, shard->queue_deadline_cap),
                      (gpr_atm)(deadline_delta * 1000.0));
 
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  .. shard[%d]->queue_deadline_cap --> %" PRIdPTR,
             (int)(shard - g_shards), shard->queue_deadline_cap);
   }
@@ -476,7 +471,7 @@
     next = timer->next;
 
     if (timer->deadline < shard->queue_deadline_cap) {
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG, "  .. add timer with deadline %" PRIdPTR " to heap",
                 timer->deadline);
       }
@@ -493,7 +488,7 @@
 static grpc_timer* pop_one(timer_shard* shard, gpr_atm now) {
   grpc_timer* timer;
   for (;;) {
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  .. shard[%d]: heap_empty=%s",
               (int)(shard - g_shards),
               grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false");
@@ -503,13 +498,13 @@
       if (!refill_heap(shard, now)) return nullptr;
     }
     timer = grpc_timer_heap_top(&shard->heap);
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "  .. check top timer deadline=%" PRIdPTR " now=%" PRIdPTR,
               timer->deadline, now);
     }
     if (timer->deadline > now) return nullptr;
-    if (GRPC_TRACER_ON(grpc_timer_trace)) {
+    if (grpc_timer_trace.enabled()) {
       gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRIdPTR "ms late via %s scheduler",
               timer, now - timer->deadline,
               timer->closure->scheduler->vtable->name);
@@ -534,7 +529,7 @@
   }
   *new_min_deadline = compute_min_deadline(shard);
   gpr_mu_unlock(&shard->mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  .. shard[%d] popped %" PRIdPTR,
             (int)(shard - g_shards), n);
   }
@@ -558,7 +553,7 @@
     gpr_mu_lock(&g_shared_mutables.mu);
     result = GRPC_TIMERS_CHECKED_AND_EMPTY;
 
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  .. shard[%d]->min_deadline = %" PRIdPTR,
               (int)(g_shard_queue[0] - g_shards),
               g_shard_queue[0]->min_deadline);
@@ -576,7 +571,7 @@
         result = GRPC_TIMERS_FIRED;
       }
 
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "  .. result --> %d"
                 ", shard[%d]->min_deadline %" PRIdPTR " --> %" PRIdPTR
@@ -621,7 +616,7 @@
     if (next != nullptr) {
       *next = GPR_MIN(*next, min_timer);
     }
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "TIMER CHECK SKIP: now=%" PRIdPTR " min_timer=%" PRIdPTR, now,
               min_timer);
@@ -635,7 +630,7 @@
           : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system");
 
   // tracing
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     char* next_str;
     if (next == nullptr) {
       next_str = gpr_strdup("NULL");
@@ -653,7 +648,7 @@
   grpc_timer_check_result r =
       run_some_expired_timers(exec_ctx, now, next, shutdown_error);
   // tracing
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     char* next_str;
     if (next == nullptr) {
       next_str = gpr_strdup("NULL");
diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc
index acc40b6..dac74ae 100644
--- a/src/core/lib/iomgr/timer_manager.cc
+++ b/src/core/lib/iomgr/timer_manager.cc
@@ -33,7 +33,7 @@
   struct completed_thread* next;
 } completed_thread;
 
-extern "C" grpc_tracer_flag grpc_timer_check_trace;
+extern grpc_core::TraceFlag grpc_timer_check_trace;
 
 // global mutex
 static gpr_mu g_mu;
@@ -81,7 +81,7 @@
   ++g_waiter_count;
   ++g_thread_count;
   gpr_mu_unlock(&g_mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Spawn timer thread");
   }
   gpr_thd_options opt = gpr_thd_options_default();
@@ -115,7 +115,7 @@
     // if there's no thread waiting with a timeout, kick an existing
     // waiter so that the next deadline is not missed
     if (!g_has_timed_waiter) {
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG, "kick untimed waiter");
       }
       gpr_cv_signal(&g_cv_wait);
@@ -123,7 +123,7 @@
     gpr_mu_unlock(&g_mu);
   }
   // without our lock, flush the exec_ctx
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "flush exec_ctx");
   }
   grpc_exec_ctx_flush(exec_ctx);
@@ -178,7 +178,7 @@
         g_has_timed_waiter = true;
         g_timed_waiter_deadline = next;
 
-        if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+        if (grpc_timer_check_trace.enabled()) {
           grpc_millis wait_time = next - grpc_exec_ctx_now(exec_ctx);
           gpr_log(GPR_DEBUG, "sleep for a %" PRIdPTR " milliseconds",
                   wait_time);
@@ -188,15 +188,14 @@
       }
     }
 
-    if (GRPC_TRACER_ON(grpc_timer_check_trace) &&
-        next == GRPC_MILLIS_INF_FUTURE) {
+    if (grpc_timer_check_trace.enabled() && next == GRPC_MILLIS_INF_FUTURE) {
       gpr_log(GPR_DEBUG, "sleep until kicked");
     }
 
     gpr_cv_wait(&g_cv_wait, &g_mu,
                 grpc_millis_to_timespec(next, GPR_CLOCK_REALTIME));
 
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "wait ended: was_timed:%d kicked:%d",
               my_timed_waiter_generation == g_timed_waiter_generation,
               g_kicked);
@@ -226,10 +225,6 @@
     grpc_millis next = GRPC_MILLIS_INF_FUTURE;
     grpc_exec_ctx_invalidate_now(exec_ctx);
 
-    /* Calibrate g_start_time in exec_ctx.cc with a regular interval in case the
-     * system clock has changed */
-    grpc_exec_ctx_maybe_update_start_time(exec_ctx);
-
     // check timer state, updates next to the next time to run a check
     switch (grpc_timer_check(exec_ctx, &next)) {
       case GRPC_TIMERS_FIRED:
@@ -245,7 +240,7 @@
 
            Consequently, we can just sleep forever here and be happy at some
            saved wakeup cycles. */
-        if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+        if (grpc_timer_check_trace.enabled()) {
           gpr_log(GPR_DEBUG, "timers not checked: expect another thread to");
         }
         next = GRPC_MILLIS_INF_FUTURE;
@@ -271,7 +266,7 @@
   ct->next = g_completed_threads;
   g_completed_threads = ct;
   gpr_mu_unlock(&g_mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "End timer thread");
   }
 }
@@ -314,18 +309,18 @@
 
 static void stop_threads(void) {
   gpr_mu_lock(&g_mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "stop timer threads: threaded=%d", g_threaded);
   }
   if (g_threaded) {
     g_threaded = false;
     gpr_cv_broadcast(&g_cv_wait);
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count);
     }
     while (g_thread_count > 0) {
       gpr_cv_wait(&g_cv_shutdown, &g_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count);
       }
       gc_completed_threads();
diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc
index df40e54..fac2026 100644
--- a/src/core/lib/iomgr/timer_uv.cc
+++ b/src/core/lib/iomgr/timer_uv.cc
@@ -29,11 +29,8 @@
 
 #include <uv.h>
 
-extern "C" {
-grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer");
-grpc_tracer_flag grpc_timer_check_trace =
-    GRPC_TRACER_INITIALIZER(false, "timer_check");
-}
+grpc_core::TraceFlag grpc_timer_trace(false, "timer");
+grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check");
 
 static void timer_close_callback(uv_handle_t* handle) { gpr_free(handle); }
 
diff --git a/src/core/lib/security/context/security_context.cc b/src/core/lib/security/context/security_context.cc
index 36362d9..19c6148 100644
--- a/src/core/lib/security/context/security_context.cc
+++ b/src/core/lib/security/context/security_context.cc
@@ -29,10 +29,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_auth_context_refcount =
-    GRPC_TRACER_INITIALIZER(false, "auth_context_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount(
+    false, "auth_context_refcount");
 
 /* --- grpc_call --- */
 
@@ -135,7 +133,7 @@
                                          const char* file, int line,
                                          const char* reason) {
   if (ctx == nullptr) return nullptr;
-  if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) {
+  if (grpc_trace_auth_context_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "AUTH_CONTEXT:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val,
@@ -153,7 +151,7 @@
 void grpc_auth_context_unref(grpc_auth_context* ctx, const char* file, int line,
                              const char* reason) {
   if (ctx == nullptr) return;
-  if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) {
+  if (grpc_trace_auth_context_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "AUTH_CONTEXT:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val,
diff --git a/src/core/lib/security/context/security_context.h b/src/core/lib/security/context/security_context.h
index 4f049c4..5b27d1a 100644
--- a/src/core/lib/security/context/security_context.h
+++ b/src/core/lib/security/context/security_context.h
@@ -22,9 +22,7 @@
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/security/credentials/credentials.h"
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_auth_context_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount;
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc
index d666e6b..77163c0 100644
--- a/src/core/lib/security/credentials/jwt/jwt_credentials.cc
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc
@@ -172,7 +172,7 @@
 
 grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
     const char* json_key, gpr_timespec token_lifetime, void* reserved) {
-  if (GRPC_TRACER_ON(grpc_api_trace)) {
+  if (grpc_api_trace.enabled()) {
     char* clean_json = redact_private_key(json_key);
     gpr_log(GPR_INFO,
             "grpc_service_account_jwt_access_credentials_create("
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
index 943d23f..ccefb4d 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
@@ -467,7 +467,7 @@
     const char* json_refresh_token, void* reserved) {
   grpc_auth_refresh_token token =
       grpc_auth_refresh_token_create_from_string(json_refresh_token);
-  if (GRPC_TRACER_ON(grpc_api_trace)) {
+  if (grpc_api_trace.enabled()) {
     char* loggable_token = create_loggable_refresh_token(&token);
     gpr_log(GPR_INFO,
             "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/src/core/lib/security/credentials/plugin/plugin_credentials.cc
index b83a1b4..1f1efd0 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.cc
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.cc
@@ -31,8 +31,7 @@
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/validate_metadata.h"
 
-grpc_tracer_flag grpc_plugin_credentials_trace =
-    GRPC_TRACER_INITIALIZER(false, "plugin_credentials");
+grpc_core::TraceFlag grpc_plugin_credentials_trace(false, "plugin_credentials");
 
 static void plugin_destruct(grpc_exec_ctx* exec_ctx,
                             grpc_call_credentials* creds) {
@@ -123,7 +122,7 @@
       nullptr, nullptr);
   grpc_plugin_credentials_pending_request* r =
       (grpc_plugin_credentials_pending_request*)request;
-  if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+  if (grpc_plugin_credentials_trace.enabled()) {
     gpr_log(GPR_INFO,
             "plugin_credentials[%p]: request %p: plugin returned "
             "asynchronously",
@@ -136,7 +135,7 @@
     grpc_error* error =
         process_plugin_result(&exec_ctx, r, md, num_md, status, error_details);
     GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, error);
-  } else if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+  } else if (grpc_plugin_credentials_trace.enabled()) {
     gpr_log(GPR_INFO,
             "plugin_credentials[%p]: request %p: plugin was previously "
             "cancelled",
@@ -172,7 +171,7 @@
     c->pending_requests = pending_request;
     gpr_mu_unlock(&c->mu);
     // Invoke the plugin.  The callback holds a ref to us.
-    if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+    if (grpc_plugin_credentials_trace.enabled()) {
       gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin",
               c, pending_request);
     }
@@ -185,7 +184,7 @@
                                 plugin_md_request_metadata_ready,
                                 pending_request, creds_md, &num_creds_md,
                                 &status, &error_details)) {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p: plugin will return "
                 "asynchronously",
@@ -200,7 +199,7 @@
     // asynchronously by plugin_cancel_get_request_metadata(), so return
     // false.  Otherwise, process the result.
     if (pending_request->cancelled) {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p was cancelled, error "
                 "will be returned asynchronously",
@@ -208,7 +207,7 @@
       }
       retval = false;
     } else {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p: plugin returned "
                 "synchronously",
@@ -237,7 +236,7 @@
            c->pending_requests;
        pending_request != nullptr; pending_request = pending_request->next) {
     if (pending_request->md_array == md_array) {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", c,
                 pending_request);
       }
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h
index fc0955c..e1467b0 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.h
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h
@@ -21,7 +21,7 @@
 
 #include "src/core/lib/security/credentials/credentials.h"
 
-extern grpc_tracer_flag grpc_plugin_credentials_trace;
+extern grpc_core::TraceFlag grpc_plugin_credentials_trace;
 
 struct grpc_plugin_credentials;
 
diff --git a/src/core/lib/security/transport/secure_endpoint.cc b/src/core/lib/security/transport/secure_endpoint.cc
index 3ba987a..4cd317a 100644
--- a/src/core/lib/security/transport/secure_endpoint.cc
+++ b/src/core/lib/security/transport/secure_endpoint.cc
@@ -61,8 +61,7 @@
   gpr_refcount ref;
 } secure_endpoint;
 
-grpc_tracer_flag grpc_trace_secure_endpoint =
-    GRPC_TRACER_INITIALIZER(false, "secure_endpoint");
+grpc_core::TraceFlag grpc_trace_secure_endpoint(false, "secure_endpoint");
 
 static void destroy(grpc_exec_ctx* exec_ctx, secure_endpoint* secure_ep) {
   secure_endpoint* ep = secure_ep;
@@ -86,7 +85,7 @@
 static void secure_endpoint_unref(grpc_exec_ctx* exec_ctx, secure_endpoint* ep,
                                   const char* reason, const char* file,
                                   int line) {
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECENDP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
@@ -99,7 +98,7 @@
 
 static void secure_endpoint_ref(secure_endpoint* ep, const char* reason,
                                 const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECENDP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
@@ -131,7 +130,7 @@
 
 static void call_read_cb(grpc_exec_ctx* exec_ctx, secure_endpoint* ep,
                          grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     size_t i;
     for (i = 0; i < ep->read_buffer->count; i++) {
       char* data = grpc_dump_slice(ep->read_buffer->slices[i],
@@ -271,7 +270,7 @@
 
   grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer);
 
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     for (i = 0; i < slices->count; i++) {
       char* data =
           grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
diff --git a/src/core/lib/security/transport/secure_endpoint.h b/src/core/lib/security/transport/secure_endpoint.h
index db8233f..92c4574 100644
--- a/src/core/lib/security/transport/secure_endpoint.h
+++ b/src/core/lib/security/transport/secure_endpoint.h
@@ -29,7 +29,7 @@
 struct tsi_frame_protector;
 struct tsi_zero_copy_grpc_protector;
 
-extern grpc_tracer_flag grpc_trace_secure_endpoint;
+extern grpc_core::TraceFlag grpc_trace_secure_endpoint;
 
 /* Takes ownership of protector, zero_copy_protector, and to_wrap, and refs
  * leftover_slices. If zero_copy_protector is not NULL, protector will never be
diff --git a/src/core/lib/security/transport/security_connector.cc b/src/core/lib/security/transport/security_connector.cc
index b996cc8..c56e459 100644
--- a/src/core/lib/security/transport/security_connector.cc
+++ b/src/core/lib/security/transport/security_connector.cc
@@ -44,10 +44,8 @@
 #include "src/core/tsi/ssl_transport_security.h"
 #include "src/core/tsi/transport_security_adapter.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_security_connector_refcount =
-    GRPC_TRACER_INITIALIZER(false, "security_connector_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount(
+    false, "security_connector_refcount");
 
 /* -- Constants. -- */
 
@@ -198,7 +196,7 @@
     grpc_security_connector* sc, const char* file, int line,
     const char* reason) {
   if (sc == nullptr) return nullptr;
-  if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) {
+  if (grpc_trace_security_connector_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECURITY_CONNECTOR:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", sc,
@@ -219,7 +217,7 @@
                                    const char* file, int line,
                                    const char* reason) {
   if (sc == nullptr) return;
-  if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) {
+  if (grpc_trace_security_connector_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECURITY_CONNECTOR:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", sc,
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h
index 79fdbc1..7cde358 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -33,9 +33,7 @@
 extern "C" {
 #endif
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_security_connector_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount;
 
 /* --- status enum. --- */
 
diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h
index 2439fc0..10527dc 100644
--- a/src/core/lib/slice/slice_internal.h
+++ b/src/core/lib/slice/slice_internal.h
@@ -32,6 +32,9 @@
 void grpc_slice_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice slice);
 void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx* exec_ctx,
                                                 grpc_slice_buffer* sb);
+void grpc_slice_buffer_partial_unref_internal(grpc_exec_ctx* exec_ctx,
+                                              grpc_slice_buffer* sb,
+                                              size_t idx);
 void grpc_slice_buffer_destroy_internal(grpc_exec_ctx* exec_ctx,
                                         grpc_slice_buffer* sb);
 
diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc
index 503a96b..bca14a0 100644
--- a/src/core/lib/support/cpu_posix.cc
+++ b/src/core/lib/support/cpu_posix.cc
@@ -18,21 +18,23 @@
 
 #include <grpc/support/port_platform.h>
 
-#ifdef GPR_CPU_POSIX
+#if defined(GPR_CPU_POSIX)
 
 #include <errno.h>
+#include <pthread.h>
 #include <string.h>
 #include <unistd.h>
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/cpu.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/useful.h>
 
-static __thread char magic_thread_local;
-
 static long ncpus = 0;
 
+static pthread_key_t thread_id_key;
+
 static void init_ncpus() {
   ncpus = sysconf(_SC_NPROCESSORS_ONLN);
   if (ncpus < 1 || ncpus > INT32_MAX) {
@@ -47,12 +49,32 @@
   return (unsigned)ncpus;
 }
 
+static void delete_thread_id(void* value) {
+  if (value) {
+    gpr_free(value);
+  }
+}
+
+static void init_thread_id_key(void) {
+  pthread_key_create(&thread_id_key, delete_thread_id);
+}
+
 unsigned gpr_cpu_current_cpu(void) {
   /* NOTE: there's no way I know to return the actual cpu index portably...
      most code that's using this is using it to shard across work queues though,
      so here we use thread identity instead to achieve a similar though not
      identical effect */
-  return (unsigned)GPR_HASH_POINTER(&magic_thread_local, gpr_cpu_num_cores());
+  static gpr_once once = GPR_ONCE_INIT;
+  gpr_once_init(&once, init_thread_id_key);
+
+  unsigned int* thread_id =
+      static_cast<unsigned int*>(pthread_getspecific(thread_id_key));
+  if (thread_id == nullptr) {
+    thread_id = static_cast<unsigned int*>(gpr_malloc(sizeof(unsigned int)));
+    pthread_setspecific(thread_id_key, thread_id);
+  }
+
+  return (unsigned)GPR_HASH_POINTER(thread_id, gpr_cpu_num_cores());
 }
 
 #endif /* GPR_CPU_POSIX */
diff --git a/src/core/lib/surface/alarm.cc b/src/core/lib/surface/alarm.cc
index d8b1f18..b1c9f7b 100644
--- a/src/core/lib/surface/alarm.cc
+++ b/src/core/lib/surface/alarm.cc
@@ -27,10 +27,8 @@
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/surface/completion_queue.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_alarm_refcount =
-    GRPC_TRACER_INITIALIZER(false, "alarm_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_alarm_refcount(false,
+                                                        "alarm_refcount");
 
 struct grpc_alarm {
   gpr_refcount refs;
@@ -59,7 +57,7 @@
 #ifndef NDEBUG
 static void alarm_ref_dbg(grpc_alarm* alarm, const char* reason,
                           const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
+  if (grpc_trace_alarm_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "Alarm:%p  ref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val,
@@ -71,7 +69,7 @@
 
 static void alarm_unref_dbg(grpc_alarm* alarm, const char* reason,
                             const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
+  if (grpc_trace_alarm_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "Alarm:%p  Unref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val,
@@ -103,7 +101,7 @@
   grpc_alarm* alarm = (grpc_alarm*)gpr_malloc(sizeof(grpc_alarm));
 
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
+  if (grpc_trace_alarm_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "Alarm:%p created (ref: 1)", alarm);
   }
 #endif
diff --git a/src/core/lib/surface/alarm_internal.h b/src/core/lib/surface/alarm_internal.h
index 136b605..2ee3a31 100644
--- a/src/core/lib/surface/alarm_internal.h
+++ b/src/core/lib/surface/alarm_internal.h
@@ -22,14 +22,14 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
 
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_alarm_refcount;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #ifndef NDEBUG
 
-extern grpc_tracer_flag grpc_trace_alarm_refcount;
-
 #define GRPC_ALARM_REF(a, reason) alarm_ref_dbg(a, reason, __FILE__, __LINE__)
 #define GRPC_ALARM_UNREF(a, reason) \
   alarm_unref_dbg(a, reason, __FILE__, __LINE__)
diff --git a/src/core/lib/surface/api_trace.cc b/src/core/lib/surface/api_trace.cc
index 5697330..7ab836a 100644
--- a/src/core/lib/surface/api_trace.cc
+++ b/src/core/lib/surface/api_trace.cc
@@ -19,4 +19,4 @@
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/debug/trace.h"
 
-grpc_tracer_flag grpc_api_trace = GRPC_TRACER_INITIALIZER(false, "api");
+grpc_core::TraceFlag grpc_api_trace(false, "api");
diff --git a/src/core/lib/surface/api_trace.h b/src/core/lib/surface/api_trace.h
index 105abdf..a4e11ce 100644
--- a/src/core/lib/surface/api_trace.h
+++ b/src/core/lib/surface/api_trace.h
@@ -22,11 +22,7 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern grpc_tracer_flag grpc_api_trace;
+extern grpc_core::TraceFlag grpc_api_trace;
 
 /* Provide unwrapping macros because we're in C89 and variadic macros weren't
    introduced until C99... */
@@ -47,12 +43,8 @@
 /* Due to the limitations of C89's preprocessor, the arity of the var-arg list
    'nargs' must be specified. */
 #define GRPC_API_TRACE(fmt, nargs, args)                      \
-  if (GRPC_TRACER_ON(grpc_api_trace)) {                       \
+  if (grpc_api_trace.enabled()) {                             \
     gpr_log(GPR_INFO, fmt GRPC_API_TRACE_UNWRAP##nargs args); \
   }
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* GRPC_CORE_LIB_SURFACE_API_TRACE_H */
diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc
index 3a06b0c..a83c95c 100644
--- a/src/core/lib/surface/call.cc
+++ b/src/core/lib/surface/call.cc
@@ -259,10 +259,8 @@
   gpr_atm recv_state;
 };
 
-grpc_tracer_flag grpc_call_error_trace =
-    GRPC_TRACER_INITIALIZER(false, "call_error");
-grpc_tracer_flag grpc_compression_trace =
-    GRPC_TRACER_INITIALIZER(false, "compression");
+grpc_core::TraceFlag grpc_call_error_trace(false, "call_error");
+grpc_core::TraceFlag grpc_compression_trace(false, "compression");
 
 #define CALL_STACK_FROM_CALL(call) ((grpc_call_stack*)((call) + 1))
 #define CALL_FROM_CALL_STACK(call_stack) (((grpc_call*)(call_stack)) - 1)
@@ -765,7 +763,7 @@
   for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
     status[i] = unpack_received_status(gpr_atm_acq_load(&call->status[i]));
   }
-  if (GRPC_TRACER_ON(grpc_call_error_trace)) {
+  if (grpc_call_error_trace.enabled()) {
     gpr_log(GPR_DEBUG, "get_final_status %s", call->is_client ? "CLI" : "SVR");
     for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
       if (status[i].is_set) {
@@ -1427,7 +1425,7 @@
   }
 
   if (error != GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures)) {
+    if (grpc_trace_operation_failures.enabled()) {
       GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error));
     }
     grpc_byte_stream_destroy(exec_ctx, call->receiving_stream);
@@ -1531,7 +1529,7 @@
     GPR_ASSERT(call->stream_encodings_accepted_by_peer != 0);
     if (!GPR_BITGET(call->stream_encodings_accepted_by_peer,
                     call->incoming_stream_compression_algorithm)) {
-      if (GRPC_TRACER_ON(grpc_compression_trace)) {
+      if (grpc_compression_trace.enabled()) {
         const char* algo_name = nullptr;
         grpc_stream_compression_algorithm_name(
             call->incoming_stream_compression_algorithm, &algo_name);
@@ -1574,7 +1572,7 @@
     GPR_ASSERT(call->encodings_accepted_by_peer != 0);
     if (!GPR_BITGET(call->encodings_accepted_by_peer,
                     call->incoming_compression_algorithm)) {
-      if (GRPC_TRACER_ON(grpc_compression_trace)) {
+      if (grpc_compression_trace.enabled()) {
         const char* algo_name = nullptr;
         grpc_compression_algorithm_name(call->incoming_compression_algorithm,
                                         &algo_name);
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index d4e596f..07c4e48 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -102,8 +102,7 @@
 void* grpc_call_context_get(grpc_call* call, grpc_context_index elem);
 
 #define GRPC_CALL_LOG_BATCH(sev, call, ops, nops, tag) \
-  if (GRPC_TRACER_ON(grpc_api_trace))                  \
-  grpc_call_log_batch(sev, call, ops, nops, tag)
+  if (grpc_api_trace.enabled()) grpc_call_log_batch(sev, call, ops, nops, tag)
 
 uint8_t grpc_call_is_client(grpc_call* call);
 
@@ -112,8 +111,8 @@
 grpc_compression_algorithm grpc_call_compression_for_level(
     grpc_call* call, grpc_compression_level level);
 
-extern grpc_tracer_flag grpc_call_error_trace;
-extern grpc_tracer_flag grpc_compression_trace;
+extern grpc_core::TraceFlag grpc_call_error_trace;
+extern grpc_core::TraceFlag grpc_compression_trace;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc
index a171f90..98d7e35 100644
--- a/src/core/lib/surface/completion_queue.cc
+++ b/src/core/lib/surface/completion_queue.cc
@@ -40,14 +40,9 @@
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/event_string.h"
 
-grpc_tracer_flag grpc_trace_operation_failures =
-    GRPC_TRACER_INITIALIZER(false, "op_failure");
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_pending_tags =
-    GRPC_TRACER_INITIALIZER(false, "pending_tags");
-grpc_tracer_flag grpc_trace_cq_refcount =
-    GRPC_TRACER_INITIALIZER(false, "cq_refcount");
-#endif
+grpc_core::TraceFlag grpc_trace_operation_failures(false, "op_failure");
+grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags(false, "pending_tags");
+grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount(false, "cq_refcount");
 
 // Specifies a cq thread local cache.
 // The first event that occurs on a thread
@@ -340,18 +335,15 @@
 #define POLLSET_FROM_CQ(cq) \
   ((grpc_pollset*)(cq->vtable->data_size + (char*)DATA_FROM_CQ(cq)))
 
-grpc_tracer_flag grpc_cq_pluck_trace =
-    GRPC_TRACER_INITIALIZER(true, "queue_pluck");
-grpc_tracer_flag grpc_cq_event_timeout_trace =
-    GRPC_TRACER_INITIALIZER(true, "queue_timeout");
+grpc_core::TraceFlag grpc_cq_pluck_trace(true, "queue_pluck");
+grpc_core::TraceFlag grpc_cq_event_timeout_trace(true, "queue_timeout");
 
-#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event)    \
-  if (GRPC_TRACER_ON(grpc_api_trace) &&                 \
-      (GRPC_TRACER_ON(grpc_cq_pluck_trace) ||           \
-       (event)->type != GRPC_QUEUE_TIMEOUT)) {          \
-    char* _ev = grpc_event_string(event);               \
-    gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \
-    gpr_free(_ev);                                      \
+#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event)                       \
+  if (grpc_api_trace.enabled() && (grpc_cq_pluck_trace.enabled() ||        \
+                                   (event)->type != GRPC_QUEUE_TIMEOUT)) { \
+    char* _ev = grpc_event_string(event);                                  \
+    gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev);                    \
+    gpr_free(_ev);                                                         \
   }
 
 static void on_pollset_shutdown_done(grpc_exec_ctx* exec_ctx, void* cq,
@@ -533,7 +525,7 @@
 #ifndef NDEBUG
 void grpc_cq_internal_ref(grpc_completion_queue* cq, const char* reason,
                           const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) {
+  if (grpc_trace_cq_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "CQ:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val + 1,
@@ -554,7 +546,7 @@
 #ifndef NDEBUG
 void grpc_cq_internal_unref(grpc_exec_ctx* exec_ctx, grpc_completion_queue* cq,
                             const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) {
+  if (grpc_trace_cq_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "CQ:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val - 1,
@@ -656,16 +648,14 @@
                                void* done_arg, grpc_cq_completion* storage) {
   GPR_TIMER_BEGIN("cq_end_op_for_next", 0);
 
-  if (GRPC_TRACER_ON(grpc_api_trace) ||
-      (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-       error != GRPC_ERROR_NONE)) {
+  if (grpc_api_trace.enabled() ||
+      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
     const char* errmsg = grpc_error_string(error);
     GRPC_API_TRACE(
         "cq_end_op_for_next(exec_ctx=%p, cq=%p, tag=%p, error=%s, "
         "done=%p, done_arg=%p, storage=%p)",
         7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage));
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-        error != GRPC_ERROR_NONE) {
+    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
@@ -745,16 +735,14 @@
 
   GPR_TIMER_BEGIN("cq_end_op_for_pluck", 0);
 
-  if (GRPC_TRACER_ON(grpc_api_trace) ||
-      (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-       error != GRPC_ERROR_NONE)) {
+  if (grpc_api_trace.enabled() ||
+      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
     const char* errmsg = grpc_error_string(error);
     GRPC_API_TRACE(
         "cq_end_op_for_pluck(exec_ctx=%p, cq=%p, tag=%p, error=%s, "
         "done=%p, done_arg=%p, storage=%p)",
         7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage));
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-        error != GRPC_ERROR_NONE) {
+    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
@@ -848,7 +836,7 @@
 
 #ifndef NDEBUG
 static void dump_pending_tags(grpc_completion_queue* cq) {
-  if (!GRPC_TRACER_ON(grpc_trace_pending_tags)) return;
+  if (!grpc_trace_pending_tags.enabled()) return;
 
   gpr_strvec v;
   gpr_strvec_init(&v);
@@ -1112,7 +1100,7 @@
 
   GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0);
 
-  if (GRPC_TRACER_ON(grpc_cq_pluck_trace)) {
+  if (grpc_cq_pluck_trace.enabled()) {
     GRPC_API_TRACE(
         "grpc_completion_queue_pluck("
         "cq=%p, tag=%p, "
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index 0ed9875..9fdb48d 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -27,14 +27,11 @@
 
 /* These trace flags default to 1. The corresponding lines are only traced
    if grpc_api_trace is also truthy */
-extern grpc_tracer_flag grpc_cq_pluck_trace;
-extern grpc_tracer_flag grpc_cq_event_timeout_trace;
-extern grpc_tracer_flag grpc_trace_operation_failures;
-
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_pending_tags;
-extern grpc_tracer_flag grpc_trace_cq_refcount;
-#endif
+extern grpc_core::TraceFlag grpc_cq_pluck_trace;
+extern grpc_core::TraceFlag grpc_cq_event_timeout_trace;
+extern grpc_core::TraceFlag grpc_trace_operation_failures;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount;
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc
index 673e13c..c6d2f0a 100644
--- a/src/core/lib/surface/init.cc
+++ b/src/core/lib/surface/init.cc
@@ -126,30 +126,6 @@
     grpc_slice_intern_init();
     grpc_mdctx_global_init();
     grpc_channel_init_init();
-    grpc_register_tracer(&grpc_api_trace);
-    grpc_register_tracer(&grpc_trace_channel);
-    grpc_register_tracer(&grpc_connectivity_state_trace);
-    grpc_register_tracer(&grpc_trace_channel_stack_builder);
-    grpc_register_tracer(&grpc_http1_trace);
-    grpc_register_tracer(&grpc_cq_pluck_trace);  // default on
-    grpc_register_tracer(&grpc_call_combiner_trace);
-    grpc_register_tracer(&grpc_combiner_trace);
-    grpc_register_tracer(&grpc_server_channel_trace);
-    grpc_register_tracer(&grpc_bdp_estimator_trace);
-    grpc_register_tracer(&grpc_cq_event_timeout_trace);  // default on
-    grpc_register_tracer(&grpc_trace_operation_failures);
-    grpc_register_tracer(&grpc_resource_quota_trace);
-    grpc_register_tracer(&grpc_call_error_trace);
-#ifndef NDEBUG
-    grpc_register_tracer(&grpc_trace_pending_tags);
-    grpc_register_tracer(&grpc_trace_alarm_refcount);
-    grpc_register_tracer(&grpc_trace_cq_refcount);
-    grpc_register_tracer(&grpc_trace_closure);
-    grpc_register_tracer(&grpc_trace_error_refcount);
-    grpc_register_tracer(&grpc_trace_stream_refcount);
-    grpc_register_tracer(&grpc_trace_fd_refcount);
-    grpc_register_tracer(&grpc_trace_metadata);
-#endif
     grpc_security_pre_init();
     grpc_iomgr_init(&exec_ctx);
     gpr_timers_global_init();
diff --git a/src/core/lib/surface/init_secure.cc b/src/core/lib/surface/init_secure.cc
index deb9a8e..3eee570 100644
--- a/src/core/lib/surface/init_secure.cc
+++ b/src/core/lib/surface/init_secure.cc
@@ -24,6 +24,7 @@
 #include <string.h>
 
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
 #include "src/core/lib/security/transport/auth_filters.h"
@@ -33,18 +34,7 @@
 #include "src/core/lib/surface/channel_init.h"
 #include "src/core/tsi/transport_security_interface.h"
 
-#ifndef NDEBUG
-#include "src/core/lib/security/context/security_context.h"
-#endif
-
-void grpc_security_pre_init(void) {
-  grpc_register_tracer(&grpc_trace_secure_endpoint);
-  grpc_register_tracer(&tsi_tracing_enabled);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_auth_context_refcount);
-  grpc_register_tracer(&grpc_trace_security_connector_refcount);
-#endif
-}
+void grpc_security_pre_init(void) {}
 
 static bool maybe_prepend_client_auth_filter(
     grpc_exec_ctx* exec_ctx, grpc_channel_stack_builder* builder, void* arg) {
@@ -85,7 +75,4 @@
                                    maybe_prepend_server_auth_filter, nullptr);
 }
 
-void grpc_security_init() {
-  grpc_security_register_handshaker_factories();
-  grpc_register_tracer(&grpc_plugin_credentials_trace);
-}
+void grpc_security_init() { grpc_security_register_handshaker_factories(); }
diff --git a/src/core/lib/surface/lame_client.cc b/src/core/lib/surface/lame_client.cc
index 7114a9f..d1cf4d7 100644
--- a/src/core/lib/surface/lame_client.cc
+++ b/src/core/lib/surface/lame_client.cc
@@ -25,7 +25,6 @@
 
 #include "src/core/lib/support/atomic.h"
 
-extern "C" {
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -33,7 +32,6 @@
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/lame_client.h"
 #include "src/core/lib/transport/static_metadata.h"
-}
 
 namespace grpc_core {
 
diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index b816439..57bb6cc 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -60,8 +60,7 @@
 
 typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type;
 
-grpc_tracer_flag grpc_server_channel_trace =
-    GRPC_TRACER_INITIALIZER(false, "server_channel");
+grpc_core::TraceFlag grpc_server_channel_trace(false, "server_channel");
 
 typedef struct requested_call {
   gpr_mpscq_node request_link; /* must be first */
@@ -350,14 +349,8 @@
                                           grpc_error* error) {
   requested_call* rc;
   for (size_t i = 0; i < server->cq_count; i++) {
-    /* Here we know:
-       1. no requests are being added (since the server is shut down)
-       2. no other threads are pulling (since the shut down process is single
-          threaded)
-       So, we can ignore the queue lock and just pop, with the guarantee that a
-       NULL returned here truly means that the queue is empty */
-    while ((rc = (requested_call*)gpr_mpscq_pop(
-                &rm->requests_per_cq[i].queue)) != nullptr) {
+    while ((rc = (requested_call*)gpr_locked_mpscq_pop(
+                &rm->requests_per_cq[i])) != nullptr) {
       fail_call(exec_ctx, server, i, rc, GRPC_ERROR_REF(error));
     }
   }
@@ -434,7 +427,7 @@
   GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure,
                     finish_destroy_channel, chand, grpc_schedule_on_exec_ctx);
 
-  if (GRPC_TRACER_ON(grpc_server_channel_trace) && error != GRPC_ERROR_NONE) {
+  if (grpc_server_channel_trace.enabled() && error != GRPC_ERROR_NONE) {
     const char* msg = grpc_error_string(error);
     gpr_log(GPR_INFO, "Disconnected client: %s", msg);
   }
diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
index e3c43f9..d5c2b0f 100644
--- a/src/core/lib/surface/server.h
+++ b/src/core/lib/surface/server.h
@@ -31,7 +31,7 @@
 extern const grpc_channel_filter grpc_server_top_filter;
 
 /** Lightweight tracing of server channel state */
-extern grpc_tracer_flag grpc_server_channel_trace;
+extern grpc_core::TraceFlag grpc_server_channel_trace;
 
 /* Add a listener to the server: when the server starts, it will call start,
    and when it shuts down, it will call destroy */
diff --git a/src/core/lib/transport/bdp_estimator.cc b/src/core/lib/transport/bdp_estimator.cc
index e09ae8e..bb0e583 100644
--- a/src/core/lib/transport/bdp_estimator.cc
+++ b/src/core/lib/transport/bdp_estimator.cc
@@ -23,8 +23,7 @@
 
 #include <grpc/support/useful.h>
 
-grpc_tracer_flag grpc_bdp_estimator_trace =
-    GRPC_TRACER_INITIALIZER(false, "bdp_estimator");
+grpc_core::TraceFlag grpc_bdp_estimator_trace(false, "bdp_estimator");
 
 namespace grpc_core {
 
@@ -44,7 +43,7 @@
   double dt = (double)dt_ts.tv_sec + 1e-9 * (double)dt_ts.tv_nsec;
   double bw = dt > 0 ? ((double)accumulator_ / dt) : 0;
   int start_inter_ping_delay = inter_ping_delay_;
-  if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+  if (grpc_bdp_estimator_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64
             " dt=%lf bw=%lfMbs bw_est=%lfMbs",
@@ -55,7 +54,7 @@
   if (accumulator_ > 2 * estimate_ / 3 && bw > bw_est_) {
     estimate_ = GPR_MAX(accumulator_, estimate_ * 2);
     bw_est_ = bw;
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]: estimate increased to %" PRId64, name_,
               estimate_);
     }
@@ -72,7 +71,7 @@
   }
   if (start_inter_ping_delay != inter_ping_delay_) {
     stable_estimate_count_ = 0;
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]:update_inter_time to %dms", name_,
               inter_ping_delay_);
     }
diff --git a/src/core/lib/transport/bdp_estimator.h b/src/core/lib/transport/bdp_estimator.h
index f7b94a8..df3a86c 100644
--- a/src/core/lib/transport/bdp_estimator.h
+++ b/src/core/lib/transport/bdp_estimator.h
@@ -31,7 +31,7 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 
-extern grpc_tracer_flag grpc_bdp_estimator_trace;
+extern grpc_core::TraceFlag grpc_bdp_estimator_trace;
 
 namespace grpc_core {
 
@@ -49,7 +49,7 @@
   // grpc_bdp_estimator_add_incoming_bytes once a ping has been scheduled by a
   // transport (but not necessarily started)
   void SchedulePing() {
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]:sched acc=%" PRId64 " est=%" PRId64, name_,
               accumulator_, estimate_);
     }
@@ -62,7 +62,7 @@
   // once
   // the ping is on the wire
   void StartPing() {
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]:start acc=%" PRId64 " est=%" PRId64, name_,
               accumulator_, estimate_);
     }
diff --git a/src/core/lib/transport/connectivity_state.cc b/src/core/lib/transport/connectivity_state.cc
index 55f4236..e7e5dbd 100644
--- a/src/core/lib/transport/connectivity_state.cc
+++ b/src/core/lib/transport/connectivity_state.cc
@@ -24,8 +24,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-grpc_tracer_flag grpc_connectivity_state_trace =
-    GRPC_TRACER_INITIALIZER(false, "connectivity_state");
+grpc_core::TraceFlag grpc_connectivity_state_trace(false, "connectivity_state");
 
 const char* grpc_connectivity_state_name(grpc_connectivity_state state) {
   switch (state) {
@@ -78,7 +77,7 @@
   grpc_connectivity_state cur =
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name,
             grpc_connectivity_state_name(cur));
   }
@@ -90,7 +89,7 @@
   grpc_connectivity_state cur =
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name,
             grpc_connectivity_state_name(cur));
   }
@@ -111,7 +110,7 @@
   grpc_connectivity_state cur =
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     if (current == nullptr) {
       gpr_log(GPR_DEBUG, "CONWATCH: %p %s: unsubscribe notify=%p", tracker,
               tracker->name, notify);
@@ -165,7 +164,7 @@
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
   grpc_connectivity_state_watcher* w;
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     const char* error_string = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker,
             tracker->name, grpc_connectivity_state_name(cur),
@@ -192,7 +191,7 @@
   while ((w = tracker->watchers) != nullptr) {
     *w->current = state;
     tracker->watchers = w->next;
-    if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+    if (grpc_connectivity_state_trace.enabled()) {
       gpr_log(GPR_DEBUG, "NOTIFY: %p %s: %p", tracker, tracker->name,
               w->notify);
     }
diff --git a/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h
index 792e27c..60d20dd 100644
--- a/src/core/lib/transport/connectivity_state.h
+++ b/src/core/lib/transport/connectivity_state.h
@@ -47,7 +47,7 @@
   char* name;
 } grpc_connectivity_state_tracker;
 
-extern grpc_tracer_flag grpc_connectivity_state_trace;
+extern grpc_core::TraceFlag grpc_connectivity_state_trace;
 
 /** enum --> string conversion */
 const char* grpc_connectivity_state_name(grpc_connectivity_state state);
diff --git a/src/core/lib/transport/metadata.cc b/src/core/lib/transport/metadata.cc
index a16f050..0f30c75 100644
--- a/src/core/lib/transport/metadata.cc
+++ b/src/core/lib/transport/metadata.cc
@@ -48,9 +48,9 @@
  * used to determine which kind of element a pointer refers to.
  */
 
+grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
+
 #ifndef NDEBUG
-grpc_tracer_flag grpc_trace_metadata =
-    GRPC_TRACER_INITIALIZER(false, "metadata");
 #define DEBUG_ARGS , const char *file, int line
 #define FWD_DEBUG_ARGS , file, line
 #define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
@@ -149,7 +149,7 @@
 static void ref_md_locked(mdtab_shard* shard,
                           interned_metadata* md DEBUG_ARGS) {
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+  if (grpc_trace_metadata.enabled()) {
     char* key_str = grpc_slice_to_c_string(md->key);
     char* value_str = grpc_slice_to_c_string(md->value);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -252,7 +252,7 @@
     allocated->value = grpc_slice_ref_internal(value);
     gpr_atm_rel_store(&allocated->refcnt, 1);
 #ifndef NDEBUG
-    if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+    if (grpc_trace_metadata.enabled()) {
       char* key_str = grpc_slice_to_c_string(allocated->key);
       char* value_str = grpc_slice_to_c_string(allocated->value);
       gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'",
@@ -306,7 +306,7 @@
   shard->elems[idx] = md;
   gpr_mu_init(&md->mu_user_data);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+  if (grpc_trace_metadata.enabled()) {
     char* key_str = grpc_slice_to_c_string(md->key);
     char* value_str = grpc_slice_to_c_string(md->value);
     gpr_log(GPR_DEBUG, "ELM   NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void*)md,
@@ -373,7 +373,7 @@
     case GRPC_MDELEM_STORAGE_INTERNED: {
       interned_metadata* md = (interned_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -395,7 +395,7 @@
     case GRPC_MDELEM_STORAGE_ALLOCATED: {
       allocated_metadata* md = (allocated_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -425,7 +425,7 @@
     case GRPC_MDELEM_STORAGE_INTERNED: {
       interned_metadata* md = (interned_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -451,7 +451,7 @@
     case GRPC_MDELEM_STORAGE_ALLOCATED: {
       allocated_metadata* md = (allocated_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h
index 7e7e7b4..931ba0b 100644
--- a/src/core/lib/transport/metadata.h
+++ b/src/core/lib/transport/metadata.h
@@ -25,9 +25,7 @@
 
 #include "src/core/lib/iomgr/exec_ctx.h"
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_metadata;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata;
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc
index b39e6eb..ac99814 100644
--- a/src/core/lib/transport/transport.cc
+++ b/src/core/lib/transport/transport.cc
@@ -31,14 +31,12 @@
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/transport_impl.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_stream_refcount =
-    GRPC_TRACER_INITIALIZER(false, "stream_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount(false,
+                                                         "stream_refcount");
 
 #ifndef NDEBUG
 void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) {
+  if (grpc_trace_stream_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
     gpr_log(GPR_DEBUG, "%s %p:%p   REF %" PRIdPTR "->%" PRIdPTR " %s",
             refcount->object_type, refcount, refcount->destroy.cb_arg, val,
@@ -53,7 +51,7 @@
 #ifndef NDEBUG
 void grpc_stream_unref(grpc_exec_ctx* exec_ctx, grpc_stream_refcount* refcount,
                        const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) {
+  if (grpc_trace_stream_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
     gpr_log(GPR_DEBUG, "%s %p:%p UNREF %" PRIdPTR "->%" PRIdPTR " %s",
             refcount->object_type, refcount, refcount->destroy.cb_arg, val,
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 973018e..2347005 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -43,9 +43,7 @@
    for a stream. */
 typedef struct grpc_stream grpc_stream;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_stream_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount;
 
 typedef struct grpc_stream_refcount {
   gpr_refcount refs;
diff --git a/src/core/tsi/fake_transport_security.cc b/src/core/tsi/fake_transport_security.cc
index e508d9b..f2f365f 100644
--- a/src/core/tsi/fake_transport_security.cc
+++ b/src/core/tsi/fake_transport_security.cc
@@ -575,7 +575,7 @@
     if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
       next_message_to_send = TSI_FAKE_HANDSHAKE_MESSAGE_MAX;
     }
-    if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+    if (tsi_tracing_enabled.enabled()) {
       gpr_log(GPR_INFO, "%s prepared %s.",
               impl->is_client ? "Client" : "Server",
               tsi_fake_handshake_message_to_string(impl->next_message_to_send));
@@ -587,7 +587,7 @@
   if (!impl->is_client &&
       impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
     /* We're done. */
-    if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+    if (tsi_tracing_enabled.enabled()) {
       gpr_log(GPR_INFO, "Server is done.");
     }
     impl->result = TSI_OK;
@@ -625,7 +625,7 @@
             tsi_fake_handshake_message_to_string(received_msg),
             tsi_fake_handshake_message_to_string(expected_msg));
   }
-  if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+  if (tsi_tracing_enabled.enabled()) {
     gpr_log(GPR_INFO, "%s received %s.", impl->is_client ? "Client" : "Server",
             tsi_fake_handshake_message_to_string(received_msg));
   }
@@ -633,7 +633,7 @@
   impl->needs_incoming_message = 0;
   if (impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
     /* We're done. */
-    if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+    if (tsi_tracing_enabled.enabled()) {
       gpr_log(GPR_INFO, "%s is done.", impl->is_client ? "Client" : "Server");
     }
     impl->result = TSI_OK;
diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc
index f1fff04..f35caef 100644
--- a/src/core/tsi/ssl_transport_security.cc
+++ b/src/core/tsi/ssl_transport_security.cc
@@ -175,7 +175,7 @@
 /* TODO(jboeuf): Remove when we are past the debugging phase with this code. */
 static void ssl_log_where_info(const SSL* ssl, int where, int flag,
                                const char* msg) {
-  if ((where & flag) && GRPC_TRACER_ON(tsi_tracing_enabled)) {
+  if ((where & flag) && tsi_tracing_enabled.enabled()) {
     gpr_log(GPR_INFO, "%20.20s - %30.30s  - %5.10s", msg,
             SSL_state_string_long(ssl), SSL_state_string(ssl));
   }
diff --git a/src/core/tsi/transport_security.cc b/src/core/tsi/transport_security.cc
index a2232bf..5abd2f0 100644
--- a/src/core/tsi/transport_security.cc
+++ b/src/core/tsi/transport_security.cc
@@ -26,7 +26,7 @@
 
 /* --- Tracing. --- */
 
-grpc_tracer_flag tsi_tracing_enabled = GRPC_TRACER_INITIALIZER(false, "tsi");
+grpc_core::TraceFlag tsi_tracing_enabled(false, "tsi");
 
 /* --- tsi_result common implementation. --- */
 
diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h
index d639f85..7d6dd44 100644
--- a/src/core/tsi/transport_security.h
+++ b/src/core/tsi/transport_security.h
@@ -28,7 +28,7 @@
 extern "C" {
 #endif
 
-extern grpc_tracer_flag tsi_tracing_enabled;
+extern grpc_core::TraceFlag tsi_tracing_enabled;
 
 /* Base for tsi_frame_protector implementations.
    See transport_security_interface.h for documentation. */
diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h
index 54942a6..0f3d468 100644
--- a/src/core/tsi/transport_security_interface.h
+++ b/src/core/tsi/transport_security_interface.h
@@ -60,7 +60,7 @@
 
 /* --- tsi tracing --- */
 
-extern grpc_tracer_flag tsi_tracing_enabled;
+extern grpc_core::TraceFlag tsi_tracing_enabled;
 
 /* -- tsi_zero_copy_grpc_protector object --
 
diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
index 3ce08e9..1102c8d 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
@@ -36,7 +36,8 @@
         static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<CompletionRegistry>();
 
         readonly GrpcEnvironment environment;
-        readonly ConcurrentDictionary<IntPtr, OpCompletionDelegate> dict = new ConcurrentDictionary<IntPtr, OpCompletionDelegate>(new IntPtrComparer());
+        readonly Dictionary<IntPtr, OpCompletionDelegate> dict = new Dictionary<IntPtr, OpCompletionDelegate>(new IntPtrComparer());
+        readonly object myLock = new object();
         IntPtr lastRegisteredKey;  // only for testing
 
         public CompletionRegistry(GrpcEnvironment environment)
@@ -47,32 +48,41 @@
         public void Register(IntPtr key, OpCompletionDelegate callback)
         {
             environment.DebugStats.PendingBatchCompletions.Increment();
-            GrpcPreconditions.CheckState(dict.TryAdd(key, callback));
-            this.lastRegisteredKey = key;
+            lock (myLock)
+            {
+                dict.Add(key, callback);
+                this.lastRegisteredKey = key;
+            }
         }
 
         public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback)
         {
+            // TODO(jtattermusch): get rid of new delegate creation here
             OpCompletionDelegate opCallback = ((success) => HandleBatchCompletion(success, ctx, callback));
             Register(ctx.Handle, opCallback);
         }
 
         public void RegisterRequestCallCompletion(RequestCallContextSafeHandle ctx, RequestCallCompletionDelegate callback)
         {
+            // TODO(jtattermusch): get rid of new delegate creation here
             OpCompletionDelegate opCallback = ((success) => HandleRequestCallCompletion(success, ctx, callback));
             Register(ctx.Handle, opCallback);
         }
 
         public OpCompletionDelegate Extract(IntPtr key)
         {
-            OpCompletionDelegate value;
-            GrpcPreconditions.CheckState(dict.TryRemove(key, out value));
+            OpCompletionDelegate value = null;
+            lock (myLock)
+            {
+                value = dict[key];
+                dict.Remove(key);
+            }
             environment.DebugStats.PendingBatchCompletions.Decrement();
             return value;
         }
 
         /// <summary>
-        /// For testing purposes only.
+        /// For testing purposes only. NOT threadsafe.
         /// </summary>
         public IntPtr LastRegisteredKey
         {
diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
index 3c94b60..f7f723c 100644
--- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
+++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
@@ -176,10 +176,10 @@
                     try
                     {
                         var callback = cq.CompletionRegistry.Extract(tag);
-                        // Use cached delegates to avoid unnecessary allocations
+                        queuedContinuationCounter.Increment();
                         if (!inlineHandlers)
                         {
-                            queuedContinuationCounter.Increment();
+                            // Use cached delegates to avoid unnecessary allocations
                             ThreadPool.QueueUserWorkItem(success ? runCompletionQueueEventCallbackSuccess : runCompletionQueueEventCallbackFailure, callback);
                         }
                         else
diff --git a/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs b/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
index 60696b6..48905a2 100644
--- a/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ClientRunners.cs
@@ -72,7 +72,7 @@
                 Logger.Warning("ClientConfig.CoreList is not supported for C#. Ignoring the value");
             }
 
-            var channels = CreateChannels(config.ClientChannels, config.ServerTargets, config.SecurityParams);
+            var channels = CreateChannels(config.ClientChannels, config.ServerTargets, config.SecurityParams, config.ChannelArgs);
 
             return new ClientRunnerImpl(channels,
                 config.ClientType,
@@ -84,19 +84,20 @@
                 () => GetNextProfiler());
         }
 
-        private static List<Channel> CreateChannels(int clientChannels, IEnumerable<string> serverTargets, SecurityParams securityParams)
+        private static List<Channel> CreateChannels(int clientChannels, IEnumerable<string> serverTargets, SecurityParams securityParams, IEnumerable<ChannelArg> channelArguments)
         {
             GrpcPreconditions.CheckArgument(clientChannels > 0, "clientChannels needs to be at least 1.");
             GrpcPreconditions.CheckArgument(serverTargets.Count() > 0, "at least one serverTarget needs to be specified.");
 
             var credentials = securityParams != null ? TestCredentials.CreateSslCredentials() : ChannelCredentials.Insecure;
-            List<ChannelOption> channelOptions = null;
+            var channelOptions = new List<ChannelOption>();
             if (securityParams != null && securityParams.ServerHostOverride != "")
             {
-                channelOptions = new List<ChannelOption>
-                {
-                    new ChannelOption(ChannelOptions.SslTargetNameOverride, securityParams.ServerHostOverride)
-                };
+                channelOptions.Add(new ChannelOption(ChannelOptions.SslTargetNameOverride, securityParams.ServerHostOverride));
+            }
+            foreach (var channelArgument in channelArguments)
+            {
+                channelOptions.Add(channelArgument.ToChannelOption());
             }
 
             var result = new List<Channel>();
diff --git a/src/csharp/Grpc.IntegrationTesting/ControlExtensions.cs b/src/csharp/Grpc.IntegrationTesting/ControlExtensions.cs
new file mode 100644
index 0000000..67f5fae
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting/ControlExtensions.cs
@@ -0,0 +1,43 @@
+#region Copyright notice and license
+
+// Copyright 2016 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#endregion
+
+using System;
+using Grpc.Core;
+using Grpc.Testing;
+
+namespace Grpc.IntegrationTesting
+{
+    /// <summary>
+    /// Helpers for Control.cs
+    /// </summary>
+    public static class ControlExtensions
+    {
+        public static ChannelOption ToChannelOption(this ChannelArg channelArgument)
+        {
+            switch (channelArgument.ValueCase)
+            {
+                case ChannelArg.ValueOneofCase.StrValue:
+                  return new ChannelOption(channelArgument.Name, channelArgument.StrValue);
+                case ChannelArg.ValueOneofCase.IntValue:
+                  return new ChannelOption(channelArgument.Name, channelArgument.IntValue);
+                default:
+                  throw new ArgumentException("Unsupported channel argument value.");
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs b/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs
index 45bff3a..e1b4774 100644
--- a/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs
+++ b/src/csharp/Grpc.IntegrationTesting/ServerRunners.cs
@@ -78,7 +78,8 @@
                 throw new ArgumentException("Unsupported ServerType");
             }
 
-            var server = new Server
+            var channelOptions = new List<ChannelOption>(config.ChannelArgs.Select((arg) => arg.ToChannelOption()));
+            var server = new Server(channelOptions)
             {
                 Services = { service },
                 Ports = { new ServerPort("[::]", config.Port, credentials) }
diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index c9f9ac2..70558a6 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -424,6 +424,19 @@
         self._credentials = credentials
 
 
+class ServerCertificateConfig(object):
+    """A certificate config for use with an SSL-enabled Server, e.g., can
+    be returned in the certificate config fetching callback.
+
+    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.
+    """
+
+    def __init__(self, cert_config):
+        self._cert_config = cert_config
+
+
 ########################  Multi-Callable Interfaces  ###########################
 
 
@@ -1252,6 +1265,60 @@
             ], require_client_auth))
 
 
+def ssl_server_certificate_config(private_key_certificate_chain_pairs,
+                                  root_certificates=None):
+    """Creates a ServerCertificateConfig for use with an SSL-enabled Server.
+
+    Args:
+      private_key_certificate_chain_pairs: A collection 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.
+
+    Returns:
+      A ServerCertificateConfig that can be returned in the certificate config
+      fetching callback.
+    """
+    if len(private_key_certificate_chain_pairs) == 0:
+        raise ValueError(
+            'At least one private key-certificate chain pair is required!')
+    else:
+        return ServerCertificateConfig(
+            _cygrpc.server_certificate_config_ssl(root_certificates, [
+                _cygrpc.SslPemKeyCertPair(key, pem)
+                for key, pem in private_key_certificate_chain_pairs
+            ]))
+
+
+def ssl_server_credentials_dynamic_cert_config(initial_cert_config,
+                                               cert_config_fetcher,
+                                               require_client_auth=False):
+    """Creates a ServerCredentials for use with an SSL-enabled Server.
+
+    Args:
+      initial_cert_config (ServerCertificateConfig): the certificate
+        config with which the server will be initialized.
+      cert_config_fetcher (callable): a callable that takes no
+        arguments and should return a ServerCertificateConfig to
+        replace the server's current cert, or None for no change
+        (i.e., the server will continue its current certificate
+        config). The library will call this callback on *every* new
+        client connection before starting the TLS handshake with the
+        client, thus allowing the user application to optionally
+        return a new ServerCertificateConfig that the server will then
+        use for the handshake.
+      require_client_auth: A boolean indicating whether or not to
+        require clients to be authenticated.
+
+    Returns:
+      A ServerCredentials.
+    """
+    return ServerCredentials(
+        _cygrpc.server_credentials_ssl_dynamic_cert_config(
+            initial_cert_config, cert_config_fetcher, require_client_auth))
+
+
 def channel_ready_future(channel):
     """Creates a Future that tracks when a Channel is ready.
 
@@ -1334,18 +1401,20 @@
            'ChannelConnectivity', 'StatusCode', 'RpcError', 'RpcContext',
            'Call', 'ChannelCredentials', 'CallCredentials',
            'AuthMetadataContext', 'AuthMetadataPluginCallback',
-           'AuthMetadataPlugin', 'ServerCredentials', 'UnaryUnaryMultiCallable',
-           'UnaryStreamMultiCallable', 'StreamUnaryMultiCallable',
-           'StreamStreamMultiCallable', 'Channel', 'ServicerContext',
-           'RpcMethodHandler', 'HandlerCallDetails', 'GenericRpcHandler',
-           'ServiceRpcHandler', 'Server', 'unary_unary_rpc_method_handler',
-           'unary_stream_rpc_method_handler', 'stream_unary_rpc_method_handler',
+           'AuthMetadataPlugin', 'ServerCertificateConfig', 'ServerCredentials',
+           'UnaryUnaryMultiCallable', 'UnaryStreamMultiCallable',
+           'StreamUnaryMultiCallable', 'StreamStreamMultiCallable', 'Channel',
+           'ServicerContext', 'RpcMethodHandler', 'HandlerCallDetails',
+           'GenericRpcHandler', 'ServiceRpcHandler', 'Server',
+           'unary_unary_rpc_method_handler', 'unary_stream_rpc_method_handler',
+           'stream_unary_rpc_method_handler',
            'stream_stream_rpc_method_handler',
            'method_handlers_generic_handler', 'ssl_channel_credentials',
            'metadata_call_credentials', 'access_token_call_credentials',
            'composite_call_credentials', 'composite_channel_credentials',
-           'ssl_server_credentials', 'channel_ready_future', 'insecure_channel',
-           'secure_channel', 'server',)
+           'ssl_server_credentials', 'ssl_server_certificate_config',
+           'ssl_server_credentials_dynamic_cert_config', 'channel_ready_future',
+           'insecure_channel', 'secure_channel', 'server',)
 
 ############################### Extension Shims ################################
 
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
index 41975cb..bc0f185 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
@@ -28,12 +28,27 @@
   cdef list references
 
 
+cdef class ServerCertificateConfig:
+
+  cdef grpc_ssl_server_certificate_config *c_cert_config
+  cdef const char *c_pem_root_certs
+  cdef grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs
+  cdef size_t c_ssl_pem_key_cert_pairs_count
+  cdef list references
+
+
 cdef class ServerCredentials:
 
   cdef grpc_server_credentials *c_credentials
   cdef grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs
   cdef size_t c_ssl_pem_key_cert_pairs_count
   cdef list references
+  # the cert config related state is used only if this credentials is
+  # created with cert config/fetcher
+  cdef object initial_cert_config
+  cdef object cert_config_fetcher
+  # whether C-core has asked for the initial_cert_config
+  cdef bint initial_cert_config_fetched
 
 
 cdef class CredentialsMetadataPlugin:
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
index 0fabda1..db813b7 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
@@ -14,6 +14,7 @@
 
 cimport cpython
 
+import grpc
 import threading
 import traceback
 
@@ -58,12 +59,30 @@
     grpc_shutdown()
 
 
+cdef class ServerCertificateConfig:
+
+  def __cinit__(self):
+    grpc_init()
+    self.c_cert_config = NULL
+    self.c_pem_root_certs = NULL
+    self.c_ssl_pem_key_cert_pairs = NULL
+    self.references = []
+
+  def __dealloc__(self):
+    grpc_ssl_server_certificate_config_destroy(self.c_cert_config)
+    gpr_free(self.c_ssl_pem_key_cert_pairs)
+    grpc_shutdown()
+
+
 cdef class ServerCredentials:
 
   def __cinit__(self):
     grpc_init()
     self.c_credentials = NULL
     self.references = []
+    self.initial_cert_config = None
+    self.cert_config_fetcher = None
+    self.initial_cert_config_fetched = False
 
   def __dealloc__(self):
     if self.c_credentials != NULL:
@@ -254,34 +273,85 @@
   credentials.references.append(plugin)
   return credentials
 
-def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
-                           bint force_client_auth):
-  pem_root_certs = str_to_bytes(pem_root_certs)
+cdef const char* _get_c_pem_root_certs(pem_root_certs):
   cdef char *c_pem_root_certs = NULL
-  if pem_root_certs is not None: 
+  if pem_root_certs is not None:
     c_pem_root_certs = pem_root_certs
-  pem_key_cert_pairs = list(pem_key_cert_pairs)
+  return c_pem_root_certs
+
+cdef grpc_ssl_pem_key_cert_pair* _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs):
+  # return a malloc'ed grpc_ssl_pem_key_cert_pair from a _list_ of SslPemKeyCertPair
   for pair in pem_key_cert_pairs:
     if not isinstance(pair, SslPemKeyCertPair):
       raise TypeError("expected pem_key_cert_pairs to be sequence of "
                       "SslPemKeyCertPair")
-  cdef ServerCredentials credentials = ServerCredentials()
-  credentials.references.append(pem_key_cert_pairs)
-  credentials.references.append(pem_root_certs)
-  credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
+  cdef size_t c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
+  cdef grpc_ssl_pem_key_cert_pair* c_ssl_pem_key_cert_pairs = NULL
   with nogil:
-    credentials.c_ssl_pem_key_cert_pairs = (
-        <grpc_ssl_pem_key_cert_pair *>gpr_malloc(
-            sizeof(grpc_ssl_pem_key_cert_pair) *
-                credentials.c_ssl_pem_key_cert_pairs_count
-        ))
-  for i in range(credentials.c_ssl_pem_key_cert_pairs_count):
-    credentials.c_ssl_pem_key_cert_pairs[i] = (
-        (<SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
-  credentials.c_credentials = grpc_ssl_server_credentials_create(
-      c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
-      credentials.c_ssl_pem_key_cert_pairs_count,
-      GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY if force_client_auth else GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
-      NULL)
+    c_ssl_pem_key_cert_pairs = (
+      <grpc_ssl_pem_key_cert_pair *>gpr_malloc(
+        sizeof(grpc_ssl_pem_key_cert_pair) * c_ssl_pem_key_cert_pairs_count))
+  for i in range(c_ssl_pem_key_cert_pairs_count):
+    c_ssl_pem_key_cert_pairs[i] = (
+      (<SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
+  return c_ssl_pem_key_cert_pairs
+
+def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
+                           bint force_client_auth):
+  pem_root_certs = str_to_bytes(pem_root_certs)
+  pem_key_cert_pairs = list(pem_key_cert_pairs)
+  cdef ServerCredentials credentials = ServerCredentials()
+  credentials.references.append(pem_root_certs)
+  credentials.references.append(pem_key_cert_pairs)
+  cdef char * c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs)
+  credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
+  credentials.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs)
+  cdef grpc_ssl_server_certificate_config *c_cert_config = NULL
+  c_cert_config = grpc_ssl_server_certificate_config_create(
+    c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
+    credentials.c_ssl_pem_key_cert_pairs_count)
+  cdef grpc_ssl_server_credentials_options* c_options = NULL
+  # C-core assumes ownership of c_cert_config
+  c_options = grpc_ssl_server_credentials_create_options_using_config(
+    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+    if force_client_auth else
+    GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+    c_cert_config)
+  # C-core assumes ownership of c_options
+  credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options)
   return credentials
 
+def server_certificate_config_ssl(pem_root_certs, pem_key_cert_pairs):
+  pem_root_certs = str_to_bytes(pem_root_certs)
+  pem_key_cert_pairs = list(pem_key_cert_pairs)
+  cdef ServerCertificateConfig cert_config = ServerCertificateConfig()
+  cert_config.references.append(pem_root_certs)
+  cert_config.references.append(pem_key_cert_pairs)
+  cert_config.c_pem_root_certs = _get_c_pem_root_certs(pem_root_certs)
+  cert_config.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
+  cert_config.c_ssl_pem_key_cert_pairs = _create_c_ssl_pem_key_cert_pairs(pem_key_cert_pairs)
+  cert_config.c_cert_config = grpc_ssl_server_certificate_config_create(
+    cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs,
+    cert_config.c_ssl_pem_key_cert_pairs_count)
+  return cert_config
+
+def server_credentials_ssl_dynamic_cert_config(initial_cert_config,
+                                               cert_config_fetcher,
+                                               bint force_client_auth):
+  if not isinstance(initial_cert_config, grpc.ServerCertificateConfig):
+    raise TypeError('initial_cert_config must be a grpc.ServerCertificateConfig')
+  if not callable(cert_config_fetcher):
+    raise TypeError('cert_config_fetcher must be callable')
+  cdef ServerCredentials credentials = ServerCredentials()
+  credentials.initial_cert_config = initial_cert_config
+  credentials.cert_config_fetcher = cert_config_fetcher
+  cdef grpc_ssl_server_credentials_options* c_options = NULL
+  c_options = grpc_ssl_server_credentials_create_options_using_config_fetcher(
+    GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+    if force_client_auth else
+    GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+    _server_cert_config_fetcher_wrapper,
+    <void*>credentials)
+  # C-core assumes ownership of c_options
+  credentials.c_credentials = grpc_ssl_server_credentials_create_with_options(c_options)
+  return credentials
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index f115106..660263f 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -391,6 +391,42 @@
     GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY
     GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
 
+  ctypedef enum grpc_ssl_certificate_config_reload_status:
+    GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED
+    GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW
+    GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
+
+  ctypedef struct grpc_ssl_server_certificate_config:
+    # We don't care about the internals
+    pass
+
+  ctypedef struct grpc_ssl_server_credentials_options:
+    # We don't care about the internals
+    pass
+
+  grpc_ssl_server_certificate_config * grpc_ssl_server_certificate_config_create(
+    const char *pem_root_certs,
+    const grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs)
+
+  void grpc_ssl_server_certificate_config_destroy(grpc_ssl_server_certificate_config *config)
+
+  ctypedef grpc_ssl_certificate_config_reload_status (*grpc_ssl_server_certificate_config_callback)(
+    void *user_data,
+    grpc_ssl_server_certificate_config **config)
+
+  grpc_ssl_server_credentials_options *grpc_ssl_server_credentials_create_options_using_config(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config *certificate_config)
+
+  grpc_ssl_server_credentials_options* grpc_ssl_server_credentials_create_options_using_config_fetcher(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config_callback cb,
+    void *user_data)
+
+  grpc_server_credentials *grpc_ssl_server_credentials_create_with_options(
+      grpc_ssl_server_credentials_options *options)
+
   ctypedef struct grpc_ssl_pem_key_cert_pair:
     const char *private_key
     const char *certificate_chain "cert_chain"
@@ -440,10 +476,6 @@
     # We don't care about the internals (and in fact don't know them)
     pass
 
-  grpc_server_credentials *grpc_ssl_server_credentials_create(
-      const char *pem_root_certs,
-      grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
-      size_t num_key_cert_pairs, int force_client_auth, void *reserved)
   void grpc_server_credentials_release(grpc_server_credentials *creds) nogil
 
   int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
index b8db274..66565d0 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
@@ -14,8 +14,44 @@
 
 cimport cpython
 
+import logging
 import time
+import grpc
 
+cdef grpc_ssl_certificate_config_reload_status _server_cert_config_fetcher_wrapper(
+        void* user_data, grpc_ssl_server_certificate_config **config) with gil:
+  # This is a credentials.ServerCertificateConfig
+  cdef ServerCertificateConfig cert_config = None
+  if not user_data:
+    raise ValueError('internal error: user_data must be specified')
+  credentials = <ServerCredentials>user_data
+  if not credentials.initial_cert_config_fetched:
+    # C-core is asking for the initial cert config
+    credentials.initial_cert_config_fetched = True
+    cert_config = credentials.initial_cert_config._cert_config
+  else:
+    user_cb = credentials.cert_config_fetcher
+    try:
+      cert_config_wrapper = user_cb()
+    except Exception:
+      logging.exception('Error fetching certificate config')
+      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
+    if cert_config_wrapper is None:
+      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED
+    elif not isinstance(cert_config_wrapper, grpc.ServerCertificateConfig):
+      logging.error('Error fetching certificate config: certificate '
+                    'config must be of type grpc.ServerCertificateConfig, '
+                    'not %s' % type(cert_config_wrapper).__name__)
+      return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
+    else:
+      cert_config = cert_config_wrapper._cert_config
+  config[0] = <grpc_ssl_server_certificate_config*>cert_config.c_cert_config
+  # our caller will assume ownership of memory, so we have to recreate
+  # a copy of c_cert_config here
+  cert_config.c_cert_config = grpc_ssl_server_certificate_config_create(
+      cert_config.c_pem_root_certs, cert_config.c_ssl_pem_key_cert_pairs,
+      cert_config.c_ssl_pem_key_cert_pairs_count)
+  return GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW
 
 cdef class Server:
 
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index bb7d990..330c418 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -80,7 +80,6 @@
   'src/core/lib/http/httpcli.cc',
   'src/core/lib/http/parser.cc',
   'src/core/lib/iomgr/call_combiner.cc',
-  'src/core/lib/iomgr/closure.cc',
   'src/core/lib/iomgr/combiner.cc',
   'src/core/lib/iomgr/endpoint.cc',
   'src/core/lib/iomgr/endpoint_pair_posix.cc',
diff --git a/src/python/grpcio_health_checking/MANIFEST.in b/src/python/grpcio_health_checking/MANIFEST.in
index 5255e4c..996c74a 100644
--- a/src/python/grpcio_health_checking/MANIFEST.in
+++ b/src/python/grpcio_health_checking/MANIFEST.in
@@ -1,4 +1,3 @@
 include grpc_version.py
-include health_commands.py
-graft grpc_health
+recursive-include grpc_health *.py
 global-exclude *.pyc
diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py
index 1f5e9c5..01d796f 100644
--- a/src/python/grpcio_health_checking/setup.py
+++ b/src/python/grpcio_health_checking/setup.py
@@ -20,10 +20,26 @@
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
-# Break import-style to ensure we can actually find our commands module.
-import health_commands
+# Break import-style to ensure we can actually find our local modules.
 import grpc_version
 
+
+class _NoOpCommand(setuptools.Command):
+    """No-op command."""
+
+    description = ''
+    user_options = []
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        pass
+
+
 CLASSIFIERS = [
     'Development Status :: 5 - Production/Stable',
     'Programming Language :: Python',
@@ -40,17 +56,28 @@
     '': '.',
 }
 
-SETUP_REQUIRES = (
-    'grpcio-tools>={version}'.format(version=grpc_version.VERSION),)
-
 INSTALL_REQUIRES = ('protobuf>=3.3.0',
                     'grpcio>={version}'.format(version=grpc_version.VERSION),)
 
-COMMAND_CLASS = {
-    # Run preprocess from the repository *before* doing any packaging!
-    'preprocess': health_commands.CopyProtoModules,
-    'build_package_protos': health_commands.BuildPackageProtos,
-}
+try:
+    # ensure we can load the _pb2_grpc module:
+    from grpc_health.v1 import health_pb2_grpc as _pb2_grpc
+    # if we can find the _pb2_grpc module, the package has already been built.
+    SETUP_REQUIRES = ()
+    COMMAND_CLASS = {
+        # wire up commands to no-op not to break the external dependencies
+        'preprocess': _NoOpCommand,
+        'build_package_protos': _NoOpCommand,
+    }
+except ImportError:  # we are in the build environment
+    import health_commands as _health_commands
+    SETUP_REQUIRES = (
+        'grpcio-tools=={version}'.format(version=grpc_version.VERSION),)
+    COMMAND_CLASS = {
+        # Run preprocess from the repository *before* doing any packaging!
+        'preprocess': _health_commands.CopyProtoModules,
+        'build_package_protos': _health_commands.BuildPackageProtos,
+    }
 
 setuptools.setup(
     name='grpcio-health-checking',
diff --git a/src/python/grpcio_reflection/MANIFEST.in b/src/python/grpcio_reflection/MANIFEST.in
index 0f2130c..d6fb6ce 100644
--- a/src/python/grpcio_reflection/MANIFEST.in
+++ b/src/python/grpcio_reflection/MANIFEST.in
@@ -1,4 +1,3 @@
 include grpc_version.py
-include reflection_commands.py
-graft grpc_reflection
+recursive-include grpc_reflection *.py
 global-exclude *.pyc
diff --git a/src/python/grpcio_reflection/setup.py b/src/python/grpcio_reflection/setup.py
index 9360550..ad9e869 100644
--- a/src/python/grpcio_reflection/setup.py
+++ b/src/python/grpcio_reflection/setup.py
@@ -21,10 +21,26 @@
 # Ensure we're in the proper directory whether or not we're being used by pip.
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
-# Break import-style to ensure we can actually find our commands module.
-import reflection_commands
+# Break import-style to ensure we can actually find our local modules.
 import grpc_version
 
+
+class _NoOpCommand(setuptools.Command):
+    """No-op command."""
+
+    description = ''
+    user_options = []
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        pass
+
+
 CLASSIFIERS = [
     'Development Status :: 5 - Production/Stable',
     'Programming Language :: Python',
@@ -41,17 +57,28 @@
     '': '.',
 }
 
-SETUP_REQUIRES = (
-    'grpcio-tools>={version}'.format(version=grpc_version.VERSION),)
-
 INSTALL_REQUIRES = ('protobuf>=3.3.0',
                     'grpcio>={version}'.format(version=grpc_version.VERSION),)
 
-COMMAND_CLASS = {
-    # Run preprocess from the repository *before* doing any packaging!
-    'preprocess': reflection_commands.CopyProtoModules,
-    'build_package_protos': reflection_commands.BuildPackageProtos,
-}
+try:
+    # ensure we can load the _pb2_grpc module:
+    from grpc_reflection.v1alpha import reflection_pb2_grpc as _pb2_grpc
+    # if we can find the _pb2_grpc module, the package has already been built.
+    SETUP_REQUIRES = ()
+    COMMAND_CLASS = {
+        # wire up commands to no-op not to break the external dependencies
+        'preprocess': _NoOpCommand,
+        'build_package_protos': _NoOpCommand,
+    }
+except ImportError:  # we are in the build environment
+    import reflection_commands as _reflection_commands
+    SETUP_REQUIRES = (
+        'grpcio-tools=={version}'.format(version=grpc_version.VERSION),)
+    COMMAND_CLASS = {
+        # Run preprocess from the repository *before* doing any packaging!
+        'preprocess': _reflection_commands.CopyProtoModules,
+        'build_package_protos': _reflection_commands.BuildPackageProtos,
+    }
 
 setuptools.setup(
     name='grpcio-reflection',
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index 8512d5b..e277a3e 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -46,6 +46,10 @@
   "unit._reconnect_test.ReconnectTest",
   "unit._resource_exhausted_test.ResourceExhaustedTest",
   "unit._rpc_test.RPCTest",
+  "unit._server_ssl_cert_config_test.ServerSSLCertConfigFetcherParamsChecks",
+  "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestCertConfigReuse",
+  "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithClientAuth",
+  "unit._server_ssl_cert_config_test.ServerSSLCertReloadTestWithoutClientAuth",
   "unit._thread_cleanup_test.CleanupThreadTest",
   "unit.beta._beta_features_test.BetaFeaturesTest",
   "unit.beta._beta_features_test.ContextManagementAndLifecycleTest",
diff --git a/src/python/grpcio_tests/tests/unit/_api_test.py b/src/python/grpcio_tests/tests/unit/_api_test.py
index a3351aa..d877e0f 100644
--- a/src/python/grpcio_tests/tests/unit/_api_test.py
+++ b/src/python/grpcio_tests/tests/unit/_api_test.py
@@ -30,19 +30,22 @@
             'ChannelConnectivity', 'StatusCode', 'RpcError', 'RpcContext',
             'Call', 'ChannelCredentials', 'CallCredentials',
             'AuthMetadataContext', 'AuthMetadataPluginCallback',
-            'AuthMetadataPlugin', 'ServerCredentials',
-            'UnaryUnaryMultiCallable', 'UnaryStreamMultiCallable',
-            'StreamUnaryMultiCallable', 'StreamStreamMultiCallable', 'Channel',
-            'ServicerContext', 'RpcMethodHandler', 'HandlerCallDetails',
-            'GenericRpcHandler', 'ServiceRpcHandler', 'Server',
-            'unary_unary_rpc_method_handler', 'unary_stream_rpc_method_handler',
+            'AuthMetadataPlugin', 'ServerCertificateConfig',
+            'ServerCredentials', 'UnaryUnaryMultiCallable',
+            'UnaryStreamMultiCallable', 'StreamUnaryMultiCallable',
+            'StreamStreamMultiCallable', 'Channel', 'ServicerContext',
+            'RpcMethodHandler', 'HandlerCallDetails', 'GenericRpcHandler',
+            'ServiceRpcHandler', 'Server', 'unary_unary_rpc_method_handler',
+            'unary_stream_rpc_method_handler',
             'stream_unary_rpc_method_handler',
             'stream_stream_rpc_method_handler',
             'method_handlers_generic_handler', 'ssl_channel_credentials',
             'metadata_call_credentials', 'access_token_call_credentials',
             'composite_call_credentials', 'composite_channel_credentials',
-            'ssl_server_credentials', 'channel_ready_future',
-            'insecure_channel', 'secure_channel', 'server',)
+            'ssl_server_credentials', 'ssl_server_certificate_config',
+            'ssl_server_credentials_dynamic_cert_config',
+            'channel_ready_future', 'insecure_channel', 'secure_channel',
+            'server',)
 
         six.assertCountEqual(self, expected_grpc_code_elements,
                              _from_grpc_import_star.GRPC_ELEMENTS)
diff --git a/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py b/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py
new file mode 100644
index 0000000..d2f9f11
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_server_ssl_cert_config_test.py
@@ -0,0 +1,520 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+This tests server certificate rotation support.
+
+Here we test various aspects of gRPC Python, and in some cases C-core
+by extension, support for server certificate rotation.
+
+* ServerSSLCertReloadTestWithClientAuth: test ability to rotate
+  server's SSL cert for use in future channels with clients while not
+  affecting any existing channel. The server requires client
+  authentication.
+
+* ServerSSLCertReloadTestWithoutClientAuth: like
+  ServerSSLCertReloadTestWithClientAuth except that the server does
+  not authenticate the client.
+
+* ServerSSLCertReloadTestCertConfigReuse: tests gRPC Python's ability
+  to deal with user's reuse of ServerCertificateConfig instances.
+"""
+
+import abc
+import collections
+import os
+import six
+import threading
+import unittest
+
+from concurrent import futures
+
+import grpc
+from tests.unit import resources
+from tests.testing import _application_common
+from tests.testing import _server_application
+from tests.testing.proto import services_pb2_grpc
+
+CA_1_PEM = resources.cert_hier_1_root_ca_cert()
+CA_2_PEM = resources.cert_hier_2_root_ca_cert()
+
+CLIENT_KEY_1_PEM = resources.cert_hier_1_client_1_key()
+CLIENT_CERT_CHAIN_1_PEM = (resources.cert_hier_1_client_1_cert() +
+                           resources.cert_hier_1_intermediate_ca_cert())
+
+CLIENT_KEY_2_PEM = resources.cert_hier_2_client_1_key()
+CLIENT_CERT_CHAIN_2_PEM = (resources.cert_hier_2_client_1_cert() +
+                           resources.cert_hier_2_intermediate_ca_cert())
+
+SERVER_KEY_1_PEM = resources.cert_hier_1_server_1_key()
+SERVER_CERT_CHAIN_1_PEM = (resources.cert_hier_1_server_1_cert() +
+                           resources.cert_hier_1_intermediate_ca_cert())
+
+SERVER_KEY_2_PEM = resources.cert_hier_2_server_1_key()
+SERVER_CERT_CHAIN_2_PEM = (resources.cert_hier_2_server_1_cert() +
+                           resources.cert_hier_2_intermediate_ca_cert())
+
+# for use with the CertConfigFetcher. Roughly a simple custom mock
+# implementation
+Call = collections.namedtuple('Call', ['did_raise', 'returned_cert_config'])
+
+
+def _create_client_stub(
+        port,
+        expect_success,
+        root_certificates=None,
+        private_key=None,
+        certificate_chain=None,):
+    channel = grpc.secure_channel('localhost:{}'.format(port),
+                                  grpc.ssl_channel_credentials(
+                                      root_certificates=root_certificates,
+                                      private_key=private_key,
+                                      certificate_chain=certificate_chain))
+    if expect_success:
+        # per Nathaniel: there's some robustness issue if we start
+        # using a channel without waiting for it to be actually ready
+        grpc.channel_ready_future(channel).result(timeout=10)
+    return services_pb2_grpc.FirstServiceStub(channel)
+
+
+class CertConfigFetcher(object):
+
+    def __init__(self):
+        self._lock = threading.Lock()
+        self._calls = []
+        self._should_raise = False
+        self._cert_config = None
+
+    def reset(self):
+        with self._lock:
+            self._calls = []
+            self._should_raise = False
+            self._cert_config = None
+
+    def configure(self, should_raise, cert_config):
+        assert not (should_raise and cert_config), (
+            "should not specify both should_raise and a cert_config at the same time"
+        )
+        with self._lock:
+            self._should_raise = should_raise
+            self._cert_config = cert_config
+
+    def getCalls(self):
+        with self._lock:
+            return self._calls
+
+    def __call__(self):
+        with self._lock:
+            if self._should_raise:
+                self._calls.append(Call(True, None))
+                raise ValueError('just for fun, should not affect the test')
+            else:
+                self._calls.append(Call(False, self._cert_config))
+                return self._cert_config
+
+
+class _ServerSSLCertReloadTest(
+        six.with_metaclass(abc.ABCMeta, unittest.TestCase)):
+
+    def __init__(self, *args, **kwargs):
+        super(_ServerSSLCertReloadTest, self).__init__(*args, **kwargs)
+        self.server = None
+        self.port = None
+
+    @abc.abstractmethod
+    def require_client_auth(self):
+        raise NotImplementedError()
+
+    def setUp(self):
+        self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+        services_pb2_grpc.add_FirstServiceServicer_to_server(
+            _server_application.FirstServiceServicer(), self.server)
+        switch_cert_on_client_num = 10
+        initial_cert_config = grpc.ssl_server_certificate_config(
+            [(SERVER_KEY_1_PEM, SERVER_CERT_CHAIN_1_PEM)],
+            root_certificates=CA_2_PEM)
+        self.cert_config_fetcher = CertConfigFetcher()
+        server_credentials = grpc.ssl_server_credentials_dynamic_cert_config(
+            initial_cert_config,
+            self.cert_config_fetcher,
+            require_client_auth=self.require_client_auth())
+        self.port = self.server.add_secure_port('[::]:0', server_credentials)
+        self.server.start()
+
+    def tearDown(self):
+        if self.server:
+            self.server.stop(None)
+
+    def _perform_rpc(self, client_stub, expect_success):
+        # we don't care about the actual response of the rpc; only
+        # whether we can perform it or not, and if not, the status
+        # code must be UNAVAILABLE
+        request = _application_common.UNARY_UNARY_REQUEST
+        if expect_success:
+            response = client_stub.UnUn(request)
+            self.assertEqual(response, _application_common.UNARY_UNARY_RESPONSE)
+        else:
+            with self.assertRaises(grpc.RpcError) as exception_context:
+                client_stub.UnUn(request)
+            self.assertEqual(exception_context.exception.code(),
+                             grpc.StatusCode.UNAVAILABLE)
+
+    def _do_one_shot_client_rpc(self,
+                                expect_success,
+                                root_certificates=None,
+                                private_key=None,
+                                certificate_chain=None):
+        client_stub = _create_client_stub(
+            self.port,
+            expect_success,
+            root_certificates=root_certificates,
+            private_key=private_key,
+            certificate_chain=certificate_chain)
+        self._perform_rpc(client_stub, expect_success)
+        del client_stub
+
+    def _test(self):
+        # things should work...
+        self.cert_config_fetcher.configure(False, None)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertIsNone(actual_calls[0].returned_cert_config)
+
+        # client should reject server...
+        # fails because client trusts ca2 and so will reject server
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._do_one_shot_client_rpc(
+            False,
+            root_certificates=CA_2_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertGreaterEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        for i, call in enumerate(actual_calls):
+            self.assertFalse(call.did_raise, 'i= {}'.format(i))
+            self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i))
+
+        # should work again...
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(True, None)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertTrue(actual_calls[0].did_raise)
+        self.assertIsNone(actual_calls[0].returned_cert_config)
+
+        # if with_client_auth, then client should be rejected by
+        # server because client uses key/cert1, but server trusts ca2,
+        # so server will reject
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._do_one_shot_client_rpc(
+            not self.require_client_auth(),
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_1_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_1_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertGreaterEqual(len(actual_calls), 1)
+        for i, call in enumerate(actual_calls):
+            self.assertFalse(call.did_raise, 'i= {}'.format(i))
+            self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i))
+
+        # should work again...
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertIsNone(actual_calls[0].returned_cert_config)
+
+        # now create the "persistent" clients
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        persistent_client_stub_A = _create_client_stub(
+            self.port,
+            True,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        self._perform_rpc(persistent_client_stub_A, True)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertIsNone(actual_calls[0].returned_cert_config)
+
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        persistent_client_stub_B = _create_client_stub(
+            self.port,
+            True,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        self._perform_rpc(persistent_client_stub_B, True)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertIsNone(actual_calls[0].returned_cert_config)
+
+        # moment of truth!! client should reject server because the
+        # server switch cert...
+        cert_config = grpc.ssl_server_certificate_config(
+            [(SERVER_KEY_2_PEM, SERVER_CERT_CHAIN_2_PEM)],
+            root_certificates=CA_1_PEM)
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, cert_config)
+        self._do_one_shot_client_rpc(
+            False,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertGreaterEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        for i, call in enumerate(actual_calls):
+            self.assertFalse(call.did_raise, 'i= {}'.format(i))
+            self.assertEqual(call.returned_cert_config, cert_config,
+                             'i= {}'.format(i))
+
+        # now should work again...
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_2_PEM,
+            private_key=CLIENT_KEY_1_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_1_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertIsNone(actual_calls[0].returned_cert_config)
+
+        # client should be rejected by server if with_client_auth
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._do_one_shot_client_rpc(
+            not self.require_client_auth(),
+            root_certificates=CA_2_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertGreaterEqual(len(actual_calls), 1)
+        for i, call in enumerate(actual_calls):
+            self.assertFalse(call.did_raise, 'i= {}'.format(i))
+            self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i))
+
+        # here client should reject server...
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._do_one_shot_client_rpc(
+            False,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertGreaterEqual(len(actual_calls), 1)
+        for i, call in enumerate(actual_calls):
+            self.assertFalse(call.did_raise, 'i= {}'.format(i))
+            self.assertIsNone(call.returned_cert_config, 'i= {}'.format(i))
+
+        # persistent clients should continue to work
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._perform_rpc(persistent_client_stub_A, True)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 0)
+
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, None)
+        self._perform_rpc(persistent_client_stub_B, True)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 0)
+
+
+class ServerSSLCertConfigFetcherParamsChecks(unittest.TestCase):
+
+    def test_check_on_initial_config(self):
+        with self.assertRaises(TypeError):
+            grpc.ssl_server_credentials_dynamic_cert_config(None, str)
+        with self.assertRaises(TypeError):
+            grpc.ssl_server_credentials_dynamic_cert_config(1, str)
+
+    def test_check_on_config_fetcher(self):
+        cert_config = grpc.ssl_server_certificate_config(
+            [(SERVER_KEY_2_PEM, SERVER_CERT_CHAIN_2_PEM)],
+            root_certificates=CA_1_PEM)
+        with self.assertRaises(TypeError):
+            grpc.ssl_server_credentials_dynamic_cert_config(cert_config, None)
+        with self.assertRaises(TypeError):
+            grpc.ssl_server_credentials_dynamic_cert_config(cert_config, 1)
+
+
+class ServerSSLCertReloadTestWithClientAuth(_ServerSSLCertReloadTest):
+
+    def require_client_auth(self):
+        return True
+
+    test = _ServerSSLCertReloadTest._test
+
+
+class ServerSSLCertReloadTestWithoutClientAuth(_ServerSSLCertReloadTest):
+
+    def require_client_auth(self):
+        return False
+
+    test = _ServerSSLCertReloadTest._test
+
+
+class ServerSSLCertReloadTestCertConfigReuse(_ServerSSLCertReloadTest):
+    """Ensures that `ServerCertificateConfig` instances can be reused.
+
+    Because C-core takes ownership of the
+    `grpc_ssl_server_certificate_config` encapsulated by
+    `ServerCertificateConfig`, this test reuses the same
+    `ServerCertificateConfig` instances multiple times to make sure
+    gRPC Python takes care of maintaining the validity of
+    `ServerCertificateConfig` instances, so that such instances can be
+    re-used by user application.
+    """
+
+    def require_client_auth(self):
+        return True
+
+    def setUp(self):
+        self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+        services_pb2_grpc.add_FirstServiceServicer_to_server(
+            _server_application.FirstServiceServicer(), self.server)
+        self.cert_config_A = grpc.ssl_server_certificate_config(
+            [(SERVER_KEY_1_PEM, SERVER_CERT_CHAIN_1_PEM)],
+            root_certificates=CA_2_PEM)
+        self.cert_config_B = grpc.ssl_server_certificate_config(
+            [(SERVER_KEY_2_PEM, SERVER_CERT_CHAIN_2_PEM)],
+            root_certificates=CA_1_PEM)
+        self.cert_config_fetcher = CertConfigFetcher()
+        server_credentials = grpc.ssl_server_credentials_dynamic_cert_config(
+            self.cert_config_A,
+            self.cert_config_fetcher,
+            require_client_auth=True)
+        self.port = self.server.add_secure_port('[::]:0', server_credentials)
+        self.server.start()
+
+    def test_cert_config_reuse(self):
+
+        # succeed with A
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, self.cert_config_A)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertEqual(actual_calls[0].returned_cert_config,
+                         self.cert_config_A)
+
+        # fail with A
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, self.cert_config_A)
+        self._do_one_shot_client_rpc(
+            False,
+            root_certificates=CA_2_PEM,
+            private_key=CLIENT_KEY_1_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_1_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertGreaterEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        for i, call in enumerate(actual_calls):
+            self.assertFalse(call.did_raise, 'i= {}'.format(i))
+            self.assertEqual(call.returned_cert_config, self.cert_config_A,
+                             'i= {}'.format(i))
+
+        # succeed again with A
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, self.cert_config_A)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertEqual(actual_calls[0].returned_cert_config,
+                         self.cert_config_A)
+
+        # succeed with B
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, self.cert_config_B)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_2_PEM,
+            private_key=CLIENT_KEY_1_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_1_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertEqual(actual_calls[0].returned_cert_config,
+                         self.cert_config_B)
+
+        # fail with B
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, self.cert_config_B)
+        self._do_one_shot_client_rpc(
+            False,
+            root_certificates=CA_1_PEM,
+            private_key=CLIENT_KEY_2_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_2_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertGreaterEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        for i, call in enumerate(actual_calls):
+            self.assertFalse(call.did_raise, 'i= {}'.format(i))
+            self.assertEqual(call.returned_cert_config, self.cert_config_B,
+                             'i= {}'.format(i))
+
+        # succeed again with B
+        self.cert_config_fetcher.reset()
+        self.cert_config_fetcher.configure(False, self.cert_config_B)
+        self._do_one_shot_client_rpc(
+            True,
+            root_certificates=CA_2_PEM,
+            private_key=CLIENT_KEY_1_PEM,
+            certificate_chain=CLIENT_CERT_CHAIN_1_PEM)
+        actual_calls = self.cert_config_fetcher.getCalls()
+        self.assertEqual(len(actual_calls), 1)
+        self.assertFalse(actual_calls[0].did_raise)
+        self.assertEqual(actual_calls[0].returned_cert_config,
+                         self.cert_config_B)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/credentials/README b/src/python/grpcio_tests/tests/unit/credentials/README
deleted file mode 100644
index cb20dcb..0000000
--- a/src/python/grpcio_tests/tests/unit/credentials/README
+++ /dev/null
@@ -1 +0,0 @@
-These are test keys *NOT* to be used in production.
diff --git a/src/python/grpcio_tests/tests/unit/credentials/README.md b/src/python/grpcio_tests/tests/unit/credentials/README.md
new file mode 100644
index 0000000..100b43c
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/README.md
@@ -0,0 +1,15 @@
+These are test keys *NOT* to be used in production.
+
+The `certificate_hierarchy_1` and `certificate_hierarchy_2` contain
+two disjoint but similarly organized certificate hierarchies. Each
+contains:
+
+* The respective root CA cert in `certs/ca.cert.pem`
+
+* The intermediate CA cert in
+  `intermediate/certs/intermediate.cert.pem`, signed by the root CA
+
+* A client cert and a server cert--both signed by the intermediate
+  CA--in `intermediate/certs/client.cert.pem` and
+  `intermediate/certs/localhost-1.cert.pem`; the corresponding keys
+  are in `intermediate/private`
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem
new file mode 100644
index 0000000..604b86f
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFZDCCA0ygAwIBAgIJAKfkDFZ6+Ly/MA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV
+BAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAOBgNVBAMM
+B3Jvb3QgY2EwHhcNMTcxMTAyMDAzNzA1WhcNMzcxMDI4MDAzNzA1WjA/MQswCQYD
+VQQGEwJ1czEOMAwGA1UECAwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MRAwDgYDVQQD
+DAdyb290IGNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxlSUuSbi
+o66tT2ZCqu9wNqSX8VhAJkmrAT5y6m2V0VlQ8Gz7ddynW5UVSmtvDNTebZ15FrvO
+6Ng7QnwXXNs/dEzl6oMe6AKDZpuWScVkiqH1UYWBkMLRygWCTEYpSTWTpZWk1zxj
+DJ2LlIoO1X/ufLyLOfy2a2XEz8ICzJePmqVca6fmfEtCTj1/8FcwCBF6YlUWVzlR
+wewjanQo/lorTYbub+Q6LGxPXZ8W0qoKZzLDSD9cnj4pcJzGGFeu9KkNaW4rldZG
+t7mTGQqIRc98dDRc9Jb7PqL8tMPLidw1KErUi05ofxggc5vqNnj4xBl6aX6b/EYN
+rBLzO2e0FazX6TwNKwwg68vbOanpDq5LVmIUH8bY1zNZ+JPBGO9pXlAA0YwLx86r
+R7YhQ431ZpJ2KGnYjVhYnZ2L3NjV3UYX3x5Z3OrDj9hybhucJB48DMQ1+loEabwK
+fSUJtcSPc8dCIibxVKidBFgaTPXtHy2MPXuhMhR7PCtMpE7RPUoYmdZLr9FNN1ty
+/RAbwBfuhGLbRI2qqJgbOzHJHaOY/FtShfooLz7lt4LIjPTARaNsulG2rbv+m3z9
+mhNjL+peV8gni/xyOYYTbdzZagLrtSHeTWsITvmVt0flMHkjHyv35rw23+hBlSjp
+6+S+0MmwuwxqBBccBSlZ9t3Xh1N+vFkb2UkCAwEAAaNjMGEwHQYDVR0OBBYEFJWE
+tQwTbTCgZWNN08VSxjdNA0oaMB8GA1UdIwQYMBaAFJWEtQwTbTCgZWNN08VSxjdN
+A0oaMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+CwUAA4ICAQCNjPv/e3ozX1PuN5Tluf0yOmKCxKVCK3Pp98WkDzH4Rp1urEeYrGJL
+vBNcl17avOJ0e+zTVYPXFviFbsBsU/zaf+TqEujXabsdL+nvvCJ2mMqYn4wyDFjS
+zDNbGH6O0ENZz5NSY0/UGSOHYrYnYB94QRFLbbf0Y3PmBS2eRNjIUnv7ytPZNMi/
+piM+QhPb0Ebyk0rHQZ0RAJaC/wsEtqP8TGV/fx+AzG7zW/zxgPTrgIThy138tLQ+
+xCVDP9H2c17nVP6vjYzKnMZ94uGrGqUzV9vU7EqYl0uZflIf98pLfdKHnQ3heqds
+8KQPNKRxVvcc92qv2pQY951wb1fkhLutjHn7TUvrenyAngz+Vs19NxbqLPys1CTw
+iaL7vZ8VE/aEDm1tjt5SLM474tpATjk1+qMRaWnii8J5rTodYHP+Zu2GxyIrMiGq
+tfNZMYI0tETK1XmEo75E/3s9pmIeQNGKLFp+qL7xrVyN/2ffNv0at8kkqXluunK9
+/Ki0gKYlGFm4Eu8t/nHMqhBx/njYg6pLDuarLW6ftUV7aHd7qKcCWOWqK6gnH/vX
+3Apv31eltZBBVN69p3CFy2oMnjrom2Yn/DUXFwrJLBiNJ1dd1JyDxpqpJ74ZQy+/
+pSRWMTRM5SuC7lYARx5rYPmp6cZJWyWRH/3r7bwS699/W965pa5nug==
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem
new file mode 100644
index 0000000..44bc562
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIExzCCAq+gAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx
+DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt
+ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzcwNloXDTI3MTAzMTAwMzcwNlowPjELMAkG
+A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEPMA0GA1UE
+AwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwquL6gtP
+R7P9xJK76FTj8fI5TSJa3cAMt1p6CmessjHQq7nQ6DWLGVi4XIt9Sc/1C3rXupOe
+90Ok4L0tsuVZH78Wn0EBmBH7S4IbhU9P+aJ9mcigepj1lnxWqoVblgeJYKMOOwAf
+pAKUNMWDSm+nCfwE+R5d8d8cfA41Awq1jTRjOVpiJq6aoKfs791a1ZkZde3kFrNV
+AVjC06GgA1lZd3sHf94hmLeC+xJztRXVE9e+7dcc7nFDH0t5DIKYBAklsHg77mZa
+3IK4aOZew7Lm6diPoMnAzXh2rWpJU6RrEE29gIkJBsF8CL1Ndg9MzssCg6KBjoai
+Vt5dJ+4TSEGCOwIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD
+AgWgMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2Vy
+dGlmaWNhdGUwHQYDVR0OBBYEFPeuKDCswk8jaH9tl6X+uXjo+WM1MB8GA1UdIwQY
+MBaAFCoqYgmKh3CUafVp+paXxfz+He+FMA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUE
+FjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBADYAp8XS
+UjMEpX/zVjRWpAAT4HNEJylCV1QNyhBIAyx38A6xJYuFIx966Htd6W9/Rw4sUY6y
+F4pOmnLCRxIPqFzzMYcBHSpynlcu5G7zqIod3wYIk7BNlB0AzkZn6yD8bM1y5Plf
+myzQVDEGggrDtaW2EehhNIB+wOmbRGITjIcZUEr8V4BlLXkCqOuxauxl82d5/k2w
+LAhjOb9d1VW6RT8+Lcn6drhHZdvtSCe8Z27BcXhaQLL8366mhbigKYJt5adD0KOx
+pl0MQcoL1Rth5cJEj+1/lgUaxcnvh7TaIIGEx0h3olQXsTxSTypU/nww2Ic41xdG
+xl3xvHsxe20IvOOAMRfS/LPW7MCtQ3k0BqB/rAQvmB0r5YITLlMJuBqg+zjYrG/j
+s5szSGAz9r0leFuPraeuZA41d9UBTAJMoVrrQZ4xVHMXQi1oz9E9KlIdbO9+spvC
+ulfO+D+Z4a9trYSWhnQL2dSHT0+kHqJ/8GipiUNP/yAC76dRpDVR3xtYNr73iw0j
+hyDsVjihTD8JBebs3axnt+Bc+FwoCCd6CVcsggfGUNhu/N5LS78b13PcaRzrUNjU
+Eh+8cJvMLst+UQzePlyazzpn7jjN3KsBzWUkbnXCtUs2qRMn8f2gZqliDo7JSFvy
+WtBSCYpikOivuJSQUlrHQ8NaXeddyWQzLY79
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem
new file mode 100644
index 0000000..98e1366
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCdXMx
+DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEQMA4GA1UEAwwHcm9vdCBj
+YTAeFw0xNzExMDIwMDM3MDZaFw0yNzEwMzEwMDM3MDZaMEcxCzAJBgNVBAYTAnVz
+MQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxGDAWBgNVBAMMD2ludGVy
+bWVkaWF0ZSBjYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOOxzve7
+CG2P9SvKfXkJVTXkj4y79JSZ77Kud/TiPfDbHTqZWKuLTXkOCkCCxfpuJvWXnnj5
+1AeLCdKx9hEwJQeU23EXDt1K+RsRyl09SXtPNnJnqHD1mUHRQR28vGX5ctrQzK8J
+Sa6/mHW4bX8ol100npbgVMDnM4IDfLYcsv4BXMICGkSHOW6Gn0zJaeHzRVPpmnK/
+0k/GQAcIrU2sZ39kVlVQkWq3HJC28cNL/P04hjh4gAf0evo/k9VrEtxPWYMfiPDt
+kOAKueoPv/VTA/zL5t8lyzfhrhxvsJxFg/klapPXK0gLLbhsHyOhnkbrzvmSR4Rw
+xubYJ2dDK0DKx+BIZqlFznjP9BvOtvtuVVMyqg9cfgc7J/OjvAguO0f93MLSfIWP
+uISqv7Llt/Blvy/xI5owvOKVc/hm3d+5pqjWBC1HkVwo4qugpWmM49dFWl4kc4c7
+ayYUjTmcgoj1ZR89w4Off/bPd1A6gXqSkw2VQfgFF+uOos84fP1V+zPWhp3UDY3P
+bFeJtuTdv1gR5w1jCIq6xVJ+UsyDZBaYP7yBBRiNzS1/yXJpnXrvHmDfUeQHLBPR
+N0nbMjqXJ1dVpZwydiI0Qx9DnJtOaq/spUreXr8+PU2jeQdCCAN21MB1umr2gZBJ
+8MZBStTgE7SDByfGmGfp7B5/s/r4O/rNc4WzAgMBAAGjZjBkMB0GA1UdDgQWBBQq
+KmIJiodwlGn1afqWl8X8/h3vhTAfBgNVHSMEGDAWgBSVhLUME20woGVjTdPFUsY3
+TQNKGjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
+9w0BAQsFAAOCAgEAvzLu/Jc8DlfCltVufC54UZ8DVwUfxdGapNBGv4icrs1wMV3S
+xqdaLO+vSp9NeEufi724+/hj4URapW9kSt2TMD7BNJ61QSATZFJajxTFgGa0Zz95
+RBDw8/b5Arz/2pOF4VX+FJ+wqHvoH/2A0T+fwz8hLORhxZHv/cUN6kif4FKCwryQ
+s89e694kXkEiJfquvu7DR9hYCLOJwzMOOJiTnjz3hlQg4WGu7Z8ZvqzCM+how1hr
+nYbUx6a+HfoUf79AHJB0N1EsEEetJ+omvTdrrayCvy1bHA3QgHlJ28QZIJ7MzX9E
+n11/xQ95iTuSp8iWurzjTjbrm7eHnGUh+5QubYLXOzbqKzNZu72w0uvWv6ptIudU
+usttltiwW8H9kP0ArWTcZDPhhPfS9impFlhiPDk1wUv2/7g+Zz1OaOb7IiSH0s8y
+FG72AB8ucJ5dNa/2q5dJiM8Gm5CbiVw5RXTBjlfTTkNeM6LBI3dRghpPdU7Kbfhn
+xYs9vnRZeRMJHrcodLuwVcpY/gyeJ0k5LD6eIPCJmatkYZ122isYyMX8lL2P5aR+
+7d2hhqcOCproOrtThjp6nW2jWTB+R/O2+s6mhKSPgfbY2cdky1Y9FSJxSNayb9B8
+eQ+A29iOHrGVAA0R/rvw119rLAYxjXzToM28owx7IyXKrBaU4RMv5yokgag=
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem
new file mode 100644
index 0000000..f15f1cf
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFFzCCAv+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx
+DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt
+ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzcwNloXDTI3MTAzMTAwMzcwNlowTTELMAkG
+A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEKMAgGA1UE
+CwwBMTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEArRAy0Nim9P883BAisXdFoKmgHGTtcLH/SzwkkPWTFHz0rHU1Klwz
+w8u3OkRyvgoQp7DqkohboNMDwg5VrOOcfKwtM2GZ5jixo+YKvJ25oj8Jfr+40baz
+nyWTmOcfoviKrb7u2T9BPEEz5og+lXRDAsTFATGaQDX2LN3Dd9KIw+7sWY+gc3Zi
+13HHaWYhtmfJjzFbH1vDxHKCdSdgtPyEhqcJ4OC6wbgp/mQ01VlPAr08kRfkC8mT
+TS7atqc410irKViF3sWi4YNPf7LuBrjo75FIIOp+sQgZE6xwOuZ/9bT2Zx/IUtCC
+TqzVgZI0s5NVlINtWR6eyyxQ1uDKTs4xrQIDAQABo4IBBTCCAQEwCQYDVR0TBAIw
+ADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2Vu
+ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUDE8pwi7aELJjvyNT
+ed81/KIgGfowaAYDVR0jBGEwX4AUKipiCYqHcJRp9Wn6lpfF/P4d74WhQ6RBMD8x
+CzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAO
+BgNVBAMMB3Jvb3QgY2GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
+BgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEA2cvXxJw120Z9oWXyGwR6CH7TcXoy
+1i77B1M5j0Krvkjh2/MkEU+JxpZcrhAgZODK9wMPeIUIpJNw2t6Hg+vigpInu7pY
+MXR4IA5XLnhGV/hueXa0JLia5FG1TISxr4piW6Jd9P2pOt3ECm3Url/F0OeFF/74
+jGaAlWkbhqWJ9M7Gd4QP2wUNm0P4CwAqS9DC6dnMz+JXTakEUirOpmq7U8UKT+5N
+QS1K4WuH671n4MiYye3+UoRYt4zPjOzN+QxzvAMtkUBspPmWD6txmD5tKUYDECqn
+0sSbY6ytD30OTHIbICFp40arOffmEEJSriL+uQNPPmvqMxX1G2kUFGm15NLPs8Xa
+J7ChrAaJzssN5J3myZUbDfCuxmTkWg+hGvGmxLraVNWc3fzKFmdszSkXrGIdf2HR
+gZeFI3w6M4Ktx3KctXlsjwqQTYZI/WwLOEpsrHQBPBLQhISyNw4xjZ4MxK8SFZuQ
+IiGps/do0eEgeQ+o3gD1dIXt8YxFIxrgk0pzJONpXGgv/cZrukbLNTBdkTSkIwtx
+TXKdiJbO17H24MvW+UxFdsIoJXmfQZWdQC3p+Dl0iP+K80aI6WbaysmToHuOi216
+e49nmiM72Izul2zmBi7Cq2nRQbHAETsFfqC34FzJlx0aP8WS953IBD0jNi1BB+AX
+BxwiZ1rPjeMvekI=
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem
new file mode 100644
index 0000000..d8a2163
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAwquL6gtPR7P9xJK76FTj8fI5TSJa3cAMt1p6CmessjHQq7nQ
+6DWLGVi4XIt9Sc/1C3rXupOe90Ok4L0tsuVZH78Wn0EBmBH7S4IbhU9P+aJ9mcig
+epj1lnxWqoVblgeJYKMOOwAfpAKUNMWDSm+nCfwE+R5d8d8cfA41Awq1jTRjOVpi
+Jq6aoKfs791a1ZkZde3kFrNVAVjC06GgA1lZd3sHf94hmLeC+xJztRXVE9e+7dcc
+7nFDH0t5DIKYBAklsHg77mZa3IK4aOZew7Lm6diPoMnAzXh2rWpJU6RrEE29gIkJ
+BsF8CL1Ndg9MzssCg6KBjoaiVt5dJ+4TSEGCOwIDAQABAoIBABECKAFU56JeKYfp
+Qh20fQ4Amd0RaVsCkpnaf9s037PaAl9eptADDZozVDhRv6qZTtGn8/1LNJJqCJfS
+L5H30+egLHvRlDATMh+QyJLHMTegaNTs4IiVoK97QZ84c54SHoCg/ndNNXaA+y35
+K9VvF+sZZ93UN2UQl06Hdz5Cy0YA7L5HIIH3Ezk0ArAw4AarLil5mv4yEz2ApZhm
+Tw4I4yNfxB7tZeP+ekNg0XXRL1quA0tGblp+A5fAFfVMDplqqB2d3/KxPR9FSEOi
+4PzBZ5Mq2wQBPIaNog5um9qkw6VKxjl5sQGhP1GGTA8iZqR9iM2+xh57xdCZm3g3
+jcr+aPECgYEA42mXTsF/4oBQtU6hh/sOCMWHhxAPstKpQHFMKGYLHKEJ/V1qq0Sd
+d0kswAYCmH5G9ookzu5p7pNf0hUUHO5EwelpSZ3FEmtIM+oBwSnDk3vGuadYXN5X
+fPuVUla65B1F9SSwapYNBUAiRgrY69Knca2rkTSdcZQaBuWmo684UQcCgYEA2yRE
+P23I/9N6AVhKB/zTRtil1AxnTW8o+j7AE4q1o+xly7DS7DT34INaLKLiuG6ylV1F
+UoTiqmWqH3A7m3o3Id2AnVf/oDoKV78LCXRF3dJJWvzrPdob2fLlwyjgqXYvmD3O
+UH/OFY2blYcAHOYib1Y1AAhHPlXiHA52BYZtnC0CgYAVjjitWmII0ijURrPA8+cM
+pcyG3NrgFF++n/6cBbAf8pPD1Er8GPDkEaeQPAGa+r03OTjr9GVOG+IFQ8I4S81w
+o/M66x129XxOj2vDJ3ZGUIExr88MXnbkfeRVfasRXET5S5T9RWPOj5mwEe8lyz3b
+5J5SkS4rSeJ9rN7yvPUVmQKBgAvrrB67LRzldxSNpfFLSn7nGBYx2oi2zEbYlQA7
+ImhZWqw64S5iLz2yR3x4G9cmhmZjnXrAqcfVIez14PgzLL6V2wI0ID6qCZf+V25b
+OdW4M69UZMOHks5HTUJRfe8Z87rXWdq9KQu5GUaIAnSP/D2MNfPbf2yfpV4bV0Yz
+qtC9AoGAD3/XXaeGCdV5DPomEmehp84JXU2q/YECRvph46tr4jArG67PCvx2m84B
++W6my4Yi7QJcW4gC0gsdAuxbJl4Y7MCZBnTtNIRCRnHEIciKITJ/+brFln5QUgyn
+WnXEPN8q7VjSVXGrljFuLWkzi2Vh8iZDgourNfW+iYDGCJjx1H0=
+-----END RSA PRIVATE KEY-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem
new file mode 100644
index 0000000..aa83f1a
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEArRAy0Nim9P883BAisXdFoKmgHGTtcLH/SzwkkPWTFHz0rHU1
+Klwzw8u3OkRyvgoQp7DqkohboNMDwg5VrOOcfKwtM2GZ5jixo+YKvJ25oj8Jfr+4
+0baznyWTmOcfoviKrb7u2T9BPEEz5og+lXRDAsTFATGaQDX2LN3Dd9KIw+7sWY+g
+c3Zi13HHaWYhtmfJjzFbH1vDxHKCdSdgtPyEhqcJ4OC6wbgp/mQ01VlPAr08kRfk
+C8mTTS7atqc410irKViF3sWi4YNPf7LuBrjo75FIIOp+sQgZE6xwOuZ/9bT2Zx/I
+UtCCTqzVgZI0s5NVlINtWR6eyyxQ1uDKTs4xrQIDAQABAoIBAC56mDswxH4uAmlT
+yA2Da+a/R6n4jTBkDZ1mFKf93Dd3a7rZa6Lpylk+YAI9GdfiGiD/SbB7AKjLo0m9
+0dKx+ngdQbJ39v42obbT9HQ9o/poFaO91+QyvkDytZYuFHgPaidJjRo5e8qz9D1o
+v+4hoFGhCQvOB5BRLcFU+cc3etWr5t61sNL/kKCWEDd+MWWsOCHpdhEoWC+o25pC
+bhD3FG5xoz+8zL7WdNfke/4twfKoBJ/kq89bfIkl8eKpg387WBQY44RJF7/zVr7a
+9dsUuW2y/wVXslCHChjSrxhRlOyy5ssv3EgKh8gPkZ+oeKuONqAGw27nyKyvpjxS
+i62K+WECgYEA4oKpIS2D77RCC6rpYIK6KYfbUcNSOtHFvcbf0RH9Oi8vSRYum2ZA
+/ITdWSFgWkhT6iOSPuvZlu/EvueWDgNgW1ZVsTMFeapz1+Jwk7JRoBKF1dUEwELh
+jdAswdh0MLbgBYs6NXtVVkeK2ocgZtosmt1PUktl566NlyIyhOjH6vkCgYEAw5g0
+cteTpz+noKsfWcbnjyesuQy0caICfZIE01nKv9rKTF8BtCO6Qxj10iM2o00jW7Vl
+tZa/igjuqvozXAHBI3xegtrWV05urkjj3FB/Pyuqsx3wxhAdSNchQjdTjwUBQEzp
+3ztGSlDTRPpijnpW28lg8Kkr3weryaHvl0xM1VUCgYBqnTN8QU8rgT3g/gYw/fcf
+2ylY98V5mAkqBTSN1JjLTTBFh2JSlLOb5/HDpRkUBZ0xxKJuaVaWW67QaHLRj7dH
+5oAZErnOBXPXNmbkrfcLkAxclJJS6Gf/9u9KIla2Iy2YjmrMh4uoO65Yo2eV4bVD
+A031nzWM8jUE4PzEYEjRCQKBgHDdTj6KiQg0Yg0DUabjcNEZasCpRSJhAyDkdmZi
+5OzKWnuxQvFowF1hdM/aQ/f9Vg7gYJ1lLIeBWf9NOv+3f3RzmrHVh2N/vbxSETIb
+PSH9l5WeDEauG8fhY66q8EuR7sPk3ftTX98YPqEJ/n8Ktz5COO8GH2umKInEKNXc
+UGW1AoGAfENy7vInNv0tzFWPSYdFgesvzo7e8mXyVO8hCyWvY3rxW2on7qfLF3Z9
+fHjd7P9gULja0n1kvmxwUC3u20RrvpY59F4hfi+ji2EiubS9Yhszd2e1CLeRMkln
+ojDjnflN32ZbWVHX/i6g3Dabq9JOD0FsOaOlriLMuofdA6jTUFE=
+-----END RSA PRIVATE KEY-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem
new file mode 100644
index 0000000..212b586
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFZDCCA0ygAwIBAgIJALhSfZ8i0rWTMA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV
+BAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAOBgNVBAMM
+B3Jvb3QgY2EwHhcNMTcxMTAyMDAzNzU4WhcNMzcxMDI4MDAzNzU4WjA/MQswCQYD
+VQQGEwJ1czEOMAwGA1UECAwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MRAwDgYDVQQD
+DAdyb290IGNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArHaQ3uyp
+wVaVPZDYvy/EJbnP7KbZNPBvKpQCDEqg9B2TPaC8WVjiqT6I88c+KcGuB8nADJdk
+o10iZC5AwbrXK4ELSCOvBpxYI5sjoFcv3lZ/oe4e650QO2L/ADmtwLaLYK6rZkwW
+Sd90yCGF7ZOTZTJZDwmWEl+ohi+2ow6sRMHKcSKUNfx9G5BB7TOzoqUxqH+moEds
+YpjVMEcKzQi2FmbRd+8Dlg2eGqA2V4faprGQwoYz8NqJZGa/KPpRvXE2VjSTDN6b
+rJ7mmui6eYN53mZEBRYogyoQHdFXhK02FgyoPEgR/wQlLLbQ+xxOcv02YsOljtza
+hl5LjeNUYPMjyhef0QpONp+5NcFhZf38DsSq5EWZLLxPScxwl0lBQkJTjo5ARuFl
+Mrv50RYrLwv4ImsiO2ftE7gAX4vNsgcixnCHd6rNzoGimf1+DSvDVJ9ujWo7HPN3
+7ONuoyjsU4mUJJpYXs8zHx5WSxaYiPJRcmG3LjcU5/A+Fs7bkqSrlEjJsG29xDrO
+vKR7hH+m6MwcIcXSh9wjjAIvHxAALdU9xaYE3hmVkoxew1mRBsYq34h2vpwGOY5r
+0njRQyGGZnVa8qkQd6P3U5fcvLOM8v9QImZqRDS2jAGZXYruo/RIgJpklVX7ZY0+
+CnGdz4YxgLyOBJCDu3aEgL1oON3mg2SsrVMCAwEAAaNjMGEwHQYDVR0OBBYEFOBO
+9R6yEY6KOE+aSClwD2BQtWXKMB8GA1UdIwQYMBaAFOBO9R6yEY6KOE+aSClwD2BQ
+tWXKMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+CwUAA4ICAQBElio7lLZ2eNt0tDPVSkChksc0IJ2/DpjYjqJzqxF/jZ2oToaAn2Er
+9iHl8ggTLB5WrTQxO1ev7qOwQsk9hrgvZ+EQCBTbyDyfNwVjgftH5jdQGdbyrnuJ
+yaks1mnd8is5ZZQmmQSd7GVOMTYi/7yH0xI4DHQ386dwnf5eKdoOI7yrVufEMxRx
+tB3Wv8KrX4z47zsIO28H/O0T26oUkrd4PEqFwDa5HQr6iG7QQgoGD/DPLgbBudlO
+kEos9fmXxiX60RLziKCE/DAxI3YWPvG3WhIWnVj22Oz6apz2pYWpOKwlaihNYrhq
+8xc02vIFwKh+t7D+wF4KHfduyMJ/wKVc5dzpNbTgkZePPKSB7QgbsMeRqbdPoXQF
+pMuzfj8VCWzpqBeHqE/adSCZhzeTrnyiYavF4T2vkSC5KJu+MHmbZ3nU9bcnnEy+
+24oEv9cEAiYNkvftbD+5ByEtkcBB2uT47sbiGrAeco+GxFGUVqi1IjObqrkIrPzV
+OjQhTZV6qgYCOuniJiGfoiMeHqdaDybpqo1bIrxSlvGRNcVoOsKt2/KP1DzW4ARZ
+hoRvayU2apHz/T5TAailqAW2MsrjGRaVHQTmeZKag8CKtAcjWzui0J2DnfXxUMn8
+R3ruFu3xJduOT1VghT9L9udvX9YhPCIKVL9+B5eFX9eMV6N7hUnVug==
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem
new file mode 100644
index 0000000..b6f4280
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIExzCCAq+gAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx
+DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt
+ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzgwMFoXDTI3MTAzMTAwMzgwMFowPjELMAkG
+A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEPMA0GA1UE
+AwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyxZFTLqv
+Gd9SpFAykyRyLQgHcR5hgD55mz+9fl1OfnMoAc7yTdPVLksDLmeFUlxcvCtLHysJ
+klIBX62c6LzbsVcfLg/DPJlQxFnkhJCRKen4fp7x9h62qqJkDFVXsiEFza9L1lsN
+4OwqU8i4RRgZ/xggM/s/wVBtynioeW9QADNmKZ1n6HVKkYwdOynbFSggYfFrL3HL
+54bC9roZUETin0G5wZ9QU+srgivT0a/KC3ourBYHXAI40iHuuOBf3syDVJ6xId/r
+3UO3qkiQ5q7pwglg+8Nx7Q3CFtGZY3ewxSSSDo6BOyweGYMsBaxMO3EyTqecyfXn
+3n4XPqwmDalWYQIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD
+AgWgMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2Vy
+dGlmaWNhdGUwHQYDVR0OBBYEFP2bodoNQ1tCNEOALPnygGMUfNI+MB8GA1UdIwQY
+MBaAFOWzLd7eBJwSNbzRqNsD7MQDCHg/MA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUE
+FjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBAHqUuCLt
+olOdR9p/g+KgGPnKuVgMn15Wc2VLCrbbl2P0fuCcNWmnBKqHHgQ1EJEpgnQ2N8m6
+tOGucX7IAzlZj36RP4lN3gZqFRSO/OiTOUYpE6Uv1hYRxeMzAYo5sBdCiiypjV9z
+H0Ew5NuWRf2/0nFWoywB9ktHcfD8lRFI3o8zUFXmE2JSUPQtKhW3tBkPPjYBlgzD
+RD8cq8dVK9P7i3tUENP+MNHJToNLFBqfA9De6bKnhCWHhZkfB0VeeSm4Ja9HkCg/
+DB+PAKMfbLCH5T8gCpEWxNlvj09r9mn37fNjtJPO/goAcNZNO2AURmb/ZQ4ggdry
+xb6lm832qplMUMWx//Ore0faEodlEc5d2kEtmcjj79gAypcLmm74q7CPt7xmniyd
+XvNT33S2tkh4dSirpCVwq0xyqOP3ZqTsTjudTveTBaTZNhTbCjDbaV7ga47TcH9/
++OZ3fQKjt2LAC6162wgEFZf10nUgaAXvSlI74gru93vEwWd8Pd3sWfGwuAFX3oKI
+JuwL2kxEuoZQmeRiVJu6KQb+Im7d5CIoWViDmfxcSDJfdtSePTqmDURIx87fw14Z
+XBWJP4PiK5PRmG/L0cGiDckmDKm/MuD13Z2I/NMl81GNY/q3WY2O7BmddPpAG5dr
+sc5hOqA9+jX08XbxKnfBPYllK5skYMkFH5tN
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem
new file mode 100644
index 0000000..4305e53
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCdXMx
+DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEQMA4GA1UEAwwHcm9vdCBj
+YTAeFw0xNzExMDIwMDM3NTlaFw0yNzEwMzEwMDM3NTlaMEcxCzAJBgNVBAYTAnVz
+MQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxGDAWBgNVBAMMD2ludGVy
+bWVkaWF0ZSBjYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKuM2iFz
+CCKmbs4uLj/8wjtxf+jFmkwzN4Pps4hqJ3NJqWB326LhzWyqM4WiTAJWE02wSJbS
+16RPfbjkVC77OI4+PUdwqxU9vNAP/95w0h6hBSFtkysuT5VVUt5jiY7wnUKgqTCi
+MYhYOl+HEP32O4cnxAazkUKKvtyrd4/PvejJ9zz+iYexRnaaGfOFR3co7jQ5QKar
+oK4UgJC3mVDZQEMBV0oljkpgVQMAVb4XQU7e+o25JOUkOoK6LdK/b/95khR0jTyD
+OBLqd4mCEzxNi+jZ0jLTLPk0c+DnGmRfoNUxnFb40R8QnEIEKwf+JKyl6p89oqOl
+pvIZFLZlUWIS4qL+993l1SCqPkWJOAdTg+s/Zh6DeAOhrUn9/wk0aQwZrK7wQQLJ
+4GGhxC/FfuUGsLqZszAVkP8jDEWnzhN2rw3V+C7v6lj4qHhUwqGHuYzCx2Hxl+B8
+UyBmZb9gXKVUtAvaZjaL2PDj1ZAxT0KVMlw1ZVrZu45OsHNQuBx/4uIAt6Rga8yt
+av1lsq+hFqwI4bU/oZC/oPMacOsB4qEkAA1101WjMc5bg6JOPWobwIqmUXQR1WJE
+j30e99HCpk1Cc2+9sUCzNu8KvU5kUY2K90zwqProvj5IfMuDetAVXsEjgW+ZqSho
+UMIpJ2M/hzAFl8Z5IRlG+YNfZNXl0FqJ5LzLAgMBAAGjZjBkMB0GA1UdDgQWBBTl
+sy3e3gScEjW80ajbA+zEAwh4PzAfBgNVHSMEGDAWgBTgTvUeshGOijhPmkgpcA9g
+ULVlyjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
+9w0BAQsFAAOCAgEAOS7DtliOUPcVosRfyx9dHcSUc3O+uY8uKjSHhdIrxDJm4lwP
+Q6lKg5j8CdMVb+sDQmyBkqQIA/6E13corP6R283jO6W8D4A8kjOiQWpXfjW6OcP3
+4rrDEWhCdeLFSNJIYOFkr2qWJpI/k0VpyDnmY0YluS5WbNjg6zTzGelzhFbV7/S1
+cteNAZD0vHD8NmbLVDJjjIY3E/iwzoUzBncLYbDwqyVS1g6utWdSy8LEJxzzqqWJ
+pBKlNYILAdh8efBgvotafaxsn2nfjmVmekPn3KcQZuE4Kzv1EQ2PrHpGeJKwh6up
+YBL2tav5cAki8bWoGPr2oGmWUf9L2tB57SdWdaY60ifzmQaeGiWPZBSmAz7PRSrz
+sR9SMIkBfYVRxXgWwlvr8JYnd2h/Ef5K9fI32nGfje+7/0kPEjNyjehri7sV4Sjt
+zzkDiFO+JklrRuLBPMFYOokq6Pcko32FKlE82pe8QkMDS8Sk//9PqCTK9ceB7y6E
+NYLNBW/X9SAw/TR5kdRinHHgHyEug7N4+DCU3lU1wl72ZjoiGE7V6c2AssFC2VcE
+E+WYxJT1ROJ1/5+U6BKdaIpTwMtRIFRomOEI66iOwOSEwqLIztkqxwpQ7THraWKm
+2W5e54u/efapIDcQFnP3E8r7TD0PdIeU6mD28o0+WiK3uL/OZpvyKaHPeFU=
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem
new file mode 100644
index 0000000..2850e42
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFFzCCAv+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx
+DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt
+ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzc1OVoXDTI3MTAzMTAwMzc1OVowTTELMAkG
+A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEKMAgGA1UE
+CwwBMTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAycY/7H1xk3/XHZRopULV7YsOzPIrMG25zoACbpDZxjS0I+2r1c7V
+wnvE8TszAkloLi+Skku5CYC7IvVEEEuKuIuV+8M48FJEwlCPge8LPiy18C+npCEd
+fgDzCV/O9DfJj6UaiCUayVE7UujXoke7AlKQEJcqvnD/CoTv2Y8jV1A6mPf6CTEI
+Sl1BMeFSmeFyvZll+xJ8Up1KfQZxKhtpP1s/rp6ZNlqSs1LM5+vcDHHZ6COTbq7t
+2vvcmGDTqeCLsqicBg1kJyMPRtqa0bNPj2bcVtcK0Ndfn6eL2hi+EoBy2nIXi6aG
+PpXf85b9bCLd5pZI80nHzFlhdvV+SxqrfwIDAQABo4IBBTCCAQEwCQYDVR0TBAIw
+ADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2Vu
+ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUoYjECaDz/ZELru/r
+jfTB1ShlVrAwaAYDVR0jBGEwX4AU5bMt3t4EnBI1vNGo2wPsxAMIeD+hQ6RBMD8x
+CzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAO
+BgNVBAMMB3Jvb3QgY2GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
+BgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAiiR2knMMym4O+3fD1KlYSnc2UR3v
+0FlRVAsr8wvTlVjJhx7DbRusBNJHWX66mUgK9x5OLnhyvyqlFVhR9AwlnxgfLWz9
+nnACeXzcjQZnKWFQWu8bJSC6Ene6rd1g2acK6SOjxavVbj7JVFnmlHF/naZUzvMl
+mJivYta4k7ob8UcX0I5TlJpzglU3UHyyJd5d9zhbF8wqbBq63zR2ovWci4pYCg+F
+jYcTGYVZJti3SHO+9/EqTC9x2KDNs3o0+rreJ3GuoonkInKZMQQZJQ6qILvkxlhT
+jyU5xlcaJ+0tSaiFK3eF0nXIpFYdZbIHYPCdLjh9AZ2dkFcAgSa/L8+tsVt60k8D
+HTO0Hz6dW5D2ckeebZvz5LACMN89gVzrc/rVkeg7QmpSbjkTSLC2KJS53hJzWcEI
+3KB73B9iY+ZYytcYBTYLizsAxd5g7j9z8UXrmVQ4mWbh2+xKiG+9aVOzCZ09AYi6
+WVK2aRcMQshgkkqPOloN9OeQNCE8Exf7N/zHsBhygorJXoD/PFgnV1VZm8xkOdiJ
+zTb3bpGdmL5+bzzS6wP8Q7pGZGYdlnB7JNO8oMYPPtzX8OOx92BTkPnqJnnRWTpR
+SjMEEdQe8K7iXxejQkjaAq5BlwaAOjCjPTqYomECcYjC0WaXsmrPcnZwSqpnHZZ2
+OiINYJub5cvBLNo=
+-----END CERTIFICATE-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem
new file mode 100644
index 0000000..a4c5fd4
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAyxZFTLqvGd9SpFAykyRyLQgHcR5hgD55mz+9fl1OfnMoAc7y
+TdPVLksDLmeFUlxcvCtLHysJklIBX62c6LzbsVcfLg/DPJlQxFnkhJCRKen4fp7x
+9h62qqJkDFVXsiEFza9L1lsN4OwqU8i4RRgZ/xggM/s/wVBtynioeW9QADNmKZ1n
+6HVKkYwdOynbFSggYfFrL3HL54bC9roZUETin0G5wZ9QU+srgivT0a/KC3ourBYH
+XAI40iHuuOBf3syDVJ6xId/r3UO3qkiQ5q7pwglg+8Nx7Q3CFtGZY3ewxSSSDo6B
+OyweGYMsBaxMO3EyTqecyfXn3n4XPqwmDalWYQIDAQABAoIBAFhIOR3OtVlw3BLz
+jdiq6jsrF1kUFNxTzDcxsSUiWIHde1G17Vzpre0uzJY6iBkyb1mZFFHbOpDxtwkp
+hmEh3/qqXbJ/RaatGxAP56e81G28+LnKTHJqDYwFhapa2wFjG4u7HSN0d4cEAq5j
+Pb9DZ+GdUjpmiON3HBL8+ne3bLZ42uI+DSVe8d3irbqg2rqsiANf0gdimMW4nuI4
+rVxf8HrY43PdQn/Vby+7qLRE3tmIlpbTqJGRtWRjdeBBI91APCrRljjXrKqT6Zpa
+E6Daz3YIQvXkIT0q+WkeN1VmQbtRnk7kRsPNp15kSwpHfmv6o/vkO9OUb1n71P2F
+wnB0WDECgYEA8iltnKxXnjqwZ/vzIWzcd94j+mdZg/K2/JCOqjwMvpSGCvx2zUmq
+Y2nxO2K85AVeOm/Yt87SMODB6AQ9CsrVGEUAzzacvCJDb8oUhaOL5gypnyvZiGCy
+snzXfgB+v/xuGekIjs2y7E8h3GG40j0aNQnUY1Fuc6iaeJG4BtjkuQUCgYEA1rE4
+DrTSsUh3hLYQusIHZR8Lecrrd4QUZSMKLkWjobiSTw3m4mglx1s2G4eZ3WuzOyFq
+Dp3/b3yfT8prdPBGA6shHNFf+1TO1q1/pIt15dc3sFwxMkuunai8N4QZJRqZLbYq
+FkNFkZ20hFHcH/NHDsAsRL/0tJdEmJ2ruP+Qdq0CgYBsdPGKwgVb8J0hdU4nIkJ7
+zRoABFmrJwGdjIDY7Zwnnw2JzhjHSL7vV3ubRVWkKmNReNZvPEoXahJuf7d3JfDa
+tczvAV6hRBc/8hnO4Li/h9xQVatP0T83gYJiBIbAJaaKJDyY+Lex7p8TvRCx2Hvs
+VUKyWL5HPrQwW9M3/dwyoQKBgQCNQoPA4Wcz8Jt7PZQaXaoh9eBGHab6t3P366s6
+MOXudZQG4f3FgINC/ZfHW1x43PFL+btfrMOyJkxoYqZ7hdB7f3DFFlpR80Y46GVw
+7bYAKbBhoPdZwYQ+BhT5bjhhOnQJKK/egBrZKevpmDb+6sIZSYaXIbovzMv8otmn
+WrhB7QKBgAdl+KYBQULCUBp8qCQH5sAQoWErpyuD2FNN6LGknpPqn4DdujvwEP0Z
+OSvbauLkI0Qc9/MezKPTeYXlFqdbpItwyySJsUkiI3HhVYlBgDkZ7xb6uHIH5E6I
+bKgIW5JEf5I7Eu1iurORkXxCCGMkiQmEs4X5kSXXRYgXfNgAD0FX
+-----END RSA PRIVATE KEY-----
diff --git a/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem
new file mode 100644
index 0000000..8cba174
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAycY/7H1xk3/XHZRopULV7YsOzPIrMG25zoACbpDZxjS0I+2r
+1c7VwnvE8TszAkloLi+Skku5CYC7IvVEEEuKuIuV+8M48FJEwlCPge8LPiy18C+n
+pCEdfgDzCV/O9DfJj6UaiCUayVE7UujXoke7AlKQEJcqvnD/CoTv2Y8jV1A6mPf6
+CTEISl1BMeFSmeFyvZll+xJ8Up1KfQZxKhtpP1s/rp6ZNlqSs1LM5+vcDHHZ6COT
+bq7t2vvcmGDTqeCLsqicBg1kJyMPRtqa0bNPj2bcVtcK0Ndfn6eL2hi+EoBy2nIX
+i6aGPpXf85b9bCLd5pZI80nHzFlhdvV+SxqrfwIDAQABAoIBAQC022161aoTEtjH
+m7n8v56vUCCRFVQfEYsljFohrtZ0sdLyDVwjxkSWEYiizXRYTWIDXALd/N+7o9aZ
+bAx5Kq0J45wpUYBc8PDO15T6W0DRlxPxWVXDaSddRQ6TTXxcLREPH2dbtx5+asBo
+/Woi/Haki0q0hDr8/p2sWSH/+SwtWpOezGVlrWrkMeIhlBwHZfdHVoZvSx65Uv7x
+WU07vsjrbXNDwf+2fmklAQrzhedCeh8loGyjtN3cfrTjrE1zqpEsHnlZcJxe6sRB
+1nOqpoUnpZXklDDIYC8EmeubmDJ0jnXOQCDDep3MzVcnZGyF5E/+szaa1NL70Ayj
+rbKk1Y3ZAoGBAPy/1ym7Cjl4OGHN2fdkR6iL68ebJozpr+eTSxDNLuBSi5IJxJyG
+1+B4+v1u0RwZ3DjrSQsO5DCbZ+DHU6O/DAJK2CxUED+M+G2kRyffailRQmNzjpRG
+75dIhSkSRYH8vdvEOnGpeQBZwBcCRH/2YUMlZeSfx9fHJhk1nyUxJeHjAoGBAMxe
+k+cBb0zYok+Ww1xTwOdq0PwKj0oDsEg8hOdWc8pH0SlOAB4BI5kmfd1JDMHfRc49
+7tpNqjsPrnlb9xd8l0281Lj2NoVSE5KX1JtsOsKecQsvHH5zRk4eJ3h/mNixpjfe
+79Zc/O40T4rWpQRqhat+WHveJC0/ON4AH4uT0BK1AoGBAPcTioCu6YXYsjVaCJPB
+IhPwBGOylfL2lxDoel9IVWTRDMOMbPkfEHXNjn6lECJKXW//Af6fZg7mPJwN/wN5
+xYGQLNbYrrGRW2HDUBP4YU1WtHGIC3+EAL+BEztdMzmpGuh1YTSvmSvwkMltXA1D
+iz0amArw72lOsz29n3+6FfBFAoGAIpRqMC8k9vq80/yth6TAQifnvo3G2v4uyLo8
+vqv5IaPvNy70hB8rN9G0gEnI99Dgjdoa3SNBB4dKvUwbTgUN0OB/meBHL13I5Af+
+uGGiu6V1eS/6gUbeAX/Gq/PjF99PQareKAZJ4cBGKTbSayHfBjp1nFflBSbqZ13b
++JEFJvUCgYBOs2J2XXamPbI7gu7B2TE9j/62v0SJyoHq2LHMmYUDRuPdPk3eKCt3
+283w+E8XUIFbctaxsbo8msNjjvV22D/Nci3d87aPe8bn1SVto3GnTuwnOpRq3E+3
+wAarqrhiZbGZSCcAkEOk7FlxAwYnCM6paqMxDEMCJ4qChMM42E9ZyQ==
+-----END RSA PRIVATE KEY-----
diff --git a/src/python/grpcio_tests/tests/unit/resources.py b/src/python/grpcio_tests/tests/unit/resources.py
index 823d230..11ef9e8 100644
--- a/src/python/grpcio_tests/tests/unit/resources.py
+++ b/src/python/grpcio_tests/tests/unit/resources.py
@@ -11,7 +11,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-"""Constants and functions for data used in interoperability testing."""
+"""Constants and functions for data used in testing."""
 
 import os
 
@@ -34,3 +34,81 @@
 def certificate_chain():
     return pkg_resources.resource_string(__name__,
                                          _CERTIFICATE_CHAIN_RESOURCE_PATH)
+
+
+def cert_hier_1_root_ca_cert():
+    return pkg_resources.resource_string(
+        __name__, 'credentials/certificate_hierarchy_1/certs/ca.cert.pem')
+
+
+def cert_hier_1_intermediate_ca_cert():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem'
+    )
+
+
+def cert_hier_1_client_1_key():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_1/intermediate/private/client.key.pem'
+    )
+
+
+def cert_hier_1_client_1_cert():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem')
+
+
+def cert_hier_1_server_1_key():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem'
+    )
+
+
+def cert_hier_1_server_1_cert():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem'
+    )
+
+
+def cert_hier_2_root_ca_cert():
+    return pkg_resources.resource_string(
+        __name__, 'credentials/certificate_hierarchy_2/certs/ca.cert.pem')
+
+
+def cert_hier_2_intermediate_ca_cert():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem'
+    )
+
+
+def cert_hier_2_client_1_key():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_2/intermediate/private/client.key.pem'
+    )
+
+
+def cert_hier_2_client_1_cert():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem')
+
+
+def cert_hier_2_server_1_key():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem'
+    )
+
+
+def cert_hier_2_server_1_cert():
+    return pkg_resources.resource_string(
+        __name__,
+        'credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem'
+    )
diff --git a/test/core/end2end/fixtures/http_proxy_fixture.cc b/test/core/end2end/fixtures/http_proxy_fixture.cc
index 0bdd15c..ac0c953 100644
--- a/test/core/end2end/fixtures/http_proxy_fixture.cc
+++ b/test/core/end2end/fixtures/http_proxy_fixture.cc
@@ -88,9 +88,11 @@
 
   grpc_slice_buffer client_read_buffer;
   grpc_slice_buffer client_deferred_write_buffer;
+  bool client_is_writing;
   grpc_slice_buffer client_write_buffer;
   grpc_slice_buffer server_read_buffer;
   grpc_slice_buffer server_deferred_write_buffer;
+  bool server_is_writing;
   grpc_slice_buffer server_write_buffer;
 
   grpc_http_parser http_parser;
@@ -148,6 +150,7 @@
 static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
   proxy_connection* conn = (proxy_connection*)arg;
+  conn->client_is_writing = false;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, true /* is_client */,
                             "HTTP proxy client write", error);
@@ -160,6 +163,7 @@
   if (conn->client_deferred_write_buffer.length > 0) {
     grpc_slice_buffer_move_into(&conn->client_deferred_write_buffer,
                                 &conn->client_write_buffer);
+    conn->client_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->client_endpoint,
                         &conn->client_write_buffer,
                         &conn->on_client_write_done);
@@ -173,6 +177,7 @@
 static void on_server_write_done(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
   proxy_connection* conn = (proxy_connection*)arg;
+  conn->server_is_writing = false;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, false /* is_client */,
                             "HTTP proxy server write", error);
@@ -185,6 +190,7 @@
   if (conn->server_deferred_write_buffer.length > 0) {
     grpc_slice_buffer_move_into(&conn->server_deferred_write_buffer,
                                 &conn->server_write_buffer);
+    conn->server_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->server_endpoint,
                         &conn->server_write_buffer,
                         &conn->on_server_write_done);
@@ -210,13 +216,14 @@
   // the current write is finished.
   //
   // Otherwise, move the read data into the write buffer and write it.
-  if (conn->server_write_buffer.length > 0) {
+  if (conn->server_is_writing) {
     grpc_slice_buffer_move_into(&conn->client_read_buffer,
                                 &conn->server_deferred_write_buffer);
   } else {
     grpc_slice_buffer_move_into(&conn->client_read_buffer,
                                 &conn->server_write_buffer);
     proxy_connection_ref(conn, "client_read");
+    conn->server_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->server_endpoint,
                         &conn->server_write_buffer,
                         &conn->on_server_write_done);
@@ -242,13 +249,14 @@
   // the current write is finished.
   //
   // Otherwise, move the read data into the write buffer and write it.
-  if (conn->client_write_buffer.length > 0) {
+  if (conn->client_is_writing) {
     grpc_slice_buffer_move_into(&conn->server_read_buffer,
                                 &conn->client_deferred_write_buffer);
   } else {
     grpc_slice_buffer_move_into(&conn->server_read_buffer,
                                 &conn->client_write_buffer);
     proxy_connection_ref(conn, "server_read");
+    conn->client_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->client_endpoint,
                         &conn->client_write_buffer,
                         &conn->on_client_write_done);
@@ -262,6 +270,7 @@
 static void on_write_response_done(grpc_exec_ctx* exec_ctx, void* arg,
                                    grpc_error* error) {
   proxy_connection* conn = (proxy_connection*)arg;
+  conn->client_is_writing = false;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, true /* is_client */,
                             "HTTP proxy write response", error);
@@ -302,6 +311,7 @@
   grpc_slice slice =
       grpc_slice_from_copied_string("HTTP/1.0 200 connected\r\n\r\n");
   grpc_slice_buffer_add(&conn->client_write_buffer, slice);
+  conn->client_is_writing = true;
   grpc_endpoint_write(exec_ctx, conn->client_endpoint,
                       &conn->client_write_buffer,
                       &conn->on_write_response_done);
@@ -450,9 +460,11 @@
                     grpc_combiner_scheduler(conn->proxy->combiner));
   grpc_slice_buffer_init(&conn->client_read_buffer);
   grpc_slice_buffer_init(&conn->client_deferred_write_buffer);
+  conn->client_is_writing = false;
   grpc_slice_buffer_init(&conn->client_write_buffer);
   grpc_slice_buffer_init(&conn->server_read_buffer);
   grpc_slice_buffer_init(&conn->server_deferred_write_buffer);
+  conn->server_is_writing = false;
   grpc_slice_buffer_init(&conn->server_write_buffer);
   grpc_http_parser_init(&conn->http_parser, GRPC_HTTP_REQUEST,
                         &conn->http_request);
diff --git a/test/core/iomgr/timer_list_test.cc b/test/core/iomgr/timer_list_test.cc
index dc965ef..d74ea4f 100644
--- a/test/core/iomgr/timer_list_test.cc
+++ b/test/core/iomgr/timer_list_test.cc
@@ -28,11 +28,12 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
 #include "test/core/util/test_config.h"
+#include "test/core/util/tracer_util.h"
 
 #define MAX_CB 30
 
-extern "C" grpc_tracer_flag grpc_timer_trace;
-extern "C" grpc_tracer_flag grpc_timer_check_trace;
+extern grpc_core::TraceFlag grpc_timer_trace;
+extern grpc_core::TraceFlag grpc_timer_check_trace;
 
 static int cb_called[MAX_CB][2];
 
@@ -48,8 +49,8 @@
   gpr_log(GPR_INFO, "add_test");
 
   grpc_timer_list_init(&exec_ctx);
-  grpc_timer_trace.value = 1;
-  grpc_timer_check_trace.value = 1;
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_trace);
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_check_trace);
   memset(cb_called, 0, sizeof(cb_called));
 
   grpc_millis start = grpc_exec_ctx_now(&exec_ctx);
@@ -117,8 +118,8 @@
   exec_ctx.now_is_valid = true;
   exec_ctx.now = 0;
   grpc_timer_list_init(&exec_ctx);
-  grpc_timer_trace.value = 1;
-  grpc_timer_check_trace.value = 1;
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_trace);
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_check_trace);
   memset(cb_called, 0, sizeof(cb_called));
 
   grpc_timer_init(
diff --git a/test/core/transport/connectivity_state_test.cc b/test/core/transport/connectivity_state_test.cc
index 7a22ec8..11046e1 100644
--- a/test/core/transport/connectivity_state_test.cc
+++ b/test/core/transport/connectivity_state_test.cc
@@ -23,6 +23,7 @@
 #include <grpc/support/log.h>
 
 #include "test/core/util/test_config.h"
+#include "test/core/util/tracer_util.h"
 
 #define THE_ARG ((void*)(size_t)0xcafebabe)
 
@@ -136,7 +137,7 @@
 
 int main(int argc, char** argv) {
   grpc_test_init(argc, argv);
-  grpc_connectivity_state_trace.value = 1;
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_connectivity_state_trace);
   test_connectivity_state_name();
   test_check();
   test_subscribe_then_unsubscribe();
diff --git a/test/core/util/BUILD b/test/core/util/BUILD
index 2437923..6443553 100644
--- a/test/core/util/BUILD
+++ b/test/core/util/BUILD
@@ -57,6 +57,7 @@
         "reconnect_server.cc",
         "slice_splitter.cc",
         "test_tcp_server.cc",
+        "tracer_util.cc",
         "trickle_endpoint.cc",
     ],
     hdrs = [
@@ -69,6 +70,7 @@
         "reconnect_server.h",
         "slice_splitter.h",
         "test_tcp_server.h",
+        "tracer_util.h",
         "trickle_endpoint.h",
     ],
     language = "C++",
diff --git a/test/core/util/tracer_util.cc b/test/core/util/tracer_util.cc
new file mode 100644
index 0000000..34a132d
--- /dev/null
+++ b/test/core/util/tracer_util.cc
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "test/core/util/test_config.h"
+
+#include "src/core/lib/debug/trace.h"
+
+namespace grpc_core {
+namespace testing {
+
+void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag) {
+  flag->set_enabled(1);
+}
+
+}  // namespace testing
+}  // namespace grpc_core
diff --git a/test/core/util/tracer_util.h b/test/core/util/tracer_util.h
new file mode 100644
index 0000000..0b432ff
--- /dev/null
+++ b/test/core/util/tracer_util.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_TEST_CORE_UTIL_TRACER_UTIL_H
+#define GRPC_TEST_CORE_UTIL_TRACER_UTIL_H
+
+namespace grpc_core {
+class TraceFlag;
+
+namespace testing {
+// enables the TraceFlag passed to it. Used for testing purposes.
+void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag);
+
+}  // namespace testing
+}  // namespace grpc_core
+
+#endif /* GRPC_TEST_CORE_UTIL_TRACER_UTIL_H */
diff --git a/test/cpp/naming/README.md b/test/cpp/naming/README.md
index e331846..e0dd208 100644
--- a/test/cpp/naming/README.md
+++ b/test/cpp/naming/README.md
@@ -31,7 +31,7 @@
 3. From the repo root, run:
 
 ```
-$ test/cpp/naming/create_dns_private_zone.sh
+$ test/cpp/naming/create_private_dns_zone.sh
 $ test/cpp/naming/private_dns_zone_init.sh
 ```
 
diff --git a/test/cpp/naming/create_private_dns_zone.sh b/test/cpp/naming/create_private_dns_zone.sh
index 3d7520b..55a4cfe 100755
--- a/test/cpp/naming/create_private_dns_zone.sh
+++ b/test/cpp/naming/create_private_dns_zone.sh
@@ -20,8 +20,8 @@
 cd $(dirname $0)/../../..
 
 gcloud alpha dns managed-zones create \
-  resolver-tests-version-1-grpctestingexp-zone-id \
-  --dns-name=resolver-tests-version-1.grpctestingexp. \
+  resolver-tests-version-4-grpctestingexp-zone-id \
+  --dns-name=resolver-tests-version-4.grpctestingexp. \
   --description="GCE-DNS-private-zone-for-GRPC-testing" \
   --visibility=private \
   --networks=default
diff --git a/test/cpp/naming/private_dns_zone_init.sh b/test/cpp/naming/private_dns_zone_init.sh
index 4eaf750..8fa5a8a 100755
--- a/test/cpp/naming/private_dns_zone_init.sh
+++ b/test/cpp/naming/private_dns_zone_init.sh
@@ -19,197 +19,197 @@
 
 cd $(dirname $0)/../../..
 
-gcloud dns record-sets transaction start -z=resolver-tests-version-1-grpctestingexp-zone-id
+gcloud dns record-sets transaction start -z=resolver-tests-version-4-grpctestingexp-zone-id
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=_grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp. \
   --type=SRV \
   --ttl=2100 \
-  "0 0 1234 ipv4-single-target.resolver-tests-version-1.grpctestingexp."
+  "0 0 1234 ipv4-single-target.resolver-tests-version-4.grpctestingexp."
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-single-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-single-target.resolver-tests-version-4.grpctestingexp. \
   --type=A \
   --ttl=2100 \
   "1.2.3.4"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=_grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-4.grpctestingexp. \
   --type=SRV \
   --ttl=2100 \
-  "0 0 1234 ipv4-multi-target.resolver-tests-version-1.grpctestingexp."
+  "0 0 1234 ipv4-multi-target.resolver-tests-version-4.grpctestingexp."
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-multi-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-multi-target.resolver-tests-version-4.grpctestingexp. \
   --type=A \
   --ttl=2100 \
   "1.2.3.5" "1.2.3.6" "1.2.3.7"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=_grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-4.grpctestingexp. \
   --type=SRV \
   --ttl=2100 \
-  "0 0 1234 ipv6-single-target.resolver-tests-version-1.grpctestingexp."
+  "0 0 1234 ipv6-single-target.resolver-tests-version-4.grpctestingexp."
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv6-single-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv6-single-target.resolver-tests-version-4.grpctestingexp. \
   --type=AAAA \
   --ttl=2100 \
   "2607:f8b0:400a:801::1001"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=_grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-4.grpctestingexp. \
   --type=SRV \
   --ttl=2100 \
-  "0 0 1234 ipv6-multi-target.resolver-tests-version-1.grpctestingexp."
+  "0 0 1234 ipv6-multi-target.resolver-tests-version-4.grpctestingexp."
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv6-multi-target.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv6-multi-target.resolver-tests-version-4.grpctestingexp. \
   --type=AAAA \
   --ttl=2100 \
   "2607:f8b0:400a:801::1002" "2607:f8b0:400a:801::1003" "2607:f8b0:400a:801::1004"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpc_config.srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. \
   --type=TXT \
   --ttl=2100 \
   '"grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"SimpleService\",\"waitForReady\":true}]}]}}]"'
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. \
-  --type=A \
-  --ttl=2100 \
-  "1.2.3.4"
-
-gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=_grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. \
   --type=SRV \
   --ttl=2100 \
-  "0 0 1234 ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp."
+  "0 0 1234 ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp."
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. \
   --type=A \
   --ttl=2100 \
   "1.2.3.4"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpc_config.ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp. \
   --type=TXT \
   --ttl=2100 \
   '"grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"NoSrvSimpleService\",\"waitForReady\":true}]}]}}]"'
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. \
-  --type=A \
-  --ttl=2100 \
-  "1.2.3.4"
-
-gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpc_config.ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp. \
   --type=TXT \
   --ttl=2100 \
   '"grpc_config=[{\"clientLanguage\":[\"python\"],\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"PythonService\",\"waitForReady\":true}]}]}}]"'
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp. \
   --type=A \
   --ttl=2100 \
   "1.2.3.4"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpc_config.ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp. \
   --type=TXT \
   --ttl=2100 \
   '"grpc_config=[{\"percentage\":0,\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"CppService\",\"waitForReady\":true}]}]}}]"'
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. \
-  --type=A \
-  --ttl=2100 \
-  "1.2.3.4"
-
-gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpc_config.ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp. \
   --type=TXT \
   --ttl=2100 \
   '"grpc_config=[{\"clientLanguage\":[\"go\"],\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"GoService\",\"waitForReady\":true}]}]}},{\"clientLanguage\":[\"c++\"],\"serviceConfig\":{" "\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"CppService\",\"waitForReady\":true}]}]}}]"'
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp. \
   --type=A \
   --ttl=2100 \
   "1.2.3.4"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpc_config.ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp. \
   --type=TXT \
   --ttl=2100 \
   '"grpc_config=[{\"percentage\":0,\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"NeverPickedService\",\"waitForReady\":true}]}]}},{\"percentage\":100,\"serviceConfig\":{\"loadBalanc" "ingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"AlwaysPickedService\",\"waitForReady\":true}]}]}}]"'
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=_grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. \
   --type=SRV \
   --ttl=2100 \
-  "0 0 1234 balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp."
+  "0 0 1234 balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp."
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. \
   --type=A \
   --ttl=2100 \
   "1.2.3.4"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. \
   --type=A \
   --ttl=2100 \
   "1.2.3.4"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=_grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. \
   --type=SRV \
   --ttl=2100 \
-  "0 0 1234 balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp."
+  "0 0 1234 balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp."
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. \
   --type=AAAA \
   --ttl=2100 \
   "2607:f8b0:400a:801::1002"
 
 gcloud dns record-sets transaction add \
-  -z=resolver-tests-version-1-grpctestingexp-zone-id \
-  --name=srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  -z=resolver-tests-version-4-grpctestingexp-zone-id \
+  --name=srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. \
   --type=AAAA \
   --ttl=2100 \
   "2607:f8b0:400a:801::1002"
 
-gcloud dns record-sets transaction describe -z=resolver-tests-version-1-grpctestingexp-zone-id
-gcloud dns record-sets transaction execute -z=resolver-tests-version-1-grpctestingexp-zone-id
-gcloud dns record-sets list -z=resolver-tests-version-1-grpctestingexp-zone-id
+gcloud dns record-sets transaction describe -z=resolver-tests-version-4-grpctestingexp-zone-id
+gcloud dns record-sets transaction execute -z=resolver-tests-version-4-grpctestingexp-zone-id
+gcloud dns record-sets list -z=resolver-tests-version-4-grpctestingexp-zone-id
diff --git a/test/cpp/naming/resolver_component_tests_runner.sh b/test/cpp/naming/resolver_component_tests_runner.sh
index 407db5e..11a45d7 100755
--- a/test/cpp/naming/resolver_component_tests_runner.sh
+++ b/test/cpp/naming/resolver_component_tests_runner.sh
@@ -73,7 +73,7 @@
 # in the resolver.
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -81,7 +81,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-multi-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.5:1234,True;1.2.3.6:1234,True;1.2.3.7:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -89,7 +89,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv6-single-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1001]:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -97,7 +97,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv6-multi-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1003]:1234,True;[2607:f8b0:400a:801::1004]:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -105,7 +105,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -113,7 +113,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -121,7 +121,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -129,7 +129,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -137,7 +137,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -145,7 +145,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -153,7 +153,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True;1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -161,7 +161,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1002]:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -169,7 +169,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-config-causing-fallback-to-tcp.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-config-causing-fallback-to-tcp.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}' \
   --expected_lb_policy='' \
diff --git a/test/cpp/naming/resolver_gce_integration_tests_runner.sh b/test/cpp/naming/resolver_gce_integration_tests_runner.sh
index b20d18d..091f9ef 100755
--- a/test/cpp/naming/resolver_gce_integration_tests_runner.sh
+++ b/test/cpp/naming/resolver_gce_integration_tests_runner.sh
@@ -34,191 +34,191 @@
 EXIT_CODE=0
 
 ONE_FAILED=0
-dig SRV _grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig SRV _grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-single-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-single-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig SRV _grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig SRV _grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-multi-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-multi-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig SRV _grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig SRV _grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig AAAA ipv6-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig AAAA ipv6-single-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig AAAA ipv6-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig AAAA ipv6-single-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig SRV _grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig SRV _grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig AAAA ipv6-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig AAAA ipv6-multi-target.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig AAAA ipv6-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig AAAA ipv6-multi-target.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig TXT srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig TXT _grpc_config.srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig TXT srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig TXT _grpc_config.srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig SRV _grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig SRV _grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig TXT ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig TXT _grpc_config.ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig TXT ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig TXT _grpc_config.ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig TXT _grpc_config.ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig TXT _grpc_config.ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig TXT ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig TXT ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig TXT ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig TXT _grpc_config.ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig TXT ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig TXT _grpc_config.ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig TXT _grpc_config.ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig TXT _grpc_config.ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig TXT ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig TXT ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig TXT ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig TXT _grpc_config.ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig TXT ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig TXT _grpc_config.ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig SRV _grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig SRV _grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig A srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig A srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig A srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig A srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig SRV _grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig SRV _grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig AAAA balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig AAAA balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig AAAA balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig AAAA balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
 ONE_FAILED=0
-dig AAAA srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+dig AAAA srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Sanity check: dig AAAA srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Sanity check: dig AAAA srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   exit 1
 fi
 
@@ -226,133 +226,133 @@
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: srv-ipv4-single-target.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-multi-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.5:1234,True;1.2.3.6:1234,True;1.2.3.7:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: srv-ipv4-multi-target.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv6-single-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1001]:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: srv-ipv6-single-target.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv6-multi-target.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1003]:1234,True;[2607:f8b0:400a:801::1004]:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: srv-ipv6-multi-target.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: srv-ipv4-simple-service-config.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: ipv4-no-srv-simple-service-config.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: ipv4-no-config-for-cpp.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: ipv4-cpp-config-has-zero-percentage.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: ipv4-second-language-is-cpp.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: ipv4-config-with-percentages.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True;1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
 ONE_FAILED=0
 bins/$CONFIG/resolver_component_test \
-  --target_name='srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
+  --target_name='srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1002]:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' || ONE_FAILED=1
 if [[ "$ONE_FAILED" != 0 ]]; then
-  echo "Test based on target record: srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  echo "Test based on target record: srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-4.grpctestingexp. FAILED"
   EXIT_CODE=1
 fi
 
diff --git a/test/cpp/naming/resolver_test_record_groups.yaml b/test/cpp/naming/resolver_test_record_groups.yaml
index 2b32043..6c4f89d 100644
--- a/test/cpp/naming/resolver_test_record_groups.yaml
+++ b/test/cpp/naming/resolver_test_record_groups.yaml
@@ -1,4 +1,4 @@
-resolver_tests_common_zone_name: resolver-tests-version-1.grpctestingexp.
+resolver_tests_common_zone_name: resolver-tests-version-4.grpctestingexp.
 resolver_component_tests:
 - expected_addrs:
   - {address: '1.2.3.4:1234', is_balancer: true}
@@ -58,7 +58,7 @@
     - {TTL: '2100', data: 0 0 1234 ipv4-simple-service-config, type: SRV}
     ipv4-simple-service-config:
     - {TTL: '2100', data: 1.2.3.4, type: A}
-    srv-ipv4-simple-service-config:
+    _grpc_config.srv-ipv4-simple-service-config:
     - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}}]',
       type: TXT}
 - expected_addrs:
@@ -69,6 +69,7 @@
   records:
     ipv4-no-srv-simple-service-config:
     - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-no-srv-simple-service-config:
     - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}}]',
       type: TXT}
 - expected_addrs:
@@ -79,6 +80,7 @@
   records:
     ipv4-no-config-for-cpp:
     - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-no-config-for-cpp:
     - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["python"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"PythonService","waitForReady":true}]}]}}]',
       type: TXT}
 - expected_addrs:
@@ -89,6 +91,7 @@
   records:
     ipv4-cpp-config-has-zero-percentage:
     - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-cpp-config-has-zero-percentage:
     - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}}]',
       type: TXT}
 - expected_addrs:
@@ -99,6 +102,7 @@
   records:
     ipv4-second-language-is-cpp:
     - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-second-language-is-cpp:
     - {TTL: '2100', data: 'grpc_config=[{"clientLanguage":["go"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"GoService","waitForReady":true}]}]}},{"clientLanguage":["c++"],"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}}]',
       type: TXT}
 - expected_addrs:
@@ -109,6 +113,7 @@
   records:
     ipv4-config-with-percentages:
     - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-config-with-percentages:
     - {TTL: '2100', data: 'grpc_config=[{"percentage":0,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NeverPickedService","waitForReady":true}]}]}},{"percentage":100,"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}}]',
       type: TXT}
 - expected_addrs:
@@ -145,5 +150,6 @@
   records:
     ipv4-config-causing-fallback-to-tcp:
     - {TTL: '2100', data: 1.2.3.4, type: A}
+    _grpc_config.ipv4-config-causing-fallback-to-tcp:
     - {TTL: '2100', data: 'grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}}]',
       type: TXT}
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index f7cdfc2..0788821 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -236,6 +236,22 @@
     this->EndThreads();  // this needed for resolution
   }
 
+  ClientRpcContext* ProcessTag(size_t thread_idx, void* tag) {
+    ClientRpcContext* ctx = ClientRpcContext::detag(tag);
+    if (shutdown_state_[thread_idx]->shutdown) {
+      ctx->TryCancel();
+      delete ctx;
+      bool ok;
+      while (cli_cqs_[cq_[thread_idx]]->Next(&tag, &ok)) {
+        ctx = ClientRpcContext::detag(tag);
+        ctx->TryCancel();
+        delete ctx;
+      }
+      return nullptr;
+    }
+    return ctx;
+  }
+
   void ThreadFunc(size_t thread_idx, Client::Thread* t) override final {
     void* got_tag;
     bool ok;
@@ -245,9 +261,13 @@
     if (!cli_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
       return;
     }
-    ClientRpcContext* ctx = ClientRpcContext::detag(got_tag);
     std::mutex* shutdown_mu = &shutdown_state_[thread_idx]->mutex;
     shutdown_mu->lock();
+    ClientRpcContext* ctx = ProcessTag(thread_idx, got_tag);
+    if (ctx == nullptr) {
+      shutdown_mu->unlock();
+      return;
+    }
     while (cli_cqs_[cq_[thread_idx]]->DoThenAsyncNext(
         [&, ctx, ok, entry_ptr, shutdown_mu]() {
           if (!ctx->RunNextState(ok, entry_ptr)) {
@@ -260,19 +280,9 @@
         },
         &got_tag, &ok, gpr_inf_future(GPR_CLOCK_REALTIME))) {
       t->UpdateHistogram(entry_ptr);
-      // Got a regular event, so process it
-      ctx = ClientRpcContext::detag(got_tag);
-      // Proceed while holding a lock to make sure that
-      // this thread isn't supposed to shut down
       shutdown_mu->lock();
-      if (shutdown_state_[thread_idx]->shutdown) {
-        ctx->TryCancel();
-        delete ctx;
-        while (cli_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
-          ctx = ClientRpcContext::detag(got_tag);
-          ctx->TryCancel();
-          delete ctx;
-        }
+      ctx = ProcessTag(thread_idx, got_tag);
+      if (ctx == nullptr) {
         shutdown_mu->unlock();
         return;
       }
diff --git a/tools/distrib/python/check_grpcio_tools.py b/tools/distrib/python/check_grpcio_tools.py
index c5afa71..b56ccae 100755
--- a/tools/distrib/python/check_grpcio_tools.py
+++ b/tools/distrib/python/check_grpcio_tools.py
@@ -14,17 +14,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import cStringIO
-
-import make_grpcio_tools as make
+import make_grpcio_tools as _make
 
 OUT_OF_DATE_MESSAGE = """file {} is out of date
 
 Have you called tools/distrib/python/make_grpcio_tools.py since upgrading protobuf?"""
 
-check_protoc_lib_deps_content = make.get_deps()
+submodule_commit_hash = _make.protobuf_submodule_commit_hash()
 
-with open(make.GRPC_PYTHON_PROTOC_LIB_DEPS, 'r') as protoc_lib_deps_file:
-  if protoc_lib_deps_file.read() != check_protoc_lib_deps_content:
-    print(OUT_OF_DATE_MESSAGE.format(make.GRPC_PYTHON_PROTOC_LIB_DEPS))
-    raise SystemExit(1)
+with open(_make.GRPC_PYTHON_PROTOC_LIB_DEPS, 'r') as _protoc_lib_deps_file:
+  content = _protoc_lib_deps_file.read().splitlines()
+
+testString = (_make.COMMIT_HASH_PREFIX +
+              submodule_commit_hash +
+              _make.COMMIT_HASH_SUFFIX)
+
+if testString not in content:
+  print(OUT_OF_DATE_MESSAGE.format(_make.GRPC_PYTHON_PROTOC_LIB_DEPS))
+  raise SystemExit(1)
diff --git a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
index 18470d5..2a42a51 100644
--- a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
+++ b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
@@ -1,5 +1,5 @@
 
-# Copyright 2016 gRPC authors.
+# Copyright 2017 gRPC authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -19,3 +19,5 @@
 
 CC_INCLUDE='third_party/protobuf/src'
 PROTO_INCLUDE='third_party/protobuf/src'
+
+PROTOBUF_SUBMODULE_VERSION="80a37e0782d2d702d52234b62dd4b9ec74fd2c95"
diff --git a/tools/distrib/python/make_grpcio_tools.py b/tools/distrib/python/make_grpcio_tools.py
index 5bc07ff..c865f0b 100755
--- a/tools/distrib/python/make_grpcio_tools.py
+++ b/tools/distrib/python/make_grpcio_tools.py
@@ -28,7 +28,7 @@
 import uuid
 
 DEPS_FILE_CONTENT="""
-# Copyright 2016 gRPC authors.
+# Copyright 2017 gRPC authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -48,8 +48,13 @@
 
 CC_INCLUDE={cc_include}
 PROTO_INCLUDE={proto_include}
+
+{commit_hash}
 """
 
+COMMIT_HASH_PREFIX = 'PROTOBUF_SUBMODULE_VERSION="'
+COMMIT_HASH_SUFFIX = '"'
+
 # Bazel query result prefix for expected source files in protobuf.
 PROTOBUF_CC_PREFIX = '//:src/'
 PROTOBUF_PROTO_PREFIX = '//:src/'
@@ -63,6 +68,7 @@
 
 GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT = os.path.join('third_party', 'protobuf', 'src')
 GRPC_PROTOBUF = os.path.join(GRPC_ROOT, GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT)
+GRPC_PROTOBUF_SUBMODULE_ROOT = os.path.join(GRPC_ROOT, 'third_party', 'protobuf')
 GRPC_PROTOC_PLUGINS = os.path.join(GRPC_ROOT, 'src', 'compiler')
 GRPC_PYTHON_PROTOBUF = os.path.join(GRPC_PYTHON_ROOT, 'third_party', 'protobuf',
                                     'src')
@@ -78,6 +84,14 @@
 BAZEL_DEPS_PROTOC_LIB_QUERY = '//:protoc_lib'
 BAZEL_DEPS_COMMON_PROTOS_QUERY = '//:well_known_protos'
 
+def protobuf_submodule_commit_hash():
+  """Gets the commit hash for the HEAD of the protobuf submodule currently
+     checked out."""
+  cwd = os.getcwd()
+  os.chdir(GRPC_PROTOBUF_SUBMODULE_ROOT)
+  output = subprocess.check_output(['git', 'rev-parse', 'HEAD'])
+  os.chdir(cwd)
+  return output.splitlines()[0].strip()
 
 def bazel_query(query):
   output = subprocess.check_output([BAZEL_DEPS, query])
@@ -94,11 +108,13 @@
   proto_files = [
       name[len(PROTOBUF_PROTO_PREFIX):] for name in proto_files_output
       if name.endswith('.proto') and name.startswith(PROTOBUF_PROTO_PREFIX)]
+  commit_hash = protobuf_submodule_commit_hash()
   deps_file_content = DEPS_FILE_CONTENT.format(
       cc_files=cc_files,
       proto_files=proto_files,
       cc_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT),
-      proto_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT))
+      proto_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT),
+      commit_hash=COMMIT_HASH_PREFIX + commit_hash + COMMIT_HASH_SUFFIX)
   return deps_file_content
 
 def long_path(path):
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index b9844f8..b8434bb 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -1073,7 +1073,6 @@
 src/core/lib/iomgr/block_annotate.h \
 src/core/lib/iomgr/call_combiner.cc \
 src/core/lib/iomgr/call_combiner.h \
-src/core/lib/iomgr/closure.cc \
 src/core/lib/iomgr/closure.h \
 src/core/lib/iomgr/combiner.cc \
 src/core/lib/iomgr/combiner.h \
diff --git a/tools/interop_matrix/client_matrix.py b/tools/interop_matrix/client_matrix.py
index 35e4554..c8dbb3a 100644
--- a/tools/interop_matrix/client_matrix.py
+++ b/tools/interop_matrix/client_matrix.py
@@ -27,7 +27,7 @@
     'cxx': ['cxx'],             # This is actually debian8.
     'go': ['go1.7', 'go1.8'],
     'java': ['java_oracle8'],
-    #'python': ['python'],  # All python versions fail the tests due to timeout.
+    'python': ['python'],
     'node': ['node'],
     'ruby': ['ruby'],
     'php': ['php', 'php7'],
@@ -64,14 +64,14 @@
         'v1.6.1',
         'v1.7.0',
     ],
-    #'python': [
-        #'v1.0.x',  #Fail to run the test. #13230.
-    #    'v1.1.4',
-    #    'v1.2.5',
-    #    'v1.3.9',
-    #    'v1.4.2',
-    #    'v1.6.6',
-    #],
+    'python': [
+        'v1.0.x',
+        'v1.1.4',
+        'v1.2.5',
+        'v1.3.9',
+        'v1.4.2',
+        'v1.6.6',
+    ],
     'node': [
         'v1.0.1',
         'v1.1.4',
diff --git a/tools/interop_matrix/testcases/python__master b/tools/interop_matrix/testcases/python__master
new file mode 100755
index 0000000..4a63eae
--- /dev/null
+++ b/tools/interop_matrix/testcases/python__master
@@ -0,0 +1,20 @@
+#!/bin/bash
+echo "Testing ${docker_image:=grpc_interop_python:797ca293-94e8-48d4-92e9-a4d52fcfcca9}"
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response\""
+docker run -i --rm=true -e PYTHONPATH=/var/local/git/grpc/src/python/gens -e LD_LIBRARY_PATH=/var/local/git/grpc/libs/opt -w /var/local/git/grpc --net=host $docker_image bash -c "py27/bin/python src/python/grpcio_tests/setup.py run_interop --client --args=\"--server_host=216.239.32.254 --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server\""
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index e971781..a372c82 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -7978,7 +7978,6 @@
       "src/core/lib/http/httpcli.cc", 
       "src/core/lib/http/parser.cc", 
       "src/core/lib/iomgr/call_combiner.cc", 
-      "src/core/lib/iomgr/closure.cc", 
       "src/core/lib/iomgr/combiner.cc", 
       "src/core/lib/iomgr/endpoint.cc", 
       "src/core/lib/iomgr/endpoint_pair_posix.cc", 
@@ -8903,6 +8902,7 @@
       "test/core/util/port.h", 
       "test/core/util/port_server_client.h", 
       "test/core/util/slice_splitter.h", 
+      "test/core/util/tracer_util.h", 
       "test/core/util/trickle_endpoint.h"
     ], 
     "is_filegroup": true, 
@@ -8937,6 +8937,8 @@
       "test/core/util/port_server_client.h", 
       "test/core/util/slice_splitter.cc", 
       "test/core/util/slice_splitter.h", 
+      "test/core/util/tracer_util.cc", 
+      "test/core/util/tracer_util.h", 
       "test/core/util/trickle_endpoint.cc", 
       "test/core/util/trickle_endpoint.h"
     ], 
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index b20d6ce..8f46ea9 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -319,14 +319,16 @@
 
   def client_cmd(self, args):
     return ['packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh',
-            'node', 'test/interop/interop_client.js'] + args
+            'node', '--require', './test/fixtures/native_native',
+            'test/interop/interop_client.js'] + args
 
   def cloud_to_prod_env(self):
     return {}
 
   def server_cmd(self, args):
     return ['packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh',
-            'node', 'test/interop/interop_server.js'] + args
+            'node', '--require', './test/fixtures/native_native',
+            'test/interop/interop_server.js'] + args
 
   def global_env(self):
     return {}
@@ -572,6 +574,7 @@
       continue
     if item == docker_image:
       item = "$docker_image"
+    item = item.replace('"', '\\"')
     # add quotes when necessary
     if any(character.isspace() for character in item):
       item = "\"%s\"" % item
@@ -1242,7 +1245,7 @@
       _HTTP2_TEST_CASES, http2_server_test_cases, resultset, num_failures,
       args.cloud_to_prod_auth or args.cloud_to_prod, args.prod_servers,
       args.http2_interop)
-  
+
   if num_failures:
     sys.exit(1)
   else: