diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1f647e0a988d70c317ac45d1e8321cdf945d4e72..c55825b37d0b74ef66597fe5485cf74776383df9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,29 +48,115 @@ set(PACKAGE_TARNAME   "${PACKAGE_NAME}-${PACKAGE_VERSION}")
 set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
 project(${PACKAGE_NAME} C CXX)
 
-if(NOT BORINGSSL_ROOT_DIR)
-  set(BORINGSSL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/boringssl)
+set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "Provider of zlib library")
+set_property(CACHE gRPC_ZLIB_PROVIDER PROPERTY STRINGS "module" "package")
+
+set(gRPC_SSL_PROVIDER "module" CACHE STRING "Provider of ssl library")
+set_property(CACHE gRPC_SSL_PROVIDER PROPERTY STRINGS "module" "package")
+
+set(gRPC_PROTOBUF_PROVIDER "module" CACHE STRING "Provider of protobuf library")
+set_property(CACHE gRPC_PROTOBUF_PROVIDER PROPERTY STRINGS "module" "package")
+
+set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library")
+
+if (gRPC_USE_PROTO_LITE)
+  set(_gRPC_PROTOBUF_LIBRARY_NAME "libprotobuf-lite")
+  add_definitions("-DGRPC_USE_PROTO_LITE")
+else()
+  set(_gRPC_PROTOBUF_LIBRARY_NAME "libprotobuf")
 endif()
-if(NOT PROTOBUF_ROOT_DIR)
-  set(PROTOBUF_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf)
+
+if("${gRPC_ZLIB_PROVIDER}" STREQUAL "module")
+  if(NOT ZLIB_ROOT_DIR)
+    set(ZLIB_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/zlib)
+  endif()
+  set(ZLIB_INCLUDE_DIR "${ZLIB_ROOT_DIR}")
+  if(EXISTS "${ZLIB_ROOT_DIR}/CMakeLists.txt")
+      add_subdirectory(${ZLIB_ROOT_DIR} third_party/zlib)
+      if(TARGET zlibstatic)
+          set(_gRPC_ZLIB_LIBRARIES zlibstatic)
+      endif()
+  else()
+      message(WARNING "gRPC_ZLIB_PROVIDER is \"module\" but ZLIB_ROOT_DIR is wrong")
+  endif()
+elseif("${gRPC_ZLIB_PROVIDER}" STREQUAL "package")
+  find_package(ZLIB)
+  if(TARGET ZLIB::ZLIB)
+    set(_gRPC_ZLIB_LIBRARIES ZLIB::ZLIB)
+  endif()
+  set(_gRPC_FIND_ZLIB "if(NOT ZLIB_FOUND)\n  find_package(ZLIB)\nendif()")
 endif()
-if(NOT ZLIB_ROOT_DIR)
-  set(ZLIB_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/zlib)
+
+if("${gRPC_PROTOBUF_PROVIDER}" STREQUAL "module")
+  # Building the protobuf tests require gmock what is not part of a standard protobuf checkout.
+  # Disable them unless they are explicitly requested from the cmake command line (when we assume
+  # gmock is downloaded to the right location inside protobuf).
+  if(NOT protobuf_BUILD_TESTS)
+    set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests")
+  endif()
+  if(NOT PROTOBUF_ROOT_DIR)
+    set(PROTOBUF_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf)
+  endif()
+  if(EXISTS "${PROTOBUF_ROOT_DIR}/cmake/CMakeLists.txt")
+    add_subdirectory(${PROTOBUF_ROOT_DIR}/cmake third_party/protobuf)
+    if(TARGET ${_gRPC_PROTOBUF_LIBRARY_NAME})
+      set(_gRPC_PROTOBUF_LIBRARIES ${_gRPC_PROTOBUF_LIBRARY_NAME})
+    endif()
+    if(TARGET libprotoc)
+      set(_gRPC_PROTOBUF_PROTOC_LIBRARIES libprotoc)
+    endif()
+  else()
+      message(WARNING "gRPC_PROTOBUF_PROVIDER is \"module\" but PROTOBUF_ROOT_DIR is wrong")
+  endif()
+elseif("${gRPC_PROTOBUF_PROVIDER}" STREQUAL "package")
+  find_package(protobuf CONFIG)
+  if(protobuf_FOUND)
+    if(TARGET protobuf::${_gRPC_PROTOBUF_LIBRARY_NAME})
+      set(_gRPC_PROTOBUF_LIBRARIES protobuf::${_gRPC_PROTOBUF_LIBRARY_NAME})
+    endif()
+    if(TARGET protobuf::libprotoc)
+      set(_gRPC_PROTOBUF_PROTOC_LIBRARIES protobuf::libprotoc)
+    endif()
+    set(_gRPC_FIND_PROTOBUF "if(NOT protobuf_FOUND)\n  find_package(protobuf CONFIG)\nendif()")
+  else()
+    find_package(Protobuf MODULE)
+    set(_gRPC_FIND_PROTOBUF "if(NOT Protobuf_FOUND)\n  find_package(Protobuf)\nendif()")
+  endif()
 endif()
 
-# Building the protobuf tests require gmock what is not part of a standard protobuf checkout.
-# Disable them unless they are explicitly requested from the cmake command line (when we assume
-# gmock is downloaded to the right location inside protobuf).
-if(NOT protobuf_BUILD_TESTS)
-  set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests")
+if("${gRPC_SSL_PROVIDER}" STREQUAL "module")
+  if(NOT BORINGSSL_ROOT_DIR)
+    set(BORINGSSL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/boringssl)
+  endif()
+  if(EXISTS "${BORINGSSL_ROOT_DIR}/CMakeLists.txt")
+    add_subdirectory(${BORINGSSL_ROOT_DIR} third_party/boringssl)
+    if(TARGET ssl)
+      set(_gRPC_SSL_LIBRARIES ssl)
+    endif()
+  else()
+      message(WARNING "gRPC_SSL_PROVIDER is \"module\" but BORINGSSL_ROOT_DIR is wrong")
+  endif()
+elseif("${gRPC_SSL_PROVIDER}" STREQUAL "package")
+  find_package(OpenSSL)
+  if(TARGET OpenSSL::SSL)
+    set(_gRPC_SSL_LIBRARIES OpenSSL::SSL)
+  endif()
+  set(_gRPC_FIND_SSL "if(NOT OpenSSL_FOUND)\n  find_package(OpenSSL)\nendif()")
 endif()
 
-add_subdirectory(${BORINGSSL_ROOT_DIR} third_party/boringssl)
-add_subdirectory(${PROTOBUF_ROOT_DIR}/cmake third_party/protobuf)
-add_subdirectory(${ZLIB_ROOT_DIR} third_party/zlib)
+if(NOT MSVC)
+  set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -std=c11")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+endif()
 
-set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -std=c11")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+if(WIN32 AND MSVC)
+  set(_gRPC_BASELIB_LIBRARIES wsock32 ws2_32)
+endif()
+
+include(GNUInstallDirs)
+if(NOT DEFINED CMAKE_INSTALL_CMAKEDIR)
+  set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/gRPC")
+endif()
 
   
 add_library(gpr
@@ -126,11 +212,69 @@ target_include_directories(gpr
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
 
+foreach(_hdr
+  include/grpc/support/alloc.h
+  include/grpc/support/atm.h
+  include/grpc/support/atm_gcc_atomic.h
+  include/grpc/support/atm_gcc_sync.h
+  include/grpc/support/atm_windows.h
+  include/grpc/support/avl.h
+  include/grpc/support/cmdline.h
+  include/grpc/support/cpu.h
+  include/grpc/support/histogram.h
+  include/grpc/support/host_port.h
+  include/grpc/support/log.h
+  include/grpc/support/log_windows.h
+  include/grpc/support/port_platform.h
+  include/grpc/support/slice.h
+  include/grpc/support/slice_buffer.h
+  include/grpc/support/string_util.h
+  include/grpc/support/subprocess.h
+  include/grpc/support/sync.h
+  include/grpc/support/sync_generic.h
+  include/grpc/support/sync_posix.h
+  include/grpc/support/sync_windows.h
+  include/grpc/support/thd.h
+  include/grpc/support/time.h
+  include/grpc/support/tls.h
+  include/grpc/support/tls_gcc.h
+  include/grpc/support/tls_msvc.h
+  include/grpc/support/tls_pthread.h
+  include/grpc/support/useful.h
+  include/grpc/impl/codegen/alloc.h
+  include/grpc/impl/codegen/atm.h
+  include/grpc/impl/codegen/atm_gcc_atomic.h
+  include/grpc/impl/codegen/atm_gcc_sync.h
+  include/grpc/impl/codegen/atm_windows.h
+  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/port_platform.h
+  include/grpc/impl/codegen/slice.h
+  include/grpc/impl/codegen/slice_buffer.h
+  include/grpc/impl/codegen/sync.h
+  include/grpc/impl/codegen/sync_generic.h
+  include/grpc/impl/codegen/sync_posix.h
+  include/grpc/impl/codegen/sync_windows.h
+  include/grpc/impl/codegen/time.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS gpr EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
   
 add_library(grpc
   src/core/lib/surface/init.c
@@ -327,16 +471,63 @@ target_include_directories(grpc
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
 target_link_libraries(grpc
-  ssl
-  zlibstatic
+  ${_gRPC_BASELIB_LIBRARIES}
+  ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_ZLIB_LIBRARIES}
   gpr
 )
 
+foreach(_hdr
+  include/grpc/byte_buffer.h
+  include/grpc/byte_buffer_reader.h
+  include/grpc/compression.h
+  include/grpc/grpc.h
+  include/grpc/grpc_posix.h
+  include/grpc/status.h
+  include/grpc/impl/codegen/byte_buffer.h
+  include/grpc/impl/codegen/byte_buffer_reader.h
+  include/grpc/impl/codegen/compression_types.h
+  include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/grpc_types.h
+  include/grpc/impl/codegen/propagation_bits.h
+  include/grpc/impl/codegen/status.h
+  include/grpc/impl/codegen/alloc.h
+  include/grpc/impl/codegen/atm.h
+  include/grpc/impl/codegen/atm_gcc_atomic.h
+  include/grpc/impl/codegen/atm_gcc_sync.h
+  include/grpc/impl/codegen/atm_windows.h
+  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/port_platform.h
+  include/grpc/impl/codegen/slice.h
+  include/grpc/impl/codegen/slice_buffer.h
+  include/grpc/impl/codegen/sync.h
+  include/grpc/impl/codegen/sync_generic.h
+  include/grpc/impl/codegen/sync_posix.h
+  include/grpc/impl/codegen/sync_windows.h
+  include/grpc/impl/codegen/time.h
+  include/grpc/grpc_security.h
+  include/grpc/grpc_security_constants.h
+  include/grpc/census.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS grpc EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
   
 add_library(grpc_cronet
   src/core/lib/surface/init.c
@@ -510,15 +701,62 @@ target_include_directories(grpc_cronet
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
 target_link_libraries(grpc_cronet
-  ssl
+  ${_gRPC_BASELIB_LIBRARIES}
+  ${_gRPC_SSL_LIBRARIES}
   gpr
 )
 
+foreach(_hdr
+  include/grpc/byte_buffer.h
+  include/grpc/byte_buffer_reader.h
+  include/grpc/compression.h
+  include/grpc/grpc.h
+  include/grpc/grpc_posix.h
+  include/grpc/status.h
+  include/grpc/impl/codegen/byte_buffer.h
+  include/grpc/impl/codegen/byte_buffer_reader.h
+  include/grpc/impl/codegen/compression_types.h
+  include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/grpc_types.h
+  include/grpc/impl/codegen/propagation_bits.h
+  include/grpc/impl/codegen/status.h
+  include/grpc/impl/codegen/alloc.h
+  include/grpc/impl/codegen/atm.h
+  include/grpc/impl/codegen/atm_gcc_atomic.h
+  include/grpc/impl/codegen/atm_gcc_sync.h
+  include/grpc/impl/codegen/atm_windows.h
+  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/port_platform.h
+  include/grpc/impl/codegen/slice.h
+  include/grpc/impl/codegen/slice_buffer.h
+  include/grpc/impl/codegen/sync.h
+  include/grpc/impl/codegen/sync_generic.h
+  include/grpc/impl/codegen/sync_posix.h
+  include/grpc/impl/codegen/sync_windows.h
+  include/grpc/impl/codegen/time.h
+  include/grpc/grpc_cronet.h
+  include/grpc/grpc_security.h
+  include/grpc/grpc_security_constants.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS grpc_cronet EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
   
 add_library(grpc_unsecure
   src/core/lib/surface/init.c
@@ -686,14 +924,59 @@ target_include_directories(grpc_unsecure
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
 target_link_libraries(grpc_unsecure
+  ${_gRPC_BASELIB_LIBRARIES}
   gpr
 )
 
+foreach(_hdr
+  include/grpc/byte_buffer.h
+  include/grpc/byte_buffer_reader.h
+  include/grpc/compression.h
+  include/grpc/grpc.h
+  include/grpc/grpc_posix.h
+  include/grpc/status.h
+  include/grpc/impl/codegen/byte_buffer.h
+  include/grpc/impl/codegen/byte_buffer_reader.h
+  include/grpc/impl/codegen/compression_types.h
+  include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/grpc_types.h
+  include/grpc/impl/codegen/propagation_bits.h
+  include/grpc/impl/codegen/status.h
+  include/grpc/impl/codegen/alloc.h
+  include/grpc/impl/codegen/atm.h
+  include/grpc/impl/codegen/atm_gcc_atomic.h
+  include/grpc/impl/codegen/atm_gcc_sync.h
+  include/grpc/impl/codegen/atm_windows.h
+  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/port_platform.h
+  include/grpc/impl/codegen/slice.h
+  include/grpc/impl/codegen/slice_buffer.h
+  include/grpc/impl/codegen/sync.h
+  include/grpc/impl/codegen/sync_generic.h
+  include/grpc/impl/codegen/sync_posix.h
+  include/grpc/impl/codegen/sync_windows.h
+  include/grpc/impl/codegen/time.h
+  include/grpc/census.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS grpc_unsecure EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
   
 add_library(grpc++
   src/cpp/client/secure_credentials.cc
@@ -736,16 +1019,131 @@ target_include_directories(grpc++
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
 target_link_libraries(grpc++
-  ssl
-  libprotobuf
+  ${_gRPC_BASELIB_LIBRARIES}
+  ${_gRPC_SSL_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   grpc
 )
 
+foreach(_hdr
+  include/grpc++/alarm.h
+  include/grpc++/channel.h
+  include/grpc++/client_context.h
+  include/grpc++/completion_queue.h
+  include/grpc++/create_channel.h
+  include/grpc++/create_channel_posix.h
+  include/grpc++/generic/async_generic_service.h
+  include/grpc++/generic/generic_stub.h
+  include/grpc++/grpc++.h
+  include/grpc++/impl/call.h
+  include/grpc++/impl/client_unary_call.h
+  include/grpc++/impl/codegen/core_codegen.h
+  include/grpc++/impl/grpc_library.h
+  include/grpc++/impl/method_handler_impl.h
+  include/grpc++/impl/rpc_method.h
+  include/grpc++/impl/rpc_service_method.h
+  include/grpc++/impl/serialization_traits.h
+  include/grpc++/impl/server_builder_option.h
+  include/grpc++/impl/server_builder_plugin.h
+  include/grpc++/impl/server_initializer.h
+  include/grpc++/impl/service_type.h
+  include/grpc++/impl/sync.h
+  include/grpc++/impl/sync_cxx11.h
+  include/grpc++/impl/sync_no_cxx11.h
+  include/grpc++/impl/thd.h
+  include/grpc++/impl/thd_cxx11.h
+  include/grpc++/impl/thd_no_cxx11.h
+  include/grpc++/security/auth_context.h
+  include/grpc++/security/auth_metadata_processor.h
+  include/grpc++/security/credentials.h
+  include/grpc++/security/server_credentials.h
+  include/grpc++/server.h
+  include/grpc++/server_builder.h
+  include/grpc++/server_context.h
+  include/grpc++/server_posix.h
+  include/grpc++/support/async_stream.h
+  include/grpc++/support/async_unary_call.h
+  include/grpc++/support/byte_buffer.h
+  include/grpc++/support/channel_arguments.h
+  include/grpc++/support/config.h
+  include/grpc++/support/slice.h
+  include/grpc++/support/status.h
+  include/grpc++/support/status_code_enum.h
+  include/grpc++/support/string_ref.h
+  include/grpc++/support/stub_options.h
+  include/grpc++/support/sync_stream.h
+  include/grpc++/support/time.h
+  include/grpc++/impl/codegen/async_stream.h
+  include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/call.h
+  include/grpc++/impl/codegen/call_hook.h
+  include/grpc++/impl/codegen/channel_interface.h
+  include/grpc++/impl/codegen/client_context.h
+  include/grpc++/impl/codegen/client_unary_call.h
+  include/grpc++/impl/codegen/completion_queue.h
+  include/grpc++/impl/codegen/completion_queue_tag.h
+  include/grpc++/impl/codegen/config.h
+  include/grpc++/impl/codegen/core_codegen_interface.h
+  include/grpc++/impl/codegen/create_auth_context.h
+  include/grpc++/impl/codegen/grpc_library.h
+  include/grpc++/impl/codegen/method_handler_impl.h
+  include/grpc++/impl/codegen/rpc_method.h
+  include/grpc++/impl/codegen/rpc_service_method.h
+  include/grpc++/impl/codegen/security/auth_context.h
+  include/grpc++/impl/codegen/serialization_traits.h
+  include/grpc++/impl/codegen/server_context.h
+  include/grpc++/impl/codegen/server_interface.h
+  include/grpc++/impl/codegen/service_type.h
+  include/grpc++/impl/codegen/status.h
+  include/grpc++/impl/codegen/status_code_enum.h
+  include/grpc++/impl/codegen/string_ref.h
+  include/grpc++/impl/codegen/stub_options.h
+  include/grpc++/impl/codegen/sync.h
+  include/grpc++/impl/codegen/sync_cxx11.h
+  include/grpc++/impl/codegen/sync_no_cxx11.h
+  include/grpc++/impl/codegen/sync_stream.h
+  include/grpc++/impl/codegen/time.h
+  include/grpc/impl/codegen/byte_buffer.h
+  include/grpc/impl/codegen/byte_buffer_reader.h
+  include/grpc/impl/codegen/compression_types.h
+  include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/grpc_types.h
+  include/grpc/impl/codegen/propagation_bits.h
+  include/grpc/impl/codegen/status.h
+  include/grpc/impl/codegen/alloc.h
+  include/grpc/impl/codegen/atm.h
+  include/grpc/impl/codegen/atm_gcc_atomic.h
+  include/grpc/impl/codegen/atm_gcc_sync.h
+  include/grpc/impl/codegen/atm_windows.h
+  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/port_platform.h
+  include/grpc/impl/codegen/slice.h
+  include/grpc/impl/codegen/slice_buffer.h
+  include/grpc/impl/codegen/sync.h
+  include/grpc/impl/codegen/sync_generic.h
+  include/grpc/impl/codegen/sync_posix.h
+  include/grpc/impl/codegen/sync_windows.h
+  include/grpc/impl/codegen/time.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS grpc++ EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
   
 add_library(grpc++_reflection
   src/cpp/ext/proto_server_reflection.cc
@@ -759,7 +1157,7 @@ target_include_directories(grpc++_reflection
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
@@ -767,6 +1165,78 @@ target_link_libraries(grpc++_reflection
   grpc++
 )
 
+foreach(_hdr
+  include/grpc++/ext/proto_server_reflection_plugin.h
+  include/grpc++/ext/reflection.grpc.pb.h
+  include/grpc++/ext/reflection.pb.h
+  include/grpc++/impl/codegen/proto_utils.h
+  include/grpc++/impl/codegen/async_stream.h
+  include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/call.h
+  include/grpc++/impl/codegen/call_hook.h
+  include/grpc++/impl/codegen/channel_interface.h
+  include/grpc++/impl/codegen/client_context.h
+  include/grpc++/impl/codegen/client_unary_call.h
+  include/grpc++/impl/codegen/completion_queue.h
+  include/grpc++/impl/codegen/completion_queue_tag.h
+  include/grpc++/impl/codegen/config.h
+  include/grpc++/impl/codegen/core_codegen_interface.h
+  include/grpc++/impl/codegen/create_auth_context.h
+  include/grpc++/impl/codegen/grpc_library.h
+  include/grpc++/impl/codegen/method_handler_impl.h
+  include/grpc++/impl/codegen/rpc_method.h
+  include/grpc++/impl/codegen/rpc_service_method.h
+  include/grpc++/impl/codegen/security/auth_context.h
+  include/grpc++/impl/codegen/serialization_traits.h
+  include/grpc++/impl/codegen/server_context.h
+  include/grpc++/impl/codegen/server_interface.h
+  include/grpc++/impl/codegen/service_type.h
+  include/grpc++/impl/codegen/status.h
+  include/grpc++/impl/codegen/status_code_enum.h
+  include/grpc++/impl/codegen/string_ref.h
+  include/grpc++/impl/codegen/stub_options.h
+  include/grpc++/impl/codegen/sync.h
+  include/grpc++/impl/codegen/sync_cxx11.h
+  include/grpc++/impl/codegen/sync_no_cxx11.h
+  include/grpc++/impl/codegen/sync_stream.h
+  include/grpc++/impl/codegen/time.h
+  include/grpc/impl/codegen/byte_buffer.h
+  include/grpc/impl/codegen/byte_buffer_reader.h
+  include/grpc/impl/codegen/compression_types.h
+  include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/grpc_types.h
+  include/grpc/impl/codegen/propagation_bits.h
+  include/grpc/impl/codegen/status.h
+  include/grpc/impl/codegen/alloc.h
+  include/grpc/impl/codegen/atm.h
+  include/grpc/impl/codegen/atm_gcc_atomic.h
+  include/grpc/impl/codegen/atm_gcc_sync.h
+  include/grpc/impl/codegen/atm_windows.h
+  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/port_platform.h
+  include/grpc/impl/codegen/slice.h
+  include/grpc/impl/codegen/slice_buffer.h
+  include/grpc/impl/codegen/sync.h
+  include/grpc/impl/codegen/sync_generic.h
+  include/grpc/impl/codegen/sync_posix.h
+  include/grpc/impl/codegen/sync_windows.h
+  include/grpc/impl/codegen/time.h
+  include/grpc++/impl/codegen/config_protobuf.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS grpc++_reflection EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
   
 add_library(grpc++_unsecure
   src/cpp/common/insecure_create_auth_context.cc
@@ -804,17 +1274,132 @@ target_include_directories(grpc++_unsecure
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
 target_link_libraries(grpc++_unsecure
-  libprotobuf
+  ${_gRPC_BASELIB_LIBRARIES}
+  ${_gRPC_PROTOBUF_LIBRARIES}
   gpr
   grpc_unsecure
   grpc
 )
 
+foreach(_hdr
+  include/grpc++/alarm.h
+  include/grpc++/channel.h
+  include/grpc++/client_context.h
+  include/grpc++/completion_queue.h
+  include/grpc++/create_channel.h
+  include/grpc++/create_channel_posix.h
+  include/grpc++/generic/async_generic_service.h
+  include/grpc++/generic/generic_stub.h
+  include/grpc++/grpc++.h
+  include/grpc++/impl/call.h
+  include/grpc++/impl/client_unary_call.h
+  include/grpc++/impl/codegen/core_codegen.h
+  include/grpc++/impl/grpc_library.h
+  include/grpc++/impl/method_handler_impl.h
+  include/grpc++/impl/rpc_method.h
+  include/grpc++/impl/rpc_service_method.h
+  include/grpc++/impl/serialization_traits.h
+  include/grpc++/impl/server_builder_option.h
+  include/grpc++/impl/server_builder_plugin.h
+  include/grpc++/impl/server_initializer.h
+  include/grpc++/impl/service_type.h
+  include/grpc++/impl/sync.h
+  include/grpc++/impl/sync_cxx11.h
+  include/grpc++/impl/sync_no_cxx11.h
+  include/grpc++/impl/thd.h
+  include/grpc++/impl/thd_cxx11.h
+  include/grpc++/impl/thd_no_cxx11.h
+  include/grpc++/security/auth_context.h
+  include/grpc++/security/auth_metadata_processor.h
+  include/grpc++/security/credentials.h
+  include/grpc++/security/server_credentials.h
+  include/grpc++/server.h
+  include/grpc++/server_builder.h
+  include/grpc++/server_context.h
+  include/grpc++/server_posix.h
+  include/grpc++/support/async_stream.h
+  include/grpc++/support/async_unary_call.h
+  include/grpc++/support/byte_buffer.h
+  include/grpc++/support/channel_arguments.h
+  include/grpc++/support/config.h
+  include/grpc++/support/slice.h
+  include/grpc++/support/status.h
+  include/grpc++/support/status_code_enum.h
+  include/grpc++/support/string_ref.h
+  include/grpc++/support/stub_options.h
+  include/grpc++/support/sync_stream.h
+  include/grpc++/support/time.h
+  include/grpc++/impl/codegen/async_stream.h
+  include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/call.h
+  include/grpc++/impl/codegen/call_hook.h
+  include/grpc++/impl/codegen/channel_interface.h
+  include/grpc++/impl/codegen/client_context.h
+  include/grpc++/impl/codegen/client_unary_call.h
+  include/grpc++/impl/codegen/completion_queue.h
+  include/grpc++/impl/codegen/completion_queue_tag.h
+  include/grpc++/impl/codegen/config.h
+  include/grpc++/impl/codegen/core_codegen_interface.h
+  include/grpc++/impl/codegen/create_auth_context.h
+  include/grpc++/impl/codegen/grpc_library.h
+  include/grpc++/impl/codegen/method_handler_impl.h
+  include/grpc++/impl/codegen/rpc_method.h
+  include/grpc++/impl/codegen/rpc_service_method.h
+  include/grpc++/impl/codegen/security/auth_context.h
+  include/grpc++/impl/codegen/serialization_traits.h
+  include/grpc++/impl/codegen/server_context.h
+  include/grpc++/impl/codegen/server_interface.h
+  include/grpc++/impl/codegen/service_type.h
+  include/grpc++/impl/codegen/status.h
+  include/grpc++/impl/codegen/status_code_enum.h
+  include/grpc++/impl/codegen/string_ref.h
+  include/grpc++/impl/codegen/stub_options.h
+  include/grpc++/impl/codegen/sync.h
+  include/grpc++/impl/codegen/sync_cxx11.h
+  include/grpc++/impl/codegen/sync_no_cxx11.h
+  include/grpc++/impl/codegen/sync_stream.h
+  include/grpc++/impl/codegen/time.h
+  include/grpc/impl/codegen/byte_buffer.h
+  include/grpc/impl/codegen/byte_buffer_reader.h
+  include/grpc/impl/codegen/compression_types.h
+  include/grpc/impl/codegen/connectivity_state.h
+  include/grpc/impl/codegen/grpc_types.h
+  include/grpc/impl/codegen/propagation_bits.h
+  include/grpc/impl/codegen/status.h
+  include/grpc/impl/codegen/alloc.h
+  include/grpc/impl/codegen/atm.h
+  include/grpc/impl/codegen/atm_gcc_atomic.h
+  include/grpc/impl/codegen/atm_gcc_sync.h
+  include/grpc/impl/codegen/atm_windows.h
+  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/port_platform.h
+  include/grpc/impl/codegen/slice.h
+  include/grpc/impl/codegen/slice_buffer.h
+  include/grpc/impl/codegen/sync.h
+  include/grpc/impl/codegen/sync_generic.h
+  include/grpc/impl/codegen/sync_posix.h
+  include/grpc/impl/codegen/sync_windows.h
+  include/grpc/impl/codegen/time.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS grpc++_unsecure EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
   
 add_library(grpc_plugin_support
   src/compiler/cpp_generator.cc
@@ -830,12 +1415,29 @@ target_include_directories(grpc_plugin_support
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
 target_link_libraries(grpc_plugin_support
-  libprotoc
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
+)
+
+foreach(_hdr
+  include/grpc++/impl/codegen/config_protobuf.h
+)
+  string(REPLACE "include/" "" _path ${_hdr})
+  get_filename_component(_path ${_path} PATH)
+  install(FILES ${_hdr}
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_path}"
+  )
+endforeach()
+
+  
+install(TARGETS grpc_plugin_support EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 )
 
   
@@ -848,7 +1450,7 @@ target_include_directories(grpc_csharp_ext
   PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
   PRIVATE ${BORINGSSL_ROOT_DIR}/include
   PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${ZLIB_INCLUDE_DIR}
   PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
 )
 
@@ -858,6 +1460,14 @@ target_link_libraries(grpc_csharp_ext
 )
 
 
+  
+install(TARGETS grpc_csharp_ext EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 
 add_executable(gen_hpack_tables
   tools/codegen/core/gen_hpack_tables.c
@@ -878,6 +1488,13 @@ target_link_libraries(gen_hpack_tables
 )
 
 
+install(TARGETS gen_hpack_tables EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(gen_legal_metadata_characters
   tools/codegen/core/gen_legal_metadata_characters.c
 )
@@ -893,6 +1510,13 @@ target_include_directories(gen_legal_metadata_characters
 
 
 
+install(TARGETS gen_legal_metadata_characters EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_create_jwt
   test/core/security/create_jwt.c
 )
@@ -907,12 +1531,19 @@ target_include_directories(grpc_create_jwt
 )
 
 target_link_libraries(grpc_create_jwt
-  ssl
+  ${_gRPC_SSL_LIBRARIES}
   grpc
   gpr
 )
 
 
+install(TARGETS grpc_create_jwt EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_print_google_default_creds_token
   test/core/security/print_google_default_creds_token.c
 )
@@ -932,6 +1563,13 @@ target_link_libraries(grpc_print_google_default_creds_token
 )
 
 
+install(TARGETS grpc_print_google_default_creds_token EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_verify_jwt
   test/core/security/verify_jwt.c
 )
@@ -951,6 +1589,13 @@ target_link_libraries(grpc_verify_jwt
 )
 
 
+install(TARGETS grpc_verify_jwt EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_cpp_plugin
   src/compiler/cpp_plugin.cc
 )
@@ -965,11 +1610,18 @@ target_include_directories(grpc_cpp_plugin
 )
 
 target_link_libraries(grpc_cpp_plugin
-  libprotoc
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
   grpc_plugin_support
 )
 
 
+install(TARGETS grpc_cpp_plugin EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_csharp_plugin
   src/compiler/csharp_plugin.cc
 )
@@ -984,11 +1636,18 @@ target_include_directories(grpc_csharp_plugin
 )
 
 target_link_libraries(grpc_csharp_plugin
-  libprotoc
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
   grpc_plugin_support
 )
 
 
+install(TARGETS grpc_csharp_plugin EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_node_plugin
   src/compiler/node_plugin.cc
 )
@@ -1003,11 +1662,18 @@ target_include_directories(grpc_node_plugin
 )
 
 target_link_libraries(grpc_node_plugin
-  libprotoc
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
   grpc_plugin_support
 )
 
 
+install(TARGETS grpc_node_plugin EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_objective_c_plugin
   src/compiler/objective_c_plugin.cc
 )
@@ -1022,11 +1688,18 @@ target_include_directories(grpc_objective_c_plugin
 )
 
 target_link_libraries(grpc_objective_c_plugin
-  libprotoc
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
   grpc_plugin_support
 )
 
 
+install(TARGETS grpc_objective_c_plugin EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_python_plugin
   src/compiler/python_plugin.cc
 )
@@ -1041,11 +1714,18 @@ target_include_directories(grpc_python_plugin
 )
 
 target_link_libraries(grpc_python_plugin
-  libprotoc
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
   grpc_plugin_support
 )
 
 
+install(TARGETS grpc_python_plugin EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
 add_executable(grpc_ruby_plugin
   src/compiler/ruby_plugin.cc
 )
@@ -1060,11 +1740,33 @@ target_include_directories(grpc_ruby_plugin
 )
 
 target_link_libraries(grpc_ruby_plugin
-  libprotoc
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
   grpc_plugin_support
 )
 
 
+install(TARGETS grpc_ruby_plugin EXPORT gRPCTargets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+
+
+
 
 
 
+
+install(EXPORT gRPCTargets
+  DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
+  NAMESPACE gRPC::
+)
+
+foreach(_config gRPCConfig gRPCConfigVersion)
+  configure_file(tools/cmake/${_config}.cmake.in
+    ${_config}.cmake @ONLY)
+  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_config}.cmake
+    DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
+  )
+endforeach()
diff --git a/Makefile b/Makefile
index 595e631a0bb5daf3b5ed1d47e31c9d587aca0d03..a109778c469e59432181e4f89598b2454a894f2b 100644
--- a/Makefile
+++ b/Makefile
@@ -1132,7 +1132,7 @@ h2_fd_test: $(BINDIR)/$(CONFIG)/h2_fd_test
 h2_full_test: $(BINDIR)/$(CONFIG)/h2_full_test
 h2_full+pipe_test: $(BINDIR)/$(CONFIG)/h2_full+pipe_test
 h2_full+trace_test: $(BINDIR)/$(CONFIG)/h2_full+trace_test
-h2_loadreporting_test: $(BINDIR)/$(CONFIG)/h2_loadreporting_test
+h2_load_reporting_test: $(BINDIR)/$(CONFIG)/h2_load_reporting_test
 h2_oauth2_test: $(BINDIR)/$(CONFIG)/h2_oauth2_test
 h2_proxy_test: $(BINDIR)/$(CONFIG)/h2_proxy_test
 h2_sockpair_test: $(BINDIR)/$(CONFIG)/h2_sockpair_test
@@ -1148,7 +1148,7 @@ h2_fd_nosec_test: $(BINDIR)/$(CONFIG)/h2_fd_nosec_test
 h2_full_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_nosec_test
 h2_full+pipe_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+pipe_nosec_test
 h2_full+trace_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+trace_nosec_test
-h2_loadreporting_nosec_test: $(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test
+h2_load_reporting_nosec_test: $(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test
 h2_proxy_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_nosec_test
 h2_sockpair_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_nosec_test
 h2_sockpair+trace_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_nosec_test
@@ -1348,7 +1348,7 @@ buildtests_c: privatelibs_c \
   $(BINDIR)/$(CONFIG)/h2_full_test \
   $(BINDIR)/$(CONFIG)/h2_full+pipe_test \
   $(BINDIR)/$(CONFIG)/h2_full+trace_test \
-  $(BINDIR)/$(CONFIG)/h2_loadreporting_test \
+  $(BINDIR)/$(CONFIG)/h2_load_reporting_test \
   $(BINDIR)/$(CONFIG)/h2_oauth2_test \
   $(BINDIR)/$(CONFIG)/h2_proxy_test \
   $(BINDIR)/$(CONFIG)/h2_sockpair_test \
@@ -1364,7 +1364,7 @@ buildtests_c: privatelibs_c \
   $(BINDIR)/$(CONFIG)/h2_full_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_full+pipe_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_full+trace_nosec_test \
-  $(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test \
+  $(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_proxy_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_sockpair_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_sockpair+trace_nosec_test \
@@ -6452,6 +6452,7 @@ LIBEND2END_TESTS_SRC = \
     test/core/end2end/tests/idempotent_request.c \
     test/core/end2end/tests/invoke_large_request.c \
     test/core/end2end/tests/large_metadata.c \
+    test/core/end2end/tests/load_reporting_hook.c \
     test/core/end2end/tests/max_concurrent_streams.c \
     test/core/end2end/tests/max_message_length.c \
     test/core/end2end/tests/negative_deadline.c \
@@ -6530,6 +6531,7 @@ LIBEND2END_NOSEC_TESTS_SRC = \
     test/core/end2end/tests/idempotent_request.c \
     test/core/end2end/tests/invoke_large_request.c \
     test/core/end2end/tests/large_metadata.c \
+    test/core/end2end/tests/load_reporting_hook.c \
     test/core/end2end/tests/max_concurrent_streams.c \
     test/core/end2end/tests/max_message_length.c \
     test/core/end2end/tests/negative_deadline.c \
@@ -14041,34 +14043,34 @@ endif
 endif
 
 
-H2_LOADREPORTING_TEST_SRC = \
-    test/core/end2end/fixtures/h2_loadreporting.c \
+H2_LOAD_REPORTING_TEST_SRC = \
+    test/core/end2end/fixtures/h2_load_reporting.c \
 
-H2_LOADREPORTING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOADREPORTING_TEST_SRC))))
+H2_LOAD_REPORTING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOAD_REPORTING_TEST_SRC))))
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL.
 
-$(BINDIR)/$(CONFIG)/h2_loadreporting_test: openssl_dep_error
+$(BINDIR)/$(CONFIG)/h2_load_reporting_test: openssl_dep_error
 
 else
 
 
 
-$(BINDIR)/$(CONFIG)/h2_loadreporting_test: $(H2_LOADREPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/h2_load_reporting_test: $(H2_LOAD_REPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(H2_LOADREPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_loadreporting_test
+	$(Q) $(LD) $(LDFLAGS) $(H2_LOAD_REPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_load_reporting_test
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_loadreporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_load_reporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
-deps_h2_loadreporting_test: $(H2_LOADREPORTING_TEST_OBJS:.o=.dep)
+deps_h2_load_reporting_test: $(H2_LOAD_REPORTING_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(H2_LOADREPORTING_TEST_OBJS:.o=.dep)
+-include $(H2_LOAD_REPORTING_TEST_OBJS:.o=.dep)
 endif
 endif
 
@@ -14481,23 +14483,23 @@ ifneq ($(NO_DEPS),true)
 endif
 
 
-H2_LOADREPORTING_NOSEC_TEST_SRC = \
-    test/core/end2end/fixtures/h2_loadreporting.c \
+H2_LOAD_REPORTING_NOSEC_TEST_SRC = \
+    test/core/end2end/fixtures/h2_load_reporting.c \
 
-H2_LOADREPORTING_NOSEC_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOADREPORTING_NOSEC_TEST_SRC))))
+H2_LOAD_REPORTING_NOSEC_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOAD_REPORTING_NOSEC_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test: $(H2_LOADREPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test: $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(H2_LOADREPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test
+	$(Q) $(LD) $(LDFLAGS) $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test
 
-$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_loadreporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_load_reporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
-deps_h2_loadreporting_nosec_test: $(H2_LOADREPORTING_NOSEC_TEST_OBJS:.o=.dep)
+deps_h2_load_reporting_nosec_test: $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_DEPS),true)
--include $(H2_LOADREPORTING_NOSEC_TEST_OBJS:.o=.dep)
+-include $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS:.o=.dep)
 endif
 
 
diff --git a/examples/cpp/helloworld/CMakeLists.txt b/examples/cpp/helloworld/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8f098c91a6bc09c0a2a1daf4d7411e58209d0ceb
--- /dev/null
+++ b/examples/cpp/helloworld/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Minimum CMake required
+cmake_minimum_required(VERSION 2.8)
+
+# Project
+project(HelloWorld CXX)
+
+# Protobuf
+set(protobuf_MODULE_COMPATIBLE TRUE)
+find_package(protobuf CONFIG REQUIRED)
+message(STATUS "Using protobuf ${protobuf_VERSION}")
+
+# gRPC
+find_package(gRPC CONFIG REQUIRED)
+message(STATUS "Using gRPC ${gRPC_VERSION}")
+
+# gRPC C++ plugin
+get_target_property(gRPC_CPP_PLUGIN_EXECUTABLE gRPC::grpc_cpp_plugin
+    IMPORTED_LOCATION_RELEASE)
+
+# Proto file
+get_filename_component(hw_proto "../../protos/helloworld.proto" ABSOLUTE)
+get_filename_component(hw_proto_path "${hw_proto}" PATH)
+
+# Generated sources
+protobuf_generate_cpp(hw_proto_srcs hw_proto_hdrs "${hw_proto}")
+set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.cc")
+set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.h")
+add_custom_command(
+      OUTPUT "${hw_grpc_srcs}" "${hw_grpc_hdrs}"
+      COMMAND protobuf::protoc
+      ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" -I "${hw_proto_path}"
+        --plugin=protoc-gen-grpc="${gRPC_CPP_PLUGIN_EXECUTABLE}"
+        "${hw_proto}"
+      DEPENDS "${hw_proto}")
+
+# Generated include directory
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+
+# Targets greeter_[async_](client|server)
+foreach(_target
+  greeter_client greeter_server
+  greeter_async_client greeter_async_server)
+  add_executable(${_target} "${_target}.cc"
+    ${hw_proto_srcs}
+    ${hw_grpc_srcs})
+  target_link_libraries(${_target}
+    protobuf::libprotobuf
+    gRPC::grpc++_unsecure)
+endforeach()
diff --git a/examples/cpp/helloworld/Makefile b/examples/cpp/helloworld/Makefile
index b80515abb005c89618d85c1a79ce6e6615369e31..df82f4688fc527df57a451f70411c1ea91372190 100644
--- a/examples/cpp/helloworld/Makefile
+++ b/examples/cpp/helloworld/Makefile
@@ -29,12 +29,20 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
 
+HOST_SYSTEM = $(shell uname | cut -f 1 -d_)
+SYSTEM ?= $(HOST_SYSTEM)
 CXX = g++
 CPPFLAGS += -I/usr/local/include -pthread
 CXXFLAGS += -std=c++11
+ifeq ($(SYSTEM),Darwin)
+LDFLAGS += -L/usr/local/lib `pkg-config --libs grpc++ grpc`       \
+           -lgrpc++_reflection \
+           -lprotobuf -lpthread -ldl
+else
 LDFLAGS += -L/usr/local/lib `pkg-config --libs grpc++ grpc`       \
            -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed \
            -lprotobuf -lpthread -ldl
+endif
 PROTOC = protoc
 GRPC_CPP_PLUGIN = grpc_cpp_plugin
 GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
diff --git a/examples/cpp/route_guide/Makefile b/examples/cpp/route_guide/Makefile
index 9ee511950997831db1fc683f18bea2bdfdb218e2..ba5e45c05c24123eca651f2c5d19946315a32ad0 100644
--- a/examples/cpp/route_guide/Makefile
+++ b/examples/cpp/route_guide/Makefile
@@ -29,12 +29,20 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
 
+HOST_SYSTEM = $(shell uname | cut -f 1 -d_)
+SYSTEM ?= $(HOST_SYSTEM)
 CXX = g++
 CPPFLAGS += -I/usr/local/include -pthread
 CXXFLAGS += -std=c++11
+ifeq ($(SYSTEM),Darwin)
+LDFLAGS += -L/usr/local/lib `pkg-config --libs grpc++`            \
+           -lgrpc++_reflection \
+           -lprotobuf -lpthread -ldl
+else
 LDFLAGS += -L/usr/local/lib `pkg-config --libs grpc++`            \
            -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed \
            -lprotobuf -lpthread -ldl
+endif
 PROTOC = protoc
 GRPC_CPP_PLUGIN = grpc_cpp_plugin
 GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
diff --git a/examples/objective-c/auth_sample/Misc/GoogleService-Info.plist b/examples/objective-c/auth_sample/Misc/GoogleService-Info.plist
index 86909d84a31ed01543641ba44317fa7732e2403c..ff225074238d80ee7989d10b5891b8b8351ebf6b 100644
--- a/examples/objective-c/auth_sample/Misc/GoogleService-Info.plist
+++ b/examples/objective-c/auth_sample/Misc/GoogleService-Info.plist
@@ -2,9 +2,39 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>AD_UNIT_ID_FOR_BANNER_TEST</key>
+	<string>redacted</string>
+	<key>AD_UNIT_ID_FOR_INTERSTITIAL_TEST</key>
+	<string>redacted</string>
 	<key>CLIENT_ID</key>
 	<string>15087385131-lh9bpkiai9nls53uadju0if6k7un3uih.apps.googleusercontent.com</string>
 	<key>REVERSED_CLIENT_ID</key>
 	<string>com.googleusercontent.apps.15087385131-lh9bpkiai9nls53uadju0if6k7un3uih</string>
+	<key>API_KEY</key>
+	<string>redacted</string>
+	<key>GCM_SENDER_ID</key>
+	<string>redacted</string>
+	<key>PLIST_VERSION</key>
+	<string>1</string>
+	<key>BUNDLE_ID</key>
+	<string>io.grpc.AuthSample</string>
+	<key>PROJECT_ID</key>
+	<string>grpc-authsample</string>
+	<key>STORAGE_BUCKET</key>
+	<string>grpc-authsample.appspot.com</string>
+	<key>IS_ADS_ENABLED</key>
+	<false/>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false/>
+	<key>IS_APPINVITE_ENABLED</key>
+	<false/>
+	<key>IS_GCM_ENABLED</key>
+	<false/>
+	<key>IS_SIGNIN_ENABLED</key>
+	<true/>
+	<key>GOOGLE_APP_ID</key>
+	<string>1:15087385131:ios:d547168abe3c362f</string>
+	<key>DATABASE_URL</key>
+	<string>https://grpc-authsample.firebaseio.com</string>
 </dict>
-</plist>
\ No newline at end of file
+</plist>
diff --git a/include/grpc++/impl/codegen/async_stream.h b/include/grpc++/impl/codegen/async_stream.h
index e96d224ddbeb10fc66c7f869ed506c1115254286..70533aa4d9fed069733265b350ec7baf75126487 100644
--- a/include/grpc++/impl/codegen/async_stream.h
+++ b/include/grpc++/impl/codegen/async_stream.h
@@ -330,6 +330,9 @@ class ServerAsyncReader GRPC_FINAL : public ServerAsyncReaderInterface<W, R> {
     meta_ops_.set_output_tag(tag);
     meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                   ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
     ctx_->sent_initial_metadata_ = true;
     call_.PerformOps(&meta_ops_);
   }
@@ -345,6 +348,9 @@ class ServerAsyncReader GRPC_FINAL : public ServerAsyncReaderInterface<W, R> {
     if (!ctx_->sent_initial_metadata_) {
       finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                       ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     // The response is dropped if the status is not OK.
@@ -363,6 +369,9 @@ class ServerAsyncReader GRPC_FINAL : public ServerAsyncReaderInterface<W, R> {
     if (!ctx_->sent_initial_metadata_) {
       finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                       ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
@@ -400,6 +409,9 @@ class ServerAsyncWriter GRPC_FINAL : public ServerAsyncWriterInterface<W> {
     meta_ops_.set_output_tag(tag);
     meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                   ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
     ctx_->sent_initial_metadata_ = true;
     call_.PerformOps(&meta_ops_);
   }
@@ -409,6 +421,9 @@ class ServerAsyncWriter GRPC_FINAL : public ServerAsyncWriterInterface<W> {
     if (!ctx_->sent_initial_metadata_) {
       write_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        write_ops_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     // TODO(ctiller): don't assert
@@ -421,6 +436,9 @@ class ServerAsyncWriter GRPC_FINAL : public ServerAsyncWriterInterface<W> {
     if (!ctx_->sent_initial_metadata_) {
       finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                       ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
@@ -459,6 +477,9 @@ class ServerAsyncReaderWriter GRPC_FINAL
     meta_ops_.set_output_tag(tag);
     meta_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                   ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
     ctx_->sent_initial_metadata_ = true;
     call_.PerformOps(&meta_ops_);
   }
@@ -474,6 +495,9 @@ class ServerAsyncReaderWriter GRPC_FINAL
     if (!ctx_->sent_initial_metadata_) {
       write_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        write_ops_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     // TODO(ctiller): don't assert
@@ -486,6 +510,9 @@ class ServerAsyncReaderWriter GRPC_FINAL
     if (!ctx_->sent_initial_metadata_) {
       finish_ops_.SendInitialMetadata(ctx_->initial_metadata_,
                                       ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status);
diff --git a/include/grpc++/impl/codegen/async_unary_call.h b/include/grpc++/impl/codegen/async_unary_call.h
index 05681e22425ad17dbd4bacf1b4db249f4ec3e024..5ceab73cea6dca023a82ff4918c3da5db403ae5e 100644
--- a/include/grpc++/impl/codegen/async_unary_call.h
+++ b/include/grpc++/impl/codegen/async_unary_call.h
@@ -126,6 +126,9 @@ class ServerAsyncResponseWriter GRPC_FINAL
     meta_buf_.set_output_tag(tag);
     meta_buf_.SendInitialMetadata(ctx_->initial_metadata_,
                                   ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_buf_.set_compression_level(ctx_->compression_level());
+    }
     ctx_->sent_initial_metadata_ = true;
     call_.PerformOps(&meta_buf_);
   }
@@ -135,6 +138,9 @@ class ServerAsyncResponseWriter GRPC_FINAL
     if (!ctx_->sent_initial_metadata_) {
       finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
                                       ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_buf_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     // The response is dropped if the status is not OK.
@@ -153,6 +159,9 @@ class ServerAsyncResponseWriter GRPC_FINAL
     if (!ctx_->sent_initial_metadata_) {
       finish_buf_.SendInitialMetadata(ctx_->initial_metadata_,
                                       ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_buf_.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status);
diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h
index fab85d15176a50cbd467d7f1158d7704c8b3838c..dfac177970a1aab7eebd33290a4552285220bdbf 100644
--- a/include/grpc++/impl/codegen/call.h
+++ b/include/grpc++/impl/codegen/call.h
@@ -180,17 +180,23 @@ class CallNoOp {
 
 class CallOpSendInitialMetadata {
  public:
-  CallOpSendInitialMetadata() : send_(false) {}
+  CallOpSendInitialMetadata() : send_(false) {
+    maybe_compression_level_.is_set = false;
+  }
 
   void SendInitialMetadata(
       const std::multimap<grpc::string, grpc::string>& metadata,
       uint32_t flags) {
+    maybe_compression_level_.is_set = false;
     send_ = true;
     flags_ = flags;
     initial_metadata_count_ = metadata.size();
     initial_metadata_ = FillMetadataArray(metadata);
-    // TODO(dgq): expose compression level in API so it can be properly set.
-    maybe_compression_level_.is_set = false;
+  }
+
+  void set_compression_level(grpc_compression_level level) {
+    maybe_compression_level_.is_set = true;
+    maybe_compression_level_.level = level;
   }
 
  protected:
diff --git a/include/grpc++/impl/codegen/method_handler_impl.h b/include/grpc++/impl/codegen/method_handler_impl.h
index 21ac6c4fb5545c7c3b73b2291ca9272d182a37b2..2f4be644bae73e8aa7e0c14772346ad1e19f4b00 100644
--- a/include/grpc++/impl/codegen/method_handler_impl.h
+++ b/include/grpc++/impl/codegen/method_handler_impl.h
@@ -65,6 +65,9 @@ class RpcMethodHandler : public MethodHandler {
         ops;
     ops.SendInitialMetadata(param.server_context->initial_metadata_,
                             param.server_context->initial_metadata_flags());
+    if (param.server_context->compression_level_set()) {
+      ops.set_compression_level(param.server_context->compression_level());
+    }
     if (status.ok()) {
       status = ops.SendMessage(rsp);
     }
@@ -104,6 +107,9 @@ class ClientStreamingHandler : public MethodHandler {
         ops;
     ops.SendInitialMetadata(param.server_context->initial_metadata_,
                             param.server_context->initial_metadata_flags());
+    if (param.server_context->compression_level_set()) {
+      ops.set_compression_level(param.server_context->compression_level());
+    }
     if (status.ok()) {
       status = ops.SendMessage(rsp);
     }
@@ -144,6 +150,9 @@ class ServerStreamingHandler : public MethodHandler {
     if (!param.server_context->sent_initial_metadata_) {
       ops.SendInitialMetadata(param.server_context->initial_metadata_,
                               param.server_context->initial_metadata_flags());
+      if (param.server_context->compression_level_set()) {
+        ops.set_compression_level(param.server_context->compression_level());
+      }
     }
     ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
     param.call->PerformOps(&ops);
@@ -177,6 +186,9 @@ class BidiStreamingHandler : public MethodHandler {
     if (!param.server_context->sent_initial_metadata_) {
       ops.SendInitialMetadata(param.server_context->initial_metadata_,
                               param.server_context->initial_metadata_flags());
+      if (param.server_context->compression_level_set()) {
+        ops.set_compression_level(param.server_context->compression_level());
+      }
     }
     ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
     param.call->PerformOps(&ops);
@@ -199,6 +211,9 @@ class UnknownMethodHandler : public MethodHandler {
     if (!context->sent_initial_metadata_) {
       ops->SendInitialMetadata(context->initial_metadata_,
                                context->initial_metadata_flags());
+      if (context->compression_level_set()) {
+        ops->set_compression_level(context->compression_level());
+      }
       context->sent_initial_metadata_ = true;
     }
     ops->ServerSendStatus(context->trailing_metadata_, status);
diff --git a/include/grpc++/impl/codegen/server_context.h b/include/grpc++/impl/codegen/server_context.h
index cea13a513f63b09c0df213a18d85fe017eaf4b90..08212af861b239b2717c59dc6a7b87c603151ed9 100644
--- a/include/grpc++/impl/codegen/server_context.h
+++ b/include/grpc++/impl/codegen/server_context.h
@@ -130,7 +130,13 @@ class ServerContext {
   grpc_compression_level compression_level() const {
     return compression_level_;
   }
-  void set_compression_level(grpc_compression_level level);
+
+  void set_compression_level(grpc_compression_level level) {
+    compression_level_set_ = true;
+    compression_level_ = level;
+  }
+
+  bool compression_level_set() const { return compression_level_set_; }
 
   grpc_compression_algorithm compression_algorithm() const {
     return compression_algorithm_;
@@ -217,6 +223,7 @@ class ServerContext {
   std::multimap<grpc::string, grpc::string> initial_metadata_;
   std::multimap<grpc::string, grpc::string> trailing_metadata_;
 
+  bool compression_level_set_;
   grpc_compression_level compression_level_;
   grpc_compression_algorithm compression_algorithm_;
 };
diff --git a/include/grpc++/impl/codegen/sync_stream.h b/include/grpc++/impl/codegen/sync_stream.h
index cbfa410699596d71937e48f425075b822c04a33f..b2b972760db4dc2b6a52951f2f462ac2e125d949 100644
--- a/include/grpc++/impl/codegen/sync_stream.h
+++ b/include/grpc++/impl/codegen/sync_stream.h
@@ -347,6 +347,9 @@ class ServerReader GRPC_FINAL : public ReaderInterface<R> {
     CallOpSet<CallOpSendInitialMetadata> ops;
     ops.SendInitialMetadata(ctx_->initial_metadata_,
                             ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
     ctx_->sent_initial_metadata_ = true;
     call_->PerformOps(&ops);
     call_->cq()->Pluck(&ops);
@@ -375,6 +378,9 @@ class ServerWriter GRPC_FINAL : public WriterInterface<W> {
     CallOpSet<CallOpSendInitialMetadata> ops;
     ops.SendInitialMetadata(ctx_->initial_metadata_,
                             ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
     ctx_->sent_initial_metadata_ = true;
     call_->PerformOps(&ops);
     call_->cq()->Pluck(&ops);
@@ -389,6 +395,9 @@ class ServerWriter GRPC_FINAL : public WriterInterface<W> {
     if (!ctx_->sent_initial_metadata_) {
       ops.SendInitialMetadata(ctx_->initial_metadata_,
                               ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ops.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     call_->PerformOps(&ops);
@@ -413,6 +422,9 @@ class ServerReaderWriter GRPC_FINAL : public WriterInterface<W>,
     CallOpSet<CallOpSendInitialMetadata> ops;
     ops.SendInitialMetadata(ctx_->initial_metadata_,
                             ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
     ctx_->sent_initial_metadata_ = true;
     call_->PerformOps(&ops);
     call_->cq()->Pluck(&ops);
@@ -434,6 +446,9 @@ class ServerReaderWriter GRPC_FINAL : public WriterInterface<W>,
     if (!ctx_->sent_initial_metadata_) {
       ops.SendInitialMetadata(ctx_->initial_metadata_,
                               ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ops.set_compression_level(ctx_->compression_level());
+      }
       ctx_->sent_initial_metadata_ = true;
     }
     call_->PerformOps(&ops);
diff --git a/package.xml b/package.xml
index 66bd6886bbffb1835102bcf9bdcefeefa336258e..f395334745c29baa984ae0bc84445fc11f59f662 100644
--- a/package.xml
+++ b/package.xml
@@ -10,7 +10,7 @@
   <email>grpc-packages@google.com</email>
   <active>yes</active>
  </lead>
- <date>2016-07-21</date>
+ <date>2016-07-28</date>
  <time>16:06:07</time>
  <version>
   <release>1.1.0</release>
@@ -22,7 +22,7 @@
  </stability>
  <license>BSD</license>
  <notes>
-- PHP7 Support #7464
+- PHP7 Support continued, reduce code duplication #7543
  </notes>
  <contents>
   <dir baseinstalldir="/" name="/">
@@ -1118,5 +1118,20 @@ Update to wrap gRPC C Core version 0.10.0
 - PHP7 Support #7464
    </notes>
   </release>
+  <release>
+   <version>
+    <release>1.0.0RC3</release>
+    <api>1.0.0RC3</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-07-28</date>
+   <license>BSD</license>
+   <notes>
+- PHP7 Support continued, reduce code duplication #7543
+   </notes>
+  </release>
  </changelog>
 </package>
diff --git a/src/compiler/ruby_generator_helpers-inl.h b/src/compiler/ruby_generator_helpers-inl.h
index ff6939ed9fed0a0e5bc20944e61e97e26a01ecaf..aa3c22fea6dc95a9e8e40f9607957f8916200071 100644
--- a/src/compiler/ruby_generator_helpers-inl.h
+++ b/src/compiler/ruby_generator_helpers-inl.h
@@ -48,7 +48,7 @@ inline bool ServicesFilename(const grpc::protobuf::FileDescriptor *file,
       file->name().find_last_of(".proto") == file->name().size() - 1) {
     *file_name_or_error =
         file->name().substr(0, file->name().size() - proto_suffix_length) +
-        "_services.rb";
+        "_services_pb.rb";
     return true;
   } else {
     *file_name_or_error = "Invalid proto file name:  must end with .proto";
@@ -58,7 +58,7 @@ inline bool ServicesFilename(const grpc::protobuf::FileDescriptor *file,
 
 inline grpc::string MessagesRequireName(
     const grpc::protobuf::FileDescriptor *file) {
-  return Replace(file->name(), ".proto", "");
+  return Replace(file->name(), ".proto", "_pb");
 }
 
 // Get leading or trailing comments in a string. Comment lines start with "# ".
diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c
index f51d850e013b88df231525c429a9fdaaf6319948..3004a1fc9738f59f58a1cba9d6c7c18dd7c5fa16 100644
--- a/src/core/ext/census/grpc_filter.c
+++ b/src/core/ext/census/grpc_filter.c
@@ -138,7 +138,7 @@ static void client_init_call_elem(grpc_exec_ctx *exec_ctx,
 
 static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx,
                                      grpc_call_element *elem,
-                                     const grpc_call_stats *stats,
+                                     const grpc_call_final_info *final_info,
                                      void *ignored) {
   call_data *d = elem->call_data;
   GPR_ASSERT(d != NULL);
@@ -158,7 +158,7 @@ static void server_init_call_elem(grpc_exec_ctx *exec_ctx,
 
 static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx,
                                      grpc_call_element *elem,
-                                     const grpc_call_stats *stats,
+                                     const grpc_call_final_info *final_info,
                                      void *ignored) {
   call_data *d = elem->call_data;
   GPR_ASSERT(d != NULL);
diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c
index a096435c984d93cc2b556122d0007a543c04531b..739487a06bb45282b750e0ca046b9a6e65cc0268 100644
--- a/src/core/ext/client_config/client_channel.c
+++ b/src/core/ext/client_config/client_channel.c
@@ -444,7 +444,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
+                              const grpc_call_final_info *final_info,
                               void *and_free_memory) {
   grpc_subchannel_call_holder_destroy(exec_ctx, elem->call_data);
   gpr_free(and_free_memory);
diff --git a/src/core/ext/load_reporting/load_reporting.c b/src/core/ext/load_reporting/load_reporting.c
index 9e4d32676fca01eff23a61bb47199948750d31db..df1ea0ec9ac682cc66ec829ce769fffeeb12f32b 100644
--- a/src/core/ext/load_reporting/load_reporting.c
+++ b/src/core/ext/load_reporting/load_reporting.c
@@ -42,42 +42,12 @@
 #include "src/core/lib/channel/channel_stack_builder.h"
 #include "src/core/lib/surface/channel_init.h"
 
-struct grpc_load_reporting_config {
-  grpc_load_reporting_fn fn;
-  void *user_data;
-};
-
-grpc_load_reporting_config *grpc_load_reporting_config_create(
-    grpc_load_reporting_fn fn, void *user_data) {
-  GPR_ASSERT(fn != NULL);
-  grpc_load_reporting_config *lrc =
-      gpr_malloc(sizeof(grpc_load_reporting_config));
-  lrc->fn = fn;
-  lrc->user_data = user_data;
-  return lrc;
-}
-
-grpc_load_reporting_config *grpc_load_reporting_config_copy(
-    grpc_load_reporting_config *src) {
-  return grpc_load_reporting_config_create(src->fn, src->user_data);
-}
-
-void grpc_load_reporting_config_destroy(grpc_load_reporting_config *lrc) {
-  gpr_free(lrc);
-}
-
-void grpc_load_reporting_config_call(
-    grpc_load_reporting_config *lrc,
-    const grpc_load_reporting_call_data *call_data) {
-  lrc->fn(call_data, lrc->user_data);
-}
-
 static bool is_load_reporting_enabled(const grpc_channel_args *a) {
   if (a == NULL) return false;
   for (size_t i = 0; i < a->num_args; i++) {
     if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_LOAD_REPORTING)) {
-      return a->args[i].type == GRPC_ARG_POINTER &&
-             a->args[i].value.pointer.p != NULL;
+      return a->args[i].type == GRPC_ARG_INTEGER &&
+             a->args[i].value.integer != 0;
     }
   }
   return false;
@@ -94,37 +64,17 @@ static bool maybe_add_load_reporting_filter(grpc_channel_stack_builder *builder,
   return true;
 }
 
-static void lrd_arg_destroy(void *p) { grpc_load_reporting_config_destroy(p); }
-
-static void *lrd_arg_copy(void *p) {
-  return grpc_load_reporting_config_copy(p);
-}
-
-static int lrd_arg_cmp(void *a, void *b) {
-  grpc_load_reporting_config *lhs = a;
-  grpc_load_reporting_config *rhs = b;
-  return !(lhs->fn == rhs->fn && lhs->user_data == rhs->user_data);
-}
-
-static const grpc_arg_pointer_vtable lrd_ptr_vtable = {
-    lrd_arg_copy, lrd_arg_destroy, lrd_arg_cmp};
-
-grpc_arg grpc_load_reporting_config_create_arg(
-    grpc_load_reporting_config *lrc) {
+grpc_arg grpc_load_reporting_enable_arg() {
   grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
+  arg.type = GRPC_ARG_INTEGER;
   arg.key = GRPC_ARG_ENABLE_LOAD_REPORTING;
-  arg.value.pointer.p = lrc;
-  arg.value.pointer.vtable = &lrd_ptr_vtable;
+  arg.value.integer = 1;
   return arg;
 }
 
 /* Plugin registration */
 
 void grpc_load_reporting_plugin_init(void) {
-  grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX,
-                                   maybe_add_load_reporting_filter,
-                                   (void *)&grpc_load_reporting_filter);
   grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
                                    maybe_add_load_reporting_filter,
                                    (void *)&grpc_load_reporting_filter);
diff --git a/src/core/ext/load_reporting/load_reporting.h b/src/core/ext/load_reporting/load_reporting.h
index 316cd89bd72713a6c31eb36f404f0c8e6fd3aee0..e37817d8c2fd0dc87034461cb1dfe36a988e1cfb 100644
--- a/src/core/ext/load_reporting/load_reporting.h
+++ b/src/core/ext/load_reporting/load_reporting.h
@@ -34,42 +34,47 @@
 #ifndef GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_H
 #define GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_H
 
-#include "src/core/lib/iomgr/closure.h"
-#include "src/core/lib/surface/call.h"
+#include <grpc/impl/codegen/grpc_types.h>
+#include "src/core/lib/channel/channel_stack.h"
 
-typedef struct grpc_load_reporting_config grpc_load_reporting_config;
+/** Metadata key for initial metadata coming from clients */
+/* TODO(dgq): change to the final value TBD */
+#define GRPC_LOAD_REPORTING_INITIAL_MD_KEY "load-reporting-initial"
 
-/** Call information to be passed to the provided load reporting function upon
- * completion of the call */
-typedef struct grpc_load_reporting_call_data {
-  const grpc_call_stats *stats;   /**< Stats for the call */
-  const char *trailing_md_string; /**< LR trailing metadata info */
-} grpc_load_reporting_call_data;
+/** Metadata key for trailing metadata from servers */
+/* TODO(dgq): change to the final value TBD */
+#define GRPC_LOAD_REPORTING_TRAILING_MD_KEY "load-reporting-trailing"
 
-/** Custom function to be called by the load reporting filter. */
-typedef void (*grpc_load_reporting_fn)(
-    const grpc_load_reporting_call_data *call_data, void *user_data);
+/** Identifiers for the invocation point of the users LR callback */
+typedef enum grpc_load_reporting_source {
+  GRPC_LR_POINT_UNKNOWN = 0,
+  GRPC_LR_POINT_CHANNEL_CREATION,
+  GRPC_LR_POINT_CHANNEL_DESTRUCTION,
+  GRPC_LR_POINT_CALL_CREATION,
+  GRPC_LR_POINT_CALL_DESTRUCTION
+} grpc_load_reporting_source;
 
-/** Register \a fn as the function to be invoked by the load reporting filter.
- * \a fn will be invoked at the beginning and at the end of the call.
- *
- * For the first invocation, \a fn's first argument
- * (grpc_load_reporting_call_data*) will be NULL. \a user_data is always passed
- * as-is. */
-grpc_load_reporting_config *grpc_load_reporting_config_create(
-    grpc_load_reporting_fn fn, void *user_data);
+/** Call information to be passed to the provided LR callback. */
+typedef struct grpc_load_reporting_call_data {
+  const grpc_load_reporting_source source; /**< point of last data update. */
+
+  /** Unique identifier for the channel associated with the data */
+  intptr_t channel_id;
 
-grpc_load_reporting_config *grpc_load_reporting_config_copy(
-    grpc_load_reporting_config *src);
+  /** Unique identifier for the call associated with the data. If the call
+   * hasn't been created yet, it'll have a value of zero. */
+  intptr_t call_id;
 
-void grpc_load_reporting_config_destroy(grpc_load_reporting_config *lrc);
+  /** Only valid when \a source is \a GRPC_LR_POINT_CALL_DESTRUCTION, that is,
+   * once the call has completed */
+  const grpc_call_final_info *final_info;
 
-/** Invoke the function registered by \a grpc_load_reporting_init. */
-void grpc_load_reporting_config_call(
-    grpc_load_reporting_config *lrc,
-    const grpc_load_reporting_call_data *call_data);
+  const char *initial_md_string;  /**< value string for LR's initial md key */
+  const char *trailing_md_string; /**< value string for LR's trailing md key */
+  const char *method_name;        /**< Corresponds to :path header */
+} grpc_load_reporting_call_data;
 
 /** Return a \a grpc_arg enabling load reporting */
-grpc_arg grpc_load_reporting_config_create_arg(grpc_load_reporting_config *lrc);
+grpc_arg grpc_load_reporting_enable_arg();
 
 #endif /* GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_H */
diff --git a/src/core/ext/load_reporting/load_reporting_filter.c b/src/core/ext/load_reporting/load_reporting_filter.c
index f372f88c3a636604b68fd5a8b27f10643d662189..99b560ae27fc74410296f7847cbac1e38bfd8af5 100644
--- a/src/core/ext/load_reporting/load_reporting_filter.c
+++ b/src/core/ext/load_reporting/load_reporting_filter.c
@@ -31,6 +31,7 @@
  *
  */
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
@@ -42,17 +43,67 @@
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-typedef struct call_data { const char *trailing_md_string; } call_data;
+typedef struct call_data {
+  intptr_t id; /**< an id unique to the call */
+  char *trailing_md_string;
+  char *initial_md_string;
+  const char *service_method;
+
+  /* stores the recv_initial_metadata op's ready closure, which we wrap with our
+   * own (on_initial_md_ready) in order to capture the incoming initial metadata
+   * */
+  grpc_closure *ops_recv_initial_metadata_ready;
+
+  /* to get notified of the availability of the incoming initial metadata. */
+  grpc_closure on_initial_md_ready;
+  grpc_metadata_batch *recv_initial_metadata;
+} call_data;
+
 typedef struct channel_data {
-  gpr_mu mu;
-  grpc_load_reporting_config *lrc;
+  intptr_t id; /**< an id unique to the channel */
 } channel_data;
 
-static void invoke_lr_fn_locked(grpc_load_reporting_config *lrc,
-                                grpc_load_reporting_call_data *lr_call_data) {
-  GPR_TIMER_BEGIN("load_reporting_config_fn", 0);
-  grpc_load_reporting_config_call(lrc, lr_call_data);
-  GPR_TIMER_END("load_reporting_config_fn", 0);
+typedef struct {
+  grpc_call_element *elem;
+  grpc_exec_ctx *exec_ctx;
+} recv_md_filter_args;
+
+static grpc_mdelem *recv_md_filter(void *user_data, grpc_mdelem *md) {
+  recv_md_filter_args *a = user_data;
+  grpc_call_element *elem = a->elem;
+  call_data *calld = elem->call_data;
+
+  if (md->key == GRPC_MDSTR_PATH) {
+    calld->service_method = grpc_mdstr_as_c_string(md->value);
+  } else if (md->key == GRPC_MDSTR_LOAD_REPORTING_INITIAL) {
+    calld->initial_md_string = gpr_strdup(grpc_mdstr_as_c_string(md->value));
+    return NULL;
+  }
+
+  return md;
+}
+
+static void on_initial_md_ready(grpc_exec_ctx *exec_ctx, void *user_data,
+                                grpc_error *err) {
+  grpc_call_element *elem = user_data;
+  call_data *calld = elem->call_data;
+
+  if (err == GRPC_ERROR_NONE) {
+    recv_md_filter_args a;
+    a.elem = elem;
+    a.exec_ctx = exec_ctx;
+    grpc_metadata_batch_filter(calld->recv_initial_metadata, recv_md_filter,
+                               &a);
+    if (calld->service_method == NULL) {
+      err =
+          grpc_error_add_child(err, GRPC_ERROR_CREATE("Missing :path header"));
+    }
+  } else {
+    GRPC_ERROR_REF(err);
+  }
+  calld->ops_recv_initial_metadata_ready->cb(
+      exec_ctx, calld->ops_recv_initial_metadata_ready->cb_arg, err);
+  GRPC_ERROR_UNREF(err);
 }
 
 /* Constructor for call_data */
@@ -60,20 +111,41 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                            grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   memset(calld, 0, sizeof(call_data));
+
+  calld->id = (intptr_t)args->call_stack;
+  grpc_closure_init(&calld->on_initial_md_ready, on_initial_md_ready, elem);
+
+  /* TODO(dgq): do something with the data
+  channel_data *chand = elem->channel_data;
+  grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_CREATION,
+                                                (intptr_t)chand->id,
+                                                (intptr_t)calld->id,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                NULL};
+  */
 }
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
-  channel_data *chand = elem->channel_data;
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   call_data *calld = elem->call_data;
 
-  grpc_load_reporting_call_data lr_call_data = {stats,
-                                                calld->trailing_md_string};
-
-  gpr_mu_lock(&chand->mu);
-  invoke_lr_fn_locked(chand->lrc, &lr_call_data);
-  gpr_mu_unlock(&chand->mu);
+  /* TODO(dgq): do something with the data
+  channel_data *chand = elem->channel_data;
+  grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_DESTRUCTION,
+                                                (intptr_t)chand->id,
+                                                (intptr_t)calld->id,
+                                                final_info,
+                                                calld->initial_md_string,
+                                                calld->trailing_md_string,
+                                                calld->service_method};
+  */
+
+  gpr_free(calld->initial_md_string);
+  gpr_free(calld->trailing_md_string);
 }
 
 /* Constructor for channel_data */
@@ -85,37 +157,40 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
   channel_data *chand = elem->channel_data;
   memset(chand, 0, sizeof(channel_data));
 
-  gpr_mu_init(&chand->mu);
-  for (size_t i = 0; i < args->channel_args->num_args; i++) {
-    if (0 == strcmp(args->channel_args->args[i].key,
-                    GRPC_ARG_ENABLE_LOAD_REPORTING)) {
-      grpc_load_reporting_config *arg_lrc =
-          args->channel_args->args[i].value.pointer.p;
-      chand->lrc = grpc_load_reporting_config_copy(arg_lrc);
-      GPR_ASSERT(chand->lrc != NULL);
-      break;
-    }
-  }
-  GPR_ASSERT(chand->lrc != NULL); /* arg actually found */
-
-  gpr_mu_lock(&chand->mu);
-  invoke_lr_fn_locked(chand->lrc, NULL);
-  gpr_mu_unlock(&chand->mu);
+  chand->id = (intptr_t)args->channel_stack;
+
+  /* TODO(dgq): do something with the data
+  grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CHANNEL_CREATION,
+                                                (intptr_t)chand,
+                                                0,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                NULL};
+                                                */
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
+  /* TODO(dgq): do something with the data
   channel_data *chand = elem->channel_data;
-  gpr_mu_destroy(&chand->mu);
-  grpc_load_reporting_config_destroy(chand->lrc);
+  grpc_load_reporting_call_data lr_call_data = {
+      GRPC_LR_POINT_CHANNEL_DESTRUCTION,
+      (intptr_t)chand->id,
+      0,
+      NULL,
+      NULL,
+      NULL,
+      NULL};
+  */
 }
 
 static grpc_mdelem *lr_trailing_md_filter(void *user_data, grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
 
-  if (md->key == GRPC_MDSTR_LOAD_REPORTING) {
+  if (md->key == GRPC_MDSTR_LOAD_REPORTING_TRAILING) {
     calld->trailing_md_string = gpr_strdup(grpc_mdstr_as_c_string(md->value));
     return NULL;
   }
@@ -127,8 +202,14 @@ static void lr_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
                                          grpc_call_element *elem,
                                          grpc_transport_stream_op *op) {
   GPR_TIMER_BEGIN("lr_start_transport_stream_op", 0);
+  call_data *calld = elem->call_data;
 
-  if (op->send_trailing_metadata) {
+  if (op->recv_initial_metadata) {
+    calld->recv_initial_metadata = op->recv_initial_metadata;
+    /* substitute our callback for the higher callback */
+    calld->ops_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
+    op->recv_initial_metadata_ready = &calld->on_initial_md_ready;
+  } else if (op->send_trailing_metadata) {
     grpc_metadata_batch_filter(op->send_trailing_metadata,
                                lr_trailing_md_filter, elem);
   }
diff --git a/src/core/ext/load_reporting/load_reporting_filter.h b/src/core/ext/load_reporting/load_reporting_filter.h
index f69cd6fdc6dc6bd94d6bbeece390588806f25de7..160ed32af99c352045d7465eca2a02e9ad75f834 100644
--- a/src/core/ext/load_reporting/load_reporting_filter.h
+++ b/src/core/ext/load_reporting/load_reporting_filter.h
@@ -34,6 +34,7 @@
 #ifndef GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_FILTER_H
 #define GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_FILTER_H
 
+#include "src/core/ext/load_reporting/load_reporting.h"
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_load_reporting_filter;
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index e0d87725e9dd88bf269c040803650259ceb68450..8f7a1f55c6effcf746de3ac81316a17aa8a494c7 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -202,6 +202,7 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
 
   GPR_TIMER_BEGIN("finalize_outbuf", 0);
 
+  bool is_first_data_frame = true;
   while (
       grpc_chttp2_list_pop_writing_stream(transport_writing, &stream_writing)) {
     uint32_t max_outgoing =
@@ -266,6 +267,11 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
             stream_writing->id, &stream_writing->flow_controlled_buffer,
             send_bytes, is_last_frame, &stream_writing->stats,
             &transport_writing->outbuf);
+        if (is_first_data_frame) {
+          /* TODO(dgq): this is a hack. It'll be fix in a future refactoring */
+          stream_writing->stats.data_bytes -= 5; /* discount grpc framing */
+          is_first_data_frame = false;
+        }
         GRPC_CHTTP2_FLOW_DEBIT_STREAM("write", transport_writing,
                                       stream_writing, outgoing_window,
                                       send_bytes);
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
index 87175d7943314c8a8392ab9c0e4a240ad831f802..f9b7347b891953637a62f004cf1f92496871fb6c 100644
--- a/src/core/lib/channel/channel_stack.c
+++ b/src/core/lib/channel/channel_stack.c
@@ -217,7 +217,7 @@ void grpc_call_stack_ignore_set_pollset_or_pollset_set(
     grpc_polling_entity *pollent) {}
 
 void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
-                             const grpc_call_stats *call_stats,
+                             const grpc_call_final_info *final_info,
                              void *and_free_memory) {
   grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack);
   size_t count = stack->count;
@@ -225,7 +225,7 @@ void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
 
   /* destroy per-filter data */
   for (i = 0; i < count; i++) {
-    elems[i].filter->destroy_call_elem(exec_ctx, &elems[i], call_stats,
+    elems[i].filter->destroy_call_elem(exec_ctx, &elems[i], final_info,
                                        i == count - 1 ? and_free_memory : NULL);
   }
 }
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index d72c015b677d4760e879c5eec0f30ee1b8859104..19d18ccf93cdb99d6e1768c232e4ee5452105132 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -75,9 +75,14 @@ typedef struct {
 typedef struct {
   grpc_transport_stream_stats transport_stream_stats;
   gpr_timespec latency; /* From call creating to enqueing of received status */
-  grpc_status_code final_status;
 } grpc_call_stats;
 
+/** Information about the call upon completion. */
+typedef struct {
+  grpc_call_stats stats;
+  grpc_status_code final_status;
+} grpc_call_final_info;
+
 /* Channel filters specify:
    1. the amount of memory needed in the channel & call (via the sizeof_XXX
       members)
@@ -119,16 +124,17 @@ typedef struct {
      The filter does not need to do any chaining.
      The bottom filter of a stack will be passed a non-NULL pointer to
      \a and_free_memory that should be passed to gpr_free when destruction
-     is complete. */
+     is complete. \a final_info contains data about the completed call, mainly
+     for reporting purposes. */
   void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                            const grpc_call_stats *stats,
+                            const grpc_call_final_info *final_info,
                             void *and_free_memory);
 
   /* sizeof(per channel data) */
   size_t sizeof_channel_data;
   /* Initialize per-channel data.
-     elem is initialized at the start of the call, and elem->channel_data is
-     what needs initializing.
+     elem is initialized at the creating of the channel, and elem->channel_data
+     is what needs initializing.
      is_first, is_last designate this elements position in the stack, and are
      useful for asserting correct configuration by upper layer code.
      The filter does not need to do any chaining */
@@ -243,7 +249,7 @@ void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
 
 /* Destroy a call stack */
 void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
-                             const grpc_call_stats *call_stats,
+                             const grpc_call_final_info *final_info,
                              void *and_free_memory);
 
 /* Ignore set pollset{_set} - used by filters if they don't care about pollsets
diff --git a/src/core/lib/channel/compress_filter.c b/src/core/lib/channel/compress_filter.c
index 32ebe53ee64f77359145ce11473f224d4a34a09b..af21ed794df30e171a562c3ec14e5addfc3cb811 100644
--- a/src/core/lib/channel/compress_filter.c
+++ b/src/core/lib/channel/compress_filter.c
@@ -270,7 +270,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
   gpr_slice_buffer_destroy(&calld->slices);
diff --git a/src/core/lib/channel/connected_channel.c b/src/core/lib/channel/connected_channel.c
index 0a7d27a1dc5c14bdf51ad09b2e2c1d1bcf706d75..73714369cdc7d74d0205c50a1a903f428edd9113 100644
--- a/src/core/lib/channel/connected_channel.c
+++ b/src/core/lib/channel/connected_channel.c
@@ -104,7 +104,7 @@ static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
+                              const grpc_call_final_info *final_info,
                               void *and_free_memory) {
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c
index 8057e251f0b6adf812e2fd3ef98e8f2697feae4c..f1ed22c0ade4d06c4ea7b6cb2a0f68222ed5b7a8 100644
--- a/src/core/lib/channel/http_client_filter.c
+++ b/src/core/lib/channel/http_client_filter.c
@@ -184,7 +184,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {}
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {}
 
 static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
   unsigned i;
diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c
index d0beebd817fd22ffa86565a3f37953635cc007fd..d52cc7d0184220ffb3ddd02d5e0122c0263ea088 100644
--- a/src/core/lib/channel/http_server_filter.c
+++ b/src/core/lib/channel/http_server_filter.c
@@ -235,7 +235,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {}
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {}
 
 /* Constructor for channel_data */
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index ec21e0394488ff04026fb8fb2dce799233dd0278..974d5ae479818f341665d9b4b23a42785a1aaf65 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -284,7 +284,7 @@ static void tcp_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
 }
 
 /* returns true if done, false if pending; if returning true, *error is set */
-#define MAX_WRITE_IOVEC 1024
+#define MAX_WRITE_IOVEC 1000
 static bool tcp_flush(grpc_tcp *tcp, grpc_error **error) {
   struct msghdr msg;
   struct iovec iov[MAX_WRITE_IOVEC];
diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c
index ed7929aa27e9d52fd9401f6ba4d67ced00a4a757..382d30756afc39e8c2738383dadb1ee1185ffc63 100644
--- a/src/core/lib/security/transport/client_auth_filter.c
+++ b/src/core/lib/security/transport/client_auth_filter.c
@@ -282,7 +282,8 @@ static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   call_data *calld = elem->call_data;
   grpc_call_credentials_unref(calld->creds);
   if (calld->host != NULL) {
diff --git a/src/core/lib/security/transport/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c
index 12e789bde920e11a49dd44a185c15fa6b6d6b87b..5f3d0dcd6e1339c5577c8d9cfc95206490de3b14 100644
--- a/src/core/lib/security/transport/server_auth_filter.c
+++ b/src/core/lib/security/transport/server_auth_filter.c
@@ -226,7 +226,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {}
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {}
 
 /* Constructor for channel_data */
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index fc9df76dc186418e57e6526bf217a8290d7b6418..70c94791f8cc69d2eff3e44cc68c49ce0e7d2a51 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -154,8 +154,9 @@ struct grpc_call {
   /* Received call statuses from various sources */
   received_status status[STATUS_SOURCE_COUNT];
 
-  /* Call stats: only valid after trailing metadata received */
-  grpc_call_stats stats;
+  /* Call data useful used for reporting. Only valid after the call has
+   * completed */
+  grpc_call_final_info final_info;
 
   /* Compression algorithm for *incoming* data */
   grpc_compression_algorithm incoming_compression_algorithm;
@@ -361,6 +362,25 @@ void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c REF_ARG) {
   GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON);
 }
 
+static void get_final_status(grpc_call *call,
+                             void (*set_value)(grpc_status_code code,
+                                               void *user_data),
+                             void *set_value_user_data) {
+  int i;
+  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
+    if (call->status[i].is_set) {
+      set_value(call->status[i].code, set_value_user_data);
+      return;
+    }
+  }
+  if (call->is_client) {
+    set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
+  } else {
+    set_value(GRPC_STATUS_OK, set_value_user_data);
+  }
+}
+
+static void set_status_value_directly(grpc_status_code status, void *dest);
 static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
                          grpc_error *error) {
   size_t i;
@@ -392,7 +412,11 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
     GRPC_CQ_INTERNAL_UNREF(c->cq, "bind");
   }
   grpc_channel *channel = c->channel;
-  grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->stats, c);
+
+  get_final_status(call, set_status_value_directly,
+                   &c->final_info.final_status);
+
+  grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->final_info, c);
   GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "call");
   GPR_TIMER_END("destroy_call", 0);
 }
@@ -414,24 +438,6 @@ static void set_status_details(grpc_call *call, status_source source,
   }
 }
 
-static void get_final_status(grpc_call *call,
-                             void (*set_value)(grpc_status_code code,
-                                               void *user_data),
-                             void *set_value_user_data) {
-  int i;
-  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
-    if (call->status[i].is_set) {
-      set_value(call->status[i].code, set_value_user_data);
-      return;
-    }
-  }
-  if (call->is_client) {
-    set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
-  } else {
-    set_value(GRPC_STATUS_OK, set_value_user_data);
-  }
-}
-
 static void set_status_from_error(grpc_call *call, status_source source,
                                   grpc_error *error) {
   intptr_t status;
@@ -1608,7 +1614,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
         bctl->recv_final_op = 1;
         stream_op.recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-        stream_op.collect_stats = &call->stats.transport_stream_stats;
+        stream_op.collect_stats =
+            &call->final_info.stats.transport_stream_stats;
         break;
       case GRPC_OP_RECV_CLOSE_ON_SERVER:
         /* Flag validation: currently allow no flags */
@@ -1630,7 +1637,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
         bctl->recv_final_op = 1;
         stream_op.recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-        stream_op.collect_stats = &call->stats.transport_stream_stats;
+        stream_op.collect_stats =
+            &call->final_info.stats.transport_stream_stats;
         break;
     }
   }
diff --git a/src/core/lib/surface/lame_client.c b/src/core/lib/surface/lame_client.c
index 5ea4cba5d1a97fbaac014d8850411406842d4543..0d3168e56aec314cd20a5fd0faa81b2be14309d2 100644
--- a/src/core/lib/surface/lame_client.c
+++ b/src/core/lib/surface/lame_client.c
@@ -111,7 +111,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                            grpc_call_element_args *args) {}
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
+                              const grpc_call_final_info *final_info,
                               void *and_free_memory) {
   gpr_free(and_free_memory);
 }
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index 2f108af48a1295e592cc7f8053173683d67c459d..a482ba43d8a4e91007598633a79ceae49657e482 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -872,7 +872,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
 }
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   channel_data *chand = elem->channel_data;
   call_data *calld = elem->call_data;
 
diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c
index c5f16e530d71f5083cf94db73126794b0488edf1..c396c1e0b5aa1bd54f3f1b270cd0891a862ff220 100644
--- a/src/core/lib/transport/static_metadata.c
+++ b/src/core/lib/transport/static_metadata.c
@@ -45,10 +45,10 @@ grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 
 grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 4, 8, 6, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 4, 8, 6, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] =
     {11, 35, 10, 35, 12, 35, 12, 49, 13, 35, 14, 35, 15, 35, 16, 35, 17, 35,
@@ -56,10 +56,10 @@ const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] =
      30, 18, 30, 35, 31, 35, 32, 35, 36, 35, 37, 35, 38, 35, 39, 35, 42, 33,
      42, 34, 42, 48, 42, 53, 42, 54, 42, 55, 42, 56, 43, 33, 43, 48, 43, 53,
      46, 0,  46, 1,  46, 2,  50, 35, 57, 35, 58, 35, 59, 35, 60, 35, 61, 35,
-     62, 35, 63, 35, 64, 35, 65, 35, 66, 35, 67, 40, 67, 69, 67, 72, 68, 80,
-     68, 81, 70, 35, 71, 35, 73, 35, 74, 35, 75, 35, 76, 35, 77, 41, 77, 51,
-     77, 52, 78, 35, 79, 35, 82, 3,  82, 4,  82, 5,  82, 6,  82, 7,  82, 8,
-     82, 9,  83, 35, 84, 85, 86, 35, 87, 35, 88, 35, 89, 35, 90, 35};
+     62, 35, 63, 35, 64, 35, 65, 35, 66, 35, 67, 35, 68, 40, 68, 70, 68, 73,
+     69, 81, 69, 82, 71, 35, 72, 35, 74, 35, 75, 35, 76, 35, 77, 35, 78, 41,
+     78, 51, 78, 52, 79, 35, 80, 35, 83, 3,  83, 4,  83, 5,  83, 6,  83, 7,
+     83, 8,  83, 9,  84, 35, 85, 86, 87, 35, 88, 35, 89, 35, 90, 35, 91, 35};
 
 const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "0",
@@ -126,7 +126,8 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "if-unmodified-since",
     "last-modified",
     "link",
-    "load-reporting",
+    "load-reporting-initial",
+    "load-reporting-trailing",
     "location",
     "max-forwards",
     ":method",
diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h
index 5ff0d2f3bc3b713260f51d52421617354aa6470e..491c8cf125a999de3d372397b640dbdcadfada9d 100644
--- a/src/core/lib/transport/static_metadata.h
+++ b/src/core/lib/transport/static_metadata.h
@@ -44,7 +44,7 @@
 
 #include "src/core/lib/transport/metadata.h"
 
-#define GRPC_STATIC_MDSTR_COUNT 91
+#define GRPC_STATIC_MDSTR_COUNT 92
 extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 /* "0" */
 #define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0])
@@ -175,62 +175,64 @@ extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 #define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[62])
 /* "link" */
 #define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[63])
-/* "load-reporting" */
-#define GRPC_MDSTR_LOAD_REPORTING (&grpc_static_mdstr_table[64])
+/* "load-reporting-initial" */
+#define GRPC_MDSTR_LOAD_REPORTING_INITIAL (&grpc_static_mdstr_table[64])
+/* "load-reporting-trailing" */
+#define GRPC_MDSTR_LOAD_REPORTING_TRAILING (&grpc_static_mdstr_table[65])
 /* "location" */
-#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[65])
+#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[66])
 /* "max-forwards" */
-#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[66])
+#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[67])
 /* ":method" */
-#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[67])
+#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[68])
 /* ":path" */
-#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[68])
+#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[69])
 /* "POST" */
-#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[69])
+#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[70])
 /* "proxy-authenticate" */
-#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[70])
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[71])
 /* "proxy-authorization" */
-#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[71])
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[72])
 /* "PUT" */
-#define GRPC_MDSTR_PUT (&grpc_static_mdstr_table[72])
+#define GRPC_MDSTR_PUT (&grpc_static_mdstr_table[73])
 /* "range" */
-#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[73])
+#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[74])
 /* "referer" */
-#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[74])
+#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[75])
 /* "refresh" */
-#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[75])
+#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[76])
 /* "retry-after" */
-#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[76])
+#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[77])
 /* ":scheme" */
-#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[77])
+#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[78])
 /* "server" */
-#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[78])
+#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[79])
 /* "set-cookie" */
-#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[79])
+#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[80])
 /* "/" */
-#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[80])
+#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[81])
 /* "/index.html" */
-#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[81])
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[82])
 /* ":status" */
-#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[82])
+#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[83])
 /* "strict-transport-security" */
-#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[83])
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[84])
 /* "te" */
-#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[84])
+#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[85])
 /* "trailers" */
-#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[85])
+#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[86])
 /* "transfer-encoding" */
-#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[86])
+#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[87])
 /* "user-agent" */
-#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[87])
+#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[88])
 /* "vary" */
-#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[88])
+#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[89])
 /* "via" */
-#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[89])
+#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[90])
 /* "www-authenticate" */
-#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[90])
+#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[91])
 
-#define GRPC_STATIC_MDELEM_COUNT 80
+#define GRPC_STATIC_MDELEM_COUNT 81
 extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
 /* "accept-charset": "" */
@@ -335,73 +337,76 @@ extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
 #define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45])
 /* "link": "" */
 #define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[46])
-/* "load-reporting": "" */
-#define GRPC_MDELEM_LOAD_REPORTING_EMPTY (&grpc_static_mdelem_table[47])
+/* "load-reporting-initial": "" */
+#define GRPC_MDELEM_LOAD_REPORTING_INITIAL_EMPTY (&grpc_static_mdelem_table[47])
+/* "load-reporting-trailing": "" */
+#define GRPC_MDELEM_LOAD_REPORTING_TRAILING_EMPTY \
+  (&grpc_static_mdelem_table[48])
 /* "location": "" */
-#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[48])
+#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[49])
 /* "max-forwards": "" */
-#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[49])
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[50])
 /* ":method": "GET" */
-#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[50])
+#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[51])
 /* ":method": "POST" */
-#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[51])
+#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[52])
 /* ":method": "PUT" */
-#define GRPC_MDELEM_METHOD_PUT (&grpc_static_mdelem_table[52])
+#define GRPC_MDELEM_METHOD_PUT (&grpc_static_mdelem_table[53])
 /* ":path": "/" */
-#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[53])
+#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[54])
 /* ":path": "/index.html" */
-#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[54])
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[55])
 /* "proxy-authenticate": "" */
-#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[55])
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[56])
 /* "proxy-authorization": "" */
-#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[56])
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[57])
 /* "range": "" */
-#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[57])
+#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[58])
 /* "referer": "" */
-#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[58])
+#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[59])
 /* "refresh": "" */
-#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[59])
+#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[60])
 /* "retry-after": "" */
-#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[60])
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[61])
 /* ":scheme": "grpc" */
-#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[61])
+#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[62])
 /* ":scheme": "http" */
-#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[62])
+#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[63])
 /* ":scheme": "https" */
-#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[63])
+#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[64])
 /* "server": "" */
-#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[64])
+#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[65])
 /* "set-cookie": "" */
-#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[65])
+#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[66])
 /* ":status": "200" */
-#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[66])
+#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[67])
 /* ":status": "204" */
-#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[67])
+#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[68])
 /* ":status": "206" */
-#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[68])
+#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[69])
 /* ":status": "304" */
-#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[69])
+#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[70])
 /* ":status": "400" */
-#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[70])
+#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[71])
 /* ":status": "404" */
-#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[71])
+#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[72])
 /* ":status": "500" */
-#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[72])
+#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[73])
 /* "strict-transport-security": "" */
 #define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
-  (&grpc_static_mdelem_table[73])
+  (&grpc_static_mdelem_table[74])
 /* "te": "trailers" */
-#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[74])
+#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[75])
 /* "transfer-encoding": "" */
-#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[75])
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[76])
 /* "user-agent": "" */
-#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[76])
+#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[77])
 /* "vary": "" */
-#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[77])
+#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[78])
 /* "via": "" */
-#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[78])
+#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[79])
 /* "www-authenticate": "" */
-#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[79])
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[80])
 
 extern const uint8_t
     grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc
index 43117fd1e95cc5a259f40454b7c29c6f64dab4e6..1ca6a2b906e17db091e9f89f1df37bc831cd8ef3 100644
--- a/src/cpp/server/server_context.cc
+++ b/src/cpp/server/server_context.cc
@@ -129,7 +129,8 @@ ServerContext::ServerContext()
       deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)),
       call_(nullptr),
       cq_(nullptr),
-      sent_initial_metadata_(false) {}
+      sent_initial_metadata_(false),
+      compression_level_set_(false) {}
 
 ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata* metadata,
                              size_t metadata_count)
@@ -139,7 +140,8 @@ ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata* metadata,
       deadline_(deadline),
       call_(nullptr),
       cq_(nullptr),
-      sent_initial_metadata_(false) {
+      sent_initial_metadata_(false),
+      compression_level_set_(false) {
   for (size_t i = 0; i < metadata_count; i++) {
     client_metadata_.insert(std::pair<grpc::string_ref, grpc::string_ref>(
         metadata[i].key,
@@ -194,15 +196,6 @@ bool ServerContext::IsCancelled() const {
   }
 }
 
-void ServerContext::set_compression_level(grpc_compression_level level) {
-  // TODO(dgq): get rid of grpc_call_compression_for_level and propagate the
-  // compression level by adding a new argument to
-  // CallOpSendInitialMetadata::SendInitialMetadata.
-  const grpc_compression_algorithm algorithm_for_level =
-      grpc_call_compression_for_level(call_, level);
-  set_compression_algorithm(algorithm_for_level);
-}
-
 void ServerContext::set_compression_algorithm(
     grpc_compression_algorithm algorithm) {
   char* algorithm_name = NULL;
diff --git a/src/csharp/.nuget/packages.config b/src/csharp/.nuget/packages.config
deleted file mode 100644
index 6154b3561f49cd2c44fec22d8f60c9136aa7140e..0000000000000000000000000000000000000000
--- a/src/csharp/.nuget/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="NUnit.ConsoleRunner" version="3.2.0" />
-  <package id="OpenCover" version="4.6.519" />
-  <package id="ReportGenerator" version="2.4.4.0" />
-</packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core.Tests/SanityTest.cs b/src/csharp/Grpc.Core.Tests/SanityTest.cs
index 501992c56959372f22cd0a05f5cf9e4af99706a3..9d069fa432a6284ee36eb7b114ad2c03fe547b3c 100644
--- a/src/csharp/Grpc.Core.Tests/SanityTest.cs
+++ b/src/csharp/Grpc.Core.Tests/SanityTest.cs
@@ -58,10 +58,11 @@ namespace Grpc.Core.Tests
         [Test]
         public void TestsJsonUpToDate()
         {
-            var discoveredTests = DiscoverAllTestClasses();
-            string discoveredTestsJson = JsonConvert.SerializeObject(discoveredTests, Formatting.Indented);
+            Dictionary<string, List<string>> discoveredTests = DiscoverAllTestClasses();
+			Dictionary<string, List<string>> testsFromFile 
+			    = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(ReadTestsJson());
 
-            Assert.AreEqual(discoveredTestsJson, ReadTestsJson());
+            Assert.AreEqual(discoveredTests, testsFromFile);
         }
 
         /// <summary>
diff --git a/src/csharp/Grpc.Core.Tests/packages.config b/src/csharp/Grpc.Core.Tests/packages.config
index aa7d951fdc7b46c2380456163d8b3f3856d0da57..6a930c17ee60fb9a12c4a9541f5e94bde994e70c 100644
--- a/src/csharp/Grpc.Core.Tests/packages.config
+++ b/src/csharp/Grpc.Core.Tests/packages.config
@@ -4,4 +4,7 @@
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
   <package id="NUnit" version="3.2.0" targetFramework="net45" />
   <package id="NUnitLite" version="3.2.0" targetFramework="net45" />
-</packages>
\ No newline at end of file
+  <package id="NUnit.ConsoleRunner" version="3.2.0" />
+  <package id="OpenCover" version="4.6.519" />
+  <package id="ReportGenerator" version="2.4.4.0" />
+</packages>
diff --git a/src/csharp/Grpc.Core.Tests/project.json b/src/csharp/Grpc.Core.Tests/project.json
index d4c9a2ef3168740f34b3306aaae984baf79e885d..4a682d927e46340cc21633ebf09dcbadb05f71e6 100644
--- a/src/csharp/Grpc.Core.Tests/project.json
+++ b/src/csharp/Grpc.Core.Tests/project.json
@@ -54,7 +54,10 @@
     },
     "Newtonsoft.Json": "8.0.3",
     "NUnit": "3.2.0",
-    "NUnitLite": "3.2.0-*"
+    "NUnitLite": "3.2.0-*",
+    "NUnit.ConsoleRunner": "3.2.0",
+    "OpenCover": "4.6.519",
+    "ReportGenerator": "2.4.4.0"
   },
   "frameworks": {
     "net45": { },
diff --git a/src/csharp/Grpc.Examples.MathClient/packages.config b/src/csharp/Grpc.Examples.MathClient/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..79ece06bef6a23c7f75492ed821f546d8bd403e2
--- /dev/null
+++ b/src/csharp/Grpc.Examples.MathClient/packages.config
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+</packages>
diff --git a/src/csharp/Grpc.Examples.MathServer/packages.config b/src/csharp/Grpc.Examples.MathServer/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..79ece06bef6a23c7f75492ed821f546d8bd403e2
--- /dev/null
+++ b/src/csharp/Grpc.Examples.MathServer/packages.config
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+</packages>
diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/packages.config b/src/csharp/Grpc.IntegrationTesting.QpsWorker/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..79ece06bef6a23c7f75492ed821f546d8bd403e2
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting.QpsWorker/packages.config
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+</packages>
diff --git a/src/csharp/Grpc.IntegrationTesting.StressClient/packages.config b/src/csharp/Grpc.IntegrationTesting.StressClient/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..79ece06bef6a23c7f75492ed821f546d8bd403e2
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting.StressClient/packages.config
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+</packages>
diff --git a/src/node/ext/call_credentials.cc b/src/node/ext/call_credentials.cc
index 3c8f0c56da4df23a29d60cf57fa5c55e1e55cfc1..81fc552fd10036cf2d810b7cac56f7c17b769597 100644
--- a/src/node/ext/call_credentials.cc
+++ b/src/node/ext/call_credentials.cc
@@ -68,6 +68,8 @@ using v8::Value;
 Nan::Callback *CallCredentials::constructor;
 Persistent<FunctionTemplate> CallCredentials::fun_tpl;
 
+static Callback *plugin_callback;
+
 CallCredentials::CallCredentials(grpc_call_credentials *credentials)
     : wrapped_credentials(credentials) {}
 
@@ -88,6 +90,11 @@ void CallCredentials::Init(Local<Object> exports) {
                Nan::New<FunctionTemplate>(CreateFromPlugin)).ToLocalChecked());
   Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr);
   constructor = new Nan::Callback(ctr);
+
+  Local<FunctionTemplate> callback_tpl =
+      Nan::New<FunctionTemplate>(PluginCallback);
+  plugin_callback = new Callback(
+      Nan::GetFunction(callback_tpl).ToLocalChecked());
 }
 
 bool CallCredentials::HasInstance(Local<Value> val) {
@@ -195,23 +202,28 @@ NAN_METHOD(PluginCallback) {
     return Nan::ThrowTypeError(
         "The callback's third argument must be an object");
   }
+  if (!info[3]->IsObject()) {
+    return Nan::ThrowTypeError(
+        "The callback's fourth argument must be an object");
+  }
   shared_ptr<Resources> resources(new Resources);
   grpc_status_code code = static_cast<grpc_status_code>(
       Nan::To<uint32_t>(info[0]).FromJust());
   Utf8String details_utf8_str(info[1]);
   char *details = *details_utf8_str;
   grpc_metadata_array array;
+  Local<Object> callback_data = Nan::To<Object>(info[3]).ToLocalChecked();
   if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(),
                            &array, resources)){
     return Nan::ThrowError("Failed to parse metadata");
   }
   grpc_credentials_plugin_metadata_cb cb =
       reinterpret_cast<grpc_credentials_plugin_metadata_cb>(
-          Nan::Get(info.Callee(),
+          Nan::Get(callback_data,
                    Nan::New("cb").ToLocalChecked()
                    ).ToLocalChecked().As<External>()->Value());
   void *user_data =
-      Nan::Get(info.Callee(),
+      Nan::Get(callback_data,
                Nan::New("user_data").ToLocalChecked()
                ).ToLocalChecked().As<External>()->Value();
   cb(user_data, array.metadata, array.count, code, details);
@@ -227,17 +239,17 @@ NAUV_WORK_CB(SendPluginCallback) {
   while (!callbacks.empty()) {
     plugin_callback_data *data = callbacks.front();
     callbacks.pop_front();
-    // Attach cb and user_data to plugin_callback so that it can access them later
-    v8::Local<v8::Function> plugin_callback = Nan::GetFunction(
-        Nan::New<v8::FunctionTemplate>(PluginCallback)).ToLocalChecked();
-    Nan::Set(plugin_callback, Nan::New("cb").ToLocalChecked(),
+    Local<Object> callback_data = Nan::New<Object>();
+    Nan::Set(callback_data, Nan::New("cb").ToLocalChecked(),
              Nan::New<v8::External>(reinterpret_cast<void*>(data->cb)));
-    Nan::Set(plugin_callback, Nan::New("user_data").ToLocalChecked(),
+    Nan::Set(callback_data, Nan::New("user_data").ToLocalChecked(),
              Nan::New<v8::External>(data->user_data));
-    const int argc = 2;
+    const int argc = 3;
     v8::Local<v8::Value> argv[argc] = {
       Nan::New(data->service_url).ToLocalChecked(),
-      plugin_callback
+      callback_data,
+      // Get Local<Function> from Nan::Callback*
+      **plugin_callback
     };
     Nan::Callback *callback = state->callback;
     callback->Call(argc, argv);
diff --git a/src/node/health_check/package.json b/src/node/health_check/package.json
index 67f5301df7ec8eb62a37dfd16bf57ff57c3c0225..40e276055b71a64271aaa1752907cec9de7454ff 100644
--- a/src/node/health_check/package.json
+++ b/src/node/health_check/package.json
@@ -19,11 +19,11 @@
     "lodash": "^3.9.3",
     "google-protobuf": "^3.0.0-alpha.5"
   },
-  "files": {
+  "files": [
     "LICENSE",
     "health.js",
     "v1"
-  },
+  ],
   "main": "src/node/index.js",
   "license": "BSD-3-Clause"
 }
diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js
index b746d0625dfaa1d56035c574f167d5629ad20b29..043df06a669a1a66ef518af4ea55cda576dabe5c 100644
--- a/src/node/src/credentials.js
+++ b/src/node/src/credentials.js
@@ -92,7 +92,8 @@ exports.createSsl = ChannelCredentials.createSsl;
  * @return {CallCredentials} The credentials object
  */
 exports.createFromMetadataGenerator = function(metadata_generator) {
-  return CallCredentials.createFromPlugin(function(service_url, callback) {
+  return CallCredentials.createFromPlugin(function(service_url, cb_data,
+                                                   callback) {
     metadata_generator({service_url: service_url}, function(error, metadata) {
       var code = grpc.status.OK;
       var message = '';
@@ -107,7 +108,7 @@ exports.createFromMetadataGenerator = function(metadata_generator) {
           metadata = new Metadata();
         }
       }
-      callback(code, message, metadata._getCoreRepresentation());
+      callback(code, message, metadata._getCoreRepresentation(), cb_data);
     });
   });
 };
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index fc832dacc23f0140bbed17c86354ba128f9e20ca..66ca1513ed736ccd1fa65f831bd56110b40fb04a 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -58,65 +58,27 @@
 #include "byte_buffer.h"
 
 zend_class_entry *grpc_ce_call;
-
-#if PHP_MAJOR_VERSION < 7
+#if PHP_MAJOR_VERSION >= 7
+static zend_object_handlers call_ce_handlers;
+#endif
 
 /* Frees and destroys an instance of wrapped_grpc_call */
-void free_wrapped_grpc_call(void *object TSRMLS_DC) {
-  wrapped_grpc_call *call = (wrapped_grpc_call *)object;
-  if (call->owned && call->wrapped != NULL) {
-    grpc_call_destroy(call->wrapped);
+PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_call)
+  if (p->owned && p->wrapped != NULL) {
+    grpc_call_destroy(p->wrapped);
   }
-  zend_object_std_dtor(&call->std TSRMLS_CC);
-  efree(call);
-}
+PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
 /* Initializes an instance of wrapped_grpc_call to be associated with an object
  * of a class specified by class_type */
-zend_object_value create_wrapped_grpc_call(zend_class_entry *class_type
-                                               TSRMLS_DC) {
-  zend_object_value retval;
-  wrapped_grpc_call *intern;
-
-  intern = (wrapped_grpc_call *)emalloc(sizeof(wrapped_grpc_call));
-  memset(intern, 0, sizeof(wrapped_grpc_call));
-
+php_grpc_zend_object create_wrapped_grpc_call(zend_class_entry *class_type
+                                              TSRMLS_DC) {
+  PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_call);
   zend_object_std_init(&intern->std, class_type TSRMLS_CC);
   object_properties_init(&intern->std, class_type);
-  retval.handle = zend_objects_store_put(
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      free_wrapped_grpc_call, NULL TSRMLS_CC);
-  retval.handlers = zend_get_std_object_handlers();
-  return retval;
+  PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_call, call_ce_handlers);
 }
 
-#else
-
-static zend_object_handlers call_ce_handlers;
-
-/* Frees and destroys an instance of wrapped_grpc_call */
-static void free_wrapped_grpc_call(zend_object *object) {
-  wrapped_grpc_call *call = wrapped_grpc_call_from_obj(object);
-  if (call->owned && call->wrapped != NULL) {
-    grpc_call_destroy(call->wrapped);
-  }
-  zend_object_std_dtor(&call->std);
-}
-
-/* Initializes an instance of wrapped_grpc_call to be associated with an
- * object of a class specified by class_type */
-zend_object *create_wrapped_grpc_call(zend_class_entry *class_type) {
-  wrapped_grpc_call *intern;
-  intern = ecalloc(1, sizeof(wrapped_grpc_call) +
-                   zend_object_properties_size(class_type));
-  zend_object_std_init(&intern->std, class_type);
-  object_properties_init(&intern->std, class_type);
-  intern->std.handlers = &call_ce_handlers;
-  return &intern->std;
-}
-
-#endif
-
 /* Creates and returns a PHP array object with the data in a
  * grpc_metadata_array. Returns NULL on failure */
 zval *grpc_parse_metadata_array(grpc_metadata_array
@@ -132,11 +94,7 @@ zval *grpc_parse_metadata_array(grpc_metadata_array
   char *str_key;
   char *str_val;
   size_t key_len;
-#if PHP_MAJOR_VERSION < 7
-  zval **data = NULL;
-#else
- zval *data;
-#endif
+  zval *data = NULL;
 
   array_hash = Z_ARRVAL_P(array);
   grpc_metadata *elem;
@@ -147,14 +105,9 @@ zval *grpc_parse_metadata_array(grpc_metadata_array
     memcpy(str_key, elem->key, key_len);
     str_val = ecalloc(elem->value_length + 1, sizeof(char));
     memcpy(str_val, elem->value, elem->value_length);
-#if PHP_MAJOR_VERSION < 7
-    if (zend_hash_find(array_hash, str_key, key_len, (void **)data) ==
-        SUCCESS) {
-      if (Z_TYPE_P(*data) != IS_ARRAY) {
-#else
-    if ((data = zend_hash_str_find(array_hash, str_key, key_len)) != NULL) {
+    if (php_grpc_zend_hash_find(array_hash, str_key, key_len, (void **)&data)
+        == SUCCESS) {
       if (Z_TYPE_P(data) != IS_ARRAY) {
-#endif
         zend_throw_exception(zend_exception_get_default(TSRMLS_C),
                              "Metadata hash somehow contains wrong types.",
                              1 TSRMLS_CC);
@@ -162,13 +115,8 @@ zval *grpc_parse_metadata_array(grpc_metadata_array
         efree(str_val);
         return NULL;
       }
-#if PHP_MAJOR_VERSION < 7
-      php_grpc_add_next_index_stringl(*data, str_val, elem->value_length,
-                                      false);
-#else
       php_grpc_add_next_index_stringl(data, str_val, elem->value_length,
                                       false);
-#endif
     } else {
       PHP_GRPC_MAKE_STD_ZVAL(inner_array);
       array_init(inner_array);
@@ -185,96 +133,48 @@ zval *grpc_parse_metadata_array(grpc_metadata_array
 bool create_metadata_array(zval *array, grpc_metadata_array *metadata) {
   HashTable *array_hash;
   HashTable *inner_array_hash;
-#if PHP_MAJOR_VERSION < 7
-  zval **inner_array;
-  zval **value;
-  HashPosition array_pointer;
-  HashPosition inner_array_pointer;
-  char *key;
-  uint key_len;
-  ulong index;
-#else
-  zval *inner_array;
   zval *value;
-  zend_string *key;
-#endif
+  zval *inner_array;
   if (Z_TYPE_P(array) != IS_ARRAY) {
     return false;
   }
   grpc_metadata_array_init(metadata);
   array_hash = Z_ARRVAL_P(array);
-#if PHP_MAJOR_VERSION < 7
-  for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
-       zend_hash_get_current_data_ex(array_hash, (void**)&inner_array,
-                                     &array_pointer) == SUCCESS;
-       zend_hash_move_forward_ex(array_hash, &array_pointer)) {
-    if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0,
-                                     &array_pointer) != HASH_KEY_IS_STRING) {
-      return false;
-    }
-    if (Z_TYPE_P(*inner_array) != IS_ARRAY) {
-      return false;
-    }
-    inner_array_hash = Z_ARRVAL_P(*inner_array);
-    metadata->capacity += zend_hash_num_elements(inner_array_hash);
-  }
-#else
-  ZEND_HASH_FOREACH_STR_KEY_VAL(array_hash, key, inner_array) {
-    if (key == NULL) {
+
+  char *key;
+  int key_type;
+  PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(array_hash, key, key_type,
+                                          inner_array)
+    if (key_type != HASH_KEY_IS_STRING || key == NULL) {
       return false;
     }
     if (Z_TYPE_P(inner_array) != IS_ARRAY) {
       return false;
     }
-    inner_array_hash = HASH_OF(inner_array);
+    inner_array_hash = Z_ARRVAL_P(inner_array);
     metadata->capacity += zend_hash_num_elements(inner_array_hash);
-  } ZEND_HASH_FOREACH_END();
-#endif
+  PHP_GRPC_HASH_FOREACH_END()
 
   metadata->metadata = gpr_malloc(metadata->capacity * sizeof(grpc_metadata));
 
-#if PHP_MAJOR_VERSION < 7
-  for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
-       zend_hash_get_current_data_ex(array_hash, (void**)&inner_array,
-                                     &array_pointer) == SUCCESS;
-       zend_hash_move_forward_ex(array_hash, &array_pointer)) {
-    if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0,
-                                     &array_pointer) != HASH_KEY_IS_STRING) {
-      return false;
-    }
-    inner_array_hash = Z_ARRVAL_P(*inner_array);
-    for (zend_hash_internal_pointer_reset_ex(inner_array_hash,
-                                             &inner_array_pointer);
-         zend_hash_get_current_data_ex(inner_array_hash, (void**)&value,
-                                       &inner_array_pointer) == SUCCESS;
-         zend_hash_move_forward_ex(inner_array_hash, &inner_array_pointer)) {
-      if (Z_TYPE_P(*value) != IS_STRING) {
-        return false;
-      }
-      metadata->metadata[metadata->count].key = key;
-      metadata->metadata[metadata->count].value = Z_STRVAL_P(*value);
-      metadata->metadata[metadata->count].value_length = Z_STRLEN_P(*value);
-      metadata->count += 1;
-    }
-  }
-#else
-  ZEND_HASH_FOREACH_STR_KEY_VAL(array_hash, key, inner_array) {
-    if (key == NULL) {
+  char *key1 = NULL;
+  int key_type1;
+  PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(array_hash, key1, key_type1,
+                                          inner_array)
+    if (key_type1 != HASH_KEY_IS_STRING) {
       return false;
     }
-    inner_array_hash = HASH_OF(inner_array);
-
-    ZEND_HASH_FOREACH_VAL(inner_array_hash, value) {
+    inner_array_hash = Z_ARRVAL_P(inner_array);
+    PHP_GRPC_HASH_FOREACH_VAL_START(inner_array_hash, value)
       if (Z_TYPE_P(value) != IS_STRING) {
         return false;
       }
-      metadata->metadata[metadata->count].key = ZSTR_VAL(key);
+      metadata->metadata[metadata->count].key = key1;
       metadata->metadata[metadata->count].value = Z_STRVAL_P(value);
       metadata->metadata[metadata->count].value_length = Z_STRLEN_P(value);
       metadata->count += 1;
-    } ZEND_HASH_FOREACH_END();
-  } ZEND_HASH_FOREACH_END();
-#endif
+    PHP_GRPC_HASH_FOREACH_END()
+  PHP_GRPC_HASH_FOREACH_END()
   return true;
 }
 
@@ -342,23 +242,13 @@ PHP_METHOD(Call, startBatch) {
   PHP_GRPC_MAKE_STD_ZVAL(result);
   object_init(result);
   php_grpc_ulong index;
-#if PHP_MAJOR_VERSION < 7
-  zval **value;
-  zval **inner_value;
-  HashPosition array_pointer;
-  zval **message_value;
-  zval **message_flags;
-  char *key;
-  uint key_len;
   zval *recv_status;
-#else
+  PHP_GRPC_MAKE_STD_ZVAL(recv_status);
+  object_init(recv_status);
   zval *value;
   zval *inner_value;
   zval *message_value;
   zval *message_flags;
-  zend_string *key;
-  zval recv_status;
-#endif
   wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
   
   grpc_op ops[8];
@@ -381,7 +271,6 @@ PHP_METHOD(Call, startBatch) {
   char *message_str;
   size_t message_len;
 
-
   grpc_metadata_array_init(&metadata);
   grpc_metadata_array_init(&trailing_metadata);
   grpc_metadata_array_init(&recv_metadata);
@@ -396,67 +285,63 @@ PHP_METHOD(Call, startBatch) {
     goto cleanup;
   }
 
-#if PHP_MAJOR_VERSION < 7
-
   array_hash = Z_ARRVAL_P(array);
-  for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
-       zend_hash_get_current_data_ex(array_hash, (void**)&value,
-                                     &array_pointer) == SUCCESS;
-       zend_hash_move_forward_ex(array_hash, &array_pointer)) {
-    if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0,
-                                     &array_pointer) != HASH_KEY_IS_LONG) {
+
+  char *key = NULL;
+  int key_type;
+  PHP_GRPC_HASH_FOREACH_LONG_KEY_VAL_START(array_hash, key, key_type, index,
+                                           value)
+    if (key_type != HASH_KEY_IS_LONG || key != NULL) {
       zend_throw_exception(spl_ce_InvalidArgumentException,
                            "batch keys must be integers", 1 TSRMLS_CC);
       goto cleanup;
     }
     switch(index) {
     case GRPC_OP_SEND_INITIAL_METADATA:
-      if (!create_metadata_array(*value, &metadata)) {
+      if (!create_metadata_array(value, &metadata)) {
         zend_throw_exception(spl_ce_InvalidArgumentException,
                              "Bad metadata value given", 1 TSRMLS_CC);
         goto cleanup;
       }
-      ops[op_num].data.send_initial_metadata.count =
-          metadata.count;
-      ops[op_num].data.send_initial_metadata.metadata =
-          metadata.metadata;
+      ops[op_num].data.send_initial_metadata.count = metadata.count;
+      ops[op_num].data.send_initial_metadata.metadata = metadata.metadata;
       break;
     case GRPC_OP_SEND_MESSAGE:
-      if (Z_TYPE_PP(value) != IS_ARRAY) {
+      if (Z_TYPE_P(value) != IS_ARRAY) {
         zend_throw_exception(spl_ce_InvalidArgumentException,
                              "Expected an array for send message",
                              1 TSRMLS_CC);
         goto cleanup;
       }
-      message_hash = Z_ARRVAL_PP(value);
-      if (zend_hash_find(message_hash, "flags", sizeof("flags"),
+      message_hash = Z_ARRVAL_P(value);
+      if (php_grpc_zend_hash_find(message_hash, "flags", sizeof("flags"),
                          (void **)&message_flags) == SUCCESS) {
-        if (Z_TYPE_PP(message_flags) != IS_LONG) {
+        if (Z_TYPE_P(message_flags) != IS_LONG) {
           zend_throw_exception(spl_ce_InvalidArgumentException,
                                "Expected an int for message flags",
                                1 TSRMLS_CC);
         }
-        ops[op_num].flags = Z_LVAL_PP(message_flags) & GRPC_WRITE_USED_MASK;
+        ops[op_num].flags = Z_LVAL_P(message_flags) & GRPC_WRITE_USED_MASK;
       }
-      if (zend_hash_find(message_hash, "message", sizeof("message"),
+      if (php_grpc_zend_hash_find(message_hash, "message", sizeof("message"),
                          (void **)&message_value) != SUCCESS ||
-          Z_TYPE_PP(message_value) != IS_STRING) {
+          Z_TYPE_P(message_value) != IS_STRING) {
         zend_throw_exception(spl_ce_InvalidArgumentException,
                              "Expected a string for send message",
                              1 TSRMLS_CC);
         goto cleanup;
       }
       ops[op_num].data.send_message =
-          string_to_byte_buffer(Z_STRVAL_PP(message_value),
-                                Z_STRLEN_PP(message_value));
+          string_to_byte_buffer(Z_STRVAL_P(message_value),
+                                Z_STRLEN_P(message_value));
       break;
     case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
       break;
     case GRPC_OP_SEND_STATUS_FROM_SERVER:
-      status_hash = Z_ARRVAL_PP(value);
-      if (zend_hash_find(status_hash, "metadata", sizeof("metadata"),
+      status_hash = Z_ARRVAL_P(value);
+      if (php_grpc_zend_hash_find(status_hash, "metadata", sizeof("metadata"),
                          (void **)&inner_value) == SUCCESS) {
-        if (!create_metadata_array(*inner_value, &trailing_metadata)) {
+        if (!create_metadata_array(inner_value, &trailing_metadata)) {
           zend_throw_exception(spl_ce_InvalidArgumentException,
                                "Bad trailing metadata value given",
                                1 TSRMLS_CC);
@@ -467,32 +352,32 @@ PHP_METHOD(Call, startBatch) {
         ops[op_num].data.send_status_from_server.trailing_metadata_count =
             trailing_metadata.count;
       }
-      if (zend_hash_find(status_hash, "code", sizeof("code"),
+      if (php_grpc_zend_hash_find(status_hash, "code", sizeof("code"),
                          (void**)&inner_value) == SUCCESS) {
-        if (Z_TYPE_PP(inner_value) != IS_LONG) {
+        if (Z_TYPE_P(inner_value) != IS_LONG) {
           zend_throw_exception(spl_ce_InvalidArgumentException,
                                "Status code must be an integer",
                                1 TSRMLS_CC);
           goto cleanup;
         }
         ops[op_num].data.send_status_from_server.status =
-            Z_LVAL_PP(inner_value);
+            Z_LVAL_P(inner_value);
       } else {
         zend_throw_exception(spl_ce_InvalidArgumentException,
                              "Integer status code is required",
                              1 TSRMLS_CC);
         goto cleanup;
       }
-      if (zend_hash_find(status_hash, "details", sizeof("details"),
+      if (php_grpc_zend_hash_find(status_hash, "details", sizeof("details"),
                          (void**)&inner_value) == SUCCESS) {
-        if (Z_TYPE_PP(inner_value) != IS_STRING) {
+        if (Z_TYPE_P(inner_value) != IS_STRING) {
           zend_throw_exception(spl_ce_InvalidArgumentException,
                                "Status details must be a string",
                                1 TSRMLS_CC);
           goto cleanup;
         }
         ops[op_num].data.send_status_from_server.status_details =
-            Z_STRVAL_PP(inner_value);
+            Z_STRVAL_P(inner_value);
       } else {
         zend_throw_exception(spl_ce_InvalidArgumentException,
                              "String status details is required",
@@ -527,131 +412,7 @@ PHP_METHOD(Call, startBatch) {
     ops[op_num].flags = 0;
     ops[op_num].reserved = NULL;
     op_num++;
-  }
-
-#else
-
-  array_hash = HASH_OF(array);
-  ZEND_HASH_FOREACH_KEY_VAL(array_hash, index, key, value) {
-    if (key) {
-      zend_throw_exception(spl_ce_InvalidArgumentException,
-                           "batch keys must be integers", 1);
-      goto cleanup;
-    }
-
-    switch(index) {
-    case GRPC_OP_SEND_INITIAL_METADATA:
-      if (!create_metadata_array(value, &metadata)) {
-        zend_throw_exception(spl_ce_InvalidArgumentException,
-                             "Bad metadata value given", 1);
-        goto cleanup;
-      }
-      ops[op_num].data.send_initial_metadata.count = metadata.count;
-      ops[op_num].data.send_initial_metadata.metadata = metadata.metadata;
-      break;
-    case GRPC_OP_SEND_MESSAGE:
-      if (Z_TYPE_P(value) != IS_ARRAY) {
-        zend_throw_exception(spl_ce_InvalidArgumentException,
-                             "Expected an array for send message", 1);
-        goto cleanup;
-      }
-      message_hash = HASH_OF(value);
-      if ((message_flags =
-           zend_hash_str_find(message_hash, "flags",
-                              sizeof("flags") - 1)) != NULL) {
-        if (Z_TYPE_P(message_flags) != IS_LONG) {
-          zend_throw_exception(spl_ce_InvalidArgumentException,
-                               "Expected an int for message flags", 1);
-        }
-        ops[op_num].flags = Z_LVAL_P(message_flags) & GRPC_WRITE_USED_MASK;
-      }
-      if ((message_value = zend_hash_str_find(message_hash, "message",
-                                              sizeof("message") - 1))
-          == NULL || Z_TYPE_P(message_value) != IS_STRING) {
-        zend_throw_exception(spl_ce_InvalidArgumentException,
-                             "Expected a string for send message", 1);
-        goto cleanup;
-      }
-      ops[op_num].data.send_message =
-        string_to_byte_buffer(Z_STRVAL_P(message_value),
-                              Z_STRLEN_P(message_value));
-      break;
-    case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
-      break;
-    case GRPC_OP_SEND_STATUS_FROM_SERVER:
-      status_hash = HASH_OF(value);
-      if ((inner_value = zend_hash_str_find(status_hash, "metadata",
-                                            sizeof("metadata") - 1))
-          != NULL) {
-        if (!create_metadata_array(inner_value, &trailing_metadata)) {
-          zend_throw_exception(spl_ce_InvalidArgumentException,
-                               "Bad trailing metadata value given", 1);
-          goto cleanup;
-        }
-        ops[op_num].data.send_status_from_server.trailing_metadata =
-          trailing_metadata.metadata;
-        ops[op_num].data.send_status_from_server.trailing_metadata_count =
-          trailing_metadata.count;
-      }
-      if ((inner_value = zend_hash_str_find(status_hash, "code",
-                                            sizeof("code") - 1)) != NULL) {
-        if (Z_TYPE_P(inner_value) != IS_LONG) {
-          zend_throw_exception(spl_ce_InvalidArgumentException,
-                               "Status code must be an integer", 1);
-          goto cleanup;
-        }
-        ops[op_num].data.send_status_from_server.status =
-          Z_LVAL_P(inner_value);
-      } else {
-        zend_throw_exception(spl_ce_InvalidArgumentException,
-                             "Integer status code is required", 1);
-        goto cleanup;
-      }
-      if ((inner_value = zend_hash_str_find(status_hash, "details",
-                                            sizeof("details") - 1)) != NULL) {
-        if (Z_TYPE_P(inner_value) != IS_STRING) {
-          zend_throw_exception(spl_ce_InvalidArgumentException,
-                               "Status details must be a string", 1);
-          goto cleanup;
-        }
-        ops[op_num].data.send_status_from_server.status_details =
-          Z_STRVAL_P(inner_value);
-      } else {
-        zend_throw_exception(spl_ce_InvalidArgumentException,
-                             "String status details is required", 1);
-        goto cleanup;
-      }
-      break;
-    case GRPC_OP_RECV_INITIAL_METADATA:
-      ops[op_num].data.recv_initial_metadata = &recv_metadata;
-      break;
-    case GRPC_OP_RECV_MESSAGE:
-      ops[op_num].data.recv_message = &message;
-      break;
-    case GRPC_OP_RECV_STATUS_ON_CLIENT:
-      ops[op_num].data.recv_status_on_client.trailing_metadata =
-        &recv_trailing_metadata;
-      ops[op_num].data.recv_status_on_client.status = &status;
-      ops[op_num].data.recv_status_on_client.status_details =
-        &status_details;
-      ops[op_num].data.recv_status_on_client.status_details_capacity =
-        &status_details_capacity;
-      break;
-    case GRPC_OP_RECV_CLOSE_ON_SERVER:
-      ops[op_num].data.recv_close_on_server.cancelled = &cancelled;
-      break;
-    default:
-      zend_throw_exception(spl_ce_InvalidArgumentException,
-                           "Unrecognized key in batch", 1);
-      goto cleanup;
-    }
-    ops[op_num].op = (grpc_op_type)index;
-    ops[op_num].flags = 0;
-    ops[op_num].reserved = NULL;
-    op_num++;
-  } ZEND_HASH_FOREACH_END();
-
-#endif
+  PHP_GRPC_HASH_FOREACH_END()
 
   error = grpc_call_start_batch(call->wrapped, ops, op_num, call->wrapped,
                                 NULL);
@@ -663,7 +424,9 @@ PHP_METHOD(Call, startBatch) {
   }
   grpc_completion_queue_pluck(completion_queue, call->wrapped,
                               gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-#if PHP_MAJOR_VERSION < 7
+#if PHP_MAJOR_VERSION >= 7
+  zval recv_md;
+#endif
   for (int i = 0; i < op_num; i++) {
     switch(ops[i].op) {
     case GRPC_OP_SEND_INITIAL_METADATA:
@@ -679,73 +442,38 @@ PHP_METHOD(Call, startBatch) {
       add_property_bool(result, "send_status", true);
       break;
     case GRPC_OP_RECV_INITIAL_METADATA:
+#if PHP_MAJOR_VERSION < 7
       array = grpc_parse_metadata_array(&recv_metadata TSRMLS_CC);
       add_property_zval(result, "metadata", array);
-      Z_DELREF_P(array);
-      break;
-    case GRPC_OP_RECV_MESSAGE:
-      byte_buffer_to_string(message, &message_str, &message_len);
-      if (message_str == NULL) {
-        add_property_null(result, "message");
-      } else {
-        add_property_stringl(result, "message", message_str, message_len,
-                             false);
-      }
-      break;
-    case GRPC_OP_RECV_STATUS_ON_CLIENT:
-      MAKE_STD_ZVAL(recv_status);
-      object_init(recv_status);
-      array = grpc_parse_metadata_array(&recv_trailing_metadata TSRMLS_CC);
-      add_property_zval(recv_status, "metadata", array);
-      Z_DELREF_P(array);
-      add_property_long(recv_status, "code", status);
-      add_property_string(recv_status, "details", status_details, true);
-      add_property_zval(result, "status", recv_status);
-      Z_DELREF_P(recv_status);
-      break;
-    case GRPC_OP_RECV_CLOSE_ON_SERVER:
-      add_property_bool(result, "cancelled", cancelled);
-      break;
-    default:
-      break;
-    }
-  }
 #else
-  zval recv_md;
-  for (int i = 0; i < op_num; i++) {
-    switch(ops[i].op) {
-    case GRPC_OP_SEND_INITIAL_METADATA:
-      add_property_bool(result, "send_metadata", true);
-      break;
-    case GRPC_OP_SEND_MESSAGE:
-      add_property_bool(result, "send_message", true);
-      break;
-    case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
-      add_property_bool(result, "send_close", true);
-      break;
-    case GRPC_OP_SEND_STATUS_FROM_SERVER:
-      add_property_bool(result, "send_status", true);
-      break;
-    case GRPC_OP_RECV_INITIAL_METADATA:
       recv_md = *grpc_parse_metadata_array(&recv_metadata);
       add_property_zval(result, "metadata", &recv_md);
+#endif
+      PHP_GRPC_DELREF(array);
       break;
     case GRPC_OP_RECV_MESSAGE:
       byte_buffer_to_string(message, &message_str, &message_len);
       if (message_str == NULL) {
         add_property_null(result, "message");
       } else {
-        add_property_stringl(result, "message", message_str,
-                             message_len);
+        php_grpc_add_property_stringl(result, "message", message_str,
+                                      message_len, false);
       }
       break;
     case GRPC_OP_RECV_STATUS_ON_CLIENT:
-      object_init(&recv_status);
+#if PHP_MAJOR_VERSION < 7
+      array = grpc_parse_metadata_array(&recv_trailing_metadata TSRMLS_CC);
+      add_property_zval(recv_status, "metadata", array);
+#else
       recv_md = *grpc_parse_metadata_array(&recv_trailing_metadata);
-      add_property_zval(&recv_status, "metadata", &recv_md);
-      add_property_long(&recv_status, "code", status);
-      add_property_string(&recv_status, "details", status_details);
-      add_property_zval(result, "status", &recv_status);
+      add_property_zval(recv_status, "metadata", &recv_md);
+#endif
+      PHP_GRPC_DELREF(array);
+      add_property_long(recv_status, "code", status);
+      php_grpc_add_property_string(recv_status, "details", status_details,
+                                   true);
+      add_property_zval(result, "status", recv_status);
+      PHP_GRPC_DELREF(recv_status);
       break;
     case GRPC_OP_RECV_CLOSE_ON_SERVER:
       add_property_bool(result, "cancelled", cancelled);
@@ -754,7 +482,6 @@ PHP_METHOD(Call, startBatch) {
       break;
     }
   }
-#endif
 
 cleanup:
   grpc_metadata_array_destroy(&metadata);
@@ -833,10 +560,5 @@ void grpc_init_call(TSRMLS_D) {
   INIT_CLASS_ENTRY(ce, "Grpc\\Call", call_methods);
   ce.create_object = create_wrapped_grpc_call;
   grpc_ce_call = zend_register_internal_class(&ce TSRMLS_CC);
-#if PHP_MAJOR_VERSION >= 7
-  memcpy(&call_ce_handlers, zend_get_std_object_handlers(),
-         sizeof(zend_object_handlers));
-  call_ce_handlers.offset = XtOffsetOf(wrapped_grpc_call, std);
-  call_ce_handlers.free_obj = free_wrapped_grpc_call;
-#endif
+  PHP_GRPC_INIT_HANDLER(wrapped_grpc_call, call_ce_handlers);
 }
diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c
index 29762afb297738f593ae8c3e1c9dc7170074eb61..6921a5df17ba81cfbf505abc2abacc444542cb65 100644
--- a/src/php/ext/grpc/call_credentials.c
+++ b/src/php/ext/grpc/call_credentials.c
@@ -52,69 +52,28 @@
 #include <grpc/grpc_security.h>
 
 zend_class_entry *grpc_ce_call_credentials;
-
-#if PHP_MAJOR_VERSION < 7
+#if PHP_MAJOR_VERSION >= 7
+static zend_object_handlers call_credentials_ce_handlers;
+#endif
 
 /* Frees and destroys an instance of wrapped_grpc_call_credentials */
-void free_wrapped_grpc_call_credentials(void *object TSRMLS_DC) {
-  wrapped_grpc_call_credentials *creds =
-      (wrapped_grpc_call_credentials *)object;
-  if (creds->wrapped != NULL) {
-    grpc_call_credentials_release(creds->wrapped);
+PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_call_credentials)
+  if (p->wrapped != NULL) {
+    grpc_call_credentials_release(p->wrapped);
   }
-  zend_object_std_dtor(&creds->std TSRMLS_CC);
-  efree(creds);
-}
+PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
 /* Initializes an instance of wrapped_grpc_call_credentials to be
  * associated with an object of a class specified by class_type */
-zend_object_value create_wrapped_grpc_call_credentials(
+php_grpc_zend_object create_wrapped_grpc_call_credentials(
     zend_class_entry *class_type TSRMLS_DC) {
-  zend_object_value retval;
-  wrapped_grpc_call_credentials *intern;
-
-  intern = (wrapped_grpc_call_credentials *)emalloc(
-      sizeof(wrapped_grpc_call_credentials));
-  memset(intern, 0, sizeof(wrapped_grpc_call_credentials));
-
+  PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_call_credentials);
   zend_object_std_init(&intern->std, class_type TSRMLS_CC);
   object_properties_init(&intern->std, class_type);
-  retval.handle = zend_objects_store_put(
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      free_wrapped_grpc_call_credentials, NULL TSRMLS_CC);
-  retval.handlers = zend_get_std_object_handlers();
-  return retval;
+  PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_call_credentials,
+                             call_credentials_ce_handlers);
 }
 
-#else
-
-static zend_object_handlers call_credentials_ce_handlers;
-
-/* Frees and destroys an instance of wrapped_grpc_call_credentials */
-static void free_wrapped_grpc_call_credentials(zend_object *object) {
-  wrapped_grpc_call_credentials *creds =
-    wrapped_grpc_call_creds_from_obj(object);
-  if (creds->wrapped != NULL) {
-    grpc_call_credentials_release(creds->wrapped);
-  }
-  zend_object_std_dtor(&creds->std);
-}
-
-/* Initializes an instance of wrapped_grpc_call_credentials to be
- * associated with an object of a class specified by class_type */
-zend_object *create_wrapped_grpc_call_credentials(zend_class_entry
-                                                  *class_type) {
-  wrapped_grpc_call_credentials *intern;
-  intern = ecalloc(1, sizeof(wrapped_grpc_call_credentials) +
-                   zend_object_properties_size(class_type));
-  zend_object_std_init(&intern->std, class_type);
-  object_properties_init(&intern->std, class_type);
-  intern->std.handlers = &call_credentials_ce_handlers;
-  return &intern->std;
-}
-
-#endif
-
 zval *grpc_php_wrap_call_credentials(grpc_call_credentials
                                      *wrapped TSRMLS_DC) {
   zval *credentials_object;
@@ -276,13 +235,6 @@ void grpc_init_call_credentials(TSRMLS_D) {
   INIT_CLASS_ENTRY(ce, "Grpc\\CallCredentials", call_credentials_methods);
   ce.create_object = create_wrapped_grpc_call_credentials;
   grpc_ce_call_credentials = zend_register_internal_class(&ce TSRMLS_CC);
-#if PHP_MAJOR_VERSION >= 7
-  memcpy(&call_credentials_ce_handlers,
-         zend_get_std_object_handlers(),
-         sizeof(zend_object_handlers));
-  call_credentials_ce_handlers.offset =
-    XtOffsetOf(wrapped_grpc_call_credentials, std);
-  call_credentials_ce_handlers.free_obj =
-    free_wrapped_grpc_call_credentials;
-#endif
+  PHP_GRPC_INIT_HANDLER(wrapped_grpc_call_credentials,
+                        call_credentials_ce_handlers);
 }
diff --git a/src/php/ext/grpc/call_credentials.h b/src/php/ext/grpc/call_credentials.h
index 822d077ccb0d177789cd371cc73ec1491e6c7241..c1d85c0fb283990f4c7b3365750ec93ceb1c0924 100755
--- a/src/php/ext/grpc/call_credentials.h
+++ b/src/php/ext/grpc/call_credentials.h
@@ -63,15 +63,13 @@ PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_call_credentials)
 #else
 
 static inline wrapped_grpc_call_credentials
-*wrapped_grpc_call_creds_from_obj(zend_object *obj) {
-  return
-    (wrapped_grpc_call_credentials*)((char*)(obj) -
-                                     XtOffsetOf(wrapped_grpc_call_credentials,
-                                                std));
+*wrapped_grpc_call_credentials_from_obj(zend_object *obj) {
+  return (wrapped_grpc_call_credentials*)(
+      (char*)(obj) - XtOffsetOf(wrapped_grpc_call_credentials, std));
 }
 
 #define Z_WRAPPED_GRPC_CALL_CREDS_P(zv) \
-  wrapped_grpc_call_creds_from_obj(Z_OBJ_P((zv)))
+  wrapped_grpc_call_credentials_from_obj(Z_OBJ_P((zv)))
 
 #endif /* PHP_MAJOR_VERSION */
 
diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c
index 18ebcabc26f2a03eac48a85e60579fa4ce4c2169..b5a2c9f6bafd999b1b5a8f95a79bfb1e20e925e4 100644
--- a/src/php/ext/grpc/channel.c
+++ b/src/php/ext/grpc/channel.c
@@ -56,122 +56,52 @@
 #include "timeval.h"
 
 zend_class_entry *grpc_ce_channel;
-
-#if PHP_MAJOR_VERSION < 7
-
-/* Frees and destroys an instance of wrapped_grpc_channel */
-void free_wrapped_grpc_channel(void *object TSRMLS_DC) {
-  wrapped_grpc_channel *channel = (wrapped_grpc_channel *)object;
-  if (channel->wrapped != NULL) {
-    grpc_channel_destroy(channel->wrapped);
-  }
-  zend_object_std_dtor(&channel->std TSRMLS_CC);
-  efree(channel);
-}
-
-/* Initializes an instance of wrapped_grpc_channel to be associated with an
- * object of a class specified by class_type */
-zend_object_value create_wrapped_grpc_channel(zend_class_entry *class_type
-                                                  TSRMLS_DC) {
-  zend_object_value retval;
-  wrapped_grpc_channel *intern;
-  intern = (wrapped_grpc_channel *)emalloc(sizeof(wrapped_grpc_channel));
-  memset(intern, 0, sizeof(wrapped_grpc_channel));
-  zend_object_std_init(&intern->std, class_type TSRMLS_CC);
-  object_properties_init(&intern->std, class_type);
-  retval.handle = zend_objects_store_put(
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      free_wrapped_grpc_channel, NULL TSRMLS_CC);
-  retval.handlers = zend_get_std_object_handlers();
-  return retval;
-}
-
-#else
-
+#if PHP_MAJOR_VERSION >= 7
 static zend_object_handlers channel_ce_handlers;
+#endif
 
 /* Frees and destroys an instance of wrapped_grpc_channel */
-static void free_wrapped_grpc_channel(zend_object *object) {
-  wrapped_grpc_channel *channel = wrapped_grpc_channel_from_obj(object);
-  if (channel->wrapped != NULL) {
-    grpc_channel_destroy(channel->wrapped);
+PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel)
+  if (p->wrapped != NULL) {
+    grpc_channel_destroy(p->wrapped);
   }
-  zend_object_std_dtor(&channel->std);
-}
+PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
 /* Initializes an instance of wrapped_grpc_channel to be associated with an
  * object of a class specified by class_type */
-zend_object *create_wrapped_grpc_channel(zend_class_entry *class_type) {
-  wrapped_grpc_channel *intern;
-  intern = ecalloc(1, sizeof(wrapped_grpc_channel) +
-                   zend_object_properties_size(class_type));
-  zend_object_std_init(&intern->std, class_type);
+php_grpc_zend_object create_wrapped_grpc_channel(zend_class_entry *class_type
+                                                 TSRMLS_DC) {
+  PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_channel);
+  zend_object_std_init(&intern->std, class_type TSRMLS_CC);
   object_properties_init(&intern->std, class_type);
-  intern->std.handlers = &channel_ce_handlers;
-  return &intern->std;
+  PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_channel, channel_ce_handlers);
 }
 
-#endif
-
 void php_grpc_read_args_array(zval *args_array,
                               grpc_channel_args *args TSRMLS_DC) {
   HashTable *array_hash;
   int args_index;
-#if PHP_MAJOR_VERSION < 7
-  HashPosition array_pointer;
-  zval **data;
-  char *key;
-  uint key_len;
-  ulong index;
-#else
-  zval *data;
-  zend_string *key;
-#endif
   array_hash = Z_ARRVAL_P(args_array);
   if (!array_hash) {
     zend_throw_exception(spl_ce_InvalidArgumentException,
-                         "array_hash is NULL", 1);
+                         "array_hash is NULL", 1 TSRMLS_CC);
     return;
   }
   args->num_args = zend_hash_num_elements(array_hash);
   args->args = ecalloc(args->num_args, sizeof(grpc_arg));
   args_index = 0;
 
-#if PHP_MAJOR_VERSION < 7
-  for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
-       zend_hash_get_current_data_ex(array_hash, (void **)&data,
-                                     &array_pointer) == SUCCESS;
-       zend_hash_move_forward_ex(array_hash, &array_pointer)) {
-    if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0,
-                                     &array_pointer) != HASH_KEY_IS_STRING) {
+  char *key = NULL;
+  zval *data;
+  int key_type;
+
+  PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(array_hash, key, key_type, data)
+    if (key_type != HASH_KEY_IS_STRING) {
       zend_throw_exception(spl_ce_InvalidArgumentException,
                            "args keys must be strings", 1 TSRMLS_CC);
       return;
     }
     args->args[args_index].key = key;
-    switch (Z_TYPE_P(*data)) {
-    case IS_LONG:
-      args->args[args_index].value.integer = (int)Z_LVAL_P(*data);
-      args->args[args_index].type = GRPC_ARG_INTEGER;
-      break;
-    case IS_STRING:
-      args->args[args_index].value.string = Z_STRVAL_P(*data);
-      args->args[args_index].type = GRPC_ARG_STRING;
-      break;
-    default:
-      zend_throw_exception(spl_ce_InvalidArgumentException,
-                           "args values must be int or string", 1 TSRMLS_CC);
-      return;
-    }
-    args_index++;
-  }
-#else
-  ZEND_HASH_FOREACH_STR_KEY_VAL(array_hash, key, data) {
-    if (key == NULL) {
-      zend_throw_exception(spl_ce_InvalidArgumentException,
-                           "args keys must be strings", 1);
-    }
-    args->args[args_index].key = ZSTR_VAL(key);
     switch (Z_TYPE_P(data)) {
     case IS_LONG:
       args->args[args_index].value.integer = (int)Z_LVAL_P(data);
@@ -183,12 +113,11 @@ void php_grpc_read_args_array(zval *args_array,
       break;
     default:
       zend_throw_exception(spl_ce_InvalidArgumentException,
-                           "args values must be int or string", 1);
+                           "args values must be int or string", 1 TSRMLS_CC);
       return;
     }
     args_index++;
-  } ZEND_HASH_FOREACH_END();
-#endif
+  PHP_GRPC_HASH_FOREACH_END()
 }
 
 /**
@@ -200,11 +129,7 @@ void php_grpc_read_args_array(zval *args_array,
  */
 PHP_METHOD(Channel, __construct) {
   wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
-#if PHP_MAJOR_VERSION < 7
-  zval **creds_obj = NULL;
-#else
   zval *creds_obj = NULL;
-#endif
   char *target;
   php_grpc_int target_length;
   zval *args_array = NULL;
@@ -219,43 +144,23 @@ PHP_METHOD(Channel, __construct) {
                          "Channel expects a string and an array", 1 TSRMLS_CC);
     return;
   }
-#if PHP_MAJOR_VERSION < 7
   array_hash = Z_ARRVAL_P(args_array);
-  if (zend_hash_find(array_hash, "credentials", sizeof("credentials"),
+  if (php_grpc_zend_hash_find(array_hash, "credentials", sizeof("credentials"),
                      (void **)&creds_obj) == SUCCESS) {
-    if (Z_TYPE_P(*creds_obj) == IS_NULL) {
-      creds = NULL;
-      zend_hash_del(array_hash, "credentials", 12);
-    } else if (zend_get_class_entry(*creds_obj TSRMLS_CC) !=
-        grpc_ce_channel_credentials) {
-      zend_throw_exception(spl_ce_InvalidArgumentException,
-                           "credentials must be a ChannelCredentials object",
-                           1 TSRMLS_CC);
-      return;
-    } else {
-      creds = (wrapped_grpc_channel_credentials *)zend_object_store_get_object(
-          *creds_obj TSRMLS_CC);
-      zend_hash_del(array_hash, "credentials", 12);
-    }
-  }
-#else
-  array_hash = HASH_OF(args_array);
-  if ((creds_obj = zend_hash_str_find(array_hash, "credentials",
-                                      sizeof("credentials") - 1)) != NULL) {
     if (Z_TYPE_P(creds_obj) == IS_NULL) {
       creds = NULL;
-      zend_hash_str_del(array_hash, "credentials", sizeof("credentials") - 1);
-    } else if (Z_OBJ_P(creds_obj)->ce != grpc_ce_channel_credentials) {
+      php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials"));
+    } else if (PHP_GRPC_GET_CLASS_ENTRY(creds_obj) !=
+               grpc_ce_channel_credentials) {
       zend_throw_exception(spl_ce_InvalidArgumentException,
                            "credentials must be a ChannelCredentials object",
-                           1);
+                           1 TSRMLS_CC);
       return;
     } else {
       creds = Z_WRAPPED_GRPC_CHANNEL_CREDS_P(creds_obj);
-      zend_hash_str_del(array_hash, "credentials", sizeof("credentials") - 1);
+      php_grpc_zend_hash_del(array_hash, "credentials", sizeof("credentials"));
     }
   }
-#endif
   php_grpc_read_args_array(args_array, &args TSRMLS_CC);
   if (creds == NULL) {
     channel->wrapped = grpc_insecure_channel_create(target, &args, NULL);
@@ -311,8 +216,7 @@ PHP_METHOD(Channel, watchConnectivityState) {
   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO",
           &last_state, &deadline_obj, grpc_ce_timeval) == FAILURE) {
     zend_throw_exception(spl_ce_InvalidArgumentException,
-        "watchConnectivityState expects 1 long 1 timeval",
-        1 TSRMLS_CC);
+        "watchConnectivityState expects 1 long 1 timeval", 1 TSRMLS_CC);
     return;
   }
 
@@ -352,11 +256,5 @@ void grpc_init_channel(TSRMLS_D) {
   INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods);
   ce.create_object = create_wrapped_grpc_channel;
   grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC);
-#if PHP_MAJOR_VERSION >= 7
-  memcpy(&channel_ce_handlers, zend_get_std_object_handlers(),
-         sizeof(zend_object_handlers));
-  channel_ce_handlers.offset =
-    XtOffsetOf(wrapped_grpc_channel, std);
-  channel_ce_handlers.free_obj = free_wrapped_grpc_channel;
-#endif
+  PHP_GRPC_INIT_HANDLER(wrapped_grpc_channel, channel_ce_handlers);
 }
diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c
index 1c2ea5ba829ee5cf1d82a100c4d08fecc2646143..0b356aa25fc83be05f1cf38a50ab277a0dd83c4d 100644
--- a/src/php/ext/grpc/channel_credentials.c
+++ b/src/php/ext/grpc/channel_credentials.c
@@ -52,6 +52,9 @@
 #include <grpc/grpc_security.h>
 
 zend_class_entry *grpc_ce_channel_credentials;
+#if PHP_MAJOR_VERSION >= 7
+static zend_object_handlers channel_credentials_ce_handlers;
+#endif
 static char *default_pem_root_certs = NULL;
 
 static grpc_ssl_roots_override_result get_ssl_roots_override(
@@ -63,68 +66,24 @@ static grpc_ssl_roots_override_result get_ssl_roots_override(
   return GRPC_SSL_ROOTS_OVERRIDE_OK;
 }
 
-#if PHP_MAJOR_VERSION < 7
-
 /* Frees and destroys an instance of wrapped_grpc_channel_credentials */
-void free_wrapped_grpc_channel_credentials(void *object TSRMLS_DC) {
-  wrapped_grpc_channel_credentials *creds =
-      (wrapped_grpc_channel_credentials *)object;
-  if (creds->wrapped != NULL) {
-    grpc_channel_credentials_release(creds->wrapped);
+PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel_credentials)
+  if (p->wrapped != NULL) {
+    grpc_channel_credentials_release(p->wrapped);
   }
-  zend_object_std_dtor(&creds->std TSRMLS_CC);
-  efree(creds);
-}
+PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
 /* Initializes an instance of wrapped_grpc_channel_credentials to be
  * associated with an object of a class specified by class_type */
-zend_object_value create_wrapped_grpc_channel_credentials(
+php_grpc_zend_object create_wrapped_grpc_channel_credentials(
     zend_class_entry *class_type TSRMLS_DC) {
-  zend_object_value retval;
-  wrapped_grpc_channel_credentials *intern;
-
-  intern = (wrapped_grpc_channel_credentials *)emalloc(
-      sizeof(wrapped_grpc_channel_credentials));
-  memset(intern, 0, sizeof(wrapped_grpc_channel_credentials));
-
+  PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_channel_credentials);
   zend_object_std_init(&intern->std, class_type TSRMLS_CC);
   object_properties_init(&intern->std, class_type);
-  retval.handle = zend_objects_store_put(
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      free_wrapped_grpc_channel_credentials, NULL TSRMLS_CC);
-  retval.handlers = zend_get_std_object_handlers();
-  return retval;
+  PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_channel_credentials,
+                             channel_credentials_ce_handlers);
 }
 
-#else
-
-static zend_object_handlers channel_credentials_ce_handlers;
-
-/* Frees and destroys an instance of wrapped_grpc_channel_credentials */
-static void free_wrapped_grpc_channel_credentials(zend_object *object) {
-  wrapped_grpc_channel_credentials *creds =
-    wrapped_grpc_channel_creds_from_obj(object);
-  if (creds->wrapped != NULL) {
-    grpc_channel_credentials_release(creds->wrapped);
-  }
-  zend_object_std_dtor(&creds->std);
-}
-
-/* Initializes an instance of wrapped_grpc_channel_credentials to be
- * associated with an object of a class specified by class_type */
-zend_object *create_wrapped_grpc_channel_credentials(zend_class_entry
-                                                     *class_type) {
-  wrapped_grpc_channel_credentials *intern;
-  intern = ecalloc(1, sizeof(wrapped_grpc_channel_credentials) +
-                   zend_object_properties_size(class_type));
-  zend_object_std_init(&intern->std, class_type);
-  object_properties_init(&intern->std, class_type);
-  intern->std.handlers = &channel_credentials_ce_handlers;
-  return &intern->std;
-}
-
-#endif
-
 zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials
                                         *wrapped TSRMLS_DC) {
   zval *credentials_object;
@@ -267,13 +226,6 @@ void grpc_init_channel_credentials(TSRMLS_D) {
   grpc_set_ssl_roots_override_callback(get_ssl_roots_override);
   ce.create_object = create_wrapped_grpc_channel_credentials;
   grpc_ce_channel_credentials = zend_register_internal_class(&ce TSRMLS_CC);
-#if PHP_MAJOR_VERSION >= 7
-  memcpy(&channel_credentials_ce_handlers,
-         zend_get_std_object_handlers(),
-         sizeof(zend_object_handlers));
-  channel_credentials_ce_handlers.offset =
-    XtOffsetOf(wrapped_grpc_channel_credentials, std);
-  channel_credentials_ce_handlers.free_obj =
-    free_wrapped_grpc_channel_credentials;
-#endif
+  PHP_GRPC_INIT_HANDLER(wrapped_grpc_channel_credentials,
+                        channel_credentials_ce_handlers);
 }
diff --git a/src/php/ext/grpc/channel_credentials.h b/src/php/ext/grpc/channel_credentials.h
index 21225db5fb914e620c90aab13fd62eb77c59a220..b043d91fa6972ec4c9a6b82cddfbabacf4d2ae58 100755
--- a/src/php/ext/grpc/channel_credentials.h
+++ b/src/php/ext/grpc/channel_credentials.h
@@ -63,15 +63,13 @@ PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_channel_credentials)
 #else
 
 static inline wrapped_grpc_channel_credentials
-*wrapped_grpc_channel_creds_from_obj(zend_object *obj) {
-  return
-    (wrapped_grpc_channel_credentials *)
-    ((char*)(obj) -
-     XtOffsetOf(wrapped_grpc_channel_credentials, std));
+*wrapped_grpc_channel_credentials_from_obj(zend_object *obj) {
+  return (wrapped_grpc_channel_credentials *)(
+      (char*)(obj) - XtOffsetOf(wrapped_grpc_channel_credentials, std));
 }
 
 #define Z_WRAPPED_GRPC_CHANNEL_CREDS_P(zv) \
-  wrapped_grpc_channel_creds_from_obj(Z_OBJ_P((zv)))
+  wrapped_grpc_channel_credentials_from_obj(Z_OBJ_P((zv)))
 
 #endif /* PHP_MAJOR_VERSION */
 
diff --git a/src/php/ext/grpc/package.xml b/src/php/ext/grpc/package.xml
deleted file mode 100644
index daf2ee5a53d62ca6b99dedc376143f7d91b4d6bf..0000000000000000000000000000000000000000
--- a/src/php/ext/grpc/package.xml
+++ /dev/null
@@ -1,156 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<package packagerversion="1.9.5" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
- <name>grpc</name>
- <channel>pecl.php.net</channel>
- <summary>A high performance, open source, general RPC framework that puts mobile and HTTP/2 first.</summary>
- <description>Remote Procedure Calls (RPCs) provide a useful abstraction for building distributed applications and services. The libraries in this repository provide a concrete implementation of the gRPC protocol, layered over HTTP/2. These libraries enable communication between clients and servers using any combination of the supported languages.</description>
- <lead>
-  <name>Stanley Cheung</name>
-  <user>stanleycheung</user>
-  <email>grpc-packages@google.com</email>
-  <active>yes</active>
- </lead>
- <date>2016-01-13</date>
- <time>16:06:07</time>
- <version>
-  <release>0.7.0</release>
-  <api>0.7.0</api>
- </version>
- <stability>
-  <release>beta</release>
-  <api>beta</api>
- </stability>
- <license>BSD</license>
- <notes>
-- Breaking change to Credentials class (removed) #3765
-- Replaced by ChannelCredentials and CallCredentials class #3765
-- New plugin based metadata auth API #4394
-- Explicit ChannelCredentials::createInsecure() call
- </notes>
- <contents>
-  <dir baseinstalldir="/" name="/">
-   <file baseinstalldir="/" md5sum="f201d644fdbd8228ffd1d4a69cc44f1f" name="tests/grpc-basic.phpt" role="test" />
-   <file baseinstalldir="/" md5sum="6f19828fb869b7b8a590cbb76b4f996d" name="byte_buffer.c" role="src" />
-   <file baseinstalldir="/" md5sum="c8de0f819499c48adfc8d7f472c0196b" name="byte_buffer.h" role="src" />
-   <file baseinstalldir="/" md5sum="ee7eb7757f9e6f0e36f8f616b6bd0af5" name="call.c" role="src" />
-   <file baseinstalldir="/" md5sum="44c56bd9912d2538cbd6059e3e0452b6" name="call.h" role="src" />
-   <file baseinstalldir="/" md5sum="ff90f6c03ed44b5f4170bf3259a6704e" name="call_credentials.c" role="src" />
-   <file baseinstalldir="/" md5sum="3c3860e1d84f43cb6b2fbaa8d2ae1ab7" name="call_credentials.h" role="src" />
-   <file baseinstalldir="/" md5sum="aee9b63f790522aec2c682055240cc61" name="channel.c" role="src" />
-   <file baseinstalldir="/" md5sum="ed4b00c0cf3702b115d0cfa87450dc09" name="channel.h" role="src" />
-   <file baseinstalldir="/" md5sum="1a51c76d0b7b7d3ab570ed7d60c2ea46" name="channel_credentials.c" role="src" />
-   <file baseinstalldir="/" md5sum="a86250e03f610ce6c2c7595a84e08821" name="channel_credentials.h" role="src" />
-   <file baseinstalldir="/" md5sum="55ab7a42f9dd9bfc7e28a61cfc5fca63" name="completion_queue.c" role="src" />
-   <file baseinstalldir="/" md5sum="f10b5bb232d74a6878e829e2e76cdaa2" name="completion_queue.h" role="src" />
-   <file baseinstalldir="/" md5sum="cafed254127007ff2271dad7d56a06c8" name="config.m4" role="src" />
-   <file baseinstalldir="/" md5sum="38a1bc979d810c36ebc2a52d4b7b5319" name="CREDITS" role="doc" />
-   <file baseinstalldir="/" md5sum="8847cf67b1b54c981d47ecbb0d139a0c" name="LICENSE" role="doc" />
-   <file baseinstalldir="/" md5sum="3131a8af38fe5918e5409016b89d6cdb" name="php_grpc.c" role="src" />
-   <file baseinstalldir="/" md5sum="673b07859d9f69232f8a754c56780686" name="php_grpc.h" role="src" />
-   <file baseinstalldir="/" md5sum="7533a6d3ea02c78cad23a9651de0825d" name="README.md" role="doc" />
-   <file baseinstalldir="/" md5sum="3e4e960454ebb2fc7b78a840493f5315" name="server.c" role="src" />
-   <file baseinstalldir="/" md5sum="4b730f06d14cbbb0642bdbd194749595" name="server.h" role="src" />
-   <file baseinstalldir="/" md5sum="34ea881f1fe960d190d0713422cf8916" name="server_credentials.c" role="src" />
-   <file baseinstalldir="/" md5sum="9c4b4cc06356a8a39a16a085a9b85996" name="server_credentials.h" role="src" />
-   <file baseinstalldir="/" md5sum="7646ec78cb133f66ba59e03c6f451e39" name="timeval.c" role="src" />
-   <file baseinstalldir="/" md5sum="496e27a100b4d93ca3fb35c924c5e163" name="timeval.h" role="src" />
-  </dir>
- </contents>
- <dependencies>
-  <required>
-   <php>
-    <min>5.5.0</min>
-   </php>
-   <pearinstaller>
-    <min>1.4.0</min>
-   </pearinstaller>
-  </required>
- </dependencies>
- <providesextension>grpc</providesextension>
- <extsrcrelease />
- <changelog>
-  <release>
-   <version>
-    <release>0.5.0</release>
-    <api>0.5.0</api>
-   </version>
-   <stability>
-    <release>alpha</release>
-    <api>alpha</api>
-   </stability>
-   <date>2015-06-16</date>
-   <license>BSD</license>
-   <notes>
-First alpha release
-   </notes>
-  </release>
-  <release>
-   <version>
-    <release>0.5.1</release>
-    <api>0.5.1</api>
-   </version>
-   <stability>
-    <release>alpha</release>
-    <api>alpha</api>
-   </stability>
-   <date>2015-07-09</date>
-   <license>BSD</license>
-   <notes>
-Update to wrap gRPC C Core version 0.10.0
-   </notes>
-  </release>
-  <release>
-   <version>
-    <release>0.6.0</release>
-    <api>0.6.0</api>
-   </version>
-   <stability>
-    <release>beta</release>
-    <api>beta</api>
-   </stability>
-   <date>2015-09-24</date>
-   <license>BSD</license>
-   <notes>
-- support per message compression disable
-- expose per-call host override option
-- expose connectivity API
-- expose channel target and call peer
-- add user-agent
-- update to wrap gRPC C core library beta version 0.11.0
-   </notes>
-  </release>
-  <release>
-   <version>
-    <release>0.6.1</release>
-    <api>0.6.0</api>
-   </version>
-   <stability>
-    <release>beta</release>
-    <api>beta</api>
-   </stability>
-   <date>2015-10-21</date>
-   <license>BSD</license>
-   <notes>
-- fixed undefined constant fatal error when run with apache/nginx #2275
-   </notes>
-  </release>
-  <release>
-   <version>
-    <release>0.7.0</release>
-    <api>0.7.0</api>
-   </version>
-   <stability>
-    <release>beta</release>
-    <api>beta</api>
-   </stability>
-   <date>2016-01-13</date>
-   <license>BSD</license>
-   <notes>
-- Breaking change to Credentials class (removed) #3765
-- Replaced by ChannelCredentials and CallCredentials class #3765
-- New plugin based metadata auth API #4394
-- Explicit ChannelCredentials::createInsecure() call
-   </notes>
-  </release>
- </changelog>
-</package>
diff --git a/src/php/ext/grpc/php7_wrapper.h b/src/php/ext/grpc/php7_wrapper.h
index 58dffc38dc0af7f5126253d10e8a898527b88988..fd8d35636f09ab7f3e139272277fbf5fe9da1648 100644
--- a/src/php/ext/grpc/php7_wrapper.h
+++ b/src/php/ext/grpc/php7_wrapper.h
@@ -40,6 +40,7 @@
 #define php_grpc_int int
 #define php_grpc_long long
 #define php_grpc_ulong ulong
+#define php_grpc_zend_object zend_object_value
 #define php_grpc_add_property_string(arg, name, context, b) \
   add_property_string(arg, name, context, b)
 #define php_grpc_add_property_stringl(res, name, str, len, b) \
@@ -49,6 +50,7 @@
 
 #define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val, dup)
 #define PHP_GRPC_MAKE_STD_ZVAL(pzv) MAKE_STD_ZVAL(pzv)
+#define PHP_GRPC_DELREF(zv) Z_DELREF_P(zv)
 
 #define PHP_GRPC_WRAP_OBJECT_START(name) \
   typedef struct name { \
@@ -56,11 +58,82 @@
 #define PHP_GRPC_WRAP_OBJECT_END(name) \
   } name;
 
+#define PHP_GRPC_FREE_WRAPPED_FUNC_START(class_object) \
+  void free_##class_object(void *object TSRMLS_DC) { \
+    class_object *p = (class_object *)object;
+#define PHP_GRPC_FREE_WRAPPED_FUNC_END() \
+    zend_object_std_dtor(&p->std TSRMLS_CC); \
+    efree(p); \
+  }
+
+#define PHP_GRPC_ALLOC_CLASS_OBJECT(class_object) \
+  class_object *intern; \
+  zend_object_value retval; \
+  intern = (class_object *)emalloc(sizeof(class_object)); \
+  memset(intern, 0, sizeof(class_object));
+
+#define PHP_GRPC_FREE_CLASS_OBJECT(class_object, handler) \
+  retval.handle = zend_objects_store_put( \
+    intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
+    free_##class_object, NULL TSRMLS_CC); \
+  retval.handlers = zend_get_std_object_handlers(); \
+  return retval;
+
+#define PHP_GRPC_HASH_FOREACH_VAL_START(ht, data) \
+  zval **tmp_data = NULL; \
+  for (zend_hash_internal_pointer_reset(ht); \
+       zend_hash_get_current_data(ht, (void**)&tmp_data) == SUCCESS; \
+       zend_hash_move_forward(ht)) { \
+    data = *tmp_data;
+
+#define PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(ht, key, key_type, data) \
+  zval **tmp##key = NULL; \
+  ulong index##key; \
+  uint len##key; \
+  for (zend_hash_internal_pointer_reset(ht); \
+       zend_hash_get_current_data(ht, (void**)&tmp##key) == SUCCESS; \
+       zend_hash_move_forward(ht)) { \
+    key_type = zend_hash_get_current_key_ex(ht, &key, &len##key, &index##key,\
+                                         0, NULL); \
+    data = *tmp##key;
+
+#define PHP_GRPC_HASH_FOREACH_LONG_KEY_VAL_START(ht, key, key_type, index,\
+                                                 data) \
+  zval **tmp##key = NULL; \
+  uint len##key; \
+  for (zend_hash_internal_pointer_reset(ht); \
+       zend_hash_get_current_data(ht, (void**)&tmp##key) == SUCCESS; \
+       zend_hash_move_forward(ht)) { \
+    key_type = zend_hash_get_current_key_ex(ht, &key, &len##key, &index,\
+                                         0, NULL); \
+    data = *tmp##key;
+
+#define PHP_GRPC_HASH_FOREACH_END() }
+
+static inline int php_grpc_zend_hash_find(HashTable *ht, char *key, int len,
+                                          void **value) {
+  zval **data = NULL;
+  if (zend_hash_find(ht, key, len, (void **)&data) == SUCCESS) {
+    *value = *data;
+    return SUCCESS;
+  } else {
+    *value = NULL;
+    return FAILURE;
+  }
+}
+
+#define php_grpc_zend_hash_del zend_hash_del
+
+#define PHP_GRPC_GET_CLASS_ENTRY(object) zend_get_class_entry(object TSRMLS_CC)
+
+#define PHP_GRPC_INIT_HANDLER(class_object, handler_name)
+
 #else
 
 #define php_grpc_int size_t
 #define php_grpc_long zend_long
 #define php_grpc_ulong zend_ulong
+#define php_grpc_zend_object zend_object*
 #define php_grpc_add_property_string(arg, name, context, b) \
   add_property_string(arg, name, context)
 #define php_grpc_add_property_stringl(res, name, str, len, b) \
@@ -72,6 +145,7 @@
 #define PHP_GRPC_MAKE_STD_ZVAL(pzv) \
   zval _stack_zval_##pzv; \
   pzv = &(_stack_zval_##pzv)
+#define PHP_GRPC_DELREF(zv)
 
 #define PHP_GRPC_WRAP_OBJECT_START(name) \
   typedef struct name {
@@ -79,6 +153,66 @@
     zend_object std; \
   } name;
 
+#define WRAPPED_OBJECT_FROM_OBJ(class_object, obj) \
+  class_object##_from_obj(obj);
+
+#define PHP_GRPC_FREE_WRAPPED_FUNC_START(class_object) \
+  static void free_##class_object(zend_object *object) { \
+    class_object *p = WRAPPED_OBJECT_FROM_OBJ(class_object, object)
+#define PHP_GRPC_FREE_WRAPPED_FUNC_END() \
+    zend_object_std_dtor(&p->std); \
+  }
+
+#define PHP_GRPC_ALLOC_CLASS_OBJECT(class_object) \
+  class_object *intern; \
+  intern = ecalloc(1, sizeof(class_object) + \
+                   zend_object_properties_size(class_type));
+
+#define PHP_GRPC_FREE_CLASS_OBJECT(class_object, handler) \
+  intern->std.handlers = &handler; \
+  return &intern->std;
+
+#define PHP_GRPC_HASH_FOREACH_VAL_START(ht, data) \
+  ZEND_HASH_FOREACH_VAL(ht, data) {
+
+#define PHP_GRPC_HASH_FOREACH_STR_KEY_VAL_START(ht, key, key_type, data) \
+  zend_string *(zs_##key); \
+  ZEND_HASH_FOREACH_STR_KEY_VAL(ht, (zs_##key), data) { \
+    if ((zs_##key) == NULL) {key = NULL; key_type = HASH_KEY_IS_LONG;} \
+    else {key = (zs_##key)->val; key_type = HASH_KEY_IS_STRING;}
+
+#define PHP_GRPC_HASH_FOREACH_LONG_KEY_VAL_START(ht, key, key_type, index, \
+                                                 data) \
+  zend_string *(zs_##key); \
+  ZEND_HASH_FOREACH_KEY_VAL(ht, index, zs_##key, data) { \
+    if ((zs_##key) == NULL) {key = NULL; key_type = HASH_KEY_IS_LONG;} \
+    else {key = (zs_##key)->val; key_type = HASH_KEY_IS_STRING;}
+
+#define PHP_GRPC_HASH_FOREACH_END() } ZEND_HASH_FOREACH_END();
+
+static inline int php_grpc_zend_hash_find(HashTable *ht, char *key, int len,
+                                          void **value) {
+  zval *value_tmp = zend_hash_str_find(ht, key, len -1);
+  if (value_tmp == NULL) {
+    return FAILURE;
+  } else {
+    *value = (void *)value_tmp;
+    return SUCCESS;
+  }
+}
+
+static inline int php_grpc_zend_hash_del(HashTable *ht, char *key, int len) {
+  return zend_hash_str_del(ht, key, len - 1);
+}
+
+#define PHP_GRPC_GET_CLASS_ENTRY(object) Z_OBJ_P(object)->ce
+
+#define PHP_GRPC_INIT_HANDLER(class_object, handler_name) \
+  memcpy(&handler_name, zend_get_std_object_handlers(), \
+         sizeof(zend_object_handlers)); \
+  handler_name.offset = XtOffsetOf(class_object, std); \
+  handler_name.free_obj = free_##class_object
+
 #endif /* PHP_MAJOR_VERSION */
 
 #endif /* PHP7_WRAPPER_GRPC_H */
diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c
index 577b7070892f12b12c46d2bb3ce3611a91deaeab..fc20c42b169ebf1b1d020004c541068b9cba136a 100644
--- a/src/php/ext/grpc/server.c
+++ b/src/php/ext/grpc/server.c
@@ -57,73 +57,31 @@
 #include "timeval.h"
 
 zend_class_entry *grpc_ce_server;
-
-#if PHP_MAJOR_VERSION < 7
-
-/* Frees and destroys an instance of wrapped_grpc_server */
-void free_wrapped_grpc_server(void *object TSRMLS_DC) {
-  wrapped_grpc_server *server = (wrapped_grpc_server *)object;
-  if (server->wrapped != NULL) {
-    grpc_server_shutdown_and_notify(server->wrapped, completion_queue, NULL);
-    grpc_server_cancel_all_calls(server->wrapped);
-    grpc_completion_queue_pluck(completion_queue, NULL,
-                                gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-    grpc_server_destroy(server->wrapped);
-  }
-  zend_object_std_dtor(&server->std TSRMLS_CC);
-  efree(server);
-}
-
-/* Initializes an instance of wrapped_grpc_call to be associated with an object
- * of a class specified by class_type */
-zend_object_value create_wrapped_grpc_server(zend_class_entry *class_type
-                                                 TSRMLS_DC) {
-  zend_object_value retval;
-  wrapped_grpc_server *intern;
-
-  intern = (wrapped_grpc_server *)emalloc(sizeof(wrapped_grpc_server));
-  memset(intern, 0, sizeof(wrapped_grpc_server));
-
-  zend_object_std_init(&intern->std, class_type TSRMLS_CC);
-  object_properties_init(&intern->std, class_type);
-  retval.handle = zend_objects_store_put(
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      free_wrapped_grpc_server, NULL TSRMLS_CC);
-  retval.handlers = zend_get_std_object_handlers();
-  return retval;
-}
-
-#else
-
+#if PHP_MAJOR_VERSION >= 7
 static zend_object_handlers server_ce_handlers;
+#endif
 
 /* Frees and destroys an instance of wrapped_grpc_server */
-static void free_wrapped_grpc_server(zend_object *object) {
-  wrapped_grpc_server *server = wrapped_grpc_server_from_obj(object);
-  if (server->wrapped != NULL) {
-    grpc_server_shutdown_and_notify(server->wrapped, completion_queue, NULL);
-    grpc_server_cancel_all_calls(server->wrapped);
+PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_server)
+  if (p->wrapped != NULL) {
+    grpc_server_shutdown_and_notify(p->wrapped, completion_queue, NULL);
+    grpc_server_cancel_all_calls(p->wrapped);
     grpc_completion_queue_pluck(completion_queue, NULL,
                                 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
-    grpc_server_destroy(server->wrapped);
+    grpc_server_destroy(p->wrapped);
   }
-  zend_object_std_dtor(&server->std);
-}
+PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
 /* Initializes an instance of wrapped_grpc_call to be associated with an object
  * of a class specified by class_type */
-zend_object *create_wrapped_grpc_server(zend_class_entry *class_type) {
-  wrapped_grpc_server *intern;
-  intern = ecalloc(1, sizeof(wrapped_grpc_server) +
-                   zend_object_properties_size(class_type));
-  zend_object_std_init(&intern->std, class_type);
+php_grpc_zend_object create_wrapped_grpc_server(zend_class_entry *class_type
+                                                TSRMLS_DC) {
+  PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_server);
+  zend_object_std_init(&intern->std, class_type TSRMLS_CC);
   object_properties_init(&intern->std, class_type);
-  intern->std.handlers = &server_ce_handlers;
-  return &intern->std;
+  PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_server, server_ce_handlers);
 }
 
-#endif
-
 /**
  * Constructs a new instance of the Server class
  * @param array $args The arguments to pass to the server (optional)
@@ -275,7 +233,7 @@ static zend_function_entry server_methods[] = {
   PHP_ME(Server, addHttp2Port, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(Server, addSecureHttp2Port, NULL, ZEND_ACC_PUBLIC)
   PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC)
- PHP_FE_END
+  PHP_FE_END
 };
 
 void grpc_init_server(TSRMLS_D) {
@@ -283,10 +241,5 @@ void grpc_init_server(TSRMLS_D) {
   INIT_CLASS_ENTRY(ce, "Grpc\\Server", server_methods);
   ce.create_object = create_wrapped_grpc_server;
   grpc_ce_server = zend_register_internal_class(&ce TSRMLS_CC);
-#if PHP_MAJOR_VERSION >= 7
-  memcpy(&server_ce_handlers, zend_get_std_object_handlers(),
-         sizeof(zend_object_handlers));
-  server_ce_handlers.offset = XtOffsetOf(wrapped_grpc_server, std);
-  server_ce_handlers.free_obj = free_wrapped_grpc_server;
-#endif
+  PHP_GRPC_INIT_HANDLER(wrapped_grpc_server, server_ce_handlers);
 }
diff --git a/src/php/ext/grpc/server.h b/src/php/ext/grpc/server.h
index 02ef1f797573b6cbf395f72dcc3c3fe95a84f909..a635bc11df938ad214899dbc67165594029f68b5 100755
--- a/src/php/ext/grpc/server.h
+++ b/src/php/ext/grpc/server.h
@@ -66,8 +66,7 @@ static inline wrapped_grpc_server
                                 XtOffsetOf(wrapped_grpc_server, std));
 }
 
-#define Z_WRAPPED_GRPC_SERVER_P(zv) \
-  wrapped_grpc_server_from_obj(Z_OBJ_P((zv)))
+#define Z_WRAPPED_GRPC_SERVER_P(zv) wrapped_grpc_server_from_obj(Z_OBJ_P((zv)))
 
 #endif /* PHP_MAJOR_VERSION */
 
diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c
index 46416f5b47aafc5a687b723d4bb9b842f13de18d..b05896af4afc435d1b2a45a5551442efb753ed2b 100644
--- a/src/php/ext/grpc/server_credentials.c
+++ b/src/php/ext/grpc/server_credentials.c
@@ -50,69 +50,28 @@
 #include <grpc/grpc_security.h>
 
 zend_class_entry *grpc_ce_server_credentials;
-
-#if PHP_MAJOR_VERSION < 7
+#if PHP_MAJOR_VERSION >= 7
+static zend_object_handlers server_credentials_ce_handlers;
+#endif
 
 /* Frees and destroys an instace of wrapped_grpc_server_credentials */
-void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC) {
-  wrapped_grpc_server_credentials *creds =
-      (wrapped_grpc_server_credentials *)object;
-  if (creds->wrapped != NULL) {
-    grpc_server_credentials_release(creds->wrapped);
+PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_server_credentials)
+  if (p->wrapped != NULL) {
+    grpc_server_credentials_release(p->wrapped);
   }
-  zend_object_std_dtor(&creds->std TSRMLS_CC);
-  efree(creds);
-}
+PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
 /* Initializes an instace of wrapped_grpc_server_credentials to be associated
  * with an object of a class specified by class_type */
-zend_object_value create_wrapped_grpc_server_credentials(
+php_grpc_zend_object create_wrapped_grpc_server_credentials(
     zend_class_entry *class_type TSRMLS_DC) {
-  zend_object_value retval;
-  wrapped_grpc_server_credentials *intern;
-
-  intern = (wrapped_grpc_server_credentials *)emalloc(
-      sizeof(wrapped_grpc_server_credentials));
-  memset(intern, 0, sizeof(wrapped_grpc_server_credentials));
-
+  PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_server_credentials);
   zend_object_std_init(&intern->std, class_type TSRMLS_CC);
   object_properties_init(&intern->std, class_type);
-  retval.handle = zend_objects_store_put(
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      free_wrapped_grpc_server_credentials, NULL TSRMLS_CC);
-  retval.handlers = zend_get_std_object_handlers();
-  return retval;
+  PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_server_credentials,
+                             server_credentials_ce_handlers);
 }
 
-#else
-
-static zend_object_handlers server_credentials_ce_handlers;
-
-/* Frees and destroys an instace of wrapped_grpc_server_credentials */
-static void free_wrapped_grpc_server_credentials(zend_object *object) {
-  wrapped_grpc_server_credentials *creds =
-    wrapped_grpc_server_creds_from_obj(object);
-  if (creds->wrapped != NULL) {
-    grpc_server_credentials_release(creds->wrapped);
-  }
-  zend_object_std_dtor(&creds->std);
-}
-
-/* Initializes an instace of wrapped_grpc_server_credentials to be associated
- * with an object of a class specified by class_type */
-zend_object *create_wrapped_grpc_server_credentials(zend_class_entry
-                                                    *class_type) {
-  wrapped_grpc_server_credentials *intern;
-  intern = ecalloc(1, sizeof(wrapped_grpc_server_credentials) +
-                   zend_object_properties_size(class_type));
-  zend_object_std_init(&intern->std, class_type);
-  object_properties_init(&intern->std, class_type);
-  intern->std.handlers = &server_credentials_ce_handlers;
-  return &intern->std;
-}
-
-#endif
-
 zval *grpc_php_wrap_server_credentials(grpc_server_credentials
                                        *wrapped TSRMLS_DC) {
   zval *server_credentials_object;
@@ -163,7 +122,7 @@ PHP_METHOD(ServerCredentials, createSsl) {
 static zend_function_entry server_credentials_methods[] = {
   PHP_ME(ServerCredentials, createSsl, NULL,
          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
-   PHP_FE_END
+  PHP_FE_END
  };
 
 void grpc_init_server_credentials(TSRMLS_D) {
@@ -171,13 +130,6 @@ void grpc_init_server_credentials(TSRMLS_D) {
   INIT_CLASS_ENTRY(ce, "Grpc\\ServerCredentials", server_credentials_methods);
   ce.create_object = create_wrapped_grpc_server_credentials;
   grpc_ce_server_credentials = zend_register_internal_class(&ce TSRMLS_CC);
-#if PHP_MAJOR_VERSION >= 7
-  memcpy(&server_credentials_ce_handlers,
-         zend_get_std_object_handlers(),
-         sizeof(zend_object_handlers));
-  server_credentials_ce_handlers.offset =
-    XtOffsetOf(wrapped_grpc_server_credentials, std);
-  server_credentials_ce_handlers.free_obj =
-    free_wrapped_grpc_server_credentials;
-#endif
+  PHP_GRPC_INIT_HANDLER(wrapped_grpc_server_credentials,
+                        server_credentials_ce_handlers);
 }
diff --git a/src/php/ext/grpc/server_credentials.h b/src/php/ext/grpc/server_credentials.h
index 2a90aa69a29bc8c034e85427dd1afc591fc24c70..6781a614b17e6869155f9d33b8a0ea1a031d723a 100755
--- a/src/php/ext/grpc/server_credentials.h
+++ b/src/php/ext/grpc/server_credentials.h
@@ -63,13 +63,13 @@ PHP_GRPC_WRAP_OBJECT_END(wrapped_grpc_server_credentials)
 #else
 
 static inline wrapped_grpc_server_credentials
-*wrapped_grpc_server_creds_from_obj(zend_object *obj) {
-  return (wrapped_grpc_server_credentials*)
-    ((char*)(obj) - XtOffsetOf(wrapped_grpc_server_credentials, std));
+*wrapped_grpc_server_credentials_from_obj(zend_object *obj) {
+  return (wrapped_grpc_server_credentials*)(
+      (char*)(obj) - XtOffsetOf(wrapped_grpc_server_credentials, std));
 }
 
 #define Z_WRAPPED_GRPC_SERVER_CREDS_P(zv) \
-  wrapped_grpc_server_creds_from_obj(Z_OBJ_P((zv)))
+  wrapped_grpc_server_credentials_from_obj(Z_OBJ_P((zv)))
 
 #endif /* PHP_MAJOR_VERSION */
 
diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c
index 7d1ee2178aefe6f87951abafac2b3bc667773a95..e145d9677267c5180c5f95b103e58c5b8c9b63a1 100644
--- a/src/php/ext/grpc/timeval.c
+++ b/src/php/ext/grpc/timeval.c
@@ -51,57 +51,24 @@
 #include <grpc/support/time.h>
 
 zend_class_entry *grpc_ce_timeval;
-
-#if PHP_MAJOR_VERSION < 7
-
-/* Frees and destroys an instance of wrapped_grpc_call */
-void free_wrapped_grpc_timeval(void *object TSRMLS_DC) {
-  wrapped_grpc_timeval *timeval = (wrapped_grpc_timeval *)object;
-  zend_object_std_dtor(&timeval->std TSRMLS_CC);
-  efree(timeval);
-}
-
-/* Initializes an instance of wrapped_grpc_timeval to be associated with an
- * object of a class specified by class_type */
-zend_object_value create_wrapped_grpc_timeval(zend_class_entry *class_type
-                                                  TSRMLS_DC) {
-  zend_object_value retval;
-  wrapped_grpc_timeval *intern;
-  intern = (wrapped_grpc_timeval *)emalloc(sizeof(wrapped_grpc_timeval));
-  memset(intern, 0, sizeof(wrapped_grpc_timeval));
-  zend_object_std_init(&intern->std, class_type TSRMLS_CC);
-  object_properties_init(&intern->std, class_type);
-  retval.handle = zend_objects_store_put(
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      free_wrapped_grpc_timeval, NULL TSRMLS_CC);
-  retval.handlers = zend_get_std_object_handlers();
-  return retval;
-}
-
-#else
-
+#if PHP_MAJOR_VERSION >= 7
 static zend_object_handlers timeval_ce_handlers;
+#endif
 
 /* Frees and destroys an instance of wrapped_grpc_call */
-static void free_wrapped_grpc_timeval(zend_object *object) {
-  wrapped_grpc_timeval *timeval = wrapped_grpc_timeval_from_obj(object);
-  zend_object_std_dtor(&timeval->std);
-}
+PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_timeval)
+PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
 /* Initializes an instance of wrapped_grpc_timeval to be associated with an
  * object of a class specified by class_type */
-zend_object *create_wrapped_grpc_timeval(zend_class_entry *class_type) {
-  wrapped_grpc_timeval *intern;
-  intern = ecalloc(1, sizeof(wrapped_grpc_timeval) +
-                   zend_object_properties_size(class_type));
-  zend_object_std_init(&intern->std, class_type);
+php_grpc_zend_object create_wrapped_grpc_timeval(zend_class_entry *class_type
+                                                 TSRMLS_DC) {
+  PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_timeval);
+  zend_object_std_init(&intern->std, class_type TSRMLS_CC);
   object_properties_init(&intern->std, class_type);
-  intern->std.handlers = &timeval_ce_handlers;
-  return &intern->std;
+  PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_timeval, timeval_ce_handlers);
 }
 
-#endif
-
 zval *grpc_php_wrap_timeval(gpr_timespec wrapped TSRMLS_DC) {
   zval *timeval_object;
   PHP_GRPC_MAKE_STD_ZVAL(timeval_object);
@@ -311,13 +278,7 @@ void grpc_init_timeval(TSRMLS_D) {
   INIT_CLASS_ENTRY(ce, "Grpc\\Timeval", timeval_methods);
   ce.create_object = create_wrapped_grpc_timeval;
   grpc_ce_timeval = zend_register_internal_class(&ce TSRMLS_CC);
-#if PHP_MAJOR_VERSION >= 7
-  memcpy(&timeval_ce_handlers, zend_get_std_object_handlers(),
-         sizeof(zend_object_handlers));
-  timeval_ce_handlers.offset =
-    XtOffsetOf(wrapped_grpc_timeval, std);
-  timeval_ce_handlers.free_obj = free_wrapped_grpc_timeval;
-#endif
+  PHP_GRPC_INIT_HANDLER(wrapped_grpc_timeval, timeval_ce_handlers);
 }
 
 void grpc_shutdown_timeval(TSRMLS_D) {}
diff --git a/src/ruby/.rubocop.yml b/src/ruby/.rubocop.yml
index 34bb4775435bbcda79de87ea353ffd695887c4f9..0f61ccfa812406ed193f39327cae64da241c7ac8 100644
--- a/src/ruby/.rubocop.yml
+++ b/src/ruby/.rubocop.yml
@@ -5,8 +5,8 @@ inherit_from: .rubocop_todo.yml
 AllCops:
   Exclude:
     - 'bin/apis/**/*'
-    - 'bin/math.rb'
-    - 'bin/math_services.rb'
+    - 'bin/math_pb.rb'
+    - 'bin/math_services_pb.rb'
     - 'pb/grpc/health/v1/*'
     - 'pb/test/**/*'
 
diff --git a/src/ruby/bin/math_client.rb b/src/ruby/bin/math_client.rb
index d7e00e429384f01bd475f8858a74f1ee4eb09ce7..1f238a798bac3b77aec038b6d394d0a2268e1f8e 100755
--- a/src/ruby/bin/math_client.rb
+++ b/src/ruby/bin/math_client.rb
@@ -40,7 +40,7 @@ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
 $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
 
 require 'grpc'
-require 'math_services'
+require 'math_services_pb'
 require 'optparse'
 
 include GRPC::Core::TimeConsts
diff --git a/src/ruby/bin/math.rb b/src/ruby/bin/math_pb.rb
old mode 100755
new mode 100644
similarity index 100%
rename from src/ruby/bin/math.rb
rename to src/ruby/bin/math_pb.rb
diff --git a/src/ruby/bin/math_server.rb b/src/ruby/bin/math_server.rb
index 1ee4c5632d92e8649bd6e74d104167a0109bb021..751a6ebcab7b9928028519d352053ee17ef4d8a1 100755
--- a/src/ruby/bin/math_server.rb
+++ b/src/ruby/bin/math_server.rb
@@ -42,7 +42,7 @@ $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
 require 'forwardable'
 require 'grpc'
 require 'logger'
-require 'math_services'
+require 'math_services_pb'
 require 'optparse'
 
 # RubyLogger defines a logger for gRPC based on the standard ruby logger.
diff --git a/src/ruby/bin/math_services.rb b/src/ruby/bin/math_services_pb.rb
old mode 100755
new mode 100644
similarity index 99%
rename from src/ruby/bin/math_services.rb
rename to src/ruby/bin/math_services_pb.rb
index 2b97602b6fb41abbda016d39b5a69084a7a06c16..2ba1825d4f4f27635d22327e6b63e2df6d5e5500
--- a/src/ruby/bin/math_services.rb
+++ b/src/ruby/bin/math_services_pb.rb
@@ -32,7 +32,7 @@
 #
 
 require 'grpc'
-require 'math'
+require 'math_pb'
 
 module Math
   module Math
diff --git a/src/ruby/pb/grpc/health/checker.rb b/src/ruby/pb/grpc/health/checker.rb
index f7310d92894733e04e0cc74f54ff3d732ee21fb6..4bce1744c48f2e32c20ea2cdbbfe2041aceadff7 100644
--- a/src/ruby/pb/grpc/health/checker.rb
+++ b/src/ruby/pb/grpc/health/checker.rb
@@ -28,7 +28,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 require 'grpc'
-require 'grpc/health/v1/health_services'
+require 'grpc/health/v1/health_services_pb'
 require 'thread'
 
 module Grpc
diff --git a/src/ruby/pb/grpc/health/v1/health.rb b/src/ruby/pb/grpc/health/v1/health_pb.rb
similarity index 100%
rename from src/ruby/pb/grpc/health/v1/health.rb
rename to src/ruby/pb/grpc/health/v1/health_pb.rb
diff --git a/src/ruby/pb/grpc/health/v1/health_services.rb b/src/ruby/pb/grpc/health/v1/health_services_pb.rb
similarity index 98%
rename from src/ruby/pb/grpc/health/v1/health_services.rb
rename to src/ruby/pb/grpc/health/v1/health_services_pb.rb
index 68a3956f54a09c6f6c0ce40cf0c98810087e05b7..8cc01e91dc5b0058dc178ad8780b043d9056f33d 100644
--- a/src/ruby/pb/grpc/health/v1/health_services.rb
+++ b/src/ruby/pb/grpc/health/v1/health_services_pb.rb
@@ -32,7 +32,7 @@
 #
 
 require 'grpc'
-require 'grpc/health/v1/health'
+require 'grpc/health/v1/health_pb'
 
 module Grpc
   module Health
diff --git a/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services.rb b/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb
similarity index 93%
rename from src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services.rb
rename to src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb
index eb523ffa6f0ac82355b60f5952b5fb145f9cc073..e51c2f087a055d110b766811ec5b1ae1c946a3e7 100644
--- a/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services.rb
+++ b/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb
@@ -1,5 +1,5 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
-# Source: grpc/testing/duplicate/echo_duplicate.proto for package 'grpc.testing.duplicate'
+# Source: src/proto/grpc/testing/duplicate/echo_duplicate.proto for package 'grpc.testing.duplicate'
 # Original file comments:
 # Copyright 2015, Google Inc.
 # All rights reserved.
@@ -34,7 +34,7 @@
 #
 
 require 'grpc'
-require 'grpc/testing/duplicate/echo_duplicate'
+require 'src/proto/grpc/testing/duplicate/echo_duplicate_pb'
 
 module Grpc
   module Testing
diff --git a/src/ruby/pb/grpc/testing/metrics.rb b/src/ruby/pb/grpc/testing/metrics_pb.rb
similarity index 94%
rename from src/ruby/pb/grpc/testing/metrics.rb
rename to src/ruby/pb/grpc/testing/metrics_pb.rb
index 3b3c8cd61bbae356fa14ae003bea2b51901beb0e..77b6c90970fd888caee987632e8a1ed9c0c0d94c 100644
--- a/src/ruby/pb/grpc/testing/metrics.rb
+++ b/src/ruby/pb/grpc/testing/metrics_pb.rb
@@ -1,5 +1,5 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
-# source: grpc/testing/metrics.proto
+# source: src/proto/grpc/testing/metrics.proto
 
 require 'google/protobuf'
 
diff --git a/src/ruby/pb/grpc/testing/metrics_services.rb b/src/ruby/pb/grpc/testing/metrics_services_pb.rb
similarity index 95%
rename from src/ruby/pb/grpc/testing/metrics_services.rb
rename to src/ruby/pb/grpc/testing/metrics_services_pb.rb
index 467b7b3ee509b6df5a51d3bd37b0236178688eda..e46366b1fbeb90325eefac1e4dbf099a04589dba 100644
--- a/src/ruby/pb/grpc/testing/metrics_services.rb
+++ b/src/ruby/pb/grpc/testing/metrics_services_pb.rb
@@ -1,5 +1,5 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
-# Source: grpc/testing/metrics.proto for package 'grpc.testing'
+# Source: src/proto/grpc/testing/metrics.proto for package 'grpc.testing'
 # Original file comments:
 # Copyright 2015-2016, Google Inc.
 # All rights reserved.
@@ -38,7 +38,7 @@
 # service.
 
 require 'grpc'
-require 'grpc/testing/metrics'
+require 'src/proto/grpc/testing/metrics_pb'
 
 module Grpc
   module Testing
diff --git a/src/ruby/pb/src/proto/grpc/testing/empty.rb b/src/ruby/pb/src/proto/grpc/testing/empty_pb.rb
similarity index 100%
rename from src/ruby/pb/src/proto/grpc/testing/empty.rb
rename to src/ruby/pb/src/proto/grpc/testing/empty_pb.rb
diff --git a/src/ruby/pb/src/proto/grpc/testing/messages.rb b/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb
similarity index 100%
rename from src/ruby/pb/src/proto/grpc/testing/messages.rb
rename to src/ruby/pb/src/proto/grpc/testing/messages_pb.rb
diff --git a/src/ruby/pb/src/proto/grpc/testing/test.rb b/src/ruby/pb/src/proto/grpc/testing/test_pb.rb
similarity index 72%
rename from src/ruby/pb/src/proto/grpc/testing/test.rb
rename to src/ruby/pb/src/proto/grpc/testing/test_pb.rb
index 245b5ce00cdc66e273888b96d0980400ecb01152..2cc98630314db4cf638bf8f94f15f98231a8d137 100644
--- a/src/ruby/pb/src/proto/grpc/testing/test.rb
+++ b/src/ruby/pb/src/proto/grpc/testing/test_pb.rb
@@ -3,8 +3,8 @@
 
 require 'google/protobuf'
 
-require 'src/proto/grpc/testing/empty'
-require 'src/proto/grpc/testing/messages'
+require 'src/proto/grpc/testing/empty_pb'
+require 'src/proto/grpc/testing/messages_pb'
 Google::Protobuf::DescriptorPool.generated_pool.build do
 end
 
diff --git a/src/ruby/pb/src/proto/grpc/testing/test_services.rb b/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
similarity index 99%
rename from src/ruby/pb/src/proto/grpc/testing/test_services.rb
rename to src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
index 2652de5e6d255a3cf4247aafc18587cd661a9884..fde328e4c5d2053cf3bc7f58683bb543098991bf 100644
--- a/src/ruby/pb/src/proto/grpc/testing/test_services.rb
+++ b/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
@@ -35,7 +35,7 @@
 #
 
 require 'grpc'
-require 'src/proto/grpc/testing/test'
+require 'src/proto/grpc/testing/test_pb'
 
 module Grpc
   module Testing
diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb
index 4c6d441dcb63623786b8b31ad9a1d61b5658acbc..1e3ae656302da8e8fd8ad1f58b4bf2cd8919e009 100755
--- a/src/ruby/pb/test/client.rb
+++ b/src/ruby/pb/test/client.rb
@@ -52,9 +52,9 @@ require_relative '../../lib/grpc'
 require 'googleauth'
 require 'google/protobuf'
 
-require_relative '../src/proto/grpc/testing/empty'
-require_relative '../src/proto/grpc/testing/messages'
-require_relative '../src/proto/grpc/testing/test_services'
+require_relative '../src/proto/grpc/testing/empty_pb'
+require_relative '../src/proto/grpc/testing/messages_pb'
+require_relative '../src/proto/grpc/testing/test_services_pb'
 
 AUTH_ENV = Google::Auth::CredentialsLoader::ENV_VAR
 
diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb
index 11ee3d465d8d3616215654527f9bd5c2c2880146..0808121661c5bb24693b29ed7de78399eb4e72ad 100755
--- a/src/ruby/pb/test/server.rb
+++ b/src/ruby/pb/test/server.rb
@@ -50,9 +50,9 @@ require 'optparse'
 
 require 'grpc'
 
-require_relative '../src/proto/grpc/testing/empty'
-require_relative '../src/proto/grpc/testing/messages'
-require_relative '../src/proto/grpc/testing/test_services'
+require_relative '../src/proto/grpc/testing/empty_pb'
+require_relative '../src/proto/grpc/testing/messages_pb'
+require_relative '../src/proto/grpc/testing/test_services_pb'
 
 # DebugIsTruncated extends the default Logger to truncate debug messages
 class DebugIsTruncated < Logger
diff --git a/src/ruby/qps/client.rb b/src/ruby/qps/client.rb
index 917b01271e8666b427fd771367ea8e8bfb0f2439..7ed648acefc5682a4180dff42ed8149cf73ca67d 100644
--- a/src/ruby/qps/client.rb
+++ b/src/ruby/qps/client.rb
@@ -38,7 +38,7 @@ $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
 
 require 'grpc'
 require 'histogram'
-require 'src/proto/grpc/testing/services_services'
+require 'src/proto/grpc/testing/services_services_pb'
 
 class Poisson
   def interarrival
diff --git a/src/ruby/qps/server.rb b/src/ruby/qps/server.rb
index 52a89ce847c37a4bb80d302622c53578166665b9..cd98ee1fd9439c9066a59bda06cc0772212f76fc 100644
--- a/src/ruby/qps/server.rb
+++ b/src/ruby/qps/server.rb
@@ -38,9 +38,9 @@ $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
 
 require 'grpc'
 require 'qps-common'
-require 'src/proto/grpc/testing/messages'
-require 'src/proto/grpc/testing/services_services'
-require 'src/proto/grpc/testing/stats'
+require 'src/proto/grpc/testing/messages_pb'
+require 'src/proto/grpc/testing/services_services_pb'
+require 'src/proto/grpc/testing/stats_pb'
 
 class BenchmarkServiceImpl < Grpc::Testing::BenchmarkService::Service
   def unary_call(req, _call)
diff --git a/src/ruby/qps/src/proto/grpc/testing/control.rb b/src/ruby/qps/src/proto/grpc/testing/control_pb.rb
similarity index 97%
rename from src/ruby/qps/src/proto/grpc/testing/control.rb
rename to src/ruby/qps/src/proto/grpc/testing/control_pb.rb
index 958fca320bca8c9d10c92c08f6969d74129f1bb4..02207a2b5d8feec318512b239b6a570809388f3f 100644
--- a/src/ruby/qps/src/proto/grpc/testing/control.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/control_pb.rb
@@ -3,8 +3,8 @@
 
 require 'google/protobuf'
 
-require 'src/proto/grpc/testing/payloads'
-require 'src/proto/grpc/testing/stats'
+require 'src/proto/grpc/testing/payloads_pb'
+require 'src/proto/grpc/testing/stats_pb'
 Google::Protobuf::DescriptorPool.generated_pool.build do
   add_message "grpc.testing.PoissonParams" do
     optional :offered_load, :double, 1
@@ -109,6 +109,8 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
     repeated :server_stats, :message, 4, "grpc.testing.ServerStats"
     repeated :server_cores, :int32, 5
     optional :summary, :message, 6, "grpc.testing.ScenarioResultSummary"
+    repeated :client_success, :bool, 7
+    repeated :server_success, :bool, 8
   end
   add_enum "grpc.testing.ClientType" do
     value :SYNC_CLIENT, 0
diff --git a/src/ruby/qps/src/proto/grpc/testing/messages.rb b/src/ruby/qps/src/proto/grpc/testing/messages_pb.rb
similarity index 100%
rename from src/ruby/qps/src/proto/grpc/testing/messages.rb
rename to src/ruby/qps/src/proto/grpc/testing/messages_pb.rb
diff --git a/src/ruby/qps/src/proto/grpc/testing/payloads.rb b/src/ruby/qps/src/proto/grpc/testing/payloads_pb.rb
similarity index 100%
rename from src/ruby/qps/src/proto/grpc/testing/payloads.rb
rename to src/ruby/qps/src/proto/grpc/testing/payloads_pb.rb
diff --git a/src/ruby/qps/src/proto/grpc/testing/services.rb b/src/ruby/qps/src/proto/grpc/testing/services_pb.rb
similarity index 72%
rename from src/ruby/qps/src/proto/grpc/testing/services.rb
rename to src/ruby/qps/src/proto/grpc/testing/services_pb.rb
index b2675c2afe50c98c3d0842a881b583ee74964e8f..5ce13bf8b04b39677229e2373a08c955d099fb16 100644
--- a/src/ruby/qps/src/proto/grpc/testing/services.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/services_pb.rb
@@ -3,8 +3,8 @@
 
 require 'google/protobuf'
 
-require 'src/proto/grpc/testing/messages'
-require 'src/proto/grpc/testing/control'
+require 'src/proto/grpc/testing/messages_pb'
+require 'src/proto/grpc/testing/control_pb'
 Google::Protobuf::DescriptorPool.generated_pool.build do
 end
 
diff --git a/src/ruby/qps/src/proto/grpc/testing/services_services.rb b/src/ruby/qps/src/proto/grpc/testing/services_services_pb.rb
similarity index 98%
rename from src/ruby/qps/src/proto/grpc/testing/services_services.rb
rename to src/ruby/qps/src/proto/grpc/testing/services_services_pb.rb
index 94b9a1e164e726d7bef4e875eb2b7373a5b722eb..bdbb9c86d0042b85a7e9f8989e9c26e75c420da9 100644
--- a/src/ruby/qps/src/proto/grpc/testing/services_services.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/services_services_pb.rb
@@ -34,7 +34,7 @@
 # of unary/streaming requests/responses.
 
 require 'grpc'
-require 'src/proto/grpc/testing/services'
+require 'src/proto/grpc/testing/services_pb'
 
 module Grpc
   module Testing
diff --git a/src/ruby/qps/src/proto/grpc/testing/stats.rb b/src/ruby/qps/src/proto/grpc/testing/stats_pb.rb
similarity index 100%
rename from src/ruby/qps/src/proto/grpc/testing/stats.rb
rename to src/ruby/qps/src/proto/grpc/testing/stats_pb.rb
diff --git a/src/ruby/qps/worker.rb b/src/ruby/qps/worker.rb
index 665fb863526ba722ab6d000f35f0b14a40e9fec5..12b8087ca0590dc27c419403f3fe7276369cc97f 100755
--- a/src/ruby/qps/worker.rb
+++ b/src/ruby/qps/worker.rb
@@ -44,7 +44,7 @@ require 'facter'
 require 'client'
 require 'qps-common'
 require 'server'
-require 'src/proto/grpc/testing/services_services'
+require 'src/proto/grpc/testing/services_services_pb'
 
 class WorkerServiceImpl < Grpc::Testing::WorkerService::Service
   def cpu_cores
diff --git a/src/ruby/spec/pb/duplicate/codegen_spec.rb b/src/ruby/spec/pb/duplicate/codegen_spec.rb
index 54c136c51023e62ea31be3e63913263cde2ca128..ea0240965caebefa9cbcd8ac1d9b442454db2fad 100644
--- a/src/ruby/spec/pb/duplicate/codegen_spec.rb
+++ b/src/ruby/spec/pb/duplicate/codegen_spec.rb
@@ -44,7 +44,7 @@ describe 'Ping protobuf code generation' do
       # Get the current content
       service_path = File.join(root_dir, 'src', 'ruby', 'pb', 'grpc',
                                'testing', 'duplicate',
-                               'echo_duplicate_services.rb')
+                               'echo_duplicate_services_pb.rb')
       want = nil
       File.open(service_path) { |f| want = f.read }
 
@@ -54,7 +54,7 @@ describe 'Ping protobuf code generation' do
       got = nil
       Dir.mktmpdir do |tmp_dir|
         gen_out = File.join(tmp_dir, 'src', 'proto', 'grpc', 'testing',
-                            'duplicate', 'echo_duplicate_services.rb')
+                            'duplicate', 'echo_duplicate_services_pb.rb')
         pid = spawn(
           'protoc',
           '-I.',
diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb
index de11c9fedf7512590ff29b819f6431e8986dd231..1b2fa9682715fb8f0193b521cdb948fd159ed96b 100644
--- a/src/ruby/spec/pb/health/checker_spec.rb
+++ b/src/ruby/spec/pb/health/checker_spec.rb
@@ -28,7 +28,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 require 'grpc'
-require 'grpc/health/v1/health'
+require 'grpc/health/v1/health_pb'
 require 'grpc/health/checker'
 require 'open3'
 require 'tmpdir'
@@ -43,7 +43,7 @@ describe 'Health protobuf code generation' do
       skip 'protoc || grpc_ruby_plugin missing, cannot verify health code-gen'
     else
       it 'should already be loaded indirectly i.e, used by the other specs' do
-        expect(require('grpc/health/v1/health_services')).to be(false)
+        expect(require('grpc/health/v1/health_services_pb')).to be(false)
       end
 
       it 'should have the same content as created by code generation' do
@@ -52,7 +52,7 @@ describe 'Health protobuf code generation' do
 
         # Get the current content
         service_path = File.join(root_dir, 'ruby', 'pb', 'grpc',
-                                 'health', 'v1', 'health_services.rb')
+                                 'health', 'v1', 'health_services_pb.rb')
         want = nil
         File.open(service_path) { |f| want = f.read }
 
@@ -62,7 +62,7 @@ describe 'Health protobuf code generation' do
         got = nil
         Dir.mktmpdir do |tmp_dir|
           gen_out = File.join(tmp_dir, 'grpc', 'health', 'v1',
-                              'health_services.rb')
+                              'health_services_pb.rb')
           pid = spawn(
             'protoc',
             '-I.',
diff --git a/src/ruby/stress/metrics_server.rb b/src/ruby/stress/metrics_server.rb
index 13638c4d211df470465481f532d518406a3e2f2c..2b7f78577d920c44ea8776976d621414944556d4 100644
--- a/src/ruby/stress/metrics_server.rb
+++ b/src/ruby/stress/metrics_server.rb
@@ -27,8 +27,8 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-require_relative '../pb/grpc/testing/metrics.rb'
-require_relative '../pb/grpc/testing/metrics_services.rb'
+require_relative '../pb/grpc/testing/metrics_pb.rb'
+require_relative '../pb/grpc/testing/metrics_services_pb.rb'
 
 class Gauge
   def get_name
diff --git a/src/ruby/tools/bin/grpc_tools_ruby_protoc.rb b/src/ruby/tools/bin/grpc_tools_ruby_protoc
similarity index 79%
rename from src/ruby/tools/bin/grpc_tools_ruby_protoc.rb
rename to src/ruby/tools/bin/grpc_tools_ruby_protoc
index 3a2a5b8dc967baeac7bab975710e68b3f63a8b03..dab06e7958da8c45ed00f3f380fdb8bf5afb0504 100755
--- a/src/ruby/tools/bin/grpc_tools_ruby_protoc.rb
+++ b/src/ruby/tools/bin/grpc_tools_ruby_protoc
@@ -32,10 +32,17 @@ require 'rbconfig'
 
 require_relative '../os_check'
 
-protoc_name = 'protoc' + RbConfig::CONFIG['EXEEXT']
+ext = RbConfig::CONFIG['EXEEXT']
 
-protoc_path = File.join(File.dirname(__FILE__),
-                        RbConfig::CONFIG['host_cpu'] + '-' + OS.os_name,
-                        protoc_name)
+protoc_name = 'protoc' + ext
 
-exec([ protoc_path, protoc_path ], *ARGV)
+plugin_name = 'grpc_ruby_plugin' + ext
+
+protoc_dir = File.join(File.dirname(__FILE__),
+                       RbConfig::CONFIG['host_cpu'] + '-' + OS.os_name)
+
+protoc_path = File.join(protoc_dir, protoc_name)
+
+plugin_path = File.join(protoc_dir, plugin_name)
+
+exec([ protoc_path, protoc_path ], "--plugin=protoc-gen-grpc=#{plugin_path}", *ARGV)
diff --git a/src/ruby/tools/bin/grpc_tools_ruby_protoc_plugin.rb b/src/ruby/tools/bin/grpc_tools_ruby_protoc_plugin
similarity index 100%
rename from src/ruby/tools/bin/grpc_tools_ruby_protoc_plugin.rb
rename to src/ruby/tools/bin/grpc_tools_ruby_protoc_plugin
diff --git a/src/ruby/tools/grpc-tools.gemspec b/src/ruby/tools/grpc-tools.gemspec
index 9fa4b66392d9d78c3f4113c3b4295e6208680b2c..68e2a7a1133e314b0ba4923b663ced55ac7910d4 100644
--- a/src/ruby/tools/grpc-tools.gemspec
+++ b/src/ruby/tools/grpc-tools.gemspec
@@ -18,5 +18,5 @@ Gem::Specification.new do |s|
 
   s.platform = Gem::Platform::RUBY
 
-  s.executables = %w( grpc_tools_ruby_protoc.rb grpc_tools_ruby_protoc_plugin.rb )
+  s.executables = %w( grpc_tools_ruby_protoc grpc_tools_ruby_protoc_plugin )
 end
diff --git a/templates/CMakeLists.txt.template b/templates/CMakeLists.txt.template
index 52e8b866be1b6a645349101fbd0b03a3a712cf34..4e4223493b9ab8dffe96b68fa6089a981ce71489 100644
--- a/templates/CMakeLists.txt.template
+++ b/templates/CMakeLists.txt.template
@@ -42,14 +42,16 @@
   <%!
   def get_deps(target_dict):
     deps = []
+    if target_dict.get('baselib', False):
+      deps.append("${_gRPC_BASELIB_LIBRARIES}")
     if target_dict.get('build', None) in ['protoc']:
-      deps.append("libprotoc")
+      deps.append("${_gRPC_PROTOBUF_PROTOC_LIBRARIES}")
     if target_dict.get('secure', False):
-      deps = ["ssl"]
+      deps.append("${_gRPC_SSL_LIBRARIES}")
     if target_dict['name'] in ['grpc++', 'grpc++_unsecure', 'grpc++_codegen_lib']:
-      deps.append("libprotobuf")
+      deps.append("${_gRPC_PROTOBUF_LIBRARIES}")
     elif target_dict['name'] in ['grpc']:
-      deps.append("zlibstatic")
+      deps.append("${_gRPC_ZLIB_LIBRARIES}")
     for d in target_dict.get('deps', []):
       deps.append(d)
     return deps
@@ -64,39 +66,127 @@
   set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
   project(<%text>${PACKAGE_NAME}</%text> C CXX)
 
-  if(NOT BORINGSSL_ROOT_DIR)
-    set(BORINGSSL_ROOT_DIR <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/third_party/boringssl)
+  set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "Provider of zlib library")
+  set_property(CACHE gRPC_ZLIB_PROVIDER PROPERTY STRINGS "module" "package")
+
+  set(gRPC_SSL_PROVIDER "module" CACHE STRING "Provider of ssl library")
+  set_property(CACHE gRPC_SSL_PROVIDER PROPERTY STRINGS "module" "package")
+
+  set(gRPC_PROTOBUF_PROVIDER "module" CACHE STRING "Provider of protobuf library")
+  set_property(CACHE gRPC_PROTOBUF_PROVIDER PROPERTY STRINGS "module" "package")
+
+  set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library")
+
+  if (gRPC_USE_PROTO_LITE)
+    set(_gRPC_PROTOBUF_LIBRARY_NAME "libprotobuf-lite")
+    add_definitions("-DGRPC_USE_PROTO_LITE")
+  else()
+    set(_gRPC_PROTOBUF_LIBRARY_NAME "libprotobuf")
+  endif()
+
+  if("<%text>${gRPC_ZLIB_PROVIDER}</%text>" STREQUAL "module")
+    if(NOT ZLIB_ROOT_DIR)
+      set(ZLIB_ROOT_DIR <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/third_party/zlib)
+    endif()
+    set(ZLIB_INCLUDE_DIR "<%text>${ZLIB_ROOT_DIR}</%text>")
+    if(EXISTS "<%text>${ZLIB_ROOT_DIR}</%text>/CMakeLists.txt")
+        add_subdirectory(<%text>${ZLIB_ROOT_DIR}</%text> third_party/zlib)
+        if(TARGET zlibstatic)
+            set(_gRPC_ZLIB_LIBRARIES zlibstatic)
+        endif()
+    else()
+        message(WARNING "gRPC_ZLIB_PROVIDER is \"module\" but ZLIB_ROOT_DIR is wrong")
+    endif()
+  elseif("<%text>${gRPC_ZLIB_PROVIDER}</%text>" STREQUAL "package")
+    find_package(ZLIB)
+    if(TARGET ZLIB::ZLIB)
+      set(_gRPC_ZLIB_LIBRARIES ZLIB::ZLIB)
+    endif()
+    set(_gRPC_FIND_ZLIB "if(NOT ZLIB_FOUND)\n  find_package(ZLIB)\nendif()")
   endif()
-  if(NOT PROTOBUF_ROOT_DIR)
-    set(PROTOBUF_ROOT_DIR <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/third_party/protobuf)
+
+  if("<%text>${gRPC_PROTOBUF_PROVIDER}</%text>" STREQUAL "module")
+    # Building the protobuf tests require gmock what is not part of a standard protobuf checkout.
+    # Disable them unless they are explicitly requested from the cmake command line (when we assume
+    # gmock is downloaded to the right location inside protobuf).
+    if(NOT protobuf_BUILD_TESTS)
+      set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests")
+    endif()
+    if(NOT PROTOBUF_ROOT_DIR)
+      set(PROTOBUF_ROOT_DIR <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/third_party/protobuf)
+    endif()
+    if(EXISTS "<%text>${PROTOBUF_ROOT_DIR}</%text>/cmake/CMakeLists.txt")
+      add_subdirectory(<%text>${PROTOBUF_ROOT_DIR}</%text>/cmake third_party/protobuf)
+      if(TARGET <%text>${_gRPC_PROTOBUF_LIBRARY_NAME}</%text>)
+        set(_gRPC_PROTOBUF_LIBRARIES <%text>${_gRPC_PROTOBUF_LIBRARY_NAME}</%text>)
+      endif()
+      if(TARGET libprotoc)
+        set(_gRPC_PROTOBUF_PROTOC_LIBRARIES libprotoc)
+      endif()
+    else()
+        message(WARNING "gRPC_PROTOBUF_PROVIDER is \"module\" but PROTOBUF_ROOT_DIR is wrong")
+    endif()
+  elseif("<%text>${gRPC_PROTOBUF_PROVIDER}</%text>" STREQUAL "package")
+    find_package(protobuf CONFIG)
+    if(protobuf_FOUND)
+      if(TARGET protobuf::<%text>${_gRPC_PROTOBUF_LIBRARY_NAME}</%text>)
+        set(_gRPC_PROTOBUF_LIBRARIES protobuf::<%text>${_gRPC_PROTOBUF_LIBRARY_NAME}</%text>)
+      endif()
+      if(TARGET protobuf::libprotoc)
+        set(_gRPC_PROTOBUF_PROTOC_LIBRARIES protobuf::libprotoc)
+      endif()
+      set(_gRPC_FIND_PROTOBUF "if(NOT protobuf_FOUND)\n  find_package(protobuf CONFIG)\nendif()")
+    else()
+      find_package(Protobuf MODULE)
+      set(_gRPC_FIND_PROTOBUF "if(NOT Protobuf_FOUND)\n  find_package(Protobuf)\nendif()")
+    endif()
   endif()
-  if(NOT ZLIB_ROOT_DIR)
-    set(ZLIB_ROOT_DIR <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/third_party/zlib)
+
+  if("<%text>${gRPC_SSL_PROVIDER}</%text>" STREQUAL "module")
+    if(NOT BORINGSSL_ROOT_DIR)
+      set(BORINGSSL_ROOT_DIR <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/third_party/boringssl)
+    endif()
+    if(EXISTS "<%text>${BORINGSSL_ROOT_DIR}</%text>/CMakeLists.txt")
+      add_subdirectory(<%text>${BORINGSSL_ROOT_DIR}</%text> third_party/boringssl)
+      if(TARGET ssl)
+        set(_gRPC_SSL_LIBRARIES ssl)
+      endif()
+    else()
+        message(WARNING "gRPC_SSL_PROVIDER is \"module\" but BORINGSSL_ROOT_DIR is wrong")
+    endif()
+  elseif("<%text>${gRPC_SSL_PROVIDER}</%text>" STREQUAL "package")
+    find_package(OpenSSL)
+    if(TARGET OpenSSL::SSL)
+      set(_gRPC_SSL_LIBRARIES OpenSSL::SSL)
+    endif()
+    set(_gRPC_FIND_SSL "if(NOT OpenSSL_FOUND)\n  find_package(OpenSSL)\nendif()")
   endif()
 
-  # Building the protobuf tests require gmock what is not part of a standard protobuf checkout.
-  # Disable them unless they are explicitly requested from the cmake command line (when we assume
-  # gmock is downloaded to the right location inside protobuf).
-  if(NOT protobuf_BUILD_TESTS)
-    set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests")
+  if(NOT MSVC)
+    set(CMAKE_C_FLAGS   "<%text>${CMAKE_C_FLAGS}</%text>   -std=c11")
+    set(CMAKE_CXX_FLAGS "<%text>${CMAKE_CXX_FLAGS}</%text> -std=c++11")
   endif()
 
-  add_subdirectory(<%text>${BORINGSSL_ROOT_DIR}</%text> third_party/boringssl)
-  add_subdirectory(<%text>${PROTOBUF_ROOT_DIR}</%text>/cmake third_party/protobuf)
-  add_subdirectory(<%text>${ZLIB_ROOT_DIR}</%text> third_party/zlib)
+  if(WIN32 AND MSVC)
+    set(_gRPC_BASELIB_LIBRARIES wsock32 ws2_32)
+  endif()
 
-  set(CMAKE_C_FLAGS   "<%text>${CMAKE_C_FLAGS}</%text>   -std=c11")
-  set(CMAKE_CXX_FLAGS "<%text>${CMAKE_CXX_FLAGS}</%text> -std=c++11")
+  include(GNUInstallDirs)
+  if(NOT DEFINED CMAKE_INSTALL_CMAKEDIR)
+    set(CMAKE_INSTALL_CMAKEDIR "<%text>${CMAKE_INSTALL_LIBDIR}</%text>/cmake/gRPC")
+  endif()
 
   % for lib in libs:
   % if lib.build in ["all", "protoc", "tool"]:
     ${cc_library(lib)}
+    ${cc_install(lib)}
   % endif
   % endfor
 
   % for tgt in targets:
   % if tgt.build in ["all", "protoc", "tool"]:
   ${cc_binary(tgt)}
+  ${cc_install(tgt)}
   % endif
   % endfor
 
@@ -112,7 +202,7 @@
     PRIVATE <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/include
     PRIVATE <%text>${BORINGSSL_ROOT_DIR}</%text>/include
     PRIVATE <%text>${PROTOBUF_ROOT_DIR}</%text>/src
-    PRIVATE <%text>${ZLIB_ROOT_DIR}</%text>
+    PRIVATE <%text>${ZLIB_INCLUDE_DIR}</%text>
     PRIVATE <%text>${CMAKE_CURRENT_BINARY_DIR}</%text>/third_party/zlib
   )
 
@@ -123,6 +213,20 @@
   % endfor
   )
   % endif
+
+  % if len(lib.get('public_headers', [])) > 0:
+  foreach(_hdr
+  % for hdr in lib.get('public_headers', []):
+    ${hdr}
+  % endfor
+  )
+    string(REPLACE "include/" "" _path <%text>${_hdr}</%text>)
+    get_filename_component(_path <%text>${_path}</%text> PATH)
+    install(FILES <%text>${_hdr}</%text>
+      DESTINATION "<%text>${CMAKE_INSTALL_INCLUDEDIR}/${_path}</%text>"
+    )
+  endforeach()
+  % endif
   </%def>
 
   <%def name="cc_binary(tgt)">
@@ -150,3 +254,23 @@
   % endif
   </%def>
 
+  <%def name="cc_install(tgt)">
+  install(TARGETS ${tgt.name} EXPORT gRPCTargets
+    RUNTIME DESTINATION <%text>${CMAKE_INSTALL_BINDIR}</%text>
+    LIBRARY DESTINATION <%text>${CMAKE_INSTALL_LIBDIR}</%text>
+    ARCHIVE DESTINATION <%text>${CMAKE_INSTALL_LIBDIR}</%text>
+  )
+  </%def>
+
+  install(EXPORT gRPCTargets
+    DESTINATION <%text>${CMAKE_INSTALL_CMAKEDIR}</%text>
+    NAMESPACE gRPC::
+  )
+
+  foreach(_config gRPCConfig gRPCConfigVersion)
+    configure_file(tools/cmake/<%text>${_config}</%text>.cmake.in
+      <%text>${_config}</%text>.cmake @ONLY)
+    install(FILES <%text>${CMAKE_CURRENT_BINARY_DIR}/${_config}</%text>.cmake
+      DESTINATION <%text>${CMAKE_INSTALL_CMAKEDIR}</%text>
+    )
+  endforeach()
diff --git a/templates/package.xml.template b/templates/package.xml.template
index 76c6fff7a7cfc0a6a5ba54f85cfb96e83f41cfef..87b1038959875e14d98d59a7a499ce0f9e1e1b20 100644
--- a/templates/package.xml.template
+++ b/templates/package.xml.template
@@ -12,7 +12,7 @@
     <email>grpc-packages@google.com</email>
     <active>yes</active>
    </lead>
-   <date>2016-07-21</date>
+   <date>2016-07-28</date>
    <time>16:06:07</time>
    <version>
     <release>${settings.php_version.php()}</release>
@@ -24,7 +24,7 @@
    </stability>
    <license>BSD</license>
    <notes>
-  - PHP7 Support #7464
+  - PHP7 Support continued, reduce code duplication #7543
    </notes>
    <contents>
     <dir baseinstalldir="/" name="/">
@@ -234,5 +234,20 @@
   - PHP7 Support #7464
      </notes>
     </release>
+    <release>
+     <version>
+      <release>1.0.0RC3</release>
+      <api>1.0.0RC3</api>
+     </version>
+     <stability>
+      <release>stable</release>
+      <api>stable</api>
+     </stability>
+     <date>2016-07-28</date>
+     <license>BSD</license>
+     <notes>
+  - PHP7 Support continued, reduce code duplication #7543
+     </notes>
+    </release>
    </changelog>
   </package>
diff --git a/templates/src/csharp/Grpc.Core.Tests/project.json.template b/templates/src/csharp/Grpc.Core.Tests/project.json.template
index bc9fa3e63a9b5d381c08f31fe7ed79f849546416..d1ab9316f61e92046165db79c66d9a3377a74500 100644
--- a/templates/src/csharp/Grpc.Core.Tests/project.json.template
+++ b/templates/src/csharp/Grpc.Core.Tests/project.json.template
@@ -8,7 +8,10 @@
       },
       "Newtonsoft.Json": "8.0.3",
       "NUnit": "3.2.0",
-      "NUnitLite": "3.2.0-*"
+      "NUnitLite": "3.2.0-*",
+      "NUnit.ConsoleRunner": "3.2.0",
+      "OpenCover": "4.6.519",
+      "ReportGenerator": "2.4.4.0"
     },
     "frameworks": {
       "net45": { },
diff --git a/templates/src/node/health_check/package.json.template b/templates/src/node/health_check/package.json.template
index 1248ced1e16da04b2be1bca228c03cde5c4199a7..96b9748aa74a129d227107242b338057a2d9c454 100644
--- a/templates/src/node/health_check/package.json.template
+++ b/templates/src/node/health_check/package.json.template
@@ -21,11 +21,11 @@
       "lodash": "^3.9.3",
       "google-protobuf": "^3.0.0-alpha.5"
     },
-    "files": {
+    "files": [
       "LICENSE",
       "health.js",
       "v1"
-    },
+    ],
     "main": "src/node/index.js",
     "license": "BSD-3-Clause"
   }
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index f9561bed707787ed7120aa1bd65b689e5292de7e..806fd0a6cc1ac671d4a7c25a9d496811ec3d4c43 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -63,7 +63,8 @@ static void channel_destroy_func(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {}
 
 static void call_destroy_func(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   ++*(int *)(elem->channel_data);
 }
 
diff --git a/test/core/end2end/end2end_nosec_tests.c b/test/core/end2end/end2end_nosec_tests.c
index 03e55f1181221ee69c87951760f43c0b6f88855c..59ef6ba997444895bf10c9516d4e6c91c8045533 100644
--- a/test/core/end2end/end2end_nosec_tests.c
+++ b/test/core/end2end/end2end_nosec_tests.c
@@ -83,6 +83,8 @@ extern void invoke_large_request(grpc_end2end_test_config config);
 extern void invoke_large_request_pre_init(void);
 extern void large_metadata(grpc_end2end_test_config config);
 extern void large_metadata_pre_init(void);
+extern void load_reporting_hook(grpc_end2end_test_config config);
+extern void load_reporting_hook_pre_init(void);
 extern void max_concurrent_streams(grpc_end2end_test_config config);
 extern void max_concurrent_streams_pre_init(void);
 extern void max_message_length(grpc_end2end_test_config config);
@@ -145,6 +147,7 @@ void grpc_end2end_tests_pre_init(void) {
   idempotent_request_pre_init();
   invoke_large_request_pre_init();
   large_metadata_pre_init();
+  load_reporting_hook_pre_init();
   max_concurrent_streams_pre_init();
   max_message_length_pre_init();
   negative_deadline_pre_init();
@@ -193,6 +196,7 @@ void grpc_end2end_tests(int argc, char **argv,
     idempotent_request(config);
     invoke_large_request(config);
     large_metadata(config);
+    load_reporting_hook(config);
     max_concurrent_streams(config);
     max_message_length(config);
     negative_deadline(config);
@@ -296,6 +300,10 @@ void grpc_end2end_tests(int argc, char **argv,
       large_metadata(config);
       continue;
     }
+    if (0 == strcmp("load_reporting_hook", argv[i])) {
+      load_reporting_hook(config);
+      continue;
+    }
     if (0 == strcmp("max_concurrent_streams", argv[i])) {
       max_concurrent_streams(config);
       continue;
diff --git a/test/core/end2end/end2end_tests.c b/test/core/end2end/end2end_tests.c
index 877b1b198930118ce2b36a33062a7384dcbfa6db..cbc7c3c0e98496125d09cdd06cc610485af2867d 100644
--- a/test/core/end2end/end2end_tests.c
+++ b/test/core/end2end/end2end_tests.c
@@ -85,6 +85,8 @@ extern void invoke_large_request(grpc_end2end_test_config config);
 extern void invoke_large_request_pre_init(void);
 extern void large_metadata(grpc_end2end_test_config config);
 extern void large_metadata_pre_init(void);
+extern void load_reporting_hook(grpc_end2end_test_config config);
+extern void load_reporting_hook_pre_init(void);
 extern void max_concurrent_streams(grpc_end2end_test_config config);
 extern void max_concurrent_streams_pre_init(void);
 extern void max_message_length(grpc_end2end_test_config config);
@@ -148,6 +150,7 @@ void grpc_end2end_tests_pre_init(void) {
   idempotent_request_pre_init();
   invoke_large_request_pre_init();
   large_metadata_pre_init();
+  load_reporting_hook_pre_init();
   max_concurrent_streams_pre_init();
   max_message_length_pre_init();
   negative_deadline_pre_init();
@@ -197,6 +200,7 @@ void grpc_end2end_tests(int argc, char **argv,
     idempotent_request(config);
     invoke_large_request(config);
     large_metadata(config);
+    load_reporting_hook(config);
     max_concurrent_streams(config);
     max_message_length(config);
     negative_deadline(config);
@@ -304,6 +308,10 @@ void grpc_end2end_tests(int argc, char **argv,
       large_metadata(config);
       continue;
     }
+    if (0 == strcmp("load_reporting_hook", argv[i])) {
+      load_reporting_hook(config);
+      continue;
+    }
     if (0 == strcmp("max_concurrent_streams", argv[i])) {
       max_concurrent_streams(config);
       continue;
diff --git a/test/core/end2end/fixtures/h2_loadreporting.c b/test/core/end2end/fixtures/h2_load_reporting.c
similarity index 52%
rename from test/core/end2end/fixtures/h2_loadreporting.c
rename to test/core/end2end/fixtures/h2_load_reporting.c
index 4ed02f972868a52fd3ce60dd6fadd2753c4b7737..f6d3923db94d6f59ed09e215e678c07ae90f9882 100644
--- a/test/core/end2end/fixtures/h2_loadreporting.c
+++ b/test/core/end2end/fixtures/h2_load_reporting.c
@@ -52,18 +52,16 @@
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
-static grpc_load_reporting_config *g_client_lrc;
-static grpc_load_reporting_config *g_server_lrc;
-
-typedef struct fullstack_fixture_data {
+typedef struct load_reporting_fixture_data {
   char *localaddr;
-} fullstack_fixture_data;
+} load_reporting_fixture_data;
 
-static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
+static grpc_end2end_test_fixture chttp2_create_fixture_load_reporting(
     grpc_channel_args *client_args, grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
   int port = grpc_pick_unused_port_or_die();
-  fullstack_fixture_data *ffd = gpr_malloc(sizeof(fullstack_fixture_data));
+  load_reporting_fixture_data *ffd =
+      gpr_malloc(sizeof(load_reporting_fixture_data));
   memset(&f, 0, sizeof(f));
 
   gpr_join_host_port(&ffd->localaddr, "localhost", port);
@@ -74,47 +72,20 @@ static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
   return f;
 }
 
-typedef struct {
-  int64_t total_bytes;
-  bool fully_processed;
-  uint32_t initial_token;
-  uint32_t final_token;
-} aggregated_bw_stats;
-
-static void sample_fn(const grpc_load_reporting_call_data *call_data,
-                      void *user_data) {
-  GPR_ASSERT(user_data != NULL);
-  aggregated_bw_stats *custom_stats = (aggregated_bw_stats *)user_data;
-  if (call_data == NULL) {
-    /* initial invocation */
-    custom_stats->initial_token = 0xDEADBEEF;
-  } else {
-    /* final invocation */
-    custom_stats->total_bytes =
-        (int64_t)(call_data->stats->transport_stream_stats.outgoing.data_bytes +
-                  call_data->stats->transport_stream_stats.incoming.data_bytes);
-    custom_stats->final_token = 0xCAFED00D;
-    custom_stats->fully_processed = true;
-  }
-}
-
-void chttp2_init_client_fullstack(grpc_end2end_test_fixture *f,
-                                  grpc_channel_args *client_args) {
-  fullstack_fixture_data *ffd = f->fixture_data;
-  grpc_arg arg = grpc_load_reporting_config_create_arg(g_client_lrc);
-  client_args = grpc_channel_args_copy_and_add(client_args, &arg, 1);
+void chttp2_init_client_load_reporting(grpc_end2end_test_fixture *f,
+                                       grpc_channel_args *client_args) {
+  load_reporting_fixture_data *ffd = f->fixture_data;
   f->client = grpc_insecure_channel_create(ffd->localaddr, client_args, NULL);
-  grpc_channel_args_destroy(client_args);
   GPR_ASSERT(f->client);
 }
 
-void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f,
-                                  grpc_channel_args *server_args) {
-  fullstack_fixture_data *ffd = f->fixture_data;
+void chttp2_init_server_load_reporting(grpc_end2end_test_fixture *f,
+                                       grpc_channel_args *server_args) {
+  load_reporting_fixture_data *ffd = f->fixture_data;
+  grpc_arg arg = grpc_load_reporting_enable_arg();
   if (f->server) {
     grpc_server_destroy(f->server);
   }
-  grpc_arg arg = grpc_load_reporting_config_create_arg(g_server_lrc);
   server_args = grpc_channel_args_copy_and_add(server_args, &arg, 1);
   f->server = grpc_server_create(server_args, NULL);
   grpc_channel_args_destroy(server_args);
@@ -123,36 +94,23 @@ void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f,
   grpc_server_start(f->server);
 }
 
-void chttp2_tear_down_fullstack(grpc_end2end_test_fixture *f) {
-  fullstack_fixture_data *ffd = f->fixture_data;
+void chttp2_tear_down_load_reporting(grpc_end2end_test_fixture *f) {
+  load_reporting_fixture_data *ffd = f->fixture_data;
   gpr_free(ffd->localaddr);
   gpr_free(ffd);
 }
 
 /* All test configurations */
 static grpc_end2end_test_config configs[] = {
-    {"chttp2/fullstack+loadreporting", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
-     chttp2_create_fixture_fullstack, chttp2_init_client_fullstack,
-     chttp2_init_server_fullstack, chttp2_tear_down_fullstack},
+    {"chttp2/fullstack+load_reporting",
+     FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+     chttp2_create_fixture_load_reporting, chttp2_init_client_load_reporting,
+     chttp2_init_server_load_reporting, chttp2_tear_down_load_reporting},
 };
 
 int main(int argc, char **argv) {
   size_t i;
 
-  aggregated_bw_stats *aggr_stats_client =
-      gpr_malloc(sizeof(aggregated_bw_stats));
-  aggr_stats_client->total_bytes = -1;
-  aggr_stats_client->fully_processed = false;
-  aggregated_bw_stats *aggr_stats_server =
-      gpr_malloc(sizeof(aggregated_bw_stats));
-  aggr_stats_server->total_bytes = -1;
-  aggr_stats_server->fully_processed = false;
-
-  g_client_lrc =
-      grpc_load_reporting_config_create(sample_fn, aggr_stats_client);
-  g_server_lrc =
-      grpc_load_reporting_config_create(sample_fn, aggr_stats_server);
-
   grpc_test_init(argc, argv);
   grpc_end2end_tests_pre_init();
   grpc_init();
@@ -163,22 +121,5 @@ int main(int argc, char **argv) {
 
   grpc_shutdown();
 
-  grpc_load_reporting_config_destroy(g_client_lrc);
-  grpc_load_reporting_config_destroy(g_server_lrc);
-
-  if (aggr_stats_client->fully_processed) {
-    GPR_ASSERT(aggr_stats_client->total_bytes >= 0);
-    GPR_ASSERT(aggr_stats_client->initial_token == 0xDEADBEEF);
-    GPR_ASSERT(aggr_stats_client->final_token == 0xCAFED00D);
-  }
-  if (aggr_stats_server->fully_processed) {
-    GPR_ASSERT(aggr_stats_server->total_bytes >= 0);
-    GPR_ASSERT(aggr_stats_server->initial_token == 0xDEADBEEF);
-    GPR_ASSERT(aggr_stats_server->final_token == 0xCAFED00D);
-  }
-
-  gpr_free(aggr_stats_client);
-  gpr_free(aggr_stats_server);
-
   return 0;
 }
diff --git a/test/core/end2end/fuzzers/hpack.dictionary b/test/core/end2end/fuzzers/hpack.dictionary
index 097e9a89224fe4dcd8cbcd19a34ed61c22edaa99..af075c09ef149de67031d13a620bc66ddf58713d 100644
--- a/test/core/end2end/fuzzers/hpack.dictionary
+++ b/test/core/end2end/fuzzers/hpack.dictionary
@@ -63,7 +63,8 @@
 "\x13if-unmodified-since"
 "\x0Dlast-modified"
 "\x04link"
-"\x0Eload-reporting"
+"\x16load-reporting-initial"
+"\x17load-reporting-trailing"
 "\x08location"
 "\x0Cmax-forwards"
 "\x07:method"
@@ -137,7 +138,8 @@
 "\x00\x13if-unmodified-since\x00"
 "\x00\x0Dlast-modified\x00"
 "\x00\x04link\x00"
-"\x00\x0Eload-reporting\x00"
+"\x00\x16load-reporting-initial\x00"
+"\x00\x17load-reporting-trailing\x00"
 "\x00\x08location\x00"
 "\x00\x0Cmax-forwards\x00"
 "\x00\x07:method\x03GET"
diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py
index fb7275474d394d7edba25d80edffbfee58451cb5..1c948ba24a9fdb9bace22eed201ae97a4408c7b0 100755
--- a/test/core/end2end/gen_build_yaml.py
+++ b/test/core/end2end/gen_build_yaml.py
@@ -53,13 +53,13 @@ fd_unsecure_fixture_options = default_unsecure_fixture_options._replace(
 END2END_FIXTURES = {
     'h2_compress': default_unsecure_fixture_options,
     'h2_census': default_unsecure_fixture_options,
+    'h2_load_reporting': default_unsecure_fixture_options,
     'h2_fakesec': default_secure_fixture_options._replace(ci_mac=False),
     'h2_fd': fd_unsecure_fixture_options,
     'h2_full': default_unsecure_fixture_options,
     'h2_full+pipe': default_unsecure_fixture_options._replace(
         platforms=['linux']),
     'h2_full+trace': default_unsecure_fixture_options._replace(tracing=True),
-    'h2_loadreporting': default_unsecure_fixture_options,
     'h2_oauth2': default_secure_fixture_options._replace(ci_mac=False),
     'h2_proxy': default_unsecure_fixture_options._replace(includes_proxy=True,
                                                           ci_mac=False),
@@ -115,6 +115,7 @@ END2END_TESTS = {
     'network_status_change': default_test_options,
     'no_op': default_test_options,
     'payload': default_test_options,
+    'load_reporting_hook': default_test_options,
     'ping_pong_streaming': default_test_options,
     'ping': connectivity_test_options._replace(proxyable=False),
     'registered_call': default_test_options,
diff --git a/test/core/end2end/tests/filter_causes_close.c b/test/core/end2end/tests/filter_causes_close.c
index 526c05ca3e8c7cf01e5faffbf44bafb07ce11f68..5a4803bcdcaf52bf238dafc1e06cf319769b83dc 100644
--- a/test/core/end2end/tests/filter_causes_close.c
+++ b/test/core/end2end/tests/filter_causes_close.c
@@ -237,7 +237,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                            grpc_call_element_args *args) {}
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
+                              const grpc_call_final_info *final_info,
                               void *and_free_memory) {}
 
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/test/core/end2end/tests/load_reporting_hook.c b/test/core/end2end/tests/load_reporting_hook.c
new file mode 100644
index 0000000000000000000000000000000000000000..2c6519881aa6516dcb9a52d3b0532086e3e17f15
--- /dev/null
+++ b/test/core/end2end/tests/load_reporting_hook.c
@@ -0,0 +1,321 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+#include "src/core/ext/load_reporting/load_reporting.h"
+#include "src/core/ext/load_reporting/load_reporting_filter.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+typedef struct {
+  gpr_mu mu;
+  intptr_t channel_id;
+  intptr_t call_id;
+
+  char *initial_md_str;
+  char *trailing_md_str;
+  char *method_name;
+
+  uint64_t incoming_bytes;
+  uint64_t outgoing_bytes;
+
+  grpc_status_code call_final_status;
+
+  bool fully_processed;
+} load_reporting_data;
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(
+                 f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+}
+
+static void request_response_with_payload(grpc_end2end_test_fixture f,
+                                          const char *method_name,
+                                          const char *request_msg,
+                                          const char *response_msg,
+                                          grpc_metadata *initial_lr_metadata,
+                                          grpc_metadata *trailing_lr_metadata) {
+  gpr_slice request_payload_slice = gpr_slice_from_static_string(request_msg);
+  gpr_slice response_payload_slice = gpr_slice_from_static_string(response_msg);
+  grpc_call *c;
+  grpc_call *s;
+  grpc_byte_buffer *request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer *response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  gpr_timespec deadline = five_seconds_time();
+  cq_verifier *cqv = cq_verifier_create(f.cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_byte_buffer *request_payload_recv = NULL;
+  grpc_byte_buffer *response_payload_recv = NULL;
+  grpc_call_details call_details;
+  grpc_status_code status;
+  grpc_call_error error;
+  char *details = NULL;
+  size_t details_capacity = 0;
+  int was_cancelled = 2;
+
+  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+                               method_name, "foo.test.google.fr", deadline,
+                               NULL);
+  GPR_ASSERT(c);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  GPR_ASSERT(initial_lr_metadata != NULL);
+  op->data.send_initial_metadata.count = 1;
+  op->data.send_initial_metadata.metadata = initial_lr_metadata;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = request_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &response_payload_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  cq_expect_completion(cqv, tag(101), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &request_payload_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  cq_expect_completion(cqv, tag(102), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = response_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  GPR_ASSERT(trailing_lr_metadata != NULL);
+  op->data.send_status_from_server.trailing_metadata_count = 1;
+  op->data.send_status_from_server.trailing_metadata = trailing_lr_metadata;
+  op->data.send_status_from_server.status = GRPC_STATUS_OK;
+  op->data.send_status_from_server.status_details = "xyz";
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  cq_expect_completion(cqv, tag(103), 1);
+  cq_expect_completion(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+
+  gpr_free(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+
+  grpc_call_destroy(c);
+  grpc_call_destroy(s);
+
+  cq_verifier_destroy(cqv);
+
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+}
+
+/* override the default for testing purposes */
+extern void (*g_load_reporting_fn)(
+    const grpc_load_reporting_call_data *call_data);
+
+static void test_load_reporting_hook(grpc_end2end_test_config config) {
+  /* TODO(dgq): this test is currently a noop until LR is fully defined.
+   * Leaving the rest here, as it'll likely be reusable. */
+
+  /* Introduce load reporting for the server through its arguments */
+  grpc_arg arg = grpc_load_reporting_enable_arg();
+  grpc_channel_args *lr_server_args =
+      grpc_channel_args_copy_and_add(NULL, &arg, 1);
+
+  grpc_end2end_test_fixture f =
+      begin_test(config, "test_load_reporting_hook", NULL, lr_server_args);
+
+  const char *method_name = "/gRPCFTW";
+  const char *request_msg = "the msg from the client";
+  const char *response_msg = "... and the response from the server";
+
+  grpc_metadata initial_lr_metadata;
+  grpc_metadata trailing_lr_metadata;
+
+  initial_lr_metadata.key = GRPC_LOAD_REPORTING_INITIAL_MD_KEY;
+  initial_lr_metadata.value = "client-token";
+  initial_lr_metadata.value_length = strlen(initial_lr_metadata.value);
+  memset(&initial_lr_metadata.internal_data, 0,
+         sizeof(initial_lr_metadata.internal_data));
+
+  trailing_lr_metadata.key = GRPC_LOAD_REPORTING_TRAILING_MD_KEY;
+  trailing_lr_metadata.value = "server-token";
+  trailing_lr_metadata.value_length = strlen(trailing_lr_metadata.value);
+  memset(&trailing_lr_metadata.internal_data, 0,
+         sizeof(trailing_lr_metadata.internal_data));
+
+  request_response_with_payload(f, method_name, request_msg, response_msg,
+                                &initial_lr_metadata, &trailing_lr_metadata);
+  end_test(&f);
+  grpc_channel_args_destroy(lr_server_args);
+  config.tear_down_data(&f);
+}
+
+void load_reporting_hook(grpc_end2end_test_config config) {
+  test_load_reporting_hook(config);
+}
+
+void load_reporting_hook_pre_init(void) {}
diff --git a/test/cpp/end2end/server_builder_plugin_test.cc b/test/cpp/end2end/server_builder_plugin_test.cc
index 778a2be573ea7f6609d1b6e15e089e82a83382ee..b967a5d1e99a551fd7d93210f8472a26a3298e83 100644
--- a/test/cpp/end2end/server_builder_plugin_test.cc
+++ b/test/cpp/end2end/server_builder_plugin_test.cc
@@ -191,7 +191,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
     // we run some tests without a service, and for those we need to supply a
     // frequently polled completion queue
     cq_ = builder_->AddCompletionQueue();
-    cq_thread_ = grpc::thread(std::bind(&ServerBuilderPluginTest::RunCQ, this));
+    cq_thread_ = new grpc::thread(&ServerBuilderPluginTest::RunCQ, this);
     server_ = builder_->BuildAndStart();
     EXPECT_TRUE(CheckPresent());
   }
@@ -209,7 +209,8 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
     EXPECT_TRUE(plugin->finish_is_called());
     server_->Shutdown();
     cq_->Shutdown();
-    cq_thread_.join();
+    cq_thread_->join();
+    delete cq_thread_;
   }
 
   string to_string(const int number) {
@@ -224,7 +225,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
   std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<ServerCompletionQueue> cq_;
   std::unique_ptr<Server> server_;
-  grpc::thread cq_thread_;
+  grpc::thread* cq_thread_;
   TestServiceImpl service_;
   int port_;
 
diff --git a/tools/cmake/gRPCConfig.cmake.in b/tools/cmake/gRPCConfig.cmake.in
new file mode 100644
index 0000000000000000000000000000000000000000..48f06745798dda074d55e80d61c3518f4a9bf09b
--- /dev/null
+++ b/tools/cmake/gRPCConfig.cmake.in
@@ -0,0 +1,7 @@
+# Depend packages
+@_gRPC_FIND_ZLIB@
+@_gRPC_FIND_PROTOBUF@
+@_gRPC_FIND_SSL@
+
+# Targets
+include(${CMAKE_CURRENT_LIST_DIR}/gRPCTargets.cmake)
diff --git a/tools/cmake/gRPCConfigVersion.cmake.in b/tools/cmake/gRPCConfigVersion.cmake.in
new file mode 100644
index 0000000000000000000000000000000000000000..f3c19fd403aeec295f9c3dc584f5f1ea5412147d
--- /dev/null
+++ b/tools/cmake/gRPCConfigVersion.cmake.in
@@ -0,0 +1,11 @@
+set(PACKAGE_VERSION "@PACKAGE_VERSION@")
+
+# Check whether the requested PACKAGE_FIND_VERSION is compatible
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
+    set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else()
+    set(PACKAGE_VERSION_COMPATIBLE TRUE)
+    if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
+        set(PACKAGE_VERSION_EXACT TRUE)
+    endif()
+endif()
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
index faa83867a6170cdbf0b65d2603d2764322730e7b..c3c04966dfdefd463cd7c775809ba59c175230dd 100755
--- a/tools/codegen/core/gen_static_metadata.py
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -108,7 +108,8 @@ CONFIG = [
     ('if-range', ''),
     ('if-unmodified-since', ''),
     ('last-modified', ''),
-    ('load-reporting', ''),
+    ('load-reporting-initial', ''),
+    ('load-reporting-trailing', ''),
     ('link', ''),
     ('location', ''),
     ('max-forwards', ''),
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
index c4b5d98a53b298831e50d6d6b22199a308a96918..727b11e273d8d362166a0d135c5872970116dd18 100755
--- a/tools/run_tests/build_python.sh
+++ b/tools/run_tests/build_python.sh
@@ -155,7 +155,9 @@ pip_install_dir() {
   cd $PWD
 }
 
-$VENV_PYTHON -m pip install --upgrade pip setuptools
+$VENV_PYTHON -m pip install --upgrade pip
+# TODO(https://github.com/pypa/setuptools/issues/709) get the latest setuptools
+$VENV_PYTHON -m pip install setuptools==25.1.1
 $VENV_PYTHON -m pip install cython
 pip_install_dir $ROOT
 $VENV_PYTHON $ROOT/tools/distrib/python/make_grpcio_tools.py
diff --git a/tools/run_tests/pre_build_csharp.bat b/tools/run_tests/pre_build_csharp.bat
index e7131d504c8cdb26a42a593860999e1ccfd1ee2e..580d5638fda3ac1f354a517e9dc3c33ce6f6ce4e 100644
--- a/tools/run_tests/pre_build_csharp.bat
+++ b/tools/run_tests/pre_build_csharp.bat
@@ -38,8 +38,61 @@ cd /d %~dp0\..\..
 set NUGET=C:\nuget\nuget.exe
 
 if exist %NUGET% (
+  @rem Restore Grpc packages by packages since Nuget client 3.4.4 doesnt support restore
+  @rem by solution
+  @rem Moving into each directory to let the restores work with both nuget 3.4 and 2.8
   %NUGET% restore vsprojects/grpc_csharp_ext.sln || goto :error
-  %NUGET% restore src/csharp/Grpc.sln || goto :error
+
+  cd src/csharp
+
+  cd Grpc.Auth || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.Core || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.Core.Tests || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.Examples.MathClient || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.Examples.MathServer || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.Examples || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.HealthCheck.Tests || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.HealthCheck || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.IntegrationTesting.Client || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.IntegrationTesting.QpsWorker || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.IntegrationTesting.StressClient || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+  cd ..
+
+  cd Grpc.IntegrationTesting || goto :error
+  %NUGET% restore -PackagesDirectory ../packages || goto :error
+
+  cd /d %~dp0\..\.. || goto :error
 )
 
 endlocal
diff --git a/tools/run_tests/pre_build_csharp.sh b/tools/run_tests/pre_build_csharp.sh
index 3ff1a4e5a8b5545e247fc506e50029d5effad104..0fd3b92a9513507898e840c0e5f19126413465d5 100755
--- a/tools/run_tests/pre_build_csharp.sh
+++ b/tools/run_tests/pre_build_csharp.sh
@@ -37,5 +37,54 @@ root=`pwd`
 
 if [ -x "$(command -v nuget)" ]
 then
-  nuget restore Grpc.sln
+  # Restoring Nuget packages by packages rather than by solution because of
+  # inability to restore by solution with Nuget client 3.4.4
+  # Moving into each directory to let the restores work with nuget 3.4 and 2.8
+  cd Grpc.Auth
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.Core.Tests
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.Core
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.Examples.MathClient
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.Examples.MathServer
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.Examples
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.HealthCheck.Tests
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.HealthCheck
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.IntegrationTesting.Client
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.IntegrationTesting.QpsWorker
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.IntegrationTesting.StressClient
+  nuget restore -PackagesDirectory ../packages
+  cd ..
+
+  cd Grpc.IntegrationTesting
+  nuget restore -PackagesDirectory ../packages
+  cd ..
 fi
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 9dc1c66eeef224e2f108bd77ad9dc7ddb1075a43..087f4feb105cf90da51836e06bdc4b85385daa25 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -3611,9 +3611,9 @@
     ], 
     "headers": [], 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "src": [
-      "test/core/end2end/fixtures/h2_loadreporting.c"
+      "test/core/end2end/fixtures/h2_load_reporting.c"
     ], 
     "third_party": false, 
     "type": "target"
@@ -3883,9 +3883,9 @@
     ], 
     "headers": [], 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "src": [
-      "test/core/end2end/fixtures/h2_loadreporting.c"
+      "test/core/end2end/fixtures/h2_load_reporting.c"
     ], 
     "third_party": false, 
     "type": "target"
@@ -5404,6 +5404,7 @@
       "test/core/end2end/tests/idempotent_request.c", 
       "test/core/end2end/tests/invoke_large_request.c", 
       "test/core/end2end/tests/large_metadata.c", 
+      "test/core/end2end/tests/load_reporting_hook.c", 
       "test/core/end2end/tests/max_concurrent_streams.c", 
       "test/core/end2end/tests/max_message_length.c", 
       "test/core/end2end/tests/negative_deadline.c", 
@@ -5464,6 +5465,7 @@
       "test/core/end2end/tests/idempotent_request.c", 
       "test/core/end2end/tests/invoke_large_request.c", 
       "test/core/end2end/tests/large_metadata.c", 
+      "test/core/end2end/tests/load_reporting_hook.c", 
       "test/core/end2end/tests/max_concurrent_streams.c", 
       "test/core/end2end/tests/max_message_length.c", 
       "test/core/end2end/tests/negative_deadline.c", 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index d94301b946bbb514010fd34cfa0b47c78ceeae7d..d5e6f50703a5a09306511cb68526a8da569ef2e1 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -4830,6 +4830,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -5710,6 +5732,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -6569,6 +6613,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -7328,6 +7393,26 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -8130,6 +8215,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -8884,6 +8991,22 @@
       "linux"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -9628,6 +9751,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -10060,7 +10205,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10082,7 +10227,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10104,7 +10249,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10126,7 +10271,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10148,7 +10293,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10170,7 +10315,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10192,7 +10337,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10214,7 +10359,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10236,7 +10381,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10258,7 +10403,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10280,7 +10425,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10302,7 +10447,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10324,7 +10469,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10346,7 +10491,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10368,7 +10513,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10390,7 +10535,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10412,7 +10557,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10434,7 +10579,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10456,7 +10601,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10478,7 +10623,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10500,7 +10645,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10522,7 +10689,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10544,7 +10711,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10566,7 +10733,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10588,7 +10755,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10610,7 +10777,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10632,7 +10799,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10654,7 +10821,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10676,7 +10843,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10698,7 +10865,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10720,7 +10887,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10742,7 +10909,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10764,7 +10931,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10786,7 +10953,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10808,7 +10975,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10830,7 +10997,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10852,7 +11019,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10874,7 +11041,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10896,7 +11063,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10918,7 +11085,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -11367,6 +11534,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -12144,6 +12332,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_message_length"
@@ -12858,6 +13067,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -13572,6 +13802,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -14309,7 +14560,7 @@
   }, 
   {
     "args": [
-      "max_concurrent_streams"
+      "load_reporting_hook"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14330,7 +14581,7 @@
   }, 
   {
     "args": [
-      "max_message_length"
+      "max_concurrent_streams"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14351,7 +14602,7 @@
   }, 
   {
     "args": [
-      "negative_deadline"
+      "max_message_length"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14372,7 +14623,7 @@
   }, 
   {
     "args": [
-      "network_status_change"
+      "negative_deadline"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14393,7 +14644,7 @@
   }, 
   {
     "args": [
-      "no_op"
+      "network_status_change"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14414,7 +14665,7 @@
   }, 
   {
     "args": [
-      "payload"
+      "no_op"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14435,7 +14686,7 @@
   }, 
   {
     "args": [
-      "ping_pong_streaming"
+      "payload"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14456,7 +14707,7 @@
   }, 
   {
     "args": [
-      "registered_call"
+      "ping_pong_streaming"
     ], 
     "ci_platforms": [
       "windows", 
@@ -14477,14 +14728,35 @@
   }, 
   {
     "args": [
-      "request_with_flags"
+      "registered_call"
     ], 
     "ci_platforms": [
       "windows", 
       "linux", 
       "posix"
     ], 
-    "cpu_cost": 0.1, 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_flags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
@@ -15126,6 +15398,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -16006,6 +16300,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -16802,6 +17118,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_message_length"
@@ -17538,6 +17875,26 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -18358,6 +18715,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -19216,6 +19595,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -19974,6 +20375,26 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -20754,6 +21175,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -21492,6 +21935,22 @@
       "linux"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -22214,6 +22673,28 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -22646,7 +23127,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22668,7 +23149,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22690,7 +23171,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22712,7 +23193,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22734,7 +23215,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22756,7 +23237,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22778,7 +23259,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22800,7 +23281,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22822,7 +23303,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22844,7 +23325,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22866,7 +23347,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22888,7 +23369,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22910,7 +23391,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22932,7 +23413,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22954,7 +23435,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22976,7 +23457,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22998,7 +23479,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23020,7 +23501,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23042,7 +23523,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23064,7 +23545,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23086,7 +23589,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23108,7 +23611,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23130,7 +23633,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23152,7 +23655,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23174,7 +23677,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23196,7 +23699,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23218,7 +23721,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23240,7 +23743,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23262,7 +23765,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23284,7 +23787,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23306,7 +23809,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23328,7 +23831,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23350,7 +23853,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23372,7 +23875,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23394,7 +23897,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23416,7 +23919,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23438,7 +23941,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23460,7 +23963,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23482,7 +23985,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23847,6 +24350,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_message_length"
@@ -24540,6 +25064,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -25233,6 +25778,27 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -25981,6 +26547,29 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [
+      "msan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
@@ -26752,6 +27341,26 @@
       "posix"
     ]
   }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
   {
     "args": [
       "max_concurrent_streams"
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index 7232440ab7627fbc7c9f9804a94a3589a9b58af2..6c92e7743bd5514eef11d4a225c3bce2f5a7a846 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -748,7 +748,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_full_test", "vcxproj\tes
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_loadreporting_nosec_test", "vcxproj\test/end2end/fixtures\h2_loadreporting_nosec_test\h2_loadreporting_nosec_test.vcxproj", "{679EA55C-7399-53E8-79F0-82FBDB3DDE07}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_load_reporting_nosec_test", "vcxproj\test/end2end/fixtures\h2_load_reporting_nosec_test\h2_load_reporting_nosec_test.vcxproj", "{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
 	EndProjectSection
@@ -760,7 +760,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_loadreporting_nosec_test
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_loadreporting_test", "vcxproj\test/end2end/fixtures\h2_loadreporting_test\h2_loadreporting_test.vcxproj", "{B107130E-EA33-C114-9CB6-78A18C929F64}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_load_reporting_test", "vcxproj\test/end2end/fixtures\h2_load_reporting_test\h2_load_reporting_test.vcxproj", "{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
 	EndProjectSection
@@ -2607,38 +2607,38 @@ Global
 		{EEBEFA75-C625-C823-FE96-9AD64887B57D}.Release-DLL|Win32.Build.0 = Release|Win32
 		{EEBEFA75-C625-C823-FE96-9AD64887B57D}.Release-DLL|x64.ActiveCfg = Release|x64
 		{EEBEFA75-C625-C823-FE96-9AD64887B57D}.Release-DLL|x64.Build.0 = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|Win32.ActiveCfg = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|x64.ActiveCfg = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|Win32.ActiveCfg = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|x64.ActiveCfg = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|Win32.Build.0 = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|x64.Build.0 = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|Win32.Build.0 = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|x64.Build.0 = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|x64.Build.0 = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|Win32.Build.0 = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|x64.ActiveCfg = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|x64.Build.0 = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|Win32.ActiveCfg = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|x64.ActiveCfg = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|Win32.ActiveCfg = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|x64.ActiveCfg = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|Win32.Build.0 = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|x64.Build.0 = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|Win32.Build.0 = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|x64.Build.0 = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|x64.Build.0 = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|Win32.Build.0 = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|x64.ActiveCfg = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|x64.Build.0 = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|Win32.ActiveCfg = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|x64.ActiveCfg = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|Win32.ActiveCfg = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|x64.ActiveCfg = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|Win32.Build.0 = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|x64.Build.0 = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|Win32.Build.0 = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|x64.Build.0 = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|x64.Build.0 = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|Win32.Build.0 = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|x64.ActiveCfg = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|x64.Build.0 = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|Win32.ActiveCfg = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|x64.ActiveCfg = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|Win32.ActiveCfg = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|x64.ActiveCfg = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|Win32.Build.0 = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|x64.Build.0 = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|Win32.Build.0 = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|x64.Build.0 = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|x64.Build.0 = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|Win32.Build.0 = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|x64.ActiveCfg = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|x64.Build.0 = Release|x64
 		{0F761FF3-342A-C429-711F-F76181BAA52D}.Debug|Win32.ActiveCfg = Debug|Win32
 		{0F761FF3-342A-C429-711F-F76181BAA52D}.Debug|x64.ActiveCfg = Debug|x64
 		{0F761FF3-342A-C429-711F-F76181BAA52D}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj
similarity index 98%
rename from vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj
rename to vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj
index 6a6ac5ebf1490da915b9e24dcf6b9fe48d2fae39..1ccc6bb8dc686c0bff3045631dcebc2168f2ac0f 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj
@@ -20,7 +20,7 @@
     </ProjectConfiguration>
   </ItemGroup>
   <PropertyGroup Label="Globals">
-    <ProjectGuid>{679EA55C-7399-53E8-79F0-82FBDB3DDE07}</ProjectGuid>
+    <ProjectGuid>{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}</ProjectGuid>
     <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
     <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
@@ -60,14 +60,14 @@
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>h2_loadreporting_nosec_test</TargetName>
+    <TargetName>h2_load_reporting_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
     <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
     <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>h2_loadreporting_nosec_test</TargetName>
+    <TargetName>h2_load_reporting_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
     <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
@@ -158,7 +158,7 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj.filters
similarity index 68%
rename from vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj.filters
rename to vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj.filters
index 4ed1bb0c4573bed0a65f4555863b92101698a76b..72df23ebc1da15703779b9afa5103cfc09954cbd 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj.filters
@@ -1,23 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
       <Filter>test\core\end2end\fixtures</Filter>
     </ClCompile>
   </ItemGroup>
 
   <ItemGroup>
     <Filter Include="test">
-      <UniqueIdentifier>{8adc89fb-e447-77bc-c462-3dba6abcf344}</UniqueIdentifier>
+      <UniqueIdentifier>{abff9aef-586e-65d2-beb8-e6392ecf7440}</UniqueIdentifier>
     </Filter>
     <Filter Include="test\core">
-      <UniqueIdentifier>{3c2c01f5-2a18-1bee-6ee0-217d415e2a95}</UniqueIdentifier>
+      <UniqueIdentifier>{fd31cbf1-8cff-2e6a-618d-a01855997839}</UniqueIdentifier>
     </Filter>
     <Filter Include="test\core\end2end">
-      <UniqueIdentifier>{3efa0f41-5802-6a8e-36ee-f246a201a1a5}</UniqueIdentifier>
+      <UniqueIdentifier>{c6cc113f-ddb4-733b-5c10-8f98aa2d7d22}</UniqueIdentifier>
     </Filter>
     <Filter Include="test\core\end2end\fixtures">
-      <UniqueIdentifier>{366eb24f-49e9-d57f-e20f-729d1e0fb892}</UniqueIdentifier>
+      <UniqueIdentifier>{2cd15a3b-a7e6-b847-b6c4-7c6cc03eacc2}</UniqueIdentifier>
     </Filter>
   </ItemGroup>
 </Project>
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj
similarity index 98%
rename from vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj
rename to vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj
index 20765487bcb5bbc237269bd6cce91dc04d638785..d5d1a15a82a3a75dd8d431a14c66f242ab9e6ec7 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj
@@ -20,7 +20,7 @@
     </ProjectConfiguration>
   </ItemGroup>
   <PropertyGroup Label="Globals">
-    <ProjectGuid>{B107130E-EA33-C114-9CB6-78A18C929F64}</ProjectGuid>
+    <ProjectGuid>{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}</ProjectGuid>
     <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
     <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
@@ -60,14 +60,14 @@
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>h2_loadreporting_test</TargetName>
+    <TargetName>h2_load_reporting_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
     <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
     <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>h2_loadreporting_test</TargetName>
+    <TargetName>h2_load_reporting_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
     <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
@@ -158,7 +158,7 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj.filters
similarity index 68%
rename from vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj.filters
rename to vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj.filters
index afe54329ad3cb6bb32d0098fc8ee9487d0ecf403..95b049c85286071ab14144c6b74faa9ec7a73e29 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj.filters
@@ -1,23 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
       <Filter>test\core\end2end\fixtures</Filter>
     </ClCompile>
   </ItemGroup>
 
   <ItemGroup>
     <Filter Include="test">
-      <UniqueIdentifier>{8f73760a-74dc-05ef-65e1-fa8c44ccf918}</UniqueIdentifier>
+      <UniqueIdentifier>{467ceaa9-3a51-d5df-0556-1ef8e91f5d7d}</UniqueIdentifier>
     </Filter>
     <Filter Include="test\core">
-      <UniqueIdentifier>{a280079e-b626-333e-0636-8fe6eb788ca1}</UniqueIdentifier>
+      <UniqueIdentifier>{1ec29254-9064-0338-78ca-94d8cfdbd95c}</UniqueIdentifier>
     </Filter>
     <Filter Include="test\core\end2end">
-      <UniqueIdentifier>{c1aa73d6-503a-06c0-42b2-0793a4805e96}</UniqueIdentifier>
+      <UniqueIdentifier>{58fc84e1-7dc9-a517-359e-7d7e1c417432}</UniqueIdentifier>
     </Filter>
     <Filter Include="test\core\end2end\fixtures">
-      <UniqueIdentifier>{3e738e89-dc27-f929-cc8f-1aa94c24345b}</UniqueIdentifier>
+      <UniqueIdentifier>{92ef09e0-592b-0ca5-4d79-5929ae159ed5}</UniqueIdentifier>
     </Filter>
   </ItemGroup>
 </Project>
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
index 28ced2dc7ef7b6eafcfa7fb39ce85c8ec83e8386..fc1068f97c60d87a666a856581790d4b64f41d4d 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
@@ -193,6 +193,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
index 7c725355d9109127123e07e947678927bcda4111..de8c0dd8dd56f9121e4732e356695ec9cf061138 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
@@ -64,6 +64,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
index bc064cd6acd52a3c2a60db53dacd147c18ab46b9..a902bb40083a74dcb7c0d02d4ee37676f0b878ba 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
@@ -195,6 +195,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
index d959c804854e6b88869dc9d243f926ed7152e602..522f73d802acb964db1b6e7e983f324937b62fc8 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
@@ -67,6 +67,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>