mirror of https://github.com/grpc/grpc.git
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).reviewable/pr13883/r1
parent
2e04b2dd0c
commit
7d9b6358b5
29 changed files with 1146 additions and 3 deletions
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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 */ |
@ -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; |
||||||
|
} |
@ -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 */ |
@ -0,0 +1,210 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* 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 <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/useful.h> |
||||||
|
|
||||||
|
#include "src/core/lib/channel/channel_tracer.h" |
||||||
|
#include "src/core/lib/iomgr/exec_ctx.h" |
||||||
|
|
||||||
|
#include "test/core/util/channel_tracing_utils.h" |
||||||
|
#include "test/core/util/test_config.h" |
||||||
|
|
||||||
|
static void add_simple_trace(grpc_channel_tracer* tracer) { |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("simple trace"), |
||||||
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_CHANNEL_READY, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
// checks for the existence of all the required members of the tracer.
|
||||||
|
static void validate_tracer(grpc_channel_tracer* tracer, |
||||||
|
size_t expected_num_nodes_logged, |
||||||
|
size_t max_nodes) { |
||||||
|
char* json_str = grpc_channel_tracer_render_trace(tracer, true); |
||||||
|
grpc_json* json = grpc_json_parse_string(json_str); |
||||||
|
validate_channel_data(json, expected_num_nodes_logged, |
||||||
|
GPR_MIN(expected_num_nodes_logged, max_nodes)); |
||||||
|
grpc_json_destroy(json); |
||||||
|
gpr_free(json_str); |
||||||
|
} |
||||||
|
|
||||||
|
// ensures the tracer has the correct number of children tracers.
|
||||||
|
static void validate_children(grpc_channel_tracer* tracer, |
||||||
|
size_t expected_num_children) { |
||||||
|
char* json_str = grpc_channel_tracer_render_trace(tracer, true); |
||||||
|
grpc_json* json = grpc_json_parse_string(json_str); |
||||||
|
validate_json_array_size(json, "children", expected_num_children); |
||||||
|
grpc_json_destroy(json); |
||||||
|
gpr_free(json_str); |
||||||
|
} |
||||||
|
|
||||||
|
static intptr_t uuid = 0; |
||||||
|
|
||||||
|
static void test_basic_channel_tracing(size_t max_nodes) { |
||||||
|
grpc_channel_tracer* tracer = GRPC_CHANNEL_TRACER_CREATE(max_nodes, uuid++); |
||||||
|
grpc_core::ExecCtx exec_ctx; |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("trace three"), |
||||||
|
grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), |
||||||
|
GRPC_ERROR_INT_HTTP2_ERROR, 2), |
||||||
|
GRPC_CHANNEL_IDLE, NULL); |
||||||
|
grpc_channel_tracer_add_trace(tracer, |
||||||
|
grpc_slice_from_static_string("trace four"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_SHUTDOWN, NULL); |
||||||
|
validate_tracer(tracer, 4, max_nodes); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
validate_tracer(tracer, 6, max_nodes); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
validate_tracer(tracer, 10, max_nodes); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(tracer); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_basic_channel_sizing() { |
||||||
|
test_basic_channel_tracing(0); |
||||||
|
test_basic_channel_tracing(1); |
||||||
|
test_basic_channel_tracing(2); |
||||||
|
test_basic_channel_tracing(6); |
||||||
|
test_basic_channel_tracing(10); |
||||||
|
test_basic_channel_tracing(15); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_complex_channel_tracing(size_t max_nodes) { |
||||||
|
grpc_channel_tracer* tracer = GRPC_CHANNEL_TRACER_CREATE(max_nodes, uuid++); |
||||||
|
grpc_core::ExecCtx exec_ctx; |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
grpc_channel_tracer* sc1 = GRPC_CHANNEL_TRACER_CREATE(max_nodes, uuid++); |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("subchannel one created"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, sc1); |
||||||
|
validate_tracer(tracer, 3, max_nodes); |
||||||
|
add_simple_trace(sc1); |
||||||
|
add_simple_trace(sc1); |
||||||
|
add_simple_trace(sc1); |
||||||
|
validate_tracer(sc1, 3, max_nodes); |
||||||
|
add_simple_trace(sc1); |
||||||
|
add_simple_trace(sc1); |
||||||
|
add_simple_trace(sc1); |
||||||
|
validate_tracer(sc1, 6, max_nodes); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
validate_tracer(tracer, 5, max_nodes); |
||||||
|
grpc_channel_tracer* sc2 = GRPC_CHANNEL_TRACER_CREATE(max_nodes, uuid++); |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("subchannel two created"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, sc2); |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("subchannel one inactive"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, sc1); |
||||||
|
validate_tracer(tracer, 7, max_nodes); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(sc1); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(sc2); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(tracer); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_complex_channel_sizing() { |
||||||
|
test_complex_channel_tracing(0); |
||||||
|
test_complex_channel_tracing(1); |
||||||
|
test_complex_channel_tracing(2); |
||||||
|
test_complex_channel_tracing(6); |
||||||
|
test_complex_channel_tracing(10); |
||||||
|
test_complex_channel_tracing(15); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_delete_parent_first() { |
||||||
|
grpc_channel_tracer* tracer = GRPC_CHANNEL_TRACER_CREATE(3, uuid++); |
||||||
|
grpc_core::ExecCtx exec_ctx; |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
grpc_channel_tracer* sc1 = GRPC_CHANNEL_TRACER_CREATE(3, uuid++); |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("subchannel one created"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, sc1); |
||||||
|
// this will cause the tracer destructor to run.
|
||||||
|
GRPC_CHANNEL_TRACER_UNREF(tracer); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(sc1); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_nesting() { |
||||||
|
grpc_channel_tracer* tracer = GRPC_CHANNEL_TRACER_CREATE(10, uuid++); |
||||||
|
grpc_core::ExecCtx exec_ctx; |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
grpc_channel_tracer* sc1 = GRPC_CHANNEL_TRACER_CREATE(5, uuid++); |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("subchannel one created"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, sc1); |
||||||
|
// channel has only one subchannel right here.
|
||||||
|
validate_children(tracer, 1); |
||||||
|
add_simple_trace(sc1); |
||||||
|
grpc_channel_tracer* conn1 = GRPC_CHANNEL_TRACER_CREATE(5, uuid++); |
||||||
|
// nesting one level deeper.
|
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
sc1, grpc_slice_from_static_string("connection one created"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, conn1); |
||||||
|
validate_children(sc1, 1); |
||||||
|
add_simple_trace(conn1); |
||||||
|
add_simple_trace(tracer); |
||||||
|
add_simple_trace(tracer); |
||||||
|
grpc_channel_tracer* sc2 = GRPC_CHANNEL_TRACER_CREATE(5, uuid++); |
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("subchannel two created"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, sc2); |
||||||
|
validate_children(tracer, 2); |
||||||
|
// this trace should not get added to the parents children since it is already
|
||||||
|
// present in the tracer.
|
||||||
|
grpc_channel_tracer_add_trace( |
||||||
|
tracer, grpc_slice_from_static_string("subchannel one inactive"), |
||||||
|
GRPC_ERROR_NONE, GRPC_CHANNEL_IDLE, sc1); |
||||||
|
validate_children(tracer, 2); |
||||||
|
add_simple_trace(tracer); |
||||||
|
|
||||||
|
GRPC_CHANNEL_TRACER_UNREF(conn1); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(sc1); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(sc2); |
||||||
|
GRPC_CHANNEL_TRACER_UNREF(tracer); |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
grpc_test_init(argc, argv); |
||||||
|
grpc_init(); |
||||||
|
test_basic_channel_tracing(5); |
||||||
|
test_basic_channel_sizing(); |
||||||
|
test_complex_channel_tracing(5); |
||||||
|
test_complex_channel_sizing(); |
||||||
|
test_delete_parent_first(); |
||||||
|
test_nesting(); |
||||||
|
grpc_shutdown(); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* 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 <grpc/support/log.h> |
||||||
|
#include <grpc/support/useful.h> |
||||||
|
#include "src/core/lib/channel/channel_tracer.h" |
||||||
|
#include "src/core/lib/json/json.h" |
||||||
|
|
||||||
|
static grpc_json* get_json_child(grpc_json* parent, const char* key) { |
||||||
|
GPR_ASSERT(parent != NULL); |
||||||
|
for (grpc_json* child = parent->child; child != NULL; child = child->next) { |
||||||
|
if (child->key != NULL && strcmp(child->key, key) == 0) return child; |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void validate_json_array_size(grpc_json* json, const char* key, |
||||||
|
size_t expected_size) { |
||||||
|
grpc_json* arr = get_json_child(json, key); |
||||||
|
GPR_ASSERT(arr); |
||||||
|
GPR_ASSERT(arr->type == GRPC_JSON_ARRAY); |
||||||
|
size_t count = 0; |
||||||
|
for (grpc_json* child = arr->child; child != NULL; child = child->next) { |
||||||
|
++count; |
||||||
|
} |
||||||
|
GPR_ASSERT(count == expected_size); |
||||||
|
} |
||||||
|
|
||||||
|
void validate_channel_data(grpc_json* json, size_t num_nodes_logged_expected, |
||||||
|
size_t actual_num_nodes_expected) { |
||||||
|
GPR_ASSERT(json); |
||||||
|
grpc_json* channel_data = get_json_child(json, "channelData"); |
||||||
|
grpc_json* num_nodes_logged_json = |
||||||
|
get_json_child(channel_data, "numNodesLogged"); |
||||||
|
GPR_ASSERT(num_nodes_logged_json); |
||||||
|
grpc_json* start_time = get_json_child(channel_data, "startTime"); |
||||||
|
GPR_ASSERT(start_time); |
||||||
|
size_t num_nodes_logged = |
||||||
|
(size_t)strtol(num_nodes_logged_json->value, NULL, 0); |
||||||
|
GPR_ASSERT(num_nodes_logged == num_nodes_logged_expected); |
||||||
|
validate_json_array_size(channel_data, "nodes", actual_num_nodes_expected); |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* 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_TEST_CORE_UTIL_CHANNEL_TRACING_UTILS_H |
||||||
|
#define GRPC_TEST_CORE_UTIL_CHANNEL_TRACING_UTILS_H |
||||||
|
|
||||||
|
#include "src/core/lib/channel/channel_tracer.h" |
||||||
|
|
||||||
|
void validate_json_array_size(grpc_json* json, const char* key, |
||||||
|
size_t expected_size); |
||||||
|
|
||||||
|
void validate_channel_data(grpc_json* json, size_t num_nodes_logged_expected, |
||||||
|
size_t actual_num_nodes_expected); |
||||||
|
|
||||||
|
#endif /* GRPC_TEST_CORE_UTIL_CHANNEL_TRACING_UTILS_H */ |
Loading…
Reference in new issue