Эх сурвалжийг харах

Merge branch 'master' into minor-timer-fix

Sree Kuchibhotla 7 жил өмнө
parent
commit
a1bcb328d9
95 өөрчлөгдсөн 4350 нэмэгдсэн , 736 устгасан
  1. 47 7
      BUILD
  2. 41 69
      BUILDING.md
  3. 107 86
      CMakeLists.txt
  4. 63 0
      CONCEPTS.md
  5. 26 5
      CONTRIBUTING.md
  6. 2 1
      Makefile
  7. 44 76
      README.md
  8. 41 23
      build.yaml
  9. 4 2
      config.m4
  10. 4 2
      config.w32
  11. 1 1
      doc/command_line_tool.md
  12. 1 1
      examples/cpp/README.md
  13. 2 2
      examples/cpp/cpptutorial.md
  14. 2 2
      examples/cpp/helloworld/README.md
  15. 6 0
      examples/cpp/route_guide/README.md
  16. 2 0
      gRPC-C++.podspec
  17. 4 0
      gRPC-Core.podspec
  18. 0 2
      grpc.def
  19. 2 0
      grpc.gemspec
  20. 6 10
      grpc.gyp
  21. 0 8
      include/grpc/grpc.h
  22. 4 0
      include/grpc/impl/codegen/grpc_types.h
  23. 2 2
      include/grpcpp/impl/codegen/call.h
  24. 2 0
      package.xml
  25. 21 16
      src/core/ext/transport/chttp2/transport/writing.cc
  26. 23 26
      src/core/lib/channel/channel_trace.cc
  27. 13 17
      src/core/lib/channel/channel_trace.h
  28. 185 0
      src/core/lib/channel/channelz.cc
  29. 85 0
      src/core/lib/channel/channelz.h
  30. 9 0
      src/core/lib/iomgr/socket_utils.h
  31. 4 0
      src/core/lib/iomgr/socket_utils_common_posix.cc
  32. 4 0
      src/core/lib/iomgr/socket_utils_uv.cc
  33. 4 0
      src/core/lib/iomgr/socket_utils_windows.cc
  34. 21 8
      src/core/lib/surface/call.cc
  35. 20 13
      src/core/lib/surface/channel.cc
  36. 4 0
      src/core/lib/surface/channel.h
  37. 56 8
      src/cpp/README.md
  38. 4 2
      src/cpp/ext/filters/census/client_filter.cc
  39. 71 0
      src/cpp/server/load_reporter/constants.h
  40. 36 0
      src/cpp/server/load_reporter/get_cpu_stats.h
  41. 45 0
      src/cpp/server/load_reporter/get_cpu_stats_linux.cc
  42. 45 0
      src/cpp/server/load_reporter/get_cpu_stats_macos.cc
  43. 40 0
      src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc
  44. 55 0
      src/cpp/server/load_reporter/get_cpu_stats_windows.cc
  45. 65 0
      src/cpp/server/load_reporter/load_data_store.cc
  46. 21 13
      src/cpp/server/load_reporter/load_data_store.h
  47. 498 0
      src/cpp/server/load_reporter/load_reporter.cc
  48. 225 0
      src/cpp/server/load_reporter/load_reporter.h
  49. 2 2
      src/csharp/README.md
  50. 102 41
      src/proto/grpc/channelz/channelz.proto
  51. 12 2
      src/proto/grpc/lb/v1/BUILD
  52. 180 0
      src/proto/grpc/lb/v1/load_reporter.proto
  53. 1 0
      src/python/grpcio/grpc_core_dependencies.py
  54. 3 0
      src/python/grpcio_testing/MANIFEST.in
  55. 10 0
      src/python/grpcio_testing/README.rst
  56. 0 4
      src/ruby/end2end/multiple_killed_watching_threads_driver.rb
  57. 0 4
      src/ruby/ext/grpc/rb_grpc_imports.generated.c
  58. 0 6
      src/ruby/ext/grpc/rb_grpc_imports.generated.h
  59. 1 1
      src/ruby/lib/grpc/generic/active_call.rb
  60. 0 11
      src/ruby/spec/client_auth_spec.rb
  61. 0 11
      src/ruby/spec/google_rpc_status_utils_spec.rb
  62. 6 0
      templates/CMakeLists.txt.template
  63. 3 2
      templates/config.m4.template
  64. 3 2
      templates/config.w32.template
  65. 17 0
      test/core/channel/BUILD
  66. 107 89
      test/core/channel/channel_trace_test.cc
  67. 216 0
      test/core/channel/channelz_test.cc
  68. 8 0
      test/core/end2end/end2end_nosec_tests.cc
  69. 8 0
      test/core/end2end/end2end_tests.cc
  70. 1 0
      test/core/end2end/fuzzers/api_fuzzer.cc
  71. 1 0
      test/core/end2end/gen_build_yaml.py
  72. 1 0
      test/core/end2end/generate_tests.bzl
  73. 299 0
      test/core/end2end/tests/channelz.cc
  74. 0 2
      test/core/surface/public_headers_must_be_c89.c
  75. 14 0
      test/cpp/microbenchmarks/BUILD
  76. 90 0
      test/cpp/microbenchmarks/bm_channel.cc
  77. 33 1
      test/cpp/server/load_reporter/BUILD
  78. 61 0
      test/cpp/server/load_reporter/get_cpu_stats_test.cc
  79. 2 2
      test/cpp/server/load_reporter/load_data_store_test.cc
  80. 498 0
      test/cpp/server/load_reporter/load_reporter_test.cc
  81. 31 10
      test/cpp/util/channel_trace_proto_helper.cc
  82. 1 0
      test/cpp/util/channel_trace_proto_helper.h
  83. 7 9
      test/distrib/python/test_packages.sh
  84. 1 0
      tools/doxygen/Doxyfile.c++.internal
  85. 2 0
      tools/doxygen/Doxyfile.core.internal
  86. 3 0
      tools/interop_matrix/client_matrix.py
  87. 6 2
      tools/run_tests/artifacts/build_artifact_python.sh
  88. 48 37
      tools/run_tests/generated/sources_and_headers.json
  89. 591 59
      tools/run_tests/generated/tests.json
  90. 11 11
      tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh
  91. 1 1
      tools/run_tests/python_utils/filter_pull_request_tests.py
  92. 1 1
      tools/run_tests/python_utils/upload_rbe_results.py
  93. 1 1
      tools/run_tests/run_tests.py
  94. 0 12
      vsprojects/.gitignore
  95. 0 11
      vsprojects/README.md

+ 47 - 7
BUILD

@@ -29,8 +29,8 @@ package(
 load(
     "//bazel:grpc_build_system.bzl",
     "grpc_cc_library",
-    "grpc_proto_plugin",
     "grpc_generate_one_off_targets",
+    "grpc_proto_plugin",
 )
 
 config_setting(
@@ -675,6 +675,7 @@ grpc_cc_library(
         "src/core/lib/channel/channel_stack.cc",
         "src/core/lib/channel/channel_stack_builder.cc",
         "src/core/lib/channel/channel_trace.cc",
+        "src/core/lib/channel/channelz.cc",
         "src/core/lib/channel/channelz_registry.cc",
         "src/core/lib/channel/connected_channel.cc",
         "src/core/lib/channel/handshaker.cc",
@@ -822,6 +823,7 @@ grpc_cc_library(
         "src/core/lib/channel/channel_stack.h",
         "src/core/lib/channel/channel_stack_builder.h",
         "src/core/lib/channel/channel_trace.h",
+        "src/core/lib/channel/channelz.h",
         "src/core/lib/channel/channelz_registry.h",
         "src/core/lib/channel/connected_channel.h",
         "src/core/lib/channel/context.h",
@@ -1306,6 +1308,7 @@ grpc_cc_library(
         "src/cpp/server/load_reporter/load_data_store.cc",
     ],
     hdrs = [
+        "src/cpp/server/load_reporter/constants.h",
         "src/cpp/server/load_reporter/load_data_store.h",
     ],
     language = "c++",
@@ -1314,6 +1317,43 @@ grpc_cc_library(
     ],
 )
 
+grpc_cc_library(
+    name = "lb_get_cpu_stats",
+    srcs = [
+        "src/cpp/server/load_reporter/get_cpu_stats_linux.cc",
+        "src/cpp/server/load_reporter/get_cpu_stats_macos.cc",
+        "src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc",
+        "src/cpp/server/load_reporter/get_cpu_stats_windows.cc",
+    ],
+    hdrs = [
+        "src/cpp/server/load_reporter/get_cpu_stats.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++",
+    ],
+)
+
+grpc_cc_library(
+    name = "lb_load_reporter",
+    srcs = [
+        "src/cpp/server/load_reporter/load_reporter.cc",
+    ],
+    hdrs = [
+        "src/cpp/server/load_reporter/constants.h",
+        "src/cpp/server/load_reporter/load_reporter.h",
+    ],
+    external_deps = [
+        "opencensus-stats",
+    ],
+    language = "c++",
+    deps = [
+        "lb_get_cpu_stats",
+        "lb_load_data_store",
+        "//src/proto/grpc/lb/v1:load_reporter_proto",
+    ],
+)
+
 grpc_cc_library(
     name = "grpc_resolver_dns_native",
     srcs = [
@@ -1737,11 +1777,11 @@ grpc_cc_library(
         "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h",
         "src/core/tsi/alts/handshaker/transport_security_common_api.h",
     ],
-    public_hdrs = GRPC_SECURE_PUBLIC_HDRS, 
     external_deps = [
         "nanopb",
     ],
     language = "c++",
+    public_hdrs = GRPC_SECURE_PUBLIC_HDRS,
     deps = [
         "alts_proto",
         "gpr",
@@ -1990,33 +2030,33 @@ grpc_cc_library(
 grpc_cc_library(
     name = "grpc_opencensus_plugin",
     srcs = [
-        "src/cpp/ext/filters/census/client_filter.cc",
-        "src/cpp/ext/filters/census/server_filter.cc",
         "src/cpp/ext/filters/census/channel_filter.cc",
+        "src/cpp/ext/filters/census/client_filter.cc",
         "src/cpp/ext/filters/census/context.cc",
         "src/cpp/ext/filters/census/grpc_context.cc",
         "src/cpp/ext/filters/census/grpc_plugin.cc",
         "src/cpp/ext/filters/census/measures.cc",
         "src/cpp/ext/filters/census/rpc_encoding.cc",
+        "src/cpp/ext/filters/census/server_filter.cc",
         "src/cpp/ext/filters/census/views.cc",
     ],
     hdrs = [
         "include/grpcpp/opencensus.h",
-        "src/cpp/ext/filters/census/client_filter.h",
-        "src/cpp/ext/filters/census/server_filter.h",
         "src/cpp/ext/filters/census/channel_filter.h",
+        "src/cpp/ext/filters/census/client_filter.h",
         "src/cpp/ext/filters/census/context.h",
         "src/cpp/ext/filters/census/grpc_plugin.h",
         "src/cpp/ext/filters/census/measures.h",
         "src/cpp/ext/filters/census/rpc_encoding.h",
+        "src/cpp/ext/filters/census/server_filter.h",
     ],
-    language = "c++",
     external_deps = [
         "absl-base",
         "absl-time",
         "opencensus-trace",
         "opencensus-stats",
     ],
+    language = "c++",
     deps = [
         ":census",
         ":grpc++",

+ 41 - 69
INSTALL.md → BUILDING.md

@@ -1,18 +1,5 @@
-# If you are in a hurry
-
-For language-specific installation instructions for gRPC runtime, please
-refer to these documents
-
- * [C++](examples/cpp): Currently to install gRPC for C++, you need to build from source as described below.
- * [C#](src/csharp): NuGet package `Grpc`
- * [Go](https://github.com/grpc/grpc-go): `go get google.golang.org/grpc`
- * [Java](https://github.com/grpc/grpc-java)
- * [Node](src/node): `npm install grpc`
- * [Objective-C](src/objective-c)
- * [PHP](src/php): `pecl install grpc`
- * [Python](src/python/grpcio): `pip install grpcio`
- * [Ruby](src/ruby): `gem install grpc`
-
+gRPC C++ - Building from source
+===========================
 
 # Pre-requisites
 
@@ -28,7 +15,7 @@ If you plan to build from source and run tests, install the following as well:
  $ [sudo] apt-get install clang libc++-dev
 ```
 
-## macOS 
+## MacOS
 
 On a Mac, you will first need to
 install Xcode or
@@ -60,6 +47,17 @@ installed by `brew` is being used:
  $ LIBTOOL=glibtool LIBTOOLIZE=glibtoolize make
 ```
 
+## Windows
+
+To prepare for cmake + Microsoft Visual C++ compiler build
+- Install Visual Studio 2015 or 2017 (Visual C++ compiler will be used).
+- Install [Git](https://git-scm.com/).
+- Install [CMake](https://cmake.org/download/).
+- Install [Active State Perl](https://www.activestate.com/activeperl/) (`choco install activeperl`) - *required by boringssl*
+- Install [Go](https://golang.org/dl/) (`choco install golang`) - *required by boringssl*
+- Install [yasm](http://yasm.tortall.net/) and add it to `PATH` (`choco install yasm`) - *required by boringssl*
+- (Optional) Install [Ninja](https://ninja-build.org/) (`choco install ninja`)
+
 ## Protoc
 
 By default gRPC uses [protocol buffers](https://github.com/google/protobuf),
@@ -77,48 +75,49 @@ $ cd grpc/third_party/protobuf
 $ sudo make install   # 'make' should have been run by core grpc
 ```
 
-# Build from Source
+# Clone the repository (including submodules)
 
-For developers who are interested to contribute, the following commands show how to compile the
-gRPC C Core library.
+Before building, you need to clone the gRPC github repository and download submodules containing source code 
+for gRPC's dependencies (that's done by the `submodule` command or `--recursive` flag). The following commands will clone the gRPC
+repository at the latest stable version.
+
+## Unix
 
 ```sh
  $ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
  $ cd grpc
  $ git submodule update --init
- $ make
- $ [sudo] make install
-```
+ ```
 
 ## Windows
 
-There are several ways to build under Windows, of varying complexity depending
-on experience with the tools involved.
+```
+> @rem You can also do just "git clone --recursive -b THE_BRANCH_YOU_WANT https://github.com/grpc/grpc"
+> powershell git clone --recursive -b ((New-Object System.Net.WebClient).DownloadString(\"https://grpc.io/release\").Trim()) https://github.com/grpc/grpc
+> cd grpc
+> @rem To update submodules at later time, run "git submodule update --init"
+```
 
+# Build from source
 
+In the C++ world, there's no "standard" build system that would work for in all supported use cases and on all supported platforms.
+Therefore, gRPC supports several major build systems, which should satisfy most users.
 
-### Building using CMake (RECOMMENDED)
+## make (on UNIX systems)
 
-Builds gRPC C and C++ with boringssl.
-- Install Visual Studio 2015 or 2017 (Visual C++ compiler will be used).
-- Install [Git](https://git-scm.com/).
-- Install [CMake](https://cmake.org/download/).
-- Install [Active State Perl](https://www.activestate.com/activeperl/) (`choco install activeperl`) - *required by boringssl*
-- Install [Go](https://golang.org/dl/) (`choco install golang`) - *required by boringssl*
-- Install [yasm](http://yasm.tortall.net/) and add it to `PATH` (`choco install yasm`) - *required by boringssl*
-- (Optional) Install [Ninja](https://ninja-build.org/) (`choco install ninja`)
+From the grpc repository root
+```sh
+ $ make
+```
 
-#### Clone grpc sources including submodules
-Before building, you need to clone the gRPC github repository and download submodules containing source code 
-for gRPC's dependencies (that's done by the `submodule` command).
+## bazel
+
+From the grpc repository root
 ```
-> @rem You can also do just "git clone --recursive -b THE_BRANCH_YOU_WANT https://github.com/grpc/grpc"
-> powershell git clone --recursive -b ((New-Object System.Net.WebClient).DownloadString(\"https://grpc.io/release\").Trim()) https://github.com/grpc/grpc
-> cd grpc
-> @rem To update submodules at later time, run "git submodule update --init"
+bazel build :all
 ```
 
-#### cmake: Using Visual Studio 2015 or 2017 (can only build with OPENSSL_NO_ASM).
+## cmake: Windows, Using Visual Studio 2015 or 2017 (can only build with OPENSSL_NO_ASM).
 When using the "Visual Studio" generator,
 cmake will generate a solution (`grpc.sln`) that contains a VS project for 
 every target defined in `CMakeLists.txt` (+ few extra convenience projects
@@ -132,7 +131,7 @@ you will be able to browse and build the code.
 > cmake --build .
 ```
 
-#### cmake: Using Ninja (faster build, supports boringssl's assembly optimizations).
+## cmake: Windows, Using Ninja (faster build, supports boringssl's assembly optimizations).
 Please note that when using Ninja, you will still need Visual C++ (part of Visual Studio)
 installed to be able to compile the C/C++ sources.
 ```
@@ -143,30 +142,3 @@ installed to be able to compile the C/C++ sources.
 > cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release
 > cmake --build .
 ```
-
-### msys2 (with mingw)
-
-The Makefile (and source code) should support msys2's mingw32 and mingw64
-compilers. Building with msys2's native compiler is possible, but
-difficult.
-
-This approach requires having [msys2](https://msys2.github.io/) installed.
-
-```
-# Install prerequisites
-MSYS2$ pacman -S autoconf automake gcc libtool mingw-w64-x86_64-toolchain perl pkg-config zlib
-MSYS2$ pacman -S mingw-w64-x86_64-gflags
-```
-
-```
-# From mingw shell
-MINGW64$ export CPPFLAGS="-D_WIN32_WINNT=0x0600"
-MINGW64$ make
-```
-
-NOTE: Though most of the make targets are buildable under Mingw, some haven't been ported to Windows yet
-and may fail to build (mostly trying to include POSIX headers not available on Mingw).
-
-### Pre-generated Visual Studio solution (DELETED)
-
-*WARNING: This used to be the recommended way to build on Windows, but because of significant limitations (hard to build dependencies including boringssl, .proto codegen is hard to support, ..) we are no longer providing them. Use cmake to build on Windows instead.*

+ 107 - 86
CMakeLists.txt

@@ -39,6 +39,7 @@ set(gRPC_INSTALL_SHAREDIR "share/grpc" CACHE STRING "Installation directory for
 # Options
 option(gRPC_BUILD_TESTS "Build tests" OFF)
 option(gRPC_BUILD_CODEGEN "Build codegen" ON)
+option(gRPC_BUILD_CSHARP_EXT "Build C# extensions" ON)
 
 set(gRPC_INSTALL_default ON)
 if (NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
@@ -503,6 +504,9 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx bm_call_create)
 endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+add_dependencies(buildtests_cxx bm_channel)
+endif()
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx bm_chttp2_hpack)
 endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@@ -543,6 +547,7 @@ add_dependencies(buildtests_cxx channel_arguments_test)
 add_dependencies(buildtests_cxx channel_filter_test)
 add_dependencies(buildtests_cxx channel_trace_test)
 add_dependencies(buildtests_cxx channelz_registry_test)
+add_dependencies(buildtests_cxx channelz_test)
 add_dependencies(buildtests_cxx check_gcp_environment_linux_test)
 add_dependencies(buildtests_cxx check_gcp_environment_windows_test)
 add_dependencies(buildtests_cxx chttp2_settings_timeout_test)
@@ -594,7 +599,6 @@ endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx json_run_localhost)
 endif()
-add_dependencies(buildtests_cxx lb_load_data_store_test)
 add_dependencies(buildtests_cxx memory_test)
 add_dependencies(buildtests_cxx metrics_client)
 add_dependencies(buildtests_cxx mock_test)
@@ -935,6 +939,7 @@ add_library(grpc
   src/core/lib/channel/channel_stack.cc
   src/core/lib/channel/channel_stack_builder.cc
   src/core/lib/channel/channel_trace.cc
+  src/core/lib/channel/channelz.cc
   src/core/lib/channel/channelz_registry.cc
   src/core/lib/channel/connected_channel.cc
   src/core/lib/channel/handshaker.cc
@@ -1330,6 +1335,7 @@ add_library(grpc_cronet
   src/core/lib/channel/channel_stack.cc
   src/core/lib/channel/channel_stack_builder.cc
   src/core/lib/channel/channel_trace.cc
+  src/core/lib/channel/channelz.cc
   src/core/lib/channel/channelz_registry.cc
   src/core/lib/channel/connected_channel.cc
   src/core/lib/channel/handshaker.cc
@@ -1716,6 +1722,7 @@ add_library(grpc_test_util
   src/core/lib/channel/channel_stack.cc
   src/core/lib/channel/channel_stack_builder.cc
   src/core/lib/channel/channel_trace.cc
+  src/core/lib/channel/channelz.cc
   src/core/lib/channel/channelz_registry.cc
   src/core/lib/channel/connected_channel.cc
   src/core/lib/channel/handshaker.cc
@@ -2021,6 +2028,7 @@ add_library(grpc_test_util_unsecure
   src/core/lib/channel/channel_stack.cc
   src/core/lib/channel/channel_stack_builder.cc
   src/core/lib/channel/channel_trace.cc
+  src/core/lib/channel/channelz.cc
   src/core/lib/channel/channelz_registry.cc
   src/core/lib/channel/connected_channel.cc
   src/core/lib/channel/handshaker.cc
@@ -2305,6 +2313,7 @@ add_library(grpc_unsecure
   src/core/lib/channel/channel_stack.cc
   src/core/lib/channel/channel_stack_builder.cc
   src/core/lib/channel/channel_trace.cc
+  src/core/lib/channel/channelz.cc
   src/core/lib/channel/channelz_registry.cc
   src/core/lib/channel/connected_channel.cc
   src/core/lib/channel/handshaker.cc
@@ -3137,6 +3146,7 @@ add_library(grpc++_cronet
   src/core/lib/channel/channel_stack.cc
   src/core/lib/channel/channel_stack_builder.cc
   src/core/lib/channel/channel_trace.cc
+  src/core/lib/channel/channelz.cc
   src/core/lib/channel/channelz_registry.cc
   src/core/lib/channel/connected_channel.cc
   src/core/lib/channel/handshaker.cc
@@ -5017,50 +5027,6 @@ target_link_libraries(interop_server_main
 )
 
 
-endif (gRPC_BUILD_TESTS)
-if (gRPC_BUILD_TESTS)
-
-add_library(lb_load_data_store
-  src/cpp/server/load_reporter/load_data_store.cc
-)
-
-if(WIN32 AND MSVC)
-  set_target_properties(lb_load_data_store PROPERTIES COMPILE_PDB_NAME "lb_load_data_store"
-    COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
-  )
-  if (gRPC_INSTALL)
-    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lb_load_data_store.pdb
-      DESTINATION ${gRPC_INSTALL_LIBDIR} OPTIONAL
-    )
-  endif()
-endif()
-
-
-target_include_directories(lb_load_data_store
-  PUBLIC $<INSTALL_INTERFACE:${gRPC_INSTALL_INCLUDEDIR}> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
-  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
-  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
-  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
-  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
-  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
-  PRIVATE third_party/googletest/googletest/include
-  PRIVATE third_party/googletest/googletest
-  PRIVATE third_party/googletest/googlemock/include
-  PRIVATE third_party/googletest/googlemock
-  PRIVATE ${_gRPC_PROTO_GENS_DIR}
-)
-
-target_link_libraries(lb_load_data_store
-  ${_gRPC_PROTOBUF_LIBRARIES}
-  ${_gRPC_ALLTARGETS_LIBRARIES}
-  grpc++
-)
-
-
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
@@ -5170,6 +5136,7 @@ target_link_libraries(qps
 endif (gRPC_BUILD_CODEGEN)
 
 endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_CSHARP_EXT)
 
 add_library(grpc_csharp_ext SHARED
   src/csharp/ext/grpc_csharp_ext.c
@@ -5216,6 +5183,7 @@ if (gRPC_INSTALL)
   )
 endif()
 
+endif (gRPC_BUILD_CSHARP_EXT)
 if (gRPC_BUILD_TESTS)
 
 add_library(bad_client_test
@@ -5316,6 +5284,7 @@ add_library(end2end_tests
   test/core/end2end/tests/cancel_before_invoke.cc
   test/core/end2end/tests/cancel_in_a_vacuum.cc
   test/core/end2end/tests/cancel_with_status.cc
+  test/core/end2end/tests/channelz.cc
   test/core/end2end/tests/compressed_payload.cc
   test/core/end2end/tests/connectivity.cc
   test/core/end2end/tests/default_host.cc
@@ -5436,6 +5405,7 @@ add_library(end2end_nosec_tests
   test/core/end2end/tests/cancel_before_invoke.cc
   test/core/end2end/tests/cancel_in_a_vacuum.cc
   test/core/end2end/tests/cancel_with_status.cc
+  test/core/end2end/tests/channelz.cc
   test/core/end2end/tests/compressed_payload.cc
   test/core/end2end/tests/connectivity.cc
   test/core/end2end/tests/default_host.cc
@@ -10060,6 +10030,51 @@ endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 
+add_executable(bm_channel
+  test/cpp/microbenchmarks/bm_channel.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(bm_channel
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(bm_channel
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_benchmark
+  ${_gRPC_BENCHMARK_LIBRARIES}
+  grpc++_test_util_unsecure
+  grpc_test_util_unsecure
+  grpc++_unsecure
+  grpc_unsecure
+  gpr_test_util
+  gpr
+  grpc++_test_config
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif()
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+
 add_executable(bm_chttp2_hpack
   test/cpp/microbenchmarks/bm_chttp2_hpack.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -10798,6 +10813,53 @@ target_link_libraries(channelz_registry_test
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(channelz_test
+  test/core/channel/channelz_test.cc
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/channelz/channelz.pb.cc
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/channelz/channelz.grpc.pb.cc
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/channelz/channelz.pb.h
+  ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/channelz/channelz.grpc.pb.h
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+protobuf_generate_grpc_cpp(
+  src/proto/grpc/channelz/channelz.proto
+)
+
+target_include_directories(channelz_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
+  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
+  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
+  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
+  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
+  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
+  PRIVATE third_party/googletest/googletest/include
+  PRIVATE third_party/googletest/googletest
+  PRIVATE third_party/googletest/googlemock/include
+  PRIVATE third_party/googletest/googlemock
+  PRIVATE ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(channelz_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc++_test_util
+  grpc++
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(check_gcp_environment_linux_test
   test/core/security/check_gcp_environment_linux_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
@@ -12676,47 +12738,6 @@ endif()
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
-add_executable(lb_load_data_store_test
-  test/cpp/server/load_reporter/load_data_store_test.cc
-  third_party/googletest/googletest/src/gtest-all.cc
-  third_party/googletest/googlemock/src/gmock-all.cc
-)
-
-
-target_include_directories(lb_load_data_store_test
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
-  PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
-  PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
-  PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
-  PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
-  PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
-  PRIVATE ${_gRPC_NANOPB_INCLUDE_DIR}
-  PRIVATE third_party/googletest/googletest/include
-  PRIVATE third_party/googletest/googletest
-  PRIVATE third_party/googletest/googlemock/include
-  PRIVATE third_party/googletest/googlemock
-  PRIVATE ${_gRPC_PROTO_GENS_DIR}
-)
-
-target_link_libraries(lb_load_data_store_test
-  ${_gRPC_PROTOBUF_LIBRARIES}
-  ${_gRPC_ALLTARGETS_LIBRARIES}
-  lb_load_data_store
-  grpc++_test_util
-  grpc_test_util
-  grpc++
-  grpc
-  gpr_test_util
-  gpr
-  ${_gRPC_GFLAGS_LIBRARIES}
-)
-
-endif (gRPC_BUILD_TESTS)
-if (gRPC_BUILD_TESTS)
-
 add_executable(memory_test
   test/core/gprpp/memory_test.cc
   third_party/googletest/googletest/src/gtest-all.cc

+ 63 - 0
CONCEPTS.md

@@ -0,0 +1,63 @@
+# gRPC Concepts Overview
+
+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.
+
+
+## Interface
+
+Developers using gRPC start with a language agnostic description of an RPC service (a collection
+of methods). From this description, gRPC will generate client and server side interfaces
+in any of the supported languages. The server implements
+the service interface, which can be remotely invoked by the client interface.
+
+By default, gRPC uses [Protocol Buffers](https://github.com/google/protobuf) as the
+Interface Definition Language (IDL) for describing both the service interface
+and the structure of the payload messages. It is possible to use other
+alternatives if desired.
+
+### Invoking & handling remote calls
+Starting from an interface definition in a .proto file, gRPC provides
+Protocol Compiler plugins that generate Client- and Server-side APIs.
+gRPC users call into these APIs on the Client side and implement
+the corresponding API on the server side.
+
+#### Synchronous vs. asynchronous
+Synchronous RPC calls, that block until a response arrives from the server, are
+the closest approximation to the abstraction of a procedure call that RPC
+aspires to.
+
+On the other hand, networks are inherently asynchronous and in many scenarios,
+it is desirable to have the ability to start RPCs without blocking the current
+thread.
+
+The gRPC programming surface in most languages comes in both synchronous and
+asynchronous flavors.
+
+
+## Streaming
+
+gRPC supports streaming semantics, where either the client or the server (or both)
+send a stream of messages on a single RPC call. The most general case is
+Bidirectional Streaming where a single gRPC call establishes a stream in which both
+the client and the server can send a stream of messages to each other. The streamed
+messages are delivered in the order they were sent.
+
+
+# Protocol
+
+The [gRPC protocol](doc/PROTOCOL-HTTP2.md) specifies the abstract requirements for communication between
+clients and servers. A concrete embedding over HTTP/2 completes the picture by
+fleshing out the details of each of the required operations.
+
+## Abstract gRPC protocol
+A gRPC call comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory `Call Header`, followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata`).
+
+## Implementation over HTTP/2
+The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPACK compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers).
+
+## Flow Control
+gRPC uses the flow control mechanism in HTTP/2. This enables fine-grained control of memory used for buffering in-flight messages.

+ 26 - 5
CONTRIBUTING.md

@@ -11,13 +11,34 @@ In order to protect both you and ourselves, you will need to sign the
 [Contributor License
 Agreement](https://identity.linuxfoundation.org/projects/cncf).
 
-## Running tests
+## Cloning the repository
 
-Use `tools/run_tests/run_tests.py` script to run the unit tests.  See
-[tools/run_tests](tools/run_tests) for how to run tests for a given language.
+Before starting any development work you will need a local copy of the gRPC repository.
+Please follow the instructions in [Building gRPC C++: Clone the repository](BUILDING.md#clone-the-repository-including-submodules).
 
-Prerequisites for building and running tests are listed in
-[INSTALL.md](INSTALL.md) and in `src/YOUR-LANGUAGE` (e.g. `src/csharp`)
+## Building & Running tests
+
+Different languages use different build systems. To hide the complexity
+of needing to build with many different build systems, a portable python
+script that unifies the experience of building and testing gRPC in different
+languages and on different platforms is provided.
+
+To build gRPC in the language of choice (e.g. `c++`, `csharp`, `php`, `python`, `ruby`, ...)
+- Prepare you development environment based on language-specific instructions in `src/YOUR-LANGUAGE` directory.
+- The language-specific instructions might involve installing C/C++ prerequisites listed in
+  [Building gRPC C++: Prerequisites](BUILDING.md#pre-requisites) as gRPC implementations
+  in this repository are using the native gRPC "core" library internally.
+- Run
+  ```
+  python tools/run_tests/run_tests.py -l YOUR_LANGUAGE --build_only
+  ```
+- To also run all the unit tests after building
+  ```
+  python tools/run_tests/run_tests.py -l YOUR_LANGUAGE
+  ```
+
+You can also run `python tools/run_tests/run_tests.py --help` to discover useful command line flags supported. For more details,
+see [tools/run_tests](tools/run_tests) where you will also find guidance on how to run various other test suites (e.g. interop tests, benchmarks)
 
 ## Generated project files
 

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 2 - 1
Makefile


+ 44 - 76
README.md

@@ -1,24 +1,59 @@
-[gRPC - An RPC library and framework](http://github.com/grpc/grpc)
+gRPC - An RPC library and framework
 ===================================
 
+gRPC is a modern, open source, high-performance remote procedure call (RPC) framework that can run anywhere. It enables client and server applications to communicate transparently, and makes it easier to build connected systems.
+
+<table>
+  <tr>
+    <td><b>Homepage:</b></td>
+    <td><a href="https://grpc.io/">grpc.io</a></td>
+  </tr>
+  <tr>
+    <td><b>Mailing List:</b></td>
+    <td><a href="https://groups.google.com/forum/#!forum/grpc-io">grpc-io@googlegroups.com</a></td>
+  </tr>
+</table>
+
 [![Join the chat at https://gitter.im/grpc/grpc](https://badges.gitter.im/grpc/grpc.svg)](https://gitter.im/grpc/grpc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 
-Copyright 2015
-[The gRPC Authors](https://github.com/grpc/grpc/blob/master/AUTHORS)
+# To start using gRPC
+
+To maximize usability, gRPC supports the standard way of adding dependencies in your language of choice (if there is one).
+In most languages, the gRPC runtime comes in form of a package available in your language's package manager.
+
+For instructions on how to use the language-specific gRPC runtime in your project, please refer to these documents
 
-# Documentation
+ * [C++](src/cpp): follow the instructions under the `src/cpp` directory
+ * [C#](src/csharp): NuGet package `Grpc`
+ * [Dart](https://github.com/grpc/grpc-dart): pub package `grpc`
+ * [Go](https://github.com/grpc/grpc-go): `go get google.golang.org/grpc`
+ * [Java](https://github.com/grpc/grpc-java): Use JARs from Maven Central Repository
+ * [Node](https://github.com/grpc/grpc-node): `npm install grpc`
+ * [Objective-C](src/objective-c): Add `gRPC-ProtoRPC` dependency to podspec
+ * [PHP](src/php): `pecl install grpc`
+ * [Python](src/python/grpcio): `pip install grpcio`
+ * [Ruby](src/ruby): `gem install grpc`
+ * [WebJS](https://github.com/grpc/grpc-web): follow the grpc-web instructions
 
-You can find more detailed documentation and examples in the [doc](doc) and [examples](examples) directories respectively.
+You can find per-language quickstart guides and tutorials in [Documentation section on grpc.io website](https://grpc.io/docs/). The code examples are available in the [examples](examples) directory.
 
-# Installation & Testing
+# To start developing gRPC
 
-See [INSTALL](INSTALL.md) for installation instructions for various platforms.
+Contributions are welcome!
 
-See [tools/run_tests](tools/run_tests) for more guidance on how to run various test suites (e.g. unit tests, interop tests, benchmarks)
+Please read [How to contribute](CONTRIBUTING.md) which will guide you through the entire workflow of how to build the source code, how to run the tests and how to contribute your changes to
+the gRPC codebase.
+The document also contains info on how the contributing process works and contains best practices for creating contributions.
+
+# Performance 
 
 See [Performance dashboard](http://performance-dot-grpc-testing.appspot.com/explore?dashboard=5636470266134528) for the performance numbers for the latest released version.
 
-# Repository Structure & Status
+# Concepts
+
+See [gRPC Concepts](CONCEPTS.md)
+
+# About This Repository
 
 This repository contains source code for gRPC libraries for multiple languages written on top of shared C core library [src/core](src/core).
 
@@ -42,70 +77,3 @@ Libraries in different languages may be in different states of development. We a
 | WebJS                   | [grpc-web](https://github.com/grpc/grpc-web)         |
 | Dart                    | [grpc-dart](https://github.com/grpc/grpc-dart)       |
 
-See [MANIFEST.md](MANIFEST.md) for a listing of top-level items in the
-repository.
-
-# Overview
-
-
-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.
-
-
-## Interface
-
-
-Developers using gRPC typically start with the description of an RPC service
-(a collection of methods), and generate client and server side interfaces
-which they use on the client-side and implement on the server side.
-
-By default, gRPC uses [Protocol Buffers](https://github.com/google/protobuf) as the
-Interface Definition Language (IDL) for describing both the service interface
-and the structure of the payload messages. It is possible to use other
-alternatives if desired.
-
-### Surface API
-Starting from an interface definition in a .proto file, gRPC provides
-Protocol Compiler plugins that generate Client- and Server-side APIs.
-gRPC users typically call into these APIs on the Client side and implement
-the corresponding API on the server side.
-
-#### Synchronous vs. asynchronous
-Synchronous RPC calls, that block until a response arrives from the server, are
-the closest approximation to the abstraction of a procedure call that RPC
-aspires to.
-
-On the other hand, networks are inherently asynchronous and in many scenarios,
-it is desirable to have the ability to start RPCs without blocking the current
-thread.
-
-The gRPC programming surface in most languages comes in both synchronous and
-asynchronous flavors.
-
-
-## Streaming
-
-gRPC supports streaming semantics, where either the client or the server (or both)
-send a stream of messages on a single RPC call. The most general case is
-Bidirectional Streaming where a single gRPC call establishes a stream where both
-the client and the server can send a stream of messages to each other. The streamed
-messages are delivered in the order they were sent.
-
-
-# Protocol
-
-The [gRPC protocol](doc/PROTOCOL-HTTP2.md) specifies the abstract requirements for communication between
-clients and servers. A concrete embedding over HTTP/2 completes the picture by
-fleshing out the details of each of the required operations.
-
-## Abstract gRPC protocol
-A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory `Call Header`, followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata`).
-
-## Implementation over HTTP/2
-The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPACK compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers).
-
-## Flow Control
-gRPC inherits the flow control mechanisms in HTTP/2 and uses them to enable fine-grained control of the amount of memory used for buffering in-flight messages.

+ 41 - 23
build.yaml

@@ -234,6 +234,7 @@ filegroups:
   - src/core/lib/channel/channel_stack.cc
   - src/core/lib/channel/channel_stack_builder.cc
   - src/core/lib/channel/channel_trace.cc
+  - src/core/lib/channel/channelz.cc
   - src/core/lib/channel/channelz_registry.cc
   - src/core/lib/channel/connected_channel.cc
   - src/core/lib/channel/handshaker.cc
@@ -404,6 +405,7 @@ filegroups:
   - src/core/lib/channel/channel_stack.h
   - src/core/lib/channel/channel_stack_builder.h
   - src/core/lib/channel/channel_trace.h
+  - src/core/lib/channel/channelz.h
   - src/core/lib/channel/channelz_registry.h
   - src/core/lib/channel/connected_channel.h
   - src/core/lib/channel/context.h
@@ -1904,15 +1906,6 @@ libs:
   - test/cpp/interop/interop_server_bootstrap.cc
   deps:
   - interop_server_lib
-- name: lb_load_data_store
-  build: private
-  language: c++
-  headers:
-  - src/cpp/server/load_reporter/load_data_store.h
-  src:
-  - src/cpp/server/load_reporter/load_data_store.cc
-  deps:
-  - grpc++
 - name: qps
   build: private
   language: c++
@@ -3906,6 +3899,28 @@ targets:
   - linux
   - posix
   uses_polling: false
+- name: bm_channel
+  build: test
+  language: c++
+  src:
+  - test/cpp/microbenchmarks/bm_channel.cc
+  deps:
+  - grpc_benchmark
+  - benchmark
+  - grpc++_test_util_unsecure
+  - grpc_test_util_unsecure
+  - grpc++_unsecure
+  - grpc_unsecure
+  - gpr_test_util
+  - gpr
+  - grpc++_test_config
+  benchmark: true
+  defaults: benchmark
+  platforms:
+  - mac
+  - linux
+  - posix
+  uses_polling: false
 - name: bm_chttp2_hpack
   build: test
   language: c++
@@ -4252,6 +4267,23 @@ targets:
   uses:
   - grpc++_test
   uses_polling: false
+- name: channelz_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/core/channel/channelz_test.cc
+  deps:
+  - grpc_test_util
+  - grpc++_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
+  filegroups:
+  - grpc++_channelz_proto
+  uses:
+  - grpc++_test
 - name: check_gcp_environment_linux_test
   build: test
   language: c++
@@ -4845,20 +4877,6 @@ targets:
   - mac
   - linux
   - posix
-- name: lb_load_data_store_test
-  gtest: true
-  build: test
-  language: c++
-  src:
-  - test/cpp/server/load_reporter/load_data_store_test.cc
-  deps:
-  - lb_load_data_store
-  - grpc++_test_util
-  - grpc_test_util
-  - grpc++
-  - grpc
-  - gpr_test_util
-  - gpr
 - name: memory_test
   gtest: true
   build: test

+ 4 - 2
config.m4

@@ -9,11 +9,12 @@ if test "$PHP_GRPC" != "no"; then
   PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
   PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/boringssl/include)
   PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/address_sorting/include)
+  PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/nanopb)
 
   LIBS="-lpthread $LIBS"
 
-  CFLAGS="-Wall -Werror -Wno-parentheses-equality -Wno-unused-value -std=c11 -g -O2 -D PB_FIELD_16BIT=1"
-  CXXFLAGS="-std=c++11 -fno-exceptions -fno-rtti -g -O2 -D PB_FIELD_16BIT=1"
+  CFLAGS="-Wall -Werror -Wno-parentheses-equality -Wno-unused-value -std=c11 -g -O2 -D PB_FIELD_32BIT=1"
+  CXXFLAGS="-std=c++11 -fno-exceptions -fno-rtti -g -O2 -D PB_FIELD_32BIT=1"
   GRPC_SHARED_LIBADD="-lpthread $GRPC_SHARED_LIBADD"
   PHP_REQUIRE_CXX()
   PHP_ADD_LIBRARY(pthread)
@@ -89,6 +90,7 @@ if test "$PHP_GRPC" != "no"; then
     src/core/lib/channel/channel_stack.cc \
     src/core/lib/channel/channel_stack_builder.cc \
     src/core/lib/channel/channel_trace.cc \
+    src/core/lib/channel/channelz.cc \
     src/core/lib/channel/channelz_registry.cc \
     src/core/lib/channel/connected_channel.cc \
     src/core/lib/channel/handshaker.cc \

+ 4 - 2
config.w32

@@ -65,6 +65,7 @@ if (PHP_GRPC != "no") {
     "src\\core\\lib\\channel\\channel_stack.cc " +
     "src\\core\\lib\\channel\\channel_stack_builder.cc " +
     "src\\core\\lib\\channel\\channel_trace.cc " +
+    "src\\core\\lib\\channel\\channelz.cc " +
     "src\\core\\lib\\channel\\channelz_registry.cc " +
     "src\\core\\lib\\channel\\connected_channel.cc " +
     "src\\core\\lib\\channel\\handshaker.cc " +
@@ -638,13 +639,14 @@ if (PHP_GRPC != "no") {
   EXTENSION("grpc", grpc_source, null,
     "/DOPENSSL_NO_ASM /D_GNU_SOURCE /DWIN32_LEAN_AND_MEAN "+
     "/D_HAS_EXCEPTIONS=0 /DNOMINMAX /DGRPC_ARES=0 /D_WIN32_WINNT=0x600 "+
-    "/DPB_FIELD_16BIT "+
+    "/DPB_FIELD_32BIT "+
     "/I"+configure_module_dirname+" "+
     "/I"+configure_module_dirname+"\\include "+
     "/I"+configure_module_dirname+"\\src\\php\\ext\\grpc "+
     "/I"+configure_module_dirname+"\\third_party\\boringssl\\include "+
     "/I"+configure_module_dirname+"\\third_party\\zlib "+
-    "/I"+configure_module_dirname+"\\third_party\\address_sorting\\include");
+    "/I"+configure_module_dirname+"\\third_party\\address_sorting\\include "+
+    "/I"+configure_module_dirname+"\\third_party\\nanopb");
 
   base_dir = get_define('BUILD_DIR');
   FSO.CreateFolder(base_dir+"\\ext");

+ 1 - 1
doc/command_line_tool.md

@@ -32,7 +32,7 @@ The command line tool should support the following things:
 
 To use the tool, you need to get the grpc repository and make sure your system
 has the prerequisites for building grpc from source, given in the [installation
-instructions](https://github.com/grpc/grpc/blob/master/INSTALL.md).
+instructions](../BUILDING.md).
 
 In order to build the grpc command line tool from a fresh clone of the grpc
 repository, you need to run the following command to update submodules:

+ 1 - 1
examples/cpp/README.md

@@ -3,7 +3,7 @@
 ## Installation
 
 To install gRPC on your system, follow the instructions to build from source
-[here](../../INSTALL.md). This also installs the protocol buffer compiler
+[here](../../BUILDING.md). This also installs the protocol buffer compiler
 `protoc` (if you don't have it already), and the C++ gRPC plugin for `protoc`.
 
 ## Hello C++ gRPC!

+ 2 - 2
examples/cpp/cpptutorial.md

@@ -35,7 +35,7 @@ updating.
 The example code for our tutorial is in [examples/cpp/route_guide](route_guide).
 You also should have the relevant tools installed to generate the server and
 client interface code - if you don't already, follow the setup instructions in
-[INSTALL.md](../../INSTALL.md).
+[BUILDING.md](../../BUILDING.md).
 
 ## Defining the service
 
@@ -130,7 +130,7 @@ a special gRPC C++ plugin.
 For simplicity, we've provided a [Makefile](route_guide/Makefile) that runs
 `protoc` for you with the appropriate plugin, input, and output (if you want to
 run this yourself, make sure you've installed protoc and followed the gRPC code
-[installation instructions](../../INSTALL.md) first):
+[installation instructions](../../BUILDING.md) first):
 
 ```shell
 $ make route_guide.grpc.pb.cc route_guide.pb.cc

+ 2 - 2
examples/cpp/helloworld/README.md

@@ -1,8 +1,8 @@
 # gRPC C++ Hello World Tutorial
 
 ### Install gRPC
-Make sure you have installed gRPC on your system. Follow the instructions here:
-[https://github.com/grpc/grpc/blob/master/INSTALL](../../../INSTALL.md).
+Make sure you have installed gRPC on your system. Follow the
+[BUILDING.md](../../../BUILDING.md) instructions.
 
 ### Get the tutorial source code
 

+ 6 - 0
examples/cpp/route_guide/README.md

@@ -0,0 +1,6 @@
+# gRPC Basics: C++ sample code
+
+The files in this folder are the samples used in [gRPC Basics: C++][],
+a detailed tutorial for using gRPC in C++.
+
+[gRPC Basics: C++]:https://grpc.io/docs/tutorials/basic/c.html

+ 2 - 0
gRPC-C++.podspec

@@ -347,6 +347,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/channel_stack.h',
                       'src/core/lib/channel/channel_stack_builder.h',
                       'src/core/lib/channel/channel_trace.h',
+                      'src/core/lib/channel/channelz.h',
                       'src/core/lib/channel/channelz_registry.h',
                       'src/core/lib/channel/connected_channel.h',
                       'src/core/lib/channel/context.h',
@@ -532,6 +533,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/channel/channel_stack.h',
                               'src/core/lib/channel/channel_stack_builder.h',
                               'src/core/lib/channel/channel_trace.h',
+                              'src/core/lib/channel/channelz.h',
                               'src/core/lib/channel/channelz_registry.h',
                               'src/core/lib/channel/connected_channel.h',
                               'src/core/lib/channel/context.h',

+ 4 - 0
gRPC-Core.podspec

@@ -357,6 +357,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/channel_stack.h',
                       'src/core/lib/channel/channel_stack_builder.h',
                       'src/core/lib/channel/channel_trace.h',
+                      'src/core/lib/channel/channelz.h',
                       'src/core/lib/channel/channelz_registry.h',
                       'src/core/lib/channel/connected_channel.h',
                       'src/core/lib/channel/context.h',
@@ -507,6 +508,7 @@ Pod::Spec.new do |s|
                       'src/core/lib/channel/channel_stack.cc',
                       'src/core/lib/channel/channel_stack_builder.cc',
                       'src/core/lib/channel/channel_trace.cc',
+                      'src/core/lib/channel/channelz.cc',
                       'src/core/lib/channel/channelz_registry.cc',
                       'src/core/lib/channel/connected_channel.cc',
                       'src/core/lib/channel/handshaker.cc',
@@ -936,6 +938,7 @@ Pod::Spec.new do |s|
                               'src/core/lib/channel/channel_stack.h',
                               'src/core/lib/channel/channel_stack_builder.h',
                               'src/core/lib/channel/channel_trace.h',
+                              'src/core/lib/channel/channelz.h',
                               'src/core/lib/channel/channelz_registry.h',
                               'src/core/lib/channel/connected_channel.h',
                               'src/core/lib/channel/context.h',
@@ -1194,6 +1197,7 @@ Pod::Spec.new do |s|
                       'test/core/end2end/tests/cancel_before_invoke.cc',
                       'test/core/end2end/tests/cancel_in_a_vacuum.cc',
                       'test/core/end2end/tests/cancel_with_status.cc',
+                      'test/core/end2end/tests/channelz.cc',
                       'test/core/end2end/tests/compressed_payload.cc',
                       'test/core/end2end/tests/connectivity.cc',
                       'test/core/end2end/tests/default_host.cc',

+ 0 - 2
grpc.def

@@ -45,8 +45,6 @@ EXPORTS
     grpc_insecure_channel_create
     grpc_lame_client_channel_create
     grpc_channel_destroy
-    grpc_channel_get_trace
-    grpc_channel_get_uuid
     grpc_call_cancel
     grpc_call_cancel_with_status
     grpc_call_ref

+ 2 - 0
grpc.gemspec

@@ -294,6 +294,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/channel_stack.h )
   s.files += %w( src/core/lib/channel/channel_stack_builder.h )
   s.files += %w( src/core/lib/channel/channel_trace.h )
+  s.files += %w( src/core/lib/channel/channelz.h )
   s.files += %w( src/core/lib/channel/channelz_registry.h )
   s.files += %w( src/core/lib/channel/connected_channel.h )
   s.files += %w( src/core/lib/channel/context.h )
@@ -444,6 +445,7 @@ Gem::Specification.new do |s|
   s.files += %w( src/core/lib/channel/channel_stack.cc )
   s.files += %w( src/core/lib/channel/channel_stack_builder.cc )
   s.files += %w( src/core/lib/channel/channel_trace.cc )
+  s.files += %w( src/core/lib/channel/channelz.cc )
   s.files += %w( src/core/lib/channel/channelz_registry.cc )
   s.files += %w( src/core/lib/channel/connected_channel.cc )
   s.files += %w( src/core/lib/channel/handshaker.cc )

+ 6 - 10
grpc.gyp

@@ -256,6 +256,7 @@
         'src/core/lib/channel/channel_stack.cc',
         'src/core/lib/channel/channel_stack_builder.cc',
         'src/core/lib/channel/channel_trace.cc',
+        'src/core/lib/channel/channelz.cc',
         'src/core/lib/channel/channelz_registry.cc',
         'src/core/lib/channel/connected_channel.cc',
         'src/core/lib/channel/handshaker.cc',
@@ -606,6 +607,7 @@
         'src/core/lib/channel/channel_stack.cc',
         'src/core/lib/channel/channel_stack_builder.cc',
         'src/core/lib/channel/channel_trace.cc',
+        'src/core/lib/channel/channelz.cc',
         'src/core/lib/channel/channelz_registry.cc',
         'src/core/lib/channel/connected_channel.cc',
         'src/core/lib/channel/handshaker.cc',
@@ -837,6 +839,7 @@
         'src/core/lib/channel/channel_stack.cc',
         'src/core/lib/channel/channel_stack_builder.cc',
         'src/core/lib/channel/channel_trace.cc',
+        'src/core/lib/channel/channelz.cc',
         'src/core/lib/channel/channelz_registry.cc',
         'src/core/lib/channel/connected_channel.cc',
         'src/core/lib/channel/handshaker.cc',
@@ -1046,6 +1049,7 @@
         'src/core/lib/channel/channel_stack.cc',
         'src/core/lib/channel/channel_stack_builder.cc',
         'src/core/lib/channel/channel_trace.cc',
+        'src/core/lib/channel/channelz.cc',
         'src/core/lib/channel/channelz_registry.cc',
         'src/core/lib/channel/connected_channel.cc',
         'src/core/lib/channel/handshaker.cc',
@@ -1645,16 +1649,6 @@
         'test/cpp/interop/interop_server_bootstrap.cc',
       ],
     },
-    {
-      'target_name': 'lb_load_data_store',
-      'type': 'static_library',
-      'dependencies': [
-        'grpc++',
-      ],
-      'sources': [
-        'src/cpp/server/load_reporter/load_data_store.cc',
-      ],
-    },
     {
       'target_name': 'qps',
       'type': 'static_library',
@@ -2616,6 +2610,7 @@
         'test/core/end2end/tests/cancel_before_invoke.cc',
         'test/core/end2end/tests/cancel_in_a_vacuum.cc',
         'test/core/end2end/tests/cancel_with_status.cc',
+        'test/core/end2end/tests/channelz.cc',
         'test/core/end2end/tests/compressed_payload.cc',
         'test/core/end2end/tests/connectivity.cc',
         'test/core/end2end/tests/default_host.cc',
@@ -2707,6 +2702,7 @@
         'test/core/end2end/tests/cancel_before_invoke.cc',
         'test/core/end2end/tests/cancel_in_a_vacuum.cc',
         'test/core/end2end/tests/cancel_with_status.cc',
+        'test/core/end2end/tests/channelz.cc',
         'test/core/end2end/tests/compressed_payload.cc',
         'test/core/end2end/tests/connectivity.cc',
         'test/core/end2end/tests/default_host.cc',

+ 0 - 8
include/grpc/grpc.h

@@ -286,14 +286,6 @@ GRPCAPI grpc_channel* grpc_lame_client_channel_create(
 /** Close and destroy a grpc channel */
 GRPCAPI void grpc_channel_destroy(grpc_channel* channel);
 
-/** Returns the JSON formatted channel trace for this channel. The caller
-    owns the returned string and is responsible for freeing it. */
-GRPCAPI char* grpc_channel_get_trace(grpc_channel* channel);
-
-/** Returns the channel uuid, which can be used to look up its trace at a
-    later time. */
-GRPCAPI intptr_t grpc_channel_get_uuid(grpc_channel* channel);
-
 /** Error handling for grpc_call
    Most grpc_call functions return a grpc_error. If the error is not GRPC_OK
    then the operation failed due to some unsatisfied precondition.

+ 4 - 0
include/grpc/impl/codegen/grpc_types.h

@@ -289,6 +289,10 @@ typedef struct {
  * subchannel. The default is 10. If set to 0, channel tracing is disabled. */
 #define GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE \
   "grpc.max_channel_trace_events_per_node"
+/** If non-zero, gRPC library will track stats and information at at per channel
+ * level. Disabling channelz naturally disables channel tracing. The default
+ * is for channelz to be disabled. */
+#define GRPC_ARG_ENABLE_CHANNELZ "grpc.enable_channelz"
 /** If non-zero, Cronet transport will coalesce packets to fewer frames
  * when possible. */
 #define GRPC_ARG_USE_CRONET_PACKET_COALESCING \

+ 2 - 2
include/grpcpp/impl/codegen/call.h

@@ -171,8 +171,8 @@ class WriteOptions {
     return *this;
   }
 
-  /// Guarantee that all bytes have been written to the wire before completing
-  /// this write (usually writes are completed when they pass flow control)
+  /// Guarantee that all bytes have been written to the socket before completing
+  /// this write (usually writes are completed when they pass flow control).
   inline WriteOptions& set_write_through() {
     SetBit(GRPC_WRITE_THROUGH);
     return *this;

+ 2 - 0
package.xml

@@ -299,6 +299,7 @@
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack_builder.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_trace.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/channelz.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channelz_registry.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/connected_channel.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/context.h" role="src" />
@@ -449,6 +450,7 @@
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack_builder.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_trace.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/channelz.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channelz_registry.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/connected_channel.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker.cc" role="src" />

+ 21 - 16
src/core/ext/transport/chttp2/transport/writing.cc

@@ -139,22 +139,27 @@ static bool update_list(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
 
 static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
                          const char* staller) {
-  gpr_log(
-      GPR_DEBUG,
-      "%s:%p stream %d stalled by %s [fc:pending=%" PRIdPTR
-      ":pending-compressed=%" PRIdPTR ":flowed=%" PRId64
-      ":peer_initwin=%d:t_win=%" PRId64 ":s_win=%d:s_delta=%" PRId64 "]",
-      t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length,
-      s->compressed_data_buffer.length, s->flow_controlled_bytes_flowed,
-      t->settings[GRPC_ACKED_SETTINGS]
-                 [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
-      t->flow_control->remote_window(),
-      static_cast<uint32_t> GPR_MAX(
-          0,
-          s->flow_control->remote_window_delta() +
-              (int64_t)t->settings[GRPC_PEER_SETTINGS]
-                                  [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]),
-      s->flow_control->remote_window_delta());
+  if (grpc_flowctl_trace.enabled()) {
+    gpr_log(
+        GPR_DEBUG,
+        "%s:%p stream %d moved to stalled list by %s. This is FULLY expected "
+        "to happen in a healthy program that is not seeing flow control stalls."
+        " However, if you know that there are unwanted stalls, here is some "
+        "helpful data: [fc:pending=%" PRIdPTR ":pending-compressed=%" PRIdPTR
+        ":flowed=%" PRId64 ":peer_initwin=%d:t_win=%" PRId64
+        ":s_win=%d:s_delta=%" PRId64 "]",
+        t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length,
+        s->compressed_data_buffer.length, s->flow_controlled_bytes_flowed,
+        t->settings[GRPC_ACKED_SETTINGS]
+                   [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        t->flow_control->remote_window(),
+        static_cast<uint32_t> GPR_MAX(
+            0,
+            s->flow_control->remote_window_delta() +
+                (int64_t)t->settings[GRPC_PEER_SETTINGS]
+                                    [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]),
+        s->flow_control->remote_window_delta());
+  }
 }
 
 static bool stream_ref_if_not_destroyed(gpr_refcount* r) {

+ 23 - 26
src/core/lib/channel/channel_trace.cc

@@ -28,7 +28,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "src/core/lib/channel/channelz_registry.h"
 #include "src/core/lib/channel/status_util.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gpr/useful.h"
@@ -40,16 +39,17 @@
 #include "src/core/lib/transport/error_utils.h"
 
 namespace grpc_core {
+namespace channelz {
 
 ChannelTrace::TraceEvent::TraceEvent(
     Severity severity, grpc_slice data,
-    RefCountedPtr<ChannelTrace> referenced_tracer, ReferencedType type)
+    RefCountedPtr<ChannelNode> referenced_channel, ReferencedType type)
     : severity_(severity),
       data_(data),
       timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
                                          GPR_CLOCK_REALTIME)),
       next_(nullptr),
-      referenced_tracer_(std::move(referenced_tracer)),
+      referenced_channel_(std::move(referenced_channel)),
       referenced_type_(type) {}
 
 ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data)
@@ -62,15 +62,13 @@ ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data)
 ChannelTrace::TraceEvent::~TraceEvent() { grpc_slice_unref_internal(data_); }
 
 ChannelTrace::ChannelTrace(size_t max_events)
-    : channel_uuid_(-1),
-      num_events_logged_(0),
+    : num_events_logged_(0),
       list_size_(0),
       max_list_size_(max_events),
       head_trace_(nullptr),
       tail_trace_(nullptr) {
   if (max_list_size_ == 0) return;  // tracing is disabled if max_events == 0
   gpr_mu_init(&tracer_mu_);
-  channel_uuid_ = ChannelzRegistry::Register(this);
   time_created_ = grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
                                           GPR_CLOCK_REALTIME);
 }
@@ -83,12 +81,9 @@ ChannelTrace::~ChannelTrace() {
     it = it->next();
     Delete<TraceEvent>(to_free);
   }
-  ChannelzRegistry::Unregister(channel_uuid_);
   gpr_mu_destroy(&tracer_mu_);
 }
 
-intptr_t ChannelTrace::GetUuid() const { return channel_uuid_; }
-
 void ChannelTrace::AddTraceEventHelper(TraceEvent* new_trace_event) {
   ++num_events_logged_;
   // first event case
@@ -117,20 +112,21 @@ void ChannelTrace::AddTraceEvent(Severity severity, grpc_slice data) {
 
 void ChannelTrace::AddTraceEventReferencingChannel(
     Severity severity, grpc_slice data,
-    RefCountedPtr<ChannelTrace> referenced_tracer) {
+    RefCountedPtr<ChannelNode> referenced_channel) {
   if (max_list_size_ == 0) return;  // tracing is disabled if max_events == 0
   // create and fill up the new event
-  AddTraceEventHelper(
-      New<TraceEvent>(severity, data, std::move(referenced_tracer), Channel));
+  AddTraceEventHelper(New<TraceEvent>(
+      severity, data, std::move(referenced_channel), ReferencedType::Channel));
 }
 
 void ChannelTrace::AddTraceEventReferencingSubchannel(
     Severity severity, grpc_slice data,
-    RefCountedPtr<ChannelTrace> referenced_tracer) {
+    RefCountedPtr<ChannelNode> referenced_subchannel) {
   if (max_list_size_ == 0) return;  // tracing is disabled if max_events == 0
   // create and fill up the new event
-  AddTraceEventHelper(New<TraceEvent>(
-      severity, data, std::move(referenced_tracer), Subchannel));
+  AddTraceEventHelper(New<TraceEvent>(severity, data,
+                                      std::move(referenced_subchannel),
+                                      ReferencedType::Subchannel));
 }
 
 namespace {
@@ -193,22 +189,24 @@ void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const {
   json_iterator =
       grpc_json_create_child(json_iterator, json, "timestamp",
                              fmt_time(timestamp_), GRPC_JSON_STRING, true);
-  if (referenced_tracer_ != nullptr) {
+  if (referenced_channel_ != nullptr) {
     char* uuid_str;
-    gpr_asprintf(&uuid_str, "%" PRIdPTR, referenced_tracer_->channel_uuid_);
+    gpr_asprintf(&uuid_str, "%" PRIdPTR, referenced_channel_->channel_uuid());
     grpc_json* child_ref = grpc_json_create_child(
         json_iterator, json,
-        (referenced_type_ == Channel) ? "channelRef" : "subchannelRef", nullptr,
-        GRPC_JSON_OBJECT, false);
+        (referenced_type_ == ReferencedType::Channel) ? "channelRef"
+                                                      : "subchannelRef",
+        nullptr, GRPC_JSON_OBJECT, false);
     json_iterator = grpc_json_create_child(
         nullptr, child_ref,
-        (referenced_type_ == Channel) ? "channelId" : "subchannelId", uuid_str,
-        GRPC_JSON_STRING, true);
+        (referenced_type_ == ReferencedType::Channel) ? "channelId"
+                                                      : "subchannelId",
+        uuid_str, GRPC_JSON_STRING, true);
     json_iterator = child_ref;
   }
 }
 
-char* ChannelTrace::RenderTrace() const {
+grpc_json* ChannelTrace::RenderJSON() const {
   if (!max_list_size_)
     return nullptr;  // tracing is disabled if max_events == 0
   grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT);
@@ -219,7 +217,7 @@ char* ChannelTrace::RenderTrace() const {
       grpc_json_create_child(json_iterator, json, "numEventsLogged",
                              num_events_logged_str, GRPC_JSON_STRING, true);
   json_iterator =
-      grpc_json_create_child(json_iterator, json, "creationTime",
+      grpc_json_create_child(json_iterator, json, "creationTimestamp",
                              fmt_time(time_created_), GRPC_JSON_STRING, true);
   grpc_json* events = grpc_json_create_child(json_iterator, json, "events",
                                              nullptr, GRPC_JSON_ARRAY, false);
@@ -231,9 +229,8 @@ char* ChannelTrace::RenderTrace() const {
     it->RenderTraceEvent(json_iterator);
     it = it->next();
   }
-  char* json_str = grpc_json_dump_to_string(json, 0);
-  grpc_json_destroy(json);
-  return json_str;
+  return json;
 }
 
+}  // namespace channelz
 }  // namespace grpc_core

+ 13 - 17
src/core/lib/channel/channel_trace.h

@@ -28,18 +28,18 @@
 #include "src/core/lib/json/json.h"
 
 namespace grpc_core {
+namespace channelz {
+
+class ChannelNode;
 
 // Object used to hold live data for a channel. This data is exposed via the
 // channelz service:
 // https://github.com/grpc/proposal/blob/master/A14-channelz.md
-class ChannelTrace : public RefCounted<ChannelTrace> {
+class ChannelTrace {
  public:
   ChannelTrace(size_t max_events);
   ~ChannelTrace();
 
-  // returns the tracer's uuid
-  intptr_t GetUuid() const;
-
   enum Severity {
     Unset = 0,  // never to be used
     Info,       // we start at 1 to avoid using proto default values
@@ -59,34 +59,30 @@ class ChannelTrace : public RefCounted<ChannelTrace> {
   // created a new subchannel, then it would record that with a TraceEvent
   // referencing the new subchannel.
   //
-  // TODO(ncteisen): Once channelz is implemented, the events should reference
-  // the overall channelz object, not just the ChannelTrace object.
   // TODO(ncteisen): as this call is used more and more throughout the gRPC
   // stack, determine if it makes more sense to accept a char* instead of a
   // slice.
   void AddTraceEventReferencingChannel(
       Severity severity, grpc_slice data,
-      RefCountedPtr<ChannelTrace> referenced_tracer);
+      RefCountedPtr<ChannelNode> referenced_channel);
   void AddTraceEventReferencingSubchannel(
       Severity severity, grpc_slice data,
-      RefCountedPtr<ChannelTrace> referenced_tracer);
+      RefCountedPtr<ChannelNode> referenced_subchannel);
 
-  // Returns the tracing data rendered as a grpc json string.
-  // The string is owned by the caller and must be freed.
-  char* RenderTrace() const;
+  // Creates and returns the raw grpc_json object, so a parent channelz
+  // object may incorporate the json before rendering.
+  grpc_json* RenderJSON() const;
 
  private:
   // Types of objects that can be references by trace events.
-  enum ReferencedType { Channel, Subchannel };
+  enum class ReferencedType { Channel, Subchannel };
   // Private class to encapsulate all the data and bookkeeping needed for a
   // a trace event.
   class TraceEvent {
    public:
     // Constructor for a TraceEvent that references a different channel.
-    // TODO(ncteisen): once channelz is implemented, this should reference the
-    // overall channelz object, not just the ChannelTrace object
     TraceEvent(Severity severity, grpc_slice data,
-               RefCountedPtr<ChannelTrace> referenced_tracer,
+               RefCountedPtr<ChannelNode> referenced_channel,
                ReferencedType type);
 
     // Constructor for a TraceEvent that does not reverence a different
@@ -109,7 +105,7 @@ class ChannelTrace : public RefCounted<ChannelTrace> {
     gpr_timespec timestamp_;
     TraceEvent* next_;
     // the tracer object for the (sub)channel that this trace event refers to.
-    RefCountedPtr<ChannelTrace> referenced_tracer_;
+    RefCountedPtr<ChannelNode> referenced_channel_;
     // the type that the referenced tracer points to. Unused if this trace
     // does not point to any channel or subchannel
     ReferencedType referenced_type_;
@@ -119,7 +115,6 @@ class ChannelTrace : public RefCounted<ChannelTrace> {
   void AddTraceEventHelper(TraceEvent* new_trace_event);
 
   gpr_mu tracer_mu_;
-  intptr_t channel_uuid_;
   uint64_t num_events_logged_;
   size_t list_size_;
   size_t max_list_size_;
@@ -128,6 +123,7 @@ class ChannelTrace : public RefCounted<ChannelTrace> {
   gpr_timespec time_created_;
 };
 
+}  // namespace channelz
 }  // namespace grpc_core
 
 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H */

+ 185 - 0
src/core/lib/channel/channelz.cc

@@ -0,0 +1,185 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include "src/core/lib/channel/channelz.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/core/lib/channel/channelz_registry.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+
+namespace grpc_core {
+namespace channelz {
+
+namespace {
+
+// TODO(ncteisen): move this function to a common helper location.
+//
+// returns an allocated string that represents tm according to RFC-3339, and,
+// more specifically, follows:
+// https://developers.google.com/protocol-buffers/docs/proto3#json
+//
+// "Uses RFC 3339, where generated output will always be Z-normalized and uses
+// 0, 3, 6 or 9 fractional digits."
+char* fmt_time(gpr_timespec tm) {
+  char time_buffer[35];
+  char ns_buffer[11];  // '.' + 9 digits of precision
+  struct tm* tm_info = localtime((const time_t*)&tm.tv_sec);
+  strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%H:%M:%S", tm_info);
+  snprintf(ns_buffer, 11, ".%09d", tm.tv_nsec);
+  // This loop trims off trailing zeros by inserting a null character that the
+  // right point. We iterate in chunks of three because we want 0, 3, 6, or 9
+  // fractional digits.
+  for (int i = 7; i >= 1; i -= 3) {
+    if (ns_buffer[i] == '0' && ns_buffer[i + 1] == '0' &&
+        ns_buffer[i + 2] == '0') {
+      ns_buffer[i] = '\0';
+      // Edge case in which all fractional digits were 0.
+      if (i == 1) {
+        ns_buffer[0] = '\0';
+      }
+    } else {
+      break;
+    }
+  }
+  char* full_time_str;
+  gpr_asprintf(&full_time_str, "%s%sZ", time_buffer, ns_buffer);
+  return full_time_str;
+}
+
+// TODO(ncteisen); move this to json library
+grpc_json* add_num_str(grpc_json* parent, grpc_json* it, const char* name,
+                       int64_t num) {
+  char* num_str;
+  gpr_asprintf(&num_str, "%" PRId64, num);
+  return grpc_json_create_child(it, parent, name, num_str, GRPC_JSON_STRING,
+                                true);
+}
+
+}  // namespace
+
+ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes)
+    : channel_(channel), target_(nullptr), channel_uuid_(-1) {
+  trace_.Init(channel_tracer_max_nodes);
+  target_ = UniquePtr<char>(grpc_channel_get_target(channel_));
+  channel_uuid_ = ChannelzRegistry::Register(this);
+  gpr_atm_no_barrier_store(&last_call_started_millis_,
+                           (gpr_atm)ExecCtx::Get()->Now());
+}
+
+ChannelNode::~ChannelNode() {
+  trace_.Destroy();
+  ChannelzRegistry::Unregister(channel_uuid_);
+}
+
+void ChannelNode::RecordCallStarted() {
+  gpr_atm_no_barrier_fetch_add(&calls_started_, (gpr_atm)1);
+  gpr_atm_no_barrier_store(&last_call_started_millis_,
+                           (gpr_atm)ExecCtx::Get()->Now());
+}
+
+grpc_connectivity_state ChannelNode::GetConnectivityState() {
+  if (channel_ == nullptr) {
+    return GRPC_CHANNEL_SHUTDOWN;
+  } else {
+    return grpc_channel_check_connectivity_state(channel_, false);
+  }
+}
+
+char* ChannelNode::RenderJSON() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = add_num_str(json, json_iterator, "channelId", channel_uuid_);
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  // create and fill the connectivity state child.
+  grpc_connectivity_state connectivity_state = GetConnectivityState();
+  json_iterator = grpc_json_create_child(json_iterator, json, "state", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  grpc_json_create_child(nullptr, json, "state",
+                         grpc_connectivity_state_name(connectivity_state),
+                         GRPC_JSON_STRING, false);
+  // reset the parent to be the data object.
+  json = data;
+  json_iterator = grpc_json_create_child(
+      json_iterator, json, "target", target_.get(), GRPC_JSON_STRING, false);
+  // fill in the channel trace if applicable
+  grpc_json* trace = trace_->RenderJSON();
+  if (trace != nullptr) {
+    // we manuall link up and fill the child since it was created for us in
+    // ChannelTrace::RenderJSON
+    json_iterator = grpc_json_link_child(json, trace, json_iterator);
+    trace->parent = json;
+    trace->value = nullptr;
+    trace->key = "trace";
+    trace->owns_value = false;
+  }
+  // reset the parent to be the data object.
+  json = data;
+  json_iterator = nullptr;
+  // We use -1 as sentinel values since proto default value for integers is
+  // zero, and the confuses the parser into thinking the value weren't present
+  json_iterator =
+      add_num_str(json, json_iterator, "callsStarted", calls_started_);
+  json_iterator =
+      add_num_str(json, json_iterator, "callsSucceeded", calls_succeeded_);
+  json_iterator =
+      add_num_str(json, json_iterator, "callsFailed", calls_failed_);
+  gpr_timespec ts =
+      grpc_millis_to_timespec(last_call_started_millis_, GPR_CLOCK_REALTIME);
+  json_iterator =
+      grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
+                             fmt_time(ts), GRPC_JSON_STRING, true);
+  // render and return the over json object
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+}  // namespace channelz
+}  // namespace grpc_core

+ 85 - 0
src/core/lib/channel/channelz.h

@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNELZ_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNELZ_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/json/json.h"
+
+namespace grpc_core {
+namespace channelz {
+
+namespace testing {
+class ChannelNodePeer;
+}
+
+class ChannelNode : public RefCounted<ChannelNode> {
+ public:
+  ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes);
+  ~ChannelNode();
+
+  void RecordCallStarted();
+  void RecordCallFailed() {
+    gpr_atm_no_barrier_fetch_add(&calls_failed_, (gpr_atm(1)));
+  }
+  void RecordCallSucceeded() {
+    gpr_atm_no_barrier_fetch_add(&calls_succeeded_, (gpr_atm(1)));
+  }
+
+  char* RenderJSON();
+
+  ChannelTrace* trace() { return trace_.get(); }
+
+  void set_channel_destroyed() {
+    GPR_ASSERT(channel_ != nullptr);
+    channel_ = nullptr;
+  }
+
+  intptr_t channel_uuid() { return channel_uuid_; }
+
+ private:
+  // testing peer friend.
+  friend class testing::ChannelNodePeer;
+
+  // helper for getting connectivity state.
+  grpc_connectivity_state GetConnectivityState();
+
+  grpc_channel* channel_ = nullptr;
+  UniquePtr<char> target_;
+  gpr_atm calls_started_ = 0;
+  gpr_atm calls_succeeded_ = 0;
+  gpr_atm calls_failed_ = 0;
+  gpr_atm last_call_started_millis_ = 0;
+  intptr_t channel_uuid_;
+  ManualConstructor<ChannelTrace> trace_;
+};
+
+}  // namespace channelz
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNELZ_H */

+ 9 - 0
src/core/lib/iomgr/socket_utils.h

@@ -23,12 +23,21 @@
 
 #include <stddef.h>
 
+// TODO(juanlishen): The following functions might be simple enough to implement
+// ourselves, so that they don't cause any portability hassle.
+
 /* A wrapper for htons on POSIX and Windows */
 uint16_t grpc_htons(uint16_t hostshort);
 
 /* A wrapper for ntohs on POSIX and WINDOWS */
 uint16_t grpc_ntohs(uint16_t netshort);
 
+/* A wrapper for htonl on POSIX and Windows */
+uint32_t grpc_htonl(uint32_t hostlong);
+
+/* A wrapper for ntohl on POSIX and WINDOWS */
+uint32_t grpc_ntohl(uint32_t netlong);
+
 /* A wrapper for inet_pton on POSIX and WINDOWS */
 int grpc_inet_pton(int af, const char* src, void* dst);
 

+ 4 - 0
src/core/lib/iomgr/socket_utils_common_posix.cc

@@ -339,6 +339,10 @@ uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); }
 
 uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); }
 
+uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); }
+
+uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); }
+
 int grpc_inet_pton(int af, const char* src, void* dst) {
   return inet_pton(af, src, dst);
 }

+ 4 - 0
src/core/lib/iomgr/socket_utils_uv.cc

@@ -33,6 +33,10 @@ uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); }
 
 uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); }
 
+uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); }
+
+uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); }
+
 int grpc_inet_pton(int af, const char* src, void* dst) {
   return inet_pton(af, src, dst);
 }

+ 4 - 0
src/core/lib/iomgr/socket_utils_windows.cc

@@ -31,6 +31,10 @@ uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); }
 
 uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); }
 
+uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); }
+
+uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); }
+
 int grpc_inet_pton(int af, const char* src, void* dst) {
   return inet_pton(af, src, dst);
 }

+ 21 - 8
src/core/lib/surface/call.cc

@@ -489,6 +489,12 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args,
                                                &call->pollent);
   }
 
+  grpc_core::channelz::ChannelNode* channelz_channel =
+      grpc_channel_get_channelz_node(call->channel);
+  if (channelz_channel != nullptr) {
+    channelz_channel->RecordCallStarted();
+  }
+
   grpc_slice_unref_internal(path);
 
   return error;
@@ -531,7 +537,6 @@ static void release_call(void* call, grpc_error* error) {
   GRPC_CHANNEL_INTERNAL_UNREF(channel, "call");
 }
 
-static void set_status_value_directly(grpc_status_code status, void* dest);
 static void destroy_call(void* call, grpc_error* error) {
   GPR_TIMER_SCOPE("destroy_call", 0);
   size_t i;
@@ -1087,13 +1092,12 @@ static void recv_trailing_filter(void* args, grpc_metadata_batch* b) {
   if (b->idx.named.grpc_status != nullptr) {
     grpc_status_code status_code =
         grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md);
-    grpc_error* error =
-        status_code == GRPC_STATUS_OK
-            ? GRPC_ERROR_NONE
-            : grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
-                                     "Error received from peer"),
-                                 GRPC_ERROR_INT_GRPC_STATUS,
-                                 static_cast<intptr_t>(status_code));
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (status_code != GRPC_STATUS_OK) {
+      error = grpc_error_set_int(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error received from peer"),
+          GRPC_ERROR_INT_GRPC_STATUS, static_cast<intptr_t>(status_code));
+    }
     if (b->idx.named.grpc_message != nullptr) {
       error = grpc_error_set_str(
           error, GRPC_ERROR_STR_GRPC_MESSAGE,
@@ -1260,6 +1264,15 @@ static void post_batch_completion(batch_control* bctl) {
       get_final_status(call, set_cancelled_value,
                        call->final_op.server.cancelled, nullptr, nullptr);
     }
+    grpc_core::channelz::ChannelNode* channelz_channel =
+        grpc_channel_get_channelz_node(call->channel);
+    if (channelz_channel != nullptr) {
+      if (*call->final_op.client.status != GRPC_STATUS_OK) {
+        channelz_channel->RecordCallFailed();
+      } else {
+        channelz_channel->RecordCallSucceeded();
+      }
+    }
     GRPC_ERROR_UNREF(error);
     error = GRPC_ERROR_NONE;
   }

+ 20 - 13
src/core/lib/surface/channel.cc

@@ -32,6 +32,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/channel/channelz.h"
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/gpr/string.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
@@ -66,7 +67,7 @@ struct grpc_channel {
   gpr_mu registered_call_mu;
   registered_call* registered_calls;
 
-  grpc_core::RefCountedPtr<grpc_core::ChannelTrace> tracer;
+  grpc_core::RefCountedPtr<grpc_core::channelz::ChannelNode> channelz_channel;
 
   char* target;
 };
@@ -103,6 +104,7 @@ grpc_channel* grpc_channel_create_with_builder(
   channel->target = target;
   channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type);
   size_t channel_tracer_max_nodes = 0;  // default to off
+  bool channelz_enabled = false;
   gpr_mu_init(&channel->registered_call_mu);
   channel->registered_calls = nullptr;
 
@@ -141,15 +143,20 @@ grpc_channel* grpc_channel_create_with_builder(
       const grpc_integer_options options = {0, 0, INT_MAX};
       channel_tracer_max_nodes =
           (size_t)grpc_channel_arg_get_integer(&args->args[i], options);
+    } else if (0 == strcmp(args->args[i].key, GRPC_ARG_ENABLE_CHANNELZ)) {
+      channelz_enabled = grpc_channel_arg_get_bool(&args->args[i], false);
     }
   }
 
   grpc_channel_args_destroy(args);
-  channel->tracer = grpc_core::MakeRefCounted<grpc_core::ChannelTrace>(
-      channel_tracer_max_nodes);
-  channel->tracer->AddTraceEvent(
-      grpc_core::ChannelTrace::Severity::Info,
-      grpc_slice_from_static_string("Channel created"));
+  if (channelz_enabled) {
+    channel->channelz_channel =
+        grpc_core::MakeRefCounted<grpc_core::channelz::ChannelNode>(
+            channel, channel_tracer_max_nodes);
+    channel->channelz_channel->trace()->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string("Channel created"));
+  }
   return channel;
 }
 
@@ -184,12 +191,9 @@ static grpc_channel_args* build_channel_args(
   return grpc_channel_args_copy_and_add(input_args, new_args, num_new_args);
 }
 
-char* grpc_channel_get_trace(grpc_channel* channel) {
-  return channel->tracer->RenderTrace();
-}
-
-intptr_t grpc_channel_get_uuid(grpc_channel* channel) {
-  return channel->tracer->GetUuid();
+grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node(
+    grpc_channel* channel) {
+  return channel->channelz_channel.get();
 }
 
 grpc_channel* grpc_channel_create(const char* target,
@@ -395,6 +399,10 @@ void grpc_channel_internal_unref(grpc_channel* c REF_ARG) {
 
 static void destroy_channel(void* arg, grpc_error* error) {
   grpc_channel* channel = static_cast<grpc_channel*>(arg);
+  if (channel->channelz_channel != nullptr) {
+    channel->channelz_channel->set_channel_destroyed();
+    channel->channelz_channel.reset();
+  }
   grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel));
   while (channel->registered_calls) {
     registered_call* rc = channel->registered_calls;
@@ -403,7 +411,6 @@ static void destroy_channel(void* arg, grpc_error* error) {
     GRPC_MDELEM_UNREF(rc->authority);
     gpr_free(rc);
   }
-  channel->tracer.reset();
   gpr_mu_destroy(&channel->registered_call_mu);
   gpr_free(channel->target);
   gpr_free(channel);

+ 4 - 0
src/core/lib/surface/channel.h

@@ -23,6 +23,7 @@
 
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/channel/channelz.h"
 #include "src/core/lib/surface/channel_stack_type.h"
 
 grpc_channel* grpc_channel_create(const char* target,
@@ -50,6 +51,9 @@ grpc_call* grpc_channel_create_pollset_set_call(
 /** Get a (borrowed) pointer to this channels underlying channel stack */
 grpc_channel_stack* grpc_channel_get_channel_stack(grpc_channel* channel);
 
+grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node(
+    grpc_channel* channel);
+
 /** Get a grpc_mdelem of grpc-status: X where X is the numeric value of
     status_code.
 

+ 56 - 8
src/cpp/README.md

@@ -1,13 +1,62 @@
 
 # Overview
 
-This directory contains source code for C++ implementation of gRPC.
+A C++ implementation of gRPC
 
-To install gRPC for C++ on your system, follow the instructions to build from source
-[here](../../INSTALL.md). This also installs the protocol buffer compiler
-`protoc` (if you don't have it already), and the C++ gRPC plugin for `protoc`.
+# To start using gRPC C++
 
-# Documentation
+In the C++ world, there's no universally accepted standard for managing project dependencies.
+Therefore, gRPC supports several major build systems, which should satisfy most users.
+
+## bazel
+
+We recommend using Bazel for projects that use gRPC as it will give you the best developer experience
+(easy handling of dependencies that support bazel & fast builds).
+
+To add gRPC as a dependency in bazel:
+1. determine commit SHA for the grpc release you want to use
+2. Use the [http_archive](https://docs.bazel.build/versions/master/be/workspace.html#http_archive) bazel rule to include gRPC source
+  ```
+  http_archive(
+      name = "grpc",
+      urls = [
+          "https://github.com/grpc/grpc/archive/YOUR_GRPC_COMMIT_SHA.tar.gz",
+      ],
+      strip_prefix = "grpc-YOUR_GRPC_COMMIT_SHA",
+  )
+  ```
+
+NOTE: currently bazel is only supported for building gRPC on Linux.
+
+## make
+
+Currently the default choice for building on UNIX based systems is `make`.
+
+To install gRPC for C++ on your system using `make`, follow the [Building gRPC C++](../../BUILDING.md)
+instructions to build from source and then install locally using `make install`.
+This also installs the protocol buffer compiler `protoc` (if you don't have it already),
+and the C++ gRPC plugin for `protoc`.
+
+WARNING: After installing with `make install` there is no easy way to uninstall, which can cause issues
+if you later want to remove the grpc and/or protobuf installation or upgrade to a newer version.
+
+## cmake
+
+`cmake` is the default build option on Windows, but also works on Linux, MacOS. `cmake` has good
+support for crosscompiling and can be used for targeting Android platform.
+
+If your project is using cmake, there are several ways to add gRPC dependency.
+- install gRPC via cmake first and then locate it with `find_package(gRPC CONFIG)`. [Example](../../examples/cpp/helloworld/CMakeLists.txt)
+- via cmake's `ExternalProject_Add` using a technique called "superbuild". [Example](../../examples/cpp/helloworld/cmake_externalproject/CMakeLists.txt)
+- add gRPC source tree to your project (preferrably as a git submodule) and add it to your cmake project with `add_subdirectory`. [Example](../../examples/cpp/helloworld/CMakeLists.txt)
+
+## Packaging systems
+
+There's no standard packaging system for C++. We've looked into supporting some (e.g. Conan and vcpkg) but we are not there yet.
+Contributions and community-maintained packages for popular packaging systems are welcome!
+
+
+## Examples & Additional Documentation
 
 You can find out how to build and run our simplest gRPC C++ example in our
 [C++ quick start](../../examples/cpp).
@@ -25,7 +74,6 @@ documentation site at [grpc.io](https://grpc.io), specifically:
   APIs.
 
 
-# Examples
+# To start developing gRPC C++
 
-Code examples for gRPC C++ live in this repository's
-[examples/cpp](../../examples/cpp) directory.
+For instructions on how to build gRPC C++ from source, follow the [Building gRPC C++](../../BUILDING.md) instructions.

+ 4 - 2
src/cpp/ext/filters/census/client_filter.cc

@@ -119,8 +119,10 @@ void CensusClientCallData::StartTransportStreamOpBatch(
   }
   if (op->recv_trailing_metadata() != nullptr) {
     recv_trailing_metadata_ = op->recv_trailing_metadata()->batch();
-    initial_on_done_recv_trailing_metadata_ = op->on_complete();
-    op->set_on_complete(&on_done_recv_trailing_metadata_);
+    initial_on_done_recv_trailing_metadata_ =
+        op->op()->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    op->op()->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &on_done_recv_trailing_metadata_;
   }
   // Call next op.
   grpc_call_next_op(elem, op->op());

+ 71 - 0
src/cpp/server/load_reporter/constants.h

@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+namespace grpc {
+namespace load_reporter {
+
+constexpr size_t kLbIdLength = 8;
+constexpr size_t kIpv4AddressLength = 8;
+constexpr size_t kIpv6AddressLength = 32;
+
+constexpr char kInvalidLbId[] = "<INVALID_LBID_238dsb234890rb>";
+
+// Call statuses.
+
+constexpr char kCallStatusOk[] = "OK";
+constexpr char kCallStatusServerError[] = "5XX";
+constexpr char kCallStatusClientError[] = "4XX";
+
+// Tag keys.
+
+constexpr char kTagKeyToken[] = "token";
+constexpr char kTagKeyHost[] = "host";
+constexpr char kTagKeyUserId[] = "user_id";
+constexpr char kTagKeyStatus[] = "status";
+constexpr char kTagKeyMetricName[] = "metric_name";
+
+// Measure names.
+
+constexpr char kMeasureStartCount[] = "grpc.io/lb/start_count";
+constexpr char kMeasureEndCount[] = "grpc.io/lb/end_count";
+constexpr char kMeasureEndBytesSent[] = "grpc.io/lb/bytes_sent";
+constexpr char kMeasureEndBytesReceived[] = "grpc.io/lb/bytes_received";
+constexpr char kMeasureEndLatencyMs[] = "grpc.io/lb/latency_ms";
+constexpr char kMeasureOtherCallMetric[] = "grpc.io/lb/other_call_metric";
+
+// View names.
+
+constexpr char kViewStartCount[] = "grpc.io/lb_view/start_count";
+constexpr char kViewEndCount[] = "grpc.io/lb_view/end_count";
+constexpr char kViewEndBytesSent[] = "grpc.io/lb_view/bytes_sent";
+constexpr char kViewEndBytesReceived[] = "grpc.io/lb_view/bytes_received";
+constexpr char kViewEndLatencyMs[] = "grpc.io/lb_view/latency_ms";
+constexpr char kViewOtherCallMetricCount[] =
+    "grpc.io/lb_view/other_call_metric_count";
+constexpr char kViewOtherCallMetricValue[] =
+    "grpc.io/lb_view/other_call_metric_value";
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H

+ 36 - 0
src/cpp/server/load_reporter/get_cpu_stats.h

@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <utility>
+
+namespace grpc {
+namespace load_reporter {
+
+// Reads the CPU stats (in a pair of busy and total numbers) from the system.
+// The units of the stats should be the same.
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl();
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H

+ 45 - 0
src/cpp/server/load_reporter/get_cpu_stats_linux.cc

@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX
+
+#include <cstdio>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  FILE* fp;
+  fp = fopen("/proc/stat", "r");
+  uint64_t user, nice, system, idle;
+  fscanf(fp, "cpu %lu %lu %lu %lu", &user, &nice, &system, &idle);
+  fclose(fp);
+  busy = user + nice + system;
+  total = busy + idle;
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GPR_LINUX

+ 45 - 0
src/cpp/server/load_reporter/get_cpu_stats_macos.cc

@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_APPLE
+
+#include <mach/mach.h>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  host_cpu_load_info_data_t cpuinfo;
+  mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
+  if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
+                      (host_info_t)&cpuinfo, &count) == KERN_SUCCESS) {
+    for (int i = 0; i < CPU_STATE_MAX; i++) total += cpuinfo.cpu_ticks[i];
+    busy = total - cpuinfo.cpu_ticks[CPU_STATE_IDLE];
+  }
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GPR_APPLE

+ 40 - 0
src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc

@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS) && !defined(GPR_APPLE)
+
+#include <grpc/support/log.h>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  gpr_log(GPR_ERROR,
+          "Platforms other than Linux, Windows, and MacOS are not supported.");
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // !defined(GPR_LINUX) && !defined(GPR_WINDOWS) && !defined(GPR_APPLE)

+ 55 - 0
src/cpp/server/load_reporter/get_cpu_stats_windows.cc

@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+#include <windows.h>
+#include <cstdint>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+namespace {
+
+uint64_t FiletimeToInt(const FILETIME& ft) {
+  ULARGE_INTEGER i;
+  i.LowPart = ft.dwLowDateTime;
+  i.HighPart = ft.dwHighDateTime;
+  return i.QuadPart;
+}
+
+}  // namespace
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  FILETIME idle, kernel, user;
+  if (GetSystemTimes(&idle, &kernel, &user) != 0) {
+    total = FiletimeToInt(kernel) + FiletimeToInt(user);
+    busy = total - FiletimeToInt(idle);
+  }
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GPR_WINDOWS

+ 65 - 0
src/cpp/server/load_reporter/load_data_store.cc

@@ -16,11 +16,15 @@
  *
  */
 
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <stdio.h>
 #include <cstdlib>
 #include <set>
 #include <unordered_map>
 #include <vector>
 
+#include "src/core/lib/iomgr/socket_utils.h"
 #include "src/cpp/server/load_reporter/load_data_store.h"
 
 namespace grpc {
@@ -73,6 +77,67 @@ const typename C::value_type* RandomElement(const C& container) {
 
 }  // namespace
 
+LoadRecordKey::LoadRecordKey(const grpc::string& client_ip_and_token,
+                             grpc::string user_id)
+    : user_id_(std::move(user_id)) {
+  GPR_ASSERT(client_ip_and_token.size() >= 2);
+  int ip_hex_size;
+  GPR_ASSERT(sscanf(client_ip_and_token.substr(0, 2).c_str(), "%d",
+                    &ip_hex_size) == 1);
+  GPR_ASSERT(ip_hex_size == 0 || ip_hex_size == kIpv4AddressLength ||
+             ip_hex_size == kIpv6AddressLength);
+  size_t cur_pos = 2;
+  client_ip_hex_ = client_ip_and_token.substr(cur_pos, ip_hex_size);
+  cur_pos += ip_hex_size;
+  if (client_ip_and_token.size() - cur_pos < kLbIdLength) {
+    lb_id_ = kInvalidLbId;
+    lb_tag_ = "";
+  } else {
+    lb_id_ = client_ip_and_token.substr(cur_pos, kLbIdLength);
+    lb_tag_ = client_ip_and_token.substr(cur_pos + kLbIdLength);
+  }
+}
+
+grpc::string LoadRecordKey::GetClientIpBytes() const {
+  if (client_ip_hex_.empty()) {
+    return "";
+  } else if (client_ip_hex_.size() == kIpv4AddressLength) {
+    uint32_t ip_bytes;
+    if (sscanf(client_ip_hex_.c_str(), "%x", &ip_bytes) != 1) {
+      gpr_log(GPR_ERROR,
+              "Can't parse client IP (%s) from a hex string to an integer.",
+              client_ip_hex_.c_str());
+      return "";
+    }
+    ip_bytes = grpc_htonl(ip_bytes);
+    return grpc::string(reinterpret_cast<const char*>(&ip_bytes),
+                        sizeof(ip_bytes));
+  } else if (client_ip_hex_.size() == kIpv6AddressLength) {
+    uint32_t ip_bytes[4];
+    for (size_t i = 0; i < 4; ++i) {
+      if (sscanf(client_ip_hex_.substr(i * 8, (i + 1) * 8).c_str(), "%x",
+                 ip_bytes + i) != 1) {
+        gpr_log(
+            GPR_ERROR,
+            "Can't parse client IP part (%s) from a hex string to an integer.",
+            client_ip_hex_.substr(i * 8, (i + 1) * 8).c_str());
+        return "";
+      }
+      ip_bytes[i] = grpc_htonl(ip_bytes[i]);
+    }
+    return grpc::string(reinterpret_cast<const char*>(ip_bytes),
+                        sizeof(ip_bytes));
+  } else {
+    GPR_UNREACHABLE_CODE(return "");
+  }
+}
+
+LoadRecordValue::LoadRecordValue(grpc::string metric_name, uint64_t num_calls,
+                                 double total_metric_value) {
+  call_metrics_.emplace(std::move(metric_name),
+                        CallMetricValue(num_calls, total_metric_value));
+}
+
 void PerBalancerStore::MergeRow(const LoadRecordKey& key,
                                 const LoadRecordValue& value) {
   // During suspension, the load data received will be dropped.

+ 21 - 13
src/cpp/server/load_reporter/load_data_store.h

@@ -28,12 +28,11 @@
 #include <grpc/support/log.h>
 #include <grpcpp/impl/codegen/config.h>
 
+#include "src/cpp/server/load_reporter/constants.h"
+
 namespace grpc {
 namespace load_reporter {
 
-constexpr char kInvalidLbId[] = "<INVALID_LBID_238dsb234890rb>";
-constexpr uint8_t kLbIdLen = 8;
-
 // The load data storage is organized in hierarchy. The LoadDataStore is the
 // top-level data store. In LoadDataStore, for each host we keep a
 // PerHostStore, in which for each balancer we keep a PerBalancerStore. Each
@@ -68,13 +67,16 @@ class CallMetricValue {
 // The key of a load record.
 class LoadRecordKey {
  public:
-  explicit LoadRecordKey(grpc::string lb_id, grpc::string lb_tag,
-                         grpc::string user_id, grpc::string client_ip_hex)
+  LoadRecordKey(grpc::string lb_id, grpc::string lb_tag, grpc::string user_id,
+                grpc::string client_ip_hex)
       : lb_id_(std::move(lb_id)),
         lb_tag_(std::move(lb_tag)),
         user_id_(std::move(user_id)),
         client_ip_hex_(std::move(client_ip_hex)) {}
 
+  // Parses the input client_ip_and_token to set client IP, LB ID, and LB tag.
+  LoadRecordKey(const grpc::string& client_ip_and_token, grpc::string user_id);
+
   grpc::string ToString() const {
     return "[lb_id_=" + lb_id_ + ", lb_tag_=" + lb_tag_ +
            ", user_id_=" + user_id_ + ", client_ip_hex_=" + client_ip_hex_ +
@@ -86,6 +88,9 @@ class LoadRecordKey {
            user_id_ == other.user_id_ && client_ip_hex_ == other.client_ip_hex_;
   }
 
+  // Gets the client IP bytes in network order (i.e., big-endian).
+  grpc::string GetClientIpBytes() const;
+
   // Getters.
   const grpc::string& lb_id() const { return lb_id_; }
   const grpc::string& lb_tag() const { return lb_tag_; }
@@ -119,8 +124,8 @@ class LoadRecordKey {
 class LoadRecordValue {
  public:
   explicit LoadRecordValue(uint64_t start_count = 0, uint64_t ok_count = 0,
-                           uint64_t error_count = 0, double bytes_sent = 0,
-                           double bytes_recv = 0, double latency_ms = 0)
+                           uint64_t error_count = 0, uint64_t bytes_sent = 0,
+                           uint64_t bytes_recv = 0, uint64_t latency_ms = 0)
       : start_count_(start_count),
         ok_count_(ok_count),
         error_count_(error_count),
@@ -128,6 +133,9 @@ class LoadRecordValue {
         bytes_recv_(bytes_recv),
         latency_ms_(latency_ms) {}
 
+  LoadRecordValue(grpc::string metric_name, uint64_t num_calls,
+                  double total_metric_value);
+
   void MergeFrom(const LoadRecordValue& other) {
     start_count_ += other.start_count_;
     ok_count_ += other.ok_count_;
@@ -164,9 +172,9 @@ class LoadRecordValue {
   uint64_t start_count() const { return start_count_; }
   uint64_t ok_count() const { return ok_count_; }
   uint64_t error_count() const { return error_count_; }
-  double bytes_sent() const { return bytes_sent_; }
-  double bytes_recv() const { return bytes_recv_; }
-  double latency_ms() const { return latency_ms_; }
+  uint64_t bytes_sent() const { return bytes_sent_; }
+  uint64_t bytes_recv() const { return bytes_recv_; }
+  uint64_t latency_ms() const { return latency_ms_; }
   const std::unordered_map<grpc::string, CallMetricValue>& call_metrics()
       const {
     return call_metrics_;
@@ -176,9 +184,9 @@ class LoadRecordValue {
   uint64_t start_count_ = 0;
   uint64_t ok_count_ = 0;
   uint64_t error_count_ = 0;
-  double bytes_sent_ = 0;
-  double bytes_recv_ = 0;
-  double latency_ms_ = 0;
+  uint64_t bytes_sent_ = 0;
+  uint64_t bytes_recv_ = 0;
+  uint64_t latency_ms_ = 0;
   std::unordered_map<grpc::string, CallMetricValue> call_metrics_;
 };
 

+ 498 - 0
src/cpp/server/load_reporter/load_reporter.cc

@@ -0,0 +1,498 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <chrono>
+#include <ctime>
+
+#include "src/cpp/server/load_reporter/constants.h"
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+#include "src/cpp/server/load_reporter/load_reporter.h"
+
+#include "opencensus/stats/internal/set_aggregation_window.h"
+
+namespace grpc {
+namespace load_reporter {
+
+CpuStatsProvider::CpuStatsSample CpuStatsProviderDefaultImpl::GetCpuStats() {
+  return GetCpuStatsImpl();
+}
+
+CensusViewProvider::CensusViewProvider()
+    : tag_key_token_(::opencensus::stats::TagKey::Register(kTagKeyToken)),
+      tag_key_host_(::opencensus::stats::TagKey::Register(kTagKeyHost)),
+      tag_key_user_id_(::opencensus::stats::TagKey::Register(kTagKeyUserId)),
+      tag_key_status_(::opencensus::stats::TagKey::Register(kTagKeyStatus)),
+      tag_key_metric_name_(
+          ::opencensus::stats::TagKey::Register(kTagKeyMetricName)) {
+  // One view related to starting a call.
+  auto vd_start_count =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewStartCount)
+          .set_measure(kMeasureStartCount)
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .set_description(
+              "Delta count of calls started broken down by <token, host, "
+              "user_id>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_start_count);
+  view_descriptor_map_.emplace(kViewStartCount, vd_start_count);
+  // Four views related to ending a call.
+  // If this view is set as Count of kMeasureEndBytesSent (in hope of saving one
+  // measure), it's infeasible to prepare fake data for testing. That's because
+  // the OpenCensus API to make up view data will add the input data as separate
+  // measurements instead of setting the data values directly.
+  auto vd_end_count =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name((kViewEndCount))
+          .set_measure((kMeasureEndCount))
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta count of calls ended broken down by <token, host, "
+              "user_id, status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_count);
+  view_descriptor_map_.emplace(kViewEndCount, vd_end_count);
+  auto vd_end_bytes_sent =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name((kViewEndBytesSent))
+          .set_measure((kMeasureEndBytesSent))
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta sum of bytes sent broken down by <token, host, user_id, "
+              "status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_bytes_sent);
+  view_descriptor_map_.emplace(kViewEndBytesSent, vd_end_bytes_sent);
+  auto vd_end_bytes_received =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name((kViewEndBytesReceived))
+          .set_measure((kMeasureEndBytesReceived))
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta sum of bytes received broken down by <token, host, "
+              "user_id, status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_bytes_received);
+  view_descriptor_map_.emplace(kViewEndBytesReceived, vd_end_bytes_received);
+  auto vd_end_latency_ms =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name((kViewEndLatencyMs))
+          .set_measure((kMeasureEndLatencyMs))
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta sum of latency in ms broken down by <token, host, "
+              "user_id, status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_latency_ms);
+  view_descriptor_map_.emplace(kViewEndLatencyMs, vd_end_latency_ms);
+  // Two views related to other call metrics.
+  auto vd_metric_call_count =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name((kViewOtherCallMetricCount))
+          .set_measure((kMeasureOtherCallMetric))
+          .set_aggregation(::opencensus::stats::Aggregation::Count())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_metric_name_)
+          .set_description(
+              "Delta count of calls broken down by <token, host, user_id, "
+              "metric_name>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_metric_call_count);
+  view_descriptor_map_.emplace(kViewOtherCallMetricCount, vd_metric_call_count);
+  auto vd_metric_value =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name((kViewOtherCallMetricValue))
+          .set_measure((kMeasureOtherCallMetric))
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_metric_name_)
+          .set_description(
+              "Delta sum of call metric value broken down "
+              "by <token, host, user_id, metric_name>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_metric_value);
+  view_descriptor_map_.emplace(kViewOtherCallMetricValue, vd_metric_value);
+}
+
+double CensusViewProvider::GetRelatedViewDataRowDouble(
+    const ViewDataMap& view_data_map, const char* view_name,
+    size_t view_name_len, const std::vector<grpc::string>& tag_values) {
+  auto it_vd = view_data_map.find(grpc::string(view_name, view_name_len));
+  GPR_ASSERT(it_vd != view_data_map.end());
+  auto it_row = it_vd->second.double_data().find(tag_values);
+  GPR_ASSERT(it_row != it_vd->second.double_data().end());
+  return it_row->second;
+}
+
+CensusViewProviderDefaultImpl::CensusViewProviderDefaultImpl() {
+  for (const auto& p : view_descriptor_map()) {
+    const grpc::string& view_name = p.first;
+    const ::opencensus::stats::ViewDescriptor& vd = p.second;
+    // We need to use pair's piecewise ctor here, otherwise the deleted copy
+    // ctor of View will be called.
+    view_map_.emplace(std::piecewise_construct,
+                      std::forward_as_tuple(view_name),
+                      std::forward_as_tuple(vd));
+  }
+}
+
+CensusViewProvider::ViewDataMap CensusViewProviderDefaultImpl::FetchViewData() {
+  gpr_log(GPR_DEBUG, "[CVP %p] Starts fetching Census view data.", this);
+  ViewDataMap view_data_map;
+  for (auto& p : view_map_) {
+    const grpc::string& view_name = p.first;
+    ::opencensus::stats::View& view = p.second;
+    if (view.IsValid()) {
+      view_data_map.emplace(view_name, view.GetData());
+      gpr_log(GPR_DEBUG, "[CVP %p] Fetched view data (view: %s).", this,
+              view_name.c_str());
+    } else {
+      gpr_log(
+          GPR_DEBUG,
+          "[CVP %p] Can't fetch view data because view is invalid (view: %s).",
+          this, view_name.c_str());
+    }
+  }
+  return view_data_map;
+}
+
+grpc::string LoadReporter::GenerateLbId() {
+  while (true) {
+    if (next_lb_id_ > UINT32_MAX) {
+      gpr_log(GPR_ERROR, "[LR %p] The LB ID exceeds the max valid value!",
+              this);
+      return "";
+    }
+    int64_t lb_id = next_lb_id_++;
+    // Overflow should never happen.
+    GPR_ASSERT(lb_id >= 0);
+    // Convert to padded hex string for a 32-bit LB ID. E.g, "0000ca5b".
+    char buf[kLbIdLength + 1];
+    snprintf(buf, sizeof(buf), "%08lx", lb_id);
+    grpc::string lb_id_str(buf, kLbIdLength);
+    // The client may send requests with LB ID that has never been allocated
+    // by this load reporter. Those IDs are tracked and will be skipped when
+    // we generate a new ID.
+    if (!load_data_store_.IsTrackedUnknownBalancerId(lb_id_str)) {
+      return lb_id_str;
+    }
+  }
+}
+
+::grpc::lb::v1::LoadBalancingFeedback
+LoadReporter::GenerateLoadBalancingFeedback() {
+  std::unique_lock<std::mutex> lock(feedback_mu_);
+  auto now = std::chrono::system_clock::now();
+  // Discard records outside the window until there is only one record
+  // outside the window, which is used as the base for difference.
+  while (feedback_records_.size() > 1 &&
+         !IsRecordInWindow(feedback_records_[1], now)) {
+    feedback_records_.pop_front();
+  }
+  if (feedback_records_.size() < 2) {
+    return ::grpc::lb::v1::LoadBalancingFeedback::default_instance();
+  }
+  // Find the longest range with valid ends.
+  LoadBalancingFeedbackRecord* oldest = &feedback_records_[0];
+  LoadBalancingFeedbackRecord* newest =
+      &feedback_records_[feedback_records_.size() - 1];
+  while (newest > oldest &&
+         (newest->cpu_limit == 0 || oldest->cpu_limit == 0)) {
+    // A zero limit means that the system info reading was failed, so these
+    // records can't be used to calculate CPU utilization.
+    if (newest->cpu_limit == 0) --newest;
+    if (oldest->cpu_limit == 0) ++oldest;
+  }
+  if (newest - oldest < 1 || oldest->end_time == newest->end_time ||
+      newest->cpu_limit == oldest->cpu_limit) {
+    return ::grpc::lb::v1::LoadBalancingFeedback::default_instance();
+  }
+  uint64_t rpcs = 0;
+  uint64_t errors = 0;
+  for (LoadBalancingFeedbackRecord* p = newest; p != oldest; --p) {
+    // Because these two numbers are counters, the oldest record shouldn't be
+    // included.
+    rpcs += p->rpcs;
+    errors += p->errors;
+  }
+  double cpu_usage = newest->cpu_usage - oldest->cpu_usage;
+  double cpu_limit = newest->cpu_limit - oldest->cpu_limit;
+  std::chrono::duration<double> duration_seconds =
+      newest->end_time - oldest->end_time;
+  lock.unlock();
+  ::grpc::lb::v1::LoadBalancingFeedback feedback;
+  feedback.set_server_utilization(static_cast<float>(cpu_usage / cpu_limit));
+  feedback.set_calls_per_second(
+      static_cast<float>(rpcs / duration_seconds.count()));
+  feedback.set_errors_per_second(
+      static_cast<float>(errors / duration_seconds.count()));
+  return feedback;
+}
+
+::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load>
+LoadReporter::GenerateLoads(const grpc::string& hostname,
+                            const grpc::string& lb_id) {
+  std::lock_guard<std::mutex> lock(store_mu_);
+  auto assigned_stores = load_data_store_.GetAssignedStores(hostname, lb_id);
+  GPR_ASSERT(assigned_stores != nullptr);
+  GPR_ASSERT(!assigned_stores->empty());
+  ::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load> loads;
+  for (PerBalancerStore* per_balancer_store : *assigned_stores) {
+    GPR_ASSERT(!per_balancer_store->IsSuspended());
+    if (!per_balancer_store->load_record_map().empty()) {
+      for (const auto& p : per_balancer_store->load_record_map()) {
+        const auto& key = p.first;
+        const auto& value = p.second;
+        auto load = loads.Add();
+        load->set_load_balance_tag(key.lb_tag());
+        load->set_user_id(key.user_id());
+        load->set_client_ip_address(key.GetClientIpBytes());
+        load->set_num_calls_started(static_cast<int64_t>(value.start_count()));
+        load->set_num_calls_finished_without_error(
+            static_cast<int64_t>(value.ok_count()));
+        load->set_num_calls_finished_with_error(
+            static_cast<int64_t>(value.error_count()));
+        load->set_total_bytes_sent(static_cast<int64_t>(value.bytes_sent()));
+        load->set_total_bytes_received(
+            static_cast<int64_t>(value.bytes_recv()));
+        load->mutable_total_latency()->set_seconds(
+            static_cast<int64_t>(value.latency_ms() / 1000));
+        load->mutable_total_latency()->set_nanos(
+            (static_cast<int32_t>(value.latency_ms()) % 1000) * 1000000);
+        for (const auto& p : value.call_metrics()) {
+          const grpc::string& metric_name = p.first;
+          const CallMetricValue& metric_value = p.second;
+          auto call_metric_data = load->add_metric_data();
+          call_metric_data->set_metric_name(metric_name);
+          call_metric_data->set_num_calls_finished_with_metric(
+              metric_value.num_calls());
+          call_metric_data->set_total_metric_value(
+              metric_value.total_metric_value());
+        }
+        if (per_balancer_store->lb_id() != lb_id) {
+          // This per-balancer store is an orphan assigned to this receiving
+          // balancer.
+          AttachOrphanLoadId(load, *per_balancer_store);
+        }
+      }
+      per_balancer_store->ClearLoadRecordMap();
+    }
+    if (per_balancer_store->IsNumCallsInProgressChangedSinceLastReport()) {
+      auto load = loads.Add();
+      load->set_num_calls_in_progress(
+          per_balancer_store->GetNumCallsInProgressForReport());
+      if (per_balancer_store->lb_id() != lb_id) {
+        // This per-balancer store is an orphan assigned to this receiving
+        // balancer.
+        AttachOrphanLoadId(load, *per_balancer_store);
+      }
+    }
+  }
+  return loads;
+}
+
+void LoadReporter::AttachOrphanLoadId(
+    ::grpc::lb::v1::Load* load, const PerBalancerStore& per_balancer_store) {
+  if (per_balancer_store.lb_id() == kInvalidLbId) {
+    load->set_load_key_unknown(true);
+  } else {
+    load->set_load_key_unknown(false);
+    load->mutable_orphaned_load_identifier()->set_load_key(
+        per_balancer_store.load_key());
+    load->mutable_orphaned_load_identifier()->set_load_balancer_id(
+        per_balancer_store.lb_id());
+  }
+}
+
+void LoadReporter::AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors) {
+  CpuStatsProvider::CpuStatsSample cpu_stats;
+  if (cpu_stats_provider_ != nullptr) {
+    cpu_stats = cpu_stats_provider_->GetCpuStats();
+  } else {
+    // This will make the load balancing feedback generation a no-op.
+    cpu_stats = {0, 0};
+  }
+  std::unique_lock<std::mutex> lock(feedback_mu_);
+  feedback_records_.emplace_back(std::chrono::system_clock::now(), rpcs, errors,
+                                 cpu_stats.first, cpu_stats.second);
+}
+
+void LoadReporter::ReportStreamCreated(const grpc::string& hostname,
+                                       const grpc::string& lb_id,
+                                       const grpc::string& load_key) {
+  std::lock_guard<std::mutex> lock(store_mu_);
+  load_data_store_.ReportStreamCreated(hostname, lb_id, load_key);
+  gpr_log(GPR_INFO,
+          "[LR %p] Report stream created (host: %s, LB ID: %s, load key: %s).",
+          this, hostname.c_str(), lb_id.c_str(), load_key.c_str());
+}
+
+void LoadReporter::ReportStreamClosed(const grpc::string& hostname,
+                                      const grpc::string& lb_id) {
+  std::lock_guard<std::mutex> lock(store_mu_);
+  load_data_store_.ReportStreamClosed(hostname, lb_id);
+  gpr_log(GPR_INFO, "[LR %p] Report stream closed (host: %s, LB ID: %s).", this,
+          hostname.c_str(), lb_id.c_str());
+}
+
+void LoadReporter::ProcessViewDataCallStart(
+    const CensusViewProvider::ViewDataMap& view_data_map) {
+  auto it = view_data_map.find(kViewStartCount);
+  if (it != view_data_map.end()) {
+    // Note that the data type for any Sum view is double, whatever the data
+    // type of the original measure.
+    for (const auto& p : it->second.double_data()) {
+      const std::vector<grpc::string>& tag_values = p.first;
+      const uint64_t start_count = static_cast<uint64_t>(p.second);
+      const grpc::string& client_ip_and_token = tag_values[0];
+      const grpc::string& host = tag_values[1];
+      const grpc::string& user_id = tag_values[2];
+      LoadRecordKey key(client_ip_and_token, user_id);
+      LoadRecordValue value = LoadRecordValue(start_count);
+      {
+        std::unique_lock<std::mutex> lock(store_mu_);
+        load_data_store_.MergeRow(host, key, value);
+      }
+    }
+  }
+}
+
+void LoadReporter::ProcessViewDataCallEnd(
+    const CensusViewProvider::ViewDataMap& view_data_map) {
+  uint64_t total_end_count = 0;
+  uint64_t total_error_count = 0;
+  auto it = view_data_map.find(kViewEndCount);
+  if (it != view_data_map.end()) {
+    // Note that the data type for any Sum view is double, whatever the data
+    // type of the original measure.
+    for (const auto& p : it->second.double_data()) {
+      const std::vector<grpc::string>& tag_values = p.first;
+      const uint64_t end_count = static_cast<uint64_t>(p.second);
+      const grpc::string& client_ip_and_token = tag_values[0];
+      const grpc::string& host = tag_values[1];
+      const grpc::string& user_id = tag_values[2];
+      const grpc::string& status = tag_values[3];
+      // This is due to a bug reported internally of Java server load reporting
+      // implementation.
+      // TODO(juanlishen): Check whether this situation happens in OSS C++.
+      if (client_ip_and_token.size() == 0) {
+        gpr_log(GPR_DEBUG,
+                "Skipping processing Opencensus record with empty "
+                "client_ip_and_token tag.");
+        continue;
+      }
+      LoadRecordKey key(client_ip_and_token, user_id);
+      const uint64_t bytes_sent =
+          CensusViewProvider::GetRelatedViewDataRowDouble(
+              view_data_map, kViewEndBytesSent, sizeof(kViewEndBytesSent) - 1,
+              tag_values);
+      const uint64_t bytes_received =
+          CensusViewProvider::GetRelatedViewDataRowDouble(
+              view_data_map, kViewEndBytesReceived,
+              sizeof(kViewEndBytesReceived) - 1, tag_values);
+      const uint64_t latency_ms =
+          CensusViewProvider::GetRelatedViewDataRowDouble(
+              view_data_map, kViewEndLatencyMs, sizeof(kViewEndLatencyMs) - 1,
+              tag_values);
+      uint64_t ok_count = 0;
+      uint64_t error_count = 0;
+      total_end_count += end_count;
+      if (std::strcmp(status.c_str(), kCallStatusOk) == 0) {
+        ok_count = end_count;
+      } else {
+        error_count = end_count;
+        total_error_count += end_count;
+      }
+      LoadRecordValue value = LoadRecordValue(
+          0, ok_count, error_count, bytes_sent, bytes_received, latency_ms);
+      {
+        std::unique_lock<std::mutex> lock(store_mu_);
+        load_data_store_.MergeRow(host, key, value);
+      }
+    }
+  }
+  AppendNewFeedbackRecord(total_end_count, total_error_count);
+}
+
+void LoadReporter::ProcessViewDataOtherCallMetrics(
+    const CensusViewProvider::ViewDataMap& view_data_map) {
+  auto it = view_data_map.find(kViewOtherCallMetricCount);
+  if (it != view_data_map.end()) {
+    for (const auto& p : it->second.int_data()) {
+      const std::vector<grpc::string>& tag_values = p.first;
+      const int64_t num_calls = p.second;
+      const grpc::string& client_ip_and_token = tag_values[0];
+      const grpc::string& host = tag_values[1];
+      const grpc::string& user_id = tag_values[2];
+      const grpc::string& metric_name = tag_values[3];
+      LoadRecordKey key(client_ip_and_token, user_id);
+      const double total_metric_value =
+          CensusViewProvider::GetRelatedViewDataRowDouble(
+              view_data_map, kViewOtherCallMetricValue,
+              sizeof(kViewOtherCallMetricValue) - 1, tag_values);
+      LoadRecordValue value = LoadRecordValue(
+          metric_name, static_cast<uint64_t>(num_calls), total_metric_value);
+      {
+        std::unique_lock<std::mutex> lock(store_mu_);
+        load_data_store_.MergeRow(host, key, value);
+      }
+    }
+  }
+}
+
+void LoadReporter::FetchAndSample() {
+  gpr_log(GPR_DEBUG,
+          "[LR %p] Starts fetching Census view data and sampling LB feedback "
+          "record.",
+          this);
+  CensusViewProvider::ViewDataMap view_data_map =
+      census_view_provider_->FetchViewData();
+  ProcessViewDataCallStart(view_data_map);
+  ProcessViewDataCallEnd(view_data_map);
+  ProcessViewDataOtherCallMetrics(view_data_map);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc

+ 225 - 0
src/cpp/server/load_reporter/load_reporter.h

@@ -0,0 +1,225 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <atomic>
+#include <chrono>
+#include <deque>
+#include <vector>
+
+#include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/config.h>
+
+#include "src/cpp/server/load_reporter/load_data_store.h"
+#include "src/proto/grpc/lb/v1/load_reporter.grpc.pb.h"
+
+#include "opencensus/stats/stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+// The interface to get the Census stats. Abstracted for mocking.
+class CensusViewProvider {
+ public:
+  // Maps from the view name to the view data.
+  using ViewDataMap =
+      std::unordered_map<grpc::string, ::opencensus::stats::ViewData>;
+  // Maps from the view name to the view descriptor.
+  using ViewDescriptorMap =
+      std::unordered_map<grpc::string, ::opencensus::stats::ViewDescriptor>;
+
+  CensusViewProvider();
+  virtual ~CensusViewProvider() = default;
+
+  // Fetches the view data accumulated since last fetching, and returns it as a
+  // map from the view name to the view data.
+  virtual ViewDataMap FetchViewData() = 0;
+
+  // A helper function that gets a row with the input tag values from the view
+  // data. Only used when we know that row must exist because we have seen a row
+  // with the same tag values in a related view data. Several ViewData's are
+  // considered related if their views are based on the measures that are always
+  // recorded at the same time.
+  double static GetRelatedViewDataRowDouble(
+      const ViewDataMap& view_data_map, const char* view_name,
+      size_t view_name_len, const std::vector<grpc::string>& tag_values);
+
+ protected:
+  const ViewDescriptorMap& view_descriptor_map() const {
+    return view_descriptor_map_;
+  }
+
+ private:
+  ViewDescriptorMap view_descriptor_map_;
+  // Tag keys.
+  ::opencensus::stats::TagKey tag_key_token_;
+  ::opencensus::stats::TagKey tag_key_host_;
+  ::opencensus::stats::TagKey tag_key_user_id_;
+  ::opencensus::stats::TagKey tag_key_status_;
+  ::opencensus::stats::TagKey tag_key_metric_name_;
+};
+
+// The default implementation fetches the real stats from Census.
+class CensusViewProviderDefaultImpl : public CensusViewProvider {
+ public:
+  CensusViewProviderDefaultImpl();
+
+  ViewDataMap FetchViewData() override;
+
+ private:
+  std::unordered_map<grpc::string, ::opencensus::stats::View> view_map_;
+};
+
+// The interface to get the CPU stats. Abstracted for mocking.
+class CpuStatsProvider {
+ public:
+  // The used and total amounts of CPU usage.
+  using CpuStatsSample = std::pair<uint64_t, uint64_t>;
+
+  virtual ~CpuStatsProvider() = default;
+
+  // Gets the cumulative used CPU and total CPU resource.
+  virtual CpuStatsSample GetCpuStats() = 0;
+};
+
+// The default implementation reads CPU jiffies from the system to calculate CPU
+// utilization.
+class CpuStatsProviderDefaultImpl : public CpuStatsProvider {
+ public:
+  CpuStatsSample GetCpuStats() override;
+};
+
+// Maintains all the load data and load reporting streams.
+class LoadReporter {
+ public:
+  // TODO(juanlishen): Allow config for providers from users.
+  LoadReporter(uint32_t feedback_sample_window_seconds,
+               std::unique_ptr<CensusViewProvider> census_view_provider,
+               std::unique_ptr<CpuStatsProvider> cpu_stats_provider)
+      : feedback_sample_window_seconds_(feedback_sample_window_seconds),
+        census_view_provider_(std::move(census_view_provider)),
+        cpu_stats_provider_(std::move(cpu_stats_provider)) {
+    // Append the initial record so that the next real record can have a base.
+    AppendNewFeedbackRecord(0, 0);
+  }
+
+  // Fetches the latest data from Census and merge it into the data store.
+  // Also adds a new sample to the LB feedback sliding window.
+  // Thread-unsafe. (1). The access to the load data store and feedback records
+  // has locking. (2). The access to the Census view provider and CPU stats
+  // provider lacks locking, but we only access these two members in this method
+  // (in testing, we also access them when setting up expectation). So the
+  // invocations of this method must be serialized.
+  void FetchAndSample();
+
+  // Generates a report for that host and balancer. The report contains
+  // all the stats data accumulated between the last report (i.e., the last
+  // consumption) and the last fetch from Census (i.e., the last production).
+  // Thread-safe.
+  ::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load> GenerateLoads(
+      const grpc::string& hostname, const grpc::string& lb_id);
+
+  // The feedback is calculated from the stats data recorded in the sliding
+  // window. Outdated records are discarded.
+  // Thread-safe.
+  ::grpc::lb::v1::LoadBalancingFeedback GenerateLoadBalancingFeedback();
+
+  // Wrapper around LoadDataStore::ReportStreamCreated.
+  // Thread-safe.
+  void ReportStreamCreated(const grpc::string& hostname,
+                           const grpc::string& lb_id,
+                           const grpc::string& load_key);
+
+  // Wrapper around LoadDataStore::ReportStreamClosed.
+  // Thread-safe.
+  void ReportStreamClosed(const grpc::string& hostname,
+                          const grpc::string& lb_id);
+
+  // Generates a unique LB ID of length kLbIdLength. Returns an empty string
+  // upon failure. Thread-safe.
+  grpc::string GenerateLbId();
+
+  // Accessors only for testing.
+  CensusViewProvider* census_view_provider() {
+    return census_view_provider_.get();
+  }
+  CpuStatsProvider* cpu_stats_provider() { return cpu_stats_provider_.get(); }
+
+ private:
+  struct LoadBalancingFeedbackRecord {
+    std::chrono::system_clock::time_point end_time;
+    uint64_t rpcs;
+    uint64_t errors;
+    uint64_t cpu_usage;
+    uint64_t cpu_limit;
+
+    LoadBalancingFeedbackRecord(
+        const std::chrono::system_clock::time_point& end_time, uint64_t rpcs,
+        uint64_t errors, uint64_t cpu_usage, uint64_t cpu_limit)
+        : end_time(end_time),
+          rpcs(rpcs),
+          errors(errors),
+          cpu_usage(cpu_usage),
+          cpu_limit(cpu_limit) {}
+  };
+
+  // Finds the view data about starting call from the view_data_map and merges
+  // the data to the load data store.
+  void ProcessViewDataCallStart(
+      const CensusViewProvider::ViewDataMap& view_data_map);
+  // Finds the view data about ending call from the view_data_map and merges the
+  // data to the load data store.
+  void ProcessViewDataCallEnd(
+      const CensusViewProvider::ViewDataMap& view_data_map);
+  // Finds the view data about the customized call metrics from the
+  // view_data_map and merges the data to the load data store.
+  void ProcessViewDataOtherCallMetrics(
+      const CensusViewProvider::ViewDataMap& view_data_map);
+
+  bool IsRecordInWindow(const LoadBalancingFeedbackRecord& record,
+                        std::chrono::system_clock::time_point now) {
+    return record.end_time > now - feedback_sample_window_seconds_;
+  }
+
+  void AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors);
+
+  // Extracts an OrphanedLoadIdentifier from the per-balancer store and attaches
+  // it to the load.
+  void AttachOrphanLoadId(::grpc::lb::v1::Load* load,
+                          const PerBalancerStore& per_balancer_store);
+
+  std::atomic<int64_t> next_lb_id_{0};
+  const std::chrono::seconds feedback_sample_window_seconds_;
+  std::mutex feedback_mu_;
+  std::deque<LoadBalancingFeedbackRecord> feedback_records_;
+  // TODO(juanlishen): Lock in finer grain. Locking the whole store may be
+  // too expensive.
+  std::mutex store_mu_;
+  LoadDataStore load_data_store_;
+  std::unique_ptr<CensusViewProvider> census_view_provider_;
+  std::unique_ptr<CpuStatsProvider> cpu_stats_provider_;
+};
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H

+ 2 - 2
src/csharp/README.md

@@ -42,7 +42,7 @@ If you are a user of gRPC C#, go to Usage section above.
 
 - [dotnet SDK](https://www.microsoft.com/net/core)
 - [Mono 4+](https://www.mono-project.com/) (only needed for Linux and MacOS)
-- Prerequisites mentioned in [INSTALL.md](../../INSTALL.md#pre-requisites)
+- Prerequisites mentioned in [BUILDING.md](../../BUILDING.md#pre-requisites)
   to be able to compile the native code.
 
 **Windows, Linux or Mac OS X**
@@ -93,6 +93,6 @@ THE NATIVE DEPENDENCY
 
 Internally, gRPC C# uses a native library written in C (gRPC C core) and invokes its functionality via P/Invoke. The fact that a native library is used should be fully transparent to the users and just installing the `Grpc.Core` NuGet package is the only step needed to use gRPC C# on all supported platforms.
 
-[API Reference]: https://grpc.io/grpc/csharp/
+[API Reference]: https://grpc.io/grpc/csharp/api/Grpc.Core.html
 [Helloworld Example]: ../../examples/csharp/helloworld
 [RouteGuide Tutorial]: https://grpc.io/docs/tutorials/basic/csharp.html 

+ 102 - 41
src/proto/grpc/channelz/channelz.proto

@@ -1,4 +1,4 @@
-// Copyright 2018 gRPC authors.
+// Copyright 2018 The gRPC Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,20 +12,30 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// This file defines an interface for exporting monitoring information
+// out of gRPC servers.  See the full design at
+// https://github.com/grpc/proposal/blob/master/A14-channelz.md
+//
+// The canonical version of this proto can be found at
+// https://github.com/grpc/grpc-proto/blob/master/grpc/channelz/v1/channelz.proto
+
 syntax = "proto3";
 
-package grpc.channelz;
+package grpc.channelz.v1;
 
 import "google/protobuf/any.proto";
 import "google/protobuf/duration.proto";
 import "google/protobuf/timestamp.proto";
 import "google/protobuf/wrappers.proto";
 
-// See go/grpc-channelz.
+option go_package = "google.golang.org/grpc/channelz/grpc_channelz_v1";
+option java_multiple_files = true;
+option java_package = "io.grpc.channelz.v1";
+option java_outer_classname = "ChannelzProto";
 
 // Channel is a logical grouping of channels, subchannels, and sockets.
 message Channel {
-  // The identifier for this channel.
+  // The identifier for this channel. This should bet set.
   ChannelRef ref = 1;
   // Data specific to this channel.
   ChannelData data = 2;
@@ -43,7 +53,7 @@ message Channel {
   repeated SubchannelRef subchannel_ref = 4;
 
   // There are no ordering guarantees on the order of sockets.
-  repeated SocketRef socket = 5;
+  repeated SocketRef socket_ref = 5;
 }
 
 // Subchannel is a logical grouping of channels, subchannels, and sockets.
@@ -67,7 +77,7 @@ message Subchannel {
   repeated SubchannelRef subchannel_ref = 4;
 
   // There are no ordering guarantees on the order of sockets.
-  repeated SocketRef socket = 5;
+  repeated SocketRef socket_ref = 5;
 }
 
 // These come from the specified states in this document:
@@ -84,20 +94,23 @@ message ChannelConnectivityState {
   State state = 1;
 }
 
+// Channel data is data related to a specific Channel or Subchannel.
 message ChannelData {
-
+  // The connectivity state of the channel or subchannel.  Implementations
+  // should always set this.
   ChannelConnectivityState state = 1;
 
   // The target this channel originally tried to connect to.  May be absent
   string target = 2;
 
+  // A trace of recent events on the channel.  May be absent.
   ChannelTrace trace = 3;
 
   // The number of calls started on the channel
   int64 calls_started = 4;
   // The number of calls that have completed with an OK status
   int64 calls_succeeded = 5;
-  // The number of calls that have a completed with a non-OK status
+  // The number of calls that have completed with a non-OK status
   int64 calls_failed = 6;
 
   // The last time a call was started on the channel.
@@ -130,26 +143,29 @@ message ChannelTraceEvent {
   }
 }
 
+// ChannelTrace represents the recent events that have occurred on the channel.
 message ChannelTrace {
   // Number of events ever logged in this tracing object. This can differ from
   // events.size() because events can be overwritten or garbage collected by
   // implementations.
   int64 num_events_logged = 1;
   // Time that this channel was created.
-  google.protobuf.Timestamp creation_time = 2;
+  google.protobuf.Timestamp creation_timestamp = 2;
   // List of events that have occurred on this channel.
   repeated ChannelTraceEvent events = 3;
 }
 
+// ChannelRef is a reference to a Channel.
 message ChannelRef {
   // The globally unique id for this channel.  Must be a positive number.
   int64 channel_id = 1;
   // An optional name associated with the channel.
   string name = 2;
   // Intentionally don't use field numbers from other refs.
-  reserved 3, 4, 5, 6;
+  reserved 3, 4, 5, 6, 7, 8;
 }
 
+// ChannelRef is a reference to a Subchannel.
 message SubchannelRef {
   // The globally unique id for this subchannel.  Must be a positive number.
   int64 subchannel_id = 7;
@@ -159,6 +175,7 @@ message SubchannelRef {
   reserved 1, 2, 3, 4, 5, 6;
 }
 
+// SocketRef is a reference to a Socket.
 message SocketRef {
   int64 socket_id = 3;
   // An optional name associated with the socket.
@@ -167,8 +184,9 @@ message SocketRef {
   reserved 1, 2, 5, 6, 7, 8;
 }
 
+// ServerRef is a reference to a Server.
 message ServerRef {
-  // A globally unique identifier for this server.   Must be a positive number.
+  // A globally unique identifier for this server.  Must be a positive number.
   int64 server_id = 5;
   // An optional name associated with the server.
   string name = 6;
@@ -176,16 +194,22 @@ message ServerRef {
   reserved 1, 2, 3, 4, 7, 8;
 }
 
+// Server represents a single server.  There may be multiple servers in a single
+// program.
 message Server {
+  // The identifier for a Server.  This should be set.
   ServerRef ref = 1;
+  // The associated data of the Server.
   ServerData data = 2;
 
   // The sockets that the server is listening on.  There are no ordering
-  // guarantees.
+  // guarantees.  This may be absent.
   repeated SocketRef listen_socket = 3;
 }
 
+// ServerData is data for a specific Server.
 message ServerData {
+  // A trace of recent events on the server.  May be absent.
   ChannelTrace trace = 1;
 
   // The number of incoming calls started on the server
@@ -201,13 +225,17 @@ message ServerData {
 
 // Information about an actual connection.  Pronounced "sock-ay".
 message Socket {
+  // The identifier for the Socket.
   SocketRef ref = 1;
 
+  // Data specific to this Socket.
   SocketData data = 2;
   // The locally bound address.
   Address local = 3;
   // The remote bound address.  May be absent.
   Address remote = 4;
+  // Security details for this socket.  May be absent if not available, or
+  // there is no security on the socket.
   Security security = 5;
 
   // Optional, represents the name of the remote endpoint, if different than
@@ -215,17 +243,23 @@ message Socket {
   string remote_name = 6;
 }
 
+// SocketData is data associated for a specific Socket.  The fields present
+// are specific to the implementation, so there may be minor differences in
+// the semantics.  (e.g. flow control windows)
 message SocketData {
   // The number of streams that have been started.
   int64 streams_started = 1;
-  // The number of streams that have ended successfully with the EoS bit set for
-  //  both end points
+  // The number of streams that have ended successfully:
+  // On client side, received frame with eos bit set;
+  // On server side, sent frame with eos bit set.
   int64 streams_succeeded = 2;
-  // The number of incoming streams that have a completed with a non-OK status
+  // The number of streams that have ended unsuccessfully:
+  // On client side, ended without receiving frame with eos bit set;
+  // On server side, ended without sending frame with eos bit set.
   int64 streams_failed = 3;
-
-  // The number of messages successfully sent on this socket.
+  // The number of grpc messages successfully sent on this socket.
   int64 messages_sent = 4;
+  // The number of grpc messages received on this socket.
   int64 messages_received = 5;
 
   // The number of keep alives sent.  This is typically implemented with HTTP/2
@@ -254,12 +288,14 @@ message SocketData {
   // include stream level or TCP level flow control info.
   google.protobuf.Int64Value  remote_flow_control_window = 12;
 
+  // Socket options set on this socket.  May be absent.
   repeated SocketOption option = 13;
 }
 
+// Address represents the address used to create the socket.
 message Address {
   message TcpIpAddress {
-    // Either the IPv4 or IPv6 address in bytes.  Will either be 4 bytes or 16
+    // Either the IPv4 or IPv6 address in bytes.  Will be either 4 bytes or 16
     // bytes in length.
     bytes ip_address = 1;
     // 0-64k, or -1 if not appropriate.
@@ -271,7 +307,7 @@ message Address {
   }
   // An address type not included above.
   message OtherAddress {
-    // The human readable version of the value.
+    // The human readable version of the value.  This value should be set.
     string name = 1;
     // The actual address message.
     google.protobuf.Any value = 2;
@@ -284,12 +320,17 @@ message Address {
   }
 }
 
+// Security represents details about how secure the socket is.
 message Security {
   message Tls {
-    // The key exchange used.  e.g. X25519
-    string key_exchange = 1;
-    // The cipher used. e.g. AES_128_GCM.
-    string cipher = 2;
+    oneof cipher_suite {
+      // The cipher suite name in the RFC 4346 format:
+      // https://tools.ietf.org/html/rfc4346#appendix-C
+      string standard_name = 1;
+      // Some other way to describe the cipher suite if
+      // the RFC 4346 name is not available.
+      string other_name = 2;
+    }
     // the certificate used by this endpoint.
     bytes local_certificate = 3;
     // the certificate used by the remote endpoint.
@@ -307,7 +348,11 @@ message Security {
   }
 }
 
+// SocketOption represents socket options for a socket.  Specifically, these
+// are the options returned by getsockopt().
 message SocketOption {
+  // The full name of the socket option.  Typically this will be the upper case
+  // name, such as "SO_REUSEPORT".
   string name = 1;
   // The human readable value of this socket option.  At least one of value or
   // additional will be set.
@@ -323,12 +368,17 @@ message SocketOptionTimeout {
   google.protobuf.Duration duration = 1;
 }
 
+// For use with SocketOption's additional field.  This is primarily used for
+// SO_LINGER.
 message SocketOptionLinger {
+  // active maps to `struct linger.l_onoff`
   bool active = 1;
+  // duration maps to `struct linger.l_linger`
   google.protobuf.Duration duration = 2;
 }
 
-// Tcp info for SOL_TCP, TCP_INFO
+// For use with SocketOption's additional field.  Tcp info for
+// SOL_TCP and TCP_INFO.
 message SocketOptionTcpInfo {
   uint32 tcpi_state = 1;
 
@@ -366,8 +416,10 @@ message SocketOptionTcpInfo {
   uint32 tcpi_reordering = 29;
 }
 
+// Channelz is a service exposed by gRPC servers that provides detailed debug
+// information.
 service Channelz {
-  // Gets all root channels (e.g. channels the application has directly
+  // Gets all root channels (i.e. channels the application has directly
   // created). This does not include subchannels nor non-top level channels.
   rpc GetTopChannels(GetTopChannelsRequest) returns (GetTopChannelsResponse);
   // Gets all servers that exist in the process.
@@ -382,6 +434,22 @@ service Channelz {
   rpc GetSocket(GetSocketRequest) returns (GetSocketResponse);
 }
 
+message GetTopChannelsRequest {
+  // start_channel_id indicates that only channels at or above this id should be
+  // included in the results.
+  int64 start_channel_id = 1;
+}
+
+message GetTopChannelsResponse {
+  // list of channels that the connection detail service knows about.  Sorted in
+  // ascending channel_id order.
+  repeated Channel channel = 1;
+  // If set, indicates that the list of channels is the final list.  Requesting
+  // more channels can only return more if they are created after this RPC
+  // completes.
+  bool end = 2;
+}
+
 message GetServersRequest {
   // start_server_id indicates that only servers at or above this id should be
   // included in the results.
@@ -415,42 +483,35 @@ message GetServerSocketsResponse {
   bool end = 2;
 }
 
-message GetTopChannelsRequest {
-  // start_channel_id indicates that only channels at or above this id should be
-  // included in the results.
-  int64 start_channel_id = 1;
-}
-
-message GetTopChannelsResponse {
-  // list of channels that the connection detail service knows about.  Sorted in
-  // ascending channel_id order.
-  repeated Channel channel = 1;
-  // If set, indicates that the list of channels is the final list.  Requesting
-  // more channels can only return more if they are created after this RPC
-  // completes.
-  bool end = 2;
-}
-
 message GetChannelRequest {
+  // channel_id is the identifier of the specific channel to get.
   int64 channel_id = 1;
 }
 
 message GetChannelResponse {
+  // The Channel that corresponds to the requested channel_id.  This field
+  // should be set.
   Channel channel = 1;
 }
 
 message GetSubchannelRequest {
+  // subchannel_id is the identifier of the specific subchannel to get.
   int64 subchannel_id = 1;
 }
 
 message GetSubchannelResponse {
+  // The Subchannel that corresponds to the requested subchannel_id.  This
+  // field should be set.
   Subchannel subchannel = 1;
 }
 
 message GetSocketRequest {
+  // socket_id is the identifier of the specific socket to get.
   int64 socket_id = 1;
 }
 
 message GetSocketResponse {
+  // The Socket that corresponds to the requested socket_id.  This field
+  // should be set.
   Socket socket = 1;
 }

+ 12 - 2
src/proto/grpc/lb/v1/BUILD

@@ -14,11 +14,21 @@
 
 licenses(["notice"])  # Apache v2
 
-load("//bazel:grpc_build_system.bzl", "grpc_proto_library", "grpc_package")
+load("//bazel:grpc_build_system.bzl", "grpc_package", "grpc_proto_library")
 
-grpc_package(name = "lb", visibility = "public")
+grpc_package(
+    name = "lb",
+    visibility = "public",
+)
 
 grpc_proto_library(
     name = "load_balancer_proto",
     srcs = ["load_balancer.proto"],
 )
+
+grpc_proto_library(
+    name = "load_reporter_proto",
+    srcs = ["load_reporter.proto"],
+    has_services = True,
+    well_known_protos = True,
+)

+ 180 - 0
src/proto/grpc/lb/v1/load_reporter.proto

@@ -0,0 +1,180 @@
+// Copyright 2018 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package grpc.lb.v1;
+
+import "google/protobuf/duration.proto";
+
+// The LoadReporter service.
+service LoadReporter {
+  // Report load from server to lb.
+  rpc ReportLoad(stream LoadReportRequest)
+    returns (stream LoadReportResponse) {
+  };
+}
+
+message LoadReportRequest {
+  // This message should be sent on the first request to the gRPC server.
+  InitialLoadReportRequest initial_request = 1;
+}
+
+message InitialLoadReportRequest {
+  // The hostname this load reporter client is requesting load for.
+  string load_balanced_hostname = 1;
+
+  // Additional information to disambiguate orphaned load: load that should have
+  // gone to this load reporter client, but was not able to be sent since the
+  // load reporter client has disconnected. load_key is sent in orphaned load
+  // reports; see Load.load_key.
+  bytes load_key = 2;
+
+  // This interval defines how often the server should send load reports to
+  // the load balancer.
+  google.protobuf.Duration load_report_interval = 3;
+}
+
+message LoadReportResponse {
+  // This message should be sent on the first response to the load balancer.
+  InitialLoadReportResponse initial_response = 1;
+
+  // Reports server-wide statistics for load balancing.
+  // This should be reported with every response.
+  LoadBalancingFeedback load_balancing_feedback = 2;
+
+  // A load report for each <tag, user_id> tuple. This could be considered to be
+  // a multimap indexed by <tag, user_id>. It is not strictly necessary to
+  // aggregate all entries into one entry per <tag, user_id> tuple, although it
+  // is preferred to do so.
+  repeated Load load = 3;
+}
+
+message InitialLoadReportResponse {
+  // Initial response returns the Load balancer ID. This must be plain text
+  // (printable ASCII).
+  string load_balancer_id = 1;
+
+  enum ImplementationIdentifier {
+    IMPL_UNSPECIFIED = 0;
+    CPP = 1;   // Standard Google C++ implementation.
+    JAVA = 2;  // Standard Google Java implementation.
+    GO = 3;    // Standard Google Go implementation.
+  }
+  // Optional identifier of this implementation of the load reporting server.
+  ImplementationIdentifier implementation_id = 2;
+
+  // Optional server_version should be a value that is modified (and
+  // monotonically increased) when changes are made to the server
+  // implementation.
+  int64 server_version = 3;
+}
+
+message LoadBalancingFeedback {
+  // Reports the current utilization of the server (typical range [0.0 - 1.0]).
+  float server_utilization = 1;
+
+  // The total rate of calls handled by this server (including errors).
+  float calls_per_second = 2;
+
+  // The total rate of error responses sent by this server.
+  float errors_per_second = 3;
+}
+
+message Load {
+  // The (plain text) tag used by the calls covered by this load report. The
+  // tag is that part of the load balancer token after removing the load
+  // balancer id. Empty is equivalent to non-existent tag.
+  string load_balance_tag = 1;
+
+  // The user identity authenticated by the calls covered by this load
+  // report. Empty is equivalent to no known user_id.
+  string user_id = 3;
+
+  // IP address of the client that sent these requests, serialized in
+  // network-byte-order. It may either be an IPv4 or IPv6 address.
+  bytes client_ip_address = 15;
+
+  // The number of calls started (since the last report) with the given tag and
+  // user_id.
+  int64 num_calls_started = 4;
+
+  // Indicates whether this load report is an in-progress load report in which
+  // num_calls_in_progress is the only valid entry. If in_progress_report is not
+  // set, num_calls_in_progress will be ignored. If in_progress_report is set,
+  // fields other than num_calls_in_progress and orphaned_load will be ignored.
+  oneof in_progress_report {
+    // The number of calls in progress (instantaneously) per load balancer id.
+    int64 num_calls_in_progress = 5;
+  }
+
+  // The following values are counts or totals of call statistics that finished
+  // with the given tag and user_id.
+  int64 num_calls_finished_without_error = 6;  // Calls with status OK.
+  int64 num_calls_finished_with_error = 7;  // Calls with status non-OK.
+  // Calls that finished with a status that maps to HTTP 5XX (see
+  // googleapis/google/rpc/code.proto). Note that this is a subset of
+  // num_calls_finished_with_error.
+  int64 num_calls_finished_with_server_error = 16;
+
+  // Totals are from calls that with _and_ without error.
+  int64 total_bytes_sent = 8;
+  int64 total_bytes_received = 9;
+  google.protobuf.Duration total_latency = 10;
+
+  // Optional metrics reported for the call(s). Requires that metric_name is
+  // unique.
+  repeated CallMetricData metric_data = 11;
+
+  // The following two fields are used for reporting orphaned load: load that
+  // could not be reported to the originating balancer either since the balancer
+  // is no longer connected or because the frontend sent an invalid token. These
+  // fields must not be set with normal (unorphaned) load reports.
+  oneof orphaned_load {
+    // Load_key is the load_key from the initial_request from the originating
+    // balancer.
+    bytes load_key = 12 [deprecated=true];
+
+    // If true then this load report is for calls that had an invalid token; the
+    // user is probably abusing the gRPC protocol.
+    // TODO(yankaiz): Rename load_key_unknown.
+    bool load_key_unknown = 13;
+
+    // load_key and balancer_id are included in order to identify orphaned load
+    // from different origins.
+    OrphanedLoadIdentifier orphaned_load_identifier = 14;
+  }
+
+  reserved 2;
+}
+
+message CallMetricData {
+  // Name of the metric; may be empty.
+  string metric_name = 1;
+
+  // Number of calls that finished and included this metric.
+  int64 num_calls_finished_with_metric = 2;
+
+  // Sum of metric values across all calls that finished with this metric.
+  double total_metric_value = 3;
+}
+
+message OrphanedLoadIdentifier {
+  // The load_key from the initial_request from the originating balancer.
+  bytes load_key = 1;
+
+  // The unique ID generated by LoadReporter to identify balancers. Here it
+  // distinguishes orphaned load with a same load_key.
+  string load_balancer_id = 2;
+}

+ 1 - 0
src/python/grpcio/grpc_core_dependencies.py

@@ -64,6 +64,7 @@ CORE_SOURCE_FILES = [
     'src/core/lib/channel/channel_stack.cc',
     'src/core/lib/channel/channel_stack_builder.cc',
     'src/core/lib/channel/channel_trace.cc',
+    'src/core/lib/channel/channelz.cc',
     'src/core/lib/channel/channelz_registry.cc',
     'src/core/lib/channel/connected_channel.cc',
     'src/core/lib/channel/handshaker.cc',

+ 3 - 0
src/python/grpcio_testing/MANIFEST.in

@@ -0,0 +1,3 @@
+include grpc_version.py
+recursive-include grpc_testing *.py
+global-exclude *.pyc

+ 10 - 0
src/python/grpcio_testing/README.rst

@@ -0,0 +1,10 @@
+gRPC Python Testing Package
+===========================
+
+Testing utilities for gRPC Python
+
+Dependencies
+------------
+
+Depends on the `grpcio` package, available from PyPI via `pip install grpcio`.
+

+ 0 - 4
src/ruby/end2end/multiple_killed_watching_threads_driver.rb

@@ -58,10 +58,6 @@ def main
   run_multiple_killed_watches(10, 0.1)
   STDERR.puts '1000 iterations, sleep 0.001 before killing thread'
   run_multiple_killed_watches(1000, 0.001)
-  STDERR.puts '10000 iterations, sleep 0.00001 before killing thread'
-  run_multiple_killed_watches(10_000, 0.00001)
-  STDERR.puts '20000 iterations, sleep 0.00001 before killing thread'
-  run_multiple_killed_watches(20_000, 0.00001)
 end
 
 main

+ 0 - 4
src/ruby/ext/grpc/rb_grpc_imports.generated.c

@@ -68,8 +68,6 @@ grpc_channel_get_info_type grpc_channel_get_info_import;
 grpc_insecure_channel_create_type grpc_insecure_channel_create_import;
 grpc_lame_client_channel_create_type grpc_lame_client_channel_create_import;
 grpc_channel_destroy_type grpc_channel_destroy_import;
-grpc_channel_get_trace_type grpc_channel_get_trace_import;
-grpc_channel_get_uuid_type grpc_channel_get_uuid_import;
 grpc_call_cancel_type grpc_call_cancel_import;
 grpc_call_cancel_with_status_type grpc_call_cancel_with_status_import;
 grpc_call_ref_type grpc_call_ref_import;
@@ -316,8 +314,6 @@ void grpc_rb_load_imports(HMODULE library) {
   grpc_insecure_channel_create_import = (grpc_insecure_channel_create_type) GetProcAddress(library, "grpc_insecure_channel_create");
   grpc_lame_client_channel_create_import = (grpc_lame_client_channel_create_type) GetProcAddress(library, "grpc_lame_client_channel_create");
   grpc_channel_destroy_import = (grpc_channel_destroy_type) GetProcAddress(library, "grpc_channel_destroy");
-  grpc_channel_get_trace_import = (grpc_channel_get_trace_type) GetProcAddress(library, "grpc_channel_get_trace");
-  grpc_channel_get_uuid_import = (grpc_channel_get_uuid_type) GetProcAddress(library, "grpc_channel_get_uuid");
   grpc_call_cancel_import = (grpc_call_cancel_type) GetProcAddress(library, "grpc_call_cancel");
   grpc_call_cancel_with_status_import = (grpc_call_cancel_with_status_type) GetProcAddress(library, "grpc_call_cancel_with_status");
   grpc_call_ref_import = (grpc_call_ref_type) GetProcAddress(library, "grpc_call_ref");

+ 0 - 6
src/ruby/ext/grpc/rb_grpc_imports.generated.h

@@ -179,12 +179,6 @@ extern grpc_lame_client_channel_create_type grpc_lame_client_channel_create_impo
 typedef void(*grpc_channel_destroy_type)(grpc_channel* channel);
 extern grpc_channel_destroy_type grpc_channel_destroy_import;
 #define grpc_channel_destroy grpc_channel_destroy_import
-typedef char*(*grpc_channel_get_trace_type)(grpc_channel* channel);
-extern grpc_channel_get_trace_type grpc_channel_get_trace_import;
-#define grpc_channel_get_trace grpc_channel_get_trace_import
-typedef intptr_t(*grpc_channel_get_uuid_type)(grpc_channel* channel);
-extern grpc_channel_get_uuid_type grpc_channel_get_uuid_import;
-#define grpc_channel_get_uuid grpc_channel_get_uuid_import
 typedef grpc_call_error(*grpc_call_cancel_type)(grpc_call* call, void* reserved);
 extern grpc_call_cancel_type grpc_call_cancel_import;
 #define grpc_call_cancel grpc_call_cancel_import

+ 1 - 1
src/ruby/lib/grpc/generic/active_call.rb

@@ -120,7 +120,7 @@ module GRPC
       @send_initial_md_mutex.synchronize do
         return if @metadata_sent
         @metadata_to_send.merge!(new_metadata)
-        @metadata_tag = ActiveCall.client_invoke(@call, @metadata_to_send)
+        ActiveCall.client_invoke(@call, @metadata_to_send)
         @metadata_sent = true
       end
     end

+ 0 - 11
src/ruby/spec/client_auth_spec.rb

@@ -39,17 +39,6 @@ def create_server_creds
     true) # force client auth
 end
 
-# A test message
-class EchoMsg
-  def self.marshal(_o)
-    ''
-  end
-
-  def self.unmarshal(_o)
-    EchoMsg.new
-  end
-end
-
 # a test service that checks the cert of its peer
 class SslTestService
   include GRPC::GenericService

+ 0 - 11
src/ruby/spec/google_rpc_status_utils_spec.rb

@@ -114,17 +114,6 @@ describe 'conversion from a status struct to a google protobuf status' do
   end
 end
 
-# Test message
-class EchoMsg
-  def self.marshal(_o)
-    ''
-  end
-
-  def self.unmarshal(_o)
-    EchoMsg.new
-  end
-end
-
 # A test service that fills in the "reserved" grpc-status-details-bin trailer,
 # for client-side testing of GoogleRpcStatus protobuf extraction from trailers.
 class GoogleRpcStatusTestService

+ 6 - 0
templates/CMakeLists.txt.template

@@ -87,6 +87,7 @@
   # Options
   option(gRPC_BUILD_TESTS "Build tests" OFF)
   option(gRPC_BUILD_CODEGEN "Build codegen" ON)
+  option(gRPC_BUILD_CSHARP_EXT "Build C# extensions" ON)
 
   set(gRPC_INSTALL_default ON)
   if (NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
@@ -298,6 +299,11 @@
   if (gRPC_BUILD_TESTS)
   ${cc_library(lib)}
   endif (gRPC_BUILD_TESTS)
+  % elif lib.name in ['grpc_csharp_ext']:
+  if (gRPC_BUILD_CSHARP_EXT)
+  ${cc_library(lib)}
+  ${cc_install(lib)}
+  endif (gRPC_BUILD_CSHARP_EXT)
   % else:
   ${cc_library(lib)}
   % if not lib.build in ["tool"]:

+ 3 - 2
templates/config.m4.template

@@ -11,11 +11,12 @@
     PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
     PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/boringssl/include)
     PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/address_sorting/include)
+    PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/third_party/nanopb)
 
     LIBS="-lpthread $LIBS"
 
-    CFLAGS="-Wall -Werror -Wno-parentheses-equality -Wno-unused-value -std=c11 -g -O2 -D PB_FIELD_16BIT=1"
-    CXXFLAGS="-std=c++11 -fno-exceptions -fno-rtti -g -O2 -D PB_FIELD_16BIT=1"
+    CFLAGS="-Wall -Werror -Wno-parentheses-equality -Wno-unused-value -std=c11 -g -O2 -D PB_FIELD_32BIT=1"
+    CXXFLAGS="-std=c++11 -fno-exceptions -fno-rtti -g -O2 -D PB_FIELD_32BIT=1"
     GRPC_SHARED_LIBADD="-lpthread $GRPC_SHARED_LIBADD"
     PHP_REQUIRE_CXX()
     PHP_ADD_LIBRARY(pthread)

+ 3 - 2
templates/config.w32.template

@@ -23,13 +23,14 @@
     EXTENSION("grpc", grpc_source, null,
       "/DOPENSSL_NO_ASM /D_GNU_SOURCE /DWIN32_LEAN_AND_MEAN "+
       "/D_HAS_EXCEPTIONS=0 /DNOMINMAX /DGRPC_ARES=0 /D_WIN32_WINNT=0x600 "+
-      "/DPB_FIELD_16BIT "+
+      "/DPB_FIELD_32BIT "+
       "/I"+configure_module_dirname+" "+
       "/I"+configure_module_dirname+"\\include "+
       "/I"+configure_module_dirname+"\\src\\php\\ext\\grpc "+
       "/I"+configure_module_dirname+"\\third_party\\boringssl\\include "+
       "/I"+configure_module_dirname+"\\third_party\\zlib "+
-      "/I"+configure_module_dirname+"\\third_party\\address_sorting\\include");
+      "/I"+configure_module_dirname+"\\third_party\\address_sorting\\include "+
+      "/I"+configure_module_dirname+"\\third_party\\nanopb");
   <%
     dirs = {}
     for lib in libs:

+ 17 - 0
test/core/channel/BUILD

@@ -83,6 +83,23 @@ grpc_cc_test(
     ],
 )
 
+grpc_cc_test(
+    name = "channelz_test",
+    srcs = ["channelz_test.cc"],
+    language = "C++",
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:grpc++",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+        "//test/cpp/util:channel_trace_proto_helper",
+    ],
+    external_deps = [
+        "gtest",
+    ],
+)
+
 grpc_cc_test(
     name = "channelz_registry_test",
     srcs = ["channelz_registry_test.cc"],

+ 107 - 89
test/core/channel/channel_trace_test.cc

@@ -25,6 +25,7 @@
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/channel/channelz.h"
 #include "src/core/lib/channel/channelz_registry.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
@@ -39,6 +40,7 @@
 #include <string.h>
 
 namespace grpc_core {
+namespace channelz {
 namespace testing {
 namespace {
 
@@ -69,7 +71,7 @@ void ValidateChannelTraceData(grpc_json* json,
   ASSERT_NE(json, nullptr);
   grpc_json* num_events_logged_json = GetJsonChild(json, "numEventsLogged");
   ASSERT_NE(num_events_logged_json, nullptr);
-  grpc_json* start_time = GetJsonChild(json, "creationTime");
+  grpc_json* start_time = GetJsonChild(json, "creationTimestamp");
   ASSERT_NE(start_time, nullptr);
   size_t num_events_logged =
       (size_t)strtol(num_events_logged_json->value, nullptr, 0);
@@ -77,35 +79,47 @@ void ValidateChannelTraceData(grpc_json* json,
   ValidateJsonArraySize(json, "events", actual_num_events_expected);
 }
 
-void AddSimpleTrace(const RefCountedPtr<ChannelTrace>& tracer) {
+void AddSimpleTrace(ChannelTrace* tracer) {
   tracer->AddTraceEvent(ChannelTrace::Severity::Info,
                         grpc_slice_from_static_string("simple trace"));
 }
 
 // checks for the existence of all the required members of the tracer.
-void ValidateChannelTrace(const RefCountedPtr<ChannelTrace>& tracer,
+void ValidateChannelTrace(ChannelTrace* tracer,
                           size_t expected_num_event_logged, size_t max_nodes) {
   if (!max_nodes) return;
-  char* json_str = tracer->RenderTrace();
+  grpc_json* json = tracer->RenderJSON();
+  EXPECT_NE(json, nullptr);
+  char* json_str = grpc_json_dump_to_string(json, 0);
+  grpc_json_destroy(json);
   grpc::testing::ValidateChannelTraceProtoJsonTranslation(json_str);
-  grpc_json* json = grpc_json_parse_string(json_str);
-  ValidateChannelTraceData(json, expected_num_event_logged,
+  grpc_json* parsed_json = grpc_json_parse_string(json_str);
+  ValidateChannelTraceData(parsed_json, expected_num_event_logged,
                            GPR_MIN(expected_num_event_logged, max_nodes));
-  grpc_json_destroy(json);
+  grpc_json_destroy(parsed_json);
   gpr_free(json_str);
 }
 
-void ValidateTraceDataMatchedUuidLookup(
-    const RefCountedPtr<ChannelTrace>& tracer) {
-  intptr_t uuid = tracer->GetUuid();
-  if (uuid == -1) return;  // Doesn't make sense to lookup if tracing disabled
-  char* tracer_json_str = tracer->RenderTrace();
-  ChannelTrace* uuid_lookup = ChannelzRegistry::Get<ChannelTrace>(uuid);
-  char* uuid_lookup_json_str = uuid_lookup->RenderTrace();
-  EXPECT_EQ(strcmp(tracer_json_str, uuid_lookup_json_str), 0);
-  gpr_free(tracer_json_str);
-  gpr_free(uuid_lookup_json_str);
-}
+class ChannelFixture {
+ public:
+  ChannelFixture(int max_trace_nodes) {
+    grpc_arg client_a;
+    client_a.type = GRPC_ARG_INTEGER;
+    client_a.key =
+        const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE);
+    client_a.value.integer = max_trace_nodes;
+    grpc_channel_args client_args = {1, &client_a};
+    channel_ =
+        grpc_insecure_channel_create("fake_target", &client_args, nullptr);
+  }
+
+  ~ChannelFixture() { grpc_channel_destroy(channel_); }
+
+  grpc_channel* channel() { return channel_; }
+
+ private:
+  grpc_channel* channel_;
+};
 
 }  // anonymous namespace
 
@@ -115,25 +129,22 @@ class ChannelTracerTest : public ::testing::TestWithParam<size_t> {};
 // lookups by uuid.
 TEST_P(ChannelTracerTest, BasicTest) {
   grpc_core::ExecCtx exec_ctx;
-  RefCountedPtr<ChannelTrace> tracer = MakeRefCounted<ChannelTrace>(GetParam());
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  ValidateTraceDataMatchedUuidLookup(tracer);
-  tracer->AddTraceEvent(ChannelTrace::Severity::Info,
-                        grpc_slice_from_static_string("trace three"));
-  tracer->AddTraceEvent(ChannelTrace::Severity::Error,
-                        grpc_slice_from_static_string("trace four error"));
-  ValidateChannelTrace(tracer, 4, GetParam());
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  ValidateChannelTrace(tracer, 6, GetParam());
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  ValidateChannelTrace(tracer, 10, GetParam());
-  ValidateTraceDataMatchedUuidLookup(tracer);
-  tracer.reset(nullptr);
+  ChannelTrace tracer(GetParam());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  tracer.AddTraceEvent(ChannelTrace::Severity::Info,
+                       grpc_slice_from_static_string("trace three"));
+  tracer.AddTraceEvent(ChannelTrace::Severity::Error,
+                       grpc_slice_from_static_string("trace four error"));
+  ValidateChannelTrace(&tracer, 4, GetParam());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  ValidateChannelTrace(&tracer, 6, GetParam());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  ValidateChannelTrace(&tracer, 10, GetParam());
 }
 
 // Tests more complex functionality, like a parent channel tracking
@@ -141,42 +152,43 @@ TEST_P(ChannelTracerTest, BasicTest) {
 // and this function will both hold refs to the subchannel.
 TEST_P(ChannelTracerTest, ComplexTest) {
   grpc_core::ExecCtx exec_ctx;
-  RefCountedPtr<ChannelTrace> tracer = MakeRefCounted<ChannelTrace>(GetParam());
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  RefCountedPtr<ChannelTrace> sc1 = MakeRefCounted<ChannelTrace>(GetParam());
-  tracer->AddTraceEventReferencingSubchannel(
+  ChannelTrace tracer(GetParam());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  ChannelFixture channel1(GetParam());
+  RefCountedPtr<ChannelNode> sc1 =
+      MakeRefCounted<ChannelNode>(channel1.channel(), GetParam());
+  tracer.AddTraceEventReferencingSubchannel(
       ChannelTrace::Severity::Info,
       grpc_slice_from_static_string("subchannel one created"), sc1);
-  ValidateChannelTrace(tracer, 3, GetParam());
-  AddSimpleTrace(sc1);
-  AddSimpleTrace(sc1);
-  AddSimpleTrace(sc1);
-  ValidateChannelTrace(sc1, 3, GetParam());
-  AddSimpleTrace(sc1);
-  AddSimpleTrace(sc1);
-  AddSimpleTrace(sc1);
-  ValidateChannelTrace(sc1, 6, GetParam());
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  ValidateChannelTrace(tracer, 5, GetParam());
-  ValidateTraceDataMatchedUuidLookup(tracer);
-  RefCountedPtr<ChannelTrace> sc2 = MakeRefCounted<ChannelTrace>(GetParam());
-  tracer->AddTraceEventReferencingChannel(
+  ValidateChannelTrace(&tracer, 3, GetParam());
+  AddSimpleTrace(sc1->trace());
+  AddSimpleTrace(sc1->trace());
+  AddSimpleTrace(sc1->trace());
+  ValidateChannelTrace(sc1->trace(), 3, GetParam());
+  AddSimpleTrace(sc1->trace());
+  AddSimpleTrace(sc1->trace());
+  AddSimpleTrace(sc1->trace());
+  ValidateChannelTrace(sc1->trace(), 6, GetParam());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  ValidateChannelTrace(&tracer, 5, GetParam());
+  ChannelFixture channel2(GetParam());
+  RefCountedPtr<ChannelNode> sc2 =
+      MakeRefCounted<ChannelNode>(channel2.channel(), GetParam());
+  tracer.AddTraceEventReferencingChannel(
       ChannelTrace::Severity::Info,
       grpc_slice_from_static_string("LB channel two created"), sc2);
-  tracer->AddTraceEventReferencingSubchannel(
+  tracer.AddTraceEventReferencingSubchannel(
       ChannelTrace::Severity::Warning,
       grpc_slice_from_static_string("subchannel one inactive"), sc1);
-  ValidateChannelTrace(tracer, 7, GetParam());
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  ValidateTraceDataMatchedUuidLookup(tracer);
-  tracer.reset(nullptr);
+  ValidateChannelTrace(&tracer, 7, GetParam());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
   sc1.reset(nullptr);
   sc2.reset(nullptr);
 }
@@ -186,39 +198,44 @@ TEST_P(ChannelTracerTest, ComplexTest) {
 // gets deleted.
 TEST_P(ChannelTracerTest, TestNesting) {
   grpc_core::ExecCtx exec_ctx;
-  RefCountedPtr<ChannelTrace> tracer = MakeRefCounted<ChannelTrace>(GetParam());
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  ValidateChannelTrace(tracer, 2, GetParam());
-  RefCountedPtr<ChannelTrace> sc1 = MakeRefCounted<ChannelTrace>(GetParam());
-  tracer->AddTraceEventReferencingChannel(
+  ChannelTrace tracer(GetParam());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  ValidateChannelTrace(&tracer, 2, GetParam());
+  ChannelFixture channel1(GetParam());
+  RefCountedPtr<ChannelNode> sc1 =
+      MakeRefCounted<ChannelNode>(channel1.channel(), GetParam());
+  tracer.AddTraceEventReferencingChannel(
       ChannelTrace::Severity::Info,
       grpc_slice_from_static_string("subchannel one created"), sc1);
-  ValidateChannelTrace(tracer, 3, GetParam());
-  AddSimpleTrace(sc1);
-  RefCountedPtr<ChannelTrace> conn1 = MakeRefCounted<ChannelTrace>(GetParam());
+  ValidateChannelTrace(&tracer, 3, GetParam());
+  AddSimpleTrace(sc1->trace());
+  ChannelFixture channel2(GetParam());
+  RefCountedPtr<ChannelNode> conn1 =
+      MakeRefCounted<ChannelNode>(channel2.channel(), GetParam());
   // nesting one level deeper.
-  sc1->AddTraceEventReferencingSubchannel(
+  sc1->trace()->AddTraceEventReferencingSubchannel(
       ChannelTrace::Severity::Info,
       grpc_slice_from_static_string("connection one created"), conn1);
-  ValidateChannelTrace(tracer, 3, GetParam());
-  AddSimpleTrace(conn1);
-  AddSimpleTrace(tracer);
-  AddSimpleTrace(tracer);
-  ValidateChannelTrace(tracer, 5, GetParam());
-  ValidateChannelTrace(conn1, 1, GetParam());
-  RefCountedPtr<ChannelTrace> sc2 = MakeRefCounted<ChannelTrace>(GetParam());
-  tracer->AddTraceEventReferencingSubchannel(
+  ValidateChannelTrace(&tracer, 3, GetParam());
+  AddSimpleTrace(conn1->trace());
+  AddSimpleTrace(&tracer);
+  AddSimpleTrace(&tracer);
+  ValidateChannelTrace(&tracer, 5, GetParam());
+  ValidateChannelTrace(conn1->trace(), 1, GetParam());
+  ChannelFixture channel3(GetParam());
+  RefCountedPtr<ChannelNode> sc2 =
+      MakeRefCounted<ChannelNode>(channel3.channel(), GetParam());
+  tracer.AddTraceEventReferencingSubchannel(
       ChannelTrace::Severity::Info,
       grpc_slice_from_static_string("subchannel two created"), sc2);
   // this trace should not get added to the parents children since it is already
   // present in the tracer.
-  tracer->AddTraceEventReferencingChannel(
+  tracer.AddTraceEventReferencingChannel(
       ChannelTrace::Severity::Warning,
       grpc_slice_from_static_string("subchannel one inactive"), sc1);
-  AddSimpleTrace(tracer);
-  ValidateChannelTrace(tracer, 8, GetParam());
-  tracer.reset(nullptr);
+  AddSimpleTrace(&tracer);
+  ValidateChannelTrace(&tracer, 8, GetParam());
   sc1.reset(nullptr);
   sc2.reset(nullptr);
   conn1.reset(nullptr);
@@ -228,6 +245,7 @@ INSTANTIATE_TEST_CASE_P(ChannelTracerTestSweep, ChannelTracerTest,
                         ::testing::Values(0, 1, 2, 6, 10, 15));
 
 }  // namespace testing
+}  // namespace channelz
 }  // namespace grpc_core
 
 int main(int argc, char** argv) {

+ 216 - 0
test/core/channel/channelz_test.cc

@@ -0,0 +1,216 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/channel/channelz.h"
+#include "src/core/lib/channel/channelz_registry.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/surface/channel.h"
+
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/channel_trace_proto_helper.h"
+
+#include <grpc/support/string_util.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace grpc_core {
+namespace channelz {
+namespace testing {
+
+// testing peer to access channel internals
+class ChannelNodePeer {
+ public:
+  ChannelNodePeer(ChannelNode* channel) : channel_(channel) {}
+  grpc_millis last_call_started_millis() {
+    return (grpc_millis)gpr_atm_no_barrier_load(
+        &channel_->last_call_started_millis_);
+  }
+
+ private:
+  ChannelNode* channel_;
+};
+
+namespace {
+
+grpc_json* GetJsonChild(grpc_json* parent, const char* key) {
+  EXPECT_NE(parent, nullptr);
+  for (grpc_json* child = parent->child; child != nullptr;
+       child = child->next) {
+    if (child->key != nullptr && strcmp(child->key, key) == 0) return child;
+  }
+  return nullptr;
+}
+
+class ChannelFixture {
+ public:
+  ChannelFixture(int max_trace_nodes) {
+    grpc_arg client_a[2];
+    client_a[0].type = GRPC_ARG_INTEGER;
+    client_a[0].key =
+        const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE);
+    client_a[0].value.integer = max_trace_nodes;
+    client_a[1].type = GRPC_ARG_INTEGER;
+    client_a[1].key = const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ);
+    client_a[1].value.integer = true;
+    grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a};
+    channel_ =
+        grpc_insecure_channel_create("fake_target", &client_args, nullptr);
+  }
+
+  ~ChannelFixture() { grpc_channel_destroy(channel_); }
+
+  grpc_channel* channel() { return channel_; }
+
+ private:
+  grpc_channel* channel_;
+};
+
+struct validate_channel_data_args {
+  int64_t calls_started;
+  int64_t calls_failed;
+  int64_t calls_succeeded;
+};
+
+void ValidateChildInteger(grpc_json* json, int64_t expect, const char* key) {
+  grpc_json* gotten_json = GetJsonChild(json, key);
+  ASSERT_NE(gotten_json, nullptr);
+  int64_t gotten_number = (int64_t)strtol(gotten_json->value, nullptr, 0);
+  EXPECT_EQ(gotten_number, expect);
+}
+
+void ValidateCounters(char* json_str, validate_channel_data_args args) {
+  grpc_json* json = grpc_json_parse_string(json_str);
+  ASSERT_NE(json, nullptr);
+  grpc_json* data = GetJsonChild(json, "data");
+  ValidateChildInteger(data, args.calls_started, "callsStarted");
+  ValidateChildInteger(data, args.calls_failed, "callsFailed");
+  ValidateChildInteger(data, args.calls_succeeded, "callsSucceeded");
+  grpc_json_destroy(json);
+}
+
+void ValidateChannel(ChannelNode* channel, validate_channel_data_args args) {
+  char* json_str = channel->RenderJSON();
+  grpc::testing::ValidateChannelProtoJsonTranslation(json_str);
+  ValidateCounters(json_str, args);
+  gpr_free(json_str);
+}
+
+grpc_millis GetLastCallStartedMillis(ChannelNode* channel) {
+  ChannelNodePeer peer(channel);
+  return peer.last_call_started_millis();
+}
+
+void ChannelzSleep(int64_t sleep_us) {
+  gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                               gpr_time_from_micros(sleep_us, GPR_TIMESPAN)));
+  grpc_core::ExecCtx::Get()->InvalidateNow();
+}
+
+}  // anonymous namespace
+
+class ChannelzChannelTest : public ::testing::TestWithParam<size_t> {};
+
+TEST_P(ChannelzChannelTest, BasicChannel) {
+  grpc_core::ExecCtx exec_ctx;
+  ChannelFixture channel(GetParam());
+  ChannelNode* channelz_channel =
+      grpc_channel_get_channelz_node(channel.channel());
+  char* json_str = channelz_channel->RenderJSON();
+  ValidateCounters(json_str, {0, 0, 0});
+  gpr_free(json_str);
+}
+
+TEST(ChannelzChannelTest, ChannelzDisabled) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_channel* channel =
+      grpc_insecure_channel_create("fake_target", nullptr, nullptr);
+  ChannelNode* channelz_channel = grpc_channel_get_channelz_node(channel);
+  ASSERT_EQ(channelz_channel, nullptr);
+  grpc_channel_destroy(channel);
+}
+
+TEST_P(ChannelzChannelTest, BasicChannelAPIFunctionality) {
+  grpc_core::ExecCtx exec_ctx;
+  ChannelFixture channel(GetParam());
+  ChannelNode* channelz_channel =
+      grpc_channel_get_channelz_node(channel.channel());
+  channelz_channel->RecordCallStarted();
+  channelz_channel->RecordCallFailed();
+  channelz_channel->RecordCallSucceeded();
+  ValidateChannel(channelz_channel, {1, 1, 1});
+  channelz_channel->RecordCallStarted();
+  channelz_channel->RecordCallFailed();
+  channelz_channel->RecordCallSucceeded();
+  channelz_channel->RecordCallStarted();
+  channelz_channel->RecordCallFailed();
+  channelz_channel->RecordCallSucceeded();
+  ValidateChannel(channelz_channel, {3, 3, 3});
+}
+
+TEST_P(ChannelzChannelTest, LastCallStartedMillis) {
+  grpc_core::ExecCtx exec_ctx;
+  ChannelFixture channel(GetParam());
+  ChannelNode* channelz_channel =
+      grpc_channel_get_channelz_node(channel.channel());
+  // start a call to set the last call started timestamp
+  channelz_channel->RecordCallStarted();
+  grpc_millis millis1 = GetLastCallStartedMillis(channelz_channel);
+  // time gone by should not affect the timestamp
+  ChannelzSleep(100);
+  grpc_millis millis2 = GetLastCallStartedMillis(channelz_channel);
+  EXPECT_EQ(millis1, millis2);
+  // calls succeeded or failed should not affect the timestamp
+  ChannelzSleep(100);
+  channelz_channel->RecordCallFailed();
+  channelz_channel->RecordCallSucceeded();
+  grpc_millis millis3 = GetLastCallStartedMillis(channelz_channel);
+  EXPECT_EQ(millis1, millis3);
+  // another call started should affect the timestamp
+  // sleep for extra long to avoid flakes (since we cache Now())
+  ChannelzSleep(5000);
+  channelz_channel->RecordCallStarted();
+  grpc_millis millis4 = GetLastCallStartedMillis(channelz_channel);
+  EXPECT_NE(millis1, millis4);
+}
+
+INSTANTIATE_TEST_CASE_P(ChannelzChannelTestSweep, ChannelzChannelTest,
+                        ::testing::Values(0, 1, 2, 6, 10, 15));
+
+}  // namespace testing
+}  // namespace channelz
+}  // namespace grpc_core
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  grpc_init();
+  ::testing::InitGoogleTest(&argc, argv);
+  int ret = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return ret;
+}

+ 8 - 0
test/core/end2end/end2end_nosec_tests.cc

@@ -54,6 +54,8 @@ extern void cancel_in_a_vacuum(grpc_end2end_test_config config);
 extern void cancel_in_a_vacuum_pre_init(void);
 extern void cancel_with_status(grpc_end2end_test_config config);
 extern void cancel_with_status_pre_init(void);
+extern void channelz(grpc_end2end_test_config config);
+extern void channelz_pre_init(void);
 extern void compressed_payload(grpc_end2end_test_config config);
 extern void compressed_payload_pre_init(void);
 extern void connectivity(grpc_end2end_test_config config);
@@ -201,6 +203,7 @@ void grpc_end2end_tests_pre_init(void) {
   cancel_before_invoke_pre_init();
   cancel_in_a_vacuum_pre_init();
   cancel_with_status_pre_init();
+  channelz_pre_init();
   compressed_payload_pre_init();
   connectivity_pre_init();
   default_host_pre_init();
@@ -287,6 +290,7 @@ void grpc_end2end_tests(int argc, char **argv,
     cancel_before_invoke(config);
     cancel_in_a_vacuum(config);
     cancel_with_status(config);
+    channelz(config);
     compressed_payload(config);
     connectivity(config);
     default_host(config);
@@ -404,6 +408,10 @@ void grpc_end2end_tests(int argc, char **argv,
       cancel_with_status(config);
       continue;
     }
+    if (0 == strcmp("channelz", argv[i])) {
+      channelz(config);
+      continue;
+    }
     if (0 == strcmp("compressed_payload", argv[i])) {
       compressed_payload(config);
       continue;

+ 8 - 0
test/core/end2end/end2end_tests.cc

@@ -56,6 +56,8 @@ extern void cancel_in_a_vacuum(grpc_end2end_test_config config);
 extern void cancel_in_a_vacuum_pre_init(void);
 extern void cancel_with_status(grpc_end2end_test_config config);
 extern void cancel_with_status_pre_init(void);
+extern void channelz(grpc_end2end_test_config config);
+extern void channelz_pre_init(void);
 extern void compressed_payload(grpc_end2end_test_config config);
 extern void compressed_payload_pre_init(void);
 extern void connectivity(grpc_end2end_test_config config);
@@ -204,6 +206,7 @@ void grpc_end2end_tests_pre_init(void) {
   cancel_before_invoke_pre_init();
   cancel_in_a_vacuum_pre_init();
   cancel_with_status_pre_init();
+  channelz_pre_init();
   compressed_payload_pre_init();
   connectivity_pre_init();
   default_host_pre_init();
@@ -291,6 +294,7 @@ void grpc_end2end_tests(int argc, char **argv,
     cancel_before_invoke(config);
     cancel_in_a_vacuum(config);
     cancel_with_status(config);
+    channelz(config);
     compressed_payload(config);
     connectivity(config);
     default_host(config);
@@ -412,6 +416,10 @@ void grpc_end2end_tests(int argc, char **argv,
       cancel_with_status(config);
       continue;
     }
+    if (0 == strcmp("channelz", argv[i])) {
+      channelz(config);
+      continue;
+    }
     if (0 == strcmp("compressed_payload", argv[i])) {
       compressed_payload(config);
       continue;

+ 1 - 0
test/core/end2end/fuzzers/api_fuzzer.cc

@@ -1046,6 +1046,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
           op->reserved = nullptr;
           op->flags = grpc_fuzzer_get_next_uint32(&inp);
         }
+        if (g_channel == nullptr) ok = false;
         if (ok) {
           validator* v = make_finished_batch_validator(g_active_call, has_ops);
           g_active_call->pending_ops++;

+ 1 - 0
test/core/end2end/gen_build_yaml.py

@@ -106,6 +106,7 @@ END2END_TESTS = {
                                                         needs_compression=True),
     'connectivity': connectivity_test_options._replace(needs_names=True,
         proxyable=False, cpu_cost=LOWCPU, exclude_iomgrs=['uv']),
+    'channelz': default_test_options,
     'default_host': default_test_options._replace(
         needs_fullstack=True, needs_dns=True, needs_names=True),
     'call_host_override': default_test_options._replace(

+ 1 - 0
test/core/end2end/generate_tests.bzl

@@ -113,6 +113,7 @@ END2END_TESTS = {
     'compressed_payload': test_options(proxyable=False, exclude_inproc=True),
     'connectivity': test_options(needs_fullstack=True, needs_names=True,
                                  proxyable=False),
+    'channelz': test_options(),
     'default_host': test_options(needs_fullstack=True, needs_dns=True,
                                  needs_names=True),
     'disappearing_server': test_options(needs_fullstack=True,needs_names=True),

+ 299 - 0
test/core/end2end/tests/channelz.cc

@@ -0,0 +1,299 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/surface/channel.h"
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/gpr/string.h"
+#include "test/core/end2end/cq_verifier.h"
+
+static void* tag(intptr_t t) { return (void*)t; }
+
+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, "Running test: %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_from_now(int n) {
+  return grpc_timeout_seconds_to_deadline(n);
+}
+
+static gpr_timespec five_seconds_from_now(void) {
+  return n_seconds_from_now(5);
+}
+
+static void drain_cq(grpc_completion_queue* cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_from_now(), nullptr);
+  } 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->shutdown_cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(f->shutdown_cq, tag(1000),
+                                         grpc_timeout_seconds_to_deadline(5),
+                                         nullptr)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = nullptr;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture* f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = nullptr;
+}
+
+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);
+  grpc_completion_queue_destroy(f->shutdown_cq);
+}
+
+static void run_one_request(grpc_end2end_test_config config,
+                            grpc_end2end_test_fixture f,
+                            bool request_is_success) {
+  grpc_call* c;
+  grpc_call* s;
+  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_call_details call_details;
+  grpc_status_code status;
+  grpc_call_error error;
+  grpc_slice details;
+  int was_cancelled = 2;
+
+  gpr_timespec deadline = five_seconds_from_now();
+  c = grpc_channel_create_call(f.client, nullptr, GRPC_PROPAGATE_DEFAULTS, f.cq,
+                               grpc_slice_from_static_string("/foo"), nullptr,
+                               deadline, nullptr);
+  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;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = nullptr;
+  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.error_string = nullptr;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1),
+                                nullptr);
+  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 = nullptr;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status =
+      request_is_success ? GRPC_STATUS_OK : GRPC_STATUS_UNIMPLEMENTED;
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(102),
+                                nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == call_details.flags);
+
+  grpc_slice_unref(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_unref(c);
+  grpc_call_unref(s);
+
+  cq_verifier_destroy(cqv);
+}
+
+static void test_channelz(grpc_end2end_test_config config) {
+  grpc_end2end_test_fixture f;
+
+  grpc_arg client_a;
+  client_a.type = GRPC_ARG_INTEGER;
+  client_a.key = const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ);
+  client_a.value.integer = true;
+  grpc_channel_args client_args = {1, &client_a};
+
+  f = begin_test(config, "test_channelz", &client_args, nullptr);
+  grpc_core::channelz::ChannelNode* channelz_channel =
+      grpc_channel_get_channelz_node(f.client);
+
+  GPR_ASSERT(channelz_channel != nullptr);
+  char* json = channelz_channel->RenderJSON();
+  GPR_ASSERT(json != nullptr);
+  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"0\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"0\""));
+  gpr_free(json);
+
+  // one successful request
+  run_one_request(config, f, true);
+
+  json = channelz_channel->RenderJSON();
+  GPR_ASSERT(json != nullptr);
+  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"1\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
+  gpr_free(json);
+
+  // one failed request
+  run_one_request(config, f, false);
+
+  json = channelz_channel->RenderJSON();
+  GPR_ASSERT(json != nullptr);
+  gpr_log(GPR_INFO, "%s", json);
+  GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"2\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"1\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\""));
+  // channel tracing is not enables, so these should not be preset.
+  GPR_ASSERT(nullptr == strstr(json, "\"trace\""));
+  GPR_ASSERT(nullptr == strstr(json, "\"description\":\"Channel created\""));
+  GPR_ASSERT(nullptr == strstr(json, "\"severity\":\"CT_INFO\""));
+  gpr_free(json);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+static void test_channelz_with_channel_trace(grpc_end2end_test_config config) {
+  grpc_end2end_test_fixture f;
+
+  grpc_arg client_a[2];
+  client_a[0].type = GRPC_ARG_INTEGER;
+  client_a[0].key =
+      const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE);
+  client_a[0].value.integer = 5;
+  client_a[1].type = GRPC_ARG_INTEGER;
+  client_a[1].key = const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ);
+  client_a[1].value.integer = true;
+  grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a};
+
+  f = begin_test(config, "test_channelz_with_channel_trace", &client_args,
+                 nullptr);
+  grpc_core::channelz::ChannelNode* channelz_channel =
+      grpc_channel_get_channelz_node(f.client);
+
+  GPR_ASSERT(channelz_channel != nullptr);
+  char* json = channelz_channel->RenderJSON();
+  GPR_ASSERT(json != nullptr);
+  gpr_log(GPR_INFO, "%s", json);
+  GPR_ASSERT(nullptr != strstr(json, "\"trace\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"description\":\"Channel created\""));
+  GPR_ASSERT(nullptr != strstr(json, "\"severity\":\"CT_INFO\""));
+  gpr_free(json);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+static void test_channelz_disabled(grpc_end2end_test_config config) {
+  grpc_end2end_test_fixture f;
+
+  f = begin_test(config, "test_channelz_disabled", nullptr, nullptr);
+  grpc_core::channelz::ChannelNode* channelz_channel =
+      grpc_channel_get_channelz_node(f.client);
+  GPR_ASSERT(channelz_channel == nullptr);
+  // one successful request
+  run_one_request(config, f, true);
+  GPR_ASSERT(channelz_channel == nullptr);
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void channelz(grpc_end2end_test_config config) {
+  test_channelz(config);
+  test_channelz_with_channel_trace(config);
+  test_channelz_disabled(config);
+}
+
+void channelz_pre_init(void) {}

+ 0 - 2
test/core/surface/public_headers_must_be_c89.c

@@ -106,8 +106,6 @@ int main(int argc, char **argv) {
   printf("%lx", (unsigned long) grpc_insecure_channel_create);
   printf("%lx", (unsigned long) grpc_lame_client_channel_create);
   printf("%lx", (unsigned long) grpc_channel_destroy);
-  printf("%lx", (unsigned long) grpc_channel_get_trace);
-  printf("%lx", (unsigned long) grpc_channel_get_uuid);
   printf("%lx", (unsigned long) grpc_call_cancel);
   printf("%lx", (unsigned long) grpc_call_cancel_with_status);
   printf("%lx", (unsigned long) grpc_call_ref);

+ 14 - 0
test/cpp/microbenchmarks/BUILD

@@ -54,6 +54,20 @@ grpc_cc_binary(
     deps = [":helpers"],
 )
 
+grpc_cc_binary(
+    name = "bm_arena",
+    testonly = 1,
+    srcs = ["bm_arena.cc"],
+    deps = [":helpers"],
+)
+
+grpc_cc_binary(
+    name = "bm_channel",
+    testonly = 1,
+    srcs = ["bm_channel.cc"],
+    deps = [":helpers"],
+)
+
 grpc_cc_binary(
     name = "bm_cq",
     testonly = 1,

+ 90 - 0
test/cpp/microbenchmarks/bm_channel.cc

@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* Benchmark channel */
+
+#include <benchmark/benchmark.h>
+#include <grpc/grpc.h>
+#include "test/cpp/microbenchmarks/helpers.h"
+#include "test/cpp/util/test_config.h"
+
+auto& force_library_initialization = Library::get();
+
+class ChannelDestroyerFixture {
+ public:
+  ChannelDestroyerFixture() {}
+  virtual ~ChannelDestroyerFixture() {
+    if (channel_) {
+      grpc_channel_destroy(channel_);
+    }
+  }
+  virtual void Init() = 0;
+
+ protected:
+  grpc_channel* channel_ = nullptr;
+};
+
+class InsecureChannelFixture : public ChannelDestroyerFixture {
+ public:
+  InsecureChannelFixture() {}
+  void Init() override {
+    channel_ = grpc_insecure_channel_create("localhost:1234", nullptr, nullptr);
+  }
+};
+
+class LameChannelFixture : public ChannelDestroyerFixture {
+ public:
+  LameChannelFixture() {}
+  void Init() override {
+    channel_ = grpc_lame_client_channel_create(
+        "localhost:1234", GRPC_STATUS_UNAUTHENTICATED, "blah");
+  }
+};
+
+template <class Fixture>
+static void BM_InsecureChannelCreateDestroy(benchmark::State& state) {
+  // In order to test if channel creation time is affected by the number of
+  // already existing channels, we create some initial channels here.
+  Fixture initial_channels[512];
+  for (int i = 0; i < state.range(0); i++) {
+    initial_channels[i].Init();
+  }
+  while (state.KeepRunning()) {
+    Fixture channel;
+    channel.Init();
+  }
+}
+BENCHMARK_TEMPLATE(BM_InsecureChannelCreateDestroy, InsecureChannelFixture)
+    ->Range(0, 512);
+;
+BENCHMARK_TEMPLATE(BM_InsecureChannelCreateDestroy, LameChannelFixture)
+    ->Range(0, 512);
+;
+
+// Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
+// and others do not. This allows us to support both modes.
+namespace benchmark {
+void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
+}  // namespace benchmark
+
+int main(int argc, char** argv) {
+  ::benchmark::Initialize(&argc, argv);
+  ::grpc::testing::InitTest(&argc, &argv, false);
+  benchmark::RunTheBenchmarksNamespaced();
+  return 0;
+}

+ 33 - 1
test/cpp/server/load_reporter/BUILD

@@ -14,7 +14,7 @@
 
 licenses(["notice"])  # Apache v2
 
-load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_cc_library", "grpc_cc_binary", "grpc_package")
+load("//bazel:grpc_build_system.bzl", "grpc_cc_binary", "grpc_cc_library", "grpc_cc_test", "grpc_package")
 
 grpc_package(name = "test/cpp/server/load_reporter")
 
@@ -29,3 +29,35 @@ grpc_cc_test(
         "//test/core/util:grpc_test_util",
     ],
 )
+
+grpc_cc_test(
+    name = "lb_load_reporter_test",
+    srcs = ["load_reporter_test.cc"],
+    external_deps = [
+        "gtest",
+        "gmock",
+        "opencensus-stats-test",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:lb_load_reporter",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)
+
+grpc_cc_test(
+    name = "lb_get_cpu_stats_test",
+    srcs = ["get_cpu_stats_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    deps = [
+        "//:gpr",
+        "//:grpc",
+        "//:lb_get_cpu_stats",
+        "//test/core/util:gpr_test_util",
+        "//test/core/util:grpc_test_util",
+    ],
+)

+ 61 - 0
test/cpp/server/load_reporter/get_cpu_stats_test.cc

@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace testing {
+namespace {
+
+TEST(GetCpuStatsTest, ReadOnce) { ::grpc::load_reporter::GetCpuStatsImpl(); }
+
+TEST(GetCpuStatsTest, BusyNoLargerThanTotal) {
+  auto p = ::grpc::load_reporter::GetCpuStatsImpl();
+  uint64_t busy = p.first;
+  uint64_t total = p.second;
+  ASSERT_LE(busy, total);
+}
+
+TEST(GetCpuStatsTest, Ascending) {
+  const size_t kRuns = 100;
+  auto prev = ::grpc::load_reporter::GetCpuStatsImpl();
+  for (size_t i = 0; i < kRuns; ++i) {
+    auto cur = ::grpc::load_reporter::GetCpuStatsImpl();
+    ASSERT_LE(prev.first, cur.first);
+    ASSERT_LE(prev.second, cur.second);
+    prev = cur;
+  }
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}

+ 2 - 2
test/cpp/server/load_reporter/load_data_store_test.cc

@@ -393,9 +393,9 @@ TEST_F(PerBalancerStoreTest, Suspend) {
 TEST_F(PerBalancerStoreTest, DataAggregation) {
   PerBalancerStore per_balancer_store(kLbId1, kLoadKey1);
   // Construct some Values.
-  LoadRecordValue v1(992, 34, 13, 234.0, 164.0, 173467.38);
+  LoadRecordValue v1(992, 34, 13, 234, 164, 173467);
   v1.InsertCallMetric(kMetric1, CallMetricValue(3, 2773.2));
-  LoadRecordValue v2(4842, 213, 9, 393.0, 974.0, 1345.2398);
+  LoadRecordValue v2(4842, 213, 9, 393, 974, 1345);
   v2.InsertCallMetric(kMetric1, CallMetricValue(7, 25.234));
   v2.InsertCallMetric(kMetric2, CallMetricValue(2, 387.08));
   // v3 doesn't change the number of in-progress RPCs.

+ 498 - 0
test/cpp/server/load_reporter/load_reporter_test.cc

@@ -0,0 +1,498 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <set>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/cpp/server/load_reporter/constants.h"
+#include "src/cpp/server/load_reporter/load_reporter.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+#include "opencensus/stats/testing/test_utils.h"
+
+namespace grpc {
+namespace testing {
+namespace {
+
+using ::grpc::lb::v1::LoadBalancingFeedback;
+using ::grpc::load_reporter::CensusViewProvider;
+using ::grpc::load_reporter::CpuStatsProvider;
+using ::grpc::load_reporter::LoadReporter;
+using ::opencensus::stats::View;
+using ::opencensus::stats::ViewData;
+using ::opencensus::stats::ViewDataImpl;
+using ::opencensus::stats::ViewDescriptor;
+using ::testing::DoubleNear;
+using ::testing::Return;
+
+constexpr uint64_t kFeedbackSampleWindowSeconds = 5;
+constexpr uint64_t kFetchAndSampleIntervalSeconds = 1;
+constexpr uint64_t kNumFeedbackSamplesInWindow =
+    kFeedbackSampleWindowSeconds / kFetchAndSampleIntervalSeconds;
+
+class MockCensusViewProvider : public CensusViewProvider {
+ public:
+  MOCK_METHOD0(FetchViewData, CensusViewProvider::ViewDataMap());
+
+  const ::opencensus::stats::ViewDescriptor& FindViewDescriptor(
+      const grpc::string& view_name) {
+    auto it = view_descriptor_map().find(view_name);
+    GPR_ASSERT(it != view_descriptor_map().end());
+    return it->second;
+  }
+};
+
+class MockCpuStatsProvider : public CpuStatsProvider {
+ public:
+  MOCK_METHOD0(GetCpuStats, CpuStatsProvider::CpuStatsSample());
+};
+
+class LoadReporterTest : public ::testing::Test {
+ public:
+  LoadReporterTest() {}
+
+  MockCensusViewProvider* mock_census_view_provider() {
+    return static_cast<MockCensusViewProvider*>(
+        load_reporter_->census_view_provider());
+  }
+
+  void PrepareCpuExpectation(size_t call_num) {
+    auto mock_cpu_stats_provider = static_cast<MockCpuStatsProvider*>(
+        load_reporter_->cpu_stats_provider());
+    ::testing::InSequence s;
+    for (size_t i = 0; i < call_num; ++i) {
+      EXPECT_CALL(*mock_cpu_stats_provider, GetCpuStats())
+          .WillOnce(Return(kCpuStatsSamples[i]))
+          .RetiresOnSaturation();
+    }
+  }
+
+  CpuStatsProvider::CpuStatsSample initial_cpu_stats_{2, 20};
+  const std::vector<CpuStatsProvider::CpuStatsSample> kCpuStatsSamples = {
+      {13, 53},    {64, 96},     {245, 345},  {314, 785},
+      {874, 1230}, {1236, 2145}, {1864, 2974}};
+
+  std::unique_ptr<LoadReporter> load_reporter_;
+
+  const grpc::string kHostname1 = "kHostname1";
+  const grpc::string kHostname2 = "kHostname2";
+  const grpc::string kHostname3 = "kHostname3";
+  // Pad to the length of a valid LB ID.
+  const grpc::string kLbId1 = "kLbId111";
+  const grpc::string kLbId2 = "kLbId222";
+  const grpc::string kLbId3 = "kLbId333";
+  const grpc::string kLbId4 = "kLbId444";
+  const grpc::string kLoadKey1 = "kLoadKey1";
+  const grpc::string kLoadKey2 = "kLoadKey2";
+  const grpc::string kLoadKey3 = "kLoadKey3";
+  const grpc::string kLbTag1 = "kLbTag1";
+  const grpc::string kLbTag2 = "kLbTag2";
+  const grpc::string kLbToken1 = "kLbId111kLbTag1";
+  const grpc::string kLbToken2 = "kLbId222kLbTag2";
+  const grpc::string kUser1 = "kUser1";
+  const grpc::string kUser2 = "kUser2";
+  const grpc::string kUser3 = "kUser3";
+  const grpc::string kClientIp0 = "00";
+  const grpc::string kClientIp1 = "0800000001";
+  const grpc::string kClientIp2 = "3200000000000000000000000000000002";
+  const grpc::string kMetric1 = "kMetric1";
+  const grpc::string kMetric2 = "kMetric2";
+
+ private:
+  void SetUp() override {
+    auto mock_cpu = new MockCpuStatsProvider();
+    auto mock_census = new MockCensusViewProvider();
+    // Prepare the initial CPU stats data. Note that the expectation should be
+    // set up before the load reporter is initialized, because CPU stats is
+    // sampled at that point.
+    EXPECT_CALL(*mock_cpu, GetCpuStats())
+        .WillOnce(Return(initial_cpu_stats_))
+        .RetiresOnSaturation();
+    load_reporter_ = std::unique_ptr<LoadReporter>(
+        new LoadReporter(kFeedbackSampleWindowSeconds,
+                         std::unique_ptr<CensusViewProvider>(mock_census),
+                         std::unique_ptr<CpuStatsProvider>(mock_cpu)));
+  }
+};
+
+class LbFeedbackTest : public LoadReporterTest {
+ public:
+  // Note that [start, start + count) of the fake samples (maybe plus the
+  // initial record) are in the window now.
+  void VerifyLbFeedback(const LoadBalancingFeedback& lb_feedback, size_t start,
+                        size_t count) {
+    const CpuStatsProvider::CpuStatsSample* base =
+        start == 0 ? &initial_cpu_stats_ : &kCpuStatsSamples[start - 1];
+    double expected_cpu_util =
+        static_cast<double>(kCpuStatsSamples[start + count - 1].first -
+                            base->first) /
+        static_cast<double>(kCpuStatsSamples[start + count - 1].second -
+                            base->second);
+    ASSERT_THAT(static_cast<double>(lb_feedback.server_utilization()),
+                DoubleNear(expected_cpu_util, 0.00001));
+    double qps_sum = 0, eps_sum = 0;
+    for (size_t i = 0; i < count; ++i) {
+      qps_sum += kQpsEpsSamples[start + i].first;
+      eps_sum += kQpsEpsSamples[start + i].second;
+    }
+    double expected_qps = qps_sum / count;
+    double expected_eps = eps_sum / count;
+    // TODO(juanlishen): The error is big because we use sleep(). It should be
+    // much smaller when we use fake clock.
+    ASSERT_THAT(static_cast<double>(lb_feedback.calls_per_second()),
+                DoubleNear(expected_qps, expected_qps / 50));
+    ASSERT_THAT(static_cast<double>(lb_feedback.errors_per_second()),
+                DoubleNear(expected_eps, expected_eps / 50));
+    gpr_log(GPR_INFO,
+            "Verified LB feedback matches the samples of index [%lu, %lu).",
+            start, start + count);
+  }
+
+  const std::vector<std::pair<double, double>> kQpsEpsSamples = {
+      {546.1, 153.1},  {62.1, 54.1},   {578.1, 154.2}, {978.1, 645.1},
+      {1132.1, 846.4}, {531.5, 315.4}, {874.1, 324.9}};
+};
+
+TEST_F(LbFeedbackTest, ZeroDuration) {
+  PrepareCpuExpectation(kCpuStatsSamples.size());
+  EXPECT_CALL(*mock_census_view_provider(), FetchViewData())
+      .WillRepeatedly(
+          Return(::grpc::load_reporter::CensusViewProvider::ViewDataMap()));
+  // Verify that divide-by-zero exception doesn't happen.
+  for (size_t i = 0; i < kCpuStatsSamples.size(); ++i) {
+    load_reporter_->FetchAndSample();
+  }
+  load_reporter_->GenerateLoadBalancingFeedback();
+}
+
+TEST_F(LbFeedbackTest, Normal) {
+  // Prepare view data list using the <QPS, EPS> samples.
+  std::vector<CensusViewProvider::ViewDataMap> view_data_map_list;
+  for (const auto& p : LbFeedbackTest::kQpsEpsSamples) {
+    double qps = p.first;
+    double eps = p.second;
+    double ok_count = (qps - eps) * kFetchAndSampleIntervalSeconds;
+    double error_count = eps * kFetchAndSampleIntervalSeconds;
+    double ok_count_1 = ok_count / 3.0;
+    double ok_count_2 = ok_count - ok_count_1;
+    auto end_count_vd = ::opencensus::stats::testing::TestUtils::MakeViewData(
+        mock_census_view_provider()->FindViewDescriptor(
+            ::grpc::load_reporter::kViewEndCount),
+        {{{kClientIp0 + kLbToken1, kHostname1, kUser1,
+           ::grpc::load_reporter::kCallStatusOk},
+          ok_count_1},
+         {{kClientIp0 + kLbToken1, kHostname1, kUser2,
+           ::grpc::load_reporter::kCallStatusOk},
+          ok_count_2},
+         {{kClientIp0 + kLbToken1, kHostname1, kUser1,
+           ::grpc::load_reporter::kCallStatusClientError},
+          error_count}});
+    // Values for other view data don't matter.
+    auto end_bytes_sent_vd =
+        ::opencensus::stats::testing::TestUtils::MakeViewData(
+            mock_census_view_provider()->FindViewDescriptor(
+                ::grpc::load_reporter::kViewEndBytesSent),
+            {{{kClientIp0 + kLbToken1, kHostname1, kUser1,
+               ::grpc::load_reporter::kCallStatusOk},
+              0},
+             {{kClientIp0 + kLbToken1, kHostname1, kUser2,
+               ::grpc::load_reporter::kCallStatusOk},
+              0},
+             {{kClientIp0 + kLbToken1, kHostname1, kUser1,
+               ::grpc::load_reporter::kCallStatusClientError},
+              0}});
+    auto end_bytes_received_vd =
+        ::opencensus::stats::testing::TestUtils::MakeViewData(
+            mock_census_view_provider()->FindViewDescriptor(
+                ::grpc::load_reporter::kViewEndBytesReceived),
+            {{{kClientIp0 + kLbToken1, kHostname1, kUser1,
+               ::grpc::load_reporter::kCallStatusOk},
+              0},
+             {{kClientIp0 + kLbToken1, kHostname1, kUser2,
+               ::grpc::load_reporter::kCallStatusOk},
+              0},
+             {{kClientIp0 + kLbToken1, kHostname1, kUser1,
+               ::grpc::load_reporter::kCallStatusClientError},
+              0}});
+    auto end_latency_vd = ::opencensus::stats::testing::TestUtils::MakeViewData(
+        mock_census_view_provider()->FindViewDescriptor(
+            ::grpc::load_reporter::kViewEndLatencyMs),
+        {{{kClientIp0 + kLbToken1, kHostname1, kUser1,
+           ::grpc::load_reporter::kCallStatusOk},
+          0},
+         {{kClientIp0 + kLbToken1, kHostname1, kUser2,
+           ::grpc::load_reporter::kCallStatusOk},
+          0},
+         {{kClientIp0 + kLbToken1, kHostname1, kUser1,
+           ::grpc::load_reporter::kCallStatusClientError},
+          0}});
+    view_data_map_list.push_back(
+        {{::grpc::load_reporter::kViewEndCount, end_count_vd},
+         {::grpc::load_reporter::kViewEndBytesSent, end_bytes_sent_vd},
+         {::grpc::load_reporter::kViewEndBytesReceived, end_bytes_received_vd},
+         {::grpc::load_reporter::kViewEndLatencyMs, end_latency_vd}});
+  }
+  {
+    ::testing::InSequence s;
+    for (size_t i = 0; i < view_data_map_list.size(); ++i) {
+      EXPECT_CALL(*mock_census_view_provider(), FetchViewData())
+          .WillOnce(Return(view_data_map_list[i]))
+          .RetiresOnSaturation();
+    }
+  }
+  PrepareCpuExpectation(kNumFeedbackSamplesInWindow + 2);
+  // When the load reporter is created, a trivial LB feedback record is added.
+  // But that's not enough for generating an LB feedback.
+  // Fetch some view data so that non-trivial LB feedback can be generated.
+  for (size_t i = 0; i < kNumFeedbackSamplesInWindow / 2; ++i) {
+    // TODO(juanlishen): Find some fake clock to speed up testing.
+    sleep(1);
+    load_reporter_->FetchAndSample();
+  }
+  VerifyLbFeedback(load_reporter_->GenerateLoadBalancingFeedback(), 0,
+                   kNumFeedbackSamplesInWindow / 2);
+  // Fetch more view data so that the feedback record window is just full (the
+  // initial record just falls out of the window).
+  for (size_t i = 0; i < (kNumFeedbackSamplesInWindow + 1) / 2; ++i) {
+    sleep(1);
+    load_reporter_->FetchAndSample();
+  }
+  VerifyLbFeedback(load_reporter_->GenerateLoadBalancingFeedback(), 0,
+                   kNumFeedbackSamplesInWindow);
+  // Further fetching will cause the old records to fall out of the window.
+  for (size_t i = 0; i < 2; ++i) {
+    sleep(1);
+    load_reporter_->FetchAndSample();
+  }
+  VerifyLbFeedback(load_reporter_->GenerateLoadBalancingFeedback(), 2,
+                   kNumFeedbackSamplesInWindow);
+}
+
+using LoadReportTest = LoadReporterTest;
+
+TEST_F(LoadReportTest, BasicReport) {
+  // Make up the first view data map.
+  CensusViewProvider::ViewDataMap vdm1;
+  vdm1.emplace(
+      ::grpc::load_reporter::kViewStartCount,
+      ::opencensus::stats::testing::TestUtils::MakeViewData(
+          mock_census_view_provider()->FindViewDescriptor(
+              ::grpc::load_reporter::kViewStartCount),
+          {{{kClientIp1 + kLbToken1, kHostname1, kUser1}, 1234},
+           {{kClientIp2 + kLbToken1, kHostname1, kUser1}, 1225},
+           {{kClientIp0 + kLbToken1, kHostname1, kUser1}, 10},
+           {{kClientIp2 + kLbToken1, kHostname1, kUser2}, 464},
+           {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3}, 101},
+           {{kClientIp1 + kLbToken2, kHostname2, kUser3}, 17},
+           {{kClientIp2 + kLbId3 + kLbTag2, kHostname2, kUser3}, 23}}));
+  vdm1.emplace(::grpc::load_reporter::kViewEndCount,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndCount),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     641},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     272},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     996},
+                    {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     34},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     18}}));
+  vdm1.emplace(::grpc::load_reporter::kViewEndBytesSent,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndBytesSent),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     8977},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     266},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     1276},
+                    {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     77823},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     48}}));
+  vdm1.emplace(::grpc::load_reporter::kViewEndBytesReceived,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndBytesReceived),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     2341},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     466},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     518},
+                    {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     81},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     27}}));
+  vdm1.emplace(::grpc::load_reporter::kViewEndLatencyMs,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndLatencyMs),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     3.14},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     5.26},
+                    {{kClientIp2 + kLbToken1, kHostname1, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     45.4},
+                    {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     4.4},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser2,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     2348.0}}));
+  vdm1.emplace(
+      ::grpc::load_reporter::kViewOtherCallMetricCount,
+      ::opencensus::stats::testing::TestUtils::MakeViewData(
+          mock_census_view_provider()->FindViewDescriptor(
+              ::grpc::load_reporter::kViewOtherCallMetricCount),
+          {{{kClientIp1 + kLbToken1, kHostname1, kUser2, kMetric1}, 1},
+           {{kClientIp1 + kLbToken1, kHostname1, kUser2, kMetric1}, 1},
+           {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3, kMetric2},
+            1}}));
+  vdm1.emplace(
+      ::grpc::load_reporter::kViewOtherCallMetricValue,
+      ::opencensus::stats::testing::TestUtils::MakeViewData(
+          mock_census_view_provider()->FindViewDescriptor(
+              ::grpc::load_reporter::kViewOtherCallMetricValue),
+          {{{kClientIp1 + kLbToken1, kHostname1, kUser2, kMetric1}, 1.2},
+           {{kClientIp1 + kLbToken1, kHostname1, kUser2, kMetric1}, 1.2},
+           {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3, kMetric2},
+            3.2}}));
+  // Make up the second view data map.
+  CensusViewProvider::ViewDataMap vdm2;
+  vdm2.emplace(
+      ::grpc::load_reporter::kViewStartCount,
+      ::opencensus::stats::testing::TestUtils::MakeViewData(
+          mock_census_view_provider()->FindViewDescriptor(
+              ::grpc::load_reporter::kViewStartCount),
+          {{{kClientIp2 + kLbToken1, kHostname1, kUser1}, 3},
+           {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3}, 778}}));
+  vdm2.emplace(::grpc::load_reporter::kViewEndCount,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndCount),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     24},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     546}}));
+  vdm2.emplace(::grpc::load_reporter::kViewEndBytesSent,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndBytesSent),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     747},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     229}}));
+  vdm2.emplace(::grpc::load_reporter::kViewEndBytesReceived,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndBytesReceived),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     173},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     438}}));
+  vdm2.emplace(::grpc::load_reporter::kViewEndLatencyMs,
+               ::opencensus::stats::testing::TestUtils::MakeViewData(
+                   mock_census_view_provider()->FindViewDescriptor(
+                       ::grpc::load_reporter::kViewEndLatencyMs),
+                   {{{kClientIp1 + kLbToken1, kHostname1, kUser1,
+                      ::grpc::load_reporter::kCallStatusOk},
+                     187},
+                    {{kClientIp1 + kLbToken2, kHostname2, kUser3,
+                      ::grpc::load_reporter::kCallStatusClientError},
+                     34}}));
+  vdm2.emplace(
+      ::grpc::load_reporter::kViewOtherCallMetricCount,
+      ::opencensus::stats::testing::TestUtils::MakeViewData(
+          mock_census_view_provider()->FindViewDescriptor(
+              ::grpc::load_reporter::kViewOtherCallMetricCount),
+          {{{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3, kMetric1}, 1},
+           {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3, kMetric2},
+            1}}));
+  vdm2.emplace(
+      ::grpc::load_reporter::kViewOtherCallMetricValue,
+      ::opencensus::stats::testing::TestUtils::MakeViewData(
+          mock_census_view_provider()->FindViewDescriptor(
+              ::grpc::load_reporter::kViewOtherCallMetricValue),
+          {{{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3, kMetric1}, 9.6},
+           {{kClientIp1 + kLbId2 + kLbTag1, kHostname2, kUser3, kMetric2},
+            5.7}}));
+  // Set up mock expectation.
+  EXPECT_CALL(*mock_census_view_provider(), FetchViewData())
+      .WillOnce(Return(vdm1))
+      .WillOnce(Return(vdm2));
+  PrepareCpuExpectation(2);
+  // Start testing.
+  load_reporter_->ReportStreamCreated(kHostname1, kLbId1, kLoadKey1);
+  load_reporter_->ReportStreamCreated(kHostname2, kLbId2, kLoadKey2);
+  load_reporter_->ReportStreamCreated(kHostname2, kLbId3, kLoadKey3);
+  // First fetch.
+  load_reporter_->FetchAndSample();
+  load_reporter_->GenerateLoads(kHostname1, kLbId1);
+  gpr_log(GPR_INFO, "First load generated.");
+  // Second fetch.
+  load_reporter_->FetchAndSample();
+  load_reporter_->GenerateLoads(kHostname2, kLbId2);
+  gpr_log(GPR_INFO, "Second load generated.");
+  // TODO(juanlishen): Verify the data.
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}

+ 31 - 10
test/cpp/util/channel_trace_proto_helper.cc

@@ -30,26 +30,47 @@
 namespace grpc {
 namespace testing {
 
-void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str) {
-  std::string tracer_json_str(tracer_json_c_str);
-  grpc::channelz::ChannelTrace channel_trace;
+namespace {
+
+// Generic helper that takes in a json string, converts it to a proto, and
+// then back to json. This ensures that the json string was correctly formatted
+// according to https://developers.google.com/protocol-buffers/docs/proto3#json
+template <typename Message>
+void VaidateProtoJsonTranslation(char* json_c_str) {
+  std::string json_str(json_c_str);
+  Message msg;
   google::protobuf::util::JsonParseOptions parse_options;
   // If the following line is failing, then uncomment the last line of the
   // comment, and uncomment the lines that print the two strings. You can
   // then compare the output, and determine what fields are missing.
   //
-  // options.ignore_unknown_fields = true;
-  ASSERT_EQ(google::protobuf::util::JsonStringToMessage(
-                tracer_json_str, &channel_trace, parse_options),
+  // parse_options.ignore_unknown_fields = true;
+  EXPECT_EQ(google::protobuf::util::JsonStringToMessage(json_str, &msg,
+                                                        parse_options),
             google::protobuf::util::Status::OK);
   std::string proto_json_str;
-  ASSERT_EQ(google::protobuf::util::MessageToJsonString(channel_trace,
-                                                        &proto_json_str),
+  google::protobuf::util::JsonPrintOptions print_options;
+  // We usually do not want this to be true, however it can be helpful to
+  // uncomment and see the output produced then all fields are printed.
+  // print_options.always_print_primitive_fields = true;
+  EXPECT_EQ(google::protobuf::util::MessageToJsonString(msg, &proto_json_str,
+                                                        print_options),
             google::protobuf::util::Status::OK);
   // uncomment these to compare the the json strings.
-  // gpr_log(GPR_ERROR, "tracer json: %s", tracer_json_str.c_str());
+  // gpr_log(GPR_ERROR, "tracer json: %s", json_str.c_str());
   // gpr_log(GPR_ERROR, "proto  json: %s", proto_json_str.c_str());
-  ASSERT_EQ(tracer_json_str, proto_json_str);
+  EXPECT_EQ(json_str, proto_json_str);
+}
+
+}  // namespace
+
+void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str) {
+  VaidateProtoJsonTranslation<grpc::channelz::v1::ChannelTrace>(
+      tracer_json_c_str);
+}
+
+void ValidateChannelProtoJsonTranslation(char* channel_json_c_str) {
+  VaidateProtoJsonTranslation<grpc::channelz::v1::Channel>(channel_json_c_str);
 }
 
 }  // namespace testing

+ 1 - 0
test/cpp/util/channel_trace_proto_helper.h

@@ -23,6 +23,7 @@ namespace grpc {
 namespace testing {
 
 void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str);
+void ValidateChannelProtoJsonTranslation(char* channel_json_c_str);
 
 }  // namespace testing
 }  // namespace grpc

+ 7 - 9
test/distrib/python/test_packages.sh

@@ -28,10 +28,12 @@ else
   echo "Testing Python source distribution"
   ARCHIVES=("$EXTERNAL_GIT_ROOT"/input_artifacts/grpcio-[0-9]*.tar.gz)
   TOOLS_ARCHIVES=("$EXTERNAL_GIT_ROOT"/input_artifacts/grpcio-tools-[0-9]*.tar.gz)
-  HEALTH_ARCHIVES=("$EXTERNAL_GIT_ROOT"/input_artifacts/grpcio-health-checking-[0-9]*.tar.gz)
-  REFLECTION_ARCHIVES=("$EXTERNAL_GIT_ROOT"/input_artifacts/grpcio-reflection-[0-9]*.tar.gz)
 fi
 
+HEALTH_ARCHIVES=("$EXTERNAL_GIT_ROOT"/input_artifacts/grpcio-health-checking-[0-9]*.tar.gz)
+REFLECTION_ARCHIVES=("$EXTERNAL_GIT_ROOT"/input_artifacts/grpcio-reflection-[0-9]*.tar.gz)
+TESTING_ARCHIVES=("$EXTERNAL_GIT_ROOT"/input_artifacts/grpcio-testing-[0-9]*.tar.gz)
+
 VIRTUAL_ENV=$(mktemp -d)
 virtualenv "$VIRTUAL_ENV"
 PYTHON=$VIRTUAL_ENV/bin/python
@@ -53,13 +55,9 @@ function at_least_one_installs() {
 
 at_least_one_installs "${ARCHIVES[@]}"
 at_least_one_installs "${TOOLS_ARCHIVES[@]}"
-
-if [[ "$1" == "source" ]]
-then
-  echo "Testing Python health and reflection packages"
-  at_least_one_installs "${HEALTH_ARCHIVES[@]}"
-  at_least_one_installs "${REFLECTION_ARCHIVES[@]}"
-fi
+at_least_one_installs "${HEALTH_ARCHIVES[@]}"
+at_least_one_installs "${REFLECTION_ARCHIVES[@]}"
+at_least_one_installs "${TESTING_ARCHIVES[@]}"
 
 
 #

+ 1 - 0
tools/doxygen/Doxyfile.c++.internal

@@ -1012,6 +1012,7 @@ src/core/lib/channel/channel_args.h \
 src/core/lib/channel/channel_stack.h \
 src/core/lib/channel/channel_stack_builder.h \
 src/core/lib/channel/channel_trace.h \
+src/core/lib/channel/channelz.h \
 src/core/lib/channel/channelz_registry.h \
 src/core/lib/channel/connected_channel.h \
 src/core/lib/channel/context.h \

+ 2 - 0
tools/doxygen/Doxyfile.core.internal

@@ -1038,6 +1038,8 @@ src/core/lib/channel/channel_stack_builder.cc \
 src/core/lib/channel/channel_stack_builder.h \
 src/core/lib/channel/channel_trace.cc \
 src/core/lib/channel/channel_trace.h \
+src/core/lib/channel/channelz.cc \
+src/core/lib/channel/channelz.h \
 src/core/lib/channel/channelz_registry.cc \
 src/core/lib/channel/channelz_registry.h \
 src/core/lib/channel/connected_channel.cc \

+ 3 - 0
tools/interop_matrix/client_matrix.py

@@ -172,6 +172,9 @@ LANG_RELEASE_MATRIX = {
         {
             'v1.12.0': None
         },
+        {
+            'v1.13.1': None
+        },
     ],
     'python': [
         {

+ 6 - 2
tools/run_tests/artifacts/build_artifact_python.sh

@@ -99,12 +99,16 @@ then
   "${PIP}" install grpcio --no-index --find-links "file://$ARTIFACT_DIR/"
   "${PIP}" install grpcio-tools --no-index --find-links "file://$ARTIFACT_DIR/"
 
-  # Build gRPC health-checking source distribution
+  # Build grpcio_testing source distribution
+  ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_testing/setup.py sdist
+  cp -r src/python/grpcio_testing/dist/* "$ARTIFACT_DIR"
+
+  # Build grpcio_health_checking source distribution
   ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_health_checking/setup.py \
       preprocess build_package_protos sdist
   cp -r src/python/grpcio_health_checking/dist/* "$ARTIFACT_DIR"
 
-  # Build gRPC reflection source distribution
+  # Build grpcio_reflection source distribution
   ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_reflection/setup.py \
       preprocess build_package_protos sdist
   cp -r src/python/grpcio_reflection/dist/* "$ARTIFACT_DIR"

+ 48 - 37
tools/run_tests/generated/sources_and_headers.json

@@ -2757,6 +2757,28 @@
     "third_party": false, 
     "type": "target"
   }, 
+  {
+    "deps": [
+      "benchmark", 
+      "gpr", 
+      "gpr_test_util", 
+      "grpc++_test_config", 
+      "grpc++_test_util_unsecure", 
+      "grpc++_unsecure", 
+      "grpc_benchmark", 
+      "grpc_test_util_unsecure", 
+      "grpc_unsecure"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "bm_channel", 
+    "src": [
+      "test/cpp/microbenchmarks/bm_channel.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
   {
     "deps": [
       "benchmark", 
@@ -3120,6 +3142,27 @@
     "third_party": false, 
     "type": "target"
   }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_channelz_proto", 
+      "grpc++_test", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "channelz_test", 
+    "src": [
+      "test/core/channel/channelz_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
   {
     "deps": [
       "gpr", 
@@ -3972,26 +4015,6 @@
     "third_party": false, 
     "type": "target"
   }, 
-  {
-    "deps": [
-      "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc++", 
-      "grpc++_test_util", 
-      "grpc_test_util", 
-      "lb_load_data_store"
-    ], 
-    "headers": [], 
-    "is_filegroup": false, 
-    "language": "c++", 
-    "name": "lb_load_data_store_test", 
-    "src": [
-      "test/cpp/server/load_reporter/load_data_store_test.cc"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
   {
     "deps": [
       "gpr", 
@@ -7634,23 +7657,6 @@
     "third_party": false, 
     "type": "lib"
   }, 
-  {
-    "deps": [
-      "grpc++"
-    ], 
-    "headers": [
-      "src/cpp/server/load_reporter/load_data_store.h"
-    ], 
-    "is_filegroup": false, 
-    "language": "c++", 
-    "name": "lb_load_data_store", 
-    "src": [
-      "src/cpp/server/load_reporter/load_data_store.cc", 
-      "src/cpp/server/load_reporter/load_data_store.h"
-    ], 
-    "third_party": false, 
-    "type": "lib"
-  }, 
   {
     "deps": [
       "grpc", 
@@ -8760,6 +8766,7 @@
       "test/core/end2end/tests/cancel_in_a_vacuum.cc", 
       "test/core/end2end/tests/cancel_test_helpers.h", 
       "test/core/end2end/tests/cancel_with_status.cc", 
+      "test/core/end2end/tests/channelz.cc", 
       "test/core/end2end/tests/compressed_payload.cc", 
       "test/core/end2end/tests/connectivity.cc", 
       "test/core/end2end/tests/default_host.cc", 
@@ -8860,6 +8867,7 @@
       "test/core/end2end/tests/cancel_in_a_vacuum.cc", 
       "test/core/end2end/tests/cancel_test_helpers.h", 
       "test/core/end2end/tests/cancel_with_status.cc", 
+      "test/core/end2end/tests/channelz.cc", 
       "test/core/end2end/tests/compressed_payload.cc", 
       "test/core/end2end/tests/connectivity.cc", 
       "test/core/end2end/tests/default_host.cc", 
@@ -9332,6 +9340,7 @@
       "src/core/lib/channel/channel_stack.cc", 
       "src/core/lib/channel/channel_stack_builder.cc", 
       "src/core/lib/channel/channel_trace.cc", 
+      "src/core/lib/channel/channelz.cc", 
       "src/core/lib/channel/channelz_registry.cc", 
       "src/core/lib/channel/connected_channel.cc", 
       "src/core/lib/channel/handshaker.cc", 
@@ -9503,6 +9512,7 @@
       "src/core/lib/channel/channel_stack.h", 
       "src/core/lib/channel/channel_stack_builder.h", 
       "src/core/lib/channel/channel_trace.h", 
+      "src/core/lib/channel/channelz.h", 
       "src/core/lib/channel/channelz_registry.h", 
       "src/core/lib/channel/connected_channel.h", 
       "src/core/lib/channel/context.h", 
@@ -9652,6 +9662,7 @@
       "src/core/lib/channel/channel_stack.h", 
       "src/core/lib/channel/channel_stack_builder.h", 
       "src/core/lib/channel/channel_trace.h", 
+      "src/core/lib/channel/channelz.h", 
       "src/core/lib/channel/channelz_registry.h", 
       "src/core/lib/channel/connected_channel.h", 
       "src/core/lib/channel/context.h", 

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 591 - 59
tools/run_tests/generated/tests.json


+ 11 - 11
tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh

@@ -19,15 +19,15 @@ set -ex
 cd "$(dirname "$0")/../../.."
 
 EXIT_CODE=0
-ruby src/ruby/end2end/sig_handling_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/channel_state_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/channel_closing_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/sig_int_during_channel_watch_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/killed_client_thread_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/forking_client_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/grpc_class_init_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/multiple_killed_watching_threads_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/load_grpc_with_gc_stress_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/client_memory_usage_driver.rb || EXIT_CODE=1
-ruby src/ruby/end2end/package_with_underscore_checker.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/sig_handling_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/channel_state_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/channel_closing_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/sig_int_during_channel_watch_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/killed_client_thread_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/forking_client_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/grpc_class_init_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/multiple_killed_watching_threads_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/load_grpc_with_gc_stress_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/client_memory_usage_driver.rb || EXIT_CODE=1
+time ruby src/ruby/end2end/package_with_underscore_checker.rb || EXIT_CODE=1
 exit $EXIT_CODE

+ 1 - 1
tools/run_tests/python_utils/filter_pull_request_tests.py

@@ -96,7 +96,7 @@ _WHITELIST_DICT = {
     'gRPC\-Core\.podspec$': [_OBJC_TEST_SUITE],
     'gRPC\-ProtoRPC\.podspec$': [_OBJC_TEST_SUITE],
     'gRPC\-RxLibrary\.podspec$': [_OBJC_TEST_SUITE],
-    'INSTALL\.md$': [],
+    'BUILDING\.md$': [],
     'LICENSE$': [],
     'MANIFEST\.md$': [],
     'package\.json$': [_PHP_TEST_SUITE],

+ 1 - 1
tools/run_tests/python_utils/upload_rbe_results.py

@@ -161,7 +161,7 @@ if __name__ == "__main__":
             test_cases = action['testAction']['testSuite']['tests'][0][
                 'testSuite']['tests']
         for test_case in test_cases:
-            if 'errors' in test_case['testCase']:
+            if any(s in test_case['testCase'] for s in ['errors', 'failures']):
                 result = 'FAILED'
             elif 'timedOut' in test_case['testCase']:
                 result = 'TIMEOUT'

+ 1 - 1
tools/run_tests/run_tests.py

@@ -883,7 +883,7 @@ class RubyLanguage(object):
         tests.append(
             self.config.job_spec(
                 ['tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh'],
-                timeout_seconds=10 * 60,
+                timeout_seconds=20 * 60,
                 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
         return tests
 

+ 0 - 12
vsprojects/.gitignore

@@ -1,12 +0,0 @@
-Debug
-Debug-DLL
-Release
-Release-DLL
-*.suo
-*.user
-test_bin
-*.opensdf
-*.sdf
-third_party/*.user
-/packages
-/IntDir

+ 0 - 11
vsprojects/README.md

@@ -1,11 +0,0 @@
-# Pre-generated MS Visual Studio project & solution files: DELETED
-
-**The pre-generated MS Visual Studio project & solution files are no longer available, please use cmake instead (it can generate Visual Studio projects for you).**
-
-**Pre-generated MS Visual Studio projects used to be the recommended way to build on Windows, but there were some limitations:**
-- **hard to build dependencies, expecially boringssl (deps usually support cmake quite well)**
-- **the nuget-based openssl & zlib dependencies are hard to maintain and update. We've received issues indicating that they are flawed.**
-- **.proto codegen is hard to support in Visual Studio directly (but we have a pretty decent support in cmake)**
-- **It's a LOT of generated files. We prefer not to have too much generated code in our github repo.**
-
-See [INSTALL.md](/INSTALL.md) for detailed instructions how to build using cmake on Windows.

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно