Merge pull request #2291 from jcanizales/skylark-objc-library

Add Bazel macros to depend on an automatically-generated gRPC library
diff --git a/Makefile b/Makefile
index 5ba89be..eaabdbe 100644
--- a/Makefile
+++ b/Makefile
@@ -157,7 +157,7 @@
 LDXX_asan = clang++
 CPPFLAGS_asan = -O0 -fsanitize=address -fno-omit-frame-pointer
 LDFLAGS_asan = -fsanitize=address
-DEFINES_asan = GRPC_TEST_SLOWDOWN_BUILD_FACTOR=5
+DEFINES_asan = GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
 
 VALID_CONFIG_msan = 1
 REQUIRE_CUSTOM_LIBRARIES_msan = 1
@@ -168,7 +168,7 @@
 CPPFLAGS_msan = -O0 -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1
 OPENSSL_CFLAGS_msan = -DPURIFY
 LDFLAGS_msan = -fsanitize=memory -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1
-DEFINES_msan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=20
+DEFINES_msan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=4
 
 VALID_CONFIG_ubsan = 1
 REQUIRE_CUSTOM_LIBRARIES_ubsan = 1
@@ -179,7 +179,7 @@
 CPPFLAGS_ubsan = -O1 -fsanitize=undefined -fno-omit-frame-pointer
 OPENSSL_CFLAGS_ubsan = -DPURIFY
 LDFLAGS_ubsan = -fsanitize=undefined
-DEFINES_ubsan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=10
+DEFINES_ubsan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
 
 VALID_CONFIG_gcov = 1
 CC_gcov = gcc
@@ -333,6 +333,19 @@
 
 HAS_PKG_CONFIG = $(shell command -v pkg-config >/dev/null 2>&1 && echo true || echo false)
 
+PC_TEMPLATE = prefix=$(prefix)\nexec_prefix=\$${prefix}\nincludedir=\$${prefix}/include\nlibdir=\$${exec_prefix}/lib\n\nName: $(PC_NAME)\nDescription: $(PC_DESCRIPTION)\nVersion: $(VERSION)\nCflags: -I\$${includedir} $(PC_CFLAGS)\nRequires.private: $(PC_REQUIRES_PRIVATE)\nLibs: -L\$${libdir}\nLibs.private: $(PC_LIBS_PRIVATE)
+
+# gpr .pc file
+PC_NAME = gRPC Portable Runtime
+PC_DESCRIPTION = gRPC Portable Runtime
+PC_CFLAGS = -pthread
+PC_REQUIRES_PRIVATE =
+PC_LIBS_PRIVATE = -lpthread
+ifeq ($(SYSTEM),Darwin)
+PC_LIBS_PRIVATE += -lrt
+endif
+GPR_PC_FILE := $(PC_TEMPLATE)
+
 ifeq ($(SYSTEM),MINGW32)
 SHARED_EXT = dll
 endif
@@ -446,6 +459,9 @@
 HAS_EMBEDDED_PROTOBUF = true
 endif
 
+PC_REQUIRES_GRPC = gpr
+PC_LIBS_GRPC =
+
 ifeq ($(HAS_SYSTEM_ZLIB),false)
 ifeq ($(HAS_EMBEDDED_ZLIB),true)
 ZLIB_DEP = $(LIBDIR)/$(CONFIG)/zlib/libz.a
@@ -458,14 +474,21 @@
 ifeq ($(HAS_PKG_CONFIG),true)
 CPPFLAGS += $(shell pkg-config --cflags zlib)
 LDFLAGS += $(shell pkg-config --libs-only-L zlib)
+PC_REQUIRES_GRPC += zlib
+else
+PC_LIBS_GRPC += -lz
 endif
 endif
 
 OPENSSL_PKG_CONFIG = false
 
+PC_REQUIRES_SECURE =
+PC_LIBS_SECURE =
+
 ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true)
 ifeq ($(HAS_PKG_CONFIG),true)
 OPENSSL_PKG_CONFIG = true
+PC_REQUIRES_SECURE = openssl
 CPPFLAGS := $(shell pkg-config --cflags openssl) $(CPPFLAGS)
 LDFLAGS_OPENSSL_PKG_CONFIG = $(shell pkg-config --libs-only-L openssl)
 ifeq ($(SYSTEM),Linux)
@@ -478,6 +501,7 @@
 LIBS_SECURE = $(OPENSSL_LIBS)
 ifeq ($(OPENSSL_REQUIRES_DL),true)
 LIBS_SECURE += dl
+PC_LIBS_SECURE = $(addprefix -l, $(LIBS_SECURE))
 endif
 endif
 else
@@ -501,11 +525,31 @@
 LDLIBS_SECURE += $(addprefix -l, $(LIBS_SECURE))
 endif
 
+# grpc .pc file
+PC_NAME = gRPC
+PC_DESCRIPTION = high performance general RPC framework
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC) $(PC_REQUIRES_SECURE)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPC) $(PC_LIBS_SECURE)
+GRPC_PC_FILE := $(PC_TEMPLATE)
+
+# gprc_unsecure .pc file
+PC_NAME = gRPC unsecure
+PC_DESCRIPTION = high performance general RPC framework without SSL
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPC)
+GRPC_UNSECURE_PC_FILE := $(PC_TEMPLATE)
+
 PROTOBUF_PKG_CONFIG = false
 
+PC_REQUIRES_GRPCXX =
+PC_LIBS_GRPCXX =
+
 ifeq ($(HAS_SYSTEM_PROTOBUF),true)
 ifeq ($(HAS_PKG_CONFIG),true)
 PROTOBUF_PKG_CONFIG = true
+PC_REQUIRES_GRPCXX = protobuf
 CPPFLAGS := $(shell pkg-config --cflags protobuf) $(CPPFLAGS)
 LDFLAGS_PROTOBUF_PKG_CONFIG = $(shell pkg-config --libs-only-L protobuf)
 ifeq ($(SYSTEM),Linux)
@@ -513,6 +557,8 @@
 LDFLAGS_PROTOBUF_PKG_CONFIG += $(shell pkg-config --libs-only-L protobuf | sed s/L/Wl,-rpath,/)
 endif
 endif
+else
+PC_LIBS_GRPCXX = -lprotobuf
 endif
 else
 ifeq ($(HAS_EMBEDDED_PROTOBUF),true)
@@ -536,6 +582,22 @@
 LDLIBS_PROTOBUF += $(addprefix -l, $(LIBS_PROTOBUF))
 endif
 
+# grpc++ .pc file
+PC_NAME = gRPC++
+PC_DESCRIPTION = C++ wrapper for gRPC
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = grpc $(PC_REQUIRES_GRPCXX)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX)
+GRPCXX_PC_FILE := $(PC_TEMPLATE)
+
+# grpc++_unsecure .pc file
+PC_NAME = gRPC++ unsecure
+PC_DESCRIPTION = C++ wrapper for gRPC without SSL
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = grpc_unsecure $(PC_REQUIRES_GRPCXX)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX)
+GRPCXX_UNSECURE_PC_FILE := $(PC_TEMPLATE)
+
 ifeq ($(MAKECMDGOALS),clean)
 NO_DEPS = true
 endif
@@ -1296,15 +1358,15 @@
 
 static: static_c static_cxx
 
-static_c:  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a
+static_c: pc_c pc_c_unsecure  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a
 
-static_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a
+static_cxx: pc_cxx pc_cxx_unsecure pc_gpr $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a
 
 shared: shared_c shared_cxx
 
-shared_c:  $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT)
+shared_c: pc_c pc_c_unsecure pc_gpr $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT)
 
-shared_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT)
+shared_cxx: pc_cxx pc_cxx_unsecure  $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT)
 
 shared_csharp: shared_c  $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT)
 grpc_csharp_ext: shared_csharp
@@ -1314,6 +1376,15 @@
 privatelibs: privatelibs_c privatelibs_cxx
 
 privatelibs_c:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack_uds_posix.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_fullstack_with_poll.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack_with_poll.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_with_grpc_trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a $(LIBDIR)/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_payload_and_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request_with_high_initial_sequence_number.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libbad_client_test.a
+pc_gpr: $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc
+
+pc_c: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc
+
+pc_c_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc
+
+pc_cxx: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc
+
+pc_cxx_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc
 
 privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a
 
@@ -2530,6 +2601,31 @@
 	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT)
 endif
 
+$(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GPR_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPC_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPC_UNSECURE_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPCXX_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPCXX_UNSECURE_PC_FILE)" >$@
+
 ifeq ($(NO_PROTOC),true)
 $(GENDIR)/examples/pubsub/empty.pb.cc: protoc_dep_error
 $(GENDIR)/examples/pubsub/empty.grpc.pb.cc: protoc_dep_error
@@ -2576,6 +2672,21 @@
 endif
 
 ifeq ($(NO_PROTOC),true)
+$(GENDIR)/test/cpp/qps/perf_db.pb.cc: protoc_dep_error
+$(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc: protoc_dep_error
+else
+$(GENDIR)/test/cpp/qps/perf_db.pb.cc: test/cpp/qps/perf_db.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
+
+$(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc: test/cpp/qps/perf_db.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
+endif
+
+ifeq ($(NO_PROTOC),true)
 $(GENDIR)/test/cpp/qps/qpstest.pb.cc: protoc_dep_error
 $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc: protoc_dep_error
 else
@@ -2737,7 +2848,7 @@
 
 install-static: install-static_c install-static_cxx
 
-install-static_c: static_c strip-static_c
+install-static_c: static_c strip-static_c install-pkg-config_c
 	$(E) "[INSTALL] Installing libgpr.a"
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgpr.a $(prefix)/lib/libgpr.a
@@ -2748,7 +2859,7 @@
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(prefix)/lib/libgrpc_unsecure.a
 
-install-static_cxx: static_cxx strip-static_cxx
+install-static_cxx: static_cxx strip-static_cxx install-pkg-config_cxx
 	$(E) "[INSTALL] Installing libgrpc++.a"
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(prefix)/lib/libgrpc++.a
@@ -2758,7 +2869,7 @@
 
 
 
-install-shared_c: shared_c strip-shared_c
+install-shared_c: shared_c strip-shared_c install-pkg-config_c
 ifeq ($(SYSTEM),MINGW32)
 	$(E) "[INSTALL] Installing gpr.$(SHARED_EXT)"
 	$(Q) $(INSTALL) -d $(prefix)/lib
@@ -2808,7 +2919,7 @@
 endif
 
 
-install-shared_cxx: shared_cxx strip-shared_cxx install-shared_c
+install-shared_cxx: shared_cxx strip-shared_cxx install-shared_c install-pkg-config_cxx
 ifeq ($(SYSTEM),MINGW32)
 	$(E) "[INSTALL] Installing grpc++.$(SHARED_EXT)"
 	$(Q) $(INSTALL) -d $(prefix)/lib
@@ -2883,6 +2994,19 @@
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_ruby_plugin $(prefix)/bin/grpc_ruby_plugin
 endif
 
+install-pkg-config_c: pc_gpr pc_c pc_c_unsecure
+	$(E) "[INSTALL] Installing C pkg-config files"
+	$(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc $(prefix)/lib/pkgconfig/gpr.pc
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc $(prefix)/lib/pkgconfig/grpc.pc
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc $(prefix)/lib/pkgconfig/grpc_unsecure.pc
+
+install-pkg-config_cxx: pc_cxx pc_cxx_unsecure
+	$(E) "[INSTALL] Installing C++ pkg-config files"
+	$(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc $(prefix)/lib/pkgconfig/grpc++.pc
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc $(prefix)/lib/pkgconfig/grpc++_unsecure.pc
+
 install-certs: etc/roots.pem
 	$(E) "[INSTALL] Installing root certificates"
 	$(Q) $(INSTALL) -d $(prefix)/share/grpc
@@ -4112,9 +4236,11 @@
 
 LIBQPS_SRC = \
     $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc \
+    $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc \
     test/cpp/qps/client_async.cc \
     test/cpp/qps/client_sync.cc \
     test/cpp/qps/driver.cc \
+    test/cpp/qps/perf_db_client.cc \
     test/cpp/qps/qps_worker.cc \
     test/cpp/qps/report.cc \
     test/cpp/qps/server_async.cc \
@@ -4164,15 +4290,16 @@
 -include $(LIBQPS_OBJS:.o=.dep)
 endif
 endif
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/perf_db_client.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
 
 
 LIBGRPC_CSHARP_EXT_SRC = \
diff --git a/build.json b/build.json
index 73b95ab..cbe7b61 100644
--- a/build.json
+++ b/build.json
@@ -748,6 +748,7 @@
         "test/cpp/qps/driver.h",
         "test/cpp/qps/histogram.h",
         "test/cpp/qps/interarrival.h",
+        "test/cpp/qps/perf_db_client.h",
         "test/cpp/qps/qps_worker.h",
         "test/cpp/qps/report.h",
         "test/cpp/qps/server.h",
@@ -757,9 +758,11 @@
       ],
       "src": [
         "test/cpp/qps/qpstest.proto",
+        "test/cpp/qps/perf_db.proto",
         "test/cpp/qps/client_async.cc",
         "test/cpp/qps/client_sync.cc",
         "test/cpp/qps/driver.cc",
+        "test/cpp/qps/perf_db_client.cc",
         "test/cpp/qps/qps_worker.cc",
         "test/cpp/qps/report.cc",
         "test/cpp/qps/server_async.cc",
diff --git a/gRPC.podspec b/gRPC.podspec
index 1a4dfd4..138cf96 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -494,9 +494,9 @@
     BAD_TIME="$DIR_TIME/time.h"
     GOOD_TIME="$DIR_TIME/grpc_time.h"
     grep -rl "$BAD_TIME" grpc src/core src/objective-c/GRPCClient | xargs sed -i '' -e s@$BAD_TIME@$GOOD_TIME@g
-    if [ -f "include/$BAD_TIME" ];
+    if [ -f "$BAD_TIME" ];
     then
-      mv -f "include/$BAD_TIME" "include/$GOOD_TIME"
+      mv -f "$BAD_TIME" "$GOOD_TIME"
     fi
 
     DIR_STRING="src/core/support"
diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h
index a62054a..913e2a7 100644
--- a/include/grpc/byte_buffer.h
+++ b/include/grpc/byte_buffer.h
@@ -102,6 +102,10 @@
 int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
                                  gpr_slice *slice);
 
+/** Returns a RAW byte buffer instance from the output of \a reader. */
+grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
+    grpc_byte_buffer_reader *reader);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c
index d12974c..632d2a4 100644
--- a/src/core/iomgr/fd_posix.c
+++ b/src/core/iomgr/fd_posix.c
@@ -74,6 +74,7 @@
   gpr_mu_lock(&fd_freelist_mu);
   fd->freelist_next = fd_freelist;
   fd_freelist = fd;
+  grpc_iomgr_unregister_object(&fd->iomgr_object);
   gpr_mu_unlock(&fd_freelist_mu);
 }
 
@@ -139,7 +140,6 @@
 #endif
   old = gpr_atm_full_fetch_add(&fd->refst, -n);
   if (old == n) {
-    grpc_iomgr_unregister_object(&fd->iomgr_object);
     freelist_fd(fd);
   } else {
     GPR_ASSERT(old > n);
@@ -368,16 +368,17 @@
     watcher->fd = NULL;
     watcher->pollset = NULL;
     gpr_mu_unlock(&fd->watcher_mu);
+    GRPC_FD_UNREF(fd, "poll");
     return 0;
   }
   /* if there is nobody polling for read, but we need to, then start doing so */
-  if (!fd->read_watcher && gpr_atm_acq_load(&fd->readst) > READY) {
+  if (read_mask && !fd->read_watcher && gpr_atm_acq_load(&fd->readst) > READY) {
     fd->read_watcher = watcher;
     mask |= read_mask;
   }
   /* if there is nobody polling for write, but we need to, then start doing so
    */
-  if (!fd->write_watcher && gpr_atm_acq_load(&fd->writest) > READY) {
+  if (write_mask && !fd->write_watcher && gpr_atm_acq_load(&fd->writest) > READY) {
     fd->write_watcher = watcher;
     mask |= write_mask;
   }
diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c
index dcf08d3..1900bbf 100644
--- a/src/core/iomgr/pollset_multipoller_with_epoll.c
+++ b/src/core/iomgr/pollset_multipoller_with_epoll.c
@@ -54,17 +54,25 @@
   pollset_hdr *h = pollset->data.ptr;
   struct epoll_event ev;
   int err;
+  grpc_fd_watcher watcher;
 
-  ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
-  ev.data.ptr = fd;
-  err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev);
-  if (err < 0) {
-    /* FDs may be added to a pollset multiple times, so EEXIST is normal. */
-    if (errno != EEXIST) {
-      gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd,
-              strerror(errno));
+  /* We pretend to be polling whilst adding an fd to keep the fd from being
+     closed during the add. This may result in a spurious wakeup being assigned
+     to this pollset whilst adding, but that should be benign. */
+  GPR_ASSERT(grpc_fd_begin_poll(fd, pollset, 0, 0, &watcher) == 0);
+  if (watcher.fd != NULL) {
+    ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
+    ev.data.ptr = fd;
+    err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev);
+    if (err < 0) {
+      /* FDs may be added to a pollset multiple times, so EEXIST is normal. */
+      if (errno != EEXIST) {
+        gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd,
+                strerror(errno));
+      }
     }
   }
+  grpc_fd_end_poll(&watcher, 0, 0);
 }
 
 static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset,
diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c
index cc06269..7b717bd 100644
--- a/src/core/iomgr/pollset_multipoller_with_poll_posix.c
+++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c
@@ -179,6 +179,9 @@
       grpc_pollset_kick_consume(&pollset->kick_state, kfd);
     }
     for (i = 1; i < np; i++) {
+      if (h->watchers[i].fd == NULL) {
+        continue;
+      }
       if (h->pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) {
         grpc_fd_become_readable(h->watchers[i].fd, allow_synchronous_callback);
       }
diff --git a/src/core/iomgr/resolve_address_posix.c b/src/core/iomgr/resolve_address_posix.c
index 20d8c58..dbf884c 100644
--- a/src/core/iomgr/resolve_address_posix.c
+++ b/src/core/iomgr/resolve_address_posix.c
@@ -155,9 +155,9 @@
   grpc_resolve_cb cb = r->cb;
   gpr_free(r->name);
   gpr_free(r->default_port);
+  cb(arg, resolved);
   grpc_iomgr_unregister_object(&r->iomgr_object);
   gpr_free(r);
-  cb(arg, resolved);
 }
 
 void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
diff --git a/src/core/security/base64.c b/src/core/security/base64.c
index 3f28c09..8dfaef8 100644
--- a/src/core/security/base64.c
+++ b/src/core/security/base64.c
@@ -120,7 +120,68 @@
 }
 
 gpr_slice grpc_base64_decode(const char *b64, int url_safe) {
-  size_t b64_len = strlen(b64);
+  return grpc_base64_decode_with_len(b64, strlen(b64), url_safe);
+}
+
+static void decode_one_char(const unsigned char *codes, unsigned char *result,
+                            size_t *result_offset) {
+  gpr_uint32 packed = (codes[0] << 2) | (codes[1] >> 4);
+  result[(*result_offset)++] = (unsigned char)packed;
+}
+
+static void decode_two_chars(const unsigned char *codes, unsigned char *result,
+                             size_t *result_offset) {
+  gpr_uint32 packed = (codes[0] << 10) | (codes[1] << 4) | (codes[2] >> 2);
+  result[(*result_offset)++] = (unsigned char)(packed >> 8);
+  result[(*result_offset)++] = (unsigned char)(packed);
+}
+
+static int decode_group(const unsigned char *codes, size_t num_codes,
+                        unsigned char *result, size_t *result_offset) {
+  GPR_ASSERT(num_codes <= 4);
+
+  /* Short end groups that may not have padding. */
+  if (num_codes == 1) {
+    gpr_log(GPR_ERROR, "Invalid group. Must be at least 2 bytes.");
+    return 0;
+  }
+  if (num_codes == 2) {
+    decode_one_char(codes, result, result_offset);
+    return 1;
+  }
+  if (num_codes == 3) {
+    decode_two_chars(codes, result, result_offset);
+    return 1;
+  }
+
+  /* Regular 4 byte groups with padding or not. */
+  GPR_ASSERT(num_codes == 4);
+  if (codes[0] == GRPC_BASE64_PAD_BYTE || codes[1] == GRPC_BASE64_PAD_BYTE) {
+    gpr_log(GPR_ERROR, "Invalid padding detected.");
+    return 0;
+  }
+  if (codes[2] == GRPC_BASE64_PAD_BYTE) {
+    if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+      decode_one_char(codes, result, result_offset);
+    } else {
+      gpr_log(GPR_ERROR, "Invalid padding detected.");
+      return 0;
+    }
+  } else if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+    decode_two_chars(codes, result, result_offset);
+  } else {
+    /* No padding. */
+    gpr_uint32 packed =
+        (codes[0] << 18) | (codes[1] << 12) | (codes[2] << 6) | codes[3];
+    result[(*result_offset)++] = (unsigned char)(packed >> 16);
+    result[(*result_offset)++] = (unsigned char)(packed >> 8);
+    result[(*result_offset)++] = (unsigned char)(packed);
+  }
+  return 1;
+}
+
+gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
+                                      int url_safe) {
   gpr_slice result = gpr_slice_malloc(b64_len);
   unsigned char *current = GPR_SLICE_START_PTR(result);
   size_t result_size = 0;
@@ -151,43 +212,15 @@
     } else {
       codes[num_codes++] = (unsigned char)code;
       if (num_codes == 4) {
-        if (codes[0] == GRPC_BASE64_PAD_BYTE ||
-            codes[1] == GRPC_BASE64_PAD_BYTE) {
-          gpr_log(GPR_ERROR, "Invalid padding detected.");
-          goto fail;
-        }
-        if (codes[2] == GRPC_BASE64_PAD_BYTE) {
-          if (codes[3] == GRPC_BASE64_PAD_BYTE) {
-            /* Double padding. */
-            gpr_uint32 packed = (gpr_uint32)((codes[0] << 2) | (codes[1] >> 4));
-            current[result_size++] = (unsigned char)packed;
-          } else {
-            gpr_log(GPR_ERROR, "Invalid padding detected.");
-            goto fail;
-          }
-        } else if (codes[3] == GRPC_BASE64_PAD_BYTE) {
-          /* Single padding. */
-          gpr_uint32 packed =
-              (gpr_uint32)((codes[0] << 10) | (codes[1] << 4) | (codes[2] >> 2));
-          current[result_size++] = (unsigned char)(packed >> 8);
-          current[result_size++] = (unsigned char)(packed);
-        } else {
-          /* No padding. */
-          gpr_uint32 packed =
-              (gpr_uint32)((codes[0] << 18) | (codes[1] << 12) | (codes[2] << 6) | codes[3]);
-          current[result_size++] = (unsigned char)(packed >> 16);
-          current[result_size++] = (unsigned char)(packed >> 8);
-          current[result_size++] = (unsigned char)(packed);
-        }
+        if (!decode_group(codes, num_codes, current, &result_size)) goto fail;
         num_codes = 0;
       }
     }
   }
 
-  if (num_codes != 0) {
-    gpr_log(GPR_ERROR, "Invalid base64.");
-    gpr_slice_unref(result);
-    return gpr_empty_slice();
+  if (num_codes != 0 &&
+      !decode_group(codes, num_codes, current, &result_size)) {
+    goto fail;
   }
   GPR_SLICE_SET_LENGTH(result, result_size);
   return result;
diff --git a/src/core/security/base64.h b/src/core/security/base64.h
index 6a7cd8e..b9abc07 100644
--- a/src/core/security/base64.h
+++ b/src/core/security/base64.h
@@ -45,4 +45,8 @@
    slice in case of failure. */
 gpr_slice grpc_base64_decode(const char *b64, int url_safe);
 
+/* Same as above except that the length is provided by the caller. */
+gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
+                                      int url_safe);
+
 #endif  /* GRPC_INTERNAL_CORE_SECURITY_BASE64_H */
diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c
index 4817e00..a930949 100644
--- a/src/core/surface/byte_buffer.c
+++ b/src/core/surface/byte_buffer.c
@@ -55,6 +55,20 @@
   return bb;
 }
 
+grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
+    grpc_byte_buffer_reader *reader) {
+  grpc_byte_buffer *bb = malloc(sizeof(grpc_byte_buffer));
+  gpr_slice slice;
+  bb->type = GRPC_BB_RAW;
+  bb->data.raw.compression = GRPC_COMPRESS_NONE;
+  gpr_slice_buffer_init(&bb->data.raw.slice_buffer);
+
+  while (grpc_byte_buffer_reader_next(reader, &slice)) {
+    gpr_slice_buffer_add(&bb->data.raw.slice_buffer, slice);
+  }
+  return bb;
+}
+
 grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) {
   switch (bb->type) {
     case GRPC_BB_RAW:
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 02e0e59..181617f 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -160,6 +160,8 @@
   gpr_uint8 bound_pollset;
   /* is an error status set */
   gpr_uint8 error_status_set;
+  /** should the alarm be cancelled */
+  gpr_uint8 cancel_alarm;
 
   /* flags with bits corresponding to write states allowing us to determine
      what was sent */
@@ -472,6 +474,7 @@
   int completing_requests = 0;
   int start_op = 0;
   int i;
+  int cancel_alarm = 0;
 
   memset(&op, 0, sizeof(op));
 
@@ -479,6 +482,9 @@
   start_op = op.cancel_with_status != GRPC_STATUS_OK;
   call->cancel_with_status = GRPC_STATUS_OK; /* reset */
 
+  cancel_alarm = call->cancel_alarm;
+  call->cancel_alarm = 0;
+
   if (!call->receiving && need_more_data(call)) {
     op.recv_ops = &call->recv_ops;
     op.recv_state = &call->recv_state;
@@ -513,6 +519,10 @@
 
   gpr_mu_unlock(&call->mu);
 
+  if (cancel_alarm) {
+    grpc_alarm_cancel(&call->alarm);
+  }
+
   if (start_op) {
     execute_op(call, &op);
   }
@@ -805,10 +815,7 @@
     if (call->recv_state == GRPC_STREAM_CLOSED) {
       GPR_ASSERT(call->read_state <= READ_STATE_STREAM_CLOSED);
       call->read_state = READ_STATE_STREAM_CLOSED;
-      if (call->have_alarm) {
-        grpc_alarm_cancel(&call->alarm);
-        call->have_alarm = 0;
-      }
+      call->cancel_alarm |= call->have_alarm;
       GRPC_CALL_INTERNAL_UNREF(call, "closed", 0);
     }
     finish_read_ops(call);
@@ -987,7 +994,7 @@
 
   switch (call->read_state) {
     case READ_STATE_STREAM_CLOSED:
-      if (empty) {
+      if (empty && !call->have_alarm) {
         finish_ioreq_op(call, GRPC_IOREQ_RECV_CLOSE, 1);
       }
     /* fallthrough */
@@ -1085,10 +1092,7 @@
   lock(c);
   GPR_ASSERT(!c->destroy_called);
   c->destroy_called = 1;
-  if (c->have_alarm) {
-    grpc_alarm_cancel(&c->alarm);
-    c->have_alarm = 0;
-  }
+  c->cancel_alarm |= c->have_alarm;
   cancel = c->read_state != READ_STATE_STREAM_CLOSED;
   unlock(c);
   if (cancel) grpc_call_cancel(c);
@@ -1167,12 +1171,14 @@
 
 static void call_alarm(void *arg, int success) {
   grpc_call *call = arg;
+  lock(call);
+  call->have_alarm = 0;
   if (success) {
-    lock(call);
     cancel_with_status(call, GRPC_STATUS_DEADLINE_EXCEEDED,
                        "Deadline Exceeded");
-    unlock(call);
   }
+  finish_read_ops(call);
+  unlock(call);
   GRPC_CALL_INTERNAL_UNREF(call, "alarm", 1);
 }
 
diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c
index 8f682e9..4664a08 100644
--- a/src/core/transport/chttp2/parsing.c
+++ b/src/core/transport/chttp2/parsing.c
@@ -109,9 +109,6 @@
         transport_parsing->incoming_stream_id;
   }
 
-  /* TODO(ctiller): re-implement */
-  GPR_ASSERT(transport_parsing->initial_window_update == 0);
-
   /* copy parsing qbuf to global qbuf */
   gpr_slice_buffer_move_into(&transport_parsing->qbuf, &transport_global->qbuf);
 
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index 934a2e6..7ecc49e 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -914,6 +914,7 @@
         if (t->parsing.initial_window_update != 0) {
           grpc_chttp2_stream_map_for_each(&t->parsing_stream_map,
                                           update_global_window, t);
+          t->parsing.initial_window_update = 0;
         }
         /* handle higher level things */
         grpc_chttp2_publish_reads(&t->global, &t->parsing);
diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h
index 33aae10..cba53fa 100644
--- a/src/objective-c/GRPCClient/GRPCCall.h
+++ b/src/objective-c/GRPCClient/GRPCCall.h
@@ -48,8 +48,6 @@
 #import <Foundation/Foundation.h>
 #import <RxLibrary/GRXWriter.h>
 
-@class GRPCMethodName;
-
 // Key used in |NSError|'s |userInfo| dictionary to store the response metadata sent by the server.
 extern id const kGRPCStatusMetadataKey;
 
@@ -90,7 +88,7 @@
 // the specific remote method called).
 // To finish a call right away, invoke cancel.
 - (instancetype)initWithHost:(NSString *)host
-                      method:(GRPCMethodName *)method
+                        path:(NSString *)path
               requestsWriter:(id<GRXWriter>)requestsWriter NS_DESIGNATED_INITIALIZER;
 
 // Finishes the request side of this call, notifies the server that the RPC
diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m
index 77eebef..4ac4e4d 100644
--- a/src/objective-c/GRPCClient/GRPCCall.m
+++ b/src/objective-c/GRPCClient/GRPCCall.m
@@ -36,11 +36,9 @@
 #include <grpc/grpc.h>
 #include <grpc/support/time.h>
 
-#import "GRPCMethodName.h"
 #import "private/GRPCChannel.h"
 #import "private/GRPCCompletionQueue.h"
 #import "private/GRPCDelegateWrapper.h"
-#import "private/GRPCMethodName+HTTP2Encoding.h"
 #import "private/GRPCWrappedCall.h"
 #import "private/NSData+GRPC.h"
 #import "private/NSDictionary+GRPC.h"
@@ -90,14 +88,14 @@
 @synthesize state = _state;
 
 - (instancetype)init {
-  return [self initWithHost:nil method:nil requestsWriter:nil];
+  return [self initWithHost:nil path:nil requestsWriter:nil];
 }
 
 // Designated initializer
 - (instancetype)initWithHost:(NSString *)host
-                      method:(GRPCMethodName *)method
+                        path:(NSString *)path
               requestsWriter:(id<GRXWriter>)requestWriter {
-  if (!host || !method) {
+  if (!host || !path) {
     [NSException raise:NSInvalidArgumentException format:@"Neither host nor method can be nil."];
   }
   if (requestWriter.state != GRXWriterStateNotStarted) {
@@ -114,7 +112,7 @@
     _channel = [GRPCChannel channelToHost:host];
 
     _wrappedCall = [[GRPCWrappedCall alloc] initWithChannel:_channel
-                                                     method:method.HTTP2Path
+                                                       path:path
                                                        host:host];
 
     // Serial queue to invoke the non-reentrant methods of the grpc_call object.
diff --git a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.h b/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.h
deleted file mode 100644
index 81c80f2..0000000
--- a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#import <Foundation/Foundation.h>
-
-#import "GRPCClient/GRPCMethodName.h"
-
-@interface GRPCMethodName (HTTP2Encoding)
-- (NSString *)HTTP2Path;
-@end
diff --git a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.m b/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.m
deleted file mode 100644
index 3ad757f..0000000
--- a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.m
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#import "GRPCMethodName+HTTP2Encoding.h"
-
-@implementation GRPCMethodName (HTTP2Encoding)
-- (NSString *)HTTP2Path {
-  if (self.package) {
-    return [NSString stringWithFormat:@"/%@.%@/%@", self.package, self.interface, self.method];
-  } else {
-    return [NSString stringWithFormat:@"/%@/%@", self.interface, self.method];
-  }
-}
-@end
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
index c08aefc..18f8bb5 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
@@ -84,7 +84,7 @@
 @interface GRPCWrappedCall : NSObject
 
 - (instancetype)initWithChannel:(GRPCChannel *)channel
-                         method:(NSString *)method
+                           path:(NSString *)path
                            host:(NSString *)host NS_DESIGNATED_INITIALIZER;
 
 - (void)startBatchWithOperations:(NSArray *)ops errorHandler:(void(^)())errorHandler;
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index d94b250..45f10f5 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -225,13 +225,13 @@
 }
 
 - (instancetype)init {
-  return [self initWithChannel:nil method:nil host:nil];
+  return [self initWithChannel:nil path:nil host:nil];
 }
 
 - (instancetype)initWithChannel:(GRPCChannel *)channel
-                         method:(NSString *)method
+                           path:(NSString *)path
                            host:(NSString *)host {
-  if (!channel || !method || !host) {
+  if (!channel || !path || !host) {
     [NSException raise:NSInvalidArgumentException
                 format:@"channel, method, and host cannot be nil."];
   }
@@ -247,7 +247,7 @@
       return nil;
     }
     _call = grpc_channel_create_call(channel.unmanagedChannel, _queue.unmanagedQueue,
-                                     method.UTF8String, host.UTF8String, gpr_inf_future);
+                                     path.UTF8String, host.UTF8String, gpr_inf_future);
     if (_call == NULL) {
       return nil;
     }
diff --git a/src/objective-c/GRPCClient/GRPCMethodName.h b/src/objective-c/ProtoRPC/ProtoMethod.h
similarity index 80%
rename from src/objective-c/GRPCClient/GRPCMethodName.h
rename to src/objective-c/ProtoRPC/ProtoMethod.h
index fe153dd..8f554a0 100644
--- a/src/objective-c/GRPCClient/GRPCMethodName.h
+++ b/src/objective-c/ProtoRPC/ProtoMethod.h
@@ -33,17 +33,16 @@
 
 #import <Foundation/Foundation.h>
 
-// See the README file for an introduction to this library.
-
-// A fully-qualified gRPC method name. Full qualification is needed because a gRPC endpoint can
-// implement multiple interfaces.
-// TODO(jcanizales): Move to ProtoRPC package.
-// TODO(jcanizales): Rename interface -> service.
-@interface GRPCMethodName : NSObject
+// A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint
+// can implement multiple services.
+@interface ProtoMethod : NSObject
 @property(nonatomic, readonly) NSString *package;
-@property(nonatomic, readonly) NSString *interface;
+@property(nonatomic, readonly) NSString *service;
 @property(nonatomic, readonly) NSString *method;
+
+@property(nonatomic, readonly) NSString *HTTPPath;
+
 - (instancetype)initWithPackage:(NSString *)package
-                      interface:(NSString *)interface
+                        service:(NSString *)service
                          method:(NSString *)method;
 @end
diff --git a/src/objective-c/GRPCClient/GRPCMethodName.m b/src/objective-c/ProtoRPC/ProtoMethod.m
similarity index 83%
rename from src/objective-c/GRPCClient/GRPCMethodName.m
rename to src/objective-c/ProtoRPC/ProtoMethod.m
index 9672407..1113b4f 100644
--- a/src/objective-c/GRPCClient/GRPCMethodName.m
+++ b/src/objective-c/ProtoRPC/ProtoMethod.m
@@ -31,17 +31,25 @@
  *
  */
 
-#import "GRPCMethodName.h"
+#import "ProtoMethod.h"
 
-@implementation GRPCMethodName
+@implementation ProtoMethod
 - (instancetype)initWithPackage:(NSString *)package
-                      interface:(NSString *)interface
+                        service:(NSString *)service
                          method:(NSString *)method {
   if ((self = [super init])) {
     _package = [package copy];
-    _interface = [interface copy];
+    _service = [service copy];
     _method = [method copy];
   }
   return self;
 }
+
+- (NSString *)HTTPPath {
+  if (_package) {
+    return [NSString stringWithFormat:@"/%@.%@/%@", _package, _service, _method];
+  } else {
+    return [NSString stringWithFormat:@"/%@/%@", _service, _method];
+  }
+}
 @end
diff --git a/src/objective-c/ProtoRPC/ProtoRPC.h b/src/objective-c/ProtoRPC/ProtoRPC.h
index a383310..fcc0a50 100644
--- a/src/objective-c/ProtoRPC/ProtoRPC.h
+++ b/src/objective-c/ProtoRPC/ProtoRPC.h
@@ -34,10 +34,12 @@
 #import <Foundation/Foundation.h>
 #import <GRPCClient/GRPCCall.h>
 
+#import "ProtoMethod.h"
+
 @interface ProtoRPC : GRPCCall
 
 - (instancetype)initWithHost:(NSString *)host
-                      method:(GRPCMethodName *)method
+                      method:(ProtoMethod *)method
               requestsWriter:(id<GRXWriter>)requestsWriter
                responseClass:(Class)responseClass
           responsesWriteable:(id<GRXWriteable>)responsesWriteable NS_DESIGNATED_INITIALIZER;
diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m
index 4da646d..fe3ccf0 100644
--- a/src/objective-c/ProtoRPC/ProtoRPC.m
+++ b/src/objective-c/ProtoRPC/ProtoRPC.m
@@ -42,19 +42,20 @@
   id<GRXWriteable> _responseWriteable;
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
 - (instancetype)initWithHost:(NSString *)host
-                      method:(GRPCMethodName *)method
+                        path:(NSString *)path
               requestsWriter:(id<GRXWriter>)requestsWriter {
-  return [self initWithHost:host
-                     method:method
-             requestsWriter:requestsWriter
-              responseClass:nil
-        responsesWriteable:nil];
+  [NSException raise:NSInvalidArgumentException
+              format:@"Please use ProtoRPC's designated initializer instead."];
+  return nil;
 }
+#pragma clang diagnostic pop
 
 // Designated initializer
 - (instancetype)initWithHost:(NSString *)host
-                      method:(GRPCMethodName *)method
+                      method:(ProtoMethod *)method
               requestsWriter:(id<GRXWriter>)requestsWriter
                responseClass:(Class)responseClass
           responsesWriteable:(id<GRXWriteable>)responsesWriteable {
@@ -70,7 +71,7 @@
         // sending GPBMessages.
         return [proto data];
       }];
-  if ((self = [super initWithHost:host method:method requestsWriter:bytesWriter])) {
+  if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) {
     // A writeable that parses the proto messages received.
     _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
       [responsesWriteable writeValue:[responseClass parseFromData:value error:NULL]];
diff --git a/src/objective-c/ProtoRPC/ProtoService.m b/src/objective-c/ProtoRPC/ProtoService.m
index 47bdb5d..d7c5b6a 100644
--- a/src/objective-c/ProtoRPC/ProtoService.m
+++ b/src/objective-c/ProtoRPC/ProtoService.m
@@ -33,10 +33,10 @@
 
 #import "ProtoService.h"
 
-#import <GRPCClient/GRPCMethodName.h>
 #import <RxLibrary/GRXWriteable.h>
 #import <RxLibrary/GRXWriter.h>
 
+#import "ProtoMethod.h"
 #import "ProtoRPC.h"
 
 @implementation ProtoService {
@@ -69,9 +69,9 @@
            requestsWriter:(id<GRXWriter>)requestsWriter
             responseClass:(Class)responseClass
        responsesWriteable:(id<GRXWriteable>)responsesWriteable {
-  GRPCMethodName *methodName = [[GRPCMethodName alloc] initWithPackage:_packageName
-                                                             interface:_serviceName
-                                                                method:method];
+  ProtoMethod *methodName = [[ProtoMethod alloc] initWithPackage:_packageName
+                                                         service:_serviceName
+                                                          method:method];
   return [[ProtoRPC alloc] initWithHost:_host
                                  method:methodName
                          requestsWriter:requestsWriter
diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m
index e421127..f9c2d5d 100644
--- a/src/objective-c/tests/GRPCClientTests.m
+++ b/src/objective-c/tests/GRPCClientTests.m
@@ -35,7 +35,7 @@
 #import <XCTest/XCTest.h>
 
 #import <GRPCClient/GRPCCall.h>
-#import <GRPCClient/GRPCMethodName.h>
+#import <ProtoRPC/ProtoMethod.h>
 #import <RemoteTest/Messages.pbobjc.h>
 #import <RxLibrary/GRXWriteable.h>
 #import <RxLibrary/GRXWriter+Immediate.h>
@@ -47,9 +47,9 @@
 static NSString * const kPackage = @"grpc.testing";
 static NSString * const kService = @"TestService";
 
-static GRPCMethodName *kInexistentMethod;
-static GRPCMethodName *kEmptyCallMethod;
-static GRPCMethodName *kUnaryCallMethod;
+static ProtoMethod *kInexistentMethod;
+static ProtoMethod *kEmptyCallMethod;
+static ProtoMethod *kUnaryCallMethod;
 
 @interface GRPCClientTests : XCTestCase
 @end
@@ -58,22 +58,22 @@
 
 - (void)setUp {
   // This method isn't implemented by the remote server.
-  kInexistentMethod = [[GRPCMethodName alloc] initWithPackage:kPackage
-                                                    interface:kService
-                                                       method:@"Inexistent"];
-  kEmptyCallMethod = [[GRPCMethodName alloc] initWithPackage:kPackage
-                                                   interface:kService
-                                                      method:@"EmptyCall"];
-  kUnaryCallMethod = [[GRPCMethodName alloc] initWithPackage:kPackage
-                                                   interface:kService
-                                                      method:@"UnaryCall"];
+  kInexistentMethod = [[ProtoMethod alloc] initWithPackage:kPackage
+                                                   service:kService
+                                                    method:@"Inexistent"];
+  kEmptyCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
+                                                  service:kService
+                                                   method:@"EmptyCall"];
+  kUnaryCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
+                                                  service:kService
+                                                   method:@"UnaryCall"];
 }
 
 - (void)testConnectionToRemoteServer {
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
 
   GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
-                                           method:kInexistentMethod
+                                             path:kInexistentMethod.HTTPPath
                                    requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
 
   id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
@@ -95,7 +95,7 @@
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
 
   GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
-                                           method:kEmptyCallMethod
+                                             path:kEmptyCallMethod.HTTPPath
                                    requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
 
   id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
@@ -123,7 +123,7 @@
   id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[request data]];
 
   GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
-                                           method:kUnaryCallMethod
+                                             path:kUnaryCallMethod.HTTPPath
                                    requestsWriter:requestsWriter];
 
   id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
@@ -153,7 +153,7 @@
   id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[request data]];
 
   GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
-                                           method:kUnaryCallMethod
+                                             path:kUnaryCallMethod.HTTPPath
                                    requestsWriter:requestsWriter];
 
   call.requestMetadata[@"Authorization"] = @"Bearer bogusToken";
diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m
index 05cc104..10c9f13 100644
--- a/src/objective-c/tests/LocalClearTextTests.m
+++ b/src/objective-c/tests/LocalClearTextTests.m
@@ -35,7 +35,7 @@
 #import <XCTest/XCTest.h>
 
 #import <GRPCClient/GRPCCall.h>
-#import <GRPCClient/GRPCMethodName.h>
+#import <ProtoRPC/ProtoMethod.h>
 #import <RouteGuide/RouteGuide.pbobjc.h>
 #import <RouteGuide/RouteGuide.pbrpc.h>
 #import <RxLibrary/GRXWriteable.h>
@@ -87,14 +87,14 @@
   __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
 
-  GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage
-                                                         interface:kService
-                                                            method:@"RecordRoute"];
+  ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
+                                                     service:kService
+                                                      method:@"RecordRoute"];
 
   id<GRXWriter> requestsWriter = [GRXWriter emptyWriter];
 
   GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-                                           method:method
+                                             path:method.HTTPPath
                                    requestsWriter:requestsWriter];
 
   id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
@@ -115,9 +115,9 @@
   __weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."];
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
 
-  GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage
-                                                         interface:kService
-                                                            method:@"GetFeature"];
+  ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
+                                                     service:kService
+                                                      method:@"GetFeature"];
 
   RGDPoint *point = [RGDPoint message];
   point.latitude = 28E7;
@@ -125,7 +125,7 @@
   id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[point data]];
 
   GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-                                           method:method
+                                             path:method.HTTPPath
                                    requestsWriter:requestsWriter];
 
   id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index 0ff8bb9..6dd0234 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -89,7 +89,7 @@
 $CFLAGS << ' -Wall '
 $CFLAGS << ' -pedantic '
 
-$LDFLAGS << ' -lgrpc -lgpr -ldl'
+$LDFLAGS << ' -lgrpc -lgpr -lz -ldl'
 
 crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
 have_library('grpc', 'grpc_channel_destroy')
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 3b04d7d..f0cd0d9 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -171,7 +171,7 @@
 LDXX_asan = clang++
 CPPFLAGS_asan = -O0 -fsanitize=address -fno-omit-frame-pointer
 LDFLAGS_asan = -fsanitize=address
-DEFINES_asan = GRPC_TEST_SLOWDOWN_BUILD_FACTOR=5
+DEFINES_asan = GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
 
 VALID_CONFIG_msan = 1
 REQUIRE_CUSTOM_LIBRARIES_msan = 1
@@ -182,7 +182,7 @@
 CPPFLAGS_msan = -O0 -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1
 OPENSSL_CFLAGS_msan = -DPURIFY
 LDFLAGS_msan = -fsanitize=memory -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1
-DEFINES_msan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=20
+DEFINES_msan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=4
 
 VALID_CONFIG_ubsan = 1
 REQUIRE_CUSTOM_LIBRARIES_ubsan = 1
@@ -193,7 +193,7 @@
 CPPFLAGS_ubsan = -O1 -fsanitize=undefined -fno-omit-frame-pointer
 OPENSSL_CFLAGS_ubsan = -DPURIFY
 LDFLAGS_ubsan = -fsanitize=undefined
-DEFINES_ubsan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=10
+DEFINES_ubsan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
 
 VALID_CONFIG_gcov = 1
 CC_gcov = gcc
@@ -347,6 +347,30 @@
 
 HAS_PKG_CONFIG = $(shell command -v pkg-config >/dev/null 2>&1 && echo true || echo false)
 
+PC_TEMPLATE = prefix=$(prefix)\n\
+exec_prefix=${'\$${prefix}'}\n\
+includedir=${'\$${prefix}'}/include\n\
+libdir=${'\$${exec_prefix}'}/lib\n\
+\n\
+Name: $(PC_NAME)\n\
+Description: $(PC_DESCRIPTION)\n\
+Version: $(VERSION)\n\
+Cflags: -I${'\$${includedir}'} $(PC_CFLAGS)\n\
+Requires.private: $(PC_REQUIRES_PRIVATE)\n\
+Libs: -L${'\$${libdir}'}\n\
+Libs.private: $(PC_LIBS_PRIVATE)
+
+# gpr .pc file
+PC_NAME = gRPC Portable Runtime
+PC_DESCRIPTION = gRPC Portable Runtime
+PC_CFLAGS = -pthread
+PC_REQUIRES_PRIVATE =
+PC_LIBS_PRIVATE = -lpthread
+ifeq ($(SYSTEM),Darwin)
+PC_LIBS_PRIVATE += -lrt
+endif
+GPR_PC_FILE := $(PC_TEMPLATE)
+
 ifeq ($(SYSTEM),MINGW32)
 SHARED_EXT = dll
 endif
@@ -460,6 +484,9 @@
 HAS_EMBEDDED_PROTOBUF = true
 endif
 
+PC_REQUIRES_GRPC = gpr
+PC_LIBS_GRPC =
+
 ifeq ($(HAS_SYSTEM_ZLIB),false)
 ifeq ($(HAS_EMBEDDED_ZLIB),true)
 ZLIB_DEP = $(LIBDIR)/$(CONFIG)/zlib/libz.a
@@ -472,14 +499,21 @@
 ifeq ($(HAS_PKG_CONFIG),true)
 CPPFLAGS += $(shell pkg-config --cflags zlib)
 LDFLAGS += $(shell pkg-config --libs-only-L zlib)
+PC_REQUIRES_GRPC += zlib
+else
+PC_LIBS_GRPC += -lz
 endif
 endif
 
 OPENSSL_PKG_CONFIG = false
 
+PC_REQUIRES_SECURE =
+PC_LIBS_SECURE =
+
 ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true)
 ifeq ($(HAS_PKG_CONFIG),true)
 OPENSSL_PKG_CONFIG = true
+PC_REQUIRES_SECURE = openssl
 CPPFLAGS := $(shell pkg-config --cflags openssl) $(CPPFLAGS)
 LDFLAGS_OPENSSL_PKG_CONFIG = $(shell pkg-config --libs-only-L openssl)
 ifeq ($(SYSTEM),Linux)
@@ -492,6 +526,7 @@
 LIBS_SECURE = $(OPENSSL_LIBS)
 ifeq ($(OPENSSL_REQUIRES_DL),true)
 LIBS_SECURE += dl
+PC_LIBS_SECURE = $(addprefix -l, $(LIBS_SECURE))
 endif
 endif
 else
@@ -515,11 +550,31 @@
 LDLIBS_SECURE += $(addprefix -l, $(LIBS_SECURE))
 endif
 
+# grpc .pc file
+PC_NAME = gRPC
+PC_DESCRIPTION = high performance general RPC framework
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC) $(PC_REQUIRES_SECURE)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPC) $(PC_LIBS_SECURE)
+GRPC_PC_FILE := $(PC_TEMPLATE)
+
+# gprc_unsecure .pc file
+PC_NAME = gRPC unsecure
+PC_DESCRIPTION = high performance general RPC framework without SSL
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPC)
+GRPC_UNSECURE_PC_FILE := $(PC_TEMPLATE)
+
 PROTOBUF_PKG_CONFIG = false
 
+PC_REQUIRES_GRPCXX =
+PC_LIBS_GRPCXX =
+
 ifeq ($(HAS_SYSTEM_PROTOBUF),true)
 ifeq ($(HAS_PKG_CONFIG),true)
 PROTOBUF_PKG_CONFIG = true
+PC_REQUIRES_GRPCXX = protobuf
 CPPFLAGS := $(shell pkg-config --cflags protobuf) $(CPPFLAGS)
 LDFLAGS_PROTOBUF_PKG_CONFIG = $(shell pkg-config --libs-only-L protobuf)
 ifeq ($(SYSTEM),Linux)
@@ -527,6 +582,8 @@
 LDFLAGS_PROTOBUF_PKG_CONFIG += $(shell pkg-config --libs-only-L protobuf | sed s/L/Wl,-rpath,/)
 endif
 endif
+else
+PC_LIBS_GRPCXX = -lprotobuf
 endif
 else
 ifeq ($(HAS_EMBEDDED_PROTOBUF),true)
@@ -550,6 +607,22 @@
 LDLIBS_PROTOBUF += $(addprefix -l, $(LIBS_PROTOBUF))
 endif
 
+# grpc++ .pc file
+PC_NAME = gRPC++
+PC_DESCRIPTION = C++ wrapper for gRPC
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = grpc $(PC_REQUIRES_GRPCXX)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX)
+GRPCXX_PC_FILE := $(PC_TEMPLATE)
+
+# grpc++_unsecure .pc file
+PC_NAME = gRPC++ unsecure
+PC_DESCRIPTION = C++ wrapper for gRPC without SSL
+PC_CFLAGS =
+PC_REQUIRES_PRIVATE = grpc_unsecure $(PC_REQUIRES_GRPCXX)
+PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX)
+GRPCXX_UNSECURE_PC_FILE := $(PC_TEMPLATE)
+
 ifeq ($(MAKECMDGOALS),clean)
 NO_DEPS = true
 endif
@@ -740,7 +813,7 @@
 
 static: static_c static_cxx
 
-static_c: \
+static_c: pc_c pc_c_unsecure \
 % for lib in libs:
 % if lib.build == 'all' and lib.language == 'c':
  $(LIBDIR)/$(CONFIG)/lib${lib.name}.a\
@@ -748,7 +821,7 @@
 % endfor
 
 
-static_cxx: \
+static_cxx: pc_cxx pc_cxx_unsecure pc_gpr\
 % for lib in libs:
 % if lib.build == 'all' and lib.language == 'c++':
  $(LIBDIR)/$(CONFIG)/lib${lib.name}.a\
@@ -758,7 +831,7 @@
 
 shared: shared_c shared_cxx
 
-shared_c: \
+shared_c: pc_c pc_c_unsecure pc_gpr\
 % for lib in libs:
 % if lib.build == 'all' and lib.language == 'c':
  $(LIBDIR)/$(CONFIG)/lib${lib.name}.$(SHARED_EXT)\
@@ -766,7 +839,7 @@
 % endfor
 
 
-shared_cxx: \
+shared_cxx: pc_cxx pc_cxx_unsecure \
 % for lib in libs:
 % if lib.build == 'all' and lib.language == 'c++':
  $(LIBDIR)/$(CONFIG)/lib${lib.name}.$(SHARED_EXT)\
@@ -794,6 +867,15 @@
 % endif
 % endfor
 
+pc_gpr: $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc
+
+pc_c: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc
+
+pc_c_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc
+
+pc_cxx: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc
+
+pc_cxx_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc
 
 privatelibs_cxx: \
 % for lib in libs:
@@ -966,6 +1048,31 @@
 % endfor
 endif
 
+$(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GPR_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPC_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPC_UNSECURE_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPCXX_PC_FILE)" >$@
+
+$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc:
+	$(E) "[MAKE]    Generating $@"
+	$(Q) mkdir -p $(@D)
+	$(Q) echo -e "$(GRPCXX_UNSECURE_PC_FILE)" >$@
+
 % for p in protos:
 ifeq ($(NO_PROTOC),true)
 $(GENDIR)/${p}.pb.cc: protoc_dep_error
@@ -1040,7 +1147,7 @@
 
 install-static: install-static_c install-static_cxx
 
-install-static_c: static_c strip-static_c
+install-static_c: static_c strip-static_c install-pkg-config_c
 % for lib in libs:
 % if lib.language == "c":
 % if lib.build == "all":
@@ -1051,7 +1158,7 @@
 % endif
 % endfor
 
-install-static_cxx: static_cxx strip-static_cxx
+install-static_cxx: static_cxx strip-static_cxx install-pkg-config_cxx
 % for lib in libs:
 % if lib.language == "c++":
 % if lib.build == "all":
@@ -1090,10 +1197,10 @@
 endif
 </%def>
 
-install-shared_c: shared_c strip-shared_c
+install-shared_c: shared_c strip-shared_c install-pkg-config_c
 ${install_shared("c")}
 
-install-shared_cxx: shared_cxx strip-shared_cxx install-shared_c
+install-shared_cxx: shared_cxx strip-shared_cxx install-shared_c install-pkg-config_cxx
 ${install_shared("c++")}
 
 install-shared_csharp: shared_csharp strip-shared_csharp
@@ -1112,6 +1219,19 @@
 % endfor
 endif
 
+install-pkg-config_c: pc_gpr pc_c pc_c_unsecure
+	$(E) "[INSTALL] Installing C pkg-config files"
+	$(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc $(prefix)/lib/pkgconfig/gpr.pc
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc $(prefix)/lib/pkgconfig/grpc.pc
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc $(prefix)/lib/pkgconfig/grpc_unsecure.pc
+
+install-pkg-config_cxx: pc_cxx pc_cxx_unsecure
+	$(E) "[INSTALL] Installing C++ pkg-config files"
+	$(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc $(prefix)/lib/pkgconfig/grpc++.pc
+	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc $(prefix)/lib/pkgconfig/grpc++_unsecure.pc
+
 install-certs: etc/roots.pem
 	$(E) "[INSTALL] Installing root certificates"
 	$(Q) $(INSTALL) -d $(prefix)/share/grpc
diff --git a/templates/gRPC.podspec.template b/templates/gRPC.podspec.template
index deea07c..495ea49 100644
--- a/templates/gRPC.podspec.template
+++ b/templates/gRPC.podspec.template
@@ -111,9 +111,9 @@
     BAD_TIME="$DIR_TIME/time.h"
     GOOD_TIME="$DIR_TIME/grpc_time.h"
     grep -rl "$BAD_TIME" grpc src/core src/objective-c/GRPCClient | xargs sed -i '' -e s@$BAD_TIME@$GOOD_TIME@g
-    if [ -f "include/$BAD_TIME" ];
+    if [ -f "$BAD_TIME" ];
     then
-      mv -f "include/$BAD_TIME" "include/$GOOD_TIME"
+      mv -f "$BAD_TIME" "$GOOD_TIME"
     fi
 
     DIR_STRING="src/core/support"
diff --git a/test/core/end2end/multiple_server_queues_test.c b/test/core/end2end/multiple_server_queues_test.c
index 2d79f5a..2306015 100644
--- a/test/core/end2end/multiple_server_queues_test.c
+++ b/test/core/end2end/multiple_server_queues_test.c
@@ -55,6 +55,8 @@
   grpc_completion_queue_next(cq1, gpr_inf_future);
   grpc_completion_queue_next(cq2, gpr_inf_future);
   grpc_server_destroy(server);
+  grpc_completion_queue_destroy(cq1);
+  grpc_completion_queue_destroy(cq2);
   grpc_shutdown();
   return 0;
 }
diff --git a/test/core/security/base64_test.c b/test/core/security/base64_test.c
index a922896..f8b7ebf 100644
--- a/test/core/security/base64_test.c
+++ b/test/core/security/base64_test.c
@@ -169,6 +169,43 @@
   gpr_free(b64);
 }
 
+static void test_unpadded_decode(void) {
+  gpr_slice decoded;
+
+  decoded = grpc_base64_decode("Zm9vYmFy", 0);
+  GPR_ASSERT(!GPR_SLICE_IS_EMPTY(decoded));
+  GPR_ASSERT(gpr_slice_str_cmp(decoded, "foobar") == 0);
+  gpr_slice_unref(decoded);
+
+  decoded = grpc_base64_decode("Zm9vYmE", 0);
+  GPR_ASSERT(!GPR_SLICE_IS_EMPTY(decoded));
+  GPR_ASSERT(gpr_slice_str_cmp(decoded, "fooba") == 0);
+  gpr_slice_unref(decoded);
+
+  decoded = grpc_base64_decode("Zm9vYg", 0);
+  GPR_ASSERT(!GPR_SLICE_IS_EMPTY(decoded));
+  GPR_ASSERT(gpr_slice_str_cmp(decoded, "foob") == 0);
+  gpr_slice_unref(decoded);
+
+  decoded = grpc_base64_decode("Zm9v", 0);
+  GPR_ASSERT(!GPR_SLICE_IS_EMPTY(decoded));
+  GPR_ASSERT(gpr_slice_str_cmp(decoded, "foo") == 0);
+  gpr_slice_unref(decoded);
+
+  decoded = grpc_base64_decode("Zm8", 0);
+  GPR_ASSERT(!GPR_SLICE_IS_EMPTY(decoded));
+  GPR_ASSERT(gpr_slice_str_cmp(decoded, "fo") == 0);
+  gpr_slice_unref(decoded);
+
+  decoded = grpc_base64_decode("Zg", 0);
+  GPR_ASSERT(!GPR_SLICE_IS_EMPTY(decoded));
+  GPR_ASSERT(gpr_slice_str_cmp(decoded, "f") == 0);
+  gpr_slice_unref(decoded);
+
+  decoded = grpc_base64_decode("", 0);
+  GPR_ASSERT(GPR_SLICE_IS_EMPTY(decoded));
+}
+
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   test_simple_encode_decode_b64_no_multiline();
@@ -181,5 +218,6 @@
   test_full_range_encode_decode_b64_urlsafe_multiline();
   test_url_safe_unsafe_mismtach_failure();
   test_rfc4648_test_vectors();
+  test_unpadded_decode();
   return 0;
 }
diff --git a/test/core/surface/byte_buffer_reader_test.c b/test/core/surface/byte_buffer_reader_test.c
index 7c2cb94..d9c60e4 100644
--- a/test/core/surface/byte_buffer_reader_test.c
+++ b/test/core/surface/byte_buffer_reader_test.c
@@ -160,6 +160,30 @@
   read_compressed_slice(GRPC_COMPRESS_DEFLATE, INPUT_SIZE);
 }
 
+static void test_byte_buffer_from_reader(void) {
+  gpr_slice slice;
+  grpc_byte_buffer *buffer, *buffer_from_reader;
+  grpc_byte_buffer_reader reader;
+
+  LOG_TEST("test_byte_buffer_from_reader");
+  slice = gpr_slice_malloc(4);
+  memcpy(GPR_SLICE_START_PTR(slice), "test", 4);
+  buffer = grpc_raw_byte_buffer_create(&slice, 1);
+  gpr_slice_unref(slice);
+  grpc_byte_buffer_reader_init(&reader, buffer);
+
+  buffer_from_reader = grpc_raw_byte_buffer_from_reader(&reader);
+  GPR_ASSERT(buffer->type == buffer_from_reader->type);
+  GPR_ASSERT(buffer_from_reader->data.raw.compression == GRPC_COMPRESS_NONE);
+  GPR_ASSERT(buffer_from_reader->data.raw.slice_buffer.count == 1);
+  GPR_ASSERT(memcmp(GPR_SLICE_START_PTR(
+                        buffer_from_reader->data.raw.slice_buffer.slices[0]),
+                    "test", 4) == 0);
+
+  grpc_byte_buffer_destroy(buffer);
+  grpc_byte_buffer_destroy(buffer_from_reader);
+}
+
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   test_read_one_slice();
@@ -167,6 +191,7 @@
   test_read_none_compressed_slice();
   test_read_gzip_compressed_slice();
   test_read_deflate_compressed_slice();
+  test_byte_buffer_from_reader();
 
   return 0;
 }
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index d120a8a..e1e44f9 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -168,7 +168,7 @@
       if (!closed_loop_) {
         rpc_deadlines_.emplace_back();
         next_channel_.push_back(i % channel_count_);
-        issue_allowed_.push_back(true);
+        issue_allowed_.emplace_back(true);
 
         grpc_time next_issue;
         NextIssueTime(i, &next_issue);
@@ -199,6 +199,15 @@
         delete ClientRpcContext::detag(got_tag);
       }
     }
+    // Now clear out all the pre-allocated idle contexts
+    for (int ch = 0; ch < channel_count_; ch++) {
+      while (!contexts_[ch].empty()) {
+        // Get an idle context from the front of the list
+        auto* ctx = *(contexts_[ch].begin());
+        contexts_[ch].pop_front();
+        delete ctx;
+      }
+    }
   }
 
   bool ThreadFunc(Histogram* histogram,
@@ -307,11 +316,20 @@
   }
 
  private:
+  class boolean { // exists only to avoid data-race on vector<bool>
+   public:
+    boolean(): val_(false) {}
+    boolean(bool b): val_(b) {}
+    operator bool() const {return val_;}
+    boolean& operator=(bool b) {val_=b; return *this;}
+   private:
+    bool val_;
+  };
   std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_;
 
   std::vector<deadline_list> rpc_deadlines_;  // per thread deadlines
   std::vector<int> next_channel_;      // per thread round-robin channel ctr
-  std::vector<bool> issue_allowed_;    // may this thread attempt to issue
+  std::vector<boolean> issue_allowed_; // may this thread attempt to issue
   std::vector<grpc_time> next_issue_;  // when should it issue?
 
   std::vector<std::mutex> channel_lock_;
diff --git a/test/cpp/qps/perf_db.proto b/test/cpp/qps/perf_db.proto
new file mode 100644
index 0000000..60e0384
--- /dev/null
+++ b/test/cpp/qps/perf_db.proto
@@ -0,0 +1,71 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+import "test/cpp/qps/qpstest.proto";
+
+package grpc.testing;
+
+service PerfDbTransfer {
+  // Sends client info
+  rpc RecordSingleClientData(SingleUserRecordRequest)
+      returns (SingleUserRecordReply) {
+  }
+}
+
+// Metrics to be stored
+message Metrics {
+  double qps = 1;
+  double qps_per_core = 2;
+  double perc_lat_50 = 3;
+  double perc_lat_90 = 4;
+  double perc_lat_95 = 5;
+  double perc_lat_99 = 6;
+  double perc_lat_99_point_9 = 7;
+  double server_system_time = 8;
+  double server_user_time = 9;
+  double client_system_time = 10;
+  double client_user_time = 11;
+}
+
+// Request for storing a single user's data
+message SingleUserRecordRequest {
+  string hashed_id = 1;
+  string test_name = 2;
+  string sys_info = 3;
+  string tag = 4;
+  Metrics metrics = 5;
+  ClientConfig client_config = 6;
+  ServerConfig server_config = 7;
+}
+
+// Reply to request for storing single user's data
+message SingleUserRecordReply {
+}
diff --git a/test/cpp/qps/perf_db_client.cc b/test/cpp/qps/perf_db_client.cc
new file mode 100644
index 0000000..08d20f0
--- /dev/null
+++ b/test/cpp/qps/perf_db_client.cc
@@ -0,0 +1,143 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/qps/perf_db_client.h"
+
+namespace grpc {
+namespace testing {
+
+// sets the client and server config information
+void PerfDbClient::setConfigs(const ClientConfig& client_config,
+                              const ServerConfig& server_config) {
+  client_config_ = client_config;
+  server_config_ = server_config;
+}
+
+// sets the QPS
+void PerfDbClient::setQps(double qps) {
+  qps_ = qps;
+}
+
+// sets the QPS per core
+void PerfDbClient::setQpsPerCore(double qps_per_core) {
+  qps_per_core_ = qps_per_core;
+}
+
+// sets the 50th, 90th, 95th, 99th and 99.9th percentile latency
+void PerfDbClient::setLatencies(double perc_lat_50,
+                                double perc_lat_90,
+                                double perc_lat_95,
+                                double perc_lat_99,
+                                double perc_lat_99_point_9) {
+  perc_lat_50_ = perc_lat_50;
+  perc_lat_90_ = perc_lat_90;
+  perc_lat_95_ = perc_lat_95;
+  perc_lat_99_ = perc_lat_99;
+  perc_lat_99_point_9_ = perc_lat_99_point_9;
+}
+
+// sets the server and client, user and system times
+void PerfDbClient::setTimes(double server_system_time, double server_user_time,
+                            double client_system_time, double client_user_time) {
+  server_system_time_ = server_system_time;
+  server_user_time_ = server_user_time;
+  client_system_time_ = client_system_time;
+  client_user_time_ = client_user_time;
+}
+
+// sends the data to the performance database server
+bool PerfDbClient::sendData(std::string hashed_id, std::string test_name,
+                            std::string sys_info, std::string tag) {
+  // Data record request object
+  SingleUserRecordRequest single_user_record_request;
+
+  // setting access token, name of the test and the system information
+  single_user_record_request.set_hashed_id(hashed_id);
+  single_user_record_request.set_test_name(test_name);
+  single_user_record_request.set_sys_info(sys_info);
+  single_user_record_request.set_tag(tag);
+
+  // setting configs
+  *(single_user_record_request.mutable_client_config()) = client_config_;
+  *(single_user_record_request.mutable_server_config()) = server_config_;
+
+  Metrics* metrics = single_user_record_request.mutable_metrics();
+
+  // setting metrcs in data record request
+  if (qps_ != DBL_MIN) {
+    metrics->set_qps(qps_);
+  }
+  if (qps_per_core_ != DBL_MIN) {
+    metrics->set_qps_per_core(qps_per_core_);
+  }
+  if (perc_lat_50_ != DBL_MIN) {
+    metrics->set_perc_lat_50(perc_lat_50_);
+  }
+  if (perc_lat_90_ != DBL_MIN) {
+    metrics->set_perc_lat_90(perc_lat_90_);
+  }
+  if (perc_lat_95_ != DBL_MIN) {
+    metrics->set_perc_lat_95(perc_lat_95_);
+  }
+  if (perc_lat_99_ != DBL_MIN) {
+    metrics->set_perc_lat_99(perc_lat_99_);
+  }
+  if (perc_lat_99_point_9_ != DBL_MIN) {
+    metrics->set_perc_lat_99_point_9(perc_lat_99_point_9_);
+  }
+  if (server_system_time_ != DBL_MIN) {
+    metrics->set_server_system_time(server_system_time_);
+  }
+  if (server_user_time_ != DBL_MIN) {
+    metrics->set_server_user_time(server_user_time_);
+  }
+  if (client_system_time_ != DBL_MIN) {
+    metrics->set_client_system_time(client_system_time_);
+  }
+  if (client_user_time_ != DBL_MIN) {
+    metrics->set_client_user_time(client_user_time_);
+  }
+
+  SingleUserRecordReply single_user_record_reply;
+  ClientContext context;
+
+  Status status = stub_->RecordSingleClientData(
+      &context, single_user_record_request, &single_user_record_reply);
+  if (status.ok()) {
+    return true;  // data sent to database successfully
+  } else {
+    return false;  // error in data sending
+  }
+}
+}  // testing
+}  // grpc
diff --git a/test/cpp/qps/perf_db_client.h b/test/cpp/qps/perf_db_client.h
new file mode 100644
index 0000000..ce7a88b
--- /dev/null
+++ b/test/cpp/qps/perf_db_client.h
@@ -0,0 +1,115 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <cfloat>
+
+#include <grpc/grpc.h>
+#include <grpc++/channel_arguments.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/credentials.h>
+#include <grpc++/status.h>
+#include "test/cpp/qps/perf_db.grpc.pb.h"
+
+namespace grpc {
+namespace testing {
+
+// Manages data sending to performance database server
+class PerfDbClient {
+ public:
+  PerfDbClient() {
+    qps_ = DBL_MIN;
+    qps_per_core_ = DBL_MIN;
+    perc_lat_50_ = DBL_MIN;
+    perc_lat_90_ = DBL_MIN;
+    perc_lat_95_ = DBL_MIN;
+    perc_lat_99_ = DBL_MIN;
+    perc_lat_99_point_9_ = DBL_MIN;
+    server_system_time_ = DBL_MIN;
+    server_user_time_ = DBL_MIN;
+    client_system_time_ = DBL_MIN;
+    client_user_time_ = DBL_MIN;
+  }
+
+  void init(std::shared_ptr<ChannelInterface> channel) {
+    stub_ = PerfDbTransfer::NewStub(channel);
+  }
+
+  ~PerfDbClient() {}
+
+  // sets the client and server config information
+  void setConfigs(const ClientConfig& client_config,
+                  const ServerConfig& server_config);
+
+  // sets the qps
+  void setQps(double qps);
+
+  // sets the qps per core
+  void setQpsPerCore(double qps_per_core);
+
+  // sets the 50th, 90th, 95th, 99th and 99.9th percentile latency
+  void setLatencies(double perc_lat_50, double perc_lat_90,
+                    double perc_lat_95, double perc_lat_99,
+                    double perc_lat_99_point_9);
+
+  // sets the server and client, user and system times
+  void setTimes(double server_system_time, double server_user_time,
+                double client_system_time, double client_user_time);
+
+  // sends the data to the performance database server
+  bool sendData(std::string hashed_id, std::string test_name,
+                std::string sys_info, std::string tag);
+
+ private:
+  std::unique_ptr<PerfDbTransfer::Stub> stub_;
+  ClientConfig client_config_;
+  ServerConfig server_config_;
+  double qps_;
+  double qps_per_core_;
+  double perc_lat_50_;
+  double perc_lat_90_;
+  double perc_lat_95_;
+  double perc_lat_99_;
+  double perc_lat_99_point_9_;
+  double server_system_time_;
+  double server_user_time_;
+  double client_system_time_;
+  double client_user_time_;
+};
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/qps/qps_test_openloop.cc b/test/cpp/qps/qps_test_openloop.cc
index 52873b2..96a9b45 100644
--- a/test/cpp/qps/qps_test_openloop.cc
+++ b/test/cpp/qps/qps_test_openloop.cc
@@ -60,7 +60,7 @@
   client_config.set_rpc_type(UNARY);
   client_config.set_load_type(POISSON);
   client_config.mutable_load_params()->
-    mutable_poisson()->set_offered_load(10000.0);
+    mutable_poisson()->set_offered_load(1000.0);
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc
index 94aacdb..ff01ec1 100644
--- a/test/cpp/qps/report.cc
+++ b/test/cpp/qps/report.cc
@@ -67,7 +67,6 @@
   }
 }
 
-
 void GprLogReporter::ReportQPS(const ScenarioResult& result) {
   gpr_log(GPR_INFO, "QPS: %.1f",
           result.latencies.Count() /
@@ -76,10 +75,9 @@
 }
 
 void GprLogReporter::ReportQPSPerCore(const ScenarioResult& result) {
-  auto qps =
-      result.latencies.Count() /
-      average(result.client_resources,
-          [](ResourceUsage u) { return u.wall_time; });
+  auto qps = result.latencies.Count() /
+             average(result.client_resources,
+                     [](ResourceUsage u) { return u.wall_time; });
 
   gpr_log(GPR_INFO, "QPS: %.1f (%.1f/server core)", qps,
           qps / result.server_config.threads());
@@ -118,5 +116,71 @@
                   [](ResourceUsage u) { return u.wall_time; }));
 }
 
+void PerfDbReporter::ReportQPS(const ScenarioResult& result) {
+  auto qps = result.latencies.Count() /
+             average(result.client_resources,
+                     [](ResourceUsage u) { return u.wall_time; });
+
+  perf_db_client_.setQps(qps);
+  perf_db_client_.setConfigs(result.client_config, result.server_config);
+}
+
+void PerfDbReporter::ReportQPSPerCore(const ScenarioResult& result) {
+  auto qps = result.latencies.Count() /
+             average(result.client_resources,
+                     [](ResourceUsage u) { return u.wall_time; });
+
+  auto qpsPerCore = qps / result.server_config.threads();
+
+  perf_db_client_.setQps(qps);
+  perf_db_client_.setQpsPerCore(qpsPerCore);
+  perf_db_client_.setConfigs(result.client_config, result.server_config);
+}
+
+void PerfDbReporter::ReportLatency(const ScenarioResult& result) {
+  perf_db_client_.setLatencies(result.latencies.Percentile(50) / 1000,
+                             result.latencies.Percentile(90) / 1000,
+                             result.latencies.Percentile(95) / 1000,
+                             result.latencies.Percentile(99) / 1000,
+                             result.latencies.Percentile(99.9) / 1000);
+  perf_db_client_.setConfigs(result.client_config, result.server_config);
+}
+
+void PerfDbReporter::ReportTimes(const ScenarioResult& result) {
+  double server_system_time =
+      100.0 * sum(result.server_resources,
+                  [](ResourceUsage u) { return u.system_time; }) /
+      sum(result.server_resources, [](ResourceUsage u) { return u.wall_time; });
+  double server_user_time =
+      100.0 * sum(result.server_resources,
+                  [](ResourceUsage u) { return u.user_time; }) /
+      sum(result.server_resources, [](ResourceUsage u) { return u.wall_time; });
+  double client_system_time =
+      100.0 * sum(result.client_resources,
+                  [](ResourceUsage u) { return u.system_time; }) /
+      sum(result.client_resources, [](ResourceUsage u) { return u.wall_time; });
+  double client_user_time =
+      100.0 * sum(result.client_resources,
+                  [](ResourceUsage u) { return u.user_time; }) /
+      sum(result.client_resources, [](ResourceUsage u) { return u.wall_time; });
+
+  perf_db_client_.setTimes(server_system_time, server_user_time, client_system_time,
+                         client_user_time);
+  perf_db_client_.setConfigs(result.client_config, result.server_config);
+}
+
+void PerfDbReporter::SendData() {
+  // send data to performance database
+  bool data_state =
+      perf_db_client_.sendData(hashed_id_, test_name_, sys_info_, tag_);
+
+  // check state of data sending
+  if (data_state) {
+    gpr_log(GPR_INFO, "Data sent to performance database successfully");
+  } else {
+    gpr_log(GPR_INFO, "Data could not be sent to performance database");
+  }
+}
+
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/cpp/qps/report.h b/test/cpp/qps/report.h
index b1cf83f..aec3cbe 100644
--- a/test/cpp/qps/report.h
+++ b/test/cpp/qps/report.h
@@ -41,6 +41,7 @@
 
 #include "test/cpp/qps/driver.h"
 #include "test/cpp/qps/qpstest.grpc.pb.h"
+#include "test/cpp/qps/perf_db_client.h"
 
 namespace grpc {
 namespace testing {
@@ -103,6 +104,35 @@
   void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
 };
 
+/** Reporter for performance database tool */
+class PerfDbReporter : public Reporter {
+ public:
+  PerfDbReporter(const string& name, const string& hashed_id,
+                 const string& test_name, const string& sys_info,
+                 const string& server_address, const string& tag)
+      : Reporter(name),
+        hashed_id_(hashed_id),
+        test_name_(test_name),
+        sys_info_(sys_info),
+        tag_(tag) {
+    perf_db_client_.init(grpc::CreateChannel(
+        server_address, grpc::InsecureCredentials(), ChannelArguments()));
+  }
+  ~PerfDbReporter() GRPC_OVERRIDE { SendData(); };
+
+ private:
+  PerfDbClient perf_db_client_;
+  std::string hashed_id_;
+  std::string test_name_;
+  std::string sys_info_;
+  std::string tag_;
+  void ReportQPS(const ScenarioResult& result) GRPC_OVERRIDE;
+  void ReportQPSPerCore(const ScenarioResult& result) GRPC_OVERRIDE;
+  void ReportLatency(const ScenarioResult& result) GRPC_OVERRIDE;
+  void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
+  void SendData();
+};
+
 }  // namespace testing
 }  // namespace grpc
 
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index 210aef4..f5251e9 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -64,7 +64,7 @@
 
 class AsyncQpsServerTest : public Server {
  public:
-  AsyncQpsServerTest(const ServerConfig &config, int port) : shutdown_(false) {
+  AsyncQpsServerTest(const ServerConfig &config, int port) {
     char *server_address = NULL;
     gpr_join_host_port(&server_address, "::", port);
 
@@ -97,6 +97,9 @@
       }
     }
     for (int i = 0; i < config.threads(); i++) {
+      shutdown_state_.emplace_back(new PerThreadShutdownState());
+    }
+    for (int i = 0; i < config.threads(); i++) {
       threads_.push_back(std::thread([=]() {
         // Wait until work is available or we are shutting down
         bool ok;
@@ -105,11 +108,9 @@
           ServerRpcContext *ctx = detag(got_tag);
           // The tag is a pointer to an RPC context to invoke
           bool still_going = ctx->RunNextState(ok);
-          std::unique_lock<std::mutex> g(shutdown_mutex_);
-          if (!shutdown_) {
+          if (!shutdown_state_[i]->shutdown()) {
             // this RPC context is done, so refresh it
             if (!still_going) {
-              g.unlock();
               ctx->Reset();
             }
           } else {
@@ -122,9 +123,8 @@
   }
   ~AsyncQpsServerTest() {
     server_->Shutdown();
-    {
-      std::lock_guard<std::mutex> g(shutdown_mutex_);
-      shutdown_ = true;
+    for (auto ss = shutdown_state_.begin(); ss != shutdown_state_.end(); ++ss) {
+      (*ss)->set_shutdown();
     }
     for (auto thr = threads_.begin(); thr != threads_.end(); thr++) {
       thr->join();
@@ -316,8 +316,25 @@
   TestService::AsyncService async_service_;
   std::forward_list<ServerRpcContext *> contexts_;
 
-  std::mutex shutdown_mutex_;
-  bool shutdown_;
+  class PerThreadShutdownState {
+   public:
+    PerThreadShutdownState() : shutdown_(false) {}
+
+    bool shutdown() const {
+      std::lock_guard<std::mutex> lock(mutex_);
+      return shutdown_;
+    }
+
+    void set_shutdown() {
+      std::lock_guard<std::mutex> lock(mutex_);
+      shutdown_ = true;
+    }
+
+   private:
+    mutable std::mutex mutex_;
+    bool shutdown_;
+  };
+  std::vector<std::unique_ptr<PerThreadShutdownState>> shutdown_state_;
 };
 
 std::unique_ptr<Server> CreateAsyncServer(const ServerConfig &config,
diff --git a/test/cpp/util/benchmark_config.cc b/test/cpp/util/benchmark_config.cc
index 5b3c1da..91fbbf9 100644
--- a/test/cpp/util/benchmark_config.cc
+++ b/test/cpp/util/benchmark_config.cc
@@ -37,6 +37,18 @@
 DEFINE_bool(enable_log_reporter, true,
             "Enable reporting of benchmark results through GprLog");
 
+DEFINE_bool(report_metrics_db, false, "True if metrics to be reported to performance database");
+
+DEFINE_string(hashed_id, "", "Hash of the user id");
+
+DEFINE_string(test_name, "", "Name of the test being executed");
+
+DEFINE_string(sys_info, "", "System information");
+
+DEFINE_string(server_address, "localhost:50052", "Address of the performance database server");
+
+DEFINE_string(tag, "", "Optional tag for the test");
+
 // In some distros, gflags is in the namespace google, and in some others,
 // in gflags. This hack is enabling us to find both.
 namespace google {}
@@ -57,6 +69,12 @@
     composite_reporter->add(
         std::unique_ptr<Reporter>(new GprLogReporter("LogReporter")));
   }
+  if(FLAGS_report_metrics_db) {
+    composite_reporter->add(
+      std::unique_ptr<Reporter>(new PerfDbReporter("PerfDbReporter", FLAGS_hashed_id, FLAGS_test_name, 
+        FLAGS_sys_info, FLAGS_server_address, FLAGS_tag)));
+  }
+
   return std::shared_ptr<Reporter>(composite_reporter);
 }
 
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index e6f2ef6..453cb6e 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -9807,6 +9807,9 @@
       "test/cpp/qps/driver.h", 
       "test/cpp/qps/histogram.h", 
       "test/cpp/qps/interarrival.h", 
+      "test/cpp/qps/perf_db.grpc.pb.h", 
+      "test/cpp/qps/perf_db.pb.h", 
+      "test/cpp/qps/perf_db_client.h", 
       "test/cpp/qps/qps_worker.h", 
       "test/cpp/qps/qpstest.grpc.pb.h", 
       "test/cpp/qps/qpstest.pb.h", 
@@ -9826,6 +9829,8 @@
       "test/cpp/qps/driver.h", 
       "test/cpp/qps/histogram.h", 
       "test/cpp/qps/interarrival.h", 
+      "test/cpp/qps/perf_db_client.cc", 
+      "test/cpp/qps/perf_db_client.h", 
       "test/cpp/qps/qps_worker.cc", 
       "test/cpp/qps/qps_worker.h", 
       "test/cpp/qps/report.cc",