diff options
Diffstat (limited to 'src/core/lib/channel/channelz_registry.cc')
-rw-r--r-- | src/core/lib/channel/channelz_registry.cc | 250 |
1 files changed, 214 insertions, 36 deletions
diff --git a/src/core/lib/channel/channelz_registry.cc b/src/core/lib/channel/channelz_registry.cc index f79d2f0c17..bc23b90a66 100644 --- a/src/core/lib/channel/channelz_registry.cc +++ b/src/core/lib/channel/channelz_registry.cc @@ -38,6 +38,8 @@ namespace { // singleton instance of the registry. ChannelzRegistry* g_channelz_registry = nullptr; +const int kPaginationLimit = 100; + } // anonymous namespace void ChannelzRegistry::Init() { g_channelz_registry = New<ChannelzRegistry>(); } @@ -53,54 +55,101 @@ ChannelzRegistry::ChannelzRegistry() { gpr_mu_init(&mu_); } ChannelzRegistry::~ChannelzRegistry() { gpr_mu_destroy(&mu_); } -intptr_t ChannelzRegistry::InternalRegisterEntry(const RegistryEntry& entry) { +void ChannelzRegistry::InternalRegister(BaseNode* node) { MutexLock lock(&mu_); - entities_.push_back(entry); - intptr_t uuid = entities_.size(); - return uuid; + entities_.push_back(node); + node->uuid_ = ++uuid_generator_; +} + +void ChannelzRegistry::MaybePerformCompactionLocked() { + constexpr double kEmptinessTheshold = 1 / 3; + double emptiness_ratio = + double(num_empty_slots_) / double(entities_.capacity()); + if (emptiness_ratio > kEmptinessTheshold) { + int front = 0; + for (size_t i = 0; i < entities_.size(); ++i) { + if (entities_[i] != nullptr) { + entities_[front++] = entities_[i]; + } + } + for (int i = 0; i < num_empty_slots_; ++i) { + entities_.pop_back(); + } + num_empty_slots_ = 0; + } } -void ChannelzRegistry::InternalUnregisterEntry(intptr_t uuid, EntityType type) { +int ChannelzRegistry::FindByUuidLocked(intptr_t target_uuid, + bool direct_hit_needed) { + int left = 0; + int right = int(entities_.size() - 1); + while (left <= right) { + int true_middle = left + (right - left) / 2; + int first_non_null = true_middle; + while (first_non_null < right && entities_[first_non_null] == nullptr) { + first_non_null++; + } + if (entities_[first_non_null] == nullptr) { + right = true_middle - 1; + continue; + } + intptr_t uuid = entities_[first_non_null]->uuid(); + if (uuid == target_uuid) { + return int(first_non_null); + } + if (uuid < target_uuid) { + left = first_non_null + 1; + } else { + right = true_middle - 1; + } + } + return direct_hit_needed ? -1 : left; +} + +void ChannelzRegistry::InternalUnregister(intptr_t uuid) { GPR_ASSERT(uuid >= 1); MutexLock lock(&mu_); - GPR_ASSERT(static_cast<size_t>(uuid) <= entities_.size()); - GPR_ASSERT(entities_[uuid - 1].type == type); - entities_[uuid - 1].object = nullptr; - entities_[uuid - 1].type = EntityType::kUnset; + GPR_ASSERT(uuid <= uuid_generator_); + int idx = FindByUuidLocked(uuid, true); + GPR_ASSERT(idx >= 0); + entities_[idx] = nullptr; + num_empty_slots_++; + MaybePerformCompactionLocked(); } -void* ChannelzRegistry::InternalGetEntry(intptr_t uuid, EntityType type) { +BaseNode* ChannelzRegistry::InternalGet(intptr_t uuid) { MutexLock lock(&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 { + if (uuid < 1 || uuid > uuid_generator_) { return nullptr; } + int idx = FindByUuidLocked(uuid, true); + return idx < 0 ? nullptr : entities_[idx]; } char* ChannelzRegistry::InternalGetTopChannels(intptr_t start_channel_id) { + MutexLock lock(&mu_); 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; + InlinedVector<BaseNode*, 10> top_level_channels; + bool reached_pagination_limit = false; + int start_idx = GPR_MAX(FindByUuidLocked(start_channel_id, false), 0); 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 (entities_[i] != nullptr && + entities_[i]->type() == + grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel && + entities_[i]->uuid() >= start_channel_id) { + // check if we are over pagination limit to determine if we need to set + // the "end" element. If we don't go through this block, we know that + // when the loop terminates, we have <= to kPaginationLimit. + if (top_level_channels.size() == kPaginationLimit) { + reached_pagination_limit = true; + break; } + top_level_channels.push_back(entities_[i]); } } - if (top_level_channels.size() > 0) { + if (!top_level_channels.empty()) { // create list of channels grpc_json* array_parent = grpc_json_create_child( nullptr, json, "channel", nullptr, GRPC_JSON_ARRAY, false); @@ -110,16 +159,68 @@ char* ChannelzRegistry::InternalGetTopChannels(intptr_t start_channel_id) { 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); + if (!reached_pagination_limit) { + 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; +} + +char* ChannelzRegistry::InternalGetServers(intptr_t start_server_id) { + MutexLock lock(&mu_); + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + InlinedVector<BaseNode*, 10> servers; + bool reached_pagination_limit = false; + int start_idx = GPR_MAX(FindByUuidLocked(start_server_id, false), 0); + for (size_t i = start_idx; i < entities_.size(); ++i) { + if (entities_[i] != nullptr && + entities_[i]->type() == + grpc_core::channelz::BaseNode::EntityType::kServer && + entities_[i]->uuid() >= start_server_id) { + // check if we are over pagination limit to determine if we need to set + // the "end" element. If we don't go through this block, we know that + // when the loop terminates, we have <= to kPaginationLimit. + if (servers.size() == kPaginationLimit) { + reached_pagination_limit = true; + break; + } + servers.push_back(entities_[i]); + } + } + if (!servers.empty()) { + // create list of servers + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "server", nullptr, GRPC_JSON_ARRAY, false); + for (size_t i = 0; i < servers.size(); ++i) { + grpc_json* server_json = servers[i]->RenderJson(); + json_iterator = + grpc_json_link_child(array_parent, server_json, json_iterator); + } + } + if (!reached_pagination_limit) { + 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; } +void ChannelzRegistry::InternalLogAllEntities() { + MutexLock lock(&mu_); + for (size_t i = 0; i < entities_.size(); ++i) { + if (entities_[i] != nullptr) { + char* json = entities_[i]->RenderJsonString(); + gpr_log(GPR_INFO, "%s", json); + gpr_free(json); + } + } +} + } // namespace channelz } // namespace grpc_core @@ -128,10 +229,51 @@ char* grpc_channelz_get_top_channels(intptr_t start_channel_id) { start_channel_id); } +char* grpc_channelz_get_servers(intptr_t start_server_id) { + return grpc_core::channelz::ChannelzRegistry::GetServers(start_server_id); +} + +char* grpc_channelz_get_server(intptr_t server_id) { + grpc_core::channelz::BaseNode* server_node = + grpc_core::channelz::ChannelzRegistry::Get(server_id); + if (server_node == nullptr || + server_node->type() != + grpc_core::channelz::BaseNode::EntityType::kServer) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* channel_json = server_node->RenderJson(); + channel_json->key = "server"; + grpc_json_link_child(json, channel_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +char* grpc_channelz_get_server_sockets(intptr_t server_id, + intptr_t start_socket_id) { + grpc_core::channelz::BaseNode* base_node = + grpc_core::channelz::ChannelzRegistry::Get(server_id); + if (base_node == nullptr || + base_node->type() != grpc_core::channelz::BaseNode::EntityType::kServer) { + return nullptr; + } + // This cast is ok since we have just checked to make sure base_node is + // actually a server node + grpc_core::channelz::ServerNode* server_node = + static_cast<grpc_core::channelz::ServerNode*>(base_node); + return server_node->RenderServerSockets(start_socket_id); +} + char* grpc_channelz_get_channel(intptr_t channel_id) { - grpc_core::channelz::ChannelNode* channel_node = - grpc_core::channelz::ChannelzRegistry::GetChannelNode(channel_id); - if (channel_node == nullptr) { + grpc_core::channelz::BaseNode* channel_node = + grpc_core::channelz::ChannelzRegistry::Get(channel_id); + if (channel_node == nullptr || + (channel_node->type() != + grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel && + channel_node->type() != + grpc_core::channelz::BaseNode::EntityType::kInternalChannel)) { return nullptr; } grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); @@ -143,3 +285,39 @@ char* grpc_channelz_get_channel(intptr_t channel_id) { grpc_json_destroy(top_level_json); return json_str; } + +char* grpc_channelz_get_subchannel(intptr_t subchannel_id) { + grpc_core::channelz::BaseNode* subchannel_node = + grpc_core::channelz::ChannelzRegistry::Get(subchannel_id); + if (subchannel_node == nullptr || + subchannel_node->type() != + grpc_core::channelz::BaseNode::EntityType::kSubchannel) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* subchannel_json = subchannel_node->RenderJson(); + subchannel_json->key = "subchannel"; + grpc_json_link_child(json, subchannel_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +char* grpc_channelz_get_socket(intptr_t socket_id) { + grpc_core::channelz::BaseNode* socket_node = + grpc_core::channelz::ChannelzRegistry::Get(socket_id); + if (socket_node == nullptr || + socket_node->type() != + grpc_core::channelz::BaseNode::EntityType::kSocket) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* socket_json = socket_node->RenderJson(); + socket_json->key = "socket"; + grpc_json_link_child(json, socket_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} |