mirror of https://github.com/grpc/grpc.git
Merge pull request #13883 from ncteisen/channel-tracing
Channel Tracing Implementation; Part 1reviewable/pr14483/r5
commit
1571abdb7f
45 changed files with 1818 additions and 71 deletions
@ -0,0 +1,238 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include "src/core/lib/channel/channel_trace.h" |
||||
|
||||
#include <grpc/grpc.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include "src/core/lib/channel/channel_trace_registry.h" |
||||
#include "src/core/lib/channel/status_util.h" |
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "src/core/lib/gpr/useful.h" |
||||
#include "src/core/lib/gprpp/memory.h" |
||||
#include "src/core/lib/iomgr/error.h" |
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/transport/connectivity_state.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
ChannelTrace::TraceEvent::TraceEvent( |
||||
Severity severity, grpc_slice data, |
||||
RefCountedPtr<ChannelTrace> referenced_tracer, ReferencedType type) |
||||
: severity_(severity), |
||||
data_(data), |
||||
timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(), |
||||
GPR_CLOCK_REALTIME)), |
||||
next_(nullptr), |
||||
referenced_tracer_(std::move(referenced_tracer)), |
||||
referenced_type_(type) {} |
||||
|
||||
ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data) |
||||
: severity_(severity), |
||||
data_(data), |
||||
timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(), |
||||
GPR_CLOCK_REALTIME)), |
||||
next_(nullptr) {} |
||||
|
||||
ChannelTrace::TraceEvent::~TraceEvent() { grpc_slice_unref_internal(data_); } |
||||
|
||||
ChannelTrace::ChannelTrace(size_t max_events) |
||||
: channel_uuid_(-1), |
||||
num_events_logged_(0), |
||||
list_size_(0), |
||||
max_list_size_(max_events), |
||||
head_trace_(nullptr), |
||||
tail_trace_(nullptr) { |
||||
if (max_list_size_ == 0) return; // tracing is disabled if max_events == 0
|
||||
gpr_mu_init(&tracer_mu_); |
||||
channel_uuid_ = grpc_channel_trace_registry_register_channel_trace(this); |
||||
time_created_ = grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(), |
||||
GPR_CLOCK_REALTIME); |
||||
} |
||||
|
||||
ChannelTrace::~ChannelTrace() { |
||||
if (max_list_size_ == 0) return; // tracing is disabled if max_events == 0
|
||||
TraceEvent* it = head_trace_; |
||||
while (it != nullptr) { |
||||
TraceEvent* to_free = it; |
||||
it = it->next(); |
||||
Delete<TraceEvent>(to_free); |
||||
} |
||||
grpc_channel_trace_registry_unregister_channel_trace(channel_uuid_); |
||||
gpr_mu_destroy(&tracer_mu_); |
||||
} |
||||
|
||||
intptr_t ChannelTrace::GetUuid() const { return channel_uuid_; } |
||||
|
||||
void ChannelTrace::AddTraceEventHelper(TraceEvent* new_trace_event) { |
||||
++num_events_logged_; |
||||
// first event case
|
||||
if (head_trace_ == nullptr) { |
||||
head_trace_ = tail_trace_ = new_trace_event; |
||||
} |
||||
// regular event add case
|
||||
else { |
||||
tail_trace_->set_next(new_trace_event); |
||||
tail_trace_ = tail_trace_->next(); |
||||
} |
||||
++list_size_; |
||||
// maybe garbage collect the end
|
||||
if (list_size_ > max_list_size_) { |
||||
TraceEvent* to_free = head_trace_; |
||||
head_trace_ = head_trace_->next(); |
||||
Delete<TraceEvent>(to_free); |
||||
--list_size_; |
||||
} |
||||
} |
||||
|
||||
void ChannelTrace::AddTraceEvent(Severity severity, grpc_slice data) { |
||||
if (max_list_size_ == 0) return; // tracing is disabled if max_events == 0
|
||||
AddTraceEventHelper(New<TraceEvent>(severity, data)); |
||||
} |
||||
|
||||
void ChannelTrace::AddTraceEventReferencingChannel( |
||||
Severity severity, grpc_slice data, |
||||
RefCountedPtr<ChannelTrace> referenced_tracer) { |
||||
if (max_list_size_ == 0) return; // tracing is disabled if max_events == 0
|
||||
// create and fill up the new event
|
||||
AddTraceEventHelper( |
||||
New<TraceEvent>(severity, data, std::move(referenced_tracer), Channel)); |
||||
} |
||||
|
||||
void ChannelTrace::AddTraceEventReferencingSubchannel( |
||||
Severity severity, grpc_slice data, |
||||
RefCountedPtr<ChannelTrace> referenced_tracer) { |
||||
if (max_list_size_ == 0) return; // tracing is disabled if max_events == 0
|
||||
// create and fill up the new event
|
||||
AddTraceEventHelper(New<TraceEvent>( |
||||
severity, data, std::move(referenced_tracer), Subchannel)); |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
// returns an allocated string that represents tm according to RFC-3339, and,
|
||||
// more specifically, follows:
|
||||
// https://developers.google.com/protocol-buffers/docs/proto3#json
|
||||
//
|
||||
// "Uses RFC 3339, where generated output will always be Z-normalized and uses
|
||||
// 0, 3, 6 or 9 fractional digits."
|
||||
char* fmt_time(gpr_timespec tm) { |
||||
char time_buffer[35]; |
||||
char ns_buffer[11]; // '.' + 9 digits of precision
|
||||
struct tm* tm_info = localtime((const time_t*)&tm.tv_sec); |
||||
strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%H:%M:%S", tm_info); |
||||
snprintf(ns_buffer, 11, ".%09d", tm.tv_nsec); |
||||
// This loop trims off trailing zeros by inserting a null character that the
|
||||
// right point. We iterate in chunks of three because we want 0, 3, 6, or 9
|
||||
// fractional digits.
|
||||
for (int i = 7; i >= 1; i -= 3) { |
||||
if (ns_buffer[i] == '0' && ns_buffer[i + 1] == '0' && |
||||
ns_buffer[i + 2] == '0') { |
||||
ns_buffer[i] = '\0'; |
||||
// Edge case in which all fractional digits were 0.
|
||||
if (i == 1) { |
||||
ns_buffer[0] = '\0'; |
||||
} |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
char* full_time_str; |
||||
gpr_asprintf(&full_time_str, "%s%sZ", time_buffer, ns_buffer); |
||||
return full_time_str; |
||||
} |
||||
|
||||
const char* severity_string(ChannelTrace::Severity severity) { |
||||
switch (severity) { |
||||
case ChannelTrace::Severity::Info: |
||||
return "CT_INFO"; |
||||
case ChannelTrace::Severity::Warning: |
||||
return "CT_WARNING"; |
||||
case ChannelTrace::Severity::Error: |
||||
return "CT_ERROR"; |
||||
default: |
||||
GPR_UNREACHABLE_CODE(return "CT_UNKNOWN"); |
||||
} |
||||
} |
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const { |
||||
grpc_json* json_iterator = nullptr; |
||||
json_iterator = grpc_json_create_child(json_iterator, json, "description", |
||||
grpc_slice_to_c_string(data_), |
||||
GRPC_JSON_STRING, true); |
||||
json_iterator = grpc_json_create_child(json_iterator, json, "severity", |
||||
severity_string(severity_), |
||||
GRPC_JSON_STRING, false); |
||||
json_iterator = |
||||
grpc_json_create_child(json_iterator, json, "timestamp", |
||||
fmt_time(timestamp_), GRPC_JSON_STRING, true); |
||||
if (referenced_tracer_ != nullptr) { |
||||
char* uuid_str; |
||||
gpr_asprintf(&uuid_str, "%" PRIdPTR, referenced_tracer_->channel_uuid_); |
||||
grpc_json* child_ref = grpc_json_create_child( |
||||
json_iterator, json, |
||||
(referenced_type_ == Channel) ? "channelRef" : "subchannelRef", nullptr, |
||||
GRPC_JSON_OBJECT, false); |
||||
json_iterator = grpc_json_create_child( |
||||
nullptr, child_ref, |
||||
(referenced_type_ == Channel) ? "channelId" : "subchannelId", uuid_str, |
||||
GRPC_JSON_STRING, true); |
||||
json_iterator = child_ref; |
||||
} |
||||
} |
||||
|
||||
char* ChannelTrace::RenderTrace() const { |
||||
if (!max_list_size_) |
||||
return nullptr; // tracing is disabled if max_events == 0
|
||||
grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT); |
||||
char* num_events_logged_str; |
||||
gpr_asprintf(&num_events_logged_str, "%" PRId64, num_events_logged_); |
||||
grpc_json* json_iterator = nullptr; |
||||
json_iterator = |
||||
grpc_json_create_child(json_iterator, json, "numEventsLogged", |
||||
num_events_logged_str, GRPC_JSON_STRING, true); |
||||
json_iterator = |
||||
grpc_json_create_child(json_iterator, json, "creationTime", |
||||
fmt_time(time_created_), GRPC_JSON_STRING, true); |
||||
grpc_json* events = grpc_json_create_child(json_iterator, json, "events", |
||||
nullptr, GRPC_JSON_ARRAY, false); |
||||
json_iterator = nullptr; |
||||
TraceEvent* it = head_trace_; |
||||
while (it != nullptr) { |
||||
json_iterator = grpc_json_create_child(json_iterator, events, nullptr, |
||||
nullptr, GRPC_JSON_OBJECT, false); |
||||
it->RenderTraceEvent(json_iterator); |
||||
it = it->next(); |
||||
} |
||||
char* json_str = grpc_json_dump_to_string(json, 0); |
||||
grpc_json_destroy(json); |
||||
return json_str; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,133 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H |
||||
#define GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include <grpc/grpc.h> |
||||
#include "src/core/lib/gprpp/ref_counted.h" |
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h" |
||||
#include "src/core/lib/iomgr/error.h" |
||||
#include "src/core/lib/json/json.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Object used to hold live data for a channel. This data is exposed via the
|
||||
// channelz service:
|
||||
// https://github.com/grpc/proposal/blob/master/A14-channelz.md
|
||||
class ChannelTrace : public RefCounted<ChannelTrace> { |
||||
public: |
||||
ChannelTrace(size_t max_events); |
||||
~ChannelTrace(); |
||||
|
||||
// returns the tracer's uuid
|
||||
intptr_t GetUuid() const; |
||||
|
||||
enum Severity { |
||||
Unset = 0, // never to be used
|
||||
Info, // we start at 1 to avoid using proto default values
|
||||
Warning, |
||||
Error |
||||
}; |
||||
|
||||
// Adds a new trace event to the tracing object
|
||||
//
|
||||
// TODO(ncteisen): as this call is used more and more throughout the gRPC
|
||||
// stack, determine if it makes more sense to accept a char* instead of a
|
||||
// slice.
|
||||
void AddTraceEvent(Severity severity, grpc_slice data); |
||||
|
||||
// Adds a new trace event to the tracing object. This trace event refers to a
|
||||
// an event on a child of the channel. For example, if this channel has
|
||||
// created a new subchannel, then it would record that with a TraceEvent
|
||||
// referencing the new subchannel.
|
||||
//
|
||||
// TODO(ncteisen): Once channelz is implemented, the events should reference
|
||||
// the overall channelz object, not just the ChannelTrace object.
|
||||
// TODO(ncteisen): as this call is used more and more throughout the gRPC
|
||||
// stack, determine if it makes more sense to accept a char* instead of a
|
||||
// slice.
|
||||
void AddTraceEventReferencingChannel( |
||||
Severity severity, grpc_slice data, |
||||
RefCountedPtr<ChannelTrace> referenced_tracer); |
||||
void AddTraceEventReferencingSubchannel( |
||||
Severity severity, grpc_slice data, |
||||
RefCountedPtr<ChannelTrace> referenced_tracer); |
||||
|
||||
// Returns the tracing data rendered as a grpc json string.
|
||||
// The string is owned by the caller and must be freed.
|
||||
char* RenderTrace() const; |
||||
|
||||
private: |
||||
// Types of objects that can be references by trace events.
|
||||
enum ReferencedType { Channel, Subchannel }; |
||||
// Private class to encapsulate all the data and bookkeeping needed for a
|
||||
// a trace event.
|
||||
class TraceEvent { |
||||
public: |
||||
// Constructor for a TraceEvent that references a different channel.
|
||||
// TODO(ncteisen): once channelz is implemented, this should reference the
|
||||
// overall channelz object, not just the ChannelTrace object
|
||||
TraceEvent(Severity severity, grpc_slice data, |
||||
RefCountedPtr<ChannelTrace> referenced_tracer, |
||||
ReferencedType type); |
||||
|
||||
// Constructor for a TraceEvent that does not reverence a different
|
||||
// channel.
|
||||
TraceEvent(Severity severity, grpc_slice data); |
||||
|
||||
~TraceEvent(); |
||||
|
||||
// Renders the data inside of this TraceEvent into a json object. This is
|
||||
// used by the ChannelTrace, when it is rendering itself.
|
||||
void RenderTraceEvent(grpc_json* json) const; |
||||
|
||||
// set and get for the next_ pointer.
|
||||
TraceEvent* next() const { return next_; } |
||||
void set_next(TraceEvent* next) { next_ = next; } |
||||
|
||||
private: |
||||
Severity severity_; |
||||
grpc_slice data_; |
||||
gpr_timespec timestamp_; |
||||
TraceEvent* next_; |
||||
// the tracer object for the (sub)channel that this trace event refers to.
|
||||
RefCountedPtr<ChannelTrace> referenced_tracer_; |
||||
// the type that the referenced tracer points to. Unused if this trace
|
||||
// does not point to any channel or subchannel
|
||||
ReferencedType referenced_type_; |
||||
}; // TraceEvent
|
||||
|
||||
// Internal helper to add and link in a trace event
|
||||
void AddTraceEventHelper(TraceEvent* new_trace_event); |
||||
|
||||
gpr_mu tracer_mu_; |
||||
intptr_t channel_uuid_; |
||||
uint64_t num_events_logged_; |
||||
size_t list_size_; |
||||
size_t max_list_size_; |
||||
TraceEvent* head_trace_; |
||||
TraceEvent* tail_trace_; |
||||
gpr_timespec time_created_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H */ |
@ -0,0 +1,80 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include "src/core/lib/avl/avl.h" |
||||
#include "src/core/lib/channel/channel_trace.h" |
||||
#include "src/core/lib/channel/channel_trace_registry.h" |
||||
#include "src/core/lib/gpr/useful.h" |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
// file global lock and avl.
|
||||
static gpr_mu g_mu; |
||||
static grpc_avl g_avl; |
||||
static gpr_atm g_uuid = 0; |
||||
|
||||
// avl vtable for uuid (intptr_t) -> ChannelTrace
|
||||
// this table is only looking, it does not own anything.
|
||||
static void destroy_intptr(void* not_used, void* user_data) {} |
||||
static void* copy_intptr(void* key, void* user_data) { return key; } |
||||
static long compare_intptr(void* key1, void* key2, void* user_data) { |
||||
return GPR_ICMP(key1, key2); |
||||
} |
||||
|
||||
static void destroy_channel_trace(void* trace, void* user_data) {} |
||||
static void* copy_channel_trace(void* trace, void* user_data) { return trace; } |
||||
static const grpc_avl_vtable avl_vtable = { |
||||
destroy_intptr, copy_intptr, compare_intptr, destroy_channel_trace, |
||||
copy_channel_trace}; |
||||
|
||||
void grpc_channel_trace_registry_init() { |
||||
gpr_mu_init(&g_mu); |
||||
g_avl = grpc_avl_create(&avl_vtable); |
||||
} |
||||
|
||||
void grpc_channel_trace_registry_shutdown() { |
||||
grpc_avl_unref(g_avl, nullptr); |
||||
gpr_mu_destroy(&g_mu); |
||||
} |
||||
|
||||
intptr_t grpc_channel_trace_registry_register_channel_trace( |
||||
grpc_core::ChannelTrace* channel_trace) { |
||||
intptr_t prior = gpr_atm_no_barrier_fetch_add(&g_uuid, 1); |
||||
gpr_mu_lock(&g_mu); |
||||
g_avl = grpc_avl_add(g_avl, (void*)prior, channel_trace, nullptr); |
||||
gpr_mu_unlock(&g_mu); |
||||
return prior; |
||||
} |
||||
|
||||
void grpc_channel_trace_registry_unregister_channel_trace(intptr_t uuid) { |
||||
gpr_mu_lock(&g_mu); |
||||
g_avl = grpc_avl_remove(g_avl, (void*)uuid, nullptr); |
||||
gpr_mu_unlock(&g_mu); |
||||
} |
||||
|
||||
grpc_core::ChannelTrace* grpc_channel_trace_registry_get_channel_trace( |
||||
intptr_t uuid) { |
||||
gpr_mu_lock(&g_mu); |
||||
grpc_core::ChannelTrace* ret = static_cast<grpc_core::ChannelTrace*>( |
||||
grpc_avl_get(g_avl, (void*)uuid, nullptr)); |
||||
gpr_mu_unlock(&g_mu); |
||||
return ret; |
||||
} |
@ -0,0 +1,43 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_REGISTRY_H |
||||
#define GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_REGISTRY_H |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include "src/core/lib/channel/channel_trace.h" |
||||
|
||||
#include <stdint.h> |
||||
|
||||
// TODO(ncteisen): convert this file to C++
|
||||
|
||||
void grpc_channel_trace_registry_init(); |
||||
void grpc_channel_trace_registry_shutdown(); |
||||
|
||||
// globally registers a ChannelTrace. Returns its unique uuid
|
||||
intptr_t grpc_channel_trace_registry_register_channel_trace( |
||||
grpc_core::ChannelTrace* channel_trace); |
||||
// globally unregisters the ChannelTrace that is associated to uuid.
|
||||
void grpc_channel_trace_registry_unregister_channel_trace(intptr_t uuid); |
||||
// if object with uuid has previously been registered, returns the ChannelTrace
|
||||
// associated with that uuid. Else returns nullptr.
|
||||
grpc_core::ChannelTrace* grpc_channel_trace_registry_get_channel_trace( |
||||
intptr_t uuid); |
||||
|
||||
#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_REGISTRY_H */ |
@ -0,0 +1,26 @@ |
||||
# Copyright 2018 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # Apache v2 |
||||
|
||||
load("//bazel:grpc_build_system.bzl", "grpc_proto_library", "grpc_package") |
||||
|
||||
grpc_package(name = "channelz", visibility = "public") |
||||
|
||||
grpc_proto_library( |
||||
name = "channelz_proto", |
||||
srcs = ["channelz.proto"], |
||||
has_services = True, |
||||
well_known_protos = True, |
||||
) |
@ -0,0 +1,456 @@ |
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package grpc.channelz; |
||||
|
||||
import "google/protobuf/any.proto"; |
||||
import "google/protobuf/duration.proto"; |
||||
import "google/protobuf/timestamp.proto"; |
||||
import "google/protobuf/wrappers.proto"; |
||||
|
||||
// See go/grpc-channelz. |
||||
|
||||
// Channel is a logical grouping of channels, subchannels, and sockets. |
||||
message Channel { |
||||
// The identifier for this channel. |
||||
ChannelRef ref = 1; |
||||
// Data specific to this channel. |
||||
ChannelData data = 2; |
||||
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set. |
||||
|
||||
// There are no ordering guarantees on the order of channel refs. |
||||
// There may not be cycles in the ref graph. |
||||
// A channel ref may be present in more than one channel or subchannel. |
||||
repeated ChannelRef channel_ref = 3; |
||||
|
||||
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set. |
||||
// There are no ordering guarantees on the order of subchannel refs. |
||||
// There may not be cycles in the ref graph. |
||||
// A sub channel ref may be present in more than one channel or subchannel. |
||||
repeated SubchannelRef subchannel_ref = 4; |
||||
|
||||
// There are no ordering guarantees on the order of sockets. |
||||
repeated SocketRef socket = 5; |
||||
} |
||||
|
||||
// Subchannel is a logical grouping of channels, subchannels, and sockets. |
||||
// A subchannel is load balanced over by it's ancestor |
||||
message Subchannel { |
||||
// The identifier for this channel. |
||||
SubchannelRef ref = 1; |
||||
// Data specific to this channel. |
||||
ChannelData data = 2; |
||||
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set. |
||||
|
||||
// There are no ordering guarantees on the order of channel refs. |
||||
// There may not be cycles in the ref graph. |
||||
// A channel ref may be present in more than one channel or subchannel. |
||||
repeated ChannelRef channel_ref = 3; |
||||
|
||||
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set. |
||||
// There are no ordering guarantees on the order of subchannel refs. |
||||
// There may not be cycles in the ref graph. |
||||
// A sub channel ref may be present in more than one channel or subchannel. |
||||
repeated SubchannelRef subchannel_ref = 4; |
||||
|
||||
// There are no ordering guarantees on the order of sockets. |
||||
repeated SocketRef socket = 5; |
||||
} |
||||
|
||||
// These come from the specified states in this document: |
||||
// https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md |
||||
message ChannelConnectivityState { |
||||
enum State { |
||||
UNKNOWN = 0; |
||||
IDLE = 1; |
||||
CONNECTING = 2; |
||||
READY = 3; |
||||
TRANSIENT_FAILURE = 4; |
||||
SHUTDOWN = 5; |
||||
} |
||||
State state = 1; |
||||
} |
||||
|
||||
message ChannelData { |
||||
|
||||
ChannelConnectivityState state = 1; |
||||
|
||||
// The target this channel originally tried to connect to. May be absent |
||||
string target = 2; |
||||
|
||||
ChannelTrace trace = 3; |
||||
|
||||
// The number of calls started on the channel |
||||
int64 calls_started = 4; |
||||
// The number of calls that have completed with an OK status |
||||
int64 calls_succeeded = 5; |
||||
// The number of calls that have a completed with a non-OK status |
||||
int64 calls_failed = 6; |
||||
|
||||
// The last time a call was started on the channel. |
||||
google.protobuf.Timestamp last_call_started_timestamp = 7; |
||||
} |
||||
|
||||
// A trace event is an interesting thing that happened to a channel or |
||||
// subchannel, such as creation, address resolution, subchannel creation, etc. |
||||
message ChannelTraceEvent { |
||||
// High level description of the event. |
||||
string description = 1; |
||||
// The supported severity levels of trace events. |
||||
enum Severity { |
||||
CT_UNKNOWN = 0; |
||||
CT_INFO = 1; |
||||
CT_WARNING = 2; |
||||
CT_ERROR = 3; |
||||
} |
||||
// the severity of the trace event |
||||
Severity severity = 2; |
||||
// When this event occurred. |
||||
google.protobuf.Timestamp timestamp = 3; |
||||
// ref of referenced channel or subchannel. |
||||
// Optional, only present if this event refers to a child object. For example, |
||||
// this field would be filled if this trace event was for a subchannel being |
||||
// created. |
||||
oneof child_ref { |
||||
ChannelRef channel_ref = 4; |
||||
SubchannelRef subchannel_ref = 5; |
||||
} |
||||
} |
||||
|
||||
message ChannelTrace { |
||||
// Number of events ever logged in this tracing object. This can differ from |
||||
// events.size() because events can be overwritten or garbage collected by |
||||
// implementations. |
||||
int64 num_events_logged = 1; |
||||
// Time that this channel was created. |
||||
google.protobuf.Timestamp creation_time = 2; |
||||
// List of events that have occurred on this channel. |
||||
repeated ChannelTraceEvent events = 3; |
||||
} |
||||
|
||||
message ChannelRef { |
||||
// The globally unique id for this channel. Must be a positive number. |
||||
int64 channel_id = 1; |
||||
// An optional name associated with the channel. |
||||
string name = 2; |
||||
// Intentionally don't use field numbers from other refs. |
||||
reserved 3, 4, 5, 6; |
||||
} |
||||
|
||||
message SubchannelRef { |
||||
// The globally unique id for this subchannel. Must be a positive number. |
||||
int64 subchannel_id = 7; |
||||
// An optional name associated with the subchannel. |
||||
string name = 8; |
||||
// Intentionally don't use field numbers from other refs. |
||||
reserved 1, 2, 3, 4, 5, 6; |
||||
} |
||||
|
||||
message SocketRef { |
||||
int64 socket_id = 3; |
||||
// An optional name associated with the socket. |
||||
string name = 4; |
||||
// Intentionally don't use field numbers from other refs. |
||||
reserved 1, 2, 5, 6, 7, 8; |
||||
} |
||||
|
||||
message ServerRef { |
||||
// A globally unique identifier for this server. Must be a positive number. |
||||
int64 server_id = 5; |
||||
// An optional name associated with the server. |
||||
string name = 6; |
||||
// Intentionally don't use field numbers from other refs. |
||||
reserved 1, 2, 3, 4, 7, 8; |
||||
} |
||||
|
||||
message Server { |
||||
ServerRef ref = 1; |
||||
ServerData data = 2; |
||||
|
||||
// The sockets that the server is listening on. There are no ordering |
||||
// guarantees. |
||||
repeated SocketRef listen_socket = 3; |
||||
} |
||||
|
||||
message ServerData { |
||||
ChannelTrace trace = 1; |
||||
|
||||
// The number of incoming calls started on the server |
||||
int64 calls_started = 2; |
||||
// The number of incoming calls that have completed with an OK status |
||||
int64 calls_succeeded = 3; |
||||
// The number of incoming calls that have a completed with a non-OK status |
||||
int64 calls_failed = 4; |
||||
|
||||
// The last time a call was started on the server. |
||||
google.protobuf.Timestamp last_call_started_timestamp = 5; |
||||
} |
||||
|
||||
// Information about an actual connection. Pronounced "sock-ay". |
||||
message Socket { |
||||
SocketRef ref = 1; |
||||
|
||||
SocketData data = 2; |
||||
// The locally bound address. |
||||
Address local = 3; |
||||
// The remote bound address. May be absent. |
||||
Address remote = 4; |
||||
Security security = 5; |
||||
|
||||
// Optional, represents the name of the remote endpoint, if different than |
||||
// the original target name. |
||||
string remote_name = 6; |
||||
} |
||||
|
||||
message SocketData { |
||||
// The number of streams that have been started. |
||||
int64 streams_started = 1; |
||||
// The number of streams that have ended successfully with the EoS bit set for |
||||
// both end points |
||||
int64 streams_succeeded = 2; |
||||
// The number of incoming streams that have a completed with a non-OK status |
||||
int64 streams_failed = 3; |
||||
|
||||
// The number of messages successfully sent on this socket. |
||||
int64 messages_sent = 4; |
||||
int64 messages_received = 5; |
||||
|
||||
// The number of keep alives sent. This is typically implemented with HTTP/2 |
||||
// ping messages. |
||||
int64 keep_alives_sent = 6; |
||||
|
||||
// The last time a stream was created by this endpoint. Usually unset for |
||||
// servers. |
||||
google.protobuf.Timestamp last_local_stream_created_timestamp = 7; |
||||
// The last time a stream was created by the remote endpoint. Usually unset |
||||
// for clients. |
||||
google.protobuf.Timestamp last_remote_stream_created_timestamp = 8; |
||||
|
||||
// The last time a message was sent by this endpoint. |
||||
google.protobuf.Timestamp last_message_sent_timestamp = 9; |
||||
// The last time a message was received by this endpoint. |
||||
google.protobuf.Timestamp last_message_received_timestamp = 10; |
||||
|
||||
// The amount of window, granted to the local endpoint by the remote endpoint. |
||||
// This may be slightly out of date due to network latency. This does NOT |
||||
// include stream level or TCP level flow control info. |
||||
google.protobuf.Int64Value local_flow_control_window = 11; |
||||
|
||||
// The amount of window, granted to the remote endpoint by the local endpoint. |
||||
// This may be slightly out of date due to network latency. This does NOT |
||||
// include stream level or TCP level flow control info. |
||||
google.protobuf.Int64Value remote_flow_control_window = 12; |
||||
|
||||
repeated SocketOption option = 13; |
||||
} |
||||
|
||||
message Address { |
||||
message TcpIpAddress { |
||||
// Either the IPv4 or IPv6 address in bytes. Will either be 4 bytes or 16 |
||||
// bytes in length. |
||||
bytes ip_address = 1; |
||||
// 0-64k, or -1 if not appropriate. |
||||
int32 port = 2; |
||||
} |
||||
// A Unix Domain Socket address. |
||||
message UdsAddress { |
||||
string filename = 1; |
||||
} |
||||
// An address type not included above. |
||||
message OtherAddress { |
||||
// The human readable version of the value. |
||||
string name = 1; |
||||
// The actual address message. |
||||
google.protobuf.Any value = 2; |
||||
} |
||||
|
||||
oneof address { |
||||
TcpIpAddress tcpip_address = 1; |
||||
UdsAddress uds_address = 2; |
||||
OtherAddress other_address = 3; |
||||
} |
||||
} |
||||
|
||||
message Security { |
||||
message Tls { |
||||
// The key exchange used. e.g. X25519 |
||||
string key_exchange = 1; |
||||
// The cipher used. e.g. AES_128_GCM. |
||||
string cipher = 2; |
||||
// the certificate used by this endpoint. |
||||
bytes local_certificate = 3; |
||||
// the certificate used by the remote endpoint. |
||||
bytes remote_certificate = 4; |
||||
} |
||||
message OtherSecurity { |
||||
// The human readable version of the value. |
||||
string name = 1; |
||||
// The actual security details message. |
||||
google.protobuf.Any value = 2; |
||||
} |
||||
oneof model { |
||||
Tls tls = 1; |
||||
OtherSecurity other = 2; |
||||
} |
||||
} |
||||
|
||||
message SocketOption { |
||||
string name = 1; |
||||
// The human readable value of this socket option. At least one of value or |
||||
// additional will be set. |
||||
string value = 2; |
||||
// Additional data associated with the socket option. At least one of value |
||||
// or additional will be set. |
||||
google.protobuf.Any additional = 3; |
||||
} |
||||
|
||||
// For use with SocketOption's additional field. This is primarily used for |
||||
// SO_RCVTIMEO and SO_SNDTIMEO |
||||
message SocketOptionTimeout { |
||||
google.protobuf.Duration duration = 1; |
||||
} |
||||
|
||||
message SocketOptionLinger { |
||||
bool active = 1; |
||||
google.protobuf.Duration duration = 2; |
||||
} |
||||
|
||||
// Tcp info for SOL_TCP, TCP_INFO |
||||
message SocketOptionTcpInfo { |
||||
uint32 tcpi_state = 1; |
||||
|
||||
uint32 tcpi_ca_state = 2; |
||||
uint32 tcpi_retransmits = 3; |
||||
uint32 tcpi_probes = 4; |
||||
uint32 tcpi_backoff = 5; |
||||
uint32 tcpi_options = 6; |
||||
uint32 tcpi_snd_wscale = 7; |
||||
uint32 tcpi_rcv_wscale = 8; |
||||
|
||||
uint32 tcpi_rto = 9; |
||||
uint32 tcpi_ato = 10; |
||||
uint32 tcpi_snd_mss = 11; |
||||
uint32 tcpi_rcv_mss = 12; |
||||
|
||||
uint32 tcpi_unacked = 13; |
||||
uint32 tcpi_sacked = 14; |
||||
uint32 tcpi_lost = 15; |
||||
uint32 tcpi_retrans = 16; |
||||
uint32 tcpi_fackets = 17; |
||||
|
||||
uint32 tcpi_last_data_sent = 18; |
||||
uint32 tcpi_last_ack_sent = 19; |
||||
uint32 tcpi_last_data_recv = 20; |
||||
uint32 tcpi_last_ack_recv = 21; |
||||
|
||||
uint32 tcpi_pmtu = 22; |
||||
uint32 tcpi_rcv_ssthresh = 23; |
||||
uint32 tcpi_rtt = 24; |
||||
uint32 tcpi_rttvar = 25; |
||||
uint32 tcpi_snd_ssthresh = 26; |
||||
uint32 tcpi_snd_cwnd = 27; |
||||
uint32 tcpi_advmss = 28; |
||||
uint32 tcpi_reordering = 29; |
||||
} |
||||
|
||||
service Channelz { |
||||
// Gets all root channels (e.g. channels the application has directly |
||||
// created). This does not include subchannels nor non-top level channels. |
||||
rpc GetTopChannels(GetTopChannelsRequest) returns (GetTopChannelsResponse); |
||||
// Gets all servers that exist in the process. |
||||
rpc GetServers(GetServersRequest) returns (GetServersResponse); |
||||
// Gets all server sockets that exist in the process. |
||||
rpc GetServerSockets(GetServerSocketsRequest) returns (GetServerSocketsResponse); |
||||
// Returns a single Channel, or else a NOT_FOUND code. |
||||
rpc GetChannel(GetChannelRequest) returns (GetChannelResponse); |
||||
// Returns a single Subchannel, or else a NOT_FOUND code. |
||||
rpc GetSubchannel(GetSubchannelRequest) returns (GetSubchannelResponse); |
||||
// Returns a single Socket or else a NOT_FOUND code. |
||||
rpc GetSocket(GetSocketRequest) returns (GetSocketResponse); |
||||
} |
||||
|
||||
message GetServersRequest { |
||||
// start_server_id indicates that only servers at or above this id should be |
||||
// included in the results. |
||||
int64 start_server_id = 1; |
||||
} |
||||
|
||||
message GetServersResponse { |
||||
// list of servers that the connection detail service knows about. Sorted in |
||||
// ascending server_id order. |
||||
repeated Server server = 1; |
||||
// If set, indicates that the list of servers is the final list. Requesting |
||||
// more servers will only return more if they are created after this RPC |
||||
// completes. |
||||
bool end = 2; |
||||
} |
||||
|
||||
message GetServerSocketsRequest { |
||||
int64 server_id = 1; |
||||
// start_socket_id indicates that only sockets at or above this id should be |
||||
// included in the results. |
||||
int64 start_socket_id = 2; |
||||
} |
||||
|
||||
message GetServerSocketsResponse { |
||||
// list of socket refs that the connection detail service knows about. Sorted in |
||||
// ascending socket_id order. |
||||
repeated SocketRef socket_ref = 1; |
||||
// If set, indicates that the list of sockets is the final list. Requesting |
||||
// more sockets will only return more if they are created after this RPC |
||||
// completes. |
||||
bool end = 2; |
||||
} |
||||
|
||||
message GetTopChannelsRequest { |
||||
// start_channel_id indicates that only channels at or above this id should be |
||||
// included in the results. |
||||
int64 start_channel_id = 1; |
||||
} |
||||
|
||||
message GetTopChannelsResponse { |
||||
// list of channels that the connection detail service knows about. Sorted in |
||||
// ascending channel_id order. |
||||
repeated Channel channel = 1; |
||||
// If set, indicates that the list of channels is the final list. Requesting |
||||
// more channels can only return more if they are created after this RPC |
||||
// completes. |
||||
bool end = 2; |
||||
} |
||||
|
||||
message GetChannelRequest { |
||||
int64 channel_id = 1; |
||||
} |
||||
|
||||
message GetChannelResponse { |
||||
Channel channel = 1; |
||||
} |
||||
|
||||
message GetSubchannelRequest { |
||||
int64 subchannel_id = 1; |
||||
} |
||||
|
||||
message GetSubchannelResponse { |
||||
Subchannel subchannel = 1; |
||||
} |
||||
|
||||
message GetSocketRequest { |
||||
int64 socket_id = 1; |
||||
} |
||||
|
||||
message GetSocketResponse { |
||||
Socket socket = 1; |
||||
} |
@ -0,0 +1,240 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
#include "src/core/lib/channel/channel_trace.h" |
||||
#include "src/core/lib/channel/channel_trace_registry.h" |
||||
#include "src/core/lib/gpr/useful.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/json/json.h" |
||||
|
||||
#include "test/core/util/test_config.h" |
||||
#include "test/cpp/util/channel_trace_proto_helper.h" |
||||
|
||||
// remove me
|
||||
#include <grpc/support/string_util.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
namespace grpc_core { |
||||
namespace testing { |
||||
namespace { |
||||
|
||||
grpc_json* GetJsonChild(grpc_json* parent, const char* key) { |
||||
EXPECT_NE(parent, nullptr); |
||||
for (grpc_json* child = parent->child; child != nullptr; |
||||
child = child->next) { |
||||
if (child->key != nullptr && strcmp(child->key, key) == 0) return child; |
||||
} |
||||
return nullptr; |
||||
} |
||||
|
||||
void ValidateJsonArraySize(grpc_json* json, const char* key, |
||||
size_t expected_size) { |
||||
grpc_json* arr = GetJsonChild(json, key); |
||||
ASSERT_NE(arr, nullptr); |
||||
ASSERT_EQ(arr->type, GRPC_JSON_ARRAY); |
||||
size_t count = 0; |
||||
for (grpc_json* child = arr->child; child != nullptr; child = child->next) { |
||||
++count; |
||||
} |
||||
ASSERT_EQ(count, expected_size); |
||||
} |
||||
|
||||
void ValidateChannelTraceData(grpc_json* json, |
||||
size_t num_events_logged_expected, |
||||
size_t actual_num_events_expected) { |
||||
ASSERT_NE(json, nullptr); |
||||
grpc_json* num_events_logged_json = GetJsonChild(json, "numEventsLogged"); |
||||
ASSERT_NE(num_events_logged_json, nullptr); |
||||
grpc_json* start_time = GetJsonChild(json, "creationTime"); |
||||
ASSERT_NE(start_time, nullptr); |
||||
size_t num_events_logged = |
||||
(size_t)strtol(num_events_logged_json->value, nullptr, 0); |
||||
ASSERT_EQ(num_events_logged, num_events_logged_expected); |
||||
ValidateJsonArraySize(json, "events", actual_num_events_expected); |
||||
} |
||||
|
||||
void AddSimpleTrace(RefCountedPtr<ChannelTrace> tracer) { |
||||
tracer->AddTraceEvent(ChannelTrace::Severity::Info, |
||||
grpc_slice_from_static_string("simple trace")); |
||||
} |
||||
|
||||
// checks for the existence of all the required members of the tracer.
|
||||
void ValidateChannelTrace(RefCountedPtr<ChannelTrace> tracer, |
||||
size_t expected_num_event_logged, size_t max_nodes) { |
||||
if (!max_nodes) return; |
||||
char* json_str = tracer->RenderTrace(); |
||||
grpc::testing::ValidateChannelTraceProtoJsonTranslation(json_str); |
||||
grpc_json* json = grpc_json_parse_string(json_str); |
||||
ValidateChannelTraceData(json, expected_num_event_logged, |
||||
GPR_MIN(expected_num_event_logged, max_nodes)); |
||||
grpc_json_destroy(json); |
||||
gpr_free(json_str); |
||||
} |
||||
|
||||
void ValidateTraceDataMatchedUuidLookup(RefCountedPtr<ChannelTrace> tracer) { |
||||
intptr_t uuid = tracer->GetUuid(); |
||||
if (uuid == -1) return; // Doesn't make sense to lookup if tracing disabled
|
||||
char* tracer_json_str = tracer->RenderTrace(); |
||||
ChannelTrace* uuid_lookup = |
||||
grpc_channel_trace_registry_get_channel_trace(uuid); |
||||
char* uuid_lookup_json_str = uuid_lookup->RenderTrace(); |
||||
EXPECT_EQ(strcmp(tracer_json_str, uuid_lookup_json_str), 0); |
||||
gpr_free(tracer_json_str); |
||||
gpr_free(uuid_lookup_json_str); |
||||
} |
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class ChannelTracerTest : public ::testing::TestWithParam<size_t> {}; |
||||
|
||||
// Tests basic ChannelTrace functionality like construction, adding trace, and
|
||||
// lookups by uuid.
|
||||
TEST_P(ChannelTracerTest, BasicTest) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
RefCountedPtr<ChannelTrace> tracer = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
ValidateTraceDataMatchedUuidLookup(tracer); |
||||
tracer->AddTraceEvent(ChannelTrace::Severity::Info, |
||||
grpc_slice_from_static_string("trace three")); |
||||
tracer->AddTraceEvent(ChannelTrace::Severity::Error, |
||||
grpc_slice_from_static_string("trace four error")); |
||||
ValidateChannelTrace(tracer, 4, GetParam()); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
ValidateChannelTrace(tracer, 6, GetParam()); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
ValidateChannelTrace(tracer, 10, GetParam()); |
||||
ValidateTraceDataMatchedUuidLookup(tracer); |
||||
tracer.reset(nullptr); |
||||
} |
||||
|
||||
// Tests more complex functionality, like a parent channel tracking
|
||||
// subchannles. This exercises the ref/unref patterns since the parent tracer
|
||||
// and this function will both hold refs to the subchannel.
|
||||
TEST_P(ChannelTracerTest, ComplexTest) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
RefCountedPtr<ChannelTrace> tracer = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
RefCountedPtr<ChannelTrace> sc1 = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
tracer->AddTraceEventReferencingSubchannel( |
||||
ChannelTrace::Severity::Info, |
||||
grpc_slice_from_static_string("subchannel one created"), sc1); |
||||
ValidateChannelTrace(tracer, 3, GetParam()); |
||||
AddSimpleTrace(sc1); |
||||
AddSimpleTrace(sc1); |
||||
AddSimpleTrace(sc1); |
||||
ValidateChannelTrace(sc1, 3, GetParam()); |
||||
AddSimpleTrace(sc1); |
||||
AddSimpleTrace(sc1); |
||||
AddSimpleTrace(sc1); |
||||
ValidateChannelTrace(sc1, 6, GetParam()); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
ValidateChannelTrace(tracer, 5, GetParam()); |
||||
ValidateTraceDataMatchedUuidLookup(tracer); |
||||
RefCountedPtr<ChannelTrace> sc2 = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
tracer->AddTraceEventReferencingChannel( |
||||
ChannelTrace::Severity::Info, |
||||
grpc_slice_from_static_string("LB channel two created"), sc2); |
||||
tracer->AddTraceEventReferencingSubchannel( |
||||
ChannelTrace::Severity::Warning, |
||||
grpc_slice_from_static_string("subchannel one inactive"), sc1); |
||||
ValidateChannelTrace(tracer, 7, GetParam()); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
ValidateTraceDataMatchedUuidLookup(tracer); |
||||
tracer.reset(nullptr); |
||||
sc1.reset(nullptr); |
||||
sc2.reset(nullptr); |
||||
} |
||||
|
||||
// Test a case in which the parent channel has subchannels and the subchannels
|
||||
// have connections. Ensures that everything lives as long as it should then
|
||||
// gets deleted.
|
||||
TEST_P(ChannelTracerTest, TestNesting) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
RefCountedPtr<ChannelTrace> tracer = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
ValidateChannelTrace(tracer, 2, GetParam()); |
||||
RefCountedPtr<ChannelTrace> sc1 = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
tracer->AddTraceEventReferencingChannel( |
||||
ChannelTrace::Severity::Info, |
||||
grpc_slice_from_static_string("subchannel one created"), sc1); |
||||
ValidateChannelTrace(tracer, 3, GetParam()); |
||||
AddSimpleTrace(sc1); |
||||
RefCountedPtr<ChannelTrace> conn1 = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
// nesting one level deeper.
|
||||
sc1->AddTraceEventReferencingSubchannel( |
||||
ChannelTrace::Severity::Info, |
||||
grpc_slice_from_static_string("connection one created"), conn1); |
||||
ValidateChannelTrace(tracer, 3, GetParam()); |
||||
AddSimpleTrace(conn1); |
||||
AddSimpleTrace(tracer); |
||||
AddSimpleTrace(tracer); |
||||
ValidateChannelTrace(tracer, 5, GetParam()); |
||||
ValidateChannelTrace(conn1, 1, GetParam()); |
||||
RefCountedPtr<ChannelTrace> sc2 = MakeRefCounted<ChannelTrace>(GetParam()); |
||||
tracer->AddTraceEventReferencingSubchannel( |
||||
ChannelTrace::Severity::Info, |
||||
grpc_slice_from_static_string("subchannel two created"), sc2); |
||||
// this trace should not get added to the parents children since it is already
|
||||
// present in the tracer.
|
||||
tracer->AddTraceEventReferencingChannel( |
||||
ChannelTrace::Severity::Warning, |
||||
grpc_slice_from_static_string("subchannel one inactive"), sc1); |
||||
AddSimpleTrace(tracer); |
||||
ValidateChannelTrace(tracer, 8, GetParam()); |
||||
tracer.reset(nullptr); |
||||
sc1.reset(nullptr); |
||||
sc2.reset(nullptr); |
||||
conn1.reset(nullptr); |
||||
} |
||||
|
||||
INSTANTIATE_TEST_CASE_P(ChannelTracerTestSweep, ChannelTracerTest, |
||||
::testing::Values(0, 1, 2, 6, 10, 15)); |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_test_init(argc, argv); |
||||
grpc_init(); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
int ret = RUN_ALL_TESTS(); |
||||
grpc_shutdown(); |
||||
return ret; |
||||
} |
@ -0,0 +1,56 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2018 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include "test/cpp/util/channel_trace_proto_helper.h" |
||||
|
||||
#include <google/protobuf/text_format.h> |
||||
#include <google/protobuf/util/json_util.h> |
||||
|
||||
#include <grpc/grpc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include "src/proto/grpc/channelz/channelz.pb.h" |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
|
||||
void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str) { |
||||
std::string tracer_json_str(tracer_json_c_str); |
||||
grpc::channelz::ChannelTrace channel_trace; |
||||
google::protobuf::util::JsonParseOptions parse_options; |
||||
// If the following line is failing, then uncomment the last line of the
|
||||
// comment, and uncomment the lines that print the two strings. You can
|
||||
// then compare the output, and determine what fields are missing.
|
||||
//
|
||||
// options.ignore_unknown_fields = true;
|
||||
ASSERT_EQ(google::protobuf::util::JsonStringToMessage( |
||||
tracer_json_str, &channel_trace, parse_options), |
||||
google::protobuf::util::Status::OK); |
||||
std::string proto_json_str; |
||||
ASSERT_EQ(google::protobuf::util::MessageToJsonString(channel_trace, |
||||
&proto_json_str), |
||||
google::protobuf::util::Status::OK); |
||||
// uncomment these to compare the the json strings.
|
||||
// gpr_log(GPR_ERROR, "tracer json: %s", tracer_json_str.c_str());
|
||||
// gpr_log(GPR_ERROR, "proto json: %s", proto_json_str.c_str());
|
||||
ASSERT_EQ(tracer_json_str, proto_json_str); |
||||
} |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
@ -0,0 +1,30 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2018 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_TEST_CPP_UTIL_CHANNEL_TRACE_PROTO_HELPER_H |
||||
#define GRPC_TEST_CPP_UTIL_CHANNEL_TRACE_PROTO_HELPER_H |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
|
||||
void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str); |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPC_TEST_CPP_UTIL_CHANNEL_TRACE_PROTO_HELPER_H
|
Loading…
Reference in new issue