Merge remote-tracking branch 'upstream/master' into rq-threads

pull/16081/head
Sree Kuchibhotla 6 years ago
commit f2a57cdd75
  1. 2
      include/grpc/support/string_util.h
  2. 420
      src/boringssl/crypto_test_data.cc
  3. 1190
      src/boringssl/err_data.c
  4. 10
      src/core/ext/filters/client_channel/client_channel.cc
  5. 5
      src/core/ext/filters/client_channel/client_channel.h
  6. 41
      src/core/ext/filters/client_channel/client_channel_channelz.cc
  7. 17
      src/core/ext/filters/client_channel/client_channel_channelz.h
  8. 15
      src/core/ext/filters/client_channel/lb_policy.h
  9. 7
      src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
  10. 79
      src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
  11. 3
      src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
  12. 17
      src/core/ext/filters/client_channel/subchannel.cc
  13. 4
      src/core/ext/filters/client_channel/subchannel.h
  14. 46
      src/core/lib/channel/channel_trace.cc
  15. 2
      src/core/lib/channel/channel_trace.h
  16. 126
      src/core/lib/channel/channelz.cc
  17. 37
      src/core/lib/channel/channelz.h
  18. 71
      src/core/lib/channel/channelz_registry.cc
  19. 95
      src/core/lib/channel/channelz_registry.h
  20. 28
      src/core/lib/gpr/string.cc
  21. 10
      src/core/lib/gpr/string.h
  22. 7
      src/core/lib/gprpp/abstract.h
  23. 58
      src/core/lib/gprpp/inlined_vector.h
  24. 13
      src/core/lib/json/json.cc
  25. 5
      src/core/lib/json/json.h
  26. 9
      src/core/lib/surface/channel.cc
  27. 4
      src/core/lib/surface/init.cc
  28. 14
      test/core/channel/channel_trace_test.cc
  29. 74
      test/core/channel/channelz_registry_test.cc
  30. 90
      test/core/channel/channelz_test.cc
  31. 16
      test/core/end2end/tests/channelz.cc
  32. 192
      test/core/gprpp/inlined_vector_test.cc
  33. 14
      test/cpp/util/channel_trace_proto_helper.cc
  34. 5
      test/cpp/util/channel_trace_proto_helper.h
  35. 2
      third_party/boringssl
  36. 2
      third_party/boringssl-with-bazel
  37. 8
      tools/internal_ci/helper_scripts/prepare_build_macos_rc
  38. 27
      tools/internal_ci/macos/grpc_ios_binary_size.sh
  39. 26
      tools/internal_ci/macos/pull_request/grpc_ios_binary_size.cfg
  40. 6
      tools/interop_matrix/client_matrix.py
  41. 43
      tools/profiling/ios_bin/binary_size.py
  42. 4
      tools/run_tests/sanity/check_submodules.sh

@ -21,6 +21,8 @@
#include <grpc/support/port_platform.h>
#include <grpc/impl/codegen/gpr_types.h>
#ifdef __cplusplus
extern "C" {
#endif

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -3174,6 +3174,16 @@ static void try_to_connect_locked(void* arg, grpc_error* error_ignored) {
GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "try_to_connect");
}
void grpc_client_channel_populate_child_refs(
grpc_channel_element* elem, grpc_core::ChildRefsList* child_subchannels,
grpc_core::ChildRefsList* child_channels) {
channel_data* chand = static_cast<channel_data*>(elem->channel_data);
if (chand->lb_policy != nullptr) {
chand->lb_policy->FillChildRefsForChannelz(child_subchannels,
child_channels);
}
}
grpc_connectivity_state grpc_client_channel_check_connectivity_state(
grpc_channel_element* elem, int try_to_connect) {
channel_data* chand = static_cast<channel_data*>(elem->channel_data);

@ -21,6 +21,7 @@
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
#include "src/core/ext/filters/client_channel/client_channel_factory.h"
#include "src/core/ext/filters/client_channel/resolver.h"
#include "src/core/lib/channel/channel_stack.h"
@ -39,6 +40,10 @@ extern grpc_core::TraceFlag grpc_client_channel_trace;
extern const grpc_channel_filter grpc_client_channel_filter;
void grpc_client_channel_populate_child_refs(
grpc_channel_element* elem, grpc_core::ChildRefsList* child_subchannels,
grpc_core::ChildRefsList* child_channels);
grpc_connectivity_state grpc_client_channel_check_connectivity_state(
grpc_channel_element* elem, int try_to_connect);

@ -41,8 +41,9 @@ static const grpc_arg_pointer_vtable client_channel_channelz_vtable = {
client_channel_channelz_cmp};
ClientChannelNode::ClientChannelNode(grpc_channel* channel,
size_t channel_tracer_max_nodes)
: ChannelNode(channel, channel_tracer_max_nodes) {
size_t channel_tracer_max_nodes,
bool is_top_level_channel)
: ChannelNode(channel, channel_tracer_max_nodes, is_top_level_channel) {
client_channel_ =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
GPR_ASSERT(client_channel_->filter == &grpc_client_channel_filter);
@ -63,6 +64,37 @@ void ClientChannelNode::PopulateConnectivityState(grpc_json* json) {
false);
}
void ClientChannelNode::PopulateChildRefs(grpc_json* json) {
ChildRefsList child_subchannels;
ChildRefsList child_channels;
grpc_json* json_iterator = nullptr;
grpc_client_channel_populate_child_refs(client_channel_, &child_subchannels,
&child_channels);
if (!child_subchannels.empty()) {
grpc_json* array_parent = grpc_json_create_child(
nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false);
for (size_t i = 0; i < child_subchannels.size(); ++i) {
json_iterator =
grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
GRPC_JSON_OBJECT, false);
grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId",
child_subchannels[i]);
}
}
if (!child_channels.empty()) {
grpc_json* array_parent = grpc_json_create_child(
nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false);
json_iterator = nullptr;
for (size_t i = 0; i < child_subchannels.size(); ++i) {
json_iterator =
grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
GRPC_JSON_OBJECT, false);
grpc_json_add_number_string_child(json_iterator, nullptr, "channelId",
child_subchannels[i]);
}
}
}
grpc_arg ClientChannelNode::CreateChannelArg() {
return grpc_channel_arg_pointer_create(
const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC),
@ -71,9 +103,10 @@ grpc_arg ClientChannelNode::CreateChannelArg() {
}
RefCountedPtr<ChannelNode> ClientChannelNode::MakeClientChannelNode(
grpc_channel* channel, size_t channel_tracer_max_nodes) {
grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel) {
return MakePolymorphicRefCounted<ChannelNode, ClientChannelNode>(
channel, channel_tracer_max_nodes);
channel, channel_tracer_max_nodes, is_top_level_channel);
}
} // namespace channelz

@ -22,9 +22,17 @@
#include <grpc/support/port_platform.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/channelz.h"
#include "src/core/lib/gprpp/inlined_vector.h"
namespace grpc_core {
// TODO(ncteisen), this only contains the uuids of the children for now,
// since that is all that is strictly needed. In a future enhancement we will
// add human readable names as in the channelz.proto
typedef InlinedVector<intptr_t, 10> ChildRefsList;
namespace channelz {
// Subtype of ChannelNode that overrides and provides client_channel specific
@ -32,12 +40,16 @@ namespace channelz {
class ClientChannelNode : public ChannelNode {
public:
static RefCountedPtr<ChannelNode> MakeClientChannelNode(
grpc_channel* channel, size_t channel_tracer_max_nodes);
grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel);
// Override this functionality since client_channels have a notion of
// channel connectivity.
void PopulateConnectivityState(grpc_json* json) override;
// Override this functionality since client_channels have subchannels
void PopulateChildRefs(grpc_json* json) override;
// Helper to create a channel arg to ensure this type of ChannelNode is
// created.
static grpc_arg CreateChannelArg();
@ -45,7 +57,8 @@ class ClientChannelNode : public ChannelNode {
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
ClientChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes);
ClientChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel);
virtual ~ClientChannelNode() {}
private:

@ -21,6 +21,7 @@
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
#include "src/core/ext/filters/client_channel/client_channel_factory.h"
#include "src/core/ext/filters/client_channel/subchannel.h"
#include "src/core/lib/gprpp/abstract.h"
@ -143,6 +144,14 @@ class LoadBalancingPolicy
/// consider whether this method is still needed.
virtual void ExitIdleLocked() GRPC_ABSTRACT;
/// populates child_subchannels and child_channels with the uuids of this
/// LB policy's referenced children. This is not invoked from the
/// client_channel's combiner. The implementation is responsible for
/// providing its own synchronization.
virtual void FillChildRefsForChannelz(ChildRefsList* child_subchannels,
ChildRefsList* child_channels)
GRPC_ABSTRACT;
void Orphan() override {
// Invoke ShutdownAndUnrefLocked() inside of the combiner.
GRPC_CLOSURE_SCHED(
@ -196,6 +205,12 @@ class LoadBalancingPolicy
grpc_pollset_set* interested_parties_;
/// Callback to force a re-resolution.
grpc_closure* request_reresolution_;
// Dummy classes needed for alignment issues.
// See https://github.com/grpc/grpc/issues/16032 for context.
// TODO(ncteisen): remove this as soon as the issue is resolved.
ChildRefsList dummy_list_foo;
ChildRefsList dummy_list_bar;
};
} // namespace grpc_core

@ -135,6 +135,9 @@ class GrpcLb : public LoadBalancingPolicy {
void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
void PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) override;
void ExitIdleLocked() override;
// TODO(ncteisen): implement this in a follow up PR
void FillChildRefsForChannelz(ChildRefsList* child_subchannels,
ChildRefsList* child_channels) override {}
private:
/// Linked list of pending pick requests. It stores all information needed to
@ -1004,6 +1007,10 @@ grpc_channel_args* BuildBalancerChannelArgs(
// A channel arg indicating the target is a grpclb load balancer.
grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER), 1),
// A channel arg indicating this is an internal channels, aka it is
// owned by components in Core, not by the user application.
grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), 1),
};
// Construct channel args.
grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove(

@ -58,6 +58,8 @@ class PickFirst : public LoadBalancingPolicy {
void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
void PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) override;
void ExitIdleLocked() override;
void FillChildRefsForChannelz(ChildRefsList* child_subchannels,
ChildRefsList* ignored) override;
private:
~PickFirst();
@ -103,10 +105,23 @@ class PickFirst : public LoadBalancingPolicy {
}
};
// Helper class to ensure that any function that modifies the child refs
// data structures will update the channelz snapshot data structures before
// returning.
class AutoChildRefsUpdater {
public:
explicit AutoChildRefsUpdater(PickFirst* pf) : pf_(pf) {}
~AutoChildRefsUpdater() { pf_->UpdateChildRefsLocked(); }
private:
PickFirst* pf_;
};
void ShutdownLocked() override;
void StartPickingLocked();
void DestroyUnselectedSubchannelsLocked();
void UpdateChildRefsLocked();
// All our subchannels.
OrphanablePtr<PickFirstSubchannelList> subchannel_list_;
@ -122,10 +137,17 @@ class PickFirst : public LoadBalancingPolicy {
PickState* pending_picks_ = nullptr;
// Our connectivity state tracker.
grpc_connectivity_state_tracker state_tracker_;
/// Lock and data used to capture snapshots of this channels child
/// channels and subchannels. This data is consumed by channelz.
gpr_mu child_refs_mu_;
ChildRefsList child_subchannels_;
ChildRefsList child_channels_;
};
PickFirst::PickFirst(const Args& args) : LoadBalancingPolicy(args) {
GPR_ASSERT(args.client_channel_factory != nullptr);
gpr_mu_init(&child_refs_mu_);
grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
"pick_first");
if (grpc_lb_pick_first_trace.enabled()) {
@ -139,6 +161,7 @@ PickFirst::~PickFirst() {
if (grpc_lb_pick_first_trace.enabled()) {
gpr_log(GPR_INFO, "Destroying Pick First %p", this);
}
gpr_mu_destroy(&child_refs_mu_);
GPR_ASSERT(subchannel_list_ == nullptr);
GPR_ASSERT(latest_pending_subchannel_list_ == nullptr);
GPR_ASSERT(pending_picks_ == nullptr);
@ -158,6 +181,7 @@ void PickFirst::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
}
void PickFirst::ShutdownLocked() {
AutoChildRefsUpdater gaurd(this);
grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
if (grpc_lb_pick_first_trace.enabled()) {
gpr_log(GPR_INFO, "Pick First %p Shutting down", this);
@ -280,7 +304,61 @@ void PickFirst::PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) {
}
}
void PickFirst::FillChildRefsForChannelz(
ChildRefsList* child_subchannels_to_fill, ChildRefsList* ignored) {
mu_guard guard(&child_refs_mu_);
for (size_t i = 0; i < child_subchannels_.size(); ++i) {
// TODO(ncteisen): implement a de dup loop that is not O(n^2). Might
// have to implement lightweight set. For now, we don't care about
// performance when channelz requests are made.
bool found = false;
for (size_t j = 0; j < child_subchannels_to_fill->size(); ++j) {
if ((*child_subchannels_to_fill)[j] == child_subchannels_[i]) {
found = true;
break;
}
}
if (!found) {
child_subchannels_to_fill->push_back(child_subchannels_[i]);
}
}
}
void PickFirst::UpdateChildRefsLocked() {
ChildRefsList cs;
if (subchannel_list_ != nullptr) {
for (size_t i = 0; i < subchannel_list_->num_subchannels(); ++i) {
if (subchannel_list_->subchannel(i)->subchannel() != nullptr) {
grpc_core::channelz::SubchannelNode* subchannel_node =
grpc_subchannel_get_channelz_node(
subchannel_list_->subchannel(i)->subchannel());
if (subchannel_node != nullptr) {
cs.push_back(subchannel_node->subchannel_uuid());
}
}
}
}
if (latest_pending_subchannel_list_ != nullptr) {
for (size_t i = 0; i < latest_pending_subchannel_list_->num_subchannels();
++i) {
if (latest_pending_subchannel_list_->subchannel(i)->subchannel() !=
nullptr) {
grpc_core::channelz::SubchannelNode* subchannel_node =
grpc_subchannel_get_channelz_node(
latest_pending_subchannel_list_->subchannel(i)->subchannel());
if (subchannel_node != nullptr) {
cs.push_back(subchannel_node->subchannel_uuid());
}
}
}
}
// atomically update the data that channelz will actually be looking at.
mu_guard guard(&child_refs_mu_);
child_subchannels_ = std::move(cs);
}
void PickFirst::UpdateLocked(const grpc_channel_args& args) {
AutoChildRefsUpdater guard(this);
const grpc_arg* arg = grpc_channel_args_find(&args, GRPC_ARG_LB_ADDRESSES);
if (arg == nullptr || arg->type != GRPC_ARG_POINTER) {
if (subchannel_list_ == nullptr) {
@ -388,6 +466,7 @@ void PickFirst::UpdateLocked(const grpc_channel_args& args) {
void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
grpc_connectivity_state connectivity_state, grpc_error* error) {
PickFirst* p = static_cast<PickFirst*>(subchannel_list()->policy());
AutoChildRefsUpdater guard(p);
// The notification must be for a subchannel in either the current or
// latest pending subchannel lists.
GPR_ASSERT(subchannel_list() == p->subchannel_list_.get() ||

@ -69,6 +69,9 @@ class RoundRobin : public LoadBalancingPolicy {
void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
void PingOneLocked(grpc_closure* on_initiate, grpc_closure* on_ack) override;
void ExitIdleLocked() override;
// TODO(ncteisen): implement this in a follow up PR
void FillChildRefsForChannelz(ChildRefsList* child_subchannels,
ChildRefsList* child_channels) override {}
private:
~RoundRobin();

@ -134,6 +134,9 @@ struct grpc_subchannel {
bool backoff_begun;
/** our alarm */
grpc_timer alarm;
grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode>
channelz_subchannel;
};
struct grpc_subchannel_call {
@ -178,6 +181,7 @@ static void connection_destroy(void* arg, grpc_error* error) {
static void subchannel_destroy(void* arg, grpc_error* error) {
grpc_subchannel* c = static_cast<grpc_subchannel*>(arg);
c->channelz_subchannel.reset();
gpr_free((void*)c->filters);
grpc_channel_args_destroy(c->args);
grpc_connectivity_state_destroy(&c->state_tracker);
@ -374,9 +378,22 @@ grpc_subchannel* grpc_subchannel_create(grpc_connector* connector,
c->backoff.Init(backoff_options);
gpr_mu_init(&c->mu);
const grpc_arg* arg =
grpc_channel_args_find(c->args, GRPC_ARG_ENABLE_CHANNELZ);
bool channelz_enabled = grpc_channel_arg_get_bool(arg, false);
if (channelz_enabled) {
c->channelz_subchannel =
grpc_core::MakeRefCounted<grpc_core::channelz::SubchannelNode>();
}
return grpc_subchannel_index_register(key, c);
}
grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node(
grpc_subchannel* subchannel) {
return subchannel->channelz_subchannel.get();
}
static void continue_connect_locked(grpc_subchannel* c) {
grpc_connect_in_args args;
args.interested_parties = c->pollset_set;

@ -21,6 +21,7 @@
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
#include "src/core/ext/filters/client_channel/connector.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/gpr/arena.h"
@ -115,6 +116,9 @@ grpc_subchannel_call* grpc_subchannel_call_ref(
void grpc_subchannel_call_unref(
grpc_subchannel_call* call GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node(
grpc_subchannel* subchannel);
/** Returns a pointer to the parent data associated with \a subchannel_call.
The data will be of the size specified in \a parent_data_size
field of the args passed to \a grpc_connected_subchannel_create_call(). */

@ -131,38 +131,6 @@ void ChannelTrace::AddTraceEventReferencingSubchannel(
namespace {
// 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;
}
const char* severity_string(ChannelTrace::Severity severity) {
switch (severity) {
case ChannelTrace::Severity::Info:
@ -186,9 +154,9 @@ void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const {
json_iterator = grpc_json_create_child(json_iterator, json, "severity",
severity_string(severity_),
GRPC_JSON_STRING, false);
json_iterator =
grpc_json_create_child(json_iterator, json, "timestamp",
fmt_time(timestamp_), GRPC_JSON_STRING, true);
json_iterator = grpc_json_create_child(json_iterator, json, "timestamp",
gpr_format_timespec(timestamp_),
GRPC_JSON_STRING, true);
if (referenced_channel_ != nullptr) {
char* uuid_str;
gpr_asprintf(&uuid_str, "%" PRIdPTR, referenced_channel_->channel_uuid());
@ -206,7 +174,7 @@ void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const {
}
}
grpc_json* ChannelTrace::RenderJSON() 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);
@ -216,9 +184,9 @@ grpc_json* ChannelTrace::RenderJSON() const {
json_iterator =
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, "creationTimestamp",
fmt_time(time_created_), GRPC_JSON_STRING, true);
json_iterator = grpc_json_create_child(
json_iterator, json, "creationTimestamp",
gpr_format_timespec(time_created_), GRPC_JSON_STRING, true);
grpc_json* events = grpc_json_create_child(json_iterator, json, "events",
nullptr, GRPC_JSON_ARRAY, false);
json_iterator = nullptr;

@ -71,7 +71,7 @@ class ChannelTrace {
// Creates and returns the raw grpc_json object, so a parent channelz
// object may incorporate the json before rendering.
grpc_json* RenderJSON() const;
grpc_json* RenderJson() const;
private:
// Types of objects that can be references by trace events.

@ -41,65 +41,22 @@
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) {
ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel)
: channel_(channel),
target_(nullptr),
channel_uuid_(-1),
is_top_level_channel_(is_top_level_channel) {
trace_.Init(channel_tracer_max_nodes);
target_ = UniquePtr<char>(grpc_channel_get_target(channel_));
channel_uuid_ = ChannelzRegistry::Register(this);
channel_uuid_ = ChannelzRegistry::RegisterChannelNode(this);
gpr_atm_no_barrier_store(&last_call_started_millis_,
(gpr_atm)ExecCtx::Get()->Now());
}
ChannelNode::~ChannelNode() {
trace_.Destroy();
ChannelzRegistry::Unregister(channel_uuid_);
ChannelzRegistry::UnregisterChannelNode(channel_uuid_);
}
void ChannelNode::RecordCallStarted() {
@ -110,7 +67,9 @@ void ChannelNode::RecordCallStarted() {
void ChannelNode::PopulateConnectivityState(grpc_json* json) {}
char* ChannelNode::RenderJSON() {
void ChannelNode::PopulateChildRefs(grpc_json* json) {}
grpc_json* 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;
@ -120,7 +79,8 @@ char* ChannelNode::RenderJSON() {
GRPC_JSON_OBJECT, false);
json = json_iterator;
json_iterator = nullptr;
json_iterator = add_num_str(json, json_iterator, "channelId", channel_uuid_);
json_iterator = grpc_json_add_number_string_child(json, json_iterator,
"channelId", channel_uuid_);
// reset json iterators to top level object
json = top_level_json;
json_iterator = nullptr;
@ -130,45 +90,63 @@ char* ChannelNode::RenderJSON() {
json = data;
json_iterator = nullptr;
PopulateConnectivityState(json);
GPR_ASSERT(target_.get() != nullptr);
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();
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
// we manually link up and fill the child since it was created for us in
// ChannelTrace::RenderJson
trace->key = "trace"; // this object is named trace in channelz.proto
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_);
if (calls_started_ != 0) {
json_iterator = grpc_json_add_number_string_child(
json, json_iterator, "callsStarted", calls_started_);
}
if (calls_succeeded_ != 0) {
json_iterator = grpc_json_add_number_string_child(
json, json_iterator, "callsSucceeded", calls_succeeded_);
}
if (calls_failed_) {
json_iterator = grpc_json_add_number_string_child(
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);
gpr_format_timespec(ts), GRPC_JSON_STRING, true);
json = top_level_json;
json_iterator = nullptr;
PopulateChildRefs(json);
return top_level_json;
}
char* ChannelNode::RenderJsonString() {
grpc_json* json = RenderJson();
char* json_str = grpc_json_dump_to_string(json, 0);
grpc_json_destroy(json);
return json_str;
}
RefCountedPtr<ChannelNode> ChannelNode::MakeChannelNode(
grpc_channel* channel, size_t channel_tracer_max_nodes) {
grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel) {
return MakeRefCounted<grpc_core::channelz::ChannelNode>(
channel, channel_tracer_max_nodes);
channel, channel_tracer_max_nodes, is_top_level_channel);
}
SubchannelNode::SubchannelNode() {
subchannel_uuid_ = ChannelzRegistry::RegisterSubchannelNode(this);
}
SubchannelNode::~SubchannelNode() {
ChannelzRegistry::UnregisterSubchannelNode(subchannel_uuid_);
}
} // namespace channelz

@ -35,6 +35,10 @@
#define GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC \
"grpc.channelz_channel_node_creation_func"
// Channel arg key to signal that the channel is an internal channel.
#define GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL \
"grpc.channelz_channel_is_internal_channel"
namespace grpc_core {
namespace channelz {
@ -45,7 +49,8 @@ class ChannelNodePeer;
class ChannelNode : public RefCounted<ChannelNode> {
public:
static RefCountedPtr<ChannelNode> MakeChannelNode(
grpc_channel* channel, size_t channel_tracer_max_nodes);
grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel);
void RecordCallStarted();
void RecordCallFailed() {
@ -55,13 +60,16 @@ class ChannelNode : public RefCounted<ChannelNode> {
gpr_atm_no_barrier_fetch_add(&calls_succeeded_, (gpr_atm(1)));
}
char* RenderJSON();
grpc_json* RenderJson();
char* RenderJsonString();
// helper for getting and populating connectivity state. It is virtual
// because it allows the client_channel specific code to live in ext/
// instead of lib/
virtual void PopulateConnectivityState(grpc_json* json);
virtual void PopulateChildRefs(grpc_json* json);
ChannelTrace* trace() { return trace_.get(); }
void MarkChannelDestroyed() {
@ -72,11 +80,13 @@ class ChannelNode : public RefCounted<ChannelNode> {
bool ChannelIsDestroyed() { return channel_ == nullptr; }
intptr_t channel_uuid() { return channel_uuid_; }
bool is_top_level_channel() { return is_top_level_channel_; }
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes);
ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel);
virtual ~ChannelNode();
private:
@ -90,13 +100,32 @@ class ChannelNode : public RefCounted<ChannelNode> {
gpr_atm calls_failed_ = 0;
gpr_atm last_call_started_millis_ = 0;
intptr_t channel_uuid_;
bool is_top_level_channel_ = true;
ManualConstructor<ChannelTrace> trace_;
};
// Placeholds channelz class for subchannels. All this can do now is track its
// uuid (this information is needed by the parent channelz class).
// TODO(ncteisen): build this out to support the GetSubchannel channelz request.
class SubchannelNode : public RefCounted<SubchannelNode> {
public:
SubchannelNode();
virtual ~SubchannelNode();
intptr_t subchannel_uuid() { return subchannel_uuid_; }
protected:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
private:
intptr_t subchannel_uuid_;
};
// Creation functions
typedef RefCountedPtr<ChannelNode> (*ChannelNodeCreationFunc)(grpc_channel*,
size_t);
size_t, bool);
} // namespace channelz
} // namespace grpc_core

@ -19,16 +19,19 @@
#include <grpc/impl/codegen/port_platform.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/gprpp/memory.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <cstring>
namespace grpc_core {
namespace channelz {
namespace {
// singleton instance of the registry.
@ -49,12 +52,72 @@ ChannelzRegistry::ChannelzRegistry() { gpr_mu_init(&mu_); }
ChannelzRegistry::~ChannelzRegistry() { gpr_mu_destroy(&mu_); }
void ChannelzRegistry::InternalUnregister(intptr_t uuid) {
intptr_t ChannelzRegistry::InternalRegisterEntry(const RegistryEntry& entry) {
mu_guard guard(&mu_);
entities_.push_back(entry);
intptr_t uuid = entities_.size();
return uuid;
}
void ChannelzRegistry::InternalUnregisterEntry(intptr_t uuid, EntityType type) {
GPR_ASSERT(uuid >= 1);
gpr_mu_lock(&mu_);
mu_guard guard(&mu_);
GPR_ASSERT(static_cast<size_t>(uuid) <= entities_.size());
entities_[uuid - 1] = nullptr;
gpr_mu_unlock(&mu_);
GPR_ASSERT(entities_[uuid - 1].type == type);
entities_[uuid - 1].object = nullptr;
entities_[uuid - 1].type = EntityType::kUnset;
}
void* ChannelzRegistry::InternalGetEntry(intptr_t uuid, EntityType type) {
mu_guard guard(&mu_);
if (uuid < 1 || uuid > static_cast<intptr_t>(entities_.size())) {
return nullptr;
}
if (entities_[uuid - 1].type == type) {
return entities_[uuid - 1].object;
} else {
return nullptr;
}
}
char* ChannelzRegistry::InternalGetTopChannels(intptr_t start_channel_id) {
grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
grpc_json* json = top_level_json;
grpc_json* json_iterator = nullptr;
InlinedVector<ChannelNode*, 10> top_level_channels;
// uuids index into entities one-off (idx 0 is really uuid 1, since 0 is
// reserved). However, we want to support requests coming in with
// start_channel_id=0, which signifies "give me everything." Hence this
// funky looking line below.
size_t start_idx = start_channel_id == 0 ? 0 : start_channel_id - 1;
for (size_t i = start_idx; i < entities_.size(); ++i) {
if (entities_[i].type == EntityType::kChannelNode) {
ChannelNode* channel_node =
static_cast<ChannelNode*>(entities_[i].object);
if (channel_node->is_top_level_channel()) {
top_level_channels.push_back(channel_node);
}
}
}
if (top_level_channels.size() > 0) {
// create list of channels
grpc_json* array_parent = grpc_json_create_child(
nullptr, json, "channel", nullptr, GRPC_JSON_ARRAY, false);
for (size_t i = 0; i < top_level_channels.size(); ++i) {
grpc_json* channel_json = top_level_channels[i]->RenderJson();
json_iterator =
grpc_json_link_child(array_parent, channel_json, json_iterator);
}
}
// For now we do not have any pagination rules. In the future we could
// pick a constant for max_channels_sent for a GetTopChannels request.
// Tracking: https://github.com/grpc/grpc/issues/16019.
json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
GRPC_JSON_TRUE, false);
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

@ -22,11 +22,13 @@
#include <grpc/impl/codegen/port_platform.h>
#include "src/core/lib/channel/channel_trace.h"
#include "src/core/lib/channel/channelz.h"
#include "src/core/lib/gprpp/inlined_vector.h"
#include <stdint.h>
namespace grpc_core {
namespace channelz {
// singleton registry object to track all objects that are needed to support
// channelz bookkeeping. All objects share globally distributed uuids.
@ -35,26 +37,56 @@ class ChannelzRegistry {
// To be called in grpc_init()
static void Init();
// To be callen in grpc_shutdown();
// To be called in grpc_shutdown();
static void Shutdown();
// globally registers a channelz Object. Returns its unique uuid
template <typename Object>
static intptr_t Register(Object* object) {
return Default()->InternalRegister(object);
// Register/Unregister/Get for ChannelNode
static intptr_t RegisterChannelNode(ChannelNode* channel_node) {
RegistryEntry entry(channel_node, EntityType::kChannelNode);
return Default()->InternalRegisterEntry(entry);
}
static void UnregisterChannelNode(intptr_t uuid) {
Default()->InternalUnregisterEntry(uuid, EntityType::kChannelNode);
}
static ChannelNode* GetChannelNode(intptr_t uuid) {
void* gotten = Default()->InternalGetEntry(uuid, EntityType::kChannelNode);
return gotten == nullptr ? nullptr : static_cast<ChannelNode*>(gotten);
}
// globally unregisters the object that is associated to uuid.
static void Unregister(intptr_t uuid) { Default()->InternalUnregister(uuid); }
// Register/Unregister/Get for SubchannelNode
static intptr_t RegisterSubchannelNode(SubchannelNode* channel_node) {
RegistryEntry entry(channel_node, EntityType::kSubchannelNode);
return Default()->InternalRegisterEntry(entry);
}
static void UnregisterSubchannelNode(intptr_t uuid) {
Default()->InternalUnregisterEntry(uuid, EntityType::kSubchannelNode);
}
static SubchannelNode* GetSubchannelNode(intptr_t uuid) {
void* gotten =
Default()->InternalGetEntry(uuid, EntityType::kSubchannelNode);
return gotten == nullptr ? nullptr : static_cast<SubchannelNode*>(gotten);
}
// if object with uuid has previously been registered, returns the
// Object associated with that uuid. Else returns nullptr.
template <typename Object>
static Object* Get(intptr_t uuid) {
return Default()->InternalGet<Object>(uuid);
// Returns the allocated JSON string that represents the proto
// GetTopChannelsResponse as per channelz.proto.
static char* GetTopChannels(intptr_t start_channel_id) {
return Default()->InternalGetTopChannels(start_channel_id);
}
private:
enum class EntityType {
kChannelNode,
kSubchannelNode,
kUnset,
};
struct RegistryEntry {
RegistryEntry(void* object_in, EntityType type_in)
: object(object_in), type(type_in) {}
void* object;
EntityType type;
};
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
@ -64,40 +96,25 @@ class ChannelzRegistry {
// Returned the singleton instance of ChannelzRegistry;
static ChannelzRegistry* Default();
// globally registers a channelz Object. Returns its unique uuid
template <typename Object>
intptr_t InternalRegister(Object* object) {
gpr_mu_lock(&mu_);
entities_.push_back(static_cast<void*>(object));
intptr_t uuid = entities_.size();
gpr_mu_unlock(&mu_);
return uuid;
}
// globally registers an Entry. Returns its unique uuid
intptr_t InternalRegisterEntry(const RegistryEntry& entry);
// globally unregisters the object that is associated to uuid.
void InternalUnregister(intptr_t uuid);
// if object with uuid has previously been registered, returns the
// Object associated with that uuid. Else returns nullptr.
template <typename Object>
Object* InternalGet(intptr_t uuid) {
gpr_mu_lock(&mu_);
if (uuid < 1 || uuid > static_cast<intptr_t>(entities_.size())) {
gpr_mu_unlock(&mu_);
return nullptr;
}
Object* ret = static_cast<Object*>(entities_[uuid - 1]);
gpr_mu_unlock(&mu_);
return ret;
}
// globally unregisters the object that is associated to uuid. Also does
// sanity check that an object doesn't try to unregister the wrong type.
void InternalUnregisterEntry(intptr_t uuid, EntityType type);
// if object with uuid has previously been registered as the correct type,
// returns the void* associated with that uuid. Else returns nullptr.
void* InternalGetEntry(intptr_t uuid, EntityType type);
// private members
char* InternalGetTopChannels(intptr_t start_channel_id);
// protects entities_ and uuid_
gpr_mu mu_;
InlinedVector<void*, 20> entities_;
InlinedVector<RegistryEntry, 20> entities_;
};
} // namespace channelz
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_CHANNEL_CHANNELZ_REGISTRY_H */

@ -23,8 +23,10 @@
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@ -54,6 +56,32 @@ typedef struct {
char* data;
} dump_out;
char* gpr_format_timespec(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;
}
static dump_out dump_out_create(void) {
dump_out r = {0, 0, nullptr};
return r;

@ -21,6 +21,8 @@
#include <grpc/support/port_platform.h>
#include <grpc/impl/codegen/gpr_types.h>
#include <stdbool.h>
#include <stddef.h>
@ -81,6 +83,14 @@ char* gpr_strjoin_sep(const char** strs, size_t nstrs, const char* sep,
void gpr_string_split(const char* input, const char* sep, char*** strs,
size_t* nstrs);
/* 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* gpr_format_timespec(gpr_timespec);
/* A vector of strings... for building up a final string one piece at a time */
typedef struct {
char** strs;

@ -28,7 +28,10 @@
// gRPC currently can't depend on libstdc++, so we can't use "= 0" for
// pure virtual methods. Instead, we use this macro.
#define GRPC_ABSTRACT \
{ GPR_ASSERT(false); }
#define GRPC_ABSTRACT \
{ \
gpr_log(GPR_ERROR, "Function marked GRPC_ABSTRACT was not implemented"); \
GPR_ASSERT(false); \
}
#endif /* GRPC_CORE_LIB_GPRPP_ABSTRACT_H */

@ -22,6 +22,7 @@
#include <grpc/support/port_platform.h>
#include <cassert>
#include <cstring>
#include "src/core/lib/gprpp/memory.h"
@ -50,9 +51,33 @@ class InlinedVector {
InlinedVector() { init_data(); }
~InlinedVector() { destroy_elements(); }
// For now, we do not support copying.
InlinedVector(const InlinedVector&) = delete;
InlinedVector& operator=(const InlinedVector&) = delete;
// copy constructor
InlinedVector(const InlinedVector& v) {
init_data();
copy_from(v);
}
InlinedVector& operator=(const InlinedVector& v) {
if (this != &v) {
clear();
copy_from(v);
}
return *this;
}
// move constructor
InlinedVector(InlinedVector&& v) {
init_data();
move_from(v);
}
InlinedVector& operator=(InlinedVector&& v) {
if (this != &v) {
clear();
move_from(v);
}
return *this;
}
T* data() {
return dynamic_ != nullptr ? dynamic_ : reinterpret_cast<T*>(inline_);
@ -98,6 +123,33 @@ class InlinedVector {
void push_back(T&& value) { emplace_back(std::move(value)); }
void copy_from(const InlinedVector& v) {
// if v is allocated, copy over the buffer.
if (v.dynamic_ != nullptr) {
reserve(v.capacity_);
memcpy(dynamic_, v.dynamic_, v.size_ * sizeof(T));
} else {
memcpy(inline_, v.inline_, v.size_ * sizeof(T));
}
// copy over metadata
size_ = v.size_;
capacity_ = v.capacity_;
}
void move_from(InlinedVector& v) {
// if v is allocated, then we steal its buffer, else we copy it.
if (v.dynamic_ != nullptr) {
dynamic_ = v.dynamic_;
} else {
memcpy(inline_, v.inline_, v.size_ * sizeof(T));
}
// copy over metadata
size_ = v.size_;
capacity_ = v.capacity_;
// null out the original
v.init_data();
}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }

@ -18,10 +18,12 @@
#include <grpc/support/port_platform.h>
#include <inttypes.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/json/json.h"
@ -56,6 +58,8 @@ void grpc_json_destroy(grpc_json* json) {
grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child,
grpc_json* sibling) {
// link child up to parent
child->parent = parent;
// first child case.
if (parent->child == nullptr) {
GPR_ASSERT(sibling == nullptr);
@ -79,8 +83,15 @@ grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent,
grpc_json* child = grpc_json_create(type);
grpc_json_link_child(parent, child, sibling);
child->owns_value = owns_value;
child->parent = parent;
child->value = value;
child->key = key;
return child;
}
grpc_json* grpc_json_add_number_string_child(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);
}

@ -91,4 +91,9 @@ grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent,
const char* key, const char* value,
grpc_json_type type, bool owns_value);
/* Creates a child json string object from the integer num, then links the
json object into the parent's json tree */
grpc_json* grpc_json_add_number_string_child(grpc_json* parent, grpc_json* it,
const char* name, int64_t num);
#endif /* GRPC_CORE_LIB_JSON_JSON_H */

@ -105,6 +105,7 @@ grpc_channel* grpc_channel_create_with_builder(
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;
bool internal_channel = false;
// this creates the default ChannelNode. Different types of channels may
// override this to ensure a correct ChannelNode is created.
grpc_core::channelz::ChannelNodeCreationFunc channel_node_create_func =
@ -158,13 +159,17 @@ grpc_channel* grpc_channel_create_with_builder(
channel_node_create_func =
reinterpret_cast<grpc_core::channelz::ChannelNodeCreationFunc>(
args->args[i].value.pointer.p);
} else if (0 == strcmp(args->args[i].key,
GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL)) {
internal_channel = grpc_channel_arg_get_bool(&args->args[i], false);
}
}
grpc_channel_args_destroy(args);
if (channelz_enabled) {
channel->channelz_channel =
channel_node_create_func(channel, channel_tracer_max_nodes);
bool is_top_level_channel = channel->is_client && !internal_channel;
channel->channelz_channel = channel_node_create_func(
channel, channel_tracer_max_nodes, is_top_level_channel);
channel->channelz_channel->trace()->AddTraceEvent(
grpc_core::channelz::ChannelTrace::Severity::Info,
grpc_slice_from_static_string("Channel created"));

@ -127,7 +127,7 @@ void grpc_init(void) {
grpc_slice_intern_init();
grpc_mdctx_global_init();
grpc_channel_init_init();
grpc_core::ChannelzRegistry::Init();
grpc_core::channelz::ChannelzRegistry::Init();
grpc_security_pre_init();
grpc_core::ExecCtx::GlobalInit();
grpc_iomgr_init();
@ -176,7 +176,7 @@ void grpc_shutdown(void) {
grpc_mdctx_global_shutdown();
grpc_handshaker_factory_registry_shutdown();
grpc_slice_intern_shutdown();
grpc_core::ChannelzRegistry::Shutdown();
grpc_core::channelz::ChannelzRegistry::Shutdown();
grpc_stats_shutdown();
grpc_core::Fork::GlobalShutdown();
}

@ -34,8 +34,6 @@
#include "test/core/util/test_config.h"
#include "test/cpp/util/channel_trace_proto_helper.h"
// remove me
#include <grpc/support/string_util.h>
#include <stdlib.h>
#include <string.h>
@ -88,7 +86,7 @@ void AddSimpleTrace(ChannelTrace* tracer) {
void ValidateChannelTrace(ChannelTrace* tracer,
size_t expected_num_event_logged, size_t max_nodes) {
if (!max_nodes) return;
grpc_json* json = tracer->RenderJSON();
grpc_json* json = tracer->RenderJson();
EXPECT_NE(json, nullptr);
char* json_str = grpc_json_dump_to_string(json, 0);
grpc_json_destroy(json);
@ -157,7 +155,7 @@ TEST_P(ChannelTracerTest, ComplexTest) {
AddSimpleTrace(&tracer);
ChannelFixture channel1(GetParam());
RefCountedPtr<ChannelNode> sc1 =
MakeRefCounted<ChannelNode>(channel1.channel(), GetParam());
MakeRefCounted<ChannelNode>(channel1.channel(), GetParam(), true);
tracer.AddTraceEventReferencingSubchannel(
ChannelTrace::Severity::Info,
grpc_slice_from_static_string("subchannel one created"), sc1);
@ -175,7 +173,7 @@ TEST_P(ChannelTracerTest, ComplexTest) {
ValidateChannelTrace(&tracer, 5, GetParam());
ChannelFixture channel2(GetParam());
RefCountedPtr<ChannelNode> sc2 =
MakeRefCounted<ChannelNode>(channel2.channel(), GetParam());
MakeRefCounted<ChannelNode>(channel2.channel(), GetParam(), true);
tracer.AddTraceEventReferencingChannel(
ChannelTrace::Severity::Info,
grpc_slice_from_static_string("LB channel two created"), sc2);
@ -204,7 +202,7 @@ TEST_P(ChannelTracerTest, TestNesting) {
ValidateChannelTrace(&tracer, 2, GetParam());
ChannelFixture channel1(GetParam());
RefCountedPtr<ChannelNode> sc1 =
MakeRefCounted<ChannelNode>(channel1.channel(), GetParam());
MakeRefCounted<ChannelNode>(channel1.channel(), GetParam(), true);
tracer.AddTraceEventReferencingChannel(
ChannelTrace::Severity::Info,
grpc_slice_from_static_string("subchannel one created"), sc1);
@ -212,7 +210,7 @@ TEST_P(ChannelTracerTest, TestNesting) {
AddSimpleTrace(sc1->trace());
ChannelFixture channel2(GetParam());
RefCountedPtr<ChannelNode> conn1 =
MakeRefCounted<ChannelNode>(channel2.channel(), GetParam());
MakeRefCounted<ChannelNode>(channel2.channel(), GetParam(), true);
// nesting one level deeper.
sc1->trace()->AddTraceEventReferencingSubchannel(
ChannelTrace::Severity::Info,
@ -225,7 +223,7 @@ TEST_P(ChannelTracerTest, TestNesting) {
ValidateChannelTrace(conn1->trace(), 1, GetParam());
ChannelFixture channel3(GetParam());
RefCountedPtr<ChannelNode> sc2 =
MakeRefCounted<ChannelNode>(channel3.channel(), GetParam());
MakeRefCounted<ChannelNode>(channel3.channel(), GetParam(), true);
tracer.AddTraceEventReferencingSubchannel(
ChannelTrace::Severity::Info,
grpc_slice_from_static_string("subchannel two created"), sc2);

@ -19,17 +19,20 @@
#include <stdlib.h>
#include <string.h>
#include <grpc/grpc.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/gprpp/memory.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"
@ -37,27 +40,26 @@
#include <string.h>
namespace grpc_core {
namespace channelz {
namespace testing {
// Tests basic ChannelTrace functionality like construction, adding trace, and
// lookups by uuid.
TEST(ChannelzRegistryTest, UuidStartsAboveZeroTest) {
int object_to_register;
intptr_t uuid = ChannelzRegistry::Register(&object_to_register);
ChannelNode* channelz_channel = nullptr;
intptr_t uuid = ChannelzRegistry::RegisterChannelNode(channelz_channel);
EXPECT_GT(uuid, 0) << "First uuid chose must be greater than zero. Zero if "
"reserved according to "
"https://github.com/grpc/proposal/blob/master/"
"A14-channelz.md";
ChannelzRegistry::Unregister(uuid);
ChannelzRegistry::UnregisterChannelNode(uuid);
}
TEST(ChannelzRegistryTest, UuidsAreIncreasing) {
int object_to_register;
ChannelNode* channelz_channel = nullptr;
std::vector<intptr_t> uuids;
uuids.reserve(10);
for (int i = 0; i < 10; ++i) {
// reregister the same object. It's ok since we are just testing uuids
uuids.push_back(ChannelzRegistry::Register(&object_to_register));
uuids.push_back(ChannelzRegistry::RegisterChannelNode(channelz_channel));
}
for (size_t i = 1; i < uuids.size(); ++i) {
EXPECT_LT(uuids[i - 1], uuids[i]) << "Uuids must always be increasing";
@ -65,60 +67,36 @@ TEST(ChannelzRegistryTest, UuidsAreIncreasing) {
}
TEST(ChannelzRegistryTest, RegisterGetTest) {
int object_to_register = 42;
intptr_t uuid = ChannelzRegistry::Register(&object_to_register);
int* retrieved = ChannelzRegistry::Get<int>(uuid);
EXPECT_EQ(&object_to_register, retrieved);
}
TEST(ChannelzRegistryTest, MultipleTypeTest) {
int int_to_register = 42;
intptr_t int_uuid = ChannelzRegistry::Register(&int_to_register);
std::string str_to_register = "hello world";
intptr_t str_uuid = ChannelzRegistry::Register(&str_to_register);
int* retrieved_int = ChannelzRegistry::Get<int>(int_uuid);
std::string* retrieved_str = ChannelzRegistry::Get<std::string>(str_uuid);
EXPECT_EQ(&int_to_register, retrieved_int);
EXPECT_EQ(&str_to_register, retrieved_str);
// we hackily jam an intptr_t into this pointer to check for equality later
ChannelNode* channelz_channel = (ChannelNode*)42;
intptr_t uuid = ChannelzRegistry::RegisterChannelNode(channelz_channel);
ChannelNode* retrieved = ChannelzRegistry::GetChannelNode(uuid);
EXPECT_EQ(channelz_channel, retrieved);
}
TEST(ChannelzRegistryTest, RegisterManyItems) {
int object_to_register = 42;
// we hackily jam an intptr_t into this pointer to check for equality later
ChannelNode* channelz_channel = (ChannelNode*)42;
for (int i = 0; i < 100; i++) {
intptr_t uuid = ChannelzRegistry::Register(&object_to_register);
int* retrieved = ChannelzRegistry::Get<int>(uuid);
EXPECT_EQ(&object_to_register, retrieved);
intptr_t uuid = ChannelzRegistry::RegisterChannelNode(channelz_channel);
ChannelNode* retrieved = ChannelzRegistry::GetChannelNode(uuid);
EXPECT_EQ(channelz_channel, retrieved);
}
}
namespace {
class Foo {
public:
int bar;
};
} // namespace
TEST(ChannelzRegistryTest, CustomObjectTest) {
Foo* foo = New<Foo>();
foo->bar = 1024;
intptr_t uuid = ChannelzRegistry::Register(foo);
Foo* retrieved = ChannelzRegistry::Get<Foo>(uuid);
EXPECT_EQ(foo, retrieved);
Delete(foo);
}
TEST(ChannelzRegistryTest, NullIfNotPresentTest) {
int object_to_register = 42;
intptr_t uuid = ChannelzRegistry::Register(&object_to_register);
// we hackily jam an intptr_t into this pointer to check for equality later
ChannelNode* channelz_channel = (ChannelNode*)42;
intptr_t uuid = ChannelzRegistry::RegisterChannelNode(channelz_channel);
// try to pull out a uuid that does not exist.
int* nonexistant = ChannelzRegistry::Get<int>(uuid + 1);
ChannelNode* nonexistant = ChannelzRegistry::GetChannelNode(uuid + 1);
EXPECT_EQ(nonexistant, nullptr);
int* retrieved = ChannelzRegistry::Get<int>(uuid);
EXPECT_EQ(object_to_register, *retrieved);
EXPECT_EQ(&object_to_register, retrieved);
ChannelNode* retrieved = ChannelzRegistry::GetChannelNode(uuid);
EXPECT_EQ(channelz_channel, retrieved);
}
} // namespace testing
} // namespace channelz
} // namespace grpc_core
int main(int argc, char** argv) {

@ -67,17 +67,45 @@ grpc_json* GetJsonChild(grpc_json* parent, const char* key) {
return nullptr;
}
void ValidateJsonArraySize(grpc_json* json, const char* key,
size_t expected_size) {
grpc_json* arr = GetJsonChild(json, key);
if (expected_size == 0) {
ASSERT_EQ(arr, nullptr);
return;
}
ASSERT_NE(arr, nullptr);
ASSERT_EQ(arr->type, GRPC_JSON_ARRAY);
size_t count = 0;
for (grpc_json* child = arr->child; child != nullptr; child = child->next) {
++count;
}
EXPECT_EQ(count, expected_size);
}
void ValidateGetTopChannels(size_t expected_channels) {
char* json_str = ChannelzRegistry::GetTopChannels(0);
grpc::testing::ValidateGetTopChannelsResponseProtoJsonTranslation(json_str);
grpc_json* parsed_json = grpc_json_parse_string(json_str);
// This check will naturally have to change when we support pagination.
// tracked: https://github.com/grpc/grpc/issues/16019.
ValidateJsonArraySize(parsed_json, "channel", expected_channels);
grpc_json* end = GetJsonChild(parsed_json, "end");
ASSERT_NE(end, nullptr);
EXPECT_EQ(end->type, GRPC_JSON_TRUE);
grpc_json_destroy(parsed_json);
gpr_free(json_str);
}
class ChannelFixture {
public:
ChannelFixture(int max_trace_nodes) {
ChannelFixture(int max_trace_nodes = 0) {
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;
client_a[0] = grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE),
max_trace_nodes);
client_a[1] = grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), true);
grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a};
channel_ =
grpc_insecure_channel_create("fake_target", &client_args, nullptr);
@ -99,6 +127,10 @@ struct validate_channel_data_args {
void ValidateChildInteger(grpc_json* json, int64_t expect, const char* key) {
grpc_json* gotten_json = GetJsonChild(json, key);
if (expect == 0) {
ASSERT_EQ(gotten_json, nullptr);
return;
}
ASSERT_NE(gotten_json, nullptr);
int64_t gotten_number = (int64_t)strtol(gotten_json->value, nullptr, 0);
EXPECT_EQ(gotten_number, expect);
@ -115,7 +147,7 @@ void ValidateCounters(char* json_str, validate_channel_data_args args) {
}
void ValidateChannel(ChannelNode* channel, validate_channel_data_args args) {
char* json_str = channel->RenderJSON();
char* json_str = channel->RenderJsonString();
grpc::testing::ValidateChannelProtoJsonTranslation(json_str);
ValidateCounters(json_str, args);
gpr_free(json_str);
@ -141,9 +173,7 @@ TEST_P(ChannelzChannelTest, BasicChannel) {
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);
ValidateChannel(channelz_channel, {0, 0, 0});
}
TEST(ChannelzChannelTest, ChannelzDisabled) {
@ -199,6 +229,42 @@ TEST_P(ChannelzChannelTest, LastCallStartedMillis) {
EXPECT_NE(millis1, millis4);
}
TEST(ChannelzGetTopChannelsTest, BasicTest) {
grpc_core::ExecCtx exec_ctx;
ChannelFixture channel;
ValidateGetTopChannels(1);
}
TEST(ChannelzGetTopChannelsTest, NoChannelsTest) {
grpc_core::ExecCtx exec_ctx;
ValidateGetTopChannels(0);
}
TEST(ChannelzGetTopChannelsTest, ManyChannelsTest) {
grpc_core::ExecCtx exec_ctx;
ChannelFixture channels[10];
(void)channels; // suppress unused variable error
ValidateGetTopChannels(10);
}
TEST(ChannelzGetTopChannelsTest, InternalChannelTest) {
grpc_core::ExecCtx exec_ctx;
ChannelFixture channels[10];
(void)channels; // suppress unused variable error
// create an internal channel
grpc_arg client_a[2];
client_a[0] = grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), true);
client_a[1] = grpc_channel_arg_integer_create(
const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), true);
grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a};
grpc_channel* internal_channel =
grpc_insecure_channel_create("fake_target", &client_args, nullptr);
// The internal channel should not be returned from the request
ValidateGetTopChannels(10);
grpc_channel_destroy(internal_channel);
}
INSTANTIATE_TEST_CASE_P(ChannelzChannelTestSweep, ChannelzChannelTest,
::testing::Values(0, 1, 2, 6, 10, 15));

@ -209,27 +209,27 @@ static void test_channelz(grpc_end2end_test_config config) {
grpc_channel_get_channelz_node(f.client);
GPR_ASSERT(channelz_channel != nullptr);
char* json = channelz_channel->RenderJSON();
char* json = channelz_channel->RenderJsonString();
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\""));
// nothing is present yet
GPR_ASSERT(nullptr == strstr(json, "\"callsStarted\""));
GPR_ASSERT(nullptr == strstr(json, "\"callsFailed\""));
GPR_ASSERT(nullptr == strstr(json, "\"callsSucceeded\""));
gpr_free(json);
// one successful request
run_one_request(config, f, true);
json = channelz_channel->RenderJSON();
json = channelz_channel->RenderJsonString();
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();
json = channelz_channel->RenderJsonString();
GPR_ASSERT(json != nullptr);
gpr_log(GPR_INFO, "%s", json);
GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"2\""));
@ -264,7 +264,7 @@ static void test_channelz_with_channel_trace(grpc_end2end_test_config config) {
grpc_channel_get_channelz_node(f.client);
GPR_ASSERT(channelz_channel != nullptr);
char* json = channelz_channel->RenderJSON();
char* json = channelz_channel->RenderJsonString();
GPR_ASSERT(json != nullptr);
gpr_log(GPR_INFO, "%s", json);
GPR_ASSERT(nullptr != strstr(json, "\"trace\""));

@ -17,20 +17,32 @@
*/
#include "src/core/lib/gprpp/inlined_vector.h"
#include <grpc/support/log.h>
#include <gtest/gtest.h>
#include "src/core/lib/gprpp/memory.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
namespace {
template <typename Vector>
static void FillVector(Vector* v, int len, int start = 0) {
for (int i = 0; i < len; i++) {
v->push_back(i + start);
EXPECT_EQ(i + 1UL, v->size());
}
EXPECT_EQ(static_cast<size_t>(len), v->size());
EXPECT_LE(static_cast<size_t>(len), v->capacity());
}
} // namespace
TEST(InlinedVectorTest, CreateAndIterate) {
const int kNumElements = 9;
InlinedVector<int, 2> v;
EXPECT_TRUE(v.empty());
for (int i = 0; i < kNumElements; ++i) {
v.push_back(i);
}
FillVector(&v, kNumElements);
EXPECT_EQ(static_cast<size_t>(kNumElements), v.size());
EXPECT_FALSE(v.empty());
for (int i = 0; i < kNumElements; ++i) {
@ -42,9 +54,7 @@ TEST(InlinedVectorTest, CreateAndIterate) {
TEST(InlinedVectorTest, ValuesAreInlined) {
const int kNumElements = 5;
InlinedVector<int, 10> v;
for (int i = 0; i < kNumElements; ++i) {
v.push_back(i);
}
FillVector(&v, kNumElements);
EXPECT_EQ(static_cast<size_t>(kNumElements), v.size());
for (int i = 0; i < kNumElements; ++i) {
EXPECT_EQ(i, v[i]);
@ -71,19 +81,13 @@ TEST(InlinedVectorTest, ClearAndRepopulate) {
const int kNumElements = 10;
InlinedVector<int, 5> v;
EXPECT_EQ(0UL, v.size());
for (int i = 0; i < kNumElements; ++i) {
v.push_back(i);
EXPECT_EQ(i + 1UL, v.size());
}
FillVector(&v, kNumElements);
for (int i = 0; i < kNumElements; ++i) {
EXPECT_EQ(i, v[i]);
}
v.clear();
EXPECT_EQ(0UL, v.size());
for (int i = 0; i < kNumElements; ++i) {
v.push_back(kNumElements + i);
EXPECT_EQ(i + 1UL, v.size());
}
FillVector(&v, kNumElements, kNumElements);
for (int i = 0; i < kNumElements; ++i) {
EXPECT_EQ(kNumElements + i, v[i]);
}
@ -93,10 +97,7 @@ TEST(InlinedVectorTest, ConstIndexOperator) {
constexpr int kNumElements = 10;
InlinedVector<int, 5> v;
EXPECT_EQ(0UL, v.size());
for (int i = 0; i < kNumElements; ++i) {
v.push_back(i);
EXPECT_EQ(i + 1UL, v.size());
}
FillVector(&v, kNumElements);
// The following lambda function is exceptionally allowed to use an anonymous
// capture due to the erroneous behavior of the MSVC compiler, that refuses to
// capture the kNumElements constexpr, something allowed by the standard.
@ -108,6 +109,161 @@ TEST(InlinedVectorTest, ConstIndexOperator) {
const_func(v);
}
// the following constants and typedefs are used for copy/move
// construction/assignment
const size_t kInlinedLength = 8;
typedef InlinedVector<int, kInlinedLength> IntVec8;
const size_t kInlinedFillSize = kInlinedLength - 1;
const size_t kAllocatedFillSize = kInlinedLength + 1;
TEST(InlinedVectorTest, CopyConstructerInlined) {
IntVec8 original;
FillVector(&original, kInlinedFillSize);
IntVec8 copy_constructed(original);
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], copy_constructed[i]);
}
}
TEST(InlinedVectorTest, CopyConstructerAllocated) {
IntVec8 original;
FillVector(&original, kAllocatedFillSize);
IntVec8 copy_constructed(original);
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], copy_constructed[i]);
}
}
TEST(InlinedVectorTest, CopyAssignementInlinedInlined) {
IntVec8 original;
FillVector(&original, kInlinedFillSize);
IntVec8 copy_assigned;
FillVector(&copy_assigned, kInlinedFillSize, 99);
copy_assigned = original;
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], copy_assigned[i]);
}
}
TEST(InlinedVectorTest, CopyAssignementInlinedAllocated) {
IntVec8 original;
FillVector(&original, kInlinedFillSize);
IntVec8 copy_assigned;
FillVector(&copy_assigned, kAllocatedFillSize, 99);
copy_assigned = original;
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], copy_assigned[i]);
}
}
TEST(InlinedVectorTest, CopyAssignementAllocatedInlined) {
IntVec8 original;
FillVector(&original, kAllocatedFillSize);
IntVec8 copy_assigned;
FillVector(&copy_assigned, kInlinedFillSize, 99);
copy_assigned = original;
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], copy_assigned[i]);
}
}
TEST(InlinedVectorTest, CopyAssignementAllocatedAllocated) {
IntVec8 original;
FillVector(&original, kAllocatedFillSize);
IntVec8 copy_assigned;
FillVector(&copy_assigned, kAllocatedFillSize, 99);
copy_assigned = original;
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], copy_assigned[i]);
}
}
TEST(InlinedVectorTest, MoveConstructorInlined) {
IntVec8 original;
FillVector(&original, kInlinedFillSize);
IntVec8 tmp(original);
auto* old_data = tmp.data();
IntVec8 move_constructed(std::move(tmp));
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], move_constructed[i]);
}
// original data was inlined so it should have been copied, not moved.
EXPECT_NE(move_constructed.data(), old_data);
}
TEST(InlinedVectorTest, MoveConstructorAllocated) {
IntVec8 original;
FillVector(&original, kAllocatedFillSize);
IntVec8 tmp(original);
auto* old_data = tmp.data();
IntVec8 move_constructed(std::move(tmp));
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], move_constructed[i]);
}
// original data was allocated, so it should been moved, not copied
EXPECT_EQ(move_constructed.data(), old_data);
}
TEST(InlinedVectorTest, MoveAssignmentInlinedInlined) {
IntVec8 original;
FillVector(&original, kInlinedFillSize);
IntVec8 move_assigned;
FillVector(&move_assigned, kInlinedFillSize, 99); // Add dummy elements
IntVec8 tmp(original);
auto* old_data = tmp.data();
move_assigned = std::move(tmp);
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], move_assigned[i]);
}
// original data was inlined so it should have been copied, not moved.
EXPECT_NE(move_assigned.data(), old_data);
}
TEST(InlinedVectorTest, MoveAssignmentInlinedAllocated) {
IntVec8 original;
FillVector(&original, kInlinedFillSize);
IntVec8 move_assigned;
FillVector(&move_assigned, kAllocatedFillSize, 99); // Add dummy elements
IntVec8 tmp(original);
auto* old_data = tmp.data();
move_assigned = std::move(tmp);
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], move_assigned[i]);
}
// original data was inlined so it should have been copied, not moved.
EXPECT_NE(move_assigned.data(), old_data);
}
TEST(InlinedVectorTest, MoveAssignmentAllocatedInlined) {
IntVec8 original;
FillVector(&original, kAllocatedFillSize);
IntVec8 move_assigned;
FillVector(&move_assigned, kInlinedFillSize, 99); // Add dummy elements
IntVec8 tmp(original);
auto* old_data = tmp.data();
move_assigned = std::move(tmp);
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], move_assigned[i]);
}
// original data was allocated so it should have been moved, not copied.
EXPECT_EQ(move_assigned.data(), old_data);
}
TEST(InlinedVectorTest, MoveAssignmentAllocatedAllocated) {
IntVec8 original;
FillVector(&original, kAllocatedFillSize);
IntVec8 move_assigned;
FillVector(&move_assigned, kAllocatedFillSize, 99); // Add dummy elements
IntVec8 tmp(original);
auto* old_data = tmp.data();
move_assigned = std::move(tmp);
for (size_t i = 0; i < original.size(); ++i) {
EXPECT_EQ(original[i], move_assigned[i]);
}
// original data was allocated so it should have been moved, not copied.
EXPECT_EQ(move_assigned.data(), old_data);
}
} // namespace testing
} // namespace grpc_core

@ -64,13 +64,17 @@ void VaidateProtoJsonTranslation(char* json_c_str) {
} // namespace
void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str) {
VaidateProtoJsonTranslation<grpc::channelz::v1::ChannelTrace>(
tracer_json_c_str);
void ValidateChannelTraceProtoJsonTranslation(char* json_c_str) {
VaidateProtoJsonTranslation<grpc::channelz::v1::ChannelTrace>(json_c_str);
}
void ValidateChannelProtoJsonTranslation(char* channel_json_c_str) {
VaidateProtoJsonTranslation<grpc::channelz::v1::Channel>(channel_json_c_str);
void ValidateChannelProtoJsonTranslation(char* json_c_str) {
VaidateProtoJsonTranslation<grpc::channelz::v1::Channel>(json_c_str);
}
void ValidateGetTopChannelsResponseProtoJsonTranslation(char* json_c_str) {
VaidateProtoJsonTranslation<grpc::channelz::v1::GetTopChannelsResponse>(
json_c_str);
}
} // namespace testing

@ -22,8 +22,9 @@
namespace grpc {
namespace testing {
void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str);
void ValidateChannelProtoJsonTranslation(char* channel_json_c_str);
void ValidateChannelTraceProtoJsonTranslation(char* json_c_str);
void ValidateChannelProtoJsonTranslation(char* json_c_str);
void ValidateGetTopChannelsResponseProtoJsonTranslation(char* json_c_str);
} // namespace testing
} // namespace grpc

@ -1 +1 @@
Subproject commit 70ef9596bbcc11353b9bb8d4e91478694dd21439
Subproject commit b29b21a81b32ec273f118f589f46d56ad3332420

@ -1 +1 @@
Subproject commit dcd3e6e6ecddf059adb48fca45bc7346a108bdd9
Subproject commit 8149b351bf797bd80e063787886b7618f508e451

@ -39,11 +39,17 @@ pip install google-api-python-client==1.6.7 --user python
export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json
# If this is a PR using RUN_TESTS_FLAGS var, then add flags to filter tests
if [ -n "$KOKORO_GITHUB_PULL_REQUEST_NUMBER" ] && [ -n "$RUN_TESTS_FLAGS" ]; then
if [ -n "$KOKORO_GITHUB_PULL_REQUEST_NUMBER" ]; then
set +x
brew update
brew install jq
ghprbTargetBranch=$(curl -s https://api.github.com/repos/grpc/grpc/pulls/$KOKORO_GITHUB_PULL_REQUEST_NUMBER | jq -r .base.ref)
export RUN_TESTS_FLAGS="$RUN_TESTS_FLAGS --filter_pr_tests --base_branch origin/$ghprbTargetBranch"
# TODO(matt-kwong): rename this to GITHUB_OAUTH_TOKEN after Jenkins deprecation
export JENKINS_OAUTH_TOKEN=$(cat ${KOKORO_GFILE_DIR}/oauth_token.txt)
export ghprbPullId=$KOKORO_GITHUB_PULL_REQUEST_NUMBER
set -x
fi
set +ex # rvm script is very verbose and exits with errorcode

@ -0,0 +1,27 @@
#!/usr/bin/env bash
# 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.
#
# This script is invoked by Jenkins and runs a diff on the microbenchmarks
set -ex
# List of benchmarks that provide good signal for analyzing performance changes in pull requests
# Enter the gRPC repo root
cd $(dirname $0)/../../..
source tools/internal_ci/helper_scripts/prepare_build_macos_rc
tools/profiling/ios_bin/binary_size.py \
-d origin/$ghprbTargetBranch

@ -0,0 +1,26 @@
# 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.
# Config file for the internal CI (in protobuf text format)
# Location of the continuous shell script in repository.
build_file: "grpc/tools/internal_ci/macos/grpc_ios_binary_size.sh"
timeout_mins: 60
gfile_resources: "/bigstore/grpc-testing-secrets/github_credentials/oauth_token.txt"
action {
define_artifacts {
regex: "**/*sponge_log.xml"
regex: "github/grpc/reports/**"
}
}

@ -252,6 +252,12 @@ LANG_RELEASE_MATRIX = {
{
'v1.10.0': None
},
{
'v1.11.3': None
},
{
'v1.12.4': None
},
],
'ruby': [
{

@ -28,6 +28,9 @@ sys.path.append(
os.path.dirname(sys.argv[0]), '..', '..', 'run_tests', 'python_utils'))
import comment_on_pr
# Only show diff 1KB or greater
diff_threshold = 1000
size_labels = ('Core', 'ObjC', 'BoringSSL', 'Protobuf', 'Total')
argp = argparse.ArgumentParser(
@ -104,22 +107,40 @@ for frameworks in [False, True]:
subprocess.check_call(['git', 'checkout', where_am_i])
subprocess.check_call(['git', 'submodule', 'update'])
text += ('****************FRAMEWORKS*****************\n'
if frameworks else '******************STATIC*******************\n')
text += ('***************FRAMEWORKS****************\n'
if frameworks else '*****************STATIC******************\n')
row_format = "{:>10}{:>15}{:>15}" + '\n'
text += row_format.format('New size', '', 'Old size')
for i in range(0, len(size_labels)):
if old_size == None:
diff_sign = ' '
elif new_size[i] == old_size[i]:
diff_sign = ' (=)'
elif new_size[i] > old_size[i]:
if old_size == None:
for i in range(0, len(size_labels)):
text += ('\n'
if i == len(size_labels) - 1 else '') + row_format.format(
'{:,}'.format(new_size[i]), size_labels[i], '')
else:
has_diff = False
for i in range(0, len(size_labels) - 1):
if abs(new_size[i] - old_size[i]) < diff_threshold:
continue
if new_size[i] > old_size[i]:
diff_sign = ' (>)'
else:
diff_sign = ' (<)'
has_diff = True
text += row_format.format('{:,}'.format(new_size[i]),
size_labels[i] + diff_sign, '{:,}'.format(
old_size[i]))
i = len(size_labels) - 1
if new_size[i] > old_size[i]:
diff_sign = ' (>)'
else:
elif new_size[i] < old_size[i]:
diff_sign = ' (<)'
text += ('\n' if i == len(size_labels) - 1 else '') + row_format.format(
else:
diff_sign = ' (=)'
text += ('\n' if has_diff else '') + row_format.format(
'{:,}'.format(new_size[i]), size_labels[i] + diff_sign,
'{:,}'.format(old_size[i]) if old_size != None else '')
'{:,}'.format(old_size[i]))
if not has_diff:
text += '\n No significant differences in binary sizes\n'
text += '\n'
print text

@ -29,8 +29,8 @@ cat << EOF | awk '{ print $1 }' | sort > "$want_submodules"
cc4bed2d74f7c8717e31f9579214ab52a9c9c610 third_party/abseil-cpp (cc4bed2)
5b7683f49e1e9223cf9927b24f6fd3d6bd82e3f8 third_party/benchmark (v1.2.0)
73594cde8c9a52a102c4341c244c833aa61b9c06 third_party/bloaty (remotes/origin/wide-14-g73594cd)
70ef9596bbcc11353b9bb8d4e91478694dd21439 third_party/boringssl (fips-20170615-704-g70ef959)
dcd3e6e6ecddf059adb48fca45bc7346a108bdd9 third_party/boringssl-with-bazel (version_for_cocoapods_10.0-369-gdcd3e6e)
b29b21a81b32ec273f118f589f46d56ad3332420 third_party/boringssl (remotes/origin/chromium-stable)
8149b351bf797bd80e063787886b7618f508e451 third_party/boringssl-with-bazel (version_for_cocoapods_10.0-434-g8149b351)
3be1924221e1326df520f8498d704a5c4c8d0cce third_party/cares/cares (cares-1_13_0)
30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e third_party/gflags (v2.2.0-5-g30dbc81)
ec44c6c1675c25b9827aacd08c02433cccde7780 third_party/googletest (release-1.8.0)

Loading…
Cancel
Save