3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <grpc/impl/codegen/port_platform.h>
21 #include "src/core/lib/channel/channelz.h"
23 #include <grpc/grpc.h>
24 #include <grpc/support/alloc.h>
25 #include <grpc/support/log.h>
26 #include <grpc/support/string_util.h>
31 #include "src/core/lib/channel/channelz_registry.h"
32 #include "src/core/lib/channel/status_util.h"
33 #include "src/core/lib/gpr/host_port.h"
34 #include "src/core/lib/gpr/string.h"
35 #include "src/core/lib/gpr/useful.h"
36 #include "src/core/lib/gprpp/memory.h"
37 #include "src/core/lib/iomgr/error.h"
38 #include "src/core/lib/iomgr/exec_ctx.h"
39 #include "src/core/lib/slice/b64.h"
40 #include "src/core/lib/slice/slice_internal.h"
41 #include "src/core/lib/surface/channel.h"
42 #include "src/core/lib/surface/server.h"
43 #include "src/core/lib/transport/connectivity_state.h"
44 #include "src/core/lib/transport/error_utils.h"
45 #include "src/core/lib/uri/uri_parser.h"
56 void* parent_uuid_copy(void* p) { return p; }
57 void parent_uuid_destroy(void* p) {}
58 int parent_uuid_cmp(void* p1, void* p2) { return GPR_ICMP(p1, p2); }
59 const grpc_arg_pointer_vtable parent_uuid_vtable = {
60 parent_uuid_copy, parent_uuid_destroy, parent_uuid_cmp};
64 grpc_arg MakeParentUuidArg(intptr_t parent_uuid) {
65 // We would ideally like to store the uuid in an integer argument.
66 // Unfortunately, that won't work, because intptr_t (the type used for
67 // uuids) doesn't fit in an int (the type used for integer args).
68 // So instead, we use a hack to store it as a pointer, because
69 // intptr_t should be the same size as void*.
70 static_assert(sizeof(intptr_t) <= sizeof(void*),
71 "can't fit intptr_t inside of void*");
72 return grpc_channel_arg_pointer_create(
73 const_cast<char*>(GRPC_ARG_CHANNELZ_PARENT_UUID),
74 reinterpret_cast<void*>(parent_uuid), &parent_uuid_vtable);
77 intptr_t GetParentUuidFromArgs(const grpc_channel_args& args) {
79 grpc_channel_args_find(&args, GRPC_ARG_CHANNELZ_PARENT_UUID);
80 if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return 0;
81 return reinterpret_cast<intptr_t>(arg->value.pointer.p);
88 BaseNode::BaseNode(EntityType type) : type_(type), uuid_(-1) {
89 // The registry will set uuid_ under its lock.
90 ChannelzRegistry::Register(this);
93 BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
95 char* BaseNode::RenderJsonString() {
96 grpc_json* json = RenderJson();
97 GPR_ASSERT(json != nullptr);
98 char* json_str = grpc_json_dump_to_string(json, 0);
99 grpc_json_destroy(json);
104 // CallCountingHelper
107 CallCountingHelper::CallCountingHelper() {
108 num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
109 per_cpu_counter_data_storage_ = static_cast<AtomicCounterData*>(
110 gpr_zalloc(sizeof(AtomicCounterData) * num_cores_));
113 CallCountingHelper::~CallCountingHelper() {
114 gpr_free(per_cpu_counter_data_storage_);
117 void CallCountingHelper::RecordCallStarted() {
118 gpr_atm_no_barrier_fetch_add(
119 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
121 static_cast<gpr_atm>(1));
122 gpr_atm_no_barrier_store(
123 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
124 .last_call_started_millis,
125 (gpr_atm)ExecCtx::Get()->Now());
128 void CallCountingHelper::RecordCallFailed() {
129 gpr_atm_no_barrier_fetch_add(
130 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
132 static_cast<gpr_atm>(1));
135 void CallCountingHelper::RecordCallSucceeded() {
136 gpr_atm_no_barrier_fetch_add(
137 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
139 static_cast<gpr_atm>(1));
142 void CallCountingHelper::CollectData(CounterData* out) {
143 for (size_t core = 0; core < num_cores_; ++core) {
144 out->calls_started += gpr_atm_no_barrier_load(
145 &per_cpu_counter_data_storage_[core].calls_started);
146 out->calls_succeeded += gpr_atm_no_barrier_load(
147 &per_cpu_counter_data_storage_[core].calls_succeeded);
148 out->calls_failed += gpr_atm_no_barrier_load(
149 &per_cpu_counter_data_storage_[core].calls_failed);
150 gpr_atm last_call = gpr_atm_no_barrier_load(
151 &per_cpu_counter_data_storage_[core].last_call_started_millis);
152 if (last_call > out->last_call_started_millis) {
153 out->last_call_started_millis = last_call;
158 void CallCountingHelper::PopulateCallCounts(grpc_json* json) {
159 grpc_json* json_iterator = nullptr;
162 if (data.calls_started != 0) {
163 json_iterator = grpc_json_add_number_string_child(
164 json, json_iterator, "callsStarted", data.calls_started);
166 if (data.calls_succeeded != 0) {
167 json_iterator = grpc_json_add_number_string_child(
168 json, json_iterator, "callsSucceeded", data.calls_succeeded);
170 if (data.calls_failed) {
171 json_iterator = grpc_json_add_number_string_child(
172 json, json_iterator, "callsFailed", data.calls_failed);
174 if (data.calls_started != 0) {
175 gpr_timespec ts = grpc_millis_to_timespec(data.last_call_started_millis,
178 grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
179 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
187 ChannelNode::ChannelNode(UniquePtr<char> target,
188 size_t channel_tracer_max_nodes, intptr_t parent_uuid)
189 : BaseNode(parent_uuid == 0 ? EntityType::kTopLevelChannel
190 : EntityType::kInternalChannel),
191 target_(std::move(target)),
192 trace_(channel_tracer_max_nodes),
193 parent_uuid_(parent_uuid) {}
195 grpc_json* ChannelNode::RenderJson() {
196 // We need to track these three json objects to build our object
197 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
198 grpc_json* json = top_level_json;
199 grpc_json* json_iterator = nullptr;
200 // create and fill the ref child
201 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
202 GRPC_JSON_OBJECT, false);
203 json = json_iterator;
204 json_iterator = nullptr;
205 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
206 "channelId", uuid());
207 // reset json iterators to top level object
208 json = top_level_json;
209 json_iterator = nullptr;
210 // create and fill the data child.
211 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
212 GRPC_JSON_OBJECT, false);
214 json_iterator = nullptr;
215 // connectivity state
216 // If low-order bit is on, then the field is set.
217 int state_field = connectivity_state_.Load(MemoryOrder::RELAXED);
218 if ((state_field & 1) != 0) {
219 grpc_connectivity_state state =
220 static_cast<grpc_connectivity_state>(state_field >> 1);
221 json = grpc_json_create_child(nullptr, json, "state", nullptr,
222 GRPC_JSON_OBJECT, false);
223 grpc_json_create_child(nullptr, json, "state",
224 grpc_connectivity_state_name(state),
225 GRPC_JSON_STRING, false);
228 // populate the target.
229 GPR_ASSERT(target_.get() != nullptr);
230 grpc_json_create_child(nullptr, json, "target", target_.get(),
231 GRPC_JSON_STRING, false);
232 // fill in the channel trace if applicable
233 grpc_json* trace_json = trace_.RenderJson();
234 if (trace_json != nullptr) {
235 trace_json->key = "trace"; // this object is named trace in channelz.proto
236 grpc_json_link_child(json, trace_json, nullptr);
238 // ask CallCountingHelper to populate trace and call count data.
239 call_counter_.PopulateCallCounts(json);
240 json = top_level_json;
241 // template method. Child classes may override this to add their specific
243 PopulateChildRefs(json);
244 return top_level_json;
247 void ChannelNode::PopulateChildRefs(grpc_json* json) {
248 MutexLock lock(&child_mu_);
249 grpc_json* json_iterator = nullptr;
250 if (!child_subchannels_.empty()) {
251 grpc_json* array_parent = grpc_json_create_child(
252 nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false);
253 for (const auto& p : child_subchannels_) {
255 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
256 GRPC_JSON_OBJECT, false);
257 grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId",
261 if (!child_channels_.empty()) {
262 grpc_json* array_parent = grpc_json_create_child(
263 nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false);
264 json_iterator = nullptr;
265 for (const auto& p : child_channels_) {
267 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
268 GRPC_JSON_OBJECT, false);
269 grpc_json_add_number_string_child(json_iterator, nullptr, "channelId",
275 void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
276 // Store with low-order bit set to indicate that the field is set.
277 int state_field = (state << 1) + 1;
278 connectivity_state_.Store(state_field, MemoryOrder::RELAXED);
281 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
282 MutexLock lock(&child_mu_);
283 child_channels_.insert(MakePair(child_uuid, true));
286 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
287 MutexLock lock(&child_mu_);
288 child_channels_.erase(child_uuid);
291 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
292 MutexLock lock(&child_mu_);
293 child_subchannels_.insert(MakePair(child_uuid, true));
296 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
297 MutexLock lock(&child_mu_);
298 child_subchannels_.erase(child_uuid);
305 ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes)
306 : BaseNode(EntityType::kServer),
308 trace_(channel_tracer_max_nodes) {}
310 ServerNode::~ServerNode() {}
312 char* ServerNode::RenderServerSockets(intptr_t start_socket_id,
313 intptr_t max_results) {
314 // if user does not set max_results, we choose 500.
315 size_t pagination_limit = max_results == 0 ? 500 : max_results;
316 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
317 grpc_json* json = top_level_json;
318 grpc_json* json_iterator = nullptr;
319 ChildSocketsList socket_refs;
320 grpc_server_populate_server_sockets(server_, &socket_refs, start_socket_id);
321 // declared early so it can be used outside of the loop.
323 if (!socket_refs.empty()) {
324 // create list of socket refs
325 grpc_json* array_parent = grpc_json_create_child(
326 nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
327 for (i = 0; i < GPR_MIN(socket_refs.size(), pagination_limit); ++i) {
328 grpc_json* socket_ref_json = grpc_json_create_child(
329 nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false);
330 json_iterator = grpc_json_add_number_string_child(
331 socket_ref_json, nullptr, "socketId", socket_refs[i]->uuid());
332 grpc_json_create_child(json_iterator, socket_ref_json, "name",
333 socket_refs[i]->remote(), GRPC_JSON_STRING, false);
336 if (i == socket_refs.size()) {
337 json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
338 GRPC_JSON_TRUE, false);
340 char* json_str = grpc_json_dump_to_string(top_level_json, 0);
341 grpc_json_destroy(top_level_json);
345 grpc_json* ServerNode::RenderJson() {
346 // We need to track these three json objects to build our object
347 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
348 grpc_json* json = top_level_json;
349 grpc_json* json_iterator = nullptr;
350 // create and fill the ref child
351 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
352 GRPC_JSON_OBJECT, false);
353 json = json_iterator;
354 json_iterator = nullptr;
355 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
357 // reset json iterators to top level object
358 json = top_level_json;
359 json_iterator = nullptr;
360 // create and fill the data child.
361 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
362 GRPC_JSON_OBJECT, false);
364 json_iterator = nullptr;
365 // fill in the channel trace if applicable
366 grpc_json* trace_json = trace_.RenderJson();
367 if (trace_json != nullptr) {
368 trace_json->key = "trace"; // this object is named trace in channelz.proto
369 grpc_json_link_child(json, trace_json, nullptr);
371 // ask CallCountingHelper to populate trace and call count data.
372 call_counter_.PopulateCallCounts(json);
373 json = top_level_json;
374 ChildRefsList listen_sockets;
375 grpc_server_populate_listen_sockets(server_, &listen_sockets);
376 if (!listen_sockets.empty()) {
377 grpc_json* array_parent = grpc_json_create_child(
378 nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false);
379 for (size_t i = 0; i < listen_sockets.size(); ++i) {
381 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
382 GRPC_JSON_OBJECT, false);
383 grpc_json_add_number_string_child(json_iterator, nullptr, "socketId",
387 return top_level_json;
396 void PopulateSocketAddressJson(grpc_json* json, const char* name,
397 const char* addr_str) {
398 if (addr_str == nullptr) return;
399 grpc_json* json_iterator = nullptr;
400 json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr,
401 GRPC_JSON_OBJECT, false);
402 json = json_iterator;
403 json_iterator = nullptr;
404 grpc_uri* uri = grpc_uri_parse(addr_str, true);
405 if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) ||
406 (strcmp(uri->scheme, "ipv6") == 0))) {
407 const char* host_port = uri->path;
408 if (*host_port == '/') ++host_port;
409 char* host = nullptr;
410 char* port = nullptr;
411 GPR_ASSERT(gpr_split_host_port(host_port, &host, &port));
413 if (port != nullptr) {
414 port_num = atoi(port);
416 char* b64_host = grpc_base64_encode(host, strlen(host), false, false);
417 json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address",
418 nullptr, GRPC_JSON_OBJECT, false);
419 json = json_iterator;
420 json_iterator = nullptr;
421 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
423 json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
424 b64_host, GRPC_JSON_STRING, true);
427 } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) {
428 json_iterator = grpc_json_create_child(json_iterator, json, "uds_address",
429 nullptr, GRPC_JSON_OBJECT, false);
430 json = json_iterator;
431 json_iterator = nullptr;
433 grpc_json_create_child(json_iterator, json, "filename",
434 gpr_strdup(uri->path), GRPC_JSON_STRING, true);
436 json_iterator = grpc_json_create_child(json_iterator, json, "other_address",
437 nullptr, GRPC_JSON_OBJECT, false);
438 json = json_iterator;
439 json_iterator = nullptr;
440 json_iterator = grpc_json_create_child(json_iterator, json, "name",
441 addr_str, GRPC_JSON_STRING, false);
443 grpc_uri_destroy(uri);
448 SocketNode::SocketNode(UniquePtr<char> local, UniquePtr<char> remote)
449 : BaseNode(EntityType::kSocket),
450 local_(std::move(local)),
451 remote_(std::move(remote)) {}
453 void SocketNode::RecordStreamStartedFromLocal() {
454 gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
455 gpr_atm_no_barrier_store(&last_local_stream_created_millis_,
456 (gpr_atm)ExecCtx::Get()->Now());
459 void SocketNode::RecordStreamStartedFromRemote() {
460 gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
461 gpr_atm_no_barrier_store(&last_remote_stream_created_millis_,
462 (gpr_atm)ExecCtx::Get()->Now());
465 void SocketNode::RecordMessagesSent(uint32_t num_sent) {
466 gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast<gpr_atm>(num_sent));
467 gpr_atm_no_barrier_store(&last_message_sent_millis_,
468 (gpr_atm)ExecCtx::Get()->Now());
471 void SocketNode::RecordMessageReceived() {
472 gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast<gpr_atm>(1));
473 gpr_atm_no_barrier_store(&last_message_received_millis_,
474 (gpr_atm)ExecCtx::Get()->Now());
477 grpc_json* SocketNode::RenderJson() {
478 // We need to track these three json objects to build our object
479 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
480 grpc_json* json = top_level_json;
481 grpc_json* json_iterator = nullptr;
482 // create and fill the ref child
483 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
484 GRPC_JSON_OBJECT, false);
485 json = json_iterator;
486 json_iterator = nullptr;
487 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
489 json = top_level_json;
490 PopulateSocketAddressJson(json, "remote", remote_.get());
491 PopulateSocketAddressJson(json, "local", local_.get());
492 // reset json iterators to top level object
493 json = top_level_json;
494 json_iterator = nullptr;
495 // create and fill the data child.
496 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
497 GRPC_JSON_OBJECT, false);
499 json_iterator = nullptr;
501 gpr_atm streams_started = gpr_atm_no_barrier_load(&streams_started_);
502 if (streams_started != 0) {
503 json_iterator = grpc_json_add_number_string_child(
504 json, json_iterator, "streamsStarted", streams_started);
505 gpr_atm last_local_stream_created_millis =
506 gpr_atm_no_barrier_load(&last_local_stream_created_millis_);
507 if (last_local_stream_created_millis != 0) {
508 ts = grpc_millis_to_timespec(last_local_stream_created_millis,
510 json_iterator = grpc_json_create_child(
511 json_iterator, json, "lastLocalStreamCreatedTimestamp",
512 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
514 gpr_atm last_remote_stream_created_millis =
515 gpr_atm_no_barrier_load(&last_remote_stream_created_millis_);
516 if (last_remote_stream_created_millis != 0) {
517 ts = grpc_millis_to_timespec(last_remote_stream_created_millis,
519 json_iterator = grpc_json_create_child(
520 json_iterator, json, "lastRemoteStreamCreatedTimestamp",
521 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
524 gpr_atm streams_succeeded = gpr_atm_no_barrier_load(&streams_succeeded_);
525 if (streams_succeeded != 0) {
526 json_iterator = grpc_json_add_number_string_child(
527 json, json_iterator, "streamsSucceeded", streams_succeeded);
529 gpr_atm streams_failed = gpr_atm_no_barrier_load(&streams_failed_);
530 if (streams_failed) {
531 json_iterator = grpc_json_add_number_string_child(
532 json, json_iterator, "streamsFailed", streams_failed);
534 gpr_atm messages_sent = gpr_atm_no_barrier_load(&messages_sent_);
535 if (messages_sent != 0) {
536 json_iterator = grpc_json_add_number_string_child(
537 json, json_iterator, "messagesSent", messages_sent);
538 ts = grpc_millis_to_timespec(
539 gpr_atm_no_barrier_load(&last_message_sent_millis_),
542 grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp",
543 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
545 gpr_atm messages_received = gpr_atm_no_barrier_load(&messages_received_);
546 if (messages_received != 0) {
547 json_iterator = grpc_json_add_number_string_child(
548 json, json_iterator, "messagesReceived", messages_received);
549 ts = grpc_millis_to_timespec(
550 gpr_atm_no_barrier_load(&last_message_received_millis_),
552 json_iterator = grpc_json_create_child(
553 json_iterator, json, "lastMessageReceivedTimestamp",
554 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
556 gpr_atm keepalives_sent = gpr_atm_no_barrier_load(&keepalives_sent_);
557 if (keepalives_sent != 0) {
558 json_iterator = grpc_json_add_number_string_child(
559 json, json_iterator, "keepAlivesSent", keepalives_sent);
561 return top_level_json;
568 ListenSocketNode::ListenSocketNode(UniquePtr<char> local_addr)
569 : BaseNode(EntityType::kSocket), local_addr_(std::move(local_addr)) {}
571 grpc_json* ListenSocketNode::RenderJson() {
572 // We need to track these three json objects to build our object
573 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
574 grpc_json* json = top_level_json;
575 grpc_json* json_iterator = nullptr;
576 // create and fill the ref child
577 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
578 GRPC_JSON_OBJECT, false);
579 json = json_iterator;
580 json_iterator = nullptr;
581 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
583 json = top_level_json;
584 PopulateSocketAddressJson(json, "local", local_addr_.get());
586 return top_level_json;
589 } // namespace channelz
590 } // namespace grpc_core