aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar ncteisen <ncteisen@gmail.com>2017-12-28 15:04:48 -0800
committerGravatar ncteisen <ncteisen@gmail.com>2017-12-28 15:09:17 -0800
commit7d9b6358b507aef4188739c46db1ad3f39a5bf98 (patch)
tree84991fb473351e484207e5dcd31d406f95c01d0f /src
parent2e04b2dd0c9aed66a14e6810b18e8c5b9b4f8a04 (diff)
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).
Diffstat (limited to 'src')
-rw-r--r--src/core/ext/filters/client_channel/subchannel.cc17
-rw-r--r--src/core/ext/filters/client_channel/subchannel.h4
-rw-r--r--src/core/lib/channel/channel_tracer.cc326
-rw-r--r--src/core/lib/channel/channel_tracer.h78
-rw-r--r--src/core/lib/json/json.cc36
-rw-r--r--src/core/lib/json/json.h21
-rw-r--r--src/core/lib/support/object_registry.cc100
-rw-r--r--src/core/lib/support/object_registry.h39
-rw-r--r--src/core/lib/surface/channel.cc33
-rw-r--r--src/core/lib/surface/channel.h3
-rw-r--r--src/core/lib/surface/init.cc3
-rw-r--r--src/python/grpcio/grpc_core_dependencies.py2
12 files changed, 659 insertions, 3 deletions
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 <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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<grpc_channel_tracer*>(
+ 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<grpc_trace_node*>(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<grpc_channel*>(object),
+ recursive);
+ break;
+ case GPRC_OBJECT_REGISTRY_SUBCHANNEL:
+ return grpc_subchannel_get_trace(static_cast<grpc_subchannel*>(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 <grpc/grpc.h>
+#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 <string.h>
#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
#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 <stdbool.h>
#include <stdlib.h>
#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 <grpc/support/alloc.h>
+#include <grpc/support/avl.h>
+#include <grpc/support/log.h>
+
+// 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<object_tracker*>(value);
+ object_tracker* new_obj =
+ static_cast<object_tracker*>(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<object_tracker*>(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<object_tracker*>(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 <stdint.h>
+
+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 <grpc/support/string_util.h>
#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',