1 /*
2 *
3 * Copyright 2017 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <grpc/impl/codegen/port_platform.h>
20
21 #include "src/core/lib/channel/channel_trace.h"
22 #include "src/core/lib/channel/channelz.h"
23 #include "src/core/lib/channel/channelz_registry.h"
24 #include "src/core/lib/gpr/useful.h"
25 #include "src/core/lib/gprpp/memory.h"
26 #include "src/core/lib/gprpp/mutex_lock.h"
27
28 #include <grpc/support/alloc.h>
29 #include <grpc/support/log.h>
30 #include <grpc/support/sync.h>
31
32 #include <cstring>
33
34 namespace grpc_core {
35 namespace channelz {
36 namespace {
37
38 // singleton instance of the registry.
39 ChannelzRegistry* g_channelz_registry = nullptr;
40
41 } // anonymous namespace
42
Init()43 void ChannelzRegistry::Init() { g_channelz_registry = New<ChannelzRegistry>(); }
44
Shutdown()45 void ChannelzRegistry::Shutdown() { Delete(g_channelz_registry); }
46
Default()47 ChannelzRegistry* ChannelzRegistry::Default() {
48 GPR_DEBUG_ASSERT(g_channelz_registry != nullptr);
49 return g_channelz_registry;
50 }
51
ChannelzRegistry()52 ChannelzRegistry::ChannelzRegistry() { gpr_mu_init(&mu_); }
53
~ChannelzRegistry()54 ChannelzRegistry::~ChannelzRegistry() { gpr_mu_destroy(&mu_); }
55
InternalRegister(BaseNode * node)56 intptr_t ChannelzRegistry::InternalRegister(BaseNode* node) {
57 MutexLock lock(&mu_);
58 entities_.push_back(node);
59 intptr_t uuid = entities_.size();
60 return uuid;
61 }
62
InternalUnregister(intptr_t uuid)63 void ChannelzRegistry::InternalUnregister(intptr_t uuid) {
64 GPR_ASSERT(uuid >= 1);
65 MutexLock lock(&mu_);
66 GPR_ASSERT(static_cast<size_t>(uuid) <= entities_.size());
67 entities_[uuid - 1] = nullptr;
68 }
69
InternalGet(intptr_t uuid)70 BaseNode* ChannelzRegistry::InternalGet(intptr_t uuid) {
71 MutexLock lock(&mu_);
72 if (uuid < 1 || uuid > static_cast<intptr_t>(entities_.size())) {
73 return nullptr;
74 }
75 return entities_[uuid - 1];
76 }
77
InternalGetTopChannels(intptr_t start_channel_id)78 char* ChannelzRegistry::InternalGetTopChannels(intptr_t start_channel_id) {
79 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
80 grpc_json* json = top_level_json;
81 grpc_json* json_iterator = nullptr;
82 InlinedVector<BaseNode*, 10> top_level_channels;
83 // uuids index into entities one-off (idx 0 is really uuid 1, since 0 is
84 // reserved). However, we want to support requests coming in with
85 // start_channel_id=0, which signifies "give me everything." Hence this
86 // funky looking line below.
87 size_t start_idx = start_channel_id == 0 ? 0 : start_channel_id - 1;
88 for (size_t i = start_idx; i < entities_.size(); ++i) {
89 if (entities_[i] != nullptr &&
90 entities_[i]->type() ==
91 grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel) {
92 top_level_channels.push_back(entities_[i]);
93 }
94 }
95 if (!top_level_channels.empty()) {
96 // create list of channels
97 grpc_json* array_parent = grpc_json_create_child(
98 nullptr, json, "channel", nullptr, GRPC_JSON_ARRAY, false);
99 for (size_t i = 0; i < top_level_channels.size(); ++i) {
100 grpc_json* channel_json = top_level_channels[i]->RenderJson();
101 json_iterator =
102 grpc_json_link_child(array_parent, channel_json, json_iterator);
103 }
104 }
105 // For now we do not have any pagination rules. In the future we could
106 // pick a constant for max_channels_sent for a GetTopChannels request.
107 // Tracking: https://github.com/grpc/grpc/issues/16019.
108 json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
109 GRPC_JSON_TRUE, false);
110 char* json_str = grpc_json_dump_to_string(top_level_json, 0);
111 grpc_json_destroy(top_level_json);
112 return json_str;
113 }
114
InternalGetServers(intptr_t start_server_id)115 char* ChannelzRegistry::InternalGetServers(intptr_t start_server_id) {
116 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
117 grpc_json* json = top_level_json;
118 grpc_json* json_iterator = nullptr;
119 InlinedVector<BaseNode*, 10> servers;
120 // uuids index into entities one-off (idx 0 is really uuid 1, since 0 is
121 // reserved). However, we want to support requests coming in with
122 // start_server_id=0, which signifies "give me everything."
123 size_t start_idx = start_server_id == 0 ? 0 : start_server_id - 1;
124 for (size_t i = start_idx; i < entities_.size(); ++i) {
125 if (entities_[i] != nullptr &&
126 entities_[i]->type() ==
127 grpc_core::channelz::BaseNode::EntityType::kServer) {
128 servers.push_back(entities_[i]);
129 }
130 }
131 if (!servers.empty()) {
132 // create list of servers
133 grpc_json* array_parent = grpc_json_create_child(
134 nullptr, json, "server", nullptr, GRPC_JSON_ARRAY, false);
135 for (size_t i = 0; i < servers.size(); ++i) {
136 grpc_json* server_json = servers[i]->RenderJson();
137 json_iterator =
138 grpc_json_link_child(array_parent, server_json, json_iterator);
139 }
140 }
141 // For now we do not have any pagination rules. In the future we could
142 // pick a constant for max_channels_sent for a GetServers request.
143 // Tracking: https://github.com/grpc/grpc/issues/16019.
144 json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
145 GRPC_JSON_TRUE, false);
146 char* json_str = grpc_json_dump_to_string(top_level_json, 0);
147 grpc_json_destroy(top_level_json);
148 return json_str;
149 }
150
151 } // namespace channelz
152 } // namespace grpc_core
153
grpc_channelz_get_top_channels(intptr_t start_channel_id)154 char* grpc_channelz_get_top_channels(intptr_t start_channel_id) {
155 return grpc_core::channelz::ChannelzRegistry::GetTopChannels(
156 start_channel_id);
157 }
158
grpc_channelz_get_servers(intptr_t start_server_id)159 char* grpc_channelz_get_servers(intptr_t start_server_id) {
160 return grpc_core::channelz::ChannelzRegistry::GetServers(start_server_id);
161 }
162
grpc_channelz_get_channel(intptr_t channel_id)163 char* grpc_channelz_get_channel(intptr_t channel_id) {
164 grpc_core::channelz::BaseNode* channel_node =
165 grpc_core::channelz::ChannelzRegistry::Get(channel_id);
166 if (channel_node == nullptr ||
167 (channel_node->type() !=
168 grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel &&
169 channel_node->type() !=
170 grpc_core::channelz::BaseNode::EntityType::kInternalChannel)) {
171 return nullptr;
172 }
173 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
174 grpc_json* json = top_level_json;
175 grpc_json* channel_json = channel_node->RenderJson();
176 channel_json->key = "channel";
177 grpc_json_link_child(json, channel_json, nullptr);
178 char* json_str = grpc_json_dump_to_string(top_level_json, 0);
179 grpc_json_destroy(top_level_json);
180 return json_str;
181 }
182
grpc_channelz_get_subchannel(intptr_t subchannel_id)183 char* grpc_channelz_get_subchannel(intptr_t subchannel_id) {
184 grpc_core::channelz::BaseNode* subchannel_node =
185 grpc_core::channelz::ChannelzRegistry::Get(subchannel_id);
186 if (subchannel_node == nullptr ||
187 subchannel_node->type() !=
188 grpc_core::channelz::BaseNode::EntityType::kSubchannel) {
189 return nullptr;
190 }
191 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
192 grpc_json* json = top_level_json;
193 grpc_json* subchannel_json = subchannel_node->RenderJson();
194 subchannel_json->key = "subchannel";
195 grpc_json_link_child(json, subchannel_json, nullptr);
196 char* json_str = grpc_json_dump_to_string(top_level_json, 0);
197 grpc_json_destroy(top_level_json);
198 return json_str;
199 }
200