From 7d9b6358b507aef4188739c46db1ad3f39a5bf98 Mon Sep 17 00:00:00 2001 From: ncteisen Date: Thu, 28 Dec 2017 15:04:48 -0800 Subject: Initial commit Picks up work from https://github.com/grpc/grpc/pull/10259. A merge was impossible due to the many sweeping changed that have occured since I last touched that PR (c++-ization, exec_ctx, reorganitation of filters, etc). --- src/core/ext/filters/client_channel/subchannel.cc | 17 ++ src/core/ext/filters/client_channel/subchannel.h | 4 + src/core/lib/channel/channel_tracer.cc | 326 ++++++++++++++++++++++ src/core/lib/channel/channel_tracer.h | 78 ++++++ src/core/lib/json/json.cc | 36 +++ src/core/lib/json/json.h | 21 +- src/core/lib/support/object_registry.cc | 100 +++++++ src/core/lib/support/object_registry.h | 39 +++ src/core/lib/surface/channel.cc | 33 ++- src/core/lib/surface/channel.h | 3 + src/core/lib/surface/init.cc | 3 + src/python/grpcio/grpc_core_dependencies.py | 2 + 12 files changed, 659 insertions(+), 3 deletions(-) create mode 100644 src/core/lib/channel/channel_tracer.cc create mode 100644 src/core/lib/channel/channel_tracer.h create mode 100644 src/core/lib/support/object_registry.cc create mode 100644 src/core/lib/support/object_registry.h (limited to 'src') diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc index f07394d29b..8b8e468f16 100644 --- a/src/core/ext/filters/client_channel/subchannel.cc +++ b/src/core/ext/filters/client_channel/subchannel.cc @@ -35,6 +35,7 @@ #include "src/core/ext/filters/client_channel/uri_parser.h" #include "src/core/lib/backoff/backoff.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_tracer.h" #include "src/core/lib/channel/connected_channel.h" #include "src/core/lib/debug/stats.h" #include "src/core/lib/iomgr/sockaddr_utils.h" @@ -42,6 +43,7 @@ #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/support/manual_constructor.h" +#include "src/core/lib/support/object_registry.h" #include "src/core/lib/surface/channel.h" #include "src/core/lib/surface/channel_init.h" #include "src/core/lib/transport/connectivity_state.h" @@ -75,6 +77,7 @@ typedef struct external_state_watcher { } external_state_watcher; struct grpc_subchannel { + intptr_t uuid; grpc_connector* connector; /** refcount @@ -131,6 +134,8 @@ struct grpc_subchannel { bool backoff_begun; /** our alarm */ grpc_timer alarm; + + grpc_channel_tracer* tracer; }; struct grpc_subchannel_call { @@ -183,6 +188,7 @@ void grpc_connected_subchannel_unref( static void subchannel_destroy(void* arg, grpc_error* error) { grpc_subchannel* c = (grpc_subchannel*)arg; + grpc_object_registry_unregister_object(c->uuid); gpr_free((void*)c->filters); grpc_channel_args_destroy(c->args); grpc_connectivity_state_destroy(&c->state_tracker); @@ -337,6 +343,8 @@ grpc_subchannel* grpc_subchannel_create(grpc_connector* connector, GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED(); c = (grpc_subchannel*)gpr_zalloc(sizeof(*c)); + c->uuid = + grpc_object_registry_register_object(c, GPRC_OBJECT_REGISTRY_SUBCHANNEL); c->key = key; gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS); c->connector = connector; @@ -385,6 +393,15 @@ grpc_subchannel* grpc_subchannel_create(grpc_connector* connector, return grpc_subchannel_index_register(key, c); } +char* grpc_subchannel_get_trace(grpc_subchannel* subchannel, bool recursive) { + return subchannel->tracer != NULL + ? grpc_channel_tracer_render_trace(subchannel->tracer, recursive) + : NULL; +} +intptr_t grpc_subchannel_get_uuid(grpc_subchannel* subchannel) { + return subchannel->uuid; +} + static void continue_connect_locked(grpc_subchannel* c) { grpc_connect_in_args args; args.interested_parties = c->pollset_set; diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h index 9d34fff07a..3584d3928a 100644 --- a/src/core/ext/filters/client_channel/subchannel.h +++ b/src/core/ext/filters/client_channel/subchannel.h @@ -165,6 +165,10 @@ struct grpc_subchannel_args { grpc_subchannel* grpc_subchannel_create(grpc_connector* connector, const grpc_subchannel_args* args); +/// retrieves the trace for this subchannel in JSON form. +char* grpc_subchannel_get_trace(grpc_subchannel* subchannel, bool recursive); +intptr_t grpc_subchannel_get_uuid(grpc_subchannel* subchannel); + /// Sets \a addr from \a args. void grpc_get_subchannel_address_arg(const grpc_channel_args* args, grpc_resolved_address* addr); diff --git a/src/core/lib/channel/channel_tracer.cc b/src/core/lib/channel/channel_tracer.cc new file mode 100644 index 0000000000..0d77f33c7f --- /dev/null +++ b/src/core/lib/channel/channel_tracer.cc @@ -0,0 +1,326 @@ +/* + * + * 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 "src/core/lib/channel/channel_tracer.h" +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/object_registry.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/connectivity_state.h" + +// One node of tracing data +typedef struct grpc_trace_node { + grpc_slice data; + grpc_error* error; + gpr_timespec time_created; + grpc_connectivity_state connectivity_state; + struct grpc_trace_node* next; + + // the tracer object for the (sub)channel that this trace node refers to. + grpc_channel_tracer* referenced_tracer; +} grpc_trace_node; + +/* the channel tracing object */ +struct grpc_channel_tracer { + gpr_refcount refs; + gpr_mu tracer_mu; + intptr_t channel_uuid; + uint64_t num_nodes_logged; + size_t list_size; + size_t max_list_size; + grpc_trace_node* head_trace; + grpc_trace_node* tail_trace; + gpr_timespec time_created; +}; + +#ifdef GRPC_CHANNEL_TRACER_REFCOUNT_DEBUG +grpc_channel_tracer* grpc_channel_tracer_create(size_t max_nodes, intptr_t uuid, + const char* file, int line, + const char* func) { +#else +grpc_channel_tracer* grpc_channel_tracer_create(size_t max_nodes, + intptr_t uuid) { +#endif + grpc_channel_tracer* tracer = static_cast( + gpr_zalloc(sizeof(grpc_channel_tracer))); + gpr_mu_init(&tracer->tracer_mu); + gpr_ref_init(&tracer->refs, 1); +#ifdef GRPC_CHANNEL_TRACER_REFCOUNT_DEBUG + gpr_log(GPR_DEBUG, "%p create [%s:%d %s]", tracer, file, line, func); +#endif + tracer->channel_uuid = uuid; + tracer->max_list_size = max_nodes; + tracer->time_created = gpr_now(GPR_CLOCK_REALTIME); + return tracer; +} + +#ifdef GRPC_CHANNEL_TRACER_REFCOUNT_DEBUG +grpc_channel_tracer* grpc_channel_tracer_ref(grpc_channel_tracer* tracer, + const char* file, int line, + const char* func) { + if (!tracer) return tracer; + gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", tracer, + gpr_atm_no_barrier_load(&tracer->refs.count), + gpr_atm_no_barrier_load(&tracer->refs.count) + 1, file, line, func); + gpr_ref(&tracer->refs); + return tracer; +} +#else +grpc_channel_tracer* grpc_channel_tracer_ref(grpc_channel_tracer* tracer) { + if (!tracer) return tracer; + gpr_ref(&tracer->refs); + return tracer; +} +#endif + +static void free_node(grpc_trace_node* node) { + GRPC_ERROR_UNREF(node->error); + GRPC_CHANNEL_TRACER_UNREF(node->referenced_tracer); + grpc_slice_unref_internal(node->data); + gpr_free(node); +} + +static void grpc_channel_tracer_destroy(grpc_channel_tracer* tracer) { + grpc_trace_node* it = tracer->head_trace; + while (it != NULL) { + grpc_trace_node* to_free = it; + it = it->next; + free_node(to_free); + } + gpr_mu_destroy(&tracer->tracer_mu); + gpr_free(tracer); +} + +#ifdef GRPC_CHANNEL_TRACER_REFCOUNT_DEBUG +void grpc_channel_tracer_unref(grpc_channel_tracer* tracer, const char* file, + int line, const char* func) { + if (!tracer) return; + gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", tracer, + gpr_atm_no_barrier_load(&tracer->refs.count), + gpr_atm_no_barrier_load(&tracer->refs.count) - 1, file, line, func); + if (gpr_unref(&tracer->refs)) { + grpc_channel_tracer_destroy(tracer); + } +} +#else +void grpc_channel_tracer_unref(grpc_channel_tracer* tracer) { + if (!tracer) return; + if (gpr_unref(&tracer->refs)) { + grpc_channel_tracer_destroy(tracer); + } +} +#endif + +void grpc_channel_tracer_add_trace(grpc_channel_tracer* tracer, grpc_slice data, + grpc_error* error, + grpc_connectivity_state connectivity_state, + grpc_channel_tracer* referenced_tracer) { + if (!tracer) return; + ++tracer->num_nodes_logged; + // create and fill up the new node + grpc_trace_node* new_trace_node = + static_cast(gpr_malloc(sizeof(grpc_trace_node))); + new_trace_node->data = data; + new_trace_node->error = error; + new_trace_node->time_created = gpr_now(GPR_CLOCK_REALTIME); + new_trace_node->connectivity_state = connectivity_state; + new_trace_node->next = NULL; + new_trace_node->referenced_tracer = + GRPC_CHANNEL_TRACER_REF(referenced_tracer); + // first node case + if (tracer->head_trace == NULL) { + tracer->head_trace = tracer->tail_trace = new_trace_node; + } + // regular node add case + else { + tracer->tail_trace->next = new_trace_node; + tracer->tail_trace = tracer->tail_trace->next; + } + ++tracer->list_size; + // maybe garbage collect the end + if (tracer->list_size > tracer->max_list_size) { + grpc_trace_node* to_free = tracer->head_trace; + tracer->head_trace = tracer->head_trace->next; + free_node(to_free); + --tracer->list_size; + } +} + +// returns an allocated string that represents tm according to RFC-3339. +static char* fmt_time(gpr_timespec tm) { + char buffer[35]; + struct tm* tm_info = localtime((const time_t*)&tm.tv_sec); + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", tm_info); + char* full_time_str; + gpr_asprintf(&full_time_str, "%s.%09dZ", buffer, tm.tv_nsec); + return full_time_str; +} + +typedef struct seen_tracers { + grpc_channel_tracer** tracers; + size_t size; + size_t cap; +} seen_tracers; + +static void seen_tracers_add(seen_tracers* tracker, + grpc_channel_tracer* tracer) { + if (tracker->size >= tracker->cap) { + tracker->cap = GPR_MAX(5 * sizeof(tracer), 3 * tracker->cap / 2); + tracker->tracers = + (grpc_channel_tracer**)gpr_realloc(tracker->tracers, tracker->cap); + } + tracker->tracers[tracker->size++] = tracer; +} + +static bool seen_tracers_check(seen_tracers* tracker, + grpc_channel_tracer* tracer) { + for (size_t i = 0; i < tracker->size; ++i) { + if (tracker->tracers[i] == tracer) return true; + } + return false; +} + +static void recursively_populate_json(grpc_channel_tracer* tracer, + seen_tracers* tracker, grpc_json* json, + bool recursive); + +static void populate_node_data(grpc_trace_node* node, seen_tracers* tracker, + grpc_json* json, grpc_json* children) { + grpc_json* child = NULL; + child = grpc_json_create_child(child, json, "data", + grpc_slice_to_c_string(node->data), + GRPC_JSON_STRING, true); + if (node->error != GRPC_ERROR_NONE) { + child = grpc_json_create_child(child, json, "error", + gpr_strdup(grpc_error_string(node->error)), + GRPC_JSON_STRING, true); + } + child = + grpc_json_create_child(child, json, "time", fmt_time(node->time_created), + GRPC_JSON_STRING, true); + child = grpc_json_create_child( + child, json, "state", + grpc_connectivity_state_name(node->connectivity_state), GRPC_JSON_STRING, + false); + if (node->referenced_tracer != NULL) { + char* uuid_str; + gpr_asprintf(&uuid_str, "%ld", node->referenced_tracer->channel_uuid); + child = grpc_json_create_child(child, json, "uuid", uuid_str, + GRPC_JSON_NUMBER, true); + if (children && !seen_tracers_check(tracker, node->referenced_tracer)) { + grpc_json* referenced_tracer = grpc_json_create_child( + NULL, children, NULL, NULL, GRPC_JSON_OBJECT, false); + recursively_populate_json(node->referenced_tracer, tracker, + referenced_tracer, true); + } + } +} + +static void populate_node_list_data(grpc_channel_tracer* tracer, + seen_tracers* tracker, grpc_json* nodes, + grpc_json* children) { + grpc_json* child = NULL; + grpc_trace_node* it = tracer->head_trace; + while (it != NULL) { + child = grpc_json_create_child(child, nodes, NULL, NULL, GRPC_JSON_OBJECT, + false); + populate_node_data(it, tracker, child, children); + it = it->next; + } +} + +static void populate_tracer_data(grpc_channel_tracer* tracer, + seen_tracers* tracker, grpc_json* channel_data, + grpc_json* children) { + grpc_json* child = NULL; + + char* uuid_str; + gpr_asprintf(&uuid_str, "%ld", tracer->channel_uuid); + child = grpc_json_create_child(child, channel_data, "uuid", uuid_str, + GRPC_JSON_NUMBER, true); + char* num_nodes_logged_str; + gpr_asprintf(&num_nodes_logged_str, "%" PRId64, tracer->num_nodes_logged); + child = grpc_json_create_child(child, channel_data, "numNodesLogged", + num_nodes_logged_str, GRPC_JSON_NUMBER, true); + child = grpc_json_create_child(child, channel_data, "startTime", + fmt_time(tracer->time_created), + GRPC_JSON_STRING, true); + child = grpc_json_create_child(child, channel_data, "nodes", NULL, + GRPC_JSON_ARRAY, false); + populate_node_list_data(tracer, tracker, child, children); +} + +static void recursively_populate_json(grpc_channel_tracer* tracer, + seen_tracers* tracker, grpc_json* json, + bool recursive) { + grpc_json* channel_data = grpc_json_create_child( + NULL, json, "channelData", NULL, GRPC_JSON_OBJECT, false); + grpc_json* children = NULL; + if (recursive) { + children = grpc_json_create_child(channel_data, json, "children", NULL, + GRPC_JSON_ARRAY, false); + } + seen_tracers_add(tracker, tracer); + populate_tracer_data(tracer, tracker, channel_data, children); +} + +char* grpc_channel_tracer_render_trace(grpc_channel_tracer* tracer, + bool recursive) { + grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT); + + seen_tracers tracker; + memset(&tracker, 0, sizeof(tracker)); + + recursively_populate_json(tracer, &tracker, json, recursive); + + gpr_free(tracker.tracers); + + char* json_str = grpc_json_dump_to_string(json, 1); + grpc_json_destroy(json); + return json_str; +} + +char* grpc_channel_tracer_get_trace(intptr_t uuid, bool recursive) { + void* object; + grpc_object_registry_type type = + grpc_object_registry_get_object(uuid, &object); + GPR_ASSERT(type == GRPC_OBJECT_REGISTRY_CHANNEL || + type == GPRC_OBJECT_REGISTRY_SUBCHANNEL); + switch (type) { + case GRPC_OBJECT_REGISTRY_CHANNEL: + return grpc_channel_get_trace(static_cast(object), + recursive); + break; + case GPRC_OBJECT_REGISTRY_SUBCHANNEL: + return grpc_subchannel_get_trace(static_cast(object), + recursive); + break; + default: + abort(); + } +} diff --git a/src/core/lib/channel/channel_tracer.h b/src/core/lib/channel/channel_tracer.h new file mode 100644 index 0000000000..1f151ba899 --- /dev/null +++ b/src/core/lib/channel/channel_tracer.h @@ -0,0 +1,78 @@ +/* + * + * 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_TRACER_H +#define GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACER_H + +#include +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/json/json.h" + +/* Forward declaration */ +typedef struct grpc_channel_tracer grpc_channel_tracer; + +// #define GRPC_CHANNEL_TRACER_REFCOUNT_DEBUG + +/* Creates a new tracer. The caller owns a reference to the returned tracer. */ +#ifdef GRPC_CHANNEL_TRACER_REFCOUNT_DEBUG +grpc_channel_tracer* grpc_channel_tracer_create(size_t max_nodes, intptr_t uuid, + const char* file, int line, + const char* func); +#define GRPC_CHANNEL_TRACER_CREATE(max_nodes, id) \ + grpc_channel_tracer_create(max_nodes, id, __FILE__, __LINE__, __func__) +#else +grpc_channel_tracer* grpc_channel_tracer_create(size_t max_nodes, + intptr_t uuid); +#define GRPC_CHANNEL_TRACER_CREATE(max_nodes, id) \ + grpc_channel_tracer_create(max_nodes, id) +#endif + +#ifdef GRPC_CHANNEL_TRACER_REFCOUNT_DEBUG +grpc_channel_tracer* grpc_channel_tracer_ref(grpc_channel_tracer* tracer, + const char* file, int line, + const char* func); +void grpc_channel_tracer_unref(grpc_channel_tracer* tracer, const char* file, + int line, const char* func); +#define GRPC_CHANNEL_TRACER_REF(tracer) \ + grpc_channel_tracer_ref(tracer, __FILE__, __LINE__, __func__) +#define GRPC_CHANNEL_TRACER_UNREF(tracer) \ + grpc_channel_tracer_unref(tracer, __FILE__, __LINE__, __func__) +#else +grpc_channel_tracer* grpc_channel_tracer_ref(grpc_channel_tracer* tracer); +void grpc_channel_tracer_unref(grpc_channel_tracer* tracer); +#define GRPC_CHANNEL_TRACER_REF(tracer) grpc_channel_tracer_ref(tracer) +#define GRPC_CHANNEL_TRACER_UNREF(tracer) grpc_channel_tracer_unref(tracer) +#endif + +/* Adds a new trace node to the tracing object */ +void grpc_channel_tracer_add_trace(grpc_channel_tracer* tracer, grpc_slice data, + grpc_error* error, + grpc_connectivity_state connectivity_state, + grpc_channel_tracer* subchannel); + +/* Returns the tracing data rendered as a grpc json string. + The string is owned by the caller and must be freed. If recursive + is true, then the string will include the recursive trace for all + subtracing objects. */ +char* grpc_channel_tracer_render_trace(grpc_channel_tracer* tracer, + bool recursive); +/* util functions that perform the uuid -> tracer step for you, and then + returns the trace for the uuid given. */ +char* grpc_channel_tracer_get_trace(intptr_t uuid, bool recursive); + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACER_H */ diff --git a/src/core/lib/json/json.cc b/src/core/lib/json/json.cc index 4ad51f662a..2171e69455 100644 --- a/src/core/lib/json/json.cc +++ b/src/core/lib/json/json.cc @@ -19,6 +19,7 @@ #include #include +#include #include "src/core/lib/json/json.h" @@ -44,5 +45,40 @@ void grpc_json_destroy(grpc_json* json) { json->parent->child = json->next; } + if (json->owns_value) { + gpr_free((void*)json->value); + } + gpr_free(json); } + +grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child, + grpc_json* sibling) { + // first child case. + if (parent->child == NULL) { + GPR_ASSERT(sibling == NULL); + parent->child = child; + return child; + } + if (sibling == NULL) { + sibling = parent->child; + } + // always find the right most sibling. + while (sibling->next != NULL) { + sibling = sibling->next; + } + sibling->next = child; + return child; +} + +grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent, + const char* key, const char* value, + grpc_json_type type, bool owns_value) { + grpc_json* child = grpc_json_create(type); + grpc_json_link_child(parent, child, sibling); + child->owns_value = owns_value; + child->parent = parent; + child->value = value; + child->key = key; + return child; +} diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h index bbd43025eb..d88a79271f 100644 --- a/src/core/lib/json/json.h +++ b/src/core/lib/json/json.h @@ -19,6 +19,7 @@ #ifndef GRPC_CORE_LIB_JSON_JSON_H #define GRPC_CORE_LIB_JSON_JSON_H +#include #include #include "src/core/lib/json/json_common.h" @@ -35,6 +36,9 @@ typedef struct grpc_json { grpc_json_type type; const char* key; const char* value; + + /* if set, destructor will free value */ + bool owns_value; } grpc_json; /* The next two functions are going to parse the input string, and @@ -65,9 +69,24 @@ char* grpc_json_dump_to_string(grpc_json* json, int indent); /* Use these to create or delete a grpc_json object. * Deletion is recursive. We will not attempt to free any of the strings - * in any of the objects of that tree. + * in any of the objects of that tree, unless the boolean, owns_value, + * is true. */ grpc_json* grpc_json_create(grpc_json_type type); void grpc_json_destroy(grpc_json* json); +/* Links the child json object into the parent's json tree. If the parent + * already has children, then passing in the most recently added child as the + * sibling parameter is an optimization. For if sibling is NULL, this function + * will manually traverse the tree in order to find the right most sibling. + */ +grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child, + grpc_json* sibling); + +/* Creates a child json object into the parent's json tree then links it in + * as described above. */ +grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent, + const char* key, const char* value, + grpc_json_type type, bool owns_value); + #endif /* GRPC_CORE_LIB_JSON_JSON_H */ diff --git a/src/core/lib/support/object_registry.cc b/src/core/lib/support/object_registry.cc new file mode 100644 index 0000000000..596711804f --- /dev/null +++ b/src/core/lib/support/object_registry.cc @@ -0,0 +1,100 @@ +/* + * + * 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 "src/core/lib/support/object_registry.h" + +#include +#include +#include + +// file global lock and avl. +static gpr_mu g_mu; +static gpr_avl g_avl; +static intptr_t g_uuid = 0; + +typedef struct { + void* object; + grpc_object_registry_type type; +} object_tracker; + +// avl vtable for uuid (intptr_t) -> object_tracker +// 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 key1 > key2; +} + +static void destroy_tracker(void* tracker, void* user_data) { + gpr_free((object_tracker*)tracker); +} + +static void* copy_tracker(void* value, void* user_data) { + object_tracker* old = static_cast(value); + object_tracker* new_obj = + static_cast(gpr_malloc(sizeof(object_tracker))); + new_obj->object = old->object; + new_obj->type = old->type; + return new_obj; +} +static const gpr_avl_vtable avl_vtable = { + destroy_intptr, copy_intptr, compare_intptr, destroy_tracker, copy_tracker}; + +void grpc_object_registry_init() { + gpr_mu_init(&g_mu); + g_avl = gpr_avl_create(&avl_vtable); +} + +void grpc_object_registry_shutdown() { + gpr_avl_unref(g_avl, nullptr); + gpr_mu_destroy(&g_mu); +} + +intptr_t grpc_object_registry_register_object(void* object, + grpc_object_registry_type type) { + object_tracker* tracker = + static_cast(gpr_malloc(sizeof(object_tracker))); + tracker->object = object; + tracker->type = type; + intptr_t prior = gpr_atm_no_barrier_fetch_add(&g_uuid, 1); + gpr_mu_lock(&g_mu); + g_avl = gpr_avl_add(g_avl, (void*)prior, tracker, NULL); + gpr_mu_unlock(&g_mu); + return prior; +} + +void grpc_object_registry_unregister_object(intptr_t uuid) { + gpr_mu_lock(&g_mu); + g_avl = gpr_avl_remove(g_avl, (void*)uuid, nullptr); + gpr_mu_unlock(&g_mu); +} + +grpc_object_registry_type grpc_object_registry_get_object(intptr_t uuid, + void** object) { + GPR_ASSERT(object); + gpr_mu_lock(&g_mu); + object_tracker* tracker = + static_cast(gpr_avl_get(g_avl, (void*)uuid, nullptr)); + gpr_mu_unlock(&g_mu); + if (tracker == NULL) { + *object = NULL; + return GRPC_OBJECT_REGISTRY_UNKNOWN; + } + *object = tracker->object; + return tracker->type; +} diff --git a/src/core/lib/support/object_registry.h b/src/core/lib/support/object_registry.h new file mode 100644 index 0000000000..3a0d056e2a --- /dev/null +++ b/src/core/lib/support/object_registry.h @@ -0,0 +1,39 @@ +/* + * + * 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_SUPPORT_OBJECT_REGISTRY_H +#define GRPC_CORE_LIB_SUPPORT_OBJECT_REGISTRY_H + +#include + +typedef enum { + GRPC_OBJECT_REGISTRY_CHANNEL, + GPRC_OBJECT_REGISTRY_SUBCHANNEL, + GRPC_OBJECT_REGISTRY_UNKNOWN, +} grpc_object_registry_type; + +void grpc_object_registry_init(); +void grpc_object_registry_shutdown(); + +intptr_t grpc_object_registry_register_object(void* object, + grpc_object_registry_type type); +void grpc_object_registry_unregister_object(intptr_t uuid); +grpc_object_registry_type grpc_object_registry_get_object(intptr_t uuid, + void** object); + +#endif /* GRPC_CORE_LIB_SUPPORT_OBJECT_REGISTRY_H */ diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc index cf5e8c2150..e94413e3bc 100644 --- a/src/core/lib/surface/channel.cc +++ b/src/core/lib/surface/channel.cc @@ -28,9 +28,11 @@ #include #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_tracer.h" #include "src/core/lib/debug/stats.h" #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/object_registry.h" #include "src/core/lib/support/string.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/call.h" @@ -51,6 +53,7 @@ typedef struct registered_call { } registered_call; struct grpc_channel { + intptr_t uuid; int is_client; grpc_compression_options compression_options; grpc_mdelem default_authority; @@ -60,6 +63,8 @@ struct grpc_channel { gpr_mu registered_call_mu; registered_call* registered_calls; + grpc_channel_tracer* tracer; + char* target; }; @@ -91,12 +96,16 @@ grpc_channel* grpc_channel_create_with_builder( grpc_error_string(error)); GRPC_ERROR_UNREF(error); gpr_free(target); - goto done; + grpc_channel_args_destroy(args); + return channel; } memset(channel, 0, sizeof(*channel)); + channel->uuid = grpc_object_registry_register_object( + channel, GRPC_OBJECT_REGISTRY_CHANNEL); channel->target = target; channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type); + channel->tracer = NULL; gpr_mu_init(&channel->registered_call_mu); channel->registered_calls = nullptr; @@ -186,14 +195,34 @@ grpc_channel* grpc_channel_create_with_builder( .enabled_stream_compression_algorithms_bitset = (uint32_t)args->args[i].value.integer | 0x1; /* always support no compression */ + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_CHANNEL_TRACING_MAX_NODES)) { + GPR_ASSERT(channel->tracer == NULL); + // max_nodes defaults to 10, clamped between 0 and 100. + const grpc_integer_options options = {10, 0, 100}; + size_t max_nodes = + (size_t)grpc_channel_arg_get_integer(&args->args[i], options); + if (max_nodes > 0) { + channel->tracer = GRPC_CHANNEL_TRACER_CREATE(max_nodes, channel->uuid); + } } } -done: grpc_channel_args_destroy(args); + grpc_channel_tracer_add_trace( + channel->tracer, grpc_slice_from_static_string("Channel created"), + GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, NULL); return channel; } +char* grpc_channel_get_trace(grpc_channel* channel, bool recursive) { + return channel->tracer + ? grpc_channel_tracer_render_trace(channel->tracer, recursive) + : NULL; +} + +intptr_t grpc_channel_get_uuid(grpc_channel* channel) { return channel->uuid; } + grpc_channel* grpc_channel_create(const char* target, const grpc_channel_args* input_args, grpc_channel_stack_type channel_stack_type, diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h index 26d8fceb2f..507013f452 100644 --- a/src/core/lib/surface/channel.h +++ b/src/core/lib/surface/channel.h @@ -58,6 +58,9 @@ grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_channel* channel, size_t grpc_channel_get_call_size_estimate(grpc_channel* channel); void grpc_channel_update_call_size_estimate(grpc_channel* channel, size_t size); +char* grpc_channel_get_trace(grpc_channel* channel, bool recursive); +intptr_t grpc_channel_get_uuid(grpc_channel* channel); + #ifndef NDEBUG void grpc_channel_internal_ref(grpc_channel* channel, const char* reason); void grpc_channel_internal_unref(grpc_channel* channel, const char* reason); diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc index 0f40965f16..a33b6de8b7 100644 --- a/src/core/lib/surface/init.cc +++ b/src/core/lib/surface/init.cc @@ -41,6 +41,7 @@ #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/support/fork.h" +#include "src/core/lib/support/object_registry.h" #include "src/core/lib/support/thd_internal.h" #include "src/core/lib/surface/alarm_internal.h" #include "src/core/lib/surface/api_trace.h" @@ -129,6 +130,7 @@ void grpc_init(void) { grpc_slice_intern_init(); grpc_mdctx_global_init(); grpc_channel_init_init(); + grpc_object_registry_init(); grpc_security_pre_init(); grpc_core::ExecCtx::GlobalInit(); grpc_iomgr_init(); @@ -177,6 +179,7 @@ void grpc_shutdown(void) { grpc_mdctx_global_shutdown(); grpc_handshaker_factory_registry_shutdown(); grpc_slice_intern_shutdown(); + grpc_object_registry_shutdown(); grpc_stats_shutdown(); } grpc_core::ExecCtx::GlobalShutdown(); diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index aea0786890..1e964cebce 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -38,6 +38,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/support/log_windows.cc', 'src/core/lib/support/mpscq.cc', 'src/core/lib/support/murmur_hash.cc', + 'src/core/lib/support/object_registry.cc', 'src/core/lib/support/string.cc', 'src/core/lib/support/string_posix.cc', 'src/core/lib/support/string_util_windows.cc', @@ -64,6 +65,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/channel/channel_args.cc', 'src/core/lib/channel/channel_stack.cc', 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/channel_tracer.cc', 'src/core/lib/channel/connected_channel.cc', 'src/core/lib/channel/handshaker.cc', 'src/core/lib/channel/handshaker_factory.cc', -- cgit v1.2.3