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 
23 #include <grpc/grpc.h>
24 #include <grpc/support/alloc.h>
25 #include <grpc/support/log.h>
26 #include <grpc/support/string_util.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "src/core/lib/channel/status_util.h"
32 #include "src/core/lib/gpr/string.h"
33 #include "src/core/lib/gpr/useful.h"
34 #include "src/core/lib/gprpp/memory.h"
35 #include "src/core/lib/iomgr/error.h"
36 #include "src/core/lib/slice/slice_internal.h"
37 #include "src/core/lib/surface/channel.h"
38 #include "src/core/lib/transport/connectivity_state.h"
39 #include "src/core/lib/transport/error_utils.h"
40 
41 namespace grpc_core {
42 namespace channelz {
43 
TraceEvent(Severity severity,grpc_slice data,RefCountedPtr<BaseNode> referenced_entity)44 ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data,
45                                      RefCountedPtr<BaseNode> referenced_entity)
46     : severity_(severity),
47       data_(data),
48       timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
49                                          GPR_CLOCK_REALTIME)),
50       next_(nullptr),
51       referenced_entity_(std::move(referenced_entity)) {}
52 
TraceEvent(Severity severity,grpc_slice data)53 ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data)
54     : severity_(severity),
55       data_(data),
56       timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
57                                          GPR_CLOCK_REALTIME)),
58       next_(nullptr) {}
59 
~TraceEvent()60 ChannelTrace::TraceEvent::~TraceEvent() { grpc_slice_unref_internal(data_); }
61 
ChannelTrace(size_t max_events)62 ChannelTrace::ChannelTrace(size_t max_events)
63     : num_events_logged_(0),
64       list_size_(0),
65       max_list_size_(max_events),
66       head_trace_(nullptr),
67       tail_trace_(nullptr) {
68   if (max_list_size_ == 0) return;  // tracing is disabled if max_events == 0
69   gpr_mu_init(&tracer_mu_);
70   time_created_ = grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
71                                           GPR_CLOCK_REALTIME);
72 }
73 
~ChannelTrace()74 ChannelTrace::~ChannelTrace() {
75   if (max_list_size_ == 0) return;  // tracing is disabled if max_events == 0
76   TraceEvent* it = head_trace_;
77   while (it != nullptr) {
78     TraceEvent* to_free = it;
79     it = it->next();
80     Delete<TraceEvent>(to_free);
81   }
82   gpr_mu_destroy(&tracer_mu_);
83 }
84 
AddTraceEventHelper(TraceEvent * new_trace_event)85 void ChannelTrace::AddTraceEventHelper(TraceEvent* new_trace_event) {
86   ++num_events_logged_;
87   // first event case
88   if (head_trace_ == nullptr) {
89     head_trace_ = tail_trace_ = new_trace_event;
90   }
91   // regular event add case
92   else {
93     tail_trace_->set_next(new_trace_event);
94     tail_trace_ = tail_trace_->next();
95   }
96   ++list_size_;
97   // maybe garbage collect the end
98   if (list_size_ > max_list_size_) {
99     TraceEvent* to_free = head_trace_;
100     head_trace_ = head_trace_->next();
101     Delete<TraceEvent>(to_free);
102     --list_size_;
103   }
104 }
105 
AddTraceEvent(Severity severity,grpc_slice data)106 void ChannelTrace::AddTraceEvent(Severity severity, grpc_slice data) {
107   if (max_list_size_ == 0) return;  // tracing is disabled if max_events == 0
108   AddTraceEventHelper(New<TraceEvent>(severity, data));
109 }
110 
AddTraceEventWithReference(Severity severity,grpc_slice data,RefCountedPtr<BaseNode> referenced_entity)111 void ChannelTrace::AddTraceEventWithReference(
112     Severity severity, grpc_slice data,
113     RefCountedPtr<BaseNode> referenced_entity) {
114   if (max_list_size_ == 0) return;  // tracing is disabled if max_events == 0
115   // create and fill up the new event
116   AddTraceEventHelper(
117       New<TraceEvent>(severity, data, std::move(referenced_entity)));
118 }
119 
120 namespace {
121 
severity_string(ChannelTrace::Severity severity)122 const char* severity_string(ChannelTrace::Severity severity) {
123   switch (severity) {
124     case ChannelTrace::Severity::Info:
125       return "CT_INFO";
126     case ChannelTrace::Severity::Warning:
127       return "CT_WARNING";
128     case ChannelTrace::Severity::Error:
129       return "CT_ERROR";
130     default:
131       GPR_UNREACHABLE_CODE(return "CT_UNKNOWN");
132   }
133 }
134 
135 }  // anonymous namespace
136 
RenderTraceEvent(grpc_json * json) const137 void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const {
138   grpc_json* json_iterator = nullptr;
139   json_iterator = grpc_json_create_child(json_iterator, json, "description",
140                                          grpc_slice_to_c_string(data_),
141                                          GRPC_JSON_STRING, true);
142   json_iterator = grpc_json_create_child(json_iterator, json, "severity",
143                                          severity_string(severity_),
144                                          GRPC_JSON_STRING, false);
145   json_iterator = grpc_json_create_child(json_iterator, json, "timestamp",
146                                          gpr_format_timespec(timestamp_),
147                                          GRPC_JSON_STRING, true);
148   if (referenced_entity_ != nullptr) {
149     const bool is_channel =
150         (referenced_entity_->type() == BaseNode::EntityType::kTopLevelChannel ||
151          referenced_entity_->type() == BaseNode::EntityType::kInternalChannel);
152     char* uuid_str;
153     gpr_asprintf(&uuid_str, "%" PRIdPTR, referenced_entity_->uuid());
154     grpc_json* child_ref = grpc_json_create_child(
155         json_iterator, json, is_channel ? "channelRef" : "subchannelRef",
156         nullptr, GRPC_JSON_OBJECT, false);
157     json_iterator = grpc_json_create_child(
158         nullptr, child_ref, is_channel ? "channelId" : "subchannelId", uuid_str,
159         GRPC_JSON_STRING, true);
160     json_iterator = child_ref;
161   }
162 }
163 
RenderJson() const164 grpc_json* ChannelTrace::RenderJson() const {
165   if (!max_list_size_)
166     return nullptr;  // tracing is disabled if max_events == 0
167   grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT);
168   grpc_json* json_iterator = nullptr;
169   if (num_events_logged_ > 0) {
170     json_iterator = grpc_json_add_number_string_child(
171         json, json_iterator, "numEventsLogged", num_events_logged_);
172   }
173   json_iterator = grpc_json_create_child(
174       json_iterator, json, "creationTimestamp",
175       gpr_format_timespec(time_created_), GRPC_JSON_STRING, true);
176   // only add in the event list if it is non-empty.
177   if (num_events_logged_ > 0) {
178     grpc_json* events = grpc_json_create_child(json_iterator, json, "events",
179                                                nullptr, GRPC_JSON_ARRAY, false);
180     json_iterator = nullptr;
181     TraceEvent* it = head_trace_;
182     while (it != nullptr) {
183       json_iterator = grpc_json_create_child(json_iterator, events, nullptr,
184                                              nullptr, GRPC_JSON_OBJECT, false);
185       it->RenderTraceEvent(json_iterator);
186       it = it->next();
187     }
188   }
189   return json;
190 }
191 
192 }  // namespace channelz
193 }  // namespace grpc_core
194