Merge pull request #4998 from ctiller/syscall
Make clock_gettime syscall directly to skirt ABI issues
diff --git a/Makefile b/Makefile
index 56ab33b..631fff8 100644
--- a/Makefile
+++ b/Makefile
@@ -3784,6 +3784,7 @@
test/cpp/qps/client_async.cc \
test/cpp/qps/client_sync.cc \
test/cpp/qps/driver.cc \
+ test/cpp/qps/limit_cores.cc \
test/cpp/qps/perf_db_client.cc \
test/cpp/qps/qps_worker.cc \
test/cpp/qps/report.cc \
@@ -3838,6 +3839,7 @@
$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/limit_cores.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/perf_db_client.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.grpc.pb.cc
$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/services.pb.cc $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.pb.cc $(GENDIR)/src/proto/grpc/testing/perf_db.grpc.pb.cc
@@ -13038,6 +13040,7 @@
test/cpp/qps/client_async.cc: $(OPENSSL_DEP)
test/cpp/qps/client_sync.cc: $(OPENSSL_DEP)
test/cpp/qps/driver.cc: $(OPENSSL_DEP)
+test/cpp/qps/limit_cores.cc: $(OPENSSL_DEP)
test/cpp/qps/perf_db_client.cc: $(OPENSSL_DEP)
test/cpp/qps/qps_worker.cc: $(OPENSSL_DEP)
test/cpp/qps/report.cc: $(OPENSSL_DEP)
diff --git a/build.yaml b/build.yaml
index c4371d3..2a9dc38 100644
--- a/build.yaml
+++ b/build.yaml
@@ -836,6 +836,7 @@
- test/cpp/qps/driver.h
- test/cpp/qps/histogram.h
- test/cpp/qps/interarrival.h
+ - test/cpp/qps/limit_cores.h
- test/cpp/qps/perf_db_client.h
- test/cpp/qps/qps_worker.h
- test/cpp/qps/report.h
@@ -853,6 +854,7 @@
- test/cpp/qps/client_async.cc
- test/cpp/qps/client_sync.cc
- test/cpp/qps/driver.cc
+ - test/cpp/qps/limit_cores.cc
- test/cpp/qps/perf_db_client.cc
- test/cpp/qps/qps_worker.cc
- test/cpp/qps/report.cc
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index 30edbc7..d5294b2 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -153,19 +153,14 @@
#if __GLIBC_PREREQ(2, 10)
#define GPR_LINUX_SOCKETUTILS 1
#endif
-#if __GLIBC_PREREQ(2, 17)
+#endif
#define GPR_LINUX_ENV 1
-#endif
-#endif
#ifndef GPR_LINUX_EVENTFD
#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1
#endif
#ifndef GPR_LINUX_SOCKETUTILS
#define GPR_POSIX_SOCKETUTILS
#endif
-#ifndef GPR_LINUX_ENV
-#define GPR_POSIX_ENV 1
-#endif
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SUBPROCESS 1
diff --git a/src/core/support/env_linux.c b/src/core/support/env_linux.c
index 2e03365..b5832a5 100644
--- a/src/core/support/env_linux.c
+++ b/src/core/support/env_linux.c
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,8 +49,28 @@
#include "src/core/support/string.h"
+/* Declare weak symbols for versions of secure_getenv that *may* be
+ * on a users machine. Older libc's call this __secure_getenv, even
+ * older don't support the functionality.
+ *
+ * If a symbol is not present, these will be equal to NULL.
+ */
+char *__attribute__((weak)) secure_getenv(const char *name);
+char *__attribute__((weak)) __secure_getenv(const char *name);
+
char *gpr_getenv(const char *name) {
- char *result = secure_getenv(name);
+ static char *(*getenv_func)(const char *) = secure_getenv;
+ /* Check to see which getenv variant is supported (go from most
+ * to least secure) */
+ if (getenv_func == NULL) {
+ getenv_func = __secure_getenv;
+ if (getenv_func == NULL) {
+ gpr_log(GPR_DEBUG,
+ "No secure_getenv. Please consider upgrading your libc.");
+ getenv_func = getenv;
+ }
+ }
+ char *result = getenv_func(name);
return result == NULL ? result : gpr_strdup(result);
}
diff --git a/src/csharp/Grpc.Core/Internal/PlatformApis.cs b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
index d71e7ec..f0c5b0f 100644
--- a/src/csharp/Grpc.Core/Internal/PlatformApis.cs
+++ b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
@@ -49,6 +49,7 @@
static readonly bool isLinux;
static readonly bool isMacOSX;
static readonly bool isWindows;
+ static readonly bool isMono;
static PlatformApis()
{
@@ -58,6 +59,7 @@
isMacOSX = (platform == PlatformID.Unix && GetUname() == "Darwin");
isLinux = (platform == PlatformID.Unix && !isMacOSX);
isWindows = (platform == PlatformID.Win32NT || platform == PlatformID.Win32S || platform == PlatformID.Win32Windows);
+ isMono = Type.GetType("Mono.Runtime") != null;
}
public static bool IsLinux
@@ -75,6 +77,11 @@
get { return isWindows; }
}
+ public static bool IsMono
+ {
+ get { return isMono; }
+ }
+
public static bool Is64Bit
{
get { return IntPtr.Size == 8; }
diff --git a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
index e614cab..95a8797 100644
--- a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
+++ b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
@@ -91,6 +91,10 @@
{
if (PlatformApis.IsLinux)
{
+ if (PlatformApis.IsMono)
+ {
+ return Mono.dlsym(this.handle, symbolName);
+ }
return Linux.dlsym(this.handle, symbolName);
}
if (PlatformApis.IsMacOSX)
@@ -122,6 +126,10 @@
}
if (PlatformApis.IsLinux)
{
+ if (PlatformApis.IsMono)
+ {
+ return Mono.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
+ }
return Linux.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
}
if (PlatformApis.IsMacOSX)
@@ -154,5 +162,21 @@
[DllImport("libSystem.dylib")]
internal static extern IntPtr dlsym(IntPtr handle, string symbol);
}
+
+ /// <summary>
+ /// On Linux systems, using using dlopen and dlsym results in
+ /// DllNotFoundException("libdl.so not found") if libc6-dev
+ /// is not installed. As a workaround, we load symbols for
+ /// dlopen and dlsym from the current process as on Linux
+ /// Mono sure is linked against these symbols.
+ /// </summary>
+ private static class Mono
+ {
+ [DllImport("__Internal")]
+ internal static extern IntPtr dlopen(string filename, int flags);
+
+ [DllImport("__Internal")]
+ internal static extern IntPtr dlsym(IntPtr handle, string symbol);
+ }
}
}
diff --git a/src/proto/grpc/testing/control.proto b/src/proto/grpc/testing/control.proto
index 2f352e6..8278836 100644
--- a/src/proto/grpc/testing/control.proto
+++ b/src/proto/grpc/testing/control.proto
@@ -107,6 +107,10 @@
LoadParams load_params = 10;
PayloadConfig payload_config = 11;
HistogramParams histogram_params = 12;
+
+ // Specify the cores we should run the client on, if desired
+ repeated int32 core_list = 13;
+ int32 core_limit = 14;
}
message ClientStatus { ClientStats stats = 1; }
@@ -131,9 +135,13 @@
int32 port = 4;
// Only for async server. Number of threads used to serve the requests.
int32 async_server_threads = 7;
- // restrict core usage, currently unused
+ // Specify the number of cores to limit server to, if desired
int32 core_limit = 8;
+ // payload config, used in generic server
PayloadConfig payload_config = 9;
+
+ // Specify the cores we should run the server on, if desired
+ repeated int32 core_list = 10;
}
message ServerArgs {
@@ -147,6 +155,14 @@
ServerStats stats = 1;
// the port bound by the server
int32 port = 2;
- // Number of cores on the server. See gpr_cpu_num_cores.
+ // Number of cores available to the server
int32 cores = 3;
}
+
+message CoreRequest {
+}
+
+message CoreResponse {
+ // Number of cores available on the server
+ int32 cores = 1;
+}
diff --git a/src/proto/grpc/testing/services.proto b/src/proto/grpc/testing/services.proto
index af285ce..4c8e32b 100644
--- a/src/proto/grpc/testing/services.proto
+++ b/src/proto/grpc/testing/services.proto
@@ -1,4 +1,4 @@
-// Copyright 2015, Google Inc.
+// Copyright 2015-2016, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -62,4 +62,7 @@
// and once the shutdown has finished, the OK status is sent to terminate
// this RPC.
rpc RunClient(stream ClientArgs) returns (stream ClientStatus);
+
+ // Just return the core count - unary call
+ rpc CoreCount(CoreRequest) returns (CoreResponse);
}
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index 97487fd..50b2bf2 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -36,16 +36,20 @@
#include <condition_variable>
#include <mutex>
+#include <vector>
#include <grpc++/support/byte_buffer.h>
#include <grpc++/support/slice.h>
+#include <grpc/support/log.h>
+#include "src/proto/grpc/testing/payloads.grpc.pb.h"
+#include "src/proto/grpc/testing/services.grpc.pb.h"
+
+#include "test/cpp/qps/limit_cores.h"
#include "test/cpp/qps/histogram.h"
#include "test/cpp/qps/interarrival.h"
#include "test/cpp/qps/timer.h"
#include "test/cpp/util/create_test_channel.h"
-#include "src/proto/grpc/testing/payloads.grpc.pb.h"
-#include "src/proto/grpc/testing/services.grpc.pb.h"
namespace grpc {
@@ -320,6 +324,8 @@
std::function<std::unique_ptr<StubType>(std::shared_ptr<Channel>)>
create_stub)
: channels_(config.client_channels()), create_stub_(create_stub) {
+ cores_ = LimitCores(config.core_list().data(), config.core_list_size());
+
for (int i = 0; i < config.client_channels(); i++) {
channels_[i].init(config.server_targets(i % config.server_targets_size()),
config, create_stub_);
@@ -331,6 +337,7 @@
virtual ~ClientImpl() {}
protected:
+ int cores_;
RequestType request_;
class ClientChannelInfo {
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 4229e19..f3f8f37 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -159,6 +159,7 @@
using Client::SetupLoadTest;
using Client::NextIssueTime;
using Client::closed_loop_;
+ using ClientImpl<StubType, RequestType>::cores_;
using ClientImpl<StubType, RequestType>::channels_;
using ClientImpl<StubType, RequestType>::request_;
AsyncClient(const ClientConfig& config,
@@ -345,11 +346,11 @@
private:
bool val_;
};
- static int NumThreads(const ClientConfig& config) {
+ int NumThreads(const ClientConfig& config) {
int num_threads = config.async_client_threads();
if (num_threads <= 0) { // Use dynamic sizing
- num_threads = gpr_cpu_num_cores();
- gpr_log(GPR_INFO, "Sizing client server to %d threads", num_threads);
+ num_threads = cores_;
+ gpr_log(GPR_INFO, "Sizing async client to %d threads", num_threads);
}
return num_threads;
}
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index 490156a..c70b030 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -34,6 +34,7 @@
#include <deque>
#include <list>
#include <thread>
+#include <unordered_map>
#include <vector>
#include <grpc++/channel.h>
@@ -59,7 +60,42 @@
namespace grpc {
namespace testing {
-static deque<string> get_hosts(const string& name) {
+static std::string get_host(const std::string& worker) {
+ char* host;
+ char* port;
+
+ gpr_split_host_port(worker.c_str(), &host, &port);
+ const string s(host);
+
+ gpr_free(host);
+ gpr_free(port);
+ return s;
+}
+
+static std::unordered_map<string, std::deque<int>> get_hosts_and_cores(
+ const deque<string>& workers) {
+ std::unordered_map<string, std::deque<int>> hosts;
+ for (auto it = workers.begin(); it != workers.end(); it++) {
+ const string host = get_host(*it);
+ if (hosts.find(host) == hosts.end()) {
+ auto stub = WorkerService::NewStub(
+ CreateChannel(*it, InsecureChannelCredentials()));
+ grpc::ClientContext ctx;
+ CoreRequest dummy;
+ CoreResponse cores;
+ grpc::Status s = stub->CoreCount(&ctx, dummy, &cores);
+ assert(s.ok());
+ std::deque<int> dq;
+ for (int i = 0; i < cores.cores(); i++) {
+ dq.push_back(i);
+ }
+ hosts[host] = dq;
+ }
+ }
+ return hosts;
+}
+
+static deque<string> get_workers(const string& name) {
char* env = gpr_getenv(name.c_str());
if (!env) return deque<string>();
@@ -105,18 +141,18 @@
std::unique_ptr<ScenarioResult> RunScenario(
const ClientConfig& initial_client_config, size_t num_clients,
- const ServerConfig& server_config, size_t num_servers, int warmup_seconds,
- int benchmark_seconds, int spawn_local_worker_count) {
+ const ServerConfig& initial_server_config, size_t num_servers,
+ int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count) {
// ClientContext allocations (all are destroyed at scope exit)
list<ClientContext> contexts;
// To be added to the result, containing the final configuration used for
// client and config (including host, etc.)
ClientConfig result_client_config;
- ServerConfig result_server_config;
+ const ServerConfig result_server_config = initial_server_config;
// Get client, server lists
- auto workers = get_hosts("QPS_WORKERS");
+ auto workers = get_workers("QPS_WORKERS");
ClientConfig client_config = initial_client_config;
// Spawn some local workers if desired
@@ -143,6 +179,9 @@
}
}
+ // Setup the hosts and core counts
+ auto hosts_cores = get_hosts_and_cores(workers);
+
// if num_clients is set to <=0, do dynamic sizing: all workers
// except for servers are clients
if (num_clients <= 0) {
@@ -172,18 +211,49 @@
i);
servers[i].stub = WorkerService::NewStub(
CreateChannel(workers[i], InsecureChannelCredentials()));
+
+ ServerConfig server_config = initial_server_config;
+ char* host;
+ char* driver_port;
+ char* cli_target;
+ gpr_split_host_port(workers[i].c_str(), &host, &driver_port);
+ string host_str(host);
+ int server_core_limit = initial_server_config.core_limit();
+ int client_core_limit = initial_client_config.core_limit();
+
+ if (server_core_limit == 0 && client_core_limit > 0) {
+ // In this case, limit the server cores if it matches the
+ // same host as one or more clients
+ const auto& dq = hosts_cores.at(host_str);
+ bool match = false;
+ int limit = dq.size();
+ for (size_t cli = 0; cli < num_clients; cli++) {
+ if (host_str == get_host(workers[cli + num_servers])) {
+ limit -= client_core_limit;
+ match = true;
+ }
+ }
+ if (match) {
+ GPR_ASSERT(limit > 0);
+ server_core_limit = limit;
+ }
+ }
+ if (server_core_limit > 0) {
+ auto& dq = hosts_cores.at(host_str);
+ GPR_ASSERT(dq.size() >= static_cast<size_t>(server_core_limit));
+ for (int core = 0; core < server_core_limit; core++) {
+ server_config.add_core_list(dq.front());
+ dq.pop_front();
+ }
+ }
+
ServerArgs args;
- result_server_config = server_config;
*args.mutable_setup() = server_config;
servers[i].stream =
servers[i].stub->RunServer(runsc::AllocContext(&contexts, deadline));
GPR_ASSERT(servers[i].stream->Write(args));
ServerStatus init_status;
GPR_ASSERT(servers[i].stream->Read(&init_status));
- char* host;
- char* driver_port;
- char* cli_target;
- gpr_split_host_port(workers[i].c_str(), &host, &driver_port);
gpr_join_host_port(&cli_target, host, init_status.port());
client_config.add_server_targets(cli_target);
gpr_free(host);
@@ -191,19 +261,50 @@
gpr_free(cli_target);
}
+ // Targets are all set by now
+ result_client_config = client_config;
// Start clients
using runsc::ClientData;
// clients is array rather than std::vector to avoid gcc-4.4 issues
// where class contained in std::vector must have a copy constructor
auto* clients = new ClientData[num_clients];
for (size_t i = 0; i < num_clients; i++) {
- gpr_log(GPR_INFO, "Starting client on %s (worker #%d)",
- workers[i + num_servers].c_str(), i + num_servers);
+ const auto& worker = workers[i + num_servers];
+ gpr_log(GPR_INFO, "Starting client on %s (worker #%d)", worker.c_str(),
+ i + num_servers);
clients[i].stub = WorkerService::NewStub(
- CreateChannel(workers[i + num_servers], InsecureChannelCredentials()));
+ CreateChannel(worker, InsecureChannelCredentials()));
+ ClientConfig per_client_config = client_config;
+
+ int server_core_limit = initial_server_config.core_limit();
+ int client_core_limit = initial_client_config.core_limit();
+ if ((server_core_limit > 0) || (client_core_limit > 0)) {
+ auto& dq = hosts_cores.at(get_host(worker));
+ if (client_core_limit == 0) {
+ // limit client cores if it matches a server host
+ bool match = false;
+ int limit = dq.size();
+ for (size_t srv = 0; srv < num_servers; srv++) {
+ if (get_host(worker) == get_host(workers[srv])) {
+ match = true;
+ }
+ }
+ if (match) {
+ GPR_ASSERT(limit > 0);
+ client_core_limit = limit;
+ }
+ }
+ if (client_core_limit > 0) {
+ GPR_ASSERT(dq.size() >= static_cast<size_t>(client_core_limit));
+ for (int core = 0; core < client_core_limit; core++) {
+ per_client_config.add_core_list(dq.front());
+ dq.pop_front();
+ }
+ }
+ }
+
ClientArgs args;
- result_client_config = client_config;
- *args.mutable_setup() = client_config;
+ *args.mutable_setup() = per_client_config;
clients[i].stream =
clients[i].stub->RunClient(runsc::AllocContext(&contexts, deadline));
GPR_ASSERT(clients[i].stream->Write(args));
diff --git a/test/cpp/qps/limit_cores.cc b/test/cpp/qps/limit_cores.cc
new file mode 100644
index 0000000..c2f3ad8
--- /dev/null
+++ b/test/cpp/qps/limit_cores.cc
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/qps/limit_cores.h"
+
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <vector>
+
+namespace grpc {
+namespace testing {
+
+#ifdef GPR_CPU_LINUX
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sched.h>
+int LimitCores(const int *cores, int cores_size) {
+ const int num_cores = gpr_cpu_num_cores();
+ int cores_set = 0;
+
+ cpu_set_t *cpup = CPU_ALLOC(num_cores);
+ GPR_ASSERT(cpup);
+ const size_t size = CPU_ALLOC_SIZE(num_cores);
+ CPU_ZERO_S(size, cpup);
+
+ if (cores_size > 0) {
+ for (int i = 0; i < cores_size; i++) {
+ if (cores[i] < num_cores) {
+ CPU_SET_S(cores[i], size, cpup);
+ cores_set++;
+ }
+ }
+ } else {
+ for (int i = 0; i < num_cores; i++) {
+ CPU_SET_S(i, size, cpup);
+ cores_set++;
+ }
+ }
+ GPR_ASSERT(sched_setaffinity(0, size, cpup) == 0);
+ CPU_FREE(cpup);
+ return cores_set;
+}
+#else
+// LimitCores is not currently supported for non-Linux platforms
+int LimitCores(std::vector<int> core_vec) { return gpr_cpu_num_cores(); }
+#endif
+} // namespace testing
+} // namespace grpc
diff --git a/test/cpp/qps/limit_cores.h b/test/cpp/qps/limit_cores.h
new file mode 100644
index 0000000..5c0d1e3
--- /dev/null
+++ b/test/cpp/qps/limit_cores.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef TEST_QPS_LIMIT_CORES_H
+#define TEST_QPS_LIMIT_CORES_H
+
+#include <vector>
+
+namespace grpc {
+namespace testing {
+/// LimitCores: allow this worker to only run on the cores specified in the
+/// array \a cores, which is of length \a cores_size.
+///
+/// LimitCores takes array and size arguments (instead of vector) for direct
+/// conversion from repeated field of protobuf. Use a cores_size of 0 to remove
+/// existing limits (from an empty repeated field)
+int LimitCores(const int *cores, int cores_size);
+} // namespace testing
+} // namespace grpc
+
+#endif // TEST_QPS_LIMIT_CORES_H
diff --git a/test/cpp/qps/qps-sweep.sh b/test/cpp/qps/qps-sweep.sh
index 333f4bd..539da1d 100755
--- a/test/cpp/qps/qps-sweep.sh
+++ b/test/cpp/qps/qps-sweep.sh
@@ -57,6 +57,20 @@
--async_client_threads=0 --async_server_threads=0 --secure_test=$secure \
--num_servers=1 --num_clients=0
+ # Scenario 2b: QPS with a single server core
+ "$bins"/opt/qps_driver --rpc_type=STREAMING --client_type=ASYNC_CLIENT \
+ --server_type=ASYNC_GENERIC_SERVER --outstanding_rpcs_per_channel=100 \
+ --client_channels=64 --bbuf_req_size=0 --bbuf_resp_size=0 \
+ --async_client_threads=0 --async_server_threads=0 --secure_test=$secure \
+ --num_servers=1 --num_clients=0 --server_core_limit=1
+
+ # Scenario 2c: protobuf-based QPS
+ "$bins"/opt/qps_driver --rpc_type=STREAMING --client_type=ASYNC_CLIENT \
+ --server_type=ASYNC_SERVER --outstanding_rpcs_per_channel=100 \
+ --client_channels=64 --simple_req_size=0 --simple_resp_size=0 \
+ --async_client_threads=0 --async_server_threads=0 --secure_test=$secure \
+ --num_servers=1 --num_clients=0
+
# Scenario 3: Latency at near-peak load (TBD)
# Scenario 4: Single-channel bidirectional throughput test (like TCP_STREAM).
diff --git a/test/cpp/qps/qps_driver.cc b/test/cpp/qps/qps_driver.cc
index aa3cb68..ffc8a83 100644
--- a/test/cpp/qps/qps_driver.cc
+++ b/test/cpp/qps/qps_driver.cc
@@ -48,14 +48,13 @@
DEFINE_int32(benchmark_seconds, 30, "Benchmark time (in seconds)");
DEFINE_int32(local_workers, 0, "Number of local workers to start");
-// Common config
-DEFINE_string(rpc_type, "UNARY", "Type of RPC: UNARY or STREAMING");
-
// Server config
DEFINE_int32(async_server_threads, 1, "Number of threads for async servers");
DEFINE_string(server_type, "SYNC_SERVER", "Server type");
+DEFINE_int32(server_core_limit, -1, "Limit on server cores to use");
// Client config
+DEFINE_string(rpc_type, "UNARY", "Type of RPC: UNARY or STREAMING");
DEFINE_int32(outstanding_rpcs_per_channel, 1,
"Number of outstanding rpcs per channel");
DEFINE_int32(client_channels, 1, "Number of client channels");
@@ -75,6 +74,8 @@
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");
+
DEFINE_bool(secure_test, false, "Run a secure test");
using grpc::testing::ClientConfig;
@@ -151,10 +152,18 @@
client_config.mutable_histogram_params()->set_max_possible(
Histogram::default_max_possible());
+ if (FLAGS_client_core_limit > 0) {
+ client_config.set_core_limit(FLAGS_client_core_limit);
+ }
+
ServerConfig server_config;
server_config.set_server_type(server_type);
server_config.set_async_server_threads(FLAGS_async_server_threads);
+ if (FLAGS_server_core_limit > 0) {
+ server_config.set_core_limit(FLAGS_server_core_limit);
+ }
+
if (FLAGS_secure_test) {
// Set up security params
SecurityParams security;
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index 6316605..7e9e05f 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -47,6 +47,7 @@
#include <grpc++/server_builder.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
+#include <grpc/support/cpu.h>
#include <grpc/support/histogram.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
@@ -83,15 +84,10 @@
abort();
}
-static void LimitCores(int cores) {}
-
static std::unique_ptr<Server> CreateServer(const ServerConfig& config) {
gpr_log(GPR_INFO, "Starting server of type %s",
ServerType_Name(config.server_type()).c_str());
- if (config.core_limit() > 0) {
- LimitCores(config.core_limit());
- }
switch (config.server_type()) {
case ServerType::SYNC_SERVER:
return CreateSynchronousServer(config);
@@ -138,6 +134,12 @@
return ret;
}
+ Status CoreCount(ServerContext* ctx, const CoreRequest*,
+ CoreResponse* resp) GRPC_OVERRIDE {
+ resp->set_cores(gpr_cpu_num_cores());
+ return Status::OK;
+ }
+
private:
// Protect against multiple clients using this worker at once.
class InstanceGuard {
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
index 196fdac..94a6f8a 100644
--- a/test/cpp/qps/server.h
+++ b/test/cpp/qps/server.h
@@ -34,14 +34,16 @@
#ifndef TEST_QPS_SERVER_H
#define TEST_QPS_SERVER_H
-#include <grpc/support/cpu.h>
#include <grpc++/security/server_credentials.h>
+#include <grpc/support/cpu.h>
+#include <vector>
+#include "src/proto/grpc/testing/control.grpc.pb.h"
+#include "src/proto/grpc/testing/messages.grpc.pb.h"
#include "test/core/end2end/data/ssl_test_data.h"
#include "test/core/util/port.h"
+#include "test/cpp/qps/limit_cores.h"
#include "test/cpp/qps/timer.h"
-#include "src/proto/grpc/testing/messages.grpc.pb.h"
-#include "src/proto/grpc/testing/control.grpc.pb.h"
namespace grpc {
namespace testing {
@@ -49,8 +51,10 @@
class Server {
public:
explicit Server(const ServerConfig& config) : timer_(new Timer) {
+ cores_ = LimitCores(config.core_list().data(), config.core_list_size());
if (config.port()) {
port_ = config.port();
+
} else {
port_ = grpc_pick_unused_port_or_die();
}
@@ -86,7 +90,7 @@
}
int port() const { return port_; }
- int cores() const { return gpr_cpu_num_cores(); }
+ int cores() const { return cores_; }
static std::shared_ptr<ServerCredentials> CreateServerCredentials(
const ServerConfig& config) {
if (config.has_security_params()) {
@@ -103,6 +107,7 @@
private:
int port_;
+ int cores_;
std::unique_ptr<Timer> timer_;
};
diff --git a/test/distrib/csharp/.gitignore b/test/distrib/csharp/.gitignore
new file mode 100644
index 0000000..52503c7
--- /dev/null
+++ b/test/distrib/csharp/.gitignore
@@ -0,0 +1,6 @@
+packages
+*.userprefs
+*.csproj.user
+*.suo
+/TestNugetFeed
+
diff --git a/test/distrib/csharp/DistribTest.sln b/test/distrib/csharp/DistribTest.sln
new file mode 100644
index 0000000..0eca35c
--- /dev/null
+++ b/test/distrib/csharp/DistribTest.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistribTest", "DistribTest\DistribTest.csproj", "{A3E61CC3-3710-49A3-A830-A0066EDBCE2F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A3E61CC3-3710-49A3-A830-A0066EDBCE2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A3E61CC3-3710-49A3-A830-A0066EDBCE2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3E61CC3-3710-49A3-A830-A0066EDBCE2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A3E61CC3-3710-49A3-A830-A0066EDBCE2F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/test/distrib/csharp/DistribTest/.gitignore b/test/distrib/csharp/DistribTest/.gitignore
new file mode 100644
index 0000000..1746e32
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/.gitignore
@@ -0,0 +1,2 @@
+bin
+obj
diff --git a/test/distrib/csharp/DistribTest/App.config b/test/distrib/csharp/DistribTest/App.config
new file mode 100644
index 0000000..30d3e09
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/App.config
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+ </startup>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <dependentAssembly>
+ <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
+ </dependentAssembly>
+ </assemblyBinding>
+ </runtime>
+</configuration>
\ No newline at end of file
diff --git a/test/distrib/csharp/DistribTest/DistribTest.csproj b/test/distrib/csharp/DistribTest/DistribTest.csproj
new file mode 100644
index 0000000..124fc1b
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/DistribTest.csproj
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{A3E61CC3-3710-49A3-A830-A0066EDBCE2F}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DistribTest</RootNamespace>
+ <AssemblyName>DistribTest</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <NuGetPackageImportStamp>b86d820c</NuGetPackageImportStamp>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="BouncyCastle.Crypto">
+ <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.Apis.Auth">
+ <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.Apis.Auth.PlatformServices">
+ <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.Apis.Core">
+ <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="Grpc.Auth">
+ <HintPath>..\packages\Grpc.Auth.__GRPC_NUGET_VERSION__\lib\net45\Grpc.Auth.dll</HintPath>
+ </Reference>
+ <Reference Include="Grpc.Core">
+ <HintPath>..\packages\Grpc.Core.__GRPC_NUGET_VERSION__\lib\net45\Grpc.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.Threading.Tasks">
+ <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.Threading.Tasks.Extensions">
+ <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
+ <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
+ </Reference>
+ <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Interactive.Async">
+ <HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Net" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Net.Http.Extensions">
+ <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Net.Http.Primitives">
+ <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Net.Http.WebRequest" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ <None Include="packages.config" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="..\packages\grpc.native.csharp.__GRPC_NUGET_VERSION__\build\portable-net45+netcore45+wpa81+wp8\grpc.native.csharp.targets" Condition="Exists('..\packages\grpc.native.csharp.__GRPC_NUGET_VERSION__\build\portable-net45+netcore45+wpa81+wp8\grpc.native.csharp.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('..\packages\grpc.native.csharp.__GRPC_NUGET_VERSION__\build\portable-net45+netcore45+wpa81+wp8\grpc.native.csharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.native.csharp.__GRPC_NUGET_VERSION__\build\portable-net45+netcore45+wpa81+wp8\grpc.native.csharp.targets'))" />
+ <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
+ </Target>
+ <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file
diff --git a/test/distrib/csharp/DistribTest/Program.cs b/test/distrib/csharp/DistribTest/Program.cs
new file mode 100644
index 0000000..e944648
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/Program.cs
@@ -0,0 +1,50 @@
+#region Copyright notice and license
+
+// Copyright 2015-2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using Grpc.Core;
+
+namespace TestGrpcPackage
+{
+ class MainClass
+ {
+ public static void Main(string[] args)
+ {
+ // This code doesn't do much but makes sure the native extension is loaded
+ // which is what we are testing here.
+ Channel c = new Channel("127.0.0.1:1000", ChannelCredentials.Insecure);
+ c.ShutdownAsync().Wait();
+ Console.WriteLine("Success!");
+ }
+ }
+}
diff --git a/test/distrib/csharp/DistribTest/Properties/AssemblyInfo.cs b/test/distrib/csharp/DistribTest/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..bc25b26
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DistribTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DistribTest")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("68755789-f103-4134-9027-b80a2fc9e21a")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/test/distrib/csharp/DistribTest/packages.config b/test/distrib/csharp/DistribTest/packages.config
new file mode 100644
index 0000000..aca09f6
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/packages.config
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
+ <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
+ <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
+ <package id="Grpc" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
+ <package id="Grpc.Auth" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
+ <package id="Grpc.Core" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
+ <package id="grpc.native.csharp" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
+ <package id="Ix-Async" version="1.2.3" targetFramework="net45" />
+ <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
+ <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
+ <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
+ <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
+ <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
+</packages>
\ No newline at end of file
diff --git a/test/distrib/csharp/NuGet.Config b/test/distrib/csharp/NuGet.Config
new file mode 100644
index 0000000..e3b63c5
--- /dev/null
+++ b/test/distrib/csharp/NuGet.Config
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <packageSources>
+ <add key="https://www.nuget.org/api/v2/" value="https://www.nuget.org/api/v2/" />
+ <add key="TestNugetFeed" value="TestNugetFeed" />
+ </packageSources>
+</configuration>
+
diff --git a/test/distrib/csharp/build_vs2015.bat b/test/distrib/csharp/build_vs2015.bat
new file mode 100644
index 0000000..50485a3
--- /dev/null
+++ b/test/distrib/csharp/build_vs2015.bat
@@ -0,0 +1,10 @@
+@rem Convenience wrapper that runs specified gRPC target using msbuild
+@rem Usage: build.bat TARGET_NAME
+
+setlocal
+@rem Set VS variables (uses Visual Studio 2015)
+@call "%VS140COMNTOOLS%\..\..\vc\vcvarsall.bat" x86
+
+msbuild %*
+exit /b %ERRORLEVEL%
+endlocal
diff --git a/test/distrib/csharp/run_distrib_test.sh b/test/distrib/csharp/run_distrib_test.sh
new file mode 100755
index 0000000..5a01c91
--- /dev/null
+++ b/test/distrib/csharp/run_distrib_test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -ex
+
+cd $(dirname $0)
+
+unzip "$EXTERNAL_GIT_ROOT/input_artifacts/csharp_nugets.zip" -d TestNugetFeed
+
+# TODO(jtattermusch): replace the version number
+./update_version.sh 0.13.0
+
+nuget restore
+
+xbuild DistribTest.sln
+
+mono DistribTest/bin/Debug/DistribTest.exe
diff --git a/test/distrib/csharp/update_version.sh b/test/distrib/csharp/update_version.sh
new file mode 100755
index 0000000..569f7ff
--- /dev/null
+++ b/test/distrib/csharp/update_version.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -e
+
+cd $(dirname $0)
+
+# Replaces version placeholder with value provided as first argument.
+sed -i "s/__GRPC_NUGET_VERSION__/$1/g" DistribTest/packages.config DistribTest/DistribTest.csproj
diff --git a/tools/dockerfile/distribtest/csharp_jessie_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_jessie_x64/Dockerfile
new file mode 100644
index 0000000..ec400b6
--- /dev/null
+++ b/tools/dockerfile/distribtest/csharp_jessie_x64/Dockerfile
@@ -0,0 +1,47 @@
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FROM debian:jessie
+
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+
+RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y \
+ mono-devel \
+ ca-certificates-mono \
+ nuget
+
+RUN apt-get update && apt-get install -y git unzip
+
+# TODO(jtattermusch): Currently P/Invoke to dlopen will fail without libc6-dev
+# being installed. Figure out how to workaround this.
+RUN apt-get update && apt-get install -y libc6-dev
diff --git a/tools/dockerfile/distribtest/csharp_jessie_x86/Dockerfile b/tools/dockerfile/distribtest/csharp_jessie_x86/Dockerfile
new file mode 100644
index 0000000..f8e6401
--- /dev/null
+++ b/tools/dockerfile/distribtest/csharp_jessie_x86/Dockerfile
@@ -0,0 +1,47 @@
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FROM 32bit/debian:jessie
+
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+
+RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y \
+ mono-devel \
+ ca-certificates-mono \
+ nuget
+
+RUN apt-get update && apt-get install -y git unzip
+
+# TODO(jtattermusch): Currently P/Invoke to dlopen will fail without libc6-dev
+# being installed. Figure out how to workaround this.
+RUN apt-get update && apt-get install -y libc6-dev
diff --git a/tools/dockerfile/distribtest/csharp_wheezy_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_wheezy_x64/Dockerfile
new file mode 100644
index 0000000..4f54f14
--- /dev/null
+++ b/tools/dockerfile/distribtest/csharp_wheezy_x64/Dockerfile
@@ -0,0 +1,36 @@
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FROM mono:4.2.2.30
+
+RUN apt-get update && apt-get install -y git unzip
+
+# TODO(jtattermusch): Currently P/Invoke to dlopen will fail without libc6-dev
+# being installed. Figure out how to workaround this.
+RUN apt-get update && apt-get install -y libc6-dev
diff --git a/tools/dockerfile/grpc_artifact_linux_x64/Dockerfile b/tools/dockerfile/grpc_artifact_linux_x64/Dockerfile
index de40247..80c719d 100644
--- a/tools/dockerfile/grpc_artifact_linux_x64/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_linux_x64/Dockerfile
@@ -58,6 +58,20 @@
wget \
zip && apt-get clean
+
+##################
+# Python dependencies
+
+RUN apt-get update && apt-get install -y \
+ python-all-dev \
+ python3-all-dev \
+ python-pip
+
+RUN pip install pip --upgrade
+RUN pip install virtualenv
+RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.0.0a2 tox
+
+
RUN mkdir /var/local/jenkins
# Define the default command.
diff --git a/tools/dockerfile/grpc_artifact_linux_x86/Dockerfile b/tools/dockerfile/grpc_artifact_linux_x86/Dockerfile
index 774b452..e033a81 100644
--- a/tools/dockerfile/grpc_artifact_linux_x86/Dockerfile
+++ b/tools/dockerfile/grpc_artifact_linux_x86/Dockerfile
@@ -58,6 +58,20 @@
wget \
zip && apt-get clean
+
+##################
+# Python dependencies
+
+RUN apt-get update && apt-get install -y \
+ python-all-dev \
+ python3-all-dev \
+ python-pip
+
+# Install Python packages from PyPI
+RUN pip install pip --upgrade
+RUN pip install virtualenv
+RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.0.0a2 tox
+
RUN mkdir /var/local/jenkins
# Define the default command.
diff --git a/tools/jenkins/build_and_run_docker.sh b/tools/jenkins/build_and_run_docker.sh
index c48f58e..b77cd0a 100755
--- a/tools/jenkins/build_and_run_docker.sh
+++ b/tools/jenkins/build_and_run_docker.sh
@@ -43,6 +43,7 @@
# Inputs
# DOCKERFILE_DIR - Directory in which Dockerfile file is located.
# DOCKER_RUN_SCRIPT - Script to run under docker (relative to grpc repo root)
+# OUTPUT_DIR - Directory that will be copied from inside docker after finishing.
# $@ - Extra args to pass to docker run
# Use image name based on Dockerfile location checksum
@@ -57,6 +58,7 @@
# Run command inside docker
docker run \
"$@" \
+ -e EXTERNAL_GIT_ROOT="/var/local/jenkins/grpc" \
-e THIS_IS_REALLY_NEEDED='see https://github.com/docker/docker/issues/14203 for why docker is awful' \
-v "$git_root:/var/local/jenkins/grpc:ro" \
-w /var/local/git/grpc \
diff --git a/tools/jenkins/docker_run.sh b/tools/jenkins/docker_run.sh
index 1905926..3cc21c4 100755
--- a/tools/jenkins/docker_run.sh
+++ b/tools/jenkins/docker_run.sh
@@ -34,7 +34,7 @@
set -e
mkdir -p /var/local/git
-git clone --recursive /var/local/jenkins/grpc /var/local/git/grpc
+git clone --recursive "$EXTERNAL_GIT_ROOT" /var/local/git/grpc
cd /var/local/git/grpc
diff --git a/tools/run_tests/artifact_targets.py b/tools/run_tests/artifact_targets.py
index a34fa8e..32a98c9 100644
--- a/tools/run_tests/artifact_targets.py
+++ b/tools/run_tests/artifact_targets.py
@@ -80,6 +80,34 @@
return {'CFLAGS': arch_arg, 'LDFLAGS': arch_arg}
+class PythonArtifact:
+ """Builds Python artifacts."""
+
+ def __init__(self, platform, arch):
+ self.name = 'python_%s_%s' % (platform, arch)
+ self.platform = platform
+ self.arch = arch
+ self.labels = ['artifact', 'python', platform, arch]
+
+ def pre_build_jobspecs(self):
+ return []
+
+ def build_jobspec(self):
+ if self.platform == 'windows':
+ raise Exception('Not supported yet.')
+ else:
+ if self.platform == 'linux':
+ return create_docker_jobspec(self.name,
+ 'tools/dockerfile/grpc_artifact_linux_%s' % self.arch,
+ 'tools/run_tests/build_artifact_python.sh')
+ else:
+ return create_jobspec(self.name,
+ ['tools/run_tests/build_artifact_python.sh'])
+
+ def __str__(self):
+ return self.name
+
+
class CSharpExtArtifact:
"""Builds C# native extension library"""
@@ -134,4 +162,6 @@
CSharpExtArtifact('macos', 'x86'),
CSharpExtArtifact('macos', 'x64'),
CSharpExtArtifact('windows', 'x86'),
- CSharpExtArtifact('windows', 'x64')]
+ CSharpExtArtifact('windows', 'x64'),
+ PythonArtifact('linux', 'x86'),
+ PythonArtifact('linux', 'x64')]
diff --git a/tools/run_tests/build_artifact_python.sh b/tools/run_tests/build_artifact_python.sh
new file mode 100755
index 0000000..0ff6b2f
--- /dev/null
+++ b/tools/run_tests/build_artifact_python.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -ex
+
+cd $(dirname $0)/../..
+
+pip install --upgrade six
+pip install --upgrade setuptools
+
+pip install -rrequirements.txt
+
+GRPC_PYTHON_BUILD_WITH_CYTHON=1 python setup.py \
+ bdist_wheel \
+ sdist \
+ bdist_egg_grpc_custom
+
+mkdir -p artifacts
+
+cp -r dist/* artifacts
\ No newline at end of file
diff --git a/tools/run_tests/distribtest_targets.py b/tools/run_tests/distribtest_targets.py
new file mode 100644
index 0000000..aa1d55a
--- /dev/null
+++ b/tools/run_tests/distribtest_targets.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Definition of targets run distribution package tests."""
+
+import jobset
+
+
+def create_docker_jobspec(name, dockerfile_dir, shell_command, environ={},
+ flake_retries=0, timeout_retries=0):
+ """Creates jobspec for a task running under docker."""
+ environ = environ.copy()
+ environ['RUN_COMMAND'] = shell_command
+
+ docker_args=[]
+ for k,v in environ.iteritems():
+ docker_args += ['-e', '%s=%s' % (k, v)]
+ docker_env = {'DOCKERFILE_DIR': dockerfile_dir,
+ 'DOCKER_RUN_SCRIPT': 'tools/jenkins/docker_run.sh'}
+ jobspec = jobset.JobSpec(
+ cmdline=['tools/jenkins/build_and_run_docker.sh'] + docker_args,
+ environ=docker_env,
+ shortname='distribtest.%s' % (name),
+ timeout_seconds=30*60,
+ flake_retries=flake_retries,
+ timeout_retries=timeout_retries)
+ return jobspec
+
+
+class CSharpDistribTest:
+ """Tests C# NuGet package"""
+
+ def __init__(self, platform, arch, docker_suffix):
+ self.name = 'csharp_nuget_%s_%s_%s' % (platform, arch, docker_suffix)
+ self.platform = platform
+ self.arch = arch
+ self.docker_suffix = docker_suffix
+ self.labels = ['distribtest', 'csharp', platform, arch]
+
+ def pre_build_jobspecs(self):
+ return []
+
+ def build_jobspec(self):
+ if not self.platform == 'linux':
+ raise Exception("Not supported yet.")
+
+ return create_docker_jobspec(self.name,
+ 'tools/dockerfile/distribtest/csharp_%s_%s' % (
+ self.docker_suffix,
+ self.arch),
+ 'test/distrib/csharp/run_distrib_test.sh')
+
+ def __str__(self):
+ return self.name
+
+
+def targets():
+ """Gets list of supported targets"""
+ return [CSharpDistribTest('linux', 'x64', 'wheezy'),
+ CSharpDistribTest('linux', 'x64', 'jessie'),
+ CSharpDistribTest('linux', 'x86', 'jessie')]
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index aef0023..1723856 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -4613,6 +4613,7 @@
"test/cpp/qps/driver.h",
"test/cpp/qps/histogram.h",
"test/cpp/qps/interarrival.h",
+ "test/cpp/qps/limit_cores.h",
"test/cpp/qps/perf_db_client.h",
"test/cpp/qps/qps_worker.h",
"test/cpp/qps/report.h",
@@ -4631,6 +4632,8 @@
"test/cpp/qps/driver.h",
"test/cpp/qps/histogram.h",
"test/cpp/qps/interarrival.h",
+ "test/cpp/qps/limit_cores.cc",
+ "test/cpp/qps/limit_cores.h",
"test/cpp/qps/perf_db_client.cc",
"test/cpp/qps/perf_db_client.h",
"test/cpp/qps/qps_worker.cc",
diff --git a/tools/run_tests/task_runner.py b/tools/run_tests/task_runner.py
old mode 100644
new mode 100755
index 39b15cc..e5ecc48
--- a/tools/run_tests/task_runner.py
+++ b/tools/run_tests/task_runner.py
@@ -37,10 +37,12 @@
import sys
import artifact_targets
+import distribtest_targets
import package_targets
_TARGETS = []
_TARGETS += artifact_targets.targets()
+_TARGETS += distribtest_targets.targets()
_TARGETS += package_targets.targets()
def _create_build_map():
diff --git a/vsprojects/vcxproj/qps/qps.vcxproj b/vsprojects/vcxproj/qps/qps.vcxproj
index 7df2597..8306e2e 100644
--- a/vsprojects/vcxproj/qps/qps.vcxproj
+++ b/vsprojects/vcxproj/qps/qps.vcxproj
@@ -151,6 +151,7 @@
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\driver.h" />
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\histogram.h" />
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\interarrival.h" />
+ <ClInclude Include="$(SolutionDir)\..\test\cpp\qps\limit_cores.h" />
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\perf_db_client.h" />
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\qps_worker.h" />
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\report.h" />
@@ -214,6 +215,8 @@
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\test\cpp\qps\driver.cc">
</ClCompile>
+ <ClCompile Include="$(SolutionDir)\..\test\cpp\qps\limit_cores.cc">
+ </ClCompile>
<ClCompile Include="$(SolutionDir)\..\test\cpp\qps\perf_db_client.cc">
</ClCompile>
<ClCompile Include="$(SolutionDir)\..\test\cpp\qps\qps_worker.cc">
diff --git a/vsprojects/vcxproj/qps/qps.vcxproj.filters b/vsprojects/vcxproj/qps/qps.vcxproj.filters
index 14e18e2..650116a 100644
--- a/vsprojects/vcxproj/qps/qps.vcxproj.filters
+++ b/vsprojects/vcxproj/qps/qps.vcxproj.filters
@@ -28,6 +28,9 @@
<ClCompile Include="$(SolutionDir)\..\test\cpp\qps\driver.cc">
<Filter>test\cpp\qps</Filter>
</ClCompile>
+ <ClCompile Include="$(SolutionDir)\..\test\cpp\qps\limit_cores.cc">
+ <Filter>test\cpp\qps</Filter>
+ </ClCompile>
<ClCompile Include="$(SolutionDir)\..\test\cpp\qps\perf_db_client.cc">
<Filter>test\cpp\qps</Filter>
</ClCompile>
@@ -63,6 +66,9 @@
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\interarrival.h">
<Filter>test\cpp\qps</Filter>
</ClInclude>
+ <ClInclude Include="$(SolutionDir)\..\test\cpp\qps\limit_cores.h">
+ <Filter>test\cpp\qps</Filter>
+ </ClInclude>
<ClInclude Include="$(SolutionDir)\..\test\cpp\qps\perf_db_client.h">
<Filter>test\cpp\qps</Filter>
</ClInclude>