/* * * Copyright 2017 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include #include #include #include #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" #include #include namespace grpc_core { namespace channelz { namespace testing { // testing peer to access channel internals class ChannelNodePeer { public: explicit ChannelNodePeer(ChannelNode* node) : node_(node) {} ChannelTrace* trace() const { return &node_->trace_; } private: ChannelNode* node_; }; size_t GetSizeofTraceEvent() { return sizeof(ChannelTrace::TraceEvent); } namespace { void ValidateJsonArraySize(const Json& array, size_t expected) { if (expected == 0) { ASSERT_EQ(array.type(), Json::Type::JSON_NULL); } else { ASSERT_EQ(array.type(), Json::Type::ARRAY); EXPECT_EQ(array.array_value().size(), expected); } } void ValidateChannelTraceData(const Json& json, size_t num_events_logged_expected, size_t actual_num_events_expected) { ASSERT_EQ(json.type(), Json::Type::OBJECT); Json::Object object = json.object_value(); Json& num_events_logged_json = object["numEventsLogged"]; ASSERT_EQ(num_events_logged_json.type(), Json::Type::STRING); size_t num_events_logged = (size_t)strtol(num_events_logged_json.string_value().c_str(), nullptr, 0); ASSERT_EQ(num_events_logged, num_events_logged_expected); Json& start_time_json = object["creationTimestamp"]; ASSERT_EQ(start_time_json.type(), Json::Type::STRING); ValidateJsonArraySize(object["events"], actual_num_events_expected); } 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 ValidateChannelTraceCustom(ChannelTrace* tracer, size_t num_events_logged, size_t num_events_expected) { Json json = tracer->RenderJson(); ASSERT_EQ(json.type(), Json::Type::OBJECT); std::string json_str = json.Dump(); grpc::testing::ValidateChannelTraceProtoJsonTranslation(json_str.c_str()); ValidateChannelTraceData(json, num_events_logged, num_events_expected); } void ValidateChannelTrace(ChannelTrace* tracer, size_t num_events_logged) { ValidateChannelTraceCustom(tracer, num_events_logged, num_events_logged); } class ChannelFixture { public: ChannelFixture(int max_tracer_event_memory) { grpc_arg client_a = grpc_channel_arg_integer_create( const_cast(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE), max_tracer_event_memory); 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 const int kEventListMemoryLimit = 1024 * 1024; // Tests basic ChannelTrace functionality like construction, adding trace, and // lookups by uuid. TEST(ChannelTracerTest, BasicTest) { grpc_core::ExecCtx exec_ctx; ChannelTrace tracer(kEventListMemoryLimit); 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); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, 6); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, 10); } // Tests more complex functionality, like a parent channel tracking // subchannles. This exercises the ref/unref patterns since the parent tracer // and this function will both hold refs to the subchannel. TEST(ChannelTracerTest, ComplexTest) { grpc_core::ExecCtx exec_ctx; ChannelTrace tracer(kEventListMemoryLimit); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ChannelFixture channel1(kEventListMemoryLimit); RefCountedPtr sc1 = MakeRefCounted("fake_target", kEventListMemoryLimit, 0); ChannelNodePeer sc1_peer(sc1.get()); tracer.AddTraceEventWithReference( ChannelTrace::Severity::Info, grpc_slice_from_static_string("subchannel one created"), sc1); ValidateChannelTrace(&tracer, 3); AddSimpleTrace(sc1_peer.trace()); AddSimpleTrace(sc1_peer.trace()); AddSimpleTrace(sc1_peer.trace()); ValidateChannelTrace(sc1_peer.trace(), 3); AddSimpleTrace(sc1_peer.trace()); AddSimpleTrace(sc1_peer.trace()); AddSimpleTrace(sc1_peer.trace()); ValidateChannelTrace(sc1_peer.trace(), 6); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, 5); ChannelFixture channel2(kEventListMemoryLimit); RefCountedPtr sc2 = MakeRefCounted("fake_target", kEventListMemoryLimit, 0); tracer.AddTraceEventWithReference( ChannelTrace::Severity::Info, grpc_slice_from_static_string("LB channel two created"), sc2); tracer.AddTraceEventWithReference( ChannelTrace::Severity::Warning, grpc_slice_from_static_string("subchannel one inactive"), sc1); ValidateChannelTrace(&tracer, 7); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); sc1.reset(); sc2.reset(); } // Test a case in which the parent channel has subchannels and the subchannels // have connections. Ensures that everything lives as long as it should then // gets deleted. TEST(ChannelTracerTest, TestNesting) { grpc_core::ExecCtx exec_ctx; ChannelTrace tracer(kEventListMemoryLimit); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, 2); ChannelFixture channel1(kEventListMemoryLimit); RefCountedPtr sc1 = MakeRefCounted("fake_target", kEventListMemoryLimit, 0); ChannelNodePeer sc1_peer(sc1.get()); tracer.AddTraceEventWithReference( ChannelTrace::Severity::Info, grpc_slice_from_static_string("subchannel one created"), sc1); ValidateChannelTrace(&tracer, 3); AddSimpleTrace(sc1_peer.trace()); ChannelFixture channel2(kEventListMemoryLimit); RefCountedPtr conn1 = MakeRefCounted("fake_target", kEventListMemoryLimit, 0); ChannelNodePeer conn1_peer(conn1.get()); // nesting one level deeper. sc1_peer.trace()->AddTraceEventWithReference( ChannelTrace::Severity::Info, grpc_slice_from_static_string("connection one created"), conn1); ValidateChannelTrace(&tracer, 3); AddSimpleTrace(conn1_peer.trace()); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, 5); ValidateChannelTrace(conn1_peer.trace(), 1); ChannelFixture channel3(kEventListMemoryLimit); RefCountedPtr sc2 = MakeRefCounted("fake_target", kEventListMemoryLimit, 0); tracer.AddTraceEventWithReference( 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.AddTraceEventWithReference( ChannelTrace::Severity::Warning, grpc_slice_from_static_string("subchannel one inactive"), sc1); AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, 8); sc1.reset(); sc2.reset(); conn1.reset(); } TEST(ChannelTracerTest, TestSmallMemoryLimit) { grpc_core::ExecCtx exec_ctx; // doesn't make sense, but serves a testing purpose for the channel tracing // bookkeeping. All tracing events added should will get immediately garbage // collected. const int kSmallMemoryLimit = 1; ChannelTrace tracer(kSmallMemoryLimit); 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")); ValidateChannelTraceCustom(&tracer, 4, 0); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ValidateChannelTraceCustom(&tracer, 6, 0); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); AddSimpleTrace(&tracer); ValidateChannelTraceCustom(&tracer, 10, 0); } TEST(ChannelTracerTest, TestEviction) { grpc_core::ExecCtx exec_ctx; const int kTraceEventSize = GetSizeofTraceEvent(); const int kNumEvents = 5; ChannelTrace tracer(kTraceEventSize * kNumEvents); for (int i = 1; i <= kNumEvents; ++i) { AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, i); } // at this point the list is full, and each subsequent enntry will cause an // eviction. for (int i = 1; i <= kNumEvents; ++i) { AddSimpleTrace(&tracer); ValidateChannelTraceCustom(&tracer, kNumEvents + i, kNumEvents); } } TEST(ChannelTracerTest, TestMultipleEviction) { grpc_core::ExecCtx exec_ctx; const int kTraceEventSize = GetSizeofTraceEvent(); const int kNumEvents = 5; ChannelTrace tracer(kTraceEventSize * kNumEvents); for (int i = 1; i <= kNumEvents; ++i) { AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, i); } // at this point the list is full, and each subsequent enntry will cause an // eviction. We will now add in a trace event that has a copied string. This // uses more memory, so it will cause a double eviciction tracer.AddTraceEvent( ChannelTrace::Severity::Info, grpc_slice_from_copied_string( "long enough string to trigger a multiple eviction")); ValidateChannelTraceCustom(&tracer, kNumEvents + 1, kNumEvents - 1); } TEST(ChannelTracerTest, TestTotalEviction) { grpc_core::ExecCtx exec_ctx; const int kTraceEventSize = GetSizeofTraceEvent(); const int kNumEvents = 5; ChannelTrace tracer(kTraceEventSize * kNumEvents); for (int i = 1; i <= kNumEvents; ++i) { AddSimpleTrace(&tracer); ValidateChannelTrace(&tracer, i); } // at this point the list is full. Now we add such a big slice that // everything gets evicted. grpc_slice huge_slice = grpc_slice_malloc(kTraceEventSize * (kNumEvents + 1)); tracer.AddTraceEvent(ChannelTrace::Severity::Info, huge_slice); ValidateChannelTraceCustom(&tracer, kNumEvents + 1, 0); } } // namespace testing } // namespace channelz } // namespace grpc_core int main(int argc, char** argv) { grpc::testing::TestEnvironment env(argc, argv); grpc_init(); ::testing::InitGoogleTest(&argc, argv); int ret = RUN_ALL_TESTS(); grpc_shutdown(); return ret; }