/* * * 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 "src/core/lib/channel/channelz.h" #include #include #include #include #include #include #include #include "src/core/lib/channel/channelz_registry.h" #include "src/core/lib/channel/status_util.h" #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/memory.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/slice/b64.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/surface/channel.h" #include "src/core/lib/surface/server.h" #include "src/core/lib/transport/error_utils.h" #include "src/core/lib/uri/uri_parser.h" namespace grpc_core { namespace channelz { BaseNode::BaseNode(EntityType type) : type_(type), uuid_(-1) { // The registry will set uuid_ under its lock. ChannelzRegistry::Register(this); } BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); } char* BaseNode::RenderJsonString() { grpc_json* json = RenderJson(); GPR_ASSERT(json != nullptr); char* json_str = grpc_json_dump_to_string(json, 0); grpc_json_destroy(json); return json_str; } CallCountingHelper::CallCountingHelper() { num_cores_ = GPR_MAX(1, gpr_cpu_num_cores()); per_cpu_counter_data_storage_ = static_cast( gpr_zalloc(sizeof(AtomicCounterData) * num_cores_)); } CallCountingHelper::~CallCountingHelper() { gpr_free(per_cpu_counter_data_storage_); } void CallCountingHelper::RecordCallStarted() { gpr_atm_no_barrier_fetch_add( &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] .calls_started, static_cast(1)); gpr_atm_no_barrier_store( &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] .last_call_started_millis, (gpr_atm)ExecCtx::Get()->Now()); } void CallCountingHelper::RecordCallFailed() { gpr_atm_no_barrier_fetch_add( &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] .calls_failed, static_cast(1)); } void CallCountingHelper::RecordCallSucceeded() { gpr_atm_no_barrier_fetch_add( &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] .calls_succeeded, static_cast(1)); } void CallCountingHelper::CollectData(CounterData* out) { for (size_t core = 0; core < num_cores_; ++core) { out->calls_started += gpr_atm_no_barrier_load( &per_cpu_counter_data_storage_[core].calls_started); out->calls_succeeded += gpr_atm_no_barrier_load( &per_cpu_counter_data_storage_[core].calls_succeeded); out->calls_failed += gpr_atm_no_barrier_load( &per_cpu_counter_data_storage_[core].calls_failed); gpr_atm last_call = gpr_atm_no_barrier_load( &per_cpu_counter_data_storage_[core].last_call_started_millis); if (last_call > out->last_call_started_millis) { out->last_call_started_millis = last_call; } } } void CallCountingHelper::PopulateCallCounts(grpc_json* json) { grpc_json* json_iterator = nullptr; CounterData data; CollectData(&data); if (data.calls_started != 0) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "callsStarted", data.calls_started); } if (data.calls_succeeded != 0) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "callsSucceeded", data.calls_succeeded); } if (data.calls_failed) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "callsFailed", data.calls_failed); } if (data.calls_started != 0) { gpr_timespec ts = grpc_millis_to_timespec(data.last_call_started_millis, GPR_CLOCK_REALTIME); json_iterator = grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp", gpr_format_timespec(ts), GRPC_JSON_STRING, true); } } ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes, bool is_top_level_channel) : BaseNode(is_top_level_channel ? EntityType::kTopLevelChannel : EntityType::kInternalChannel), channel_(channel), target_(UniquePtr(grpc_channel_get_target(channel_))), trace_(channel_tracer_max_nodes) {} ChannelNode::~ChannelNode() {} 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; grpc_json* json_iterator = nullptr; // create and fill the ref child json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = grpc_json_add_number_string_child(json, json_iterator, "channelId", uuid()); // reset json iterators to top level object json = top_level_json; json_iterator = nullptr; // create and fill the data child. grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, GRPC_JSON_OBJECT, false); json = data; json_iterator = nullptr; // template method. Child classes may override this to add their specific // functionality. PopulateConnectivityState(json); // populate the target. GPR_ASSERT(target_.get() != nullptr); grpc_json_create_child(nullptr, json, "target", target_.get(), GRPC_JSON_STRING, false); // fill in the channel trace if applicable grpc_json* trace_json = trace_.RenderJson(); if (trace_json != nullptr) { trace_json->key = "trace"; // this object is named trace in channelz.proto grpc_json_link_child(json, trace_json, nullptr); } // ask CallCountingHelper to populate trace and call count data. call_counter_.PopulateCallCounts(json); json = top_level_json; // template method. Child classes may override this to add their specific // functionality. PopulateChildRefs(json); return top_level_json; } RefCountedPtr ChannelNode::MakeChannelNode( grpc_channel* channel, size_t channel_tracer_max_nodes, bool is_top_level_channel) { return MakeRefCounted( channel, channel_tracer_max_nodes, is_top_level_channel); } ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes) : BaseNode(EntityType::kServer), server_(server), trace_(channel_tracer_max_nodes) {} ServerNode::~ServerNode() {} char* ServerNode::RenderServerSockets(intptr_t start_socket_id, intptr_t max_results) { // if user does not set max_results, we choose 500. size_t pagination_limit = max_results == 0 ? 500 : max_results; grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); grpc_json* json = top_level_json; grpc_json* json_iterator = nullptr; ChildSocketsList socket_refs; grpc_server_populate_server_sockets(server_, &socket_refs, start_socket_id); // declared early so it can be used outside of the loop. size_t i = 0; if (!socket_refs.empty()) { // create list of socket refs grpc_json* array_parent = grpc_json_create_child( nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false); for (i = 0; i < GPR_MIN(socket_refs.size(), pagination_limit); ++i) { grpc_json* socket_ref_json = grpc_json_create_child( nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false); json_iterator = grpc_json_add_number_string_child( socket_ref_json, nullptr, "socketId", socket_refs[i]->uuid()); grpc_json_create_child(json_iterator, socket_ref_json, "name", socket_refs[i]->remote(), GRPC_JSON_STRING, false); } } if (i == socket_refs.size()) { 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; } grpc_json* ServerNode::RenderJson() { // We need to track these three json objects to build our object grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); grpc_json* json = top_level_json; grpc_json* json_iterator = nullptr; // create and fill the ref child json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = grpc_json_add_number_string_child(json, json_iterator, "serverId", uuid()); // reset json iterators to top level object json = top_level_json; json_iterator = nullptr; // create and fill the data child. grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, GRPC_JSON_OBJECT, false); json = data; json_iterator = nullptr; // fill in the channel trace if applicable grpc_json* trace_json = trace_.RenderJson(); if (trace_json != nullptr) { trace_json->key = "trace"; // this object is named trace in channelz.proto grpc_json_link_child(json, trace_json, nullptr); } // ask CallCountingHelper to populate trace and call count data. call_counter_.PopulateCallCounts(json); json = top_level_json; ChildRefsList listen_sockets; grpc_server_populate_listen_sockets(server_, &listen_sockets); if (!listen_sockets.empty()) { grpc_json* array_parent = grpc_json_create_child( nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false); for (size_t i = 0; i < listen_sockets.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, "socketId", listen_sockets[i]); } } return top_level_json; } static void PopulateSocketAddressJson(grpc_json* json, const char* name, const char* addr_str) { if (addr_str == nullptr) return; grpc_json* json_iterator = nullptr; json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; grpc_uri* uri = grpc_uri_parse(addr_str, true); if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) || (strcmp(uri->scheme, "ipv6") == 0))) { const char* host_port = uri->path; if (*host_port == '/') ++host_port; char* host = nullptr; char* port = nullptr; GPR_ASSERT(gpr_split_host_port(host_port, &host, &port)); int port_num = -1; if (port != nullptr) { port_num = atoi(port); } char* b64_host = grpc_base64_encode(host, strlen(host), false, false); json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address", nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = grpc_json_add_number_string_child(json, json_iterator, "port", port_num); json_iterator = grpc_json_create_child(json_iterator, json, "ip_address", b64_host, GRPC_JSON_STRING, true); gpr_free(host); gpr_free(port); } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) { json_iterator = grpc_json_create_child(json_iterator, json, "uds_address", nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = grpc_json_create_child(json_iterator, json, "filename", gpr_strdup(uri->path), GRPC_JSON_STRING, true); } else { json_iterator = grpc_json_create_child(json_iterator, json, "other_address", nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = grpc_json_create_child(json_iterator, json, "name", addr_str, GRPC_JSON_STRING, false); } grpc_uri_destroy(uri); } SocketNode::SocketNode(UniquePtr local, UniquePtr remote) : BaseNode(EntityType::kSocket), local_(std::move(local)), remote_(std::move(remote)) {} void SocketNode::RecordStreamStartedFromLocal() { gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast(1)); gpr_atm_no_barrier_store(&last_local_stream_created_millis_, (gpr_atm)ExecCtx::Get()->Now()); } void SocketNode::RecordStreamStartedFromRemote() { gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast(1)); gpr_atm_no_barrier_store(&last_remote_stream_created_millis_, (gpr_atm)ExecCtx::Get()->Now()); } void SocketNode::RecordMessagesSent(uint32_t num_sent) { gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast(num_sent)); gpr_atm_no_barrier_store(&last_message_sent_millis_, (gpr_atm)ExecCtx::Get()->Now()); } void SocketNode::RecordMessageReceived() { gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast(1)); gpr_atm_no_barrier_store(&last_message_received_millis_, (gpr_atm)ExecCtx::Get()->Now()); } grpc_json* SocketNode::RenderJson() { // We need to track these three json objects to build our object grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); grpc_json* json = top_level_json; grpc_json* json_iterator = nullptr; // create and fill the ref child json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = grpc_json_add_number_string_child(json, json_iterator, "socketId", uuid()); json = top_level_json; PopulateSocketAddressJson(json, "remote", remote_.get()); PopulateSocketAddressJson(json, "local", local_.get()); // reset json iterators to top level object json = top_level_json; json_iterator = nullptr; // create and fill the data child. grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, GRPC_JSON_OBJECT, false); json = data; json_iterator = nullptr; gpr_timespec ts; if (streams_started_ != 0) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "streamsStarted", streams_started_); if (last_local_stream_created_millis_ != 0) { ts = grpc_millis_to_timespec(last_local_stream_created_millis_, GPR_CLOCK_REALTIME); json_iterator = grpc_json_create_child( json_iterator, json, "lastLocalStreamCreatedTimestamp", gpr_format_timespec(ts), GRPC_JSON_STRING, true); } if (last_remote_stream_created_millis_ != 0) { ts = grpc_millis_to_timespec(last_remote_stream_created_millis_, GPR_CLOCK_REALTIME); json_iterator = grpc_json_create_child( json_iterator, json, "lastRemoteStreamCreatedTimestamp", gpr_format_timespec(ts), GRPC_JSON_STRING, true); } } if (streams_succeeded_ != 0) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "streamsSucceeded", streams_succeeded_); } if (streams_failed_) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "streamsFailed", streams_failed_); } if (messages_sent_ != 0) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "messagesSent", messages_sent_); ts = grpc_millis_to_timespec(last_message_sent_millis_, GPR_CLOCK_REALTIME); json_iterator = grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp", gpr_format_timespec(ts), GRPC_JSON_STRING, true); } if (messages_received_ != 0) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "messagesReceived", messages_received_); ts = grpc_millis_to_timespec(last_message_received_millis_, GPR_CLOCK_REALTIME); json_iterator = grpc_json_create_child( json_iterator, json, "lastMessageReceivedTimestamp", gpr_format_timespec(ts), GRPC_JSON_STRING, true); } if (keepalives_sent_ != 0) { json_iterator = grpc_json_add_number_string_child( json, json_iterator, "keepAlivesSent", keepalives_sent_); } return top_level_json; } ListenSocketNode::ListenSocketNode(UniquePtr local_addr) : BaseNode(EntityType::kSocket), local_addr_(std::move(local_addr)) {} grpc_json* ListenSocketNode::RenderJson() { // We need to track these three json objects to build our object grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); grpc_json* json = top_level_json; grpc_json* json_iterator = nullptr; // create and fill the ref child json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, GRPC_JSON_OBJECT, false); json = json_iterator; json_iterator = nullptr; json_iterator = grpc_json_add_number_string_child(json, json_iterator, "socketId", uuid()); json = top_level_json; PopulateSocketAddressJson(json, "local", local_addr_.get()); return top_level_json; } } // namespace channelz } // namespace grpc_core