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 <algorithm>
22 #include <cstring>
23
24 #include "absl/container/inlined_vector.h"
25
26 #include "src/core/lib/channel/channel_trace.h"
27 #include "src/core/lib/channel/channelz.h"
28 #include "src/core/lib/channel/channelz_registry.h"
29 #include "src/core/lib/gpr/useful.h"
30 #include "src/core/lib/gprpp/memory.h"
31 #include "src/core/lib/gprpp/sync.h"
32
33 #include <grpc/support/alloc.h>
34 #include <grpc/support/log.h>
35 #include <grpc/support/string_util.h>
36 #include <grpc/support/sync.h>
37
38 namespace grpc_core {
39 namespace channelz {
40 namespace {
41
42 // singleton instance of the registry.
43 ChannelzRegistry* g_channelz_registry = nullptr;
44
45 const int kPaginationLimit = 100;
46
47 } // anonymous namespace
48
Init()49 void ChannelzRegistry::Init() { g_channelz_registry = new ChannelzRegistry(); }
50
Shutdown()51 void ChannelzRegistry::Shutdown() { delete g_channelz_registry; }
52
Default()53 ChannelzRegistry* ChannelzRegistry::Default() {
54 GPR_DEBUG_ASSERT(g_channelz_registry != nullptr);
55 return g_channelz_registry;
56 }
57
InternalRegister(BaseNode * node)58 void ChannelzRegistry::InternalRegister(BaseNode* node) {
59 MutexLock lock(&mu_);
60 node->uuid_ = ++uuid_generator_;
61 node_map_[node->uuid_] = node;
62 }
63
InternalUnregister(intptr_t uuid)64 void ChannelzRegistry::InternalUnregister(intptr_t uuid) {
65 GPR_ASSERT(uuid >= 1);
66 MutexLock lock(&mu_);
67 GPR_ASSERT(uuid <= uuid_generator_);
68 node_map_.erase(uuid);
69 }
70
InternalGet(intptr_t uuid)71 RefCountedPtr<BaseNode> ChannelzRegistry::InternalGet(intptr_t uuid) {
72 MutexLock lock(&mu_);
73 if (uuid < 1 || uuid > uuid_generator_) {
74 return nullptr;
75 }
76 auto it = node_map_.find(uuid);
77 if (it == node_map_.end()) return nullptr;
78 // Found node. Return only if its refcount is not zero (i.e., when we
79 // know that there is no other thread about to destroy it).
80 BaseNode* node = it->second;
81 if (!node->RefIfNonZero()) return nullptr;
82 return RefCountedPtr<BaseNode>(node);
83 }
84
InternalGetTopChannels(intptr_t start_channel_id)85 std::string ChannelzRegistry::InternalGetTopChannels(
86 intptr_t start_channel_id) {
87 absl::InlinedVector<RefCountedPtr<BaseNode>, 10> top_level_channels;
88 RefCountedPtr<BaseNode> node_after_pagination_limit;
89 {
90 MutexLock lock(&mu_);
91 for (auto it = node_map_.lower_bound(start_channel_id);
92 it != node_map_.end(); ++it) {
93 BaseNode* node = it->second;
94 if (node->type() == BaseNode::EntityType::kTopLevelChannel &&
95 node->RefIfNonZero()) {
96 // Check if we are over pagination limit to determine if we need to set
97 // the "end" element. If we don't go through this block, we know that
98 // when the loop terminates, we have <= to kPaginationLimit.
99 // Note that because we have already increased this node's
100 // refcount, we need to decrease it, but we can't unref while
101 // holding the lock, because this may lead to a deadlock.
102 if (top_level_channels.size() == kPaginationLimit) {
103 node_after_pagination_limit.reset(node);
104 break;
105 }
106 top_level_channels.emplace_back(node);
107 }
108 }
109 }
110 Json::Object object;
111 if (!top_level_channels.empty()) {
112 // Create list of channels.
113 Json::Array array;
114 for (size_t i = 0; i < top_level_channels.size(); ++i) {
115 array.emplace_back(top_level_channels[i]->RenderJson());
116 }
117 object["channel"] = std::move(array);
118 }
119 if (node_after_pagination_limit == nullptr) object["end"] = true;
120 Json json(std::move(object));
121 return json.Dump();
122 }
123
InternalGetServers(intptr_t start_server_id)124 std::string ChannelzRegistry::InternalGetServers(intptr_t start_server_id) {
125 absl::InlinedVector<RefCountedPtr<BaseNode>, 10> servers;
126 RefCountedPtr<BaseNode> node_after_pagination_limit;
127 {
128 MutexLock lock(&mu_);
129 for (auto it = node_map_.lower_bound(start_server_id);
130 it != node_map_.end(); ++it) {
131 BaseNode* node = it->second;
132 if (node->type() == BaseNode::EntityType::kServer &&
133 node->RefIfNonZero()) {
134 // Check if we are over pagination limit to determine if we need to set
135 // the "end" element. If we don't go through this block, we know that
136 // when the loop terminates, we have <= to kPaginationLimit.
137 // Note that because we have already increased this node's
138 // refcount, we need to decrease it, but we can't unref while
139 // holding the lock, because this may lead to a deadlock.
140 if (servers.size() == kPaginationLimit) {
141 node_after_pagination_limit.reset(node);
142 break;
143 }
144 servers.emplace_back(node);
145 }
146 }
147 }
148 Json::Object object;
149 if (!servers.empty()) {
150 // Create list of servers.
151 Json::Array array;
152 for (size_t i = 0; i < servers.size(); ++i) {
153 array.emplace_back(servers[i]->RenderJson());
154 }
155 object["server"] = std::move(array);
156 }
157 if (node_after_pagination_limit == nullptr) object["end"] = true;
158 Json json(std::move(object));
159 return json.Dump();
160 }
161
InternalLogAllEntities()162 void ChannelzRegistry::InternalLogAllEntities() {
163 absl::InlinedVector<RefCountedPtr<BaseNode>, 10> nodes;
164 {
165 MutexLock lock(&mu_);
166 for (auto& p : node_map_) {
167 BaseNode* node = p.second;
168 if (node->RefIfNonZero()) {
169 nodes.emplace_back(node);
170 }
171 }
172 }
173 for (size_t i = 0; i < nodes.size(); ++i) {
174 std::string json = nodes[i]->RenderJsonString();
175 gpr_log(GPR_INFO, "%s", json.c_str());
176 }
177 }
178
179 } // namespace channelz
180 } // namespace grpc_core
181
grpc_channelz_get_top_channels(intptr_t start_channel_id)182 char* grpc_channelz_get_top_channels(intptr_t start_channel_id) {
183 return gpr_strdup(
184 grpc_core::channelz::ChannelzRegistry::GetTopChannels(start_channel_id)
185 .c_str());
186 }
187
grpc_channelz_get_servers(intptr_t start_server_id)188 char* grpc_channelz_get_servers(intptr_t start_server_id) {
189 return gpr_strdup(
190 grpc_core::channelz::ChannelzRegistry::GetServers(start_server_id)
191 .c_str());
192 }
193
grpc_channelz_get_server(intptr_t server_id)194 char* grpc_channelz_get_server(intptr_t server_id) {
195 grpc_core::RefCountedPtr<grpc_core::channelz::BaseNode> server_node =
196 grpc_core::channelz::ChannelzRegistry::Get(server_id);
197 if (server_node == nullptr ||
198 server_node->type() !=
199 grpc_core::channelz::BaseNode::EntityType::kServer) {
200 return nullptr;
201 }
202 grpc_core::Json json = grpc_core::Json::Object{
203 {"server", server_node->RenderJson()},
204 };
205 return gpr_strdup(json.Dump().c_str());
206 }
207
grpc_channelz_get_server_sockets(intptr_t server_id,intptr_t start_socket_id,intptr_t max_results)208 char* grpc_channelz_get_server_sockets(intptr_t server_id,
209 intptr_t start_socket_id,
210 intptr_t max_results) {
211 grpc_core::RefCountedPtr<grpc_core::channelz::BaseNode> base_node =
212 grpc_core::channelz::ChannelzRegistry::Get(server_id);
213 if (base_node == nullptr ||
214 base_node->type() != grpc_core::channelz::BaseNode::EntityType::kServer) {
215 return nullptr;
216 }
217 // This cast is ok since we have just checked to make sure base_node is
218 // actually a server node.
219 grpc_core::channelz::ServerNode* server_node =
220 static_cast<grpc_core::channelz::ServerNode*>(base_node.get());
221 return gpr_strdup(
222 server_node->RenderServerSockets(start_socket_id, max_results).c_str());
223 }
224
grpc_channelz_get_channel(intptr_t channel_id)225 char* grpc_channelz_get_channel(intptr_t channel_id) {
226 grpc_core::RefCountedPtr<grpc_core::channelz::BaseNode> channel_node =
227 grpc_core::channelz::ChannelzRegistry::Get(channel_id);
228 if (channel_node == nullptr ||
229 (channel_node->type() !=
230 grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel &&
231 channel_node->type() !=
232 grpc_core::channelz::BaseNode::EntityType::kInternalChannel)) {
233 return nullptr;
234 }
235 grpc_core::Json json = grpc_core::Json::Object{
236 {"channel", channel_node->RenderJson()},
237 };
238 return gpr_strdup(json.Dump().c_str());
239 }
240
grpc_channelz_get_subchannel(intptr_t subchannel_id)241 char* grpc_channelz_get_subchannel(intptr_t subchannel_id) {
242 grpc_core::RefCountedPtr<grpc_core::channelz::BaseNode> subchannel_node =
243 grpc_core::channelz::ChannelzRegistry::Get(subchannel_id);
244 if (subchannel_node == nullptr ||
245 subchannel_node->type() !=
246 grpc_core::channelz::BaseNode::EntityType::kSubchannel) {
247 return nullptr;
248 }
249 grpc_core::Json json = grpc_core::Json::Object{
250 {"subchannel", subchannel_node->RenderJson()},
251 };
252 return gpr_strdup(json.Dump().c_str());
253 }
254
grpc_channelz_get_socket(intptr_t socket_id)255 char* grpc_channelz_get_socket(intptr_t socket_id) {
256 grpc_core::RefCountedPtr<grpc_core::channelz::BaseNode> socket_node =
257 grpc_core::channelz::ChannelzRegistry::Get(socket_id);
258 if (socket_node == nullptr ||
259 socket_node->type() !=
260 grpc_core::channelz::BaseNode::EntityType::kSocket) {
261 return nullptr;
262 }
263 grpc_core::Json json = grpc_core::Json::Object{
264 {"socket", socket_node->RenderJson()},
265 };
266 return gpr_strdup(json.Dump().c_str());
267 }
268