diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index de7c39b1834..dd8a5d7d5f8 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -294,9 +294,6 @@ GRPCAPI char* grpc_channel_get_trace(grpc_channel* channel); later time. */ GRPCAPI intptr_t grpc_channel_get_uuid(grpc_channel* channel); -/** channelz support */ -GRPCAPI char* grpc_channelz_get_channel(intptr_t channel_id); - /** Error handling for grpc_call Most grpc_call functions return a grpc_error. If the error is not GRPC_OK then the operation failed due to some unsatisfied precondition. diff --git a/src/core/lib/channel/channel_trace.cc b/src/core/lib/channel/channel_trace.cc index efa034a1887..c82e42d5459 100644 --- a/src/core/lib/channel/channel_trace.cc +++ b/src/core/lib/channel/channel_trace.cc @@ -28,7 +28,6 @@ #include #include -#include "src/core/lib/channel/channelz_registry.h" #include "src/core/lib/channel/status_util.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/gpr/useful.h" @@ -63,15 +62,13 @@ ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data) ChannelTrace::TraceEvent::~TraceEvent() { grpc_slice_unref_internal(data_); } ChannelTrace::ChannelTrace(size_t max_events) - : channel_uuid_(-1), - num_events_logged_(0), + : num_events_logged_(0), list_size_(0), max_list_size_(max_events), head_trace_(nullptr), tail_trace_(nullptr) { if (max_list_size_ == 0) return; // tracing is disabled if max_events == 0 gpr_mu_init(&tracer_mu_); - channel_uuid_ = ChannelzRegistry::Register(this); time_created_ = grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(), GPR_CLOCK_REALTIME); } @@ -84,12 +81,9 @@ ChannelTrace::~ChannelTrace() { it = it->next(); Delete(to_free); } - ChannelzRegistry::Unregister(channel_uuid_); gpr_mu_destroy(&tracer_mu_); } -intptr_t ChannelTrace::GetUuid() const { return channel_uuid_; } - void ChannelTrace::AddTraceEventHelper(TraceEvent* new_trace_event) { ++num_events_logged_; // first event case @@ -212,7 +206,7 @@ void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const { } } -char* ChannelTrace::RenderTrace() const { +grpc_json* ChannelTrace::RenderJSON() const { if (!max_list_size_) return nullptr; // tracing is disabled if max_events == 0 grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT); @@ -235,6 +229,12 @@ char* ChannelTrace::RenderTrace() const { it->RenderTraceEvent(json_iterator); it = it->next(); } + return json; +} + +char* ChannelTrace::RenderTrace() const { + grpc_json* json = RenderJSON(); + if (json == nullptr) return nullptr; char* json_str = grpc_json_dump_to_string(json, 0); grpc_json_destroy(json); return json_str; diff --git a/src/core/lib/channel/channel_trace.h b/src/core/lib/channel/channel_trace.h index 687c6bc0630..b1224bb96c4 100644 --- a/src/core/lib/channel/channel_trace.h +++ b/src/core/lib/channel/channel_trace.h @@ -40,9 +40,6 @@ class ChannelTrace { ChannelTrace(size_t max_events); ~ChannelTrace(); - // returns the tracer's uuid - intptr_t GetUuid() const; - enum Severity { Unset = 0, // never to be used Info, // we start at 1 to avoid using proto default values @@ -72,8 +69,13 @@ class ChannelTrace { Severity severity, grpc_slice data, RefCountedPtr referenced_tracer); - // Returns the tracing data rendered as a grpc json string. - // The string is owned by the caller and must be freed. + // Creates and returns the raw grpc_json object, so a parent channelz + // object may incorporate the json before rendering. + grpc_json* RenderJSON() const; + + // Returns the tracing data rendered as a grpc json string. The string + // is owned by the caller and must be freed. This is used for testing only + // so that we may unit test ChannelTrace in isolation. char* RenderTrace() const; private: @@ -117,7 +119,6 @@ class ChannelTrace { void AddTraceEventHelper(TraceEvent* new_trace_event); gpr_mu tracer_mu_; - intptr_t channel_uuid_; uint64_t num_events_logged_; size_t list_size_; size_t max_list_size_; diff --git a/src/core/lib/channel/channelz.cc b/src/core/lib/channel/channelz.cc index cf7545621e5..5c3dc97e690 100644 --- a/src/core/lib/channel/channelz.cc +++ b/src/core/lib/channel/channelz.cc @@ -39,9 +39,6 @@ #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/error_utils.h" -// TODO(ncteisen): actually implement this -char* grpc_channelz_get_channel(intptr_t channel_id) { return nullptr; } - namespace grpc_core { namespace channelz { @@ -82,9 +79,9 @@ char* fmt_time(gpr_timespec tm) { // TODO(ncteisen); move this to json library grpc_json* add_num_str(grpc_json* parent, grpc_json* it, const char* name, - uint64_t num) { + int64_t num) { char* num_str; - gpr_asprintf(&num_str, "%" PRIu64, num); + gpr_asprintf(&num_str, "%" PRId64, num); return grpc_json_create_child(it, parent, name, num_str, GRPC_JSON_STRING, true); } @@ -96,10 +93,13 @@ Channel::Channel(grpc_channel* channel, size_t channel_tracer_max_nodes) trace_.Init(channel_tracer_max_nodes); target_ = grpc_channel_get_target(channel_); channel_uuid_ = ChannelzRegistry::Register(this); + last_call_started_timestamp_ = + grpc_millis_to_timespec(ExecCtx::Get()->Now(), GPR_CLOCK_REALTIME); } Channel::~Channel() { gpr_free(const_cast(target_)); + trace_.Destroy(); ChannelzRegistry::Unregister(channel_uuid_); } @@ -125,7 +125,7 @@ char* Channel::RenderJSON() { // create and fill the ref child json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, - GRPC_JSON_OBJECT, true); + GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = add_num_str(json, json_iterator, "channelId", channel_uuid_); @@ -134,33 +134,55 @@ char* Channel::RenderJSON() { json = top_level_json; json_iterator = nullptr; - // create and fill the data child - json_iterator = grpc_json_create_child(json_iterator, json, "data", nullptr, - GRPC_JSON_OBJECT, true); - json = json_iterator; + // create and fill the data child. + grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, + GRPC_JSON_OBJECT, false); + + json = data; json_iterator = nullptr; - 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_); - json_iterator = grpc_json_create_child( - json_iterator, json, "lastCallStartedTimestamp", - fmt_time(last_call_started_timestamp_), GRPC_JSON_STRING, true); + + // create and fill the connectivity state child. + grpc_connectivity_state connectivity_state = GetConnectivityState(); + json_iterator = grpc_json_create_child(json_iterator, json, "state", nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + grpc_json_create_child(nullptr, json, "state", + grpc_connectivity_state_name(connectivity_state), + GRPC_JSON_STRING, false); + + // reset the parent to be the data object. + json = data; json_iterator = grpc_json_create_child(json_iterator, json, "target", target_, GRPC_JSON_STRING, false); - grpc_connectivity_state connectivity_state = GetConnectivityState(); - json_iterator = - grpc_json_create_child(json_iterator, json, "state", - grpc_connectivity_state_name(connectivity_state), - GRPC_JSON_STRING, false); - char* trace = trace_->RenderTrace(); + + // fill in the channel trace if applicable + grpc_json* trace = trace_->RenderJSON(); if (trace != nullptr) { - json_iterator = grpc_json_create_child(json_iterator, json, "trace", trace, - GRPC_JSON_STRING, true); + // we manuall link up and fill the child since it was created for us in + // ChannelTrace::RenderJSON + json_iterator = grpc_json_link_child(json, trace, json_iterator); + trace->parent = json; + trace->value = nullptr; + trace->key = "trace"; + trace->owns_value = false; } + // reset the parent to be the data object. + json = data; + json_iterator = nullptr; + + // We use -1 as sentinel values since proto default value for integers is + // zero, and the confuses the parser into thinking the value weren't present + json_iterator = add_num_str(json, json_iterator, "callsStarted", + calls_started_ ? calls_started_ : -1); + json_iterator = add_num_str(json, json_iterator, "callsSucceeded", + calls_succeeded_ ? calls_succeeded_ : -1); + json_iterator = add_num_str(json, json_iterator, "callsFailed", + calls_failed_ ? calls_failed_ : -1); + json_iterator = grpc_json_create_child( + json_iterator, json, "lastCallStartedTimestamp", + fmt_time(last_call_started_timestamp_), 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); diff --git a/src/core/lib/channel/channelz.h b/src/core/lib/channel/channelz.h index 7cea00392f5..63b90e90a0c 100644 --- a/src/core/lib/channel/channelz.h +++ b/src/core/lib/channel/channelz.h @@ -59,9 +59,9 @@ class Channel : public RefCounted { bool channel_destroyed_ = false; grpc_channel* channel_; const char* target_; - uint64_t calls_started_ = 0; - uint64_t calls_succeeded_ = 0; - uint64_t calls_failed_ = 0; + int64_t calls_started_ = 0; + int64_t calls_succeeded_ = 0; + int64_t calls_failed_ = 0; gpr_timespec last_call_started_timestamp_; intptr_t channel_uuid_; ManualConstructor trace_; diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc index 22760f4ea93..be9869b08c4 100644 --- a/src/core/lib/surface/channel.cc +++ b/src/core/lib/surface/channel.cc @@ -146,7 +146,6 @@ grpc_channel* grpc_channel_create_with_builder( } grpc_channel_args_destroy(args); - channel_tracer_max_nodes = 10; channel->channelz_channel = grpc_core::MakeRefCounted( channel, channel_tracer_max_nodes); @@ -201,7 +200,7 @@ grpc_core::channelz::Channel* grpc_channel_get_channelz_channel( } intptr_t grpc_channel_get_uuid(grpc_channel* channel) { - return channel->channelz_channel->Trace()->GetUuid(); + return channel->channelz_channel->channel_uuid(); } grpc_channel* grpc_channel_create(const char* target, @@ -430,6 +429,8 @@ void grpc_channel_destroy(grpc_channel* channel) { GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Destroyed"); elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); elem->filter->start_transport_op(elem, op); + channel->channelz_channel->set_channel_destroyed(); + channel->channelz_channel.reset(); GRPC_CHANNEL_INTERNAL_UNREF(channel, "channel"); } diff --git a/test/core/channel/BUILD b/test/core/channel/BUILD index c336688209d..81c43531293 100644 --- a/test/core/channel/BUILD +++ b/test/core/channel/BUILD @@ -84,13 +84,8 @@ grpc_cc_test( ) grpc_cc_test( -<<<<<<< HEAD - name = "channelz_registry_test", - srcs = ["channelz_registry_test.cc"], -======= name = "channelz_test", srcs = ["channelz_test.cc"], ->>>>>>> Add channelz test language = "C++", deps = [ "//:gpr", @@ -98,10 +93,23 @@ grpc_cc_test( "//:grpc++", "//test/core/util:gpr_test_util", "//test/core/util:grpc_test_util", -<<<<<<< HEAD -======= "//test/cpp/util:channel_trace_proto_helper", ->>>>>>> Add channelz test + ], + external_deps = [ + "gtest", + ], +) + +grpc_cc_test( + name = "channelz_registry_test", + srcs = ["channelz_registry_test.cc"], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//:grpc++", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", ], external_deps = [ "gtest", diff --git a/test/core/channel/channel_trace_test.cc b/test/core/channel/channel_trace_test.cc index 9befdb4fa37..c8619be0074 100644 --- a/test/core/channel/channel_trace_test.cc +++ b/test/core/channel/channel_trace_test.cc @@ -25,6 +25,7 @@ #include #include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/channel/channelz.h" #include "src/core/lib/channel/channelz_registry.h" #include "src/core/lib/gpr/useful.h" #include "src/core/lib/iomgr/exec_ctx.h" @@ -39,6 +40,7 @@ #include namespace grpc_core { +namespace channelz { namespace testing { namespace { @@ -77,13 +79,13 @@ void ValidateChannelTraceData(grpc_json* json, ValidateJsonArraySize(json, "events", actual_num_events_expected); } -void AddSimpleTrace(RefCountedPtr tracer) { +void AddSimpleTrace(ChannelTrace* tracer) { tracer->AddTraceEvent(ChannelTrace::Severity::Info, grpc_slice_from_static_string("simple trace")); } // checks for the existence of all the required members of the tracer. -void ValidateChannelTrace(RefCountedPtr tracer, +void ValidateChannelTrace(ChannelTrace* tracer, size_t expected_num_event_logged, size_t max_nodes) { if (!max_nodes) return; char* json_str = tracer->RenderTrace(); @@ -95,16 +97,26 @@ void ValidateChannelTrace(RefCountedPtr tracer, gpr_free(json_str); } -void ValidateTraceDataMatchedUuidLookup(RefCountedPtr tracer) { - intptr_t uuid = tracer->GetUuid(); - if (uuid == -1) return; // Doesn't make sense to lookup if tracing disabled - char* tracer_json_str = tracer->RenderTrace(); - ChannelTrace* uuid_lookup = ChannelzRegistry::Get(uuid); - char* uuid_lookup_json_str = uuid_lookup->RenderTrace(); - EXPECT_EQ(strcmp(tracer_json_str, uuid_lookup_json_str), 0); - gpr_free(tracer_json_str); - gpr_free(uuid_lookup_json_str); -} +class ChannelFixture { + public: + ChannelFixture(int max_trace_nodes) { + grpc_arg client_a; + client_a.type = GRPC_ARG_INTEGER; + client_a.key = + const_cast(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE); + client_a.value.integer = max_trace_nodes; + grpc_channel_args client_args = {1, &client_a}; + channel_ = + grpc_insecure_channel_create("fake_target", &client_args, nullptr); + } + + ~ChannelFixture() { grpc_channel_destroy(channel_); } + + grpc_channel* channel() { return channel_; } + + private: + grpc_channel* channel_; +}; } // anonymous namespace @@ -114,25 +126,22 @@ class ChannelTracerTest : public ::testing::TestWithParam {}; // lookups by uuid. TEST_P(ChannelTracerTest, BasicTest) { grpc_core::ExecCtx exec_ctx; - RefCountedPtr tracer = MakeRefCounted(GetParam()); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - ValidateTraceDataMatchedUuidLookup(tracer); - tracer->AddTraceEvent(ChannelTrace::Severity::Info, - grpc_slice_from_static_string("trace three")); - tracer->AddTraceEvent(ChannelTrace::Severity::Error, - grpc_slice_from_static_string("trace four error")); - ValidateChannelTrace(tracer, 4, GetParam()); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - ValidateChannelTrace(tracer, 6, GetParam()); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - ValidateChannelTrace(tracer, 10, GetParam()); - ValidateTraceDataMatchedUuidLookup(tracer); - tracer.reset(nullptr); + ChannelTrace tracer(GetParam()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + tracer.AddTraceEvent(ChannelTrace::Severity::Info, + grpc_slice_from_static_string("trace three")); + tracer.AddTraceEvent(ChannelTrace::Severity::Error, + grpc_slice_from_static_string("trace four error")); + ValidateChannelTrace(&tracer, 4, GetParam()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + ValidateChannelTrace(&tracer, 6, GetParam()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + ValidateChannelTrace(&tracer, 10, GetParam()); } // Tests more complex functionality, like a parent channel tracking @@ -140,42 +149,43 @@ TEST_P(ChannelTracerTest, BasicTest) { // and this function will both hold refs to the subchannel. TEST_P(ChannelTracerTest, ComplexTest) { grpc_core::ExecCtx exec_ctx; - RefCountedPtr tracer = MakeRefCounted(GetParam()); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - RefCountedPtr sc1 = MakeRefCounted(GetParam()); - tracer->AddTraceEventReferencingSubchannel( + ChannelTrace tracer(GetParam()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + ChannelFixture channel1(GetParam()); + RefCountedPtr sc1 = + MakeRefCounted(channel1.channel(), GetParam()); + tracer.AddTraceEventReferencingSubchannel( ChannelTrace::Severity::Info, grpc_slice_from_static_string("subchannel one created"), sc1); - ValidateChannelTrace(tracer, 3, GetParam()); - AddSimpleTrace(sc1); - AddSimpleTrace(sc1); - AddSimpleTrace(sc1); - ValidateChannelTrace(sc1, 3, GetParam()); - AddSimpleTrace(sc1); - AddSimpleTrace(sc1); - AddSimpleTrace(sc1); - ValidateChannelTrace(sc1, 6, GetParam()); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - ValidateChannelTrace(tracer, 5, GetParam()); - ValidateTraceDataMatchedUuidLookup(tracer); - RefCountedPtr sc2 = MakeRefCounted(GetParam()); - tracer->AddTraceEventReferencingChannel( + ValidateChannelTrace(&tracer, 3, GetParam()); + AddSimpleTrace(sc1->Trace()); + AddSimpleTrace(sc1->Trace()); + AddSimpleTrace(sc1->Trace()); + ValidateChannelTrace(sc1->Trace(), 3, GetParam()); + AddSimpleTrace(sc1->Trace()); + AddSimpleTrace(sc1->Trace()); + AddSimpleTrace(sc1->Trace()); + ValidateChannelTrace(sc1->Trace(), 6, GetParam()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + ValidateChannelTrace(&tracer, 5, GetParam()); + ChannelFixture channel2(GetParam()); + RefCountedPtr sc2 = + MakeRefCounted(channel2.channel(), GetParam()); + tracer.AddTraceEventReferencingChannel( ChannelTrace::Severity::Info, grpc_slice_from_static_string("LB channel two created"), sc2); - tracer->AddTraceEventReferencingSubchannel( + tracer.AddTraceEventReferencingSubchannel( ChannelTrace::Severity::Warning, grpc_slice_from_static_string("subchannel one inactive"), sc1); - ValidateChannelTrace(tracer, 7, GetParam()); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - ValidateTraceDataMatchedUuidLookup(tracer); - tracer.reset(nullptr); + ValidateChannelTrace(&tracer, 7, GetParam()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); sc1.reset(nullptr); sc2.reset(nullptr); } @@ -185,39 +195,44 @@ TEST_P(ChannelTracerTest, ComplexTest) { // gets deleted. TEST_P(ChannelTracerTest, TestNesting) { grpc_core::ExecCtx exec_ctx; - RefCountedPtr tracer = MakeRefCounted(GetParam()); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - ValidateChannelTrace(tracer, 2, GetParam()); - RefCountedPtr sc1 = MakeRefCounted(GetParam()); - tracer->AddTraceEventReferencingChannel( + ChannelTrace tracer(GetParam()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + ValidateChannelTrace(&tracer, 2, GetParam()); + ChannelFixture channel1(GetParam()); + RefCountedPtr sc1 = + MakeRefCounted(channel1.channel(), GetParam()); + tracer.AddTraceEventReferencingChannel( ChannelTrace::Severity::Info, grpc_slice_from_static_string("subchannel one created"), sc1); - ValidateChannelTrace(tracer, 3, GetParam()); - AddSimpleTrace(sc1); - RefCountedPtr conn1 = MakeRefCounted(GetParam()); + ValidateChannelTrace(&tracer, 3, GetParam()); + AddSimpleTrace(sc1->Trace()); + ChannelFixture channel2(GetParam()); + RefCountedPtr conn1 = + MakeRefCounted(channel2.channel(), GetParam()); // nesting one level deeper. - sc1->AddTraceEventReferencingSubchannel( + sc1->Trace()->AddTraceEventReferencingSubchannel( ChannelTrace::Severity::Info, grpc_slice_from_static_string("connection one created"), conn1); - ValidateChannelTrace(tracer, 3, GetParam()); - AddSimpleTrace(conn1); - AddSimpleTrace(tracer); - AddSimpleTrace(tracer); - ValidateChannelTrace(tracer, 5, GetParam()); - ValidateChannelTrace(conn1, 1, GetParam()); - RefCountedPtr sc2 = MakeRefCounted(GetParam()); - tracer->AddTraceEventReferencingSubchannel( + ValidateChannelTrace(&tracer, 3, GetParam()); + AddSimpleTrace(conn1->Trace()); + AddSimpleTrace(&tracer); + AddSimpleTrace(&tracer); + ValidateChannelTrace(&tracer, 5, GetParam()); + ValidateChannelTrace(conn1->Trace(), 1, GetParam()); + ChannelFixture channel3(GetParam()); + RefCountedPtr sc2 = + MakeRefCounted(channel3.channel(), GetParam()); + tracer.AddTraceEventReferencingSubchannel( ChannelTrace::Severity::Info, grpc_slice_from_static_string("subchannel two created"), sc2); // this trace should not get added to the parents children since it is already // present in the tracer. - tracer->AddTraceEventReferencingChannel( + tracer.AddTraceEventReferencingChannel( ChannelTrace::Severity::Warning, grpc_slice_from_static_string("subchannel one inactive"), sc1); - AddSimpleTrace(tracer); - ValidateChannelTrace(tracer, 8, GetParam()); - tracer.reset(nullptr); + AddSimpleTrace(&tracer); + ValidateChannelTrace(&tracer, 8, GetParam()); sc1.reset(nullptr); sc2.reset(nullptr); conn1.reset(nullptr); @@ -227,6 +242,7 @@ INSTANTIATE_TEST_CASE_P(ChannelTracerTestSweep, ChannelTracerTest, ::testing::Values(0, 1, 2, 6, 10, 15)); } // namespace testing +} // namespace channelz } // namespace grpc_core int main(int argc, char** argv) { diff --git a/test/core/channel/channelz_test.cc b/test/core/channel/channelz_test.cc index 503bb9065ba..c4606426095 100644 --- a/test/core/channel/channelz_test.cc +++ b/test/core/channel/channelz_test.cc @@ -25,26 +25,170 @@ #include #include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/channel/channelz.h" #include "src/core/lib/channel/channelz_registry.h" #include "src/core/lib/gpr/useful.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/json/json.h" +#include "src/core/lib/surface/channel.h" #include "test/core/util/test_config.h" #include "test/cpp/util/channel_trace_proto_helper.h" -// remove me #include #include #include namespace grpc_core { +namespace channelz { namespace testing { -namespace {} // anonymous namespace +namespace { -TEST(ChannelzTest, Channel) {} +grpc_json* GetJsonChild(grpc_json* parent, const char* key) { + EXPECT_NE(parent, nullptr); + for (grpc_json* child = parent->child; child != nullptr; + child = child->next) { + if (child->key != nullptr && strcmp(child->key, key) == 0) return child; + } + return nullptr; +} + +class ChannelFixture { + public: + ChannelFixture(int max_trace_nodes) { + grpc_arg client_a; + client_a.type = GRPC_ARG_INTEGER; + client_a.key = + const_cast(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE); + client_a.value.integer = max_trace_nodes; + grpc_channel_args client_args = {1, &client_a}; + channel_ = + grpc_insecure_channel_create("fake_target", &client_args, nullptr); + } + + ~ChannelFixture() { grpc_channel_destroy(channel_); } + + grpc_channel* channel() { return channel_; } + + private: + grpc_channel* channel_; +}; + +struct validate_channel_data_args { + int64_t calls_started; + int64_t calls_failed; + int64_t calls_succeeded; +}; + +void ValidateChildInteger(grpc_json* json, int64_t expect, const char* key) { + grpc_json* gotten_json = GetJsonChild(json, key); + EXPECT_NE(gotten_json, nullptr); + int64_t gotten_number = (int64_t)strtol(gotten_json->value, nullptr, 0); + EXPECT_EQ(gotten_number, expect); +} + +void ValidateChannel(Channel* channel, validate_channel_data_args args) { + char* json_str = channel->RenderJSON(); + grpc::testing::ValidateChannelProtoJsonTranslation(json_str); + grpc_json* json = grpc_json_parse_string(json_str); + EXPECT_NE(json, nullptr); + grpc_json* data = GetJsonChild(json, "data"); + ValidateChildInteger(data, args.calls_started, "callsStarted"); + ValidateChildInteger(data, args.calls_failed, "callsFailed"); + ValidateChildInteger(data, args.calls_succeeded, "callsSucceeded"); + grpc_json_destroy(json); + gpr_free(json_str); +} + +char* GetLastCallStartedTimestamp(Channel* channel) { + char* json_str = channel->RenderJSON(); + grpc_json* json = grpc_json_parse_string(json_str); + grpc_json* data = GetJsonChild(json, "data"); + grpc_json* timestamp = GetJsonChild(data, "lastCallStartedTimestamp"); + char* ts_str = grpc_json_dump_to_string(timestamp, 0); + grpc_json_destroy(json); + gpr_free(json_str); + return ts_str; +} + +void ChannelzSleep(int64_t sleep_us) { + gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(sleep_us, GPR_TIMESPAN))); + grpc_core::ExecCtx::Get()->InvalidateNow(); +} + +} // anonymous namespace + +class ChannelzChannelTest : public ::testing::TestWithParam {}; + +TEST_P(ChannelzChannelTest, BasicChannel) { + grpc_core::ExecCtx exec_ctx; + ChannelFixture channel(GetParam()); + intptr_t uuid = grpc_channel_get_uuid(channel.channel()); + Channel* channelz_channel = ChannelzRegistry::Get(uuid); + ValidateChannel(channelz_channel, {-1, -1, -1}); +} + +TEST_P(ChannelzChannelTest, BasicChannelAPIFunctionality) { + grpc_core::ExecCtx exec_ctx; + ChannelFixture channel(GetParam()); + intptr_t uuid = grpc_channel_get_uuid(channel.channel()); + Channel* channelz_channel = ChannelzRegistry::Get(uuid); + channelz_channel->CallStarted(); + channelz_channel->CallFailed(); + channelz_channel->CallSucceeded(); + ValidateChannel(channelz_channel, {1, 1, 1}); + channelz_channel->CallStarted(); + channelz_channel->CallFailed(); + channelz_channel->CallSucceeded(); + channelz_channel->CallStarted(); + channelz_channel->CallFailed(); + channelz_channel->CallSucceeded(); + ValidateChannel(channelz_channel, {3, 3, 3}); +} + +TEST_P(ChannelzChannelTest, LastCallStartedTimestamp) { + grpc_core::ExecCtx exec_ctx; + ChannelFixture channel(GetParam()); + intptr_t uuid = grpc_channel_get_uuid(channel.channel()); + Channel* channelz_channel = ChannelzRegistry::Get(uuid); + + // start a call to set the last call started timestamp + channelz_channel->CallStarted(); + char* ts1 = GetLastCallStartedTimestamp(channelz_channel); + + // time gone by should not affect the timestamp + ChannelzSleep(100); + char* ts2 = GetLastCallStartedTimestamp(channelz_channel); + EXPECT_STREQ(ts1, ts2); + + // calls succeeded or failed should not affect the timestamp + ChannelzSleep(100); + channelz_channel->CallFailed(); + channelz_channel->CallSucceeded(); + char* ts3 = GetLastCallStartedTimestamp(channelz_channel); + EXPECT_STREQ(ts1, ts3); + + // another call started should affect the timestamp + // sleep for extra long to avoid flakes (since we cache Now()) + ChannelzSleep(5000); + grpc_core::ExecCtx::Get()->InvalidateNow(); + channelz_channel->CallStarted(); + char* ts4 = GetLastCallStartedTimestamp(channelz_channel); + EXPECT_STRNE(ts1, ts4); + + // clean up + gpr_free(ts1); + gpr_free(ts2); + gpr_free(ts3); + gpr_free(ts4); +} + +INSTANTIATE_TEST_CASE_P(ChannelzChannelTestSweep, ChannelzChannelTest, + ::testing::Values(0, 1, 2, 6, 10, 15)); } // namespace testing +} // namespace channelz } // namespace grpc_core int main(int argc, char** argv) { diff --git a/test/cpp/util/channel_trace_proto_helper.cc b/test/cpp/util/channel_trace_proto_helper.cc index db9390163bc..ee310784c21 100644 --- a/test/cpp/util/channel_trace_proto_helper.cc +++ b/test/cpp/util/channel_trace_proto_helper.cc @@ -45,16 +45,21 @@ void VaidateProtoJsonTranslation(char* json_c_str) { // then compare the output, and determine what fields are missing. // // parse_options.ignore_unknown_fields = true; - ASSERT_EQ(google::protobuf::util::JsonStringToMessage(json_str, &msg, + EXPECT_EQ(google::protobuf::util::JsonStringToMessage(json_str, &msg, parse_options), google::protobuf::util::Status::OK); std::string proto_json_str; - ASSERT_EQ(google::protobuf::util::MessageToJsonString(msg, &proto_json_str), + google::protobuf::util::JsonPrintOptions print_options; + // We usually do not want this to be true, however it can be helpful to + // uncomment and see the output produced then all fields are printed. + // print_options.always_print_primitive_fields = true; + EXPECT_EQ(google::protobuf::util::MessageToJsonString(msg, &proto_json_str, + print_options), google::protobuf::util::Status::OK); // uncomment these to compare the the json strings. // gpr_log(GPR_ERROR, "tracer json: %s", json_str.c_str()); // gpr_log(GPR_ERROR, "proto json: %s", proto_json_str.c_str()); - ASSERT_EQ(json_str, proto_json_str); + EXPECT_EQ(json_str, proto_json_str); } } // namespace