Merge branch 'stats_histo_ints' into stats_histo
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ecfc8c8..0deab99 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -756,6 +756,7 @@
 add_dependencies(buildtests_cxx server_crash_test_client)
 add_dependencies(buildtests_cxx server_request_call_test)
 add_dependencies(buildtests_cxx shutdown_test)
+add_dependencies(buildtests_cxx stats_test)
 add_dependencies(buildtests_cxx status_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_cxx streaming_throughput_test)
@@ -12754,6 +12755,47 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(stats_test
+  test/core/debug/stats_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+
+target_include_directories(stats_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${BENCHMARK_ROOT_DIR}/include
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+  PRIVATE ${CARES_BUILD_INCLUDE_DIR}
+  PRIVATE ${CARES_INCLUDE_DIR}
+  PRIVATE ${CARES_PLATFORM_INCLUDE_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+  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(stats_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc++_test_util
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+  ${_gRPC_GFLAGS_LIBRARIES}
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(status_test
   test/cpp/util/status_test.cc
   third_party/googletest/googletest/src/gtest-all.cc
diff --git a/Makefile b/Makefile
index 78451e2..8639c31 100644
--- a/Makefile
+++ b/Makefile
@@ -1170,6 +1170,7 @@
 server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client
 server_request_call_test: $(BINDIR)/$(CONFIG)/server_request_call_test
 shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test
+stats_test: $(BINDIR)/$(CONFIG)/stats_test
 status_test: $(BINDIR)/$(CONFIG)/status_test
 streaming_throughput_test: $(BINDIR)/$(CONFIG)/streaming_throughput_test
 stress_test: $(BINDIR)/$(CONFIG)/stress_test
@@ -1600,6 +1601,7 @@
   $(BINDIR)/$(CONFIG)/server_crash_test_client \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
+  $(BINDIR)/$(CONFIG)/stats_test \
   $(BINDIR)/$(CONFIG)/status_test \
   $(BINDIR)/$(CONFIG)/streaming_throughput_test \
   $(BINDIR)/$(CONFIG)/stress_test \
@@ -1715,6 +1717,7 @@
   $(BINDIR)/$(CONFIG)/server_crash_test_client \
   $(BINDIR)/$(CONFIG)/server_request_call_test \
   $(BINDIR)/$(CONFIG)/shutdown_test \
+  $(BINDIR)/$(CONFIG)/stats_test \
   $(BINDIR)/$(CONFIG)/status_test \
   $(BINDIR)/$(CONFIG)/streaming_throughput_test \
   $(BINDIR)/$(CONFIG)/stress_test \
@@ -2114,6 +2117,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/server_request_call_test || ( echo test server_request_call_test failed ; exit 1 )
 	$(E) "[RUN]     Testing shutdown_test"
 	$(Q) $(BINDIR)/$(CONFIG)/shutdown_test || ( echo test shutdown_test failed ; exit 1 )
+	$(E) "[RUN]     Testing stats_test"
+	$(Q) $(BINDIR)/$(CONFIG)/stats_test || ( echo test stats_test failed ; exit 1 )
 	$(E) "[RUN]     Testing status_test"
 	$(Q) $(BINDIR)/$(CONFIG)/status_test || ( echo test status_test failed ; exit 1 )
 	$(E) "[RUN]     Testing streaming_throughput_test"
@@ -16699,6 +16704,49 @@
 endif
 
 
+STATS_TEST_SRC = \
+    test/core/debug/stats_test.cc \
+
+STATS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STATS_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/stats_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/stats_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/stats_test: $(PROTOBUF_DEP) $(STATS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(STATS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/stats_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/debug/stats_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_stats_test: $(STATS_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(STATS_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 STATUS_TEST_SRC = \
     test/cpp/util/status_test.cc \
 
diff --git a/build.yaml b/build.yaml
index 00d3429..4539896 100644
--- a/build.yaml
+++ b/build.yaml
@@ -4529,6 +4529,18 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: stats_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/core/debug/stats_test.cc
+  deps:
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: status_test
   build: test
   language: c++
diff --git a/src/core/lib/debug/stats_data.c b/src/core/lib/debug/stats_data.c
index 47f482b..8ebfb23 100644
--- a/src/core/lib/debug/stats_data.c
+++ b/src/core/lib/debug/stats_data.c
@@ -19,6 +19,8 @@
  */
 
 #include "src/core/lib/debug/stats_data.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
 const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT] = {
     "client_calls_created",
     "server_calls_created",
@@ -60,11 +62,11 @@
     388125,  498910,  641316,  824370,  1059674, 1362141,  1750943,  2250722,
     2893155, 3718960, 4780478, 6144988, 7898976, 10153611, 13051794, 16777216};
 const uint8_t grpc_stats_table_1[87] = {
-    1,  1,  3,  3,  4,  6,  6,  8,  8,  9,  11, 11, 12, 14, 14, 15, 17, 17,
-    18, 20, 20, 21, 23, 23, 24, 25, 27, 27, 28, 30, 30, 31, 33, 33, 34, 35,
-    37, 37, 39, 39, 40, 41, 43, 43, 44, 46, 46, 47, 48, 50, 50, 51, 53, 53,
-    55, 55, 56, 57, 59, 59, 60, 62, 62, 63, 64, 66, 66, 67, 69, 69, 71, 71,
-    72, 73, 75, 75, 76, 78, 78, 79, 80, 82, 82, 83, 85, 85, 87};
+    0,  0,  1,  1,  2,  3,  3,  4,  4,  5,  6,  6,  7,  8,  8,  9,  10, 10,
+    11, 12, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 19, 20, 21, 21, 22, 23,
+    24, 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 35,
+    36, 36, 37, 38, 39, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46, 47, 47,
+    48, 49, 50, 50, 51, 52, 52, 53, 54, 55, 55, 56, 57, 57, 58};
 const int grpc_stats_table_2[64] = {
     0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  13,
     15,  17,  19,  21,  23,  25,  28,  31,  34,  37,  41,  45,  49,
@@ -72,14 +74,160 @@
     159, 172, 186, 201, 218, 236, 255, 276, 299, 323, 349, 377, 408,
     441, 477, 515, 556, 601, 649, 701, 757, 817, 881, 950, 1024};
 const uint8_t grpc_stats_table_3[104] = {
-    2,  2,  2,  6,  6,  6,  6,  9,  9,  9,  11,  11,  13,  13, 15, 15, 17, 17,
-    20, 20, 20, 23, 23, 23, 25, 25, 26, 28, 28,  30,  30,  32, 32, 35, 35, 35,
-    37, 37, 40, 40, 40, 41, 43, 43, 44, 46, 46,  48,  48,  50, 50, 52, 52, 55,
-    55, 55, 57, 57, 58, 59, 61, 61, 63, 63, 65,  65,  67,  67, 69, 69, 71, 71,
-    73, 73, 74, 76, 76, 77, 79, 79, 81, 81, 83,  83,  85,  85, 88, 88, 88, 89,
-    90, 92, 92, 93, 95, 95, 97, 97, 99, 99, 101, 101, 104, 104};
+    0,  0,  0,  1,  1,  1,  1,  2,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,
+    7,  7,  7,  8,  8,  8,  9,  9,  10, 11, 11, 12, 12, 13, 13, 14, 14, 14,
+    15, 15, 16, 16, 16, 17, 18, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24,
+    24, 24, 25, 25, 26, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33,
+    34, 34, 35, 36, 36, 37, 38, 38, 39, 39, 40, 40, 41, 41, 42, 42, 42, 43,
+    44, 45, 45, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51};
+void grpc_stats_inc_tcp_write_size(grpc_exec_ctx *exec_ctx, double value) {
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val;
+  _val.dbl = value;
+  if (_val.dbl < 0) _val.dbl = 0;
+  if (_val.dbl < 5.000000) {
+    GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,
+                             (int)_val.dbl);
+  } else {
+    if (_val.uint < 4682617712558473216ull) {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,
+          grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)] + 4);
+    } else {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,
+          grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,
+                                            grpc_stats_table_0, 64));
+    }
+  }
+}
+void grpc_stats_inc_tcp_write_iov_size(grpc_exec_ctx *exec_ctx, double value) {
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val;
+  _val.dbl = value;
+  if (_val.dbl < 0) _val.dbl = 0;
+  if (_val.dbl < 12.000000) {
+    GRPC_STATS_INC_HISTOGRAM(
+        (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, (int)_val.dbl);
+  } else {
+    if (_val.uint < 4637300241308057600ull) {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE,
+          grpc_stats_table_3[((_val.uint - 4622945017495814144ull) >> 48)] +
+              11);
+    } else {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE,
+          grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,
+                                            grpc_stats_table_2, 64));
+    }
+  }
+}
+void grpc_stats_inc_tcp_read_size(grpc_exec_ctx *exec_ctx, double value) {
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val;
+  _val.dbl = value;
+  if (_val.dbl < 0) _val.dbl = 0;
+  if (_val.dbl < 5.000000) {
+    GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,
+                             (int)_val.dbl);
+  } else {
+    if (_val.uint < 4682617712558473216ull) {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,
+          grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)] + 4);
+    } else {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,
+          grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,
+                                            grpc_stats_table_0, 64));
+    }
+  }
+}
+void grpc_stats_inc_tcp_read_offer(grpc_exec_ctx *exec_ctx, double value) {
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val;
+  _val.dbl = value;
+  if (_val.dbl < 0) _val.dbl = 0;
+  if (_val.dbl < 5.000000) {
+    GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,
+                             (int)_val.dbl);
+  } else {
+    if (_val.uint < 4682617712558473216ull) {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,
+          grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)] + 4);
+    } else {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,
+          grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,
+                                            grpc_stats_table_0, 64));
+    }
+  }
+}
+void grpc_stats_inc_tcp_read_iov_size(grpc_exec_ctx *exec_ctx, double value) {
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val;
+  _val.dbl = value;
+  if (_val.dbl < 0) _val.dbl = 0;
+  if (_val.dbl < 12.000000) {
+    GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_IOV_SIZE,
+                             (int)_val.dbl);
+  } else {
+    if (_val.uint < 4637300241308057600ull) {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_IOV_SIZE,
+          grpc_stats_table_3[((_val.uint - 4622945017495814144ull) >> 48)] +
+              11);
+    } else {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_IOV_SIZE,
+          grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,
+                                            grpc_stats_table_2, 64));
+    }
+  }
+}
+void grpc_stats_inc_http2_send_message_size(grpc_exec_ctx *exec_ctx,
+                                            double value) {
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val;
+  _val.dbl = value;
+  if (_val.dbl < 0) _val.dbl = 0;
+  if (_val.dbl < 5.000000) {
+    GRPC_STATS_INC_HISTOGRAM((exec_ctx),
+                             GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,
+                             (int)_val.dbl);
+  } else {
+    if (_val.uint < 4682617712558473216ull) {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,
+          grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)] + 4);
+    } else {
+      GRPC_STATS_INC_HISTOGRAM(
+          (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,
+          grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,
+                                            grpc_stats_table_0, 64));
+    }
+  }
+}
 const int grpc_stats_histo_buckets[6] = {64, 64, 64, 64, 64, 64};
 const int grpc_stats_histo_start[6] = {0, 64, 128, 192, 256, 320};
 const int *const grpc_stats_histo_bucket_boundaries[6] = {
     grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_0,
     grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_0};
+void (*const grpc_stats_inc_histogram[6])(grpc_exec_ctx *exec_ctx, double x) = {
+    grpc_stats_inc_tcp_write_size,    grpc_stats_inc_tcp_write_iov_size,
+    grpc_stats_inc_tcp_read_size,     grpc_stats_inc_tcp_read_offer,
+    grpc_stats_inc_tcp_read_iov_size, grpc_stats_inc_http2_send_message_size};
diff --git a/src/core/lib/debug/stats_data.h b/src/core/lib/debug/stats_data.h
index 5350264..13bf9d0 100644
--- a/src/core/lib/debug/stats_data.h
+++ b/src/core/lib/debug/stats_data.h
@@ -22,6 +22,7 @@
 #define GRPC_CORE_LIB_DEBUG_STATS_DATA_H
 
 #include <inttypes.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
 
 typedef enum {
   GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED,
@@ -138,157 +139,29 @@
                          GRPC_STATS_COUNTER_EXECUTOR_WAKEUP_INITIATED)
 #define GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED(exec_ctx) \
   GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_EXECUTOR_QUEUE_DRAINED)
-#define GRPC_STATS_INC_TCP_WRITE_SIZE(exec_ctx, value)                         \
-  do {                                                                         \
-    union {                                                                    \
-      double dbl;                                                              \
-      uint64_t uint;                                                           \
-    } _val;                                                                    \
-    _val.dbl = (double)(value);                                                \
-    if (_val.dbl < 0) _val.dbl = 0;                                            \
-    if (_val.dbl < 5.000000) {                                                 \
-      GRPC_STATS_INC_HISTOGRAM(                                                \
-          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, (int)_val.dbl);     \
-    } else {                                                                   \
-      if (_val.uint < 4715268809856909312ull) {                                \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,                   \
-            grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)]); \
-      } else {                                                                 \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,                   \
-            grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,            \
-                                              grpc_stats_table_0, 64));        \
-      }                                                                        \
-    }                                                                          \
-  } while (false)
-#define GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(exec_ctx, value)                     \
-  do {                                                                         \
-    union {                                                                    \
-      double dbl;                                                              \
-      uint64_t uint;                                                           \
-    } _val;                                                                    \
-    _val.dbl = (double)(value);                                                \
-    if (_val.dbl < 0) _val.dbl = 0;                                            \
-    if (_val.dbl < 12.000000) {                                                \
-      GRPC_STATS_INC_HISTOGRAM(                                                \
-          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, (int)_val.dbl); \
-    } else {                                                                   \
-      if (_val.uint < 4652218415073722368ull) {                                \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE,               \
-            grpc_stats_table_3[((_val.uint - 4622945017495814144ull) >> 48)]); \
-      } else {                                                                 \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE,               \
-            grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,            \
-                                              grpc_stats_table_2, 64));        \
-      }                                                                        \
-    }                                                                          \
-  } while (false)
-#define GRPC_STATS_INC_TCP_READ_SIZE(exec_ctx, value)                          \
-  do {                                                                         \
-    union {                                                                    \
-      double dbl;                                                              \
-      uint64_t uint;                                                           \
-    } _val;                                                                    \
-    _val.dbl = (double)(value);                                                \
-    if (_val.dbl < 0) _val.dbl = 0;                                            \
-    if (_val.dbl < 5.000000) {                                                 \
-      GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, \
-                               (int)_val.dbl);                                 \
-    } else {                                                                   \
-      if (_val.uint < 4715268809856909312ull) {                                \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,                    \
-            grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)]); \
-      } else {                                                                 \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,                    \
-            grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,            \
-                                              grpc_stats_table_0, 64));        \
-      }                                                                        \
-    }                                                                          \
-  } while (false)
-#define GRPC_STATS_INC_TCP_READ_OFFER(exec_ctx, value)                         \
-  do {                                                                         \
-    union {                                                                    \
-      double dbl;                                                              \
-      uint64_t uint;                                                           \
-    } _val;                                                                    \
-    _val.dbl = (double)(value);                                                \
-    if (_val.dbl < 0) _val.dbl = 0;                                            \
-    if (_val.dbl < 5.000000) {                                                 \
-      GRPC_STATS_INC_HISTOGRAM(                                                \
-          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, (int)_val.dbl);     \
-    } else {                                                                   \
-      if (_val.uint < 4715268809856909312ull) {                                \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,                   \
-            grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)]); \
-      } else {                                                                 \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,                   \
-            grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,            \
-                                              grpc_stats_table_0, 64));        \
-      }                                                                        \
-    }                                                                          \
-  } while (false)
-#define GRPC_STATS_INC_TCP_READ_IOV_SIZE(exec_ctx, value)                      \
-  do {                                                                         \
-    union {                                                                    \
-      double dbl;                                                              \
-      uint64_t uint;                                                           \
-    } _val;                                                                    \
-    _val.dbl = (double)(value);                                                \
-    if (_val.dbl < 0) _val.dbl = 0;                                            \
-    if (_val.dbl < 12.000000) {                                                \
-      GRPC_STATS_INC_HISTOGRAM(                                                \
-          (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_IOV_SIZE, (int)_val.dbl);  \
-    } else {                                                                   \
-      if (_val.uint < 4652218415073722368ull) {                                \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_IOV_SIZE,                \
-            grpc_stats_table_3[((_val.uint - 4622945017495814144ull) >> 48)]); \
-      } else {                                                                 \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_IOV_SIZE,                \
-            grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,            \
-                                              grpc_stats_table_2, 64));        \
-      }                                                                        \
-    }                                                                          \
-  } while (false)
-#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE(exec_ctx, value)                \
-  do {                                                                         \
-    union {                                                                    \
-      double dbl;                                                              \
-      uint64_t uint;                                                           \
-    } _val;                                                                    \
-    _val.dbl = (double)(value);                                                \
-    if (_val.dbl < 0) _val.dbl = 0;                                            \
-    if (_val.dbl < 5.000000) {                                                 \
-      GRPC_STATS_INC_HISTOGRAM((exec_ctx),                                     \
-                               GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,   \
-                               (int)_val.dbl);                                 \
-    } else {                                                                   \
-      if (_val.uint < 4715268809856909312ull) {                                \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,          \
-            grpc_stats_table_1[((_val.uint - 4617315517961601024ull) >> 50)]); \
-      } else {                                                                 \
-        GRPC_STATS_INC_HISTOGRAM(                                              \
-            (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,          \
-            grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl,            \
-                                              grpc_stats_table_0, 64));        \
-      }                                                                        \
-    }                                                                          \
-  } while (false)
-extern const int grpc_stats_table_0[64];
-extern const uint8_t grpc_stats_table_1[87];
-extern const int grpc_stats_table_2[64];
-extern const uint8_t grpc_stats_table_3[104];
+#define GRPC_STATS_INC_TCP_WRITE_SIZE(exec_ctx, value) \
+  grpc_stats_inc_tcp_write_size((exec_ctx), (int)(value))
+void grpc_stats_inc_tcp_write_size(grpc_exec_ctx *exec_ctx, int x);
+#define GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(exec_ctx, value) \
+  grpc_stats_inc_tcp_write_iov_size((exec_ctx), (int)(value))
+void grpc_stats_inc_tcp_write_iov_size(grpc_exec_ctx *exec_ctx, int x);
+#define GRPC_STATS_INC_TCP_READ_SIZE(exec_ctx, value) \
+  grpc_stats_inc_tcp_read_size((exec_ctx), (int)(value))
+void grpc_stats_inc_tcp_read_size(grpc_exec_ctx *exec_ctx, int x);
+#define GRPC_STATS_INC_TCP_READ_OFFER(exec_ctx, value) \
+  grpc_stats_inc_tcp_read_offer((exec_ctx), (int)(value))
+void grpc_stats_inc_tcp_read_offer(grpc_exec_ctx *exec_ctx, int x);
+#define GRPC_STATS_INC_TCP_READ_IOV_SIZE(exec_ctx, value) \
+  grpc_stats_inc_tcp_read_iov_size((exec_ctx), (int)(value))
+void grpc_stats_inc_tcp_read_iov_size(grpc_exec_ctx *exec_ctx, int x);
+#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE(exec_ctx, value) \
+  grpc_stats_inc_http2_send_message_size((exec_ctx), (int)(value))
+void grpc_stats_inc_http2_send_message_size(grpc_exec_ctx *exec_ctx, int x);
 extern const int grpc_stats_histo_buckets[6];
 extern const int grpc_stats_histo_start[6];
 extern const int *const grpc_stats_histo_bucket_boundaries[6];
+extern const double *const grpc_stats_histo_bucket_boundaries[6];
+extern void (*const grpc_stats_inc_histogram[6])(grpc_exec_ctx *exec_ctx,
+                                                 int x);
 
 #endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */
diff --git a/test/core/debug/stats_test.cc b/test/core/debug/stats_test.cc
new file mode 100644
index 0000000..65ccc7a
--- /dev/null
+++ b/test/core/debug/stats_test.cc
@@ -0,0 +1,127 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+extern "C" {
+#include "src/core/lib/debug/stats.h"
+}
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+
+class Snapshot {
+ public:
+  Snapshot() { grpc_stats_collect(&begin_); }
+
+  grpc_stats_data delta() {
+    grpc_stats_data now;
+    grpc_stats_collect(&now);
+    grpc_stats_data delta;
+    grpc_stats_diff(&now, &begin_, &delta);
+    return delta;
+  }
+
+ private:
+  grpc_stats_data begin_;
+};
+
+TEST(StatsTest, IncCounters) {
+  for (int i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
+    Snapshot snapshot;
+
+    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    GRPC_STATS_INC_COUNTER(&exec_ctx, (grpc_stats_counters)i);
+    grpc_exec_ctx_finish(&exec_ctx);
+
+    EXPECT_EQ(snapshot.delta().counters[i], 1);
+  }
+}
+
+TEST(StatsTest, IncSpecificCounter) {
+  Snapshot snapshot;
+
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  GRPC_STATS_INC_SYSCALL_POLL(&exec_ctx);
+  grpc_exec_ctx_finish(&exec_ctx);
+
+  EXPECT_EQ(snapshot.delta().counters[GRPC_STATS_COUNTER_SYSCALL_POLL], 1);
+}
+
+static int FindExpectedBucket(int i, int j) {
+  if (j < 0) {
+    return 0;
+  }
+  if (j >=
+      grpc_stats_histo_bucket_boundaries[i][grpc_stats_histo_buckets[i] - 1]) {
+    return grpc_stats_histo_buckets[i] - 1;
+  }
+  int r = 0;
+  while (grpc_stats_histo_bucket_boundaries[i][r + 1] <= j) r++;
+  return r;
+}
+
+static int FindNonZeroBucket(const grpc_stats_data& data, int i) {
+  for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
+    if (data.histograms[grpc_stats_histo_start[i] + j] != 0) {
+      return j;
+    }
+  }
+  return -1;
+}
+
+TEST(StatsTest, IncHistogram) {
+  for (int i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) {
+    for (int j = -1000;
+         j <
+         grpc_stats_histo_bucket_boundaries[i]
+                                           [grpc_stats_histo_buckets[i] - 1] +
+             1000;
+         j++) {
+      gpr_log(GPR_DEBUG, "histo:%d value:%d", i, j);
+
+      Snapshot snapshot;
+
+      int expected_bucket = FindExpectedBucket(i, j);
+
+      grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+      grpc_stats_inc_histogram[i](&exec_ctx, j);
+      grpc_exec_ctx_finish(&exec_ctx);
+
+      auto delta = snapshot.delta();
+      int got_bucket = FindNonZeroBucket(delta, i);
+
+      EXPECT_EQ(expected_bucket, got_bucket);
+      EXPECT_EQ(delta.histograms[grpc_stats_histo_start[i] + expected_bucket],
+                1);
+    }
+  }
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  grpc_init();
+  int ret = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return ret;
+}
diff --git a/tools/codegen/core/gen_stats_data.py b/tools/codegen/core/gen_stats_data.py
index f33f07c..857c99d 100755
--- a/tools/codegen/core/gen_stats_data.py
+++ b/tools/codegen/core/gen_stats_data.py
@@ -72,11 +72,13 @@
 def gen_map_table(mapped_bounds, shift_data):
   tbl = []
   cur = 0
+  print mapped_bounds
   mapped_bounds = [x >> shift_data[0] for x in mapped_bounds]
+  print mapped_bounds
   for i in range(0, mapped_bounds[shift_data[1]-1]):
     while i > mapped_bounds[cur]:
       cur += 1
-    tbl.append(mapped_bounds[cur])
+    tbl.append(cur)
   return tbl
 
 static_tables = []
@@ -126,34 +128,32 @@
     first_nontrivial_code = dbl2u64(first_nontrivial)
     code_bounds = [dbl2u64(x) - first_nontrivial_code for x in bounds]
     shift_data = find_ideal_shift(code_bounds[first_nontrivial:], 256 * histogram.buckets)
-  print first_nontrivial, shift_data, bounds
-  if shift_data is not None: print [hex(x >> shift_data[0]) for x in code_bounds[first_nontrivial:]]
-  code = 'do {\\\n'
-  code += '  union { double dbl; uint64_t uint; } _val;\\\n'
-  code += '_val.dbl = (double)(value);\\\n'
-  code += 'if (_val.dbl < 0) _val.dbl = 0;\\\n'
+  #print first_nontrivial, shift_data, bounds
+  #if shift_data is not None: print [hex(x >> shift_data[0]) for x in code_bounds[first_nontrivial:]]
+  code = '  union { double dbl; uint64_t uint; } _val;\n'
+  code += '_val.dbl = value;\n'
+  code += 'if (_val.dbl < 0) _val.dbl = 0;\n'
   map_table = gen_map_table(code_bounds[first_nontrivial:], shift_data)
   if first_nontrivial is None:
-    code += ('GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, (int)_val.dbl);\\\n'
+    code += ('GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, (int)_val.dbl);\n'
              % histogram.name.upper())
   else:
-    code += 'if (_val.dbl < %f) {\\\n' % first_nontrivial
-    code += ('GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, (int)_val.dbl);\\\n'
+    code += 'if (_val.dbl < %f) {\n' % first_nontrivial
+    code += ('GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, (int)_val.dbl);\n'
              % histogram.name.upper())
     code += '} else {'
     first_nontrivial_code = dbl2u64(first_nontrivial)
     if shift_data is not None:
       map_table_idx = decl_static_table(map_table, type_for_uint_table(map_table))
-      code += 'if (_val.uint < %dull) {\\\n' % ((map_table[-1] << shift_data[0]) + first_nontrivial_code)
+      code += 'if (_val.uint < %dull) {\n' % ((map_table[-1] << shift_data[0]) + first_nontrivial_code)
       code += 'GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, ' % histogram.name.upper()
-      code += 'grpc_stats_table_%d[((_val.uint - %dull) >> %d)]);\\\n' % (map_table_idx, first_nontrivial_code, shift_data[0])
-      code += '} else {\\\n'
+      code += 'grpc_stats_table_%d[((_val.uint - %dull) >> %d)] + %d);\n' % (map_table_idx, first_nontrivial_code, shift_data[0], first_nontrivial-1)
+      code += '} else {\n'
     code += 'GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, '% histogram.name.upper()
-    code += 'grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl, grpc_stats_table_%d, %d));\\\n' % (bounds_idx, len(bounds))
+    code += 'grpc_stats_histo_find_bucket_slow((exec_ctx), _val.dbl, grpc_stats_table_%d, %d));\n' % (bounds_idx, len(bounds))
     if shift_data is not None:
       code += '}'
     code += '}'
-  code += '} while (false)'
   return (code, bounds_idx)
 
 # utility: print a big comment block into a set of files
@@ -187,6 +187,7 @@
   print >>H, "#define GRPC_CORE_LIB_DEBUG_STATS_DATA_H"
   print >>H
   print >>H, "#include <inttypes.h>"
+  print >>H, "#include \"src/core/lib/iomgr/exec_ctx.h\""
   print >>H
 
   for typename, instances in sorted(inst_map.items()):
@@ -218,11 +219,9 @@
                 "GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_%s)") % (
                 ctr.name.upper(), ctr.name.upper())
   for histogram in inst_map['Histogram']:
-    code, bounds_idx = gen_bucket_code(histogram)
-    histo_bucket_boundaries.append(bounds_idx)
-    print >>H, ("#define GRPC_STATS_INC_%s(exec_ctx, value) %s") % (
-                histogram.name.upper(),
-                code)
+    print >>H, "#define GRPC_STATS_INC_%s(exec_ctx, value) grpc_stats_inc_%s((exec_ctx), (int)(value))" % (
+        histogram.name.upper(), histogram.name.lower())
+    print >>H, "void grpc_stats_inc_%s(grpc_exec_ctx *exec_ctx, int x);" % histogram.name.lower()
 
   for i, tbl in enumerate(static_tables):
     print >>H, "extern const %s grpc_stats_table_%d[%d];" % (tbl[0], i, len(tbl[1]))
@@ -230,6 +229,8 @@
   print >>H, "extern const int grpc_stats_histo_buckets[%d];" % len(inst_map['Histogram'])
   print >>H, "extern const int grpc_stats_histo_start[%d];" % len(inst_map['Histogram'])
   print >>H, "extern const int *const grpc_stats_histo_bucket_boundaries[%d];" % len(inst_map['Histogram'])
+  print >>H, "extern const double *const grpc_stats_histo_bucket_boundaries[%d];" % len(inst_map['Histogram'])
+  print >>H, "extern void (*const grpc_stats_inc_histogram[%d])(grpc_exec_ctx *exec_ctx, int x);" % len(inst_map['Histogram'])
 
   print >>H
   print >>H, "#endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */"
@@ -253,6 +254,14 @@
   put_banner([C], ["Automatically generated by tools/codegen/core/gen_stats_data.py"])
 
   print >>C, "#include \"src/core/lib/debug/stats_data.h\""
+  print >>C, "#include \"src/core/lib/debug/stats.h\""
+  print >>C, "#include \"src/core/lib/iomgr/exec_ctx.h\""
+
+  histo_code = []
+  for histogram in inst_map['Histogram']:
+    code, bounds_idx = gen_bucket_code(histogram)
+    histo_bucket_boundaries.append(bounds_idx)
+    histo_code.append(code)
 
   for typename, instances in sorted(inst_map.items()):
     print >>C, "const char *grpc_stats_%s_name[GRPC_STATS_%s_COUNT] = {" % (
@@ -264,9 +273,16 @@
     print >>C, "const %s grpc_stats_table_%d[%d] = {%s};" % (
         tbl[0], i, len(tbl[1]), ','.join('%s' % x for x in tbl[1]))
 
+  for histogram, code in zip(inst_map['Histogram'], histo_code):
+    print >>C, ("void grpc_stats_inc_%s(grpc_exec_ctx *exec_ctx, double value) {%s}") % (
+                histogram.name.lower(),
+                code)
+
   print >>C, "const int grpc_stats_histo_buckets[%d] = {%s};" % (
       len(inst_map['Histogram']), ','.join('%s' % x for x in histo_buckets))
   print >>C, "const int grpc_stats_histo_start[%d] = {%s};" % (
       len(inst_map['Histogram']), ','.join('%s' % x for x in histo_start))
   print >>C, "const int *const grpc_stats_histo_bucket_boundaries[%d] = {%s};" % (
       len(inst_map['Histogram']), ','.join('grpc_stats_table_%d' % x for x in histo_bucket_boundaries))
+  print >>C, "void (*const grpc_stats_inc_histogram[%d])(grpc_exec_ctx *exec_ctx, double x) = {%s};" % (
+      len(inst_map['Histogram']), ','.join('grpc_stats_inc_%s' % histogram.name.lower() for histogram in inst_map['Histogram']))
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index ecfb96e..186f383 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -4021,6 +4021,24 @@
       "gpr", 
       "gpr_test_util", 
       "grpc", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "stats_test", 
+    "src": [
+      "test/core/debug/stats_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
       "grpc++", 
       "grpc_test_util"
     ], 
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 4368a57..f77b458 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -3943,6 +3943,28 @@
     "exclude_configs": [], 
     "exclude_iomgrs": [], 
     "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
+    "name": "stats_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
     "gtest": false, 
     "language": "c++", 
     "name": "status_test",