diff --git a/src/csharp/Grpc.IntegrationTesting/Control.cs b/src/csharp/Grpc.IntegrationTesting/Control.cs
index 291bc753978b74a1466b9d97fb6bb32e5cb53c71..003d2428fabdebc6abcbfaa99e90a44f398d65e3 100644
--- a/src/csharp/Grpc.IntegrationTesting/Control.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Control.cs
@@ -26,58 +26,67 @@ namespace Grpc.Testing {
             "CiRzcmMvcHJvdG8vZ3JwYy90ZXN0aW5nL2NvbnRyb2wucHJvdG8SDGdycGMu",
             "dGVzdGluZxolc3JjL3Byb3RvL2dycGMvdGVzdGluZy9wYXlsb2Fkcy5wcm90",
             "bxoic3JjL3Byb3RvL2dycGMvdGVzdGluZy9zdGF0cy5wcm90byIlCg1Qb2lz",
-            "c29uUGFyYW1zEhQKDG9mZmVyZWRfbG9hZBgBIAEoASJBCg1Vbmlmb3JtUGFy",
-            "YW1zEhcKD2ludGVyYXJyaXZhbF9sbxgBIAEoARIXCg9pbnRlcmFycml2YWxf",
-            "aGkYAiABKAEiKwoTRGV0ZXJtaW5pc3RpY1BhcmFtcxIUCgxvZmZlcmVkX2xv",
-            "YWQYASABKAEiOAoMUGFyZXRvUGFyYW1zEhkKEWludGVyYXJyaXZhbF9iYXNl",
-            "GAEgASgBEg0KBWFscGhhGAIgASgBIhIKEENsb3NlZExvb3BQYXJhbXMijgIK",
-            "CkxvYWRQYXJhbXMSNQoLY2xvc2VkX2xvb3AYASABKAsyHi5ncnBjLnRlc3Rp",
-            "bmcuQ2xvc2VkTG9vcFBhcmFtc0gAEi4KB3BvaXNzb24YAiABKAsyGy5ncnBj",
-            "LnRlc3RpbmcuUG9pc3NvblBhcmFtc0gAEi4KB3VuaWZvcm0YAyABKAsyGy5n",
-            "cnBjLnRlc3RpbmcuVW5pZm9ybVBhcmFtc0gAEjMKBmRldGVybRgEIAEoCzIh",
-            "LmdycGMudGVzdGluZy5EZXRlcm1pbmlzdGljUGFyYW1zSAASLAoGcGFyZXRv",
-            "GAUgASgLMhouZ3JwYy50ZXN0aW5nLlBhcmV0b1BhcmFtc0gAQgYKBGxvYWQi",
-            "QwoOU2VjdXJpdHlQYXJhbXMSEwoLdXNlX3Rlc3RfY2EYASABKAgSHAoUc2Vy",
-            "dmVyX2hvc3Rfb3ZlcnJpZGUYAiABKAki1gMKDENsaWVudENvbmZpZxIWCg5z",
-            "ZXJ2ZXJfdGFyZ2V0cxgBIAMoCRItCgtjbGllbnRfdHlwZRgCIAEoDjIYLmdy",
-            "cGMudGVzdGluZy5DbGllbnRUeXBlEjUKD3NlY3VyaXR5X3BhcmFtcxgDIAEo",
-            "CzIcLmdycGMudGVzdGluZy5TZWN1cml0eVBhcmFtcxIkChxvdXRzdGFuZGlu",
-            "Z19ycGNzX3Blcl9jaGFubmVsGAQgASgFEhcKD2NsaWVudF9jaGFubmVscxgF",
-            "IAEoBRIcChRhc3luY19jbGllbnRfdGhyZWFkcxgHIAEoBRInCghycGNfdHlw",
-            "ZRgIIAEoDjIVLmdycGMudGVzdGluZy5ScGNUeXBlEi0KC2xvYWRfcGFyYW1z",
-            "GAogASgLMhguZ3JwYy50ZXN0aW5nLkxvYWRQYXJhbXMSMwoOcGF5bG9hZF9j",
-            "b25maWcYCyABKAsyGy5ncnBjLnRlc3RpbmcuUGF5bG9hZENvbmZpZxI3ChBo",
-            "aXN0b2dyYW1fcGFyYW1zGAwgASgLMh0uZ3JwYy50ZXN0aW5nLkhpc3RvZ3Jh",
-            "bVBhcmFtcxIRCgljb3JlX2xpc3QYDSADKAUSEgoKY29yZV9saW1pdBgOIAEo",
-            "BSI4CgxDbGllbnRTdGF0dXMSKAoFc3RhdHMYASABKAsyGS5ncnBjLnRlc3Rp",
-            "bmcuQ2xpZW50U3RhdHMiFQoETWFyaxINCgVyZXNldBgBIAEoCCJoCgpDbGll",
-            "bnRBcmdzEisKBXNldHVwGAEgASgLMhouZ3JwYy50ZXN0aW5nLkNsaWVudENv",
-            "bmZpZ0gAEiIKBG1hcmsYAiABKAsyEi5ncnBjLnRlc3RpbmcuTWFya0gAQgkK",
-            "B2FyZ3R5cGUi/AEKDFNlcnZlckNvbmZpZxItCgtzZXJ2ZXJfdHlwZRgBIAEo",
-            "DjIYLmdycGMudGVzdGluZy5TZXJ2ZXJUeXBlEjUKD3NlY3VyaXR5X3BhcmFt",
-            "cxgCIAEoCzIcLmdycGMudGVzdGluZy5TZWN1cml0eVBhcmFtcxIMCgRwb3J0",
-            "GAQgASgFEhwKFGFzeW5jX3NlcnZlcl90aHJlYWRzGAcgASgFEhIKCmNvcmVf",
-            "bGltaXQYCCABKAUSMwoOcGF5bG9hZF9jb25maWcYCSABKAsyGy5ncnBjLnRl",
-            "c3RpbmcuUGF5bG9hZENvbmZpZxIRCgljb3JlX2xpc3QYCiADKAUiaAoKU2Vy",
-            "dmVyQXJncxIrCgVzZXR1cBgBIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2ZXJD",
-            "b25maWdIABIiCgRtYXJrGAIgASgLMhIuZ3JwYy50ZXN0aW5nLk1hcmtIAEIJ",
-            "Cgdhcmd0eXBlIlUKDFNlcnZlclN0YXR1cxIoCgVzdGF0cxgBIAEoCzIZLmdy",
-            "cGMudGVzdGluZy5TZXJ2ZXJTdGF0cxIMCgRwb3J0GAIgASgFEg0KBWNvcmVz",
-            "GAMgASgFIg0KC0NvcmVSZXF1ZXN0Ih0KDENvcmVSZXNwb25zZRINCgVjb3Jl",
-            "cxgBIAEoBSIGCgRWb2lkKi8KCkNsaWVudFR5cGUSDwoLU1lOQ19DTElFTlQQ",
-            "ABIQCgxBU1lOQ19DTElFTlQQASpJCgpTZXJ2ZXJUeXBlEg8KC1NZTkNfU0VS",
-            "VkVSEAASEAoMQVNZTkNfU0VSVkVSEAESGAoUQVNZTkNfR0VORVJJQ19TRVJW",
-            "RVIQAiojCgdScGNUeXBlEgkKBVVOQVJZEAASDQoJU1RSRUFNSU5HEAFiBnBy",
-            "b3RvMw=="));
+            "c29uUGFyYW1zEhQKDG9mZmVyZWRfbG9hZBgBIAEoASISChBDbG9zZWRMb29w",
+            "UGFyYW1zInsKCkxvYWRQYXJhbXMSNQoLY2xvc2VkX2xvb3AYASABKAsyHi5n",
+            "cnBjLnRlc3RpbmcuQ2xvc2VkTG9vcFBhcmFtc0gAEi4KB3BvaXNzb24YAiAB",
+            "KAsyGy5ncnBjLnRlc3RpbmcuUG9pc3NvblBhcmFtc0gAQgYKBGxvYWQiQwoO",
+            "U2VjdXJpdHlQYXJhbXMSEwoLdXNlX3Rlc3RfY2EYASABKAgSHAoUc2VydmVy",
+            "X2hvc3Rfb3ZlcnJpZGUYAiABKAki1gMKDENsaWVudENvbmZpZxIWCg5zZXJ2",
+            "ZXJfdGFyZ2V0cxgBIAMoCRItCgtjbGllbnRfdHlwZRgCIAEoDjIYLmdycGMu",
+            "dGVzdGluZy5DbGllbnRUeXBlEjUKD3NlY3VyaXR5X3BhcmFtcxgDIAEoCzIc",
+            "LmdycGMudGVzdGluZy5TZWN1cml0eVBhcmFtcxIkChxvdXRzdGFuZGluZ19y",
+            "cGNzX3Blcl9jaGFubmVsGAQgASgFEhcKD2NsaWVudF9jaGFubmVscxgFIAEo",
+            "BRIcChRhc3luY19jbGllbnRfdGhyZWFkcxgHIAEoBRInCghycGNfdHlwZRgI",
+            "IAEoDjIVLmdycGMudGVzdGluZy5ScGNUeXBlEi0KC2xvYWRfcGFyYW1zGAog",
+            "ASgLMhguZ3JwYy50ZXN0aW5nLkxvYWRQYXJhbXMSMwoOcGF5bG9hZF9jb25m",
+            "aWcYCyABKAsyGy5ncnBjLnRlc3RpbmcuUGF5bG9hZENvbmZpZxI3ChBoaXN0",
+            "b2dyYW1fcGFyYW1zGAwgASgLMh0uZ3JwYy50ZXN0aW5nLkhpc3RvZ3JhbVBh",
+            "cmFtcxIRCgljb3JlX2xpc3QYDSADKAUSEgoKY29yZV9saW1pdBgOIAEoBSI4",
+            "CgxDbGllbnRTdGF0dXMSKAoFc3RhdHMYASABKAsyGS5ncnBjLnRlc3Rpbmcu",
+            "Q2xpZW50U3RhdHMiFQoETWFyaxINCgVyZXNldBgBIAEoCCJoCgpDbGllbnRB",
+            "cmdzEisKBXNldHVwGAEgASgLMhouZ3JwYy50ZXN0aW5nLkNsaWVudENvbmZp",
+            "Z0gAEiIKBG1hcmsYAiABKAsyEi5ncnBjLnRlc3RpbmcuTWFya0gAQgkKB2Fy",
+            "Z3R5cGUi/AEKDFNlcnZlckNvbmZpZxItCgtzZXJ2ZXJfdHlwZRgBIAEoDjIY",
+            "LmdycGMudGVzdGluZy5TZXJ2ZXJUeXBlEjUKD3NlY3VyaXR5X3BhcmFtcxgC",
+            "IAEoCzIcLmdycGMudGVzdGluZy5TZWN1cml0eVBhcmFtcxIMCgRwb3J0GAQg",
+            "ASgFEhwKFGFzeW5jX3NlcnZlcl90aHJlYWRzGAcgASgFEhIKCmNvcmVfbGlt",
+            "aXQYCCABKAUSMwoOcGF5bG9hZF9jb25maWcYCSABKAsyGy5ncnBjLnRlc3Rp",
+            "bmcuUGF5bG9hZENvbmZpZxIRCgljb3JlX2xpc3QYCiADKAUiaAoKU2VydmVy",
+            "QXJncxIrCgVzZXR1cBgBIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2ZXJDb25m",
+            "aWdIABIiCgRtYXJrGAIgASgLMhIuZ3JwYy50ZXN0aW5nLk1hcmtIAEIJCgdh",
+            "cmd0eXBlIlUKDFNlcnZlclN0YXR1cxIoCgVzdGF0cxgBIAEoCzIZLmdycGMu",
+            "dGVzdGluZy5TZXJ2ZXJTdGF0cxIMCgRwb3J0GAIgASgFEg0KBWNvcmVzGAMg",
+            "ASgFIg0KC0NvcmVSZXF1ZXN0Ih0KDENvcmVSZXNwb25zZRINCgVjb3JlcxgB",
+            "IAEoBSIGCgRWb2lkIv0BCghTY2VuYXJpbxIMCgRuYW1lGAEgASgJEjEKDWNs",
+            "aWVudF9jb25maWcYAiABKAsyGi5ncnBjLnRlc3RpbmcuQ2xpZW50Q29uZmln",
+            "EhMKC251bV9jbGllbnRzGAMgASgFEjEKDXNlcnZlcl9jb25maWcYBCABKAsy",
+            "Gi5ncnBjLnRlc3RpbmcuU2VydmVyQ29uZmlnEhMKC251bV9zZXJ2ZXJzGAUg",
+            "ASgFEhYKDndhcm11cF9zZWNvbmRzGAYgASgFEhkKEWJlbmNobWFya19zZWNv",
+            "bmRzGAcgASgFEiAKGHNwYXduX2xvY2FsX3dvcmtlcl9jb3VudBgIIAEoBSI2",
+            "CglTY2VuYXJpb3MSKQoJc2NlbmFyaW9zGAEgAygLMhYuZ3JwYy50ZXN0aW5n",
+            "LlNjZW5hcmlvIpICChVTY2VuYXJpb1Jlc3VsdFN1bW1hcnkSCwoDcXBzGAEg",
+            "ASgBEhsKE3Fwc19wZXJfc2VydmVyX2NvcmUYAiABKAESGgoSc2VydmVyX3N5",
+            "c3RlbV90aW1lGAMgASgBEhgKEHNlcnZlcl91c2VyX3RpbWUYBCABKAESGgoS",
+            "Y2xpZW50X3N5c3RlbV90aW1lGAUgASgBEhgKEGNsaWVudF91c2VyX3RpbWUY",
+            "BiABKAESEgoKbGF0ZW5jeV81MBgHIAEoARISCgpsYXRlbmN5XzkwGAggASgB",
+            "EhIKCmxhdGVuY3lfOTUYCSABKAESEgoKbGF0ZW5jeV85ORgKIAEoARITCgts",
+            "YXRlbmN5Xzk5ORgLIAEoASKYAgoOU2NlbmFyaW9SZXN1bHQSKAoIc2NlbmFy",
+            "aW8YASABKAsyFi5ncnBjLnRlc3RpbmcuU2NlbmFyaW8SLgoJbGF0ZW5jaWVz",
+            "GAIgASgLMhsuZ3JwYy50ZXN0aW5nLkhpc3RvZ3JhbURhdGESLwoMY2xpZW50",
+            "X3N0YXRzGAMgAygLMhkuZ3JwYy50ZXN0aW5nLkNsaWVudFN0YXRzEi8KDHNl",
+            "cnZlcl9zdGF0cxgEIAMoCzIZLmdycGMudGVzdGluZy5TZXJ2ZXJTdGF0cxIU",
+            "CgxzZXJ2ZXJfY29yZXMYBSADKAUSNAoHc3VtbWFyeRgGIAEoCzIjLmdycGMu",
+            "dGVzdGluZy5TY2VuYXJpb1Jlc3VsdFN1bW1hcnkqLwoKQ2xpZW50VHlwZRIP",
+            "CgtTWU5DX0NMSUVOVBAAEhAKDEFTWU5DX0NMSUVOVBABKkkKClNlcnZlclR5",
+            "cGUSDwoLU1lOQ19TRVJWRVIQABIQCgxBU1lOQ19TRVJWRVIQARIYChRBU1lO",
+            "Q19HRU5FUklDX1NFUlZFUhACKiMKB1JwY1R5cGUSCQoFVU5BUlkQABINCglT",
+            "VFJFQU1JTkcQAWIGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Grpc.Testing.PayloadsReflection.Descriptor, global::Grpc.Testing.StatsReflection.Descriptor, },
           new pbr::GeneratedCodeInfo(new[] {typeof(global::Grpc.Testing.ClientType), typeof(global::Grpc.Testing.ServerType), typeof(global::Grpc.Testing.RpcType), }, new pbr::GeneratedCodeInfo[] {
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.PoissonParams), global::Grpc.Testing.PoissonParams.Parser, new[]{ "OfferedLoad" }, null, null, null),
-            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.UniformParams), global::Grpc.Testing.UniformParams.Parser, new[]{ "InterarrivalLo", "InterarrivalHi" }, null, null, null),
-            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.DeterministicParams), global::Grpc.Testing.DeterministicParams.Parser, new[]{ "OfferedLoad" }, null, null, null),
-            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ParetoParams), global::Grpc.Testing.ParetoParams.Parser, new[]{ "InterarrivalBase", "Alpha" }, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ClosedLoopParams), global::Grpc.Testing.ClosedLoopParams.Parser, null, null, null, null),
-            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.LoadParams), global::Grpc.Testing.LoadParams.Parser, new[]{ "ClosedLoop", "Poisson", "Uniform", "Determ", "Pareto" }, new[]{ "Load" }, null, null),
+            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.LoadParams), global::Grpc.Testing.LoadParams.Parser, new[]{ "ClosedLoop", "Poisson" }, new[]{ "Load" }, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SecurityParams), global::Grpc.Testing.SecurityParams.Parser, new[]{ "UseTestCa", "ServerHostOverride" }, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ClientConfig), global::Grpc.Testing.ClientConfig.Parser, new[]{ "ServerTargets", "ClientType", "SecurityParams", "OutstandingRpcsPerChannel", "ClientChannels", "AsyncClientThreads", "RpcType", "LoadParams", "PayloadConfig", "HistogramParams", "CoreList", "CoreLimit" }, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ClientStatus), global::Grpc.Testing.ClientStatus.Parser, new[]{ "Stats" }, null, null, null),
@@ -88,7 +97,11 @@ namespace Grpc.Testing {
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ServerStatus), global::Grpc.Testing.ServerStatus.Parser, new[]{ "Stats", "Port", "Cores" }, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.CoreRequest), global::Grpc.Testing.CoreRequest.Parser, null, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.CoreResponse), global::Grpc.Testing.CoreResponse.Parser, new[]{ "Cores" }, null, null, null),
-            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Void), global::Grpc.Testing.Void.Parser, null, null, null, null)
+            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Void), global::Grpc.Testing.Void.Parser, null, null, null, null),
+            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Scenario), global::Grpc.Testing.Scenario.Parser, new[]{ "Name", "ClientConfig", "NumClients", "ServerConfig", "NumServers", "WarmupSeconds", "BenchmarkSeconds", "SpawnLocalWorkerCount" }, null, null, null),
+            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Scenarios), global::Grpc.Testing.Scenarios.Parser, new[]{ "Scenarios_" }, null, null, null),
+            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ScenarioResultSummary), global::Grpc.Testing.ScenarioResultSummary.Parser, new[]{ "Qps", "QpsPerServerCore", "ServerSystemTime", "ServerUserTime", "ClientSystemTime", "ClientUserTime", "Latency50", "Latency90", "Latency95", "Latency99", "Latency999" }, null, null, null),
+            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ScenarioResult), global::Grpc.Testing.ScenarioResult.Parser, new[]{ "Scenario", "Latencies", "ClientStats", "ServerStats", "ServerCores", "Summary" }, null, null, null)
           }));
     }
     #endregion
@@ -224,10 +237,14 @@ namespace Grpc.Testing {
 
   }
 
+  /// <summary>
+  ///  Once an RPC finishes, immediately start a new one.
+  ///  No configuration parameters needed.
+  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class UniformParams : pb::IMessage<UniformParams> {
-    private static readonly pb::MessageParser<UniformParams> _parser = new pb::MessageParser<UniformParams>(() => new UniformParams());
-    public static pb::MessageParser<UniformParams> Parser { get { return _parser; } }
+  public sealed partial class ClosedLoopParams : pb::IMessage<ClosedLoopParams> {
+    private static readonly pb::MessageParser<ClosedLoopParams> _parser = new pb::MessageParser<ClosedLoopParams>(() => new ClosedLoopParams());
+    public static pb::MessageParser<ClosedLoopParams> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
       get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[1]; }
@@ -237,61 +254,35 @@ namespace Grpc.Testing {
       get { return Descriptor; }
     }
 
-    public UniformParams() {
+    public ClosedLoopParams() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public UniformParams(UniformParams other) : this() {
-      interarrivalLo_ = other.interarrivalLo_;
-      interarrivalHi_ = other.interarrivalHi_;
-    }
-
-    public UniformParams Clone() {
-      return new UniformParams(this);
-    }
-
-    /// <summary>Field number for the "interarrival_lo" field.</summary>
-    public const int InterarrivalLoFieldNumber = 1;
-    private double interarrivalLo_;
-    public double InterarrivalLo {
-      get { return interarrivalLo_; }
-      set {
-        interarrivalLo_ = value;
-      }
+    public ClosedLoopParams(ClosedLoopParams other) : this() {
     }
 
-    /// <summary>Field number for the "interarrival_hi" field.</summary>
-    public const int InterarrivalHiFieldNumber = 2;
-    private double interarrivalHi_;
-    public double InterarrivalHi {
-      get { return interarrivalHi_; }
-      set {
-        interarrivalHi_ = value;
-      }
+    public ClosedLoopParams Clone() {
+      return new ClosedLoopParams(this);
     }
 
     public override bool Equals(object other) {
-      return Equals(other as UniformParams);
+      return Equals(other as ClosedLoopParams);
     }
 
-    public bool Equals(UniformParams other) {
+    public bool Equals(ClosedLoopParams other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (InterarrivalLo != other.InterarrivalLo) return false;
-      if (InterarrivalHi != other.InterarrivalHi) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (InterarrivalLo != 0D) hash ^= InterarrivalLo.GetHashCode();
-      if (InterarrivalHi != 0D) hash ^= InterarrivalHi.GetHashCode();
       return hash;
     }
 
@@ -300,37 +291,17 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (InterarrivalLo != 0D) {
-        output.WriteRawTag(9);
-        output.WriteDouble(InterarrivalLo);
-      }
-      if (InterarrivalHi != 0D) {
-        output.WriteRawTag(17);
-        output.WriteDouble(InterarrivalHi);
-      }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (InterarrivalLo != 0D) {
-        size += 1 + 8;
-      }
-      if (InterarrivalHi != 0D) {
-        size += 1 + 8;
-      }
       return size;
     }
 
-    public void MergeFrom(UniformParams other) {
+    public void MergeFrom(ClosedLoopParams other) {
       if (other == null) {
         return;
       }
-      if (other.InterarrivalLo != 0D) {
-        InterarrivalLo = other.InterarrivalLo;
-      }
-      if (other.InterarrivalHi != 0D) {
-        InterarrivalHi = other.InterarrivalHi;
-      }
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -340,14 +311,6 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 9: {
-            InterarrivalLo = input.ReadDouble();
-            break;
-          }
-          case 17: {
-            InterarrivalHi = input.ReadDouble();
-            break;
-          }
         }
       }
     }
@@ -355,9 +318,9 @@ namespace Grpc.Testing {
   }
 
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class DeterministicParams : pb::IMessage<DeterministicParams> {
-    private static readonly pb::MessageParser<DeterministicParams> _parser = new pb::MessageParser<DeterministicParams>(() => new DeterministicParams());
-    public static pb::MessageParser<DeterministicParams> Parser { get { return _parser; } }
+  public sealed partial class LoadParams : pb::IMessage<LoadParams> {
+    private static readonly pb::MessageParser<LoadParams> _parser = new pb::MessageParser<LoadParams>(() => new LoadParams());
+    public static pb::MessageParser<LoadParams> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
       get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[2]; }
@@ -367,48 +330,87 @@ namespace Grpc.Testing {
       get { return Descriptor; }
     }
 
-    public DeterministicParams() {
+    public LoadParams() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public DeterministicParams(DeterministicParams other) : this() {
-      offeredLoad_ = other.offeredLoad_;
+    public LoadParams(LoadParams other) : this() {
+      switch (other.LoadCase) {
+        case LoadOneofCase.ClosedLoop:
+          ClosedLoop = other.ClosedLoop.Clone();
+          break;
+        case LoadOneofCase.Poisson:
+          Poisson = other.Poisson.Clone();
+          break;
+      }
+
     }
 
-    public DeterministicParams Clone() {
-      return new DeterministicParams(this);
+    public LoadParams Clone() {
+      return new LoadParams(this);
     }
 
-    /// <summary>Field number for the "offered_load" field.</summary>
-    public const int OfferedLoadFieldNumber = 1;
-    private double offeredLoad_;
-    public double OfferedLoad {
-      get { return offeredLoad_; }
+    /// <summary>Field number for the "closed_loop" field.</summary>
+    public const int ClosedLoopFieldNumber = 1;
+    public global::Grpc.Testing.ClosedLoopParams ClosedLoop {
+      get { return loadCase_ == LoadOneofCase.ClosedLoop ? (global::Grpc.Testing.ClosedLoopParams) load_ : null; }
       set {
-        offeredLoad_ = value;
+        load_ = value;
+        loadCase_ = value == null ? LoadOneofCase.None : LoadOneofCase.ClosedLoop;
+      }
+    }
+
+    /// <summary>Field number for the "poisson" field.</summary>
+    public const int PoissonFieldNumber = 2;
+    public global::Grpc.Testing.PoissonParams Poisson {
+      get { return loadCase_ == LoadOneofCase.Poisson ? (global::Grpc.Testing.PoissonParams) load_ : null; }
+      set {
+        load_ = value;
+        loadCase_ = value == null ? LoadOneofCase.None : LoadOneofCase.Poisson;
       }
     }
 
+    private object load_;
+    /// <summary>Enum of possible cases for the "load" oneof.</summary>
+    public enum LoadOneofCase {
+      None = 0,
+      ClosedLoop = 1,
+      Poisson = 2,
+    }
+    private LoadOneofCase loadCase_ = LoadOneofCase.None;
+    public LoadOneofCase LoadCase {
+      get { return loadCase_; }
+    }
+
+    public void ClearLoad() {
+      loadCase_ = LoadOneofCase.None;
+      load_ = null;
+    }
+
     public override bool Equals(object other) {
-      return Equals(other as DeterministicParams);
+      return Equals(other as LoadParams);
     }
 
-    public bool Equals(DeterministicParams other) {
+    public bool Equals(LoadParams other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (OfferedLoad != other.OfferedLoad) return false;
+      if (!object.Equals(ClosedLoop, other.ClosedLoop)) return false;
+      if (!object.Equals(Poisson, other.Poisson)) return false;
+      if (LoadCase != other.LoadCase) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (OfferedLoad != 0D) hash ^= OfferedLoad.GetHashCode();
+      if (loadCase_ == LoadOneofCase.ClosedLoop) hash ^= ClosedLoop.GetHashCode();
+      if (loadCase_ == LoadOneofCase.Poisson) hash ^= Poisson.GetHashCode();
+      hash ^= (int) loadCase_;
       return hash;
     }
 
@@ -417,27 +419,40 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (OfferedLoad != 0D) {
-        output.WriteRawTag(9);
-        output.WriteDouble(OfferedLoad);
+      if (loadCase_ == LoadOneofCase.ClosedLoop) {
+        output.WriteRawTag(10);
+        output.WriteMessage(ClosedLoop);
+      }
+      if (loadCase_ == LoadOneofCase.Poisson) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Poisson);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (OfferedLoad != 0D) {
-        size += 1 + 8;
+      if (loadCase_ == LoadOneofCase.ClosedLoop) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ClosedLoop);
+      }
+      if (loadCase_ == LoadOneofCase.Poisson) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Poisson);
       }
       return size;
     }
 
-    public void MergeFrom(DeterministicParams other) {
+    public void MergeFrom(LoadParams other) {
       if (other == null) {
         return;
       }
-      if (other.OfferedLoad != 0D) {
-        OfferedLoad = other.OfferedLoad;
+      switch (other.LoadCase) {
+        case LoadOneofCase.ClosedLoop:
+          ClosedLoop = other.ClosedLoop;
+          break;
+        case LoadOneofCase.Poisson:
+          Poisson = other.Poisson;
+          break;
       }
+
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -447,8 +462,22 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 9: {
-            OfferedLoad = input.ReadDouble();
+          case 10: {
+            global::Grpc.Testing.ClosedLoopParams subBuilder = new global::Grpc.Testing.ClosedLoopParams();
+            if (loadCase_ == LoadOneofCase.ClosedLoop) {
+              subBuilder.MergeFrom(ClosedLoop);
+            }
+            input.ReadMessage(subBuilder);
+            ClosedLoop = subBuilder;
+            break;
+          }
+          case 18: {
+            global::Grpc.Testing.PoissonParams subBuilder = new global::Grpc.Testing.PoissonParams();
+            if (loadCase_ == LoadOneofCase.Poisson) {
+              subBuilder.MergeFrom(Poisson);
+            }
+            input.ReadMessage(subBuilder);
+            Poisson = subBuilder;
             break;
           }
         }
@@ -457,10 +486,13 @@ namespace Grpc.Testing {
 
   }
 
+  /// <summary>
+  ///  presence of SecurityParams implies use of TLS
+  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ParetoParams : pb::IMessage<ParetoParams> {
-    private static readonly pb::MessageParser<ParetoParams> _parser = new pb::MessageParser<ParetoParams>(() => new ParetoParams());
-    public static pb::MessageParser<ParetoParams> Parser { get { return _parser; } }
+  public sealed partial class SecurityParams : pb::IMessage<SecurityParams> {
+    private static readonly pb::MessageParser<SecurityParams> _parser = new pb::MessageParser<SecurityParams>(() => new SecurityParams());
+    public static pb::MessageParser<SecurityParams> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
       get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[3]; }
@@ -470,61 +502,61 @@ namespace Grpc.Testing {
       get { return Descriptor; }
     }
 
-    public ParetoParams() {
+    public SecurityParams() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ParetoParams(ParetoParams other) : this() {
-      interarrivalBase_ = other.interarrivalBase_;
-      alpha_ = other.alpha_;
+    public SecurityParams(SecurityParams other) : this() {
+      useTestCa_ = other.useTestCa_;
+      serverHostOverride_ = other.serverHostOverride_;
     }
 
-    public ParetoParams Clone() {
-      return new ParetoParams(this);
+    public SecurityParams Clone() {
+      return new SecurityParams(this);
     }
 
-    /// <summary>Field number for the "interarrival_base" field.</summary>
-    public const int InterarrivalBaseFieldNumber = 1;
-    private double interarrivalBase_;
-    public double InterarrivalBase {
-      get { return interarrivalBase_; }
+    /// <summary>Field number for the "use_test_ca" field.</summary>
+    public const int UseTestCaFieldNumber = 1;
+    private bool useTestCa_;
+    public bool UseTestCa {
+      get { return useTestCa_; }
       set {
-        interarrivalBase_ = value;
+        useTestCa_ = value;
       }
     }
 
-    /// <summary>Field number for the "alpha" field.</summary>
-    public const int AlphaFieldNumber = 2;
-    private double alpha_;
-    public double Alpha {
-      get { return alpha_; }
+    /// <summary>Field number for the "server_host_override" field.</summary>
+    public const int ServerHostOverrideFieldNumber = 2;
+    private string serverHostOverride_ = "";
+    public string ServerHostOverride {
+      get { return serverHostOverride_; }
       set {
-        alpha_ = value;
+        serverHostOverride_ = pb::Preconditions.CheckNotNull(value, "value");
       }
     }
 
     public override bool Equals(object other) {
-      return Equals(other as ParetoParams);
+      return Equals(other as SecurityParams);
     }
 
-    public bool Equals(ParetoParams other) {
+    public bool Equals(SecurityParams other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (InterarrivalBase != other.InterarrivalBase) return false;
-      if (Alpha != other.Alpha) return false;
+      if (UseTestCa != other.UseTestCa) return false;
+      if (ServerHostOverride != other.ServerHostOverride) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (InterarrivalBase != 0D) hash ^= InterarrivalBase.GetHashCode();
-      if (Alpha != 0D) hash ^= Alpha.GetHashCode();
+      if (UseTestCa != false) hash ^= UseTestCa.GetHashCode();
+      if (ServerHostOverride.Length != 0) hash ^= ServerHostOverride.GetHashCode();
       return hash;
     }
 
@@ -533,36 +565,36 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (InterarrivalBase != 0D) {
-        output.WriteRawTag(9);
-        output.WriteDouble(InterarrivalBase);
+      if (UseTestCa != false) {
+        output.WriteRawTag(8);
+        output.WriteBool(UseTestCa);
       }
-      if (Alpha != 0D) {
-        output.WriteRawTag(17);
-        output.WriteDouble(Alpha);
+      if (ServerHostOverride.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(ServerHostOverride);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (InterarrivalBase != 0D) {
-        size += 1 + 8;
+      if (UseTestCa != false) {
+        size += 1 + 1;
       }
-      if (Alpha != 0D) {
-        size += 1 + 8;
+      if (ServerHostOverride.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ServerHostOverride);
       }
       return size;
     }
 
-    public void MergeFrom(ParetoParams other) {
+    public void MergeFrom(SecurityParams other) {
       if (other == null) {
         return;
       }
-      if (other.InterarrivalBase != 0D) {
-        InterarrivalBase = other.InterarrivalBase;
+      if (other.UseTestCa != false) {
+        UseTestCa = other.UseTestCa;
       }
-      if (other.Alpha != 0D) {
-        Alpha = other.Alpha;
+      if (other.ServerHostOverride.Length != 0) {
+        ServerHostOverride = other.ServerHostOverride;
       }
     }
 
@@ -573,12 +605,12 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 9: {
-            InterarrivalBase = input.ReadDouble();
+          case 8: {
+            UseTestCa = input.ReadBool();
             break;
           }
-          case 17: {
-            Alpha = input.ReadDouble();
+          case 18: {
+            ServerHostOverride = input.ReadString();
             break;
           }
         }
@@ -587,14 +619,10 @@ namespace Grpc.Testing {
 
   }
 
-  /// <summary>
-  ///  Once an RPC finishes, immediately start a new one.
-  ///  No configuration parameters needed.
-  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ClosedLoopParams : pb::IMessage<ClosedLoopParams> {
-    private static readonly pb::MessageParser<ClosedLoopParams> _parser = new pb::MessageParser<ClosedLoopParams>(() => new ClosedLoopParams());
-    public static pb::MessageParser<ClosedLoopParams> Parser { get { return _parser; } }
+  public sealed partial class ClientConfig : pb::IMessage<ClientConfig> {
+    private static readonly pb::MessageParser<ClientConfig> _parser = new pb::MessageParser<ClientConfig>(() => new ClientConfig());
+    public static pb::MessageParser<ClientConfig> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
       get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[4]; }
@@ -604,35 +632,477 @@ namespace Grpc.Testing {
       get { return Descriptor; }
     }
 
-    public ClosedLoopParams() {
+    public ClientConfig() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ClosedLoopParams(ClosedLoopParams other) : this() {
+    public ClientConfig(ClientConfig other) : this() {
+      serverTargets_ = other.serverTargets_.Clone();
+      clientType_ = other.clientType_;
+      SecurityParams = other.securityParams_ != null ? other.SecurityParams.Clone() : null;
+      outstandingRpcsPerChannel_ = other.outstandingRpcsPerChannel_;
+      clientChannels_ = other.clientChannels_;
+      asyncClientThreads_ = other.asyncClientThreads_;
+      rpcType_ = other.rpcType_;
+      LoadParams = other.loadParams_ != null ? other.LoadParams.Clone() : null;
+      PayloadConfig = other.payloadConfig_ != null ? other.PayloadConfig.Clone() : null;
+      HistogramParams = other.histogramParams_ != null ? other.HistogramParams.Clone() : null;
+      coreList_ = other.coreList_.Clone();
+      coreLimit_ = other.coreLimit_;
     }
 
-    public ClosedLoopParams Clone() {
-      return new ClosedLoopParams(this);
+    public ClientConfig Clone() {
+      return new ClientConfig(this);
     }
 
-    public override bool Equals(object other) {
-      return Equals(other as ClosedLoopParams);
+    /// <summary>Field number for the "server_targets" field.</summary>
+    public const int ServerTargetsFieldNumber = 1;
+    private static readonly pb::FieldCodec<string> _repeated_serverTargets_codec
+        = pb::FieldCodec.ForString(10);
+    private readonly pbc::RepeatedField<string> serverTargets_ = new pbc::RepeatedField<string>();
+    /// <summary>
+    ///  List of targets to connect to. At least one target needs to be specified.
+    /// </summary>
+    public pbc::RepeatedField<string> ServerTargets {
+      get { return serverTargets_; }
     }
 
-    public bool Equals(ClosedLoopParams other) {
-      if (ReferenceEquals(other, null)) {
-        return false;
-      }
-      if (ReferenceEquals(other, this)) {
-        return true;
-      }
-      return true;
-    }
+    /// <summary>Field number for the "client_type" field.</summary>
+    public const int ClientTypeFieldNumber = 2;
+    private global::Grpc.Testing.ClientType clientType_ = global::Grpc.Testing.ClientType.SYNC_CLIENT;
+    public global::Grpc.Testing.ClientType ClientType {
+      get { return clientType_; }
+      set {
+        clientType_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "security_params" field.</summary>
+    public const int SecurityParamsFieldNumber = 3;
+    private global::Grpc.Testing.SecurityParams securityParams_;
+    public global::Grpc.Testing.SecurityParams SecurityParams {
+      get { return securityParams_; }
+      set {
+        securityParams_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "outstanding_rpcs_per_channel" field.</summary>
+    public const int OutstandingRpcsPerChannelFieldNumber = 4;
+    private int outstandingRpcsPerChannel_;
+    /// <summary>
+    ///  How many concurrent RPCs to start for each channel.
+    ///  For synchronous client, use a separate thread for each outstanding RPC.
+    /// </summary>
+    public int OutstandingRpcsPerChannel {
+      get { return outstandingRpcsPerChannel_; }
+      set {
+        outstandingRpcsPerChannel_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "client_channels" field.</summary>
+    public const int ClientChannelsFieldNumber = 5;
+    private int clientChannels_;
+    /// <summary>
+    ///  Number of independent client channels to create.
+    ///  i-th channel will connect to server_target[i % server_targets.size()]
+    /// </summary>
+    public int ClientChannels {
+      get { return clientChannels_; }
+      set {
+        clientChannels_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "async_client_threads" field.</summary>
+    public const int AsyncClientThreadsFieldNumber = 7;
+    private int asyncClientThreads_;
+    /// <summary>
+    ///  Only for async client. Number of threads to use to start/manage RPCs.
+    /// </summary>
+    public int AsyncClientThreads {
+      get { return asyncClientThreads_; }
+      set {
+        asyncClientThreads_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rpc_type" field.</summary>
+    public const int RpcTypeFieldNumber = 8;
+    private global::Grpc.Testing.RpcType rpcType_ = global::Grpc.Testing.RpcType.UNARY;
+    public global::Grpc.Testing.RpcType RpcType {
+      get { return rpcType_; }
+      set {
+        rpcType_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "load_params" field.</summary>
+    public const int LoadParamsFieldNumber = 10;
+    private global::Grpc.Testing.LoadParams loadParams_;
+    /// <summary>
+    ///  The requested load for the entire client (aggregated over all the threads).
+    /// </summary>
+    public global::Grpc.Testing.LoadParams LoadParams {
+      get { return loadParams_; }
+      set {
+        loadParams_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "payload_config" field.</summary>
+    public const int PayloadConfigFieldNumber = 11;
+    private global::Grpc.Testing.PayloadConfig payloadConfig_;
+    public global::Grpc.Testing.PayloadConfig PayloadConfig {
+      get { return payloadConfig_; }
+      set {
+        payloadConfig_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "histogram_params" field.</summary>
+    public const int HistogramParamsFieldNumber = 12;
+    private global::Grpc.Testing.HistogramParams histogramParams_;
+    public global::Grpc.Testing.HistogramParams HistogramParams {
+      get { return histogramParams_; }
+      set {
+        histogramParams_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "core_list" field.</summary>
+    public const int CoreListFieldNumber = 13;
+    private static readonly pb::FieldCodec<int> _repeated_coreList_codec
+        = pb::FieldCodec.ForInt32(106);
+    private readonly pbc::RepeatedField<int> coreList_ = new pbc::RepeatedField<int>();
+    /// <summary>
+    ///  Specify the cores we should run the client on, if desired
+    /// </summary>
+    public pbc::RepeatedField<int> CoreList {
+      get { return coreList_; }
+    }
+
+    /// <summary>Field number for the "core_limit" field.</summary>
+    public const int CoreLimitFieldNumber = 14;
+    private int coreLimit_;
+    public int CoreLimit {
+      get { return coreLimit_; }
+      set {
+        coreLimit_ = value;
+      }
+    }
+
+    public override bool Equals(object other) {
+      return Equals(other as ClientConfig);
+    }
+
+    public bool Equals(ClientConfig other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!serverTargets_.Equals(other.serverTargets_)) return false;
+      if (ClientType != other.ClientType) return false;
+      if (!object.Equals(SecurityParams, other.SecurityParams)) return false;
+      if (OutstandingRpcsPerChannel != other.OutstandingRpcsPerChannel) return false;
+      if (ClientChannels != other.ClientChannels) return false;
+      if (AsyncClientThreads != other.AsyncClientThreads) return false;
+      if (RpcType != other.RpcType) return false;
+      if (!object.Equals(LoadParams, other.LoadParams)) return false;
+      if (!object.Equals(PayloadConfig, other.PayloadConfig)) return false;
+      if (!object.Equals(HistogramParams, other.HistogramParams)) return false;
+      if(!coreList_.Equals(other.coreList_)) return false;
+      if (CoreLimit != other.CoreLimit) return false;
+      return true;
+    }
+
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= serverTargets_.GetHashCode();
+      if (ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) hash ^= ClientType.GetHashCode();
+      if (securityParams_ != null) hash ^= SecurityParams.GetHashCode();
+      if (OutstandingRpcsPerChannel != 0) hash ^= OutstandingRpcsPerChannel.GetHashCode();
+      if (ClientChannels != 0) hash ^= ClientChannels.GetHashCode();
+      if (AsyncClientThreads != 0) hash ^= AsyncClientThreads.GetHashCode();
+      if (RpcType != global::Grpc.Testing.RpcType.UNARY) hash ^= RpcType.GetHashCode();
+      if (loadParams_ != null) hash ^= LoadParams.GetHashCode();
+      if (payloadConfig_ != null) hash ^= PayloadConfig.GetHashCode();
+      if (histogramParams_ != null) hash ^= HistogramParams.GetHashCode();
+      hash ^= coreList_.GetHashCode();
+      if (CoreLimit != 0) hash ^= CoreLimit.GetHashCode();
+      return hash;
+    }
+
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    public void WriteTo(pb::CodedOutputStream output) {
+      serverTargets_.WriteTo(output, _repeated_serverTargets_codec);
+      if (ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) ClientType);
+      }
+      if (securityParams_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(SecurityParams);
+      }
+      if (OutstandingRpcsPerChannel != 0) {
+        output.WriteRawTag(32);
+        output.WriteInt32(OutstandingRpcsPerChannel);
+      }
+      if (ClientChannels != 0) {
+        output.WriteRawTag(40);
+        output.WriteInt32(ClientChannels);
+      }
+      if (AsyncClientThreads != 0) {
+        output.WriteRawTag(56);
+        output.WriteInt32(AsyncClientThreads);
+      }
+      if (RpcType != global::Grpc.Testing.RpcType.UNARY) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) RpcType);
+      }
+      if (loadParams_ != null) {
+        output.WriteRawTag(82);
+        output.WriteMessage(LoadParams);
+      }
+      if (payloadConfig_ != null) {
+        output.WriteRawTag(90);
+        output.WriteMessage(PayloadConfig);
+      }
+      if (histogramParams_ != null) {
+        output.WriteRawTag(98);
+        output.WriteMessage(HistogramParams);
+      }
+      coreList_.WriteTo(output, _repeated_coreList_codec);
+      if (CoreLimit != 0) {
+        output.WriteRawTag(112);
+        output.WriteInt32(CoreLimit);
+      }
+    }
+
+    public int CalculateSize() {
+      int size = 0;
+      size += serverTargets_.CalculateSize(_repeated_serverTargets_codec);
+      if (ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ClientType);
+      }
+      if (securityParams_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(SecurityParams);
+      }
+      if (OutstandingRpcsPerChannel != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(OutstandingRpcsPerChannel);
+      }
+      if (ClientChannels != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(ClientChannels);
+      }
+      if (AsyncClientThreads != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(AsyncClientThreads);
+      }
+      if (RpcType != global::Grpc.Testing.RpcType.UNARY) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RpcType);
+      }
+      if (loadParams_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LoadParams);
+      }
+      if (payloadConfig_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(PayloadConfig);
+      }
+      if (histogramParams_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(HistogramParams);
+      }
+      size += coreList_.CalculateSize(_repeated_coreList_codec);
+      if (CoreLimit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(CoreLimit);
+      }
+      return size;
+    }
+
+    public void MergeFrom(ClientConfig other) {
+      if (other == null) {
+        return;
+      }
+      serverTargets_.Add(other.serverTargets_);
+      if (other.ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) {
+        ClientType = other.ClientType;
+      }
+      if (other.securityParams_ != null) {
+        if (securityParams_ == null) {
+          securityParams_ = new global::Grpc.Testing.SecurityParams();
+        }
+        SecurityParams.MergeFrom(other.SecurityParams);
+      }
+      if (other.OutstandingRpcsPerChannel != 0) {
+        OutstandingRpcsPerChannel = other.OutstandingRpcsPerChannel;
+      }
+      if (other.ClientChannels != 0) {
+        ClientChannels = other.ClientChannels;
+      }
+      if (other.AsyncClientThreads != 0) {
+        AsyncClientThreads = other.AsyncClientThreads;
+      }
+      if (other.RpcType != global::Grpc.Testing.RpcType.UNARY) {
+        RpcType = other.RpcType;
+      }
+      if (other.loadParams_ != null) {
+        if (loadParams_ == null) {
+          loadParams_ = new global::Grpc.Testing.LoadParams();
+        }
+        LoadParams.MergeFrom(other.LoadParams);
+      }
+      if (other.payloadConfig_ != null) {
+        if (payloadConfig_ == null) {
+          payloadConfig_ = new global::Grpc.Testing.PayloadConfig();
+        }
+        PayloadConfig.MergeFrom(other.PayloadConfig);
+      }
+      if (other.histogramParams_ != null) {
+        if (histogramParams_ == null) {
+          histogramParams_ = new global::Grpc.Testing.HistogramParams();
+        }
+        HistogramParams.MergeFrom(other.HistogramParams);
+      }
+      coreList_.Add(other.coreList_);
+      if (other.CoreLimit != 0) {
+        CoreLimit = other.CoreLimit;
+      }
+    }
+
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            serverTargets_.AddEntriesFrom(input, _repeated_serverTargets_codec);
+            break;
+          }
+          case 16: {
+            clientType_ = (global::Grpc.Testing.ClientType) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            if (securityParams_ == null) {
+              securityParams_ = new global::Grpc.Testing.SecurityParams();
+            }
+            input.ReadMessage(securityParams_);
+            break;
+          }
+          case 32: {
+            OutstandingRpcsPerChannel = input.ReadInt32();
+            break;
+          }
+          case 40: {
+            ClientChannels = input.ReadInt32();
+            break;
+          }
+          case 56: {
+            AsyncClientThreads = input.ReadInt32();
+            break;
+          }
+          case 64: {
+            rpcType_ = (global::Grpc.Testing.RpcType) input.ReadEnum();
+            break;
+          }
+          case 82: {
+            if (loadParams_ == null) {
+              loadParams_ = new global::Grpc.Testing.LoadParams();
+            }
+            input.ReadMessage(loadParams_);
+            break;
+          }
+          case 90: {
+            if (payloadConfig_ == null) {
+              payloadConfig_ = new global::Grpc.Testing.PayloadConfig();
+            }
+            input.ReadMessage(payloadConfig_);
+            break;
+          }
+          case 98: {
+            if (histogramParams_ == null) {
+              histogramParams_ = new global::Grpc.Testing.HistogramParams();
+            }
+            input.ReadMessage(histogramParams_);
+            break;
+          }
+          case 106:
+          case 104: {
+            coreList_.AddEntriesFrom(input, _repeated_coreList_codec);
+            break;
+          }
+          case 112: {
+            CoreLimit = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public sealed partial class ClientStatus : pb::IMessage<ClientStatus> {
+    private static readonly pb::MessageParser<ClientStatus> _parser = new pb::MessageParser<ClientStatus>(() => new ClientStatus());
+    public static pb::MessageParser<ClientStatus> Parser { get { return _parser; } }
+
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    public ClientStatus() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    public ClientStatus(ClientStatus other) : this() {
+      Stats = other.stats_ != null ? other.Stats.Clone() : null;
+    }
+
+    public ClientStatus Clone() {
+      return new ClientStatus(this);
+    }
+
+    /// <summary>Field number for the "stats" field.</summary>
+    public const int StatsFieldNumber = 1;
+    private global::Grpc.Testing.ClientStats stats_;
+    public global::Grpc.Testing.ClientStats Stats {
+      get { return stats_; }
+      set {
+        stats_ = value;
+      }
+    }
+
+    public override bool Equals(object other) {
+      return Equals(other as ClientStatus);
+    }
+
+    public bool Equals(ClientStatus other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Stats, other.Stats)) return false;
+      return true;
+    }
 
     public override int GetHashCode() {
       int hash = 1;
+      if (stats_ != null) hash ^= Stats.GetHashCode();
       return hash;
     }
 
@@ -641,17 +1111,30 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
+      if (stats_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Stats);
+      }
     }
 
     public int CalculateSize() {
       int size = 0;
+      if (stats_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Stats);
+      }
       return size;
     }
 
-    public void MergeFrom(ClosedLoopParams other) {
+    public void MergeFrom(ClientStatus other) {
       if (other == null) {
         return;
       }
+      if (other.stats_ != null) {
+        if (stats_ == null) {
+          stats_ = new global::Grpc.Testing.ClientStats();
+        }
+        Stats.MergeFrom(other.Stats);
+      }
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -661,154 +1144,80 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
+          case 10: {
+            if (stats_ == null) {
+              stats_ = new global::Grpc.Testing.ClientStats();
+            }
+            input.ReadMessage(stats_);
+            break;
+          }
         }
       }
     }
 
   }
 
+  /// <summary>
+  ///  Request current stats
+  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class LoadParams : pb::IMessage<LoadParams> {
-    private static readonly pb::MessageParser<LoadParams> _parser = new pb::MessageParser<LoadParams>(() => new LoadParams());
-    public static pb::MessageParser<LoadParams> Parser { get { return _parser; } }
+  public sealed partial class Mark : pb::IMessage<Mark> {
+    private static readonly pb::MessageParser<Mark> _parser = new pb::MessageParser<Mark>(() => new Mark());
+    public static pb::MessageParser<Mark> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[5]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[6]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public LoadParams() {
+    public Mark() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public LoadParams(LoadParams other) : this() {
-      switch (other.LoadCase) {
-        case LoadOneofCase.ClosedLoop:
-          ClosedLoop = other.ClosedLoop.Clone();
-          break;
-        case LoadOneofCase.Poisson:
-          Poisson = other.Poisson.Clone();
-          break;
-        case LoadOneofCase.Uniform:
-          Uniform = other.Uniform.Clone();
-          break;
-        case LoadOneofCase.Determ:
-          Determ = other.Determ.Clone();
-          break;
-        case LoadOneofCase.Pareto:
-          Pareto = other.Pareto.Clone();
-          break;
-      }
-
-    }
-
-    public LoadParams Clone() {
-      return new LoadParams(this);
-    }
-
-    /// <summary>Field number for the "closed_loop" field.</summary>
-    public const int ClosedLoopFieldNumber = 1;
-    public global::Grpc.Testing.ClosedLoopParams ClosedLoop {
-      get { return loadCase_ == LoadOneofCase.ClosedLoop ? (global::Grpc.Testing.ClosedLoopParams) load_ : null; }
-      set {
-        load_ = value;
-        loadCase_ = value == null ? LoadOneofCase.None : LoadOneofCase.ClosedLoop;
-      }
-    }
-
-    /// <summary>Field number for the "poisson" field.</summary>
-    public const int PoissonFieldNumber = 2;
-    public global::Grpc.Testing.PoissonParams Poisson {
-      get { return loadCase_ == LoadOneofCase.Poisson ? (global::Grpc.Testing.PoissonParams) load_ : null; }
-      set {
-        load_ = value;
-        loadCase_ = value == null ? LoadOneofCase.None : LoadOneofCase.Poisson;
-      }
-    }
-
-    /// <summary>Field number for the "uniform" field.</summary>
-    public const int UniformFieldNumber = 3;
-    public global::Grpc.Testing.UniformParams Uniform {
-      get { return loadCase_ == LoadOneofCase.Uniform ? (global::Grpc.Testing.UniformParams) load_ : null; }
-      set {
-        load_ = value;
-        loadCase_ = value == null ? LoadOneofCase.None : LoadOneofCase.Uniform;
-      }
+    public Mark(Mark other) : this() {
+      reset_ = other.reset_;
     }
 
-    /// <summary>Field number for the "determ" field.</summary>
-    public const int DetermFieldNumber = 4;
-    public global::Grpc.Testing.DeterministicParams Determ {
-      get { return loadCase_ == LoadOneofCase.Determ ? (global::Grpc.Testing.DeterministicParams) load_ : null; }
-      set {
-        load_ = value;
-        loadCase_ = value == null ? LoadOneofCase.None : LoadOneofCase.Determ;
-      }
+    public Mark Clone() {
+      return new Mark(this);
     }
 
-    /// <summary>Field number for the "pareto" field.</summary>
-    public const int ParetoFieldNumber = 5;
-    public global::Grpc.Testing.ParetoParams Pareto {
-      get { return loadCase_ == LoadOneofCase.Pareto ? (global::Grpc.Testing.ParetoParams) load_ : null; }
+    /// <summary>Field number for the "reset" field.</summary>
+    public const int ResetFieldNumber = 1;
+    private bool reset_;
+    /// <summary>
+    ///  if true, the stats will be reset after taking their snapshot.
+    /// </summary>
+    public bool Reset {
+      get { return reset_; }
       set {
-        load_ = value;
-        loadCase_ = value == null ? LoadOneofCase.None : LoadOneofCase.Pareto;
+        reset_ = value;
       }
     }
 
-    private object load_;
-    /// <summary>Enum of possible cases for the "load" oneof.</summary>
-    public enum LoadOneofCase {
-      None = 0,
-      ClosedLoop = 1,
-      Poisson = 2,
-      Uniform = 3,
-      Determ = 4,
-      Pareto = 5,
-    }
-    private LoadOneofCase loadCase_ = LoadOneofCase.None;
-    public LoadOneofCase LoadCase {
-      get { return loadCase_; }
-    }
-
-    public void ClearLoad() {
-      loadCase_ = LoadOneofCase.None;
-      load_ = null;
-    }
-
     public override bool Equals(object other) {
-      return Equals(other as LoadParams);
+      return Equals(other as Mark);
     }
 
-    public bool Equals(LoadParams other) {
+    public bool Equals(Mark other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (!object.Equals(ClosedLoop, other.ClosedLoop)) return false;
-      if (!object.Equals(Poisson, other.Poisson)) return false;
-      if (!object.Equals(Uniform, other.Uniform)) return false;
-      if (!object.Equals(Determ, other.Determ)) return false;
-      if (!object.Equals(Pareto, other.Pareto)) return false;
-      if (LoadCase != other.LoadCase) return false;
+      if (Reset != other.Reset) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (loadCase_ == LoadOneofCase.ClosedLoop) hash ^= ClosedLoop.GetHashCode();
-      if (loadCase_ == LoadOneofCase.Poisson) hash ^= Poisson.GetHashCode();
-      if (loadCase_ == LoadOneofCase.Uniform) hash ^= Uniform.GetHashCode();
-      if (loadCase_ == LoadOneofCase.Determ) hash ^= Determ.GetHashCode();
-      if (loadCase_ == LoadOneofCase.Pareto) hash ^= Pareto.GetHashCode();
-      hash ^= (int) loadCase_;
+      if (Reset != false) hash ^= Reset.GetHashCode();
       return hash;
     }
 
@@ -817,70 +1226,27 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (loadCase_ == LoadOneofCase.ClosedLoop) {
-        output.WriteRawTag(10);
-        output.WriteMessage(ClosedLoop);
-      }
-      if (loadCase_ == LoadOneofCase.Poisson) {
-        output.WriteRawTag(18);
-        output.WriteMessage(Poisson);
-      }
-      if (loadCase_ == LoadOneofCase.Uniform) {
-        output.WriteRawTag(26);
-        output.WriteMessage(Uniform);
-      }
-      if (loadCase_ == LoadOneofCase.Determ) {
-        output.WriteRawTag(34);
-        output.WriteMessage(Determ);
-      }
-      if (loadCase_ == LoadOneofCase.Pareto) {
-        output.WriteRawTag(42);
-        output.WriteMessage(Pareto);
+      if (Reset != false) {
+        output.WriteRawTag(8);
+        output.WriteBool(Reset);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (loadCase_ == LoadOneofCase.ClosedLoop) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ClosedLoop);
-      }
-      if (loadCase_ == LoadOneofCase.Poisson) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Poisson);
-      }
-      if (loadCase_ == LoadOneofCase.Uniform) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Uniform);
-      }
-      if (loadCase_ == LoadOneofCase.Determ) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Determ);
-      }
-      if (loadCase_ == LoadOneofCase.Pareto) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Pareto);
+      if (Reset != false) {
+        size += 1 + 1;
       }
       return size;
     }
 
-    public void MergeFrom(LoadParams other) {
+    public void MergeFrom(Mark other) {
       if (other == null) {
         return;
       }
-      switch (other.LoadCase) {
-        case LoadOneofCase.ClosedLoop:
-          ClosedLoop = other.ClosedLoop;
-          break;
-        case LoadOneofCase.Poisson:
-          Poisson = other.Poisson;
-          break;
-        case LoadOneofCase.Uniform:
-          Uniform = other.Uniform;
-          break;
-        case LoadOneofCase.Determ:
-          Determ = other.Determ;
-          break;
-        case LoadOneofCase.Pareto:
-          Pareto = other.Pareto;
-          break;
+      if (other.Reset != false) {
+        Reset = other.Reset;
       }
-
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -890,49 +1256,8 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 10: {
-            global::Grpc.Testing.ClosedLoopParams subBuilder = new global::Grpc.Testing.ClosedLoopParams();
-            if (loadCase_ == LoadOneofCase.ClosedLoop) {
-              subBuilder.MergeFrom(ClosedLoop);
-            }
-            input.ReadMessage(subBuilder);
-            ClosedLoop = subBuilder;
-            break;
-          }
-          case 18: {
-            global::Grpc.Testing.PoissonParams subBuilder = new global::Grpc.Testing.PoissonParams();
-            if (loadCase_ == LoadOneofCase.Poisson) {
-              subBuilder.MergeFrom(Poisson);
-            }
-            input.ReadMessage(subBuilder);
-            Poisson = subBuilder;
-            break;
-          }
-          case 26: {
-            global::Grpc.Testing.UniformParams subBuilder = new global::Grpc.Testing.UniformParams();
-            if (loadCase_ == LoadOneofCase.Uniform) {
-              subBuilder.MergeFrom(Uniform);
-            }
-            input.ReadMessage(subBuilder);
-            Uniform = subBuilder;
-            break;
-          }
-          case 34: {
-            global::Grpc.Testing.DeterministicParams subBuilder = new global::Grpc.Testing.DeterministicParams();
-            if (loadCase_ == LoadOneofCase.Determ) {
-              subBuilder.MergeFrom(Determ);
-            }
-            input.ReadMessage(subBuilder);
-            Determ = subBuilder;
-            break;
-          }
-          case 42: {
-            global::Grpc.Testing.ParetoParams subBuilder = new global::Grpc.Testing.ParetoParams();
-            if (loadCase_ == LoadOneofCase.Pareto) {
-              subBuilder.MergeFrom(Pareto);
-            }
-            input.ReadMessage(subBuilder);
-            Pareto = subBuilder;
+          case 8: {
+            Reset = input.ReadBool();
             break;
           }
         }
@@ -941,77 +1266,100 @@ namespace Grpc.Testing {
 
   }
 
-  /// <summary>
-  ///  presence of SecurityParams implies use of TLS
-  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class SecurityParams : pb::IMessage<SecurityParams> {
-    private static readonly pb::MessageParser<SecurityParams> _parser = new pb::MessageParser<SecurityParams>(() => new SecurityParams());
-    public static pb::MessageParser<SecurityParams> Parser { get { return _parser; } }
+  public sealed partial class ClientArgs : pb::IMessage<ClientArgs> {
+    private static readonly pb::MessageParser<ClientArgs> _parser = new pb::MessageParser<ClientArgs>(() => new ClientArgs());
+    public static pb::MessageParser<ClientArgs> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[6]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[7]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public SecurityParams() {
+    public ClientArgs() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public SecurityParams(SecurityParams other) : this() {
-      useTestCa_ = other.useTestCa_;
-      serverHostOverride_ = other.serverHostOverride_;
+    public ClientArgs(ClientArgs other) : this() {
+      switch (other.ArgtypeCase) {
+        case ArgtypeOneofCase.Setup:
+          Setup = other.Setup.Clone();
+          break;
+        case ArgtypeOneofCase.Mark:
+          Mark = other.Mark.Clone();
+          break;
+      }
+
     }
 
-    public SecurityParams Clone() {
-      return new SecurityParams(this);
+    public ClientArgs Clone() {
+      return new ClientArgs(this);
     }
 
-    /// <summary>Field number for the "use_test_ca" field.</summary>
-    public const int UseTestCaFieldNumber = 1;
-    private bool useTestCa_;
-    public bool UseTestCa {
-      get { return useTestCa_; }
+    /// <summary>Field number for the "setup" field.</summary>
+    public const int SetupFieldNumber = 1;
+    public global::Grpc.Testing.ClientConfig Setup {
+      get { return argtypeCase_ == ArgtypeOneofCase.Setup ? (global::Grpc.Testing.ClientConfig) argtype_ : null; }
+      set {
+        argtype_ = value;
+        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Setup;
+      }
+    }
+
+    /// <summary>Field number for the "mark" field.</summary>
+    public const int MarkFieldNumber = 2;
+    public global::Grpc.Testing.Mark Mark {
+      get { return argtypeCase_ == ArgtypeOneofCase.Mark ? (global::Grpc.Testing.Mark) argtype_ : null; }
       set {
-        useTestCa_ = value;
+        argtype_ = value;
+        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Mark;
       }
     }
 
-    /// <summary>Field number for the "server_host_override" field.</summary>
-    public const int ServerHostOverrideFieldNumber = 2;
-    private string serverHostOverride_ = "";
-    public string ServerHostOverride {
-      get { return serverHostOverride_; }
-      set {
-        serverHostOverride_ = pb::Preconditions.CheckNotNull(value, "value");
-      }
+    private object argtype_;
+    /// <summary>Enum of possible cases for the "argtype" oneof.</summary>
+    public enum ArgtypeOneofCase {
+      None = 0,
+      Setup = 1,
+      Mark = 2,
+    }
+    private ArgtypeOneofCase argtypeCase_ = ArgtypeOneofCase.None;
+    public ArgtypeOneofCase ArgtypeCase {
+      get { return argtypeCase_; }
+    }
+
+    public void ClearArgtype() {
+      argtypeCase_ = ArgtypeOneofCase.None;
+      argtype_ = null;
     }
 
     public override bool Equals(object other) {
-      return Equals(other as SecurityParams);
+      return Equals(other as ClientArgs);
     }
 
-    public bool Equals(SecurityParams other) {
+    public bool Equals(ClientArgs other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (UseTestCa != other.UseTestCa) return false;
-      if (ServerHostOverride != other.ServerHostOverride) return false;
+      if (!object.Equals(Setup, other.Setup)) return false;
+      if (!object.Equals(Mark, other.Mark)) return false;
+      if (ArgtypeCase != other.ArgtypeCase) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (UseTestCa != false) hash ^= UseTestCa.GetHashCode();
-      if (ServerHostOverride.Length != 0) hash ^= ServerHostOverride.GetHashCode();
+      if (argtypeCase_ == ArgtypeOneofCase.Setup) hash ^= Setup.GetHashCode();
+      if (argtypeCase_ == ArgtypeOneofCase.Mark) hash ^= Mark.GetHashCode();
+      hash ^= (int) argtypeCase_;
       return hash;
     }
 
@@ -1020,37 +1368,40 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (UseTestCa != false) {
-        output.WriteRawTag(8);
-        output.WriteBool(UseTestCa);
+      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Setup);
       }
-      if (ServerHostOverride.Length != 0) {
+      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
         output.WriteRawTag(18);
-        output.WriteString(ServerHostOverride);
+        output.WriteMessage(Mark);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (UseTestCa != false) {
-        size += 1 + 1;
+      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Setup);
       }
-      if (ServerHostOverride.Length != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeStringSize(ServerHostOverride);
+      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Mark);
       }
       return size;
     }
 
-    public void MergeFrom(SecurityParams other) {
+    public void MergeFrom(ClientArgs other) {
       if (other == null) {
         return;
       }
-      if (other.UseTestCa != false) {
-        UseTestCa = other.UseTestCa;
-      }
-      if (other.ServerHostOverride.Length != 0) {
-        ServerHostOverride = other.ServerHostOverride;
+      switch (other.ArgtypeCase) {
+        case ArgtypeOneofCase.Setup:
+          Setup = other.Setup;
+          break;
+        case ArgtypeOneofCase.Mark:
+          Mark = other.Mark;
+          break;
       }
+
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -1060,12 +1411,22 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 8: {
-            UseTestCa = input.ReadBool();
+          case 10: {
+            global::Grpc.Testing.ClientConfig subBuilder = new global::Grpc.Testing.ClientConfig();
+            if (argtypeCase_ == ArgtypeOneofCase.Setup) {
+              subBuilder.MergeFrom(Setup);
+            }
+            input.ReadMessage(subBuilder);
+            Setup = subBuilder;
             break;
           }
           case 18: {
-            ServerHostOverride = input.ReadString();
+            global::Grpc.Testing.Mark subBuilder = new global::Grpc.Testing.Mark();
+            if (argtypeCase_ == ArgtypeOneofCase.Mark) {
+              subBuilder.MergeFrom(Mark);
+            }
+            input.ReadMessage(subBuilder);
+            Mark = subBuilder;
             break;
           }
         }
@@ -1075,67 +1436,50 @@ namespace Grpc.Testing {
   }
 
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ClientConfig : pb::IMessage<ClientConfig> {
-    private static readonly pb::MessageParser<ClientConfig> _parser = new pb::MessageParser<ClientConfig>(() => new ClientConfig());
-    public static pb::MessageParser<ClientConfig> Parser { get { return _parser; } }
+  public sealed partial class ServerConfig : pb::IMessage<ServerConfig> {
+    private static readonly pb::MessageParser<ServerConfig> _parser = new pb::MessageParser<ServerConfig>(() => new ServerConfig());
+    public static pb::MessageParser<ServerConfig> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[7]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[8]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public ClientConfig() {
+    public ServerConfig() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ClientConfig(ClientConfig other) : this() {
-      serverTargets_ = other.serverTargets_.Clone();
-      clientType_ = other.clientType_;
+    public ServerConfig(ServerConfig other) : this() {
+      serverType_ = other.serverType_;
       SecurityParams = other.securityParams_ != null ? other.SecurityParams.Clone() : null;
-      outstandingRpcsPerChannel_ = other.outstandingRpcsPerChannel_;
-      clientChannels_ = other.clientChannels_;
-      asyncClientThreads_ = other.asyncClientThreads_;
-      rpcType_ = other.rpcType_;
-      LoadParams = other.loadParams_ != null ? other.LoadParams.Clone() : null;
+      port_ = other.port_;
+      asyncServerThreads_ = other.asyncServerThreads_;
+      coreLimit_ = other.coreLimit_;
       PayloadConfig = other.payloadConfig_ != null ? other.PayloadConfig.Clone() : null;
-      HistogramParams = other.histogramParams_ != null ? other.HistogramParams.Clone() : null;
       coreList_ = other.coreList_.Clone();
-      coreLimit_ = other.coreLimit_;
-    }
-
-    public ClientConfig Clone() {
-      return new ClientConfig(this);
     }
 
-    /// <summary>Field number for the "server_targets" field.</summary>
-    public const int ServerTargetsFieldNumber = 1;
-    private static readonly pb::FieldCodec<string> _repeated_serverTargets_codec
-        = pb::FieldCodec.ForString(10);
-    private readonly pbc::RepeatedField<string> serverTargets_ = new pbc::RepeatedField<string>();
-    /// <summary>
-    ///  List of targets to connect to. At least one target needs to be specified.
-    /// </summary>
-    public pbc::RepeatedField<string> ServerTargets {
-      get { return serverTargets_; }
+    public ServerConfig Clone() {
+      return new ServerConfig(this);
     }
 
-    /// <summary>Field number for the "client_type" field.</summary>
-    public const int ClientTypeFieldNumber = 2;
-    private global::Grpc.Testing.ClientType clientType_ = global::Grpc.Testing.ClientType.SYNC_CLIENT;
-    public global::Grpc.Testing.ClientType ClientType {
-      get { return clientType_; }
+    /// <summary>Field number for the "server_type" field.</summary>
+    public const int ServerTypeFieldNumber = 1;
+    private global::Grpc.Testing.ServerType serverType_ = global::Grpc.Testing.ServerType.SYNC_SERVER;
+    public global::Grpc.Testing.ServerType ServerType {
+      get { return serverType_; }
       set {
-        clientType_ = value;
+        serverType_ = value;
       }
     }
 
     /// <summary>Field number for the "security_params" field.</summary>
-    public const int SecurityParamsFieldNumber = 3;
+    public const int SecurityParamsFieldNumber = 2;
     private global::Grpc.Testing.SecurityParams securityParams_;
     public global::Grpc.Testing.SecurityParams SecurityParams {
       get { return securityParams_; }
@@ -1144,73 +1488,51 @@ namespace Grpc.Testing {
       }
     }
 
-    /// <summary>Field number for the "outstanding_rpcs_per_channel" field.</summary>
-    public const int OutstandingRpcsPerChannelFieldNumber = 4;
-    private int outstandingRpcsPerChannel_;
-    /// <summary>
-    ///  How many concurrent RPCs to start for each channel.
-    ///  For synchronous client, use a separate thread for each outstanding RPC.
-    /// </summary>
-    public int OutstandingRpcsPerChannel {
-      get { return outstandingRpcsPerChannel_; }
-      set {
-        outstandingRpcsPerChannel_ = value;
-      }
-    }
-
-    /// <summary>Field number for the "client_channels" field.</summary>
-    public const int ClientChannelsFieldNumber = 5;
-    private int clientChannels_;
+    /// <summary>Field number for the "port" field.</summary>
+    public const int PortFieldNumber = 4;
+    private int port_;
     /// <summary>
-    ///  Number of independent client channels to create.
-    ///  i-th channel will connect to server_target[i % server_targets.size()]
+    ///  Port on which to listen. Zero means pick unused port.
     /// </summary>
-    public int ClientChannels {
-      get { return clientChannels_; }
+    public int Port {
+      get { return port_; }
       set {
-        clientChannels_ = value;
+        port_ = value;
       }
     }
 
-    /// <summary>Field number for the "async_client_threads" field.</summary>
-    public const int AsyncClientThreadsFieldNumber = 7;
-    private int asyncClientThreads_;
+    /// <summary>Field number for the "async_server_threads" field.</summary>
+    public const int AsyncServerThreadsFieldNumber = 7;
+    private int asyncServerThreads_;
     /// <summary>
-    ///  Only for async client. Number of threads to use to start/manage RPCs.
+    ///  Only for async server. Number of threads used to serve the requests.
     /// </summary>
-    public int AsyncClientThreads {
-      get { return asyncClientThreads_; }
-      set {
-        asyncClientThreads_ = value;
-      }
-    }
-
-    /// <summary>Field number for the "rpc_type" field.</summary>
-    public const int RpcTypeFieldNumber = 8;
-    private global::Grpc.Testing.RpcType rpcType_ = global::Grpc.Testing.RpcType.UNARY;
-    public global::Grpc.Testing.RpcType RpcType {
-      get { return rpcType_; }
+    public int AsyncServerThreads {
+      get { return asyncServerThreads_; }
       set {
-        rpcType_ = value;
+        asyncServerThreads_ = value;
       }
     }
 
-    /// <summary>Field number for the "load_params" field.</summary>
-    public const int LoadParamsFieldNumber = 10;
-    private global::Grpc.Testing.LoadParams loadParams_;
+    /// <summary>Field number for the "core_limit" field.</summary>
+    public const int CoreLimitFieldNumber = 8;
+    private int coreLimit_;
     /// <summary>
-    ///  The requested load for the entire client (aggregated over all the threads).
+    ///  Specify the number of cores to limit server to, if desired
     /// </summary>
-    public global::Grpc.Testing.LoadParams LoadParams {
-      get { return loadParams_; }
+    public int CoreLimit {
+      get { return coreLimit_; }
       set {
-        loadParams_ = value;
+        coreLimit_ = value;
       }
     }
 
     /// <summary>Field number for the "payload_config" field.</summary>
-    public const int PayloadConfigFieldNumber = 11;
+    public const int PayloadConfigFieldNumber = 9;
     private global::Grpc.Testing.PayloadConfig payloadConfig_;
+    /// <summary>
+    ///  payload config, used in generic server
+    /// </summary>
     public global::Grpc.Testing.PayloadConfig PayloadConfig {
       get { return payloadConfig_; }
       set {
@@ -1218,78 +1540,48 @@ namespace Grpc.Testing {
       }
     }
 
-    /// <summary>Field number for the "histogram_params" field.</summary>
-    public const int HistogramParamsFieldNumber = 12;
-    private global::Grpc.Testing.HistogramParams histogramParams_;
-    public global::Grpc.Testing.HistogramParams HistogramParams {
-      get { return histogramParams_; }
-      set {
-        histogramParams_ = value;
-      }
-    }
-
     /// <summary>Field number for the "core_list" field.</summary>
-    public const int CoreListFieldNumber = 13;
+    public const int CoreListFieldNumber = 10;
     private static readonly pb::FieldCodec<int> _repeated_coreList_codec
-        = pb::FieldCodec.ForInt32(106);
+        = pb::FieldCodec.ForInt32(82);
     private readonly pbc::RepeatedField<int> coreList_ = new pbc::RepeatedField<int>();
     /// <summary>
-    ///  Specify the cores we should run the client on, if desired
+    ///  Specify the cores we should run the server on, if desired
     /// </summary>
     public pbc::RepeatedField<int> CoreList {
       get { return coreList_; }
     }
 
-    /// <summary>Field number for the "core_limit" field.</summary>
-    public const int CoreLimitFieldNumber = 14;
-    private int coreLimit_;
-    public int CoreLimit {
-      get { return coreLimit_; }
-      set {
-        coreLimit_ = value;
-      }
-    }
-
     public override bool Equals(object other) {
-      return Equals(other as ClientConfig);
+      return Equals(other as ServerConfig);
     }
 
-    public bool Equals(ClientConfig other) {
+    public bool Equals(ServerConfig other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if(!serverTargets_.Equals(other.serverTargets_)) return false;
-      if (ClientType != other.ClientType) return false;
+      if (ServerType != other.ServerType) return false;
       if (!object.Equals(SecurityParams, other.SecurityParams)) return false;
-      if (OutstandingRpcsPerChannel != other.OutstandingRpcsPerChannel) return false;
-      if (ClientChannels != other.ClientChannels) return false;
-      if (AsyncClientThreads != other.AsyncClientThreads) return false;
-      if (RpcType != other.RpcType) return false;
-      if (!object.Equals(LoadParams, other.LoadParams)) return false;
+      if (Port != other.Port) return false;
+      if (AsyncServerThreads != other.AsyncServerThreads) return false;
+      if (CoreLimit != other.CoreLimit) return false;
       if (!object.Equals(PayloadConfig, other.PayloadConfig)) return false;
-      if (!object.Equals(HistogramParams, other.HistogramParams)) return false;
       if(!coreList_.Equals(other.coreList_)) return false;
-      if (CoreLimit != other.CoreLimit) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      hash ^= serverTargets_.GetHashCode();
-      if (ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) hash ^= ClientType.GetHashCode();
+      if (ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) hash ^= ServerType.GetHashCode();
       if (securityParams_ != null) hash ^= SecurityParams.GetHashCode();
-      if (OutstandingRpcsPerChannel != 0) hash ^= OutstandingRpcsPerChannel.GetHashCode();
-      if (ClientChannels != 0) hash ^= ClientChannels.GetHashCode();
-      if (AsyncClientThreads != 0) hash ^= AsyncClientThreads.GetHashCode();
-      if (RpcType != global::Grpc.Testing.RpcType.UNARY) hash ^= RpcType.GetHashCode();
-      if (loadParams_ != null) hash ^= LoadParams.GetHashCode();
+      if (Port != 0) hash ^= Port.GetHashCode();
+      if (AsyncServerThreads != 0) hash ^= AsyncServerThreads.GetHashCode();
+      if (CoreLimit != 0) hash ^= CoreLimit.GetHashCode();
       if (payloadConfig_ != null) hash ^= PayloadConfig.GetHashCode();
-      if (histogramParams_ != null) hash ^= HistogramParams.GetHashCode();
       hash ^= coreList_.GetHashCode();
-      if (CoreLimit != 0) hash ^= CoreLimit.GetHashCode();
       return hash;
     }
 
@@ -1298,94 +1590,63 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      serverTargets_.WriteTo(output, _repeated_serverTargets_codec);
-      if (ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) {
-        output.WriteRawTag(16);
-        output.WriteEnum((int) ClientType);
+      if (ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) ServerType);
       }
       if (securityParams_ != null) {
-        output.WriteRawTag(26);
+        output.WriteRawTag(18);
         output.WriteMessage(SecurityParams);
       }
-      if (OutstandingRpcsPerChannel != 0) {
+      if (Port != 0) {
         output.WriteRawTag(32);
-        output.WriteInt32(OutstandingRpcsPerChannel);
-      }
-      if (ClientChannels != 0) {
-        output.WriteRawTag(40);
-        output.WriteInt32(ClientChannels);
+        output.WriteInt32(Port);
       }
-      if (AsyncClientThreads != 0) {
+      if (AsyncServerThreads != 0) {
         output.WriteRawTag(56);
-        output.WriteInt32(AsyncClientThreads);
+        output.WriteInt32(AsyncServerThreads);
       }
-      if (RpcType != global::Grpc.Testing.RpcType.UNARY) {
+      if (CoreLimit != 0) {
         output.WriteRawTag(64);
-        output.WriteEnum((int) RpcType);
-      }
-      if (loadParams_ != null) {
-        output.WriteRawTag(82);
-        output.WriteMessage(LoadParams);
+        output.WriteInt32(CoreLimit);
       }
       if (payloadConfig_ != null) {
-        output.WriteRawTag(90);
+        output.WriteRawTag(74);
         output.WriteMessage(PayloadConfig);
       }
-      if (histogramParams_ != null) {
-        output.WriteRawTag(98);
-        output.WriteMessage(HistogramParams);
-      }
       coreList_.WriteTo(output, _repeated_coreList_codec);
-      if (CoreLimit != 0) {
-        output.WriteRawTag(112);
-        output.WriteInt32(CoreLimit);
-      }
     }
 
     public int CalculateSize() {
       int size = 0;
-      size += serverTargets_.CalculateSize(_repeated_serverTargets_codec);
-      if (ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) {
-        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ClientType);
+      if (ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ServerType);
       }
       if (securityParams_ != null) {
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(SecurityParams);
       }
-      if (OutstandingRpcsPerChannel != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(OutstandingRpcsPerChannel);
-      }
-      if (ClientChannels != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(ClientChannels);
-      }
-      if (AsyncClientThreads != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(AsyncClientThreads);
+      if (Port != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Port);
       }
-      if (RpcType != global::Grpc.Testing.RpcType.UNARY) {
-        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RpcType);
+      if (AsyncServerThreads != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(AsyncServerThreads);
       }
-      if (loadParams_ != null) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LoadParams);
+      if (CoreLimit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(CoreLimit);
       }
       if (payloadConfig_ != null) {
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(PayloadConfig);
       }
-      if (histogramParams_ != null) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(HistogramParams);
-      }
       size += coreList_.CalculateSize(_repeated_coreList_codec);
-      if (CoreLimit != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(CoreLimit);
-      }
       return size;
     }
 
-    public void MergeFrom(ClientConfig other) {
+    public void MergeFrom(ServerConfig other) {
       if (other == null) {
         return;
       }
-      serverTargets_.Add(other.serverTargets_);
-      if (other.ClientType != global::Grpc.Testing.ClientType.SYNC_CLIENT) {
-        ClientType = other.ClientType;
+      if (other.ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) {
+        ServerType = other.ServerType;
       }
       if (other.securityParams_ != null) {
         if (securityParams_ == null) {
@@ -1393,23 +1654,14 @@ namespace Grpc.Testing {
         }
         SecurityParams.MergeFrom(other.SecurityParams);
       }
-      if (other.OutstandingRpcsPerChannel != 0) {
-        OutstandingRpcsPerChannel = other.OutstandingRpcsPerChannel;
-      }
-      if (other.ClientChannels != 0) {
-        ClientChannels = other.ClientChannels;
-      }
-      if (other.AsyncClientThreads != 0) {
-        AsyncClientThreads = other.AsyncClientThreads;
+      if (other.Port != 0) {
+        Port = other.Port;
       }
-      if (other.RpcType != global::Grpc.Testing.RpcType.UNARY) {
-        RpcType = other.RpcType;
+      if (other.AsyncServerThreads != 0) {
+        AsyncServerThreads = other.AsyncServerThreads;
       }
-      if (other.loadParams_ != null) {
-        if (loadParams_ == null) {
-          loadParams_ = new global::Grpc.Testing.LoadParams();
-        }
-        LoadParams.MergeFrom(other.LoadParams);
+      if (other.CoreLimit != 0) {
+        CoreLimit = other.CoreLimit;
       }
       if (other.payloadConfig_ != null) {
         if (payloadConfig_ == null) {
@@ -1417,16 +1669,7 @@ namespace Grpc.Testing {
         }
         PayloadConfig.MergeFrom(other.PayloadConfig);
       }
-      if (other.histogramParams_ != null) {
-        if (histogramParams_ == null) {
-          histogramParams_ = new global::Grpc.Testing.HistogramParams();
-        }
-        HistogramParams.MergeFrom(other.HistogramParams);
-      }
       coreList_.Add(other.coreList_);
-      if (other.CoreLimit != 0) {
-        CoreLimit = other.CoreLimit;
-      }
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -1436,15 +1679,11 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 10: {
-            serverTargets_.AddEntriesFrom(input, _repeated_serverTargets_codec);
-            break;
-          }
-          case 16: {
-            clientType_ = (global::Grpc.Testing.ClientType) input.ReadEnum();
+          case 8: {
+            serverType_ = (global::Grpc.Testing.ServerType) input.ReadEnum();
             break;
           }
-          case 26: {
+          case 18: {
             if (securityParams_ == null) {
               securityParams_ = new global::Grpc.Testing.SecurityParams();
             }
@@ -1452,51 +1691,29 @@ namespace Grpc.Testing {
             break;
           }
           case 32: {
-            OutstandingRpcsPerChannel = input.ReadInt32();
-            break;
-          }
-          case 40: {
-            ClientChannels = input.ReadInt32();
+            Port = input.ReadInt32();
             break;
           }
           case 56: {
-            AsyncClientThreads = input.ReadInt32();
+            AsyncServerThreads = input.ReadInt32();
             break;
           }
           case 64: {
-            rpcType_ = (global::Grpc.Testing.RpcType) input.ReadEnum();
-            break;
-          }
-          case 82: {
-            if (loadParams_ == null) {
-              loadParams_ = new global::Grpc.Testing.LoadParams();
-            }
-            input.ReadMessage(loadParams_);
+            CoreLimit = input.ReadInt32();
             break;
           }
-          case 90: {
+          case 74: {
             if (payloadConfig_ == null) {
               payloadConfig_ = new global::Grpc.Testing.PayloadConfig();
             }
             input.ReadMessage(payloadConfig_);
             break;
           }
-          case 98: {
-            if (histogramParams_ == null) {
-              histogramParams_ = new global::Grpc.Testing.HistogramParams();
-            }
-            input.ReadMessage(histogramParams_);
-            break;
-          }
-          case 106:
-          case 104: {
+          case 82:
+          case 80: {
             coreList_.AddEntriesFrom(input, _repeated_coreList_codec);
             break;
           }
-          case 112: {
-            CoreLimit = input.ReadInt32();
-            break;
-          }
         }
       }
     }
@@ -1504,60 +1721,99 @@ namespace Grpc.Testing {
   }
 
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ClientStatus : pb::IMessage<ClientStatus> {
-    private static readonly pb::MessageParser<ClientStatus> _parser = new pb::MessageParser<ClientStatus>(() => new ClientStatus());
-    public static pb::MessageParser<ClientStatus> Parser { get { return _parser; } }
+  public sealed partial class ServerArgs : pb::IMessage<ServerArgs> {
+    private static readonly pb::MessageParser<ServerArgs> _parser = new pb::MessageParser<ServerArgs>(() => new ServerArgs());
+    public static pb::MessageParser<ServerArgs> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[8]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[9]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public ClientStatus() {
+    public ServerArgs() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ClientStatus(ClientStatus other) : this() {
-      Stats = other.stats_ != null ? other.Stats.Clone() : null;
+    public ServerArgs(ServerArgs other) : this() {
+      switch (other.ArgtypeCase) {
+        case ArgtypeOneofCase.Setup:
+          Setup = other.Setup.Clone();
+          break;
+        case ArgtypeOneofCase.Mark:
+          Mark = other.Mark.Clone();
+          break;
+      }
+
     }
 
-    public ClientStatus Clone() {
-      return new ClientStatus(this);
+    public ServerArgs Clone() {
+      return new ServerArgs(this);
     }
 
-    /// <summary>Field number for the "stats" field.</summary>
-    public const int StatsFieldNumber = 1;
-    private global::Grpc.Testing.ClientStats stats_;
-    public global::Grpc.Testing.ClientStats Stats {
-      get { return stats_; }
+    /// <summary>Field number for the "setup" field.</summary>
+    public const int SetupFieldNumber = 1;
+    public global::Grpc.Testing.ServerConfig Setup {
+      get { return argtypeCase_ == ArgtypeOneofCase.Setup ? (global::Grpc.Testing.ServerConfig) argtype_ : null; }
       set {
-        stats_ = value;
+        argtype_ = value;
+        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Setup;
+      }
+    }
+
+    /// <summary>Field number for the "mark" field.</summary>
+    public const int MarkFieldNumber = 2;
+    public global::Grpc.Testing.Mark Mark {
+      get { return argtypeCase_ == ArgtypeOneofCase.Mark ? (global::Grpc.Testing.Mark) argtype_ : null; }
+      set {
+        argtype_ = value;
+        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Mark;
       }
     }
 
+    private object argtype_;
+    /// <summary>Enum of possible cases for the "argtype" oneof.</summary>
+    public enum ArgtypeOneofCase {
+      None = 0,
+      Setup = 1,
+      Mark = 2,
+    }
+    private ArgtypeOneofCase argtypeCase_ = ArgtypeOneofCase.None;
+    public ArgtypeOneofCase ArgtypeCase {
+      get { return argtypeCase_; }
+    }
+
+    public void ClearArgtype() {
+      argtypeCase_ = ArgtypeOneofCase.None;
+      argtype_ = null;
+    }
+
     public override bool Equals(object other) {
-      return Equals(other as ClientStatus);
+      return Equals(other as ServerArgs);
     }
 
-    public bool Equals(ClientStatus other) {
+    public bool Equals(ServerArgs other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (!object.Equals(Stats, other.Stats)) return false;
+      if (!object.Equals(Setup, other.Setup)) return false;
+      if (!object.Equals(Mark, other.Mark)) return false;
+      if (ArgtypeCase != other.ArgtypeCase) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (stats_ != null) hash ^= Stats.GetHashCode();
+      if (argtypeCase_ == ArgtypeOneofCase.Setup) hash ^= Setup.GetHashCode();
+      if (argtypeCase_ == ArgtypeOneofCase.Mark) hash ^= Mark.GetHashCode();
+      hash ^= (int) argtypeCase_;
       return hash;
     }
 
@@ -1566,30 +1822,40 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (stats_ != null) {
+      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
         output.WriteRawTag(10);
-        output.WriteMessage(Stats);
+        output.WriteMessage(Setup);
+      }
+      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Mark);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (stats_ != null) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Stats);
+      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Setup);
+      }
+      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Mark);
       }
       return size;
     }
 
-    public void MergeFrom(ClientStatus other) {
+    public void MergeFrom(ServerArgs other) {
       if (other == null) {
         return;
       }
-      if (other.stats_ != null) {
-        if (stats_ == null) {
-          stats_ = new global::Grpc.Testing.ClientStats();
-        }
-        Stats.MergeFrom(other.Stats);
+      switch (other.ArgtypeCase) {
+        case ArgtypeOneofCase.Setup:
+          Setup = other.Setup;
+          break;
+        case ArgtypeOneofCase.Mark:
+          Mark = other.Mark;
+          break;
       }
+
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -1600,10 +1866,21 @@ namespace Grpc.Testing {
             input.SkipLastField();
             break;
           case 10: {
-            if (stats_ == null) {
-              stats_ = new global::Grpc.Testing.ClientStats();
+            global::Grpc.Testing.ServerConfig subBuilder = new global::Grpc.Testing.ServerConfig();
+            if (argtypeCase_ == ArgtypeOneofCase.Setup) {
+              subBuilder.MergeFrom(Setup);
             }
-            input.ReadMessage(stats_);
+            input.ReadMessage(subBuilder);
+            Setup = subBuilder;
+            break;
+          }
+          case 18: {
+            global::Grpc.Testing.Mark subBuilder = new global::Grpc.Testing.Mark();
+            if (argtypeCase_ == ArgtypeOneofCase.Mark) {
+              subBuilder.MergeFrom(Mark);
+            }
+            input.ReadMessage(subBuilder);
+            Mark = subBuilder;
             break;
           }
         }
@@ -1612,67 +1889,93 @@ namespace Grpc.Testing {
 
   }
 
-  /// <summary>
-  ///  Request current stats
-  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class Mark : pb::IMessage<Mark> {
-    private static readonly pb::MessageParser<Mark> _parser = new pb::MessageParser<Mark>(() => new Mark());
-    public static pb::MessageParser<Mark> Parser { get { return _parser; } }
+  public sealed partial class ServerStatus : pb::IMessage<ServerStatus> {
+    private static readonly pb::MessageParser<ServerStatus> _parser = new pb::MessageParser<ServerStatus>(() => new ServerStatus());
+    public static pb::MessageParser<ServerStatus> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[9]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[10]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public Mark() {
+    public ServerStatus() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public Mark(Mark other) : this() {
-      reset_ = other.reset_;
+    public ServerStatus(ServerStatus other) : this() {
+      Stats = other.stats_ != null ? other.Stats.Clone() : null;
+      port_ = other.port_;
+      cores_ = other.cores_;
     }
 
-    public Mark Clone() {
-      return new Mark(this);
+    public ServerStatus Clone() {
+      return new ServerStatus(this);
     }
 
-    /// <summary>Field number for the "reset" field.</summary>
-    public const int ResetFieldNumber = 1;
-    private bool reset_;
+    /// <summary>Field number for the "stats" field.</summary>
+    public const int StatsFieldNumber = 1;
+    private global::Grpc.Testing.ServerStats stats_;
+    public global::Grpc.Testing.ServerStats Stats {
+      get { return stats_; }
+      set {
+        stats_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "port" field.</summary>
+    public const int PortFieldNumber = 2;
+    private int port_;
     /// <summary>
-    ///  if true, the stats will be reset after taking their snapshot.
+    ///  the port bound by the server
     /// </summary>
-    public bool Reset {
-      get { return reset_; }
+    public int Port {
+      get { return port_; }
       set {
-        reset_ = value;
+        port_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "cores" field.</summary>
+    public const int CoresFieldNumber = 3;
+    private int cores_;
+    /// <summary>
+    ///  Number of cores available to the server
+    /// </summary>
+    public int Cores {
+      get { return cores_; }
+      set {
+        cores_ = value;
       }
     }
 
     public override bool Equals(object other) {
-      return Equals(other as Mark);
+      return Equals(other as ServerStatus);
     }
 
-    public bool Equals(Mark other) {
+    public bool Equals(ServerStatus other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (Reset != other.Reset) return false;
+      if (!object.Equals(Stats, other.Stats)) return false;
+      if (Port != other.Port) return false;
+      if (Cores != other.Cores) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (Reset != false) hash ^= Reset.GetHashCode();
+      if (stats_ != null) hash ^= Stats.GetHashCode();
+      if (Port != 0) hash ^= Port.GetHashCode();
+      if (Cores != 0) hash ^= Cores.GetHashCode();
       return hash;
     }
 
@@ -1681,26 +1984,49 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (Reset != false) {
-        output.WriteRawTag(8);
-        output.WriteBool(Reset);
+      if (stats_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Stats);
+      }
+      if (Port != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Port);
+      }
+      if (Cores != 0) {
+        output.WriteRawTag(24);
+        output.WriteInt32(Cores);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (Reset != false) {
-        size += 1 + 1;
+      if (stats_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Stats);
+      }
+      if (Port != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Port);
+      }
+      if (Cores != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Cores);
       }
       return size;
     }
 
-    public void MergeFrom(Mark other) {
+    public void MergeFrom(ServerStatus other) {
       if (other == null) {
         return;
       }
-      if (other.Reset != false) {
-        Reset = other.Reset;
+      if (other.stats_ != null) {
+        if (stats_ == null) {
+          stats_ = new global::Grpc.Testing.ServerStats();
+        }
+        Stats.MergeFrom(other.Stats);
+      }
+      if (other.Port != 0) {
+        Port = other.Port;
+      }
+      if (other.Cores != 0) {
+        Cores = other.Cores;
       }
     }
 
@@ -1711,8 +2037,19 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 8: {
-            Reset = input.ReadBool();
+          case 10: {
+            if (stats_ == null) {
+              stats_ = new global::Grpc.Testing.ServerStats();
+            }
+            input.ReadMessage(stats_);
+            break;
+          }
+          case 16: {
+            Port = input.ReadInt32();
+            break;
+          }
+          case 24: {
+            Cores = input.ReadInt32();
             break;
           }
         }
@@ -1722,99 +2059,47 @@ namespace Grpc.Testing {
   }
 
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ClientArgs : pb::IMessage<ClientArgs> {
-    private static readonly pb::MessageParser<ClientArgs> _parser = new pb::MessageParser<ClientArgs>(() => new ClientArgs());
-    public static pb::MessageParser<ClientArgs> Parser { get { return _parser; } }
+  public sealed partial class CoreRequest : pb::IMessage<CoreRequest> {
+    private static readonly pb::MessageParser<CoreRequest> _parser = new pb::MessageParser<CoreRequest>(() => new CoreRequest());
+    public static pb::MessageParser<CoreRequest> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[10]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[11]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public ClientArgs() {
+    public CoreRequest() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ClientArgs(ClientArgs other) : this() {
-      switch (other.ArgtypeCase) {
-        case ArgtypeOneofCase.Setup:
-          Setup = other.Setup.Clone();
-          break;
-        case ArgtypeOneofCase.Mark:
-          Mark = other.Mark.Clone();
-          break;
-      }
-
-    }
-
-    public ClientArgs Clone() {
-      return new ClientArgs(this);
-    }
-
-    /// <summary>Field number for the "setup" field.</summary>
-    public const int SetupFieldNumber = 1;
-    public global::Grpc.Testing.ClientConfig Setup {
-      get { return argtypeCase_ == ArgtypeOneofCase.Setup ? (global::Grpc.Testing.ClientConfig) argtype_ : null; }
-      set {
-        argtype_ = value;
-        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Setup;
-      }
-    }
-
-    /// <summary>Field number for the "mark" field.</summary>
-    public const int MarkFieldNumber = 2;
-    public global::Grpc.Testing.Mark Mark {
-      get { return argtypeCase_ == ArgtypeOneofCase.Mark ? (global::Grpc.Testing.Mark) argtype_ : null; }
-      set {
-        argtype_ = value;
-        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Mark;
-      }
-    }
-
-    private object argtype_;
-    /// <summary>Enum of possible cases for the "argtype" oneof.</summary>
-    public enum ArgtypeOneofCase {
-      None = 0,
-      Setup = 1,
-      Mark = 2,
-    }
-    private ArgtypeOneofCase argtypeCase_ = ArgtypeOneofCase.None;
-    public ArgtypeOneofCase ArgtypeCase {
-      get { return argtypeCase_; }
+    public CoreRequest(CoreRequest other) : this() {
     }
 
-    public void ClearArgtype() {
-      argtypeCase_ = ArgtypeOneofCase.None;
-      argtype_ = null;
+    public CoreRequest Clone() {
+      return new CoreRequest(this);
     }
 
     public override bool Equals(object other) {
-      return Equals(other as ClientArgs);
+      return Equals(other as CoreRequest);
     }
 
-    public bool Equals(ClientArgs other) {
+    public bool Equals(CoreRequest other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (!object.Equals(Setup, other.Setup)) return false;
-      if (!object.Equals(Mark, other.Mark)) return false;
-      if (ArgtypeCase != other.ArgtypeCase) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (argtypeCase_ == ArgtypeOneofCase.Setup) hash ^= Setup.GetHashCode();
-      if (argtypeCase_ == ArgtypeOneofCase.Mark) hash ^= Mark.GetHashCode();
-      hash ^= (int) argtypeCase_;
       return hash;
     }
 
@@ -1823,40 +2108,17 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
-        output.WriteRawTag(10);
-        output.WriteMessage(Setup);
-      }
-      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
-        output.WriteRawTag(18);
-        output.WriteMessage(Mark);
-      }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Setup);
-      }
-      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Mark);
-      }
       return size;
     }
 
-    public void MergeFrom(ClientArgs other) {
+    public void MergeFrom(CoreRequest other) {
       if (other == null) {
-        return;
-      }
-      switch (other.ArgtypeCase) {
-        case ArgtypeOneofCase.Setup:
-          Setup = other.Setup;
-          break;
-        case ArgtypeOneofCase.Mark:
-          Mark = other.Mark;
-          break;
+        return;
       }
-
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -1866,24 +2128,6 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 10: {
-            global::Grpc.Testing.ClientConfig subBuilder = new global::Grpc.Testing.ClientConfig();
-            if (argtypeCase_ == ArgtypeOneofCase.Setup) {
-              subBuilder.MergeFrom(Setup);
-            }
-            input.ReadMessage(subBuilder);
-            Setup = subBuilder;
-            break;
-          }
-          case 18: {
-            global::Grpc.Testing.Mark subBuilder = new global::Grpc.Testing.Mark();
-            if (argtypeCase_ == ArgtypeOneofCase.Mark) {
-              subBuilder.MergeFrom(Mark);
-            }
-            input.ReadMessage(subBuilder);
-            Mark = subBuilder;
-            break;
-          }
         }
       }
     }
@@ -1891,152 +2135,63 @@ namespace Grpc.Testing {
   }
 
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ServerConfig : pb::IMessage<ServerConfig> {
-    private static readonly pb::MessageParser<ServerConfig> _parser = new pb::MessageParser<ServerConfig>(() => new ServerConfig());
-    public static pb::MessageParser<ServerConfig> Parser { get { return _parser; } }
+  public sealed partial class CoreResponse : pb::IMessage<CoreResponse> {
+    private static readonly pb::MessageParser<CoreResponse> _parser = new pb::MessageParser<CoreResponse>(() => new CoreResponse());
+    public static pb::MessageParser<CoreResponse> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[11]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[12]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public ServerConfig() {
+    public CoreResponse() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ServerConfig(ServerConfig other) : this() {
-      serverType_ = other.serverType_;
-      SecurityParams = other.securityParams_ != null ? other.SecurityParams.Clone() : null;
-      port_ = other.port_;
-      asyncServerThreads_ = other.asyncServerThreads_;
-      coreLimit_ = other.coreLimit_;
-      PayloadConfig = other.payloadConfig_ != null ? other.PayloadConfig.Clone() : null;
-      coreList_ = other.coreList_.Clone();
-    }
-
-    public ServerConfig Clone() {
-      return new ServerConfig(this);
-    }
-
-    /// <summary>Field number for the "server_type" field.</summary>
-    public const int ServerTypeFieldNumber = 1;
-    private global::Grpc.Testing.ServerType serverType_ = global::Grpc.Testing.ServerType.SYNC_SERVER;
-    public global::Grpc.Testing.ServerType ServerType {
-      get { return serverType_; }
-      set {
-        serverType_ = value;
-      }
-    }
-
-    /// <summary>Field number for the "security_params" field.</summary>
-    public const int SecurityParamsFieldNumber = 2;
-    private global::Grpc.Testing.SecurityParams securityParams_;
-    public global::Grpc.Testing.SecurityParams SecurityParams {
-      get { return securityParams_; }
-      set {
-        securityParams_ = value;
-      }
-    }
-
-    /// <summary>Field number for the "port" field.</summary>
-    public const int PortFieldNumber = 4;
-    private int port_;
-    /// <summary>
-    ///  Port on which to listen. Zero means pick unused port.
-    /// </summary>
-    public int Port {
-      get { return port_; }
-      set {
-        port_ = value;
-      }
-    }
-
-    /// <summary>Field number for the "async_server_threads" field.</summary>
-    public const int AsyncServerThreadsFieldNumber = 7;
-    private int asyncServerThreads_;
-    /// <summary>
-    ///  Only for async server. Number of threads used to serve the requests.
-    /// </summary>
-    public int AsyncServerThreads {
-      get { return asyncServerThreads_; }
-      set {
-        asyncServerThreads_ = value;
-      }
+    public CoreResponse(CoreResponse other) : this() {
+      cores_ = other.cores_;
     }
 
-    /// <summary>Field number for the "core_limit" field.</summary>
-    public const int CoreLimitFieldNumber = 8;
-    private int coreLimit_;
-    /// <summary>
-    ///  Specify the number of cores to limit server to, if desired
-    /// </summary>
-    public int CoreLimit {
-      get { return coreLimit_; }
-      set {
-        coreLimit_ = value;
-      }
+    public CoreResponse Clone() {
+      return new CoreResponse(this);
     }
 
-    /// <summary>Field number for the "payload_config" field.</summary>
-    public const int PayloadConfigFieldNumber = 9;
-    private global::Grpc.Testing.PayloadConfig payloadConfig_;
+    /// <summary>Field number for the "cores" field.</summary>
+    public const int CoresFieldNumber = 1;
+    private int cores_;
     /// <summary>
-    ///  payload config, used in generic server
+    ///  Number of cores available on the server
     /// </summary>
-    public global::Grpc.Testing.PayloadConfig PayloadConfig {
-      get { return payloadConfig_; }
+    public int Cores {
+      get { return cores_; }
       set {
-        payloadConfig_ = value;
+        cores_ = value;
       }
     }
 
-    /// <summary>Field number for the "core_list" field.</summary>
-    public const int CoreListFieldNumber = 10;
-    private static readonly pb::FieldCodec<int> _repeated_coreList_codec
-        = pb::FieldCodec.ForInt32(82);
-    private readonly pbc::RepeatedField<int> coreList_ = new pbc::RepeatedField<int>();
-    /// <summary>
-    ///  Specify the cores we should run the server on, if desired
-    /// </summary>
-    public pbc::RepeatedField<int> CoreList {
-      get { return coreList_; }
-    }
-
     public override bool Equals(object other) {
-      return Equals(other as ServerConfig);
+      return Equals(other as CoreResponse);
     }
 
-    public bool Equals(ServerConfig other) {
+    public bool Equals(CoreResponse other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (ServerType != other.ServerType) return false;
-      if (!object.Equals(SecurityParams, other.SecurityParams)) return false;
-      if (Port != other.Port) return false;
-      if (AsyncServerThreads != other.AsyncServerThreads) return false;
-      if (CoreLimit != other.CoreLimit) return false;
-      if (!object.Equals(PayloadConfig, other.PayloadConfig)) return false;
-      if(!coreList_.Equals(other.coreList_)) return false;
+      if (Cores != other.Cores) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) hash ^= ServerType.GetHashCode();
-      if (securityParams_ != null) hash ^= SecurityParams.GetHashCode();
-      if (Port != 0) hash ^= Port.GetHashCode();
-      if (AsyncServerThreads != 0) hash ^= AsyncServerThreads.GetHashCode();
-      if (CoreLimit != 0) hash ^= CoreLimit.GetHashCode();
-      if (payloadConfig_ != null) hash ^= PayloadConfig.GetHashCode();
-      hash ^= coreList_.GetHashCode();
+      if (Cores != 0) hash ^= Cores.GetHashCode();
       return hash;
     }
 
@@ -2045,128 +2200,38 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) {
+      if (Cores != 0) {
         output.WriteRawTag(8);
-        output.WriteEnum((int) ServerType);
-      }
-      if (securityParams_ != null) {
-        output.WriteRawTag(18);
-        output.WriteMessage(SecurityParams);
-      }
-      if (Port != 0) {
-        output.WriteRawTag(32);
-        output.WriteInt32(Port);
-      }
-      if (AsyncServerThreads != 0) {
-        output.WriteRawTag(56);
-        output.WriteInt32(AsyncServerThreads);
-      }
-      if (CoreLimit != 0) {
-        output.WriteRawTag(64);
-        output.WriteInt32(CoreLimit);
-      }
-      if (payloadConfig_ != null) {
-        output.WriteRawTag(74);
-        output.WriteMessage(PayloadConfig);
+        output.WriteInt32(Cores);
       }
-      coreList_.WriteTo(output, _repeated_coreList_codec);
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) {
-        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ServerType);
-      }
-      if (securityParams_ != null) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(SecurityParams);
-      }
-      if (Port != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Port);
-      }
-      if (AsyncServerThreads != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(AsyncServerThreads);
-      }
-      if (CoreLimit != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(CoreLimit);
-      }
-      if (payloadConfig_ != null) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(PayloadConfig);
+      if (Cores != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Cores);
       }
-      size += coreList_.CalculateSize(_repeated_coreList_codec);
       return size;
     }
 
-    public void MergeFrom(ServerConfig other) {
+    public void MergeFrom(CoreResponse other) {
       if (other == null) {
         return;
       }
-      if (other.ServerType != global::Grpc.Testing.ServerType.SYNC_SERVER) {
-        ServerType = other.ServerType;
-      }
-      if (other.securityParams_ != null) {
-        if (securityParams_ == null) {
-          securityParams_ = new global::Grpc.Testing.SecurityParams();
-        }
-        SecurityParams.MergeFrom(other.SecurityParams);
-      }
-      if (other.Port != 0) {
-        Port = other.Port;
-      }
-      if (other.AsyncServerThreads != 0) {
-        AsyncServerThreads = other.AsyncServerThreads;
-      }
-      if (other.CoreLimit != 0) {
-        CoreLimit = other.CoreLimit;
-      }
-      if (other.payloadConfig_ != null) {
-        if (payloadConfig_ == null) {
-          payloadConfig_ = new global::Grpc.Testing.PayloadConfig();
-        }
-        PayloadConfig.MergeFrom(other.PayloadConfig);
+      if (other.Cores != 0) {
+        Cores = other.Cores;
       }
-      coreList_.Add(other.coreList_);
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
-      uint tag;
-      while ((tag = input.ReadTag()) != 0) {
-        switch(tag) {
-          default:
-            input.SkipLastField();
-            break;
-          case 8: {
-            serverType_ = (global::Grpc.Testing.ServerType) input.ReadEnum();
-            break;
-          }
-          case 18: {
-            if (securityParams_ == null) {
-              securityParams_ = new global::Grpc.Testing.SecurityParams();
-            }
-            input.ReadMessage(securityParams_);
-            break;
-          }
-          case 32: {
-            Port = input.ReadInt32();
-            break;
-          }
-          case 56: {
-            AsyncServerThreads = input.ReadInt32();
-            break;
-          }
-          case 64: {
-            CoreLimit = input.ReadInt32();
-            break;
-          }
-          case 74: {
-            if (payloadConfig_ == null) {
-              payloadConfig_ = new global::Grpc.Testing.PayloadConfig();
-            }
-            input.ReadMessage(payloadConfig_);
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
             break;
-          }
-          case 82:
-          case 80: {
-            coreList_.AddEntriesFrom(input, _repeated_coreList_codec);
+          case 8: {
+            Cores = input.ReadInt32();
             break;
           }
         }
@@ -2176,99 +2241,47 @@ namespace Grpc.Testing {
   }
 
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ServerArgs : pb::IMessage<ServerArgs> {
-    private static readonly pb::MessageParser<ServerArgs> _parser = new pb::MessageParser<ServerArgs>(() => new ServerArgs());
-    public static pb::MessageParser<ServerArgs> Parser { get { return _parser; } }
+  public sealed partial class Void : pb::IMessage<Void> {
+    private static readonly pb::MessageParser<Void> _parser = new pb::MessageParser<Void>(() => new Void());
+    public static pb::MessageParser<Void> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[12]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[13]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public ServerArgs() {
+    public Void() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ServerArgs(ServerArgs other) : this() {
-      switch (other.ArgtypeCase) {
-        case ArgtypeOneofCase.Setup:
-          Setup = other.Setup.Clone();
-          break;
-        case ArgtypeOneofCase.Mark:
-          Mark = other.Mark.Clone();
-          break;
-      }
-
-    }
-
-    public ServerArgs Clone() {
-      return new ServerArgs(this);
-    }
-
-    /// <summary>Field number for the "setup" field.</summary>
-    public const int SetupFieldNumber = 1;
-    public global::Grpc.Testing.ServerConfig Setup {
-      get { return argtypeCase_ == ArgtypeOneofCase.Setup ? (global::Grpc.Testing.ServerConfig) argtype_ : null; }
-      set {
-        argtype_ = value;
-        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Setup;
-      }
-    }
-
-    /// <summary>Field number for the "mark" field.</summary>
-    public const int MarkFieldNumber = 2;
-    public global::Grpc.Testing.Mark Mark {
-      get { return argtypeCase_ == ArgtypeOneofCase.Mark ? (global::Grpc.Testing.Mark) argtype_ : null; }
-      set {
-        argtype_ = value;
-        argtypeCase_ = value == null ? ArgtypeOneofCase.None : ArgtypeOneofCase.Mark;
-      }
-    }
-
-    private object argtype_;
-    /// <summary>Enum of possible cases for the "argtype" oneof.</summary>
-    public enum ArgtypeOneofCase {
-      None = 0,
-      Setup = 1,
-      Mark = 2,
-    }
-    private ArgtypeOneofCase argtypeCase_ = ArgtypeOneofCase.None;
-    public ArgtypeOneofCase ArgtypeCase {
-      get { return argtypeCase_; }
+    public Void(Void other) : this() {
     }
 
-    public void ClearArgtype() {
-      argtypeCase_ = ArgtypeOneofCase.None;
-      argtype_ = null;
+    public Void Clone() {
+      return new Void(this);
     }
 
     public override bool Equals(object other) {
-      return Equals(other as ServerArgs);
+      return Equals(other as Void);
     }
 
-    public bool Equals(ServerArgs other) {
+    public bool Equals(Void other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (!object.Equals(Setup, other.Setup)) return false;
-      if (!object.Equals(Mark, other.Mark)) return false;
-      if (ArgtypeCase != other.ArgtypeCase) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (argtypeCase_ == ArgtypeOneofCase.Setup) hash ^= Setup.GetHashCode();
-      if (argtypeCase_ == ArgtypeOneofCase.Mark) hash ^= Mark.GetHashCode();
-      hash ^= (int) argtypeCase_;
       return hash;
     }
 
@@ -2277,40 +2290,17 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
-        output.WriteRawTag(10);
-        output.WriteMessage(Setup);
-      }
-      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
-        output.WriteRawTag(18);
-        output.WriteMessage(Mark);
-      }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (argtypeCase_ == ArgtypeOneofCase.Setup) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Setup);
-      }
-      if (argtypeCase_ == ArgtypeOneofCase.Mark) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Mark);
-      }
       return size;
     }
 
-    public void MergeFrom(ServerArgs other) {
+    public void MergeFrom(Void other) {
       if (other == null) {
         return;
       }
-      switch (other.ArgtypeCase) {
-        case ArgtypeOneofCase.Setup:
-          Setup = other.Setup;
-          break;
-        case ArgtypeOneofCase.Mark:
-          Mark = other.Mark;
-          break;
-      }
-
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -2320,117 +2310,185 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 10: {
-            global::Grpc.Testing.ServerConfig subBuilder = new global::Grpc.Testing.ServerConfig();
-            if (argtypeCase_ == ArgtypeOneofCase.Setup) {
-              subBuilder.MergeFrom(Setup);
-            }
-            input.ReadMessage(subBuilder);
-            Setup = subBuilder;
-            break;
-          }
-          case 18: {
-            global::Grpc.Testing.Mark subBuilder = new global::Grpc.Testing.Mark();
-            if (argtypeCase_ == ArgtypeOneofCase.Mark) {
-              subBuilder.MergeFrom(Mark);
-            }
-            input.ReadMessage(subBuilder);
-            Mark = subBuilder;
-            break;
-          }
         }
       }
     }
 
   }
 
+  /// <summary>
+  ///  A single performance scenario: input to qps_json_driver
+  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class ServerStatus : pb::IMessage<ServerStatus> {
-    private static readonly pb::MessageParser<ServerStatus> _parser = new pb::MessageParser<ServerStatus>(() => new ServerStatus());
-    public static pb::MessageParser<ServerStatus> Parser { get { return _parser; } }
+  public sealed partial class Scenario : pb::IMessage<Scenario> {
+    private static readonly pb::MessageParser<Scenario> _parser = new pb::MessageParser<Scenario>(() => new Scenario());
+    public static pb::MessageParser<Scenario> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[13]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[14]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public ServerStatus() {
+    public Scenario() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public ServerStatus(ServerStatus other) : this() {
-      Stats = other.stats_ != null ? other.Stats.Clone() : null;
-      port_ = other.port_;
-      cores_ = other.cores_;
+    public Scenario(Scenario other) : this() {
+      name_ = other.name_;
+      ClientConfig = other.clientConfig_ != null ? other.ClientConfig.Clone() : null;
+      numClients_ = other.numClients_;
+      ServerConfig = other.serverConfig_ != null ? other.ServerConfig.Clone() : null;
+      numServers_ = other.numServers_;
+      warmupSeconds_ = other.warmupSeconds_;
+      benchmarkSeconds_ = other.benchmarkSeconds_;
+      spawnLocalWorkerCount_ = other.spawnLocalWorkerCount_;
     }
 
-    public ServerStatus Clone() {
-      return new ServerStatus(this);
+    public Scenario Clone() {
+      return new Scenario(this);
     }
 
-    /// <summary>Field number for the "stats" field.</summary>
-    public const int StatsFieldNumber = 1;
-    private global::Grpc.Testing.ServerStats stats_;
-    public global::Grpc.Testing.ServerStats Stats {
-      get { return stats_; }
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 1;
+    private string name_ = "";
+    /// <summary>
+    ///  Human readable name for this scenario
+    /// </summary>
+    public string Name {
+      get { return name_; }
       set {
-        stats_ = value;
+        name_ = pb::Preconditions.CheckNotNull(value, "value");
       }
     }
 
-    /// <summary>Field number for the "port" field.</summary>
-    public const int PortFieldNumber = 2;
-    private int port_;
+    /// <summary>Field number for the "client_config" field.</summary>
+    public const int ClientConfigFieldNumber = 2;
+    private global::Grpc.Testing.ClientConfig clientConfig_;
     /// <summary>
-    ///  the port bound by the server
+    ///  Client configuration
     /// </summary>
-    public int Port {
-      get { return port_; }
+    public global::Grpc.Testing.ClientConfig ClientConfig {
+      get { return clientConfig_; }
       set {
-        port_ = value;
+        clientConfig_ = value;
       }
     }
 
-    /// <summary>Field number for the "cores" field.</summary>
-    public const int CoresFieldNumber = 3;
-    private int cores_;
+    /// <summary>Field number for the "num_clients" field.</summary>
+    public const int NumClientsFieldNumber = 3;
+    private int numClients_;
     /// <summary>
-    ///  Number of cores available to the server
+    ///  Number of clients to start for the test
     /// </summary>
-    public int Cores {
-      get { return cores_; }
+    public int NumClients {
+      get { return numClients_; }
       set {
-        cores_ = value;
+        numClients_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "server_config" field.</summary>
+    public const int ServerConfigFieldNumber = 4;
+    private global::Grpc.Testing.ServerConfig serverConfig_;
+    /// <summary>
+    ///  Server configuration
+    /// </summary>
+    public global::Grpc.Testing.ServerConfig ServerConfig {
+      get { return serverConfig_; }
+      set {
+        serverConfig_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "num_servers" field.</summary>
+    public const int NumServersFieldNumber = 5;
+    private int numServers_;
+    /// <summary>
+    ///  Number of servers to start for the test
+    /// </summary>
+    public int NumServers {
+      get { return numServers_; }
+      set {
+        numServers_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "warmup_seconds" field.</summary>
+    public const int WarmupSecondsFieldNumber = 6;
+    private int warmupSeconds_;
+    /// <summary>
+    ///  Warmup period, in seconds
+    /// </summary>
+    public int WarmupSeconds {
+      get { return warmupSeconds_; }
+      set {
+        warmupSeconds_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "benchmark_seconds" field.</summary>
+    public const int BenchmarkSecondsFieldNumber = 7;
+    private int benchmarkSeconds_;
+    /// <summary>
+    ///  Benchmark time, in seconds
+    /// </summary>
+    public int BenchmarkSeconds {
+      get { return benchmarkSeconds_; }
+      set {
+        benchmarkSeconds_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "spawn_local_worker_count" field.</summary>
+    public const int SpawnLocalWorkerCountFieldNumber = 8;
+    private int spawnLocalWorkerCount_;
+    /// <summary>
+    ///  Number of workers to spawn locally (usually zero)
+    /// </summary>
+    public int SpawnLocalWorkerCount {
+      get { return spawnLocalWorkerCount_; }
+      set {
+        spawnLocalWorkerCount_ = value;
       }
     }
 
     public override bool Equals(object other) {
-      return Equals(other as ServerStatus);
+      return Equals(other as Scenario);
     }
 
-    public bool Equals(ServerStatus other) {
+    public bool Equals(Scenario other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (!object.Equals(Stats, other.Stats)) return false;
-      if (Port != other.Port) return false;
-      if (Cores != other.Cores) return false;
+      if (Name != other.Name) return false;
+      if (!object.Equals(ClientConfig, other.ClientConfig)) return false;
+      if (NumClients != other.NumClients) return false;
+      if (!object.Equals(ServerConfig, other.ServerConfig)) return false;
+      if (NumServers != other.NumServers) return false;
+      if (WarmupSeconds != other.WarmupSeconds) return false;
+      if (BenchmarkSeconds != other.BenchmarkSeconds) return false;
+      if (SpawnLocalWorkerCount != other.SpawnLocalWorkerCount) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (stats_ != null) hash ^= Stats.GetHashCode();
-      if (Port != 0) hash ^= Port.GetHashCode();
-      if (Cores != 0) hash ^= Cores.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (clientConfig_ != null) hash ^= ClientConfig.GetHashCode();
+      if (NumClients != 0) hash ^= NumClients.GetHashCode();
+      if (serverConfig_ != null) hash ^= ServerConfig.GetHashCode();
+      if (NumServers != 0) hash ^= NumServers.GetHashCode();
+      if (WarmupSeconds != 0) hash ^= WarmupSeconds.GetHashCode();
+      if (BenchmarkSeconds != 0) hash ^= BenchmarkSeconds.GetHashCode();
+      if (SpawnLocalWorkerCount != 0) hash ^= SpawnLocalWorkerCount.GetHashCode();
       return hash;
     }
 
@@ -2439,49 +2497,102 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (stats_ != null) {
+      if (Name.Length != 0) {
         output.WriteRawTag(10);
-        output.WriteMessage(Stats);
+        output.WriteString(Name);
       }
-      if (Port != 0) {
-        output.WriteRawTag(16);
-        output.WriteInt32(Port);
+      if (clientConfig_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(ClientConfig);
       }
-      if (Cores != 0) {
+      if (NumClients != 0) {
         output.WriteRawTag(24);
-        output.WriteInt32(Cores);
+        output.WriteInt32(NumClients);
+      }
+      if (serverConfig_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(ServerConfig);
+      }
+      if (NumServers != 0) {
+        output.WriteRawTag(40);
+        output.WriteInt32(NumServers);
+      }
+      if (WarmupSeconds != 0) {
+        output.WriteRawTag(48);
+        output.WriteInt32(WarmupSeconds);
+      }
+      if (BenchmarkSeconds != 0) {
+        output.WriteRawTag(56);
+        output.WriteInt32(BenchmarkSeconds);
+      }
+      if (SpawnLocalWorkerCount != 0) {
+        output.WriteRawTag(64);
+        output.WriteInt32(SpawnLocalWorkerCount);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (stats_ != null) {
-        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Stats);
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
       }
-      if (Port != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Port);
+      if (clientConfig_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ClientConfig);
       }
-      if (Cores != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Cores);
+      if (NumClients != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(NumClients);
+      }
+      if (serverConfig_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ServerConfig);
+      }
+      if (NumServers != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(NumServers);
+      }
+      if (WarmupSeconds != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(WarmupSeconds);
+      }
+      if (BenchmarkSeconds != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(BenchmarkSeconds);
+      }
+      if (SpawnLocalWorkerCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(SpawnLocalWorkerCount);
       }
       return size;
     }
 
-    public void MergeFrom(ServerStatus other) {
+    public void MergeFrom(Scenario other) {
       if (other == null) {
         return;
       }
-      if (other.stats_ != null) {
-        if (stats_ == null) {
-          stats_ = new global::Grpc.Testing.ServerStats();
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.clientConfig_ != null) {
+        if (clientConfig_ == null) {
+          clientConfig_ = new global::Grpc.Testing.ClientConfig();
         }
-        Stats.MergeFrom(other.Stats);
+        ClientConfig.MergeFrom(other.ClientConfig);
       }
-      if (other.Port != 0) {
-        Port = other.Port;
+      if (other.NumClients != 0) {
+        NumClients = other.NumClients;
       }
-      if (other.Cores != 0) {
-        Cores = other.Cores;
+      if (other.serverConfig_ != null) {
+        if (serverConfig_ == null) {
+          serverConfig_ = new global::Grpc.Testing.ServerConfig();
+        }
+        ServerConfig.MergeFrom(other.ServerConfig);
+      }
+      if (other.NumServers != 0) {
+        NumServers = other.NumServers;
+      }
+      if (other.WarmupSeconds != 0) {
+        WarmupSeconds = other.WarmupSeconds;
+      }
+      if (other.BenchmarkSeconds != 0) {
+        BenchmarkSeconds = other.BenchmarkSeconds;
+      }
+      if (other.SpawnLocalWorkerCount != 0) {
+        SpawnLocalWorkerCount = other.SpawnLocalWorkerCount;
       }
     }
 
@@ -2493,18 +2604,41 @@ namespace Grpc.Testing {
             input.SkipLastField();
             break;
           case 10: {
-            if (stats_ == null) {
-              stats_ = new global::Grpc.Testing.ServerStats();
+            Name = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (clientConfig_ == null) {
+              clientConfig_ = new global::Grpc.Testing.ClientConfig();
             }
-            input.ReadMessage(stats_);
+            input.ReadMessage(clientConfig_);
+            break;
+          }
+          case 24: {
+            NumClients = input.ReadInt32();
+            break;
+          }
+          case 34: {
+            if (serverConfig_ == null) {
+              serverConfig_ = new global::Grpc.Testing.ServerConfig();
+            }
+            input.ReadMessage(serverConfig_);
             break;
           }
-          case 16: {
-            Port = input.ReadInt32();
+          case 40: {
+            NumServers = input.ReadInt32();
             break;
           }
-          case 24: {
-            Cores = input.ReadInt32();
+          case 48: {
+            WarmupSeconds = input.ReadInt32();
+            break;
+          }
+          case 56: {
+            BenchmarkSeconds = input.ReadInt32();
+            break;
+          }
+          case 64: {
+            SpawnLocalWorkerCount = input.ReadInt32();
             break;
           }
         }
@@ -2513,48 +2647,63 @@ namespace Grpc.Testing {
 
   }
 
+  /// <summary>
+  ///  A set of scenarios to be run with qps_json_driver
+  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class CoreRequest : pb::IMessage<CoreRequest> {
-    private static readonly pb::MessageParser<CoreRequest> _parser = new pb::MessageParser<CoreRequest>(() => new CoreRequest());
-    public static pb::MessageParser<CoreRequest> Parser { get { return _parser; } }
+  public sealed partial class Scenarios : pb::IMessage<Scenarios> {
+    private static readonly pb::MessageParser<Scenarios> _parser = new pb::MessageParser<Scenarios>(() => new Scenarios());
+    public static pb::MessageParser<Scenarios> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[14]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[15]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public CoreRequest() {
+    public Scenarios() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public CoreRequest(CoreRequest other) : this() {
+    public Scenarios(Scenarios other) : this() {
+      scenarios_ = other.scenarios_.Clone();
     }
 
-    public CoreRequest Clone() {
-      return new CoreRequest(this);
+    public Scenarios Clone() {
+      return new Scenarios(this);
+    }
+
+    /// <summary>Field number for the "scenarios" field.</summary>
+    public const int Scenarios_FieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Grpc.Testing.Scenario> _repeated_scenarios_codec
+        = pb::FieldCodec.ForMessage(10, global::Grpc.Testing.Scenario.Parser);
+    private readonly pbc::RepeatedField<global::Grpc.Testing.Scenario> scenarios_ = new pbc::RepeatedField<global::Grpc.Testing.Scenario>();
+    public pbc::RepeatedField<global::Grpc.Testing.Scenario> Scenarios_ {
+      get { return scenarios_; }
     }
 
     public override bool Equals(object other) {
-      return Equals(other as CoreRequest);
+      return Equals(other as Scenarios);
     }
 
-    public bool Equals(CoreRequest other) {
+    public bool Equals(Scenarios other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
+      if(!scenarios_.Equals(other.scenarios_)) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
+      hash ^= scenarios_.GetHashCode();
       return hash;
     }
 
@@ -2563,17 +2712,20 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
+      scenarios_.WriteTo(output, _repeated_scenarios_codec);
     }
 
     public int CalculateSize() {
       int size = 0;
+      size += scenarios_.CalculateSize(_repeated_scenarios_codec);
       return size;
     }
 
-    public void MergeFrom(CoreRequest other) {
+    public void MergeFrom(Scenarios other) {
       if (other == null) {
         return;
       }
+      scenarios_.Add(other.scenarios_);
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -2583,70 +2735,226 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
+          case 10: {
+            scenarios_.AddEntriesFrom(input, _repeated_scenarios_codec);
+            break;
+          }
         }
       }
     }
 
   }
 
+  /// <summary>
+  ///  Basic summary that can be computed from ClientStats and ServerStats
+  ///  once the scenario has finished.
+  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class CoreResponse : pb::IMessage<CoreResponse> {
-    private static readonly pb::MessageParser<CoreResponse> _parser = new pb::MessageParser<CoreResponse>(() => new CoreResponse());
-    public static pb::MessageParser<CoreResponse> Parser { get { return _parser; } }
+  public sealed partial class ScenarioResultSummary : pb::IMessage<ScenarioResultSummary> {
+    private static readonly pb::MessageParser<ScenarioResultSummary> _parser = new pb::MessageParser<ScenarioResultSummary>(() => new ScenarioResultSummary());
+    public static pb::MessageParser<ScenarioResultSummary> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[15]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[16]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public CoreResponse() {
+    public ScenarioResultSummary() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public CoreResponse(CoreResponse other) : this() {
-      cores_ = other.cores_;
+    public ScenarioResultSummary(ScenarioResultSummary other) : this() {
+      qps_ = other.qps_;
+      qpsPerServerCore_ = other.qpsPerServerCore_;
+      serverSystemTime_ = other.serverSystemTime_;
+      serverUserTime_ = other.serverUserTime_;
+      clientSystemTime_ = other.clientSystemTime_;
+      clientUserTime_ = other.clientUserTime_;
+      latency50_ = other.latency50_;
+      latency90_ = other.latency90_;
+      latency95_ = other.latency95_;
+      latency99_ = other.latency99_;
+      latency999_ = other.latency999_;
     }
 
-    public CoreResponse Clone() {
-      return new CoreResponse(this);
+    public ScenarioResultSummary Clone() {
+      return new ScenarioResultSummary(this);
     }
 
-    /// <summary>Field number for the "cores" field.</summary>
-    public const int CoresFieldNumber = 1;
-    private int cores_;
+    /// <summary>Field number for the "qps" field.</summary>
+    public const int QpsFieldNumber = 1;
+    private double qps_;
     /// <summary>
-    ///  Number of cores available on the server
+    ///  Total number of operations per second over all clients.
     /// </summary>
-    public int Cores {
-      get { return cores_; }
+    public double Qps {
+      get { return qps_; }
       set {
-        cores_ = value;
+        qps_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "qps_per_server_core" field.</summary>
+    public const int QpsPerServerCoreFieldNumber = 2;
+    private double qpsPerServerCore_;
+    /// <summary>
+    ///  QPS per one server core.
+    /// </summary>
+    public double QpsPerServerCore {
+      get { return qpsPerServerCore_; }
+      set {
+        qpsPerServerCore_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "server_system_time" field.</summary>
+    public const int ServerSystemTimeFieldNumber = 3;
+    private double serverSystemTime_;
+    /// <summary>
+    ///  server load based on system_time (0.85 => 85%)
+    /// </summary>
+    public double ServerSystemTime {
+      get { return serverSystemTime_; }
+      set {
+        serverSystemTime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "server_user_time" field.</summary>
+    public const int ServerUserTimeFieldNumber = 4;
+    private double serverUserTime_;
+    /// <summary>
+    ///  server load based on user_time (0.85 => 85%)
+    /// </summary>
+    public double ServerUserTime {
+      get { return serverUserTime_; }
+      set {
+        serverUserTime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "client_system_time" field.</summary>
+    public const int ClientSystemTimeFieldNumber = 5;
+    private double clientSystemTime_;
+    /// <summary>
+    ///  client load based on system_time (0.85 => 85%)
+    /// </summary>
+    public double ClientSystemTime {
+      get { return clientSystemTime_; }
+      set {
+        clientSystemTime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "client_user_time" field.</summary>
+    public const int ClientUserTimeFieldNumber = 6;
+    private double clientUserTime_;
+    /// <summary>
+    ///  client load based on user_time (0.85 => 85%)
+    /// </summary>
+    public double ClientUserTime {
+      get { return clientUserTime_; }
+      set {
+        clientUserTime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "latency_50" field.</summary>
+    public const int Latency50FieldNumber = 7;
+    private double latency50_;
+    /// <summary>
+    ///  X% latency percentiles (in nanoseconds)
+    /// </summary>
+    public double Latency50 {
+      get { return latency50_; }
+      set {
+        latency50_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "latency_90" field.</summary>
+    public const int Latency90FieldNumber = 8;
+    private double latency90_;
+    public double Latency90 {
+      get { return latency90_; }
+      set {
+        latency90_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "latency_95" field.</summary>
+    public const int Latency95FieldNumber = 9;
+    private double latency95_;
+    public double Latency95 {
+      get { return latency95_; }
+      set {
+        latency95_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "latency_99" field.</summary>
+    public const int Latency99FieldNumber = 10;
+    private double latency99_;
+    public double Latency99 {
+      get { return latency99_; }
+      set {
+        latency99_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "latency_999" field.</summary>
+    public const int Latency999FieldNumber = 11;
+    private double latency999_;
+    public double Latency999 {
+      get { return latency999_; }
+      set {
+        latency999_ = value;
       }
     }
 
     public override bool Equals(object other) {
-      return Equals(other as CoreResponse);
+      return Equals(other as ScenarioResultSummary);
     }
 
-    public bool Equals(CoreResponse other) {
+    public bool Equals(ScenarioResultSummary other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
-      if (Cores != other.Cores) return false;
+      if (Qps != other.Qps) return false;
+      if (QpsPerServerCore != other.QpsPerServerCore) return false;
+      if (ServerSystemTime != other.ServerSystemTime) return false;
+      if (ServerUserTime != other.ServerUserTime) return false;
+      if (ClientSystemTime != other.ClientSystemTime) return false;
+      if (ClientUserTime != other.ClientUserTime) return false;
+      if (Latency50 != other.Latency50) return false;
+      if (Latency90 != other.Latency90) return false;
+      if (Latency95 != other.Latency95) return false;
+      if (Latency99 != other.Latency99) return false;
+      if (Latency999 != other.Latency999) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
-      if (Cores != 0) hash ^= Cores.GetHashCode();
+      if (Qps != 0D) hash ^= Qps.GetHashCode();
+      if (QpsPerServerCore != 0D) hash ^= QpsPerServerCore.GetHashCode();
+      if (ServerSystemTime != 0D) hash ^= ServerSystemTime.GetHashCode();
+      if (ServerUserTime != 0D) hash ^= ServerUserTime.GetHashCode();
+      if (ClientSystemTime != 0D) hash ^= ClientSystemTime.GetHashCode();
+      if (ClientUserTime != 0D) hash ^= ClientUserTime.GetHashCode();
+      if (Latency50 != 0D) hash ^= Latency50.GetHashCode();
+      if (Latency90 != 0D) hash ^= Latency90.GetHashCode();
+      if (Latency95 != 0D) hash ^= Latency95.GetHashCode();
+      if (Latency99 != 0D) hash ^= Latency99.GetHashCode();
+      if (Latency999 != 0D) hash ^= Latency999.GetHashCode();
       return hash;
     }
 
@@ -2655,26 +2963,126 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
-      if (Cores != 0) {
-        output.WriteRawTag(8);
-        output.WriteInt32(Cores);
+      if (Qps != 0D) {
+        output.WriteRawTag(9);
+        output.WriteDouble(Qps);
+      }
+      if (QpsPerServerCore != 0D) {
+        output.WriteRawTag(17);
+        output.WriteDouble(QpsPerServerCore);
+      }
+      if (ServerSystemTime != 0D) {
+        output.WriteRawTag(25);
+        output.WriteDouble(ServerSystemTime);
+      }
+      if (ServerUserTime != 0D) {
+        output.WriteRawTag(33);
+        output.WriteDouble(ServerUserTime);
+      }
+      if (ClientSystemTime != 0D) {
+        output.WriteRawTag(41);
+        output.WriteDouble(ClientSystemTime);
+      }
+      if (ClientUserTime != 0D) {
+        output.WriteRawTag(49);
+        output.WriteDouble(ClientUserTime);
+      }
+      if (Latency50 != 0D) {
+        output.WriteRawTag(57);
+        output.WriteDouble(Latency50);
+      }
+      if (Latency90 != 0D) {
+        output.WriteRawTag(65);
+        output.WriteDouble(Latency90);
+      }
+      if (Latency95 != 0D) {
+        output.WriteRawTag(73);
+        output.WriteDouble(Latency95);
+      }
+      if (Latency99 != 0D) {
+        output.WriteRawTag(81);
+        output.WriteDouble(Latency99);
+      }
+      if (Latency999 != 0D) {
+        output.WriteRawTag(89);
+        output.WriteDouble(Latency999);
       }
     }
 
     public int CalculateSize() {
       int size = 0;
-      if (Cores != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Cores);
+      if (Qps != 0D) {
+        size += 1 + 8;
+      }
+      if (QpsPerServerCore != 0D) {
+        size += 1 + 8;
+      }
+      if (ServerSystemTime != 0D) {
+        size += 1 + 8;
+      }
+      if (ServerUserTime != 0D) {
+        size += 1 + 8;
+      }
+      if (ClientSystemTime != 0D) {
+        size += 1 + 8;
+      }
+      if (ClientUserTime != 0D) {
+        size += 1 + 8;
+      }
+      if (Latency50 != 0D) {
+        size += 1 + 8;
+      }
+      if (Latency90 != 0D) {
+        size += 1 + 8;
+      }
+      if (Latency95 != 0D) {
+        size += 1 + 8;
+      }
+      if (Latency99 != 0D) {
+        size += 1 + 8;
+      }
+      if (Latency999 != 0D) {
+        size += 1 + 8;
       }
       return size;
     }
 
-    public void MergeFrom(CoreResponse other) {
+    public void MergeFrom(ScenarioResultSummary other) {
       if (other == null) {
         return;
       }
-      if (other.Cores != 0) {
-        Cores = other.Cores;
+      if (other.Qps != 0D) {
+        Qps = other.Qps;
+      }
+      if (other.QpsPerServerCore != 0D) {
+        QpsPerServerCore = other.QpsPerServerCore;
+      }
+      if (other.ServerSystemTime != 0D) {
+        ServerSystemTime = other.ServerSystemTime;
+      }
+      if (other.ServerUserTime != 0D) {
+        ServerUserTime = other.ServerUserTime;
+      }
+      if (other.ClientSystemTime != 0D) {
+        ClientSystemTime = other.ClientSystemTime;
+      }
+      if (other.ClientUserTime != 0D) {
+        ClientUserTime = other.ClientUserTime;
+      }
+      if (other.Latency50 != 0D) {
+        Latency50 = other.Latency50;
+      }
+      if (other.Latency90 != 0D) {
+        Latency90 = other.Latency90;
+      }
+      if (other.Latency95 != 0D) {
+        Latency95 = other.Latency95;
+      }
+      if (other.Latency99 != 0D) {
+        Latency99 = other.Latency99;
+      }
+      if (other.Latency999 != 0D) {
+        Latency999 = other.Latency999;
       }
     }
 
@@ -2685,8 +3093,48 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
-          case 8: {
-            Cores = input.ReadInt32();
+          case 9: {
+            Qps = input.ReadDouble();
+            break;
+          }
+          case 17: {
+            QpsPerServerCore = input.ReadDouble();
+            break;
+          }
+          case 25: {
+            ServerSystemTime = input.ReadDouble();
+            break;
+          }
+          case 33: {
+            ServerUserTime = input.ReadDouble();
+            break;
+          }
+          case 41: {
+            ClientSystemTime = input.ReadDouble();
+            break;
+          }
+          case 49: {
+            ClientUserTime = input.ReadDouble();
+            break;
+          }
+          case 57: {
+            Latency50 = input.ReadDouble();
+            break;
+          }
+          case 65: {
+            Latency90 = input.ReadDouble();
+            break;
+          }
+          case 73: {
+            Latency95 = input.ReadDouble();
+            break;
+          }
+          case 81: {
+            Latency99 = input.ReadDouble();
+            break;
+          }
+          case 89: {
+            Latency999 = input.ReadDouble();
             break;
           }
         }
@@ -2695,48 +3143,144 @@ namespace Grpc.Testing {
 
   }
 
+  /// <summary>
+  ///  Results of a single benchmark scenario.
+  /// </summary>
   [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-  public sealed partial class Void : pb::IMessage<Void> {
-    private static readonly pb::MessageParser<Void> _parser = new pb::MessageParser<Void>(() => new Void());
-    public static pb::MessageParser<Void> Parser { get { return _parser; } }
+  public sealed partial class ScenarioResult : pb::IMessage<ScenarioResult> {
+    private static readonly pb::MessageParser<ScenarioResult> _parser = new pb::MessageParser<ScenarioResult>(() => new ScenarioResult());
+    public static pb::MessageParser<ScenarioResult> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[16]; }
+      get { return global::Grpc.Testing.ControlReflection.Descriptor.MessageTypes[17]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
       get { return Descriptor; }
     }
 
-    public Void() {
+    public ScenarioResult() {
       OnConstruction();
     }
 
     partial void OnConstruction();
 
-    public Void(Void other) : this() {
+    public ScenarioResult(ScenarioResult other) : this() {
+      Scenario = other.scenario_ != null ? other.Scenario.Clone() : null;
+      Latencies = other.latencies_ != null ? other.Latencies.Clone() : null;
+      clientStats_ = other.clientStats_.Clone();
+      serverStats_ = other.serverStats_.Clone();
+      serverCores_ = other.serverCores_.Clone();
+      Summary = other.summary_ != null ? other.Summary.Clone() : null;
     }
 
-    public Void Clone() {
-      return new Void(this);
+    public ScenarioResult Clone() {
+      return new ScenarioResult(this);
+    }
+
+    /// <summary>Field number for the "scenario" field.</summary>
+    public const int ScenarioFieldNumber = 1;
+    private global::Grpc.Testing.Scenario scenario_;
+    /// <summary>
+    ///  Inputs used to run the scenario.
+    /// </summary>
+    public global::Grpc.Testing.Scenario Scenario {
+      get { return scenario_; }
+      set {
+        scenario_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "latencies" field.</summary>
+    public const int LatenciesFieldNumber = 2;
+    private global::Grpc.Testing.HistogramData latencies_;
+    /// <summary>
+    ///  Histograms from all clients merged into one histogram.
+    /// </summary>
+    public global::Grpc.Testing.HistogramData Latencies {
+      get { return latencies_; }
+      set {
+        latencies_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "client_stats" field.</summary>
+    public const int ClientStatsFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Grpc.Testing.ClientStats> _repeated_clientStats_codec
+        = pb::FieldCodec.ForMessage(26, global::Grpc.Testing.ClientStats.Parser);
+    private readonly pbc::RepeatedField<global::Grpc.Testing.ClientStats> clientStats_ = new pbc::RepeatedField<global::Grpc.Testing.ClientStats>();
+    /// <summary>
+    ///  Client stats for each client
+    /// </summary>
+    public pbc::RepeatedField<global::Grpc.Testing.ClientStats> ClientStats {
+      get { return clientStats_; }
+    }
+
+    /// <summary>Field number for the "server_stats" field.</summary>
+    public const int ServerStatsFieldNumber = 4;
+    private static readonly pb::FieldCodec<global::Grpc.Testing.ServerStats> _repeated_serverStats_codec
+        = pb::FieldCodec.ForMessage(34, global::Grpc.Testing.ServerStats.Parser);
+    private readonly pbc::RepeatedField<global::Grpc.Testing.ServerStats> serverStats_ = new pbc::RepeatedField<global::Grpc.Testing.ServerStats>();
+    /// <summary>
+    ///  Server stats for each server
+    /// </summary>
+    public pbc::RepeatedField<global::Grpc.Testing.ServerStats> ServerStats {
+      get { return serverStats_; }
+    }
+
+    /// <summary>Field number for the "server_cores" field.</summary>
+    public const int ServerCoresFieldNumber = 5;
+    private static readonly pb::FieldCodec<int> _repeated_serverCores_codec
+        = pb::FieldCodec.ForInt32(42);
+    private readonly pbc::RepeatedField<int> serverCores_ = new pbc::RepeatedField<int>();
+    /// <summary>
+    ///  Number of cores available to each server
+    /// </summary>
+    public pbc::RepeatedField<int> ServerCores {
+      get { return serverCores_; }
+    }
+
+    /// <summary>Field number for the "summary" field.</summary>
+    public const int SummaryFieldNumber = 6;
+    private global::Grpc.Testing.ScenarioResultSummary summary_;
+    /// <summary>
+    ///  An after-the-fact computed summary
+    /// </summary>
+    public global::Grpc.Testing.ScenarioResultSummary Summary {
+      get { return summary_; }
+      set {
+        summary_ = value;
+      }
     }
 
     public override bool Equals(object other) {
-      return Equals(other as Void);
+      return Equals(other as ScenarioResult);
     }
 
-    public bool Equals(Void other) {
+    public bool Equals(ScenarioResult other) {
       if (ReferenceEquals(other, null)) {
         return false;
       }
       if (ReferenceEquals(other, this)) {
         return true;
       }
+      if (!object.Equals(Scenario, other.Scenario)) return false;
+      if (!object.Equals(Latencies, other.Latencies)) return false;
+      if(!clientStats_.Equals(other.clientStats_)) return false;
+      if(!serverStats_.Equals(other.serverStats_)) return false;
+      if(!serverCores_.Equals(other.serverCores_)) return false;
+      if (!object.Equals(Summary, other.Summary)) return false;
       return true;
     }
 
     public override int GetHashCode() {
       int hash = 1;
+      if (scenario_ != null) hash ^= Scenario.GetHashCode();
+      if (latencies_ != null) hash ^= Latencies.GetHashCode();
+      hash ^= clientStats_.GetHashCode();
+      hash ^= serverStats_.GetHashCode();
+      hash ^= serverCores_.GetHashCode();
+      if (summary_ != null) hash ^= Summary.GetHashCode();
       return hash;
     }
 
@@ -2745,17 +3289,65 @@ namespace Grpc.Testing {
     }
 
     public void WriteTo(pb::CodedOutputStream output) {
+      if (scenario_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Scenario);
+      }
+      if (latencies_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Latencies);
+      }
+      clientStats_.WriteTo(output, _repeated_clientStats_codec);
+      serverStats_.WriteTo(output, _repeated_serverStats_codec);
+      serverCores_.WriteTo(output, _repeated_serverCores_codec);
+      if (summary_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Summary);
+      }
     }
 
     public int CalculateSize() {
       int size = 0;
+      if (scenario_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Scenario);
+      }
+      if (latencies_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Latencies);
+      }
+      size += clientStats_.CalculateSize(_repeated_clientStats_codec);
+      size += serverStats_.CalculateSize(_repeated_serverStats_codec);
+      size += serverCores_.CalculateSize(_repeated_serverCores_codec);
+      if (summary_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Summary);
+      }
       return size;
     }
 
-    public void MergeFrom(Void other) {
+    public void MergeFrom(ScenarioResult other) {
       if (other == null) {
         return;
       }
+      if (other.scenario_ != null) {
+        if (scenario_ == null) {
+          scenario_ = new global::Grpc.Testing.Scenario();
+        }
+        Scenario.MergeFrom(other.Scenario);
+      }
+      if (other.latencies_ != null) {
+        if (latencies_ == null) {
+          latencies_ = new global::Grpc.Testing.HistogramData();
+        }
+        Latencies.MergeFrom(other.Latencies);
+      }
+      clientStats_.Add(other.clientStats_);
+      serverStats_.Add(other.serverStats_);
+      serverCores_.Add(other.serverCores_);
+      if (other.summary_ != null) {
+        if (summary_ == null) {
+          summary_ = new global::Grpc.Testing.ScenarioResultSummary();
+        }
+        Summary.MergeFrom(other.Summary);
+      }
     }
 
     public void MergeFrom(pb::CodedInputStream input) {
@@ -2765,6 +3357,40 @@ namespace Grpc.Testing {
           default:
             input.SkipLastField();
             break;
+          case 10: {
+            if (scenario_ == null) {
+              scenario_ = new global::Grpc.Testing.Scenario();
+            }
+            input.ReadMessage(scenario_);
+            break;
+          }
+          case 18: {
+            if (latencies_ == null) {
+              latencies_ = new global::Grpc.Testing.HistogramData();
+            }
+            input.ReadMessage(latencies_);
+            break;
+          }
+          case 26: {
+            clientStats_.AddEntriesFrom(input, _repeated_clientStats_codec);
+            break;
+          }
+          case 34: {
+            serverStats_.AddEntriesFrom(input, _repeated_serverStats_codec);
+            break;
+          }
+          case 42:
+          case 40: {
+            serverCores_.AddEntriesFrom(input, _repeated_serverCores_codec);
+            break;
+          }
+          case 50: {
+            if (summary_ == null) {
+              summary_ = new global::Grpc.Testing.ScenarioResultSummary();
+            }
+            input.ReadMessage(summary_);
+            break;
+          }
         }
       }
     }
diff --git a/src/csharp/Grpc.IntegrationTesting/Messages.cs b/src/csharp/Grpc.IntegrationTesting/Messages.cs
index 7ca47860f65b1f64f70c994c04c879b0635a6cc4..fcff4759417ec61d2a0cabfe0464436fd14119b9 100644
--- a/src/csharp/Grpc.IntegrationTesting/Messages.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Messages.cs
@@ -47,11 +47,12 @@ namespace Grpc.Testing {
             "c3Npb24YBiABKA4yHS5ncnBjLnRlc3RpbmcuQ29tcHJlc3Npb25UeXBlEjEK",
             "D3Jlc3BvbnNlX3N0YXR1cxgHIAEoCzIYLmdycGMudGVzdGluZy5FY2hvU3Rh",
             "dHVzIkUKG1N0cmVhbWluZ091dHB1dENhbGxSZXNwb25zZRImCgdwYXlsb2Fk",
-            "GAEgASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxvYWQiMwoNUmVjb25uZWN0SW5m",
-            "bxIOCgZwYXNzZWQYASABKAgSEgoKYmFja29mZl9tcxgCIAMoBSo/CgtQYXls",
-            "b2FkVHlwZRIQCgxDT01QUkVTU0FCTEUQABISCg5VTkNPTVBSRVNTQUJMRRAB",
-            "EgoKBlJBTkRPTRACKjIKD0NvbXByZXNzaW9uVHlwZRIICgROT05FEAASCAoE",
-            "R1pJUBABEgsKB0RFRkxBVEUQAmIGcHJvdG8z"));
+            "GAEgASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxvYWQiMwoPUmVjb25uZWN0UGFy",
+            "YW1zEiAKGG1heF9yZWNvbm5lY3RfYmFja29mZl9tcxgBIAEoBSIzCg1SZWNv",
+            "bm5lY3RJbmZvEg4KBnBhc3NlZBgBIAEoCBISCgpiYWNrb2ZmX21zGAIgAygF",
+            "Kj8KC1BheWxvYWRUeXBlEhAKDENPTVBSRVNTQUJMRRAAEhIKDlVOQ09NUFJF",
+            "U1NBQkxFEAESCgoGUkFORE9NEAIqMgoPQ29tcHJlc3Npb25UeXBlEggKBE5P",
+            "TkUQABIICgRHWklQEAESCwoHREVGTEFURRACYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
           new pbr::GeneratedCodeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), typeof(global::Grpc.Testing.CompressionType), }, new pbr::GeneratedCodeInfo[] {
@@ -64,6 +65,7 @@ namespace Grpc.Testing {
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ResponseParameters), global::Grpc.Testing.ResponseParameters.Parser, new[]{ "Size", "IntervalUs" }, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), global::Grpc.Testing.StreamingOutputCallRequest.Parser, new[]{ "ResponseType", "ResponseParameters", "Payload", "ResponseCompression", "ResponseStatus" }, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallResponse), global::Grpc.Testing.StreamingOutputCallResponse.Parser, new[]{ "Payload" }, null, null, null),
+            new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ReconnectParams), global::Grpc.Testing.ReconnectParams.Parser, new[]{ "MaxReconnectBackoffMs" }, null, null, null),
             new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ReconnectInfo), global::Grpc.Testing.ReconnectInfo.Parser, new[]{ "Passed", "BackoffMs" }, null, null, null)
           }));
     }
@@ -1572,6 +1574,113 @@ namespace Grpc.Testing {
 
   }
 
+  /// <summary>
+  ///  For reconnect interop test only.
+  ///  Client tells server what reconnection parameters it used.
+  /// </summary>
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public sealed partial class ReconnectParams : pb::IMessage<ReconnectParams> {
+    private static readonly pb::MessageParser<ReconnectParams> _parser = new pb::MessageParser<ReconnectParams>(() => new ReconnectParams());
+    public static pb::MessageParser<ReconnectParams> Parser { get { return _parser; } }
+
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    public ReconnectParams() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    public ReconnectParams(ReconnectParams other) : this() {
+      maxReconnectBackoffMs_ = other.maxReconnectBackoffMs_;
+    }
+
+    public ReconnectParams Clone() {
+      return new ReconnectParams(this);
+    }
+
+    /// <summary>Field number for the "max_reconnect_backoff_ms" field.</summary>
+    public const int MaxReconnectBackoffMsFieldNumber = 1;
+    private int maxReconnectBackoffMs_;
+    public int MaxReconnectBackoffMs {
+      get { return maxReconnectBackoffMs_; }
+      set {
+        maxReconnectBackoffMs_ = value;
+      }
+    }
+
+    public override bool Equals(object other) {
+      return Equals(other as ReconnectParams);
+    }
+
+    public bool Equals(ReconnectParams other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MaxReconnectBackoffMs != other.MaxReconnectBackoffMs) return false;
+      return true;
+    }
+
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MaxReconnectBackoffMs != 0) hash ^= MaxReconnectBackoffMs.GetHashCode();
+      return hash;
+    }
+
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (MaxReconnectBackoffMs != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(MaxReconnectBackoffMs);
+      }
+    }
+
+    public int CalculateSize() {
+      int size = 0;
+      if (MaxReconnectBackoffMs != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(MaxReconnectBackoffMs);
+      }
+      return size;
+    }
+
+    public void MergeFrom(ReconnectParams other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MaxReconnectBackoffMs != 0) {
+        MaxReconnectBackoffMs = other.MaxReconnectBackoffMs;
+      }
+    }
+
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            MaxReconnectBackoffMs = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
   /// <summary>
   ///  For reconnect interop test only.
   ///  Server tells client whether its reconnects are following the spec and the
@@ -1583,7 +1692,7 @@ namespace Grpc.Testing {
     public static pb::MessageParser<ReconnectInfo> Parser { get { return _parser; } }
 
     public static pbr::MessageDescriptor Descriptor {
-      get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[9]; }
+      get { return global::Grpc.Testing.MessagesReflection.Descriptor.MessageTypes[10]; }
     }
 
     pbr::MessageDescriptor pb::IMessage.Descriptor {
diff --git a/src/csharp/Grpc.IntegrationTesting/Test.cs b/src/csharp/Grpc.IntegrationTesting/Test.cs
index 91e0a1e04c0f67af37eb2f1ae9cd8e9f239f125a..363f6444ec2ef739ad58b72fd284b65443e2881c 100644
--- a/src/csharp/Grpc.IntegrationTesting/Test.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Test.cs
@@ -40,10 +40,10 @@ namespace Grpc.Testing {
             "bWluZ091dHB1dENhbGxSZXF1ZXN0GikuZ3JwYy50ZXN0aW5nLlN0cmVhbWlu",
             "Z091dHB1dENhbGxSZXNwb25zZSgBMAEyVQoUVW5pbXBsZW1lbnRlZFNlcnZp",
             "Y2USPQoRVW5pbXBsZW1lbnRlZENhbGwSEy5ncnBjLnRlc3RpbmcuRW1wdHka",
-            "Ey5ncnBjLnRlc3RpbmcuRW1wdHkyfwoQUmVjb25uZWN0U2VydmljZRIxCgVT",
-            "dGFydBITLmdycGMudGVzdGluZy5FbXB0eRoTLmdycGMudGVzdGluZy5FbXB0",
-            "eRI4CgRTdG9wEhMuZ3JwYy50ZXN0aW5nLkVtcHR5GhsuZ3JwYy50ZXN0aW5n",
-            "LlJlY29ubmVjdEluZm9iBnByb3RvMw=="));
+            "Ey5ncnBjLnRlc3RpbmcuRW1wdHkyiQEKEFJlY29ubmVjdFNlcnZpY2USOwoF",
+            "U3RhcnQSHS5ncnBjLnRlc3RpbmcuUmVjb25uZWN0UGFyYW1zGhMuZ3JwYy50",
+            "ZXN0aW5nLkVtcHR5EjgKBFN0b3ASEy5ncnBjLnRlc3RpbmcuRW1wdHkaGy5n",
+            "cnBjLnRlc3RpbmcuUmVjb25uZWN0SW5mb2IGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Grpc.Testing.EmptyReflection.Descriptor, global::Grpc.Testing.MessagesReflection.Descriptor, },
           new pbr::GeneratedCodeInfo(null, null));
diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
index b84ec2d984ade80536ce30564e247fe748df2901..31746cbe715605364e31168ee2659998426240b1 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
@@ -367,14 +367,15 @@ namespace Grpc.Testing {
   {
     static readonly string __ServiceName = "grpc.testing.ReconnectService";
 
+    static readonly Marshaller<global::Grpc.Testing.ReconnectParams> __Marshaller_ReconnectParams = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ReconnectParams.Parser.ParseFrom);
     static readonly Marshaller<global::Grpc.Testing.Empty> __Marshaller_Empty = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.Empty.Parser.ParseFrom);
     static readonly Marshaller<global::Grpc.Testing.ReconnectInfo> __Marshaller_ReconnectInfo = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ReconnectInfo.Parser.ParseFrom);
 
-    static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_Start = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
+    static readonly Method<global::Grpc.Testing.ReconnectParams, global::Grpc.Testing.Empty> __Method_Start = new Method<global::Grpc.Testing.ReconnectParams, global::Grpc.Testing.Empty>(
         MethodType.Unary,
         __ServiceName,
         "Start",
-        __Marshaller_Empty,
+        __Marshaller_ReconnectParams,
         __Marshaller_Empty);
 
     static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo> __Method_Stop = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo>(
@@ -394,10 +395,10 @@ namespace Grpc.Testing {
     [System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
     public interface IReconnectServiceClient
     {
-      global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
-      global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, CallOptions options);
-      AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
-      AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, CallOptions options);
+      global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+      global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, CallOptions options);
+      AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
+      AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, CallOptions options);
       global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
       global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, CallOptions options);
       AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
@@ -408,14 +409,14 @@ namespace Grpc.Testing {
     [System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
     public interface IReconnectService
     {
-      Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.Empty request, ServerCallContext context);
+      Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.ReconnectParams request, ServerCallContext context);
       Task<global::Grpc.Testing.ReconnectInfo> Stop(global::Grpc.Testing.Empty request, ServerCallContext context);
     }
 
     // server-side abstract class
     public abstract class ReconnectServiceBase
     {
-      public virtual Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.Empty request, ServerCallContext context)
+      public virtual Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.ReconnectParams request, ServerCallContext context)
       {
         throw new RpcException(new Status(StatusCode.Unimplemented, ""));
       }
@@ -445,19 +446,19 @@ namespace Grpc.Testing {
       {
       }
 
-      public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return Start(request, new CallOptions(headers, deadline, cancellationToken));
       }
-      public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, CallOptions options)
+      public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, CallOptions options)
       {
         return CallInvoker.BlockingUnaryCall(__Method_Start, null, options, request);
       }
-      public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
       {
         return StartAsync(request, new CallOptions(headers, deadline, cancellationToken));
       }
-      public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, CallOptions options)
+      public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, CallOptions options)
       {
         return CallInvoker.AsyncUnaryCall(__Method_Start, null, options, request);
       }
diff --git a/src/proto/grpc/testing/control.proto b/src/proto/grpc/testing/control.proto
index 5db39d0298eb1f91bd7473c343af8e03f424dbf4..28769ef6538d568768197433b75f5f3c9f413f0e 100644
--- a/src/proto/grpc/testing/control.proto
+++ b/src/proto/grpc/testing/control.proto
@@ -57,18 +57,6 @@ message PoissonParams {
   double offered_load = 1;
 }
 
-message UniformParams {
-  double interarrival_lo = 1;
-  double interarrival_hi = 2;
-}
-
-message DeterministicParams { double offered_load = 1; }
-
-message ParetoParams {
-  double interarrival_base = 1;
-  double alpha = 2;
-}
-
 // Once an RPC finishes, immediately start a new one.
 // No configuration parameters needed.
 message ClosedLoopParams {}
@@ -77,9 +65,6 @@ message LoadParams {
   oneof load {
     ClosedLoopParams closed_loop = 1;
     PoissonParams poisson = 2;
-    UniformParams uniform = 3;
-    DeterministicParams determ = 4;
-    ParetoParams pareto = 5;
   };
 }
 
diff --git a/src/ruby/qps/src/proto/grpc/testing/control.rb b/src/ruby/qps/src/proto/grpc/testing/control.rb
index d007123f2611b5b23cb67ee07b6bf5bf3dfda526..b81e22659d03f9cec9af7b280ae5cd6b762c1099 100644
--- a/src/ruby/qps/src/proto/grpc/testing/control.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/control.rb
@@ -9,26 +9,12 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
   add_message "grpc.testing.PoissonParams" do
     optional :offered_load, :double, 1
   end
-  add_message "grpc.testing.UniformParams" do
-    optional :interarrival_lo, :double, 1
-    optional :interarrival_hi, :double, 2
-  end
-  add_message "grpc.testing.DeterministicParams" do
-    optional :offered_load, :double, 1
-  end
-  add_message "grpc.testing.ParetoParams" do
-    optional :interarrival_base, :double, 1
-    optional :alpha, :double, 2
-  end
   add_message "grpc.testing.ClosedLoopParams" do
   end
   add_message "grpc.testing.LoadParams" do
     oneof :load do
       optional :closed_loop, :message, 1, "grpc.testing.ClosedLoopParams"
       optional :poisson, :message, 2, "grpc.testing.PoissonParams"
-      optional :uniform, :message, 3, "grpc.testing.UniformParams"
-      optional :determ, :message, 4, "grpc.testing.DeterministicParams"
-      optional :pareto, :message, 5, "grpc.testing.ParetoParams"
     end
   end
   add_message "grpc.testing.SecurityParams" do
@@ -88,6 +74,40 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
   end
   add_message "grpc.testing.Void" do
   end
+  add_message "grpc.testing.Scenario" do
+    optional :name, :string, 1
+    optional :client_config, :message, 2, "grpc.testing.ClientConfig"
+    optional :num_clients, :int32, 3
+    optional :server_config, :message, 4, "grpc.testing.ServerConfig"
+    optional :num_servers, :int32, 5
+    optional :warmup_seconds, :int32, 6
+    optional :benchmark_seconds, :int32, 7
+    optional :spawn_local_worker_count, :int32, 8
+  end
+  add_message "grpc.testing.Scenarios" do
+    repeated :scenarios, :message, 1, "grpc.testing.Scenario"
+  end
+  add_message "grpc.testing.ScenarioResultSummary" do
+    optional :qps, :double, 1
+    optional :qps_per_server_core, :double, 2
+    optional :server_system_time, :double, 3
+    optional :server_user_time, :double, 4
+    optional :client_system_time, :double, 5
+    optional :client_user_time, :double, 6
+    optional :latency_50, :double, 7
+    optional :latency_90, :double, 8
+    optional :latency_95, :double, 9
+    optional :latency_99, :double, 10
+    optional :latency_999, :double, 11
+  end
+  add_message "grpc.testing.ScenarioResult" do
+    optional :scenario, :message, 1, "grpc.testing.Scenario"
+    optional :latencies, :message, 2, "grpc.testing.HistogramData"
+    repeated :client_stats, :message, 3, "grpc.testing.ClientStats"
+    repeated :server_stats, :message, 4, "grpc.testing.ServerStats"
+    repeated :server_cores, :int32, 5
+    optional :summary, :message, 6, "grpc.testing.ScenarioResultSummary"
+  end
   add_enum "grpc.testing.ClientType" do
     value :SYNC_CLIENT, 0
     value :ASYNC_CLIENT, 1
@@ -106,9 +126,6 @@ end
 module Grpc
   module Testing
     PoissonParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.PoissonParams").msgclass
-    UniformParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.UniformParams").msgclass
-    DeterministicParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.DeterministicParams").msgclass
-    ParetoParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ParetoParams").msgclass
     ClosedLoopParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ClosedLoopParams").msgclass
     LoadParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.LoadParams").msgclass
     SecurityParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.SecurityParams").msgclass
@@ -122,6 +139,10 @@ module Grpc
     CoreRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.CoreRequest").msgclass
     CoreResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.CoreResponse").msgclass
     Void = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.Void").msgclass
+    Scenario = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.Scenario").msgclass
+    Scenarios = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.Scenarios").msgclass
+    ScenarioResultSummary = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ScenarioResultSummary").msgclass
+    ScenarioResult = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ScenarioResult").msgclass
     ClientType = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ClientType").enummodule
     ServerType = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ServerType").enummodule
     RpcType = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.RpcType").enummodule
diff --git a/src/ruby/qps/src/proto/grpc/testing/messages.rb b/src/ruby/qps/src/proto/grpc/testing/messages.rb
index b9c32dbef5da257e61fcac9f089db7f57e4bec69..2bdfe0eade338c0d0101e28a3640aea113b1fc7c 100644
--- a/src/ruby/qps/src/proto/grpc/testing/messages.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/messages.rb
@@ -46,6 +46,9 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
   add_message "grpc.testing.StreamingOutputCallResponse" do
     optional :payload, :message, 1, "grpc.testing.Payload"
   end
+  add_message "grpc.testing.ReconnectParams" do
+    optional :max_reconnect_backoff_ms, :int32, 1
+  end
   add_message "grpc.testing.ReconnectInfo" do
     optional :passed, :bool, 1
     repeated :backoff_ms, :int32, 2
@@ -73,6 +76,7 @@ module Grpc
     ResponseParameters = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ResponseParameters").msgclass
     StreamingOutputCallRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.StreamingOutputCallRequest").msgclass
     StreamingOutputCallResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.StreamingOutputCallResponse").msgclass
+    ReconnectParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ReconnectParams").msgclass
     ReconnectInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ReconnectInfo").msgclass
     PayloadType = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.PayloadType").enummodule
     CompressionType = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.CompressionType").enummodule
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index e958141d4ea64defa4377fa5fb98ab1ae05c5ad1..5a9027a4a23cf8f7b939acd6e7684f1fe542a70c 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -173,20 +173,6 @@ class Client {
         random_dist.reset(
             new ExpDist(load.poisson().offered_load() / num_threads));
         break;
-      case LoadParams::kUniform:
-        random_dist.reset(
-            new UniformDist(load.uniform().interarrival_lo() * num_threads,
-                            load.uniform().interarrival_hi() * num_threads));
-        break;
-      case LoadParams::kDeterm:
-        random_dist.reset(
-            new DetDist(num_threads / load.determ().offered_load()));
-        break;
-      case LoadParams::kPareto:
-        random_dist.reset(
-            new ParetoDist(load.pareto().interarrival_base() * num_threads,
-                           load.pareto().alpha()));
-        break;
       default:
         GPR_ASSERT(false);
     }
diff --git a/test/cpp/qps/interarrival.h b/test/cpp/qps/interarrival.h
index 0cc78533ce067360d0dfc6f03a7096bee30abf59..0980d5e8baadafb012b275ee314382c3bafa123c 100644
--- a/test/cpp/qps/interarrival.h
+++ b/test/cpp/qps/interarrival.h
@@ -82,62 +82,6 @@ class ExpDist GRPC_FINAL : public RandomDistInterface {
   double lambda_recip_;
 };
 
-// UniformDist implements a random distribution that has
-// interarrival time uniformly spread between [lo,hi). The
-// mean interarrival time is (lo+hi)/2. For more information,
-// see http://en.wikipedia.org/wiki/Uniform_distribution_%28continuous%29
-
-class UniformDist GRPC_FINAL : public RandomDistInterface {
- public:
-  UniformDist(double lo, double hi) : lo_(lo), range_(hi - lo) {}
-  ~UniformDist() GRPC_OVERRIDE {}
-  double transform(double uni) const GRPC_OVERRIDE {
-    return uni * range_ + lo_;
-  }
-
- private:
-  double lo_;
-  double range_;
-};
-
-// DetDist provides a random distribution with interarrival time
-// of val. Note that this is not additive, so using this on multiple
-// flows of control (threads within the same client or separate
-// clients) will not preserve any deterministic interarrival gap across
-// requests.
-
-class DetDist GRPC_FINAL : public RandomDistInterface {
- public:
-  explicit DetDist(double val) : val_(val) {}
-  ~DetDist() GRPC_OVERRIDE {}
-  double transform(double uni) const GRPC_OVERRIDE { return val_; }
-
- private:
-  double val_;
-};
-
-// ParetoDist provides a random distribution with interarrival time
-// spread according to a Pareto (heavy-tailed) distribution. In this
-// model, many interarrival times are close to the base, but a sufficient
-// number will be high (up to infinity) as to disturb the mean. It is a
-// good representation of the response times of data center jobs. See
-// http://en.wikipedia.org/wiki/Pareto_distribution
-
-class ParetoDist GRPC_FINAL : public RandomDistInterface {
- public:
-  ParetoDist(double base, double alpha)
-      : base_(base), alpha_recip_(1.0 / alpha) {}
-  ~ParetoDist() GRPC_OVERRIDE {}
-  double transform(double uni) const GRPC_OVERRIDE {
-    // Note: Use 1.0-uni above to avoid div by zero if uni is 0
-    return base_ / pow(1.0 - uni, alpha_recip_);
-  }
-
- private:
-  double base_;
-  double alpha_recip_;
-};
-
 // A class library for generating pseudo-random interarrival times
 // in an efficient re-entrant way. The random table is built at construction
 // time, and each call must include the thread id of the invoker
diff --git a/test/cpp/qps/qps_driver.cc b/test/cpp/qps/qps_driver.cc
index 608181f77fba5b267a6831765204ef98ed7c5a35..e4683e475f21003b49cb77db8bc647d9f4d27743 100644
--- a/test/cpp/qps/qps_driver.cc
+++ b/test/cpp/qps/qps_driver.cc
@@ -68,11 +68,6 @@ DEFINE_string(client_type, "SYNC_CLIENT", "Client type");
 DEFINE_int32(async_client_threads, 1, "Async client threads");
 
 DEFINE_double(poisson_load, -1.0, "Poisson offered load (qps)");
-DEFINE_double(uniform_lo, -1.0, "Uniform low interarrival time (us)");
-DEFINE_double(uniform_hi, -1.0, "Uniform high interarrival time (us)");
-DEFINE_double(determ_load, -1.0, "Deterministic offered load (qps)");
-DEFINE_double(pareto_base, -1.0, "Pareto base interarrival time (us)");
-DEFINE_double(pareto_alpha, -1.0, "Pareto alpha value");
 
 DEFINE_int32(client_core_limit, -1, "Limit on client cores to use");
 
@@ -137,17 +132,6 @@ static void QpsDriver() {
   if (FLAGS_poisson_load > 0.0) {
     auto poisson = client_config.mutable_load_params()->mutable_poisson();
     poisson->set_offered_load(FLAGS_poisson_load);
-  } else if (FLAGS_uniform_lo > 0.0) {
-    auto uniform = client_config.mutable_load_params()->mutable_uniform();
-    uniform->set_interarrival_lo(FLAGS_uniform_lo / 1e6);
-    uniform->set_interarrival_hi(FLAGS_uniform_hi / 1e6);
-  } else if (FLAGS_determ_load > 0.0) {
-    auto determ = client_config.mutable_load_params()->mutable_determ();
-    determ->set_offered_load(FLAGS_determ_load);
-  } else if (FLAGS_pareto_base > 0.0) {
-    auto pareto = client_config.mutable_load_params()->mutable_pareto();
-    pareto->set_interarrival_base(FLAGS_pareto_base / 1e6);
-    pareto->set_alpha(FLAGS_pareto_alpha);
   } else {
     client_config.mutable_load_params()->mutable_closed_loop();
     // No further load parameters to set up for closed loop
diff --git a/test/cpp/qps/qps_interarrival_test.cc b/test/cpp/qps/qps_interarrival_test.cc
index 48585af7566e5c9211f38c4f135a5ed6be4118db..4055c8a718f0dc5009130ad10531f273eb295c46 100644
--- a/test/cpp/qps/qps_interarrival_test.cc
+++ b/test/cpp/qps/qps_interarrival_test.cc
@@ -63,14 +63,8 @@ static void RunTest(RandomDistInterface &&r, int threads, std::string title) {
 }
 
 using grpc::testing::ExpDist;
-using grpc::testing::DetDist;
-using grpc::testing::UniformDist;
-using grpc::testing::ParetoDist;
 
 int main(int argc, char **argv) {
   RunTest(ExpDist(10.0), 5, std::string("Exponential(10)"));
-  RunTest(DetDist(5.0), 5, std::string("Det(5)"));
-  RunTest(UniformDist(0.0, 10.0), 5, std::string("Uniform(0,10)"));
-  RunTest(ParetoDist(1.0, 1.0), 5, std::string("Pareto(1,1)"));
   return 0;
 }