overridable call_start_batch
diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
index 696987d..5ce792d 100644
--- a/src/csharp/Grpc.Core/Internal/NativeMethods.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
@@ -164,6 +164,8 @@
         public readonly Delegates.grpcsharp_test_callback_delegate grpcsharp_test_callback;
         public readonly Delegates.grpcsharp_test_nop_delegate grpcsharp_test_nop;
 
+        public readonly Delegates.grpcsharp_test_override_method_delegate grpcsharp_test_override_method;
+
         #endregion
 
         public NativeMethods(UnmanagedLibrary library)
@@ -278,6 +280,7 @@
 
             this.grpcsharp_test_callback = GetMethodDelegate<Delegates.grpcsharp_test_callback_delegate>(library);
             this.grpcsharp_test_nop = GetMethodDelegate<Delegates.grpcsharp_test_nop_delegate>(library);
+            this.grpcsharp_test_override_method = GetMethodDelegate<Delegates.grpcsharp_test_override_method_delegate>(library);
         }
 
         /// <summary>
@@ -434,6 +437,7 @@
 
             public delegate CallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback);
             public delegate IntPtr grpcsharp_test_nop_delegate(IntPtr ptr);
+            public delegate void grpcsharp_test_override_method(string methodName, string variant);
         }
     }
 }
diff --git a/src/csharp/Grpc.Microbenchmarks/Program.cs b/src/csharp/Grpc.Microbenchmarks/Program.cs
index 09b6eb6..a0ca1f7 100644
--- a/src/csharp/Grpc.Microbenchmarks/Program.cs
+++ b/src/csharp/Grpc.Microbenchmarks/Program.cs
@@ -34,6 +34,7 @@
 using System;
 using Grpc.Core;
 using Grpc.Core.Internal;
+using Grpc.Core.Logging;
 
 namespace Grpc.Microbenchmarks
 {
@@ -41,6 +42,7 @@
     {
         public static void Main(string[] args)
         {
+            GrpcEnvironment.SetLogger(new TextWriterLogger(Console.Error));
             var benchmark = new SendMessageBenchmark();
             benchmark.Init();
             foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12})
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index f6cff45..34f8b92 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -529,6 +529,31 @@
   grpc_call_unref(call);
 }
 
+typedef grpc_call_error (*grpcsharp_call_start_batch_func) (
+    grpc_call *call, const grpc_op *ops, size_t nops,
+    void *tag, void *reserved);
+
+/* Only for testing */
+static grpc_call_error grpcsharp_call_start_batch_nop(
+    grpc_call *call, const grpc_op *ops, size_t nops,
+    void *tag, void *reserved) {
+  return GRPC_CALL_OK;
+}
+
+static grpc_call_error grpcsharp_call_start_batch_default(
+    grpc_call *call, const grpc_op *ops, size_t nops,
+    void *tag, void *reserved) {
+  return grpc_call_start_batch(call, ops, nops, tag, reserved);
+}
+
+static grpcsharp_call_start_batch_func g_call_start_batch_func = grpcsharp_call_start_batch_default;
+
+static grpc_call_error grpcsharp_call_start_batch(
+    grpc_call *call, const grpc_op *ops, size_t nops,
+    void *tag, void *reserved) {
+  return g_call_start_batch_func(call, ops, nops, tag, reserved);
+}
+
 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary(
     grpc_call *call, grpcsharp_batch_context *ctx, const char *send_buffer,
     size_t send_buffer_len, uint32_t write_flags,
@@ -576,7 +601,7 @@
   ops[5].flags = 0;
   ops[5].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -616,7 +641,7 @@
   ops[3].flags = 0;
   ops[3].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -656,7 +681,7 @@
   ops[3].flags = 0;
   ops[3].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -685,7 +710,7 @@
   ops[1].flags = 0;
   ops[1].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -699,7 +724,7 @@
   ops[0].flags = 0;
   ops[0].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -720,7 +745,7 @@
   ops[1].flags = 0;
   ops[1].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, nops, ctx, NULL);
+  return grpcsharp_call_start_batch(call, ops, nops, ctx, NULL);
 }
 
 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_close_from_client(
@@ -731,7 +756,7 @@
   ops[0].flags = 0;
   ops[0].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -773,7 +798,7 @@
     ops[nops].reserved = NULL;
     nops++;
   }
-  return grpc_call_start_batch(call, ops, nops, ctx, NULL);
+  return grpcsharp_call_start_batch(call, ops, nops, ctx, NULL);
 }
 
 GPR_EXPORT grpc_call_error GPR_CALLTYPE
@@ -784,7 +809,7 @@
   ops[0].data.recv_message.recv_message = &(ctx->recv_message);
   ops[0].flags = 0;
   ops[0].reserved = NULL;
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -798,7 +823,7 @@
   ops[0].flags = 0;
   ops[0].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -817,7 +842,7 @@
   ops[0].flags = 0;
   ops[0].reserved = NULL;
 
-  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+  return grpcsharp_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
                                NULL);
 }
 
@@ -1092,3 +1117,21 @@
 GPR_EXPORT int32_t GPR_CALLTYPE grpcsharp_sizeof_grpc_event(void) {
   return sizeof(grpc_event);
 }
+
+/* Override a method for testing */
+GPR_EXPORT void GPR_CALLTYPE grpcsharp_test_override_method(const char *method_name,
+    const char *variant) {
+  if (strcmp("grpcsharp_call_start_batch", method_name) == 0)
+  { 
+    if (strcmp("nop", variant) == 0)
+    {
+      g_call_start_batch_func = grpcsharp_call_start_batch_nop;
+    } else {
+      GPR_ASSERT(0);
+    }
+  } else {
+    GPR_ASSERT(0);
+  }
+}
+
+