/* * * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "src/core/statistics/census_interface.h" #include "src/core/statistics/census_tracing.h" #include #include #include "src/core/statistics/hash_table.h" #include "src/core/support/string.h" #include #include #include #include void census_trace_obj_destroy(census_trace_obj *obj) { census_trace_annotation *p = obj->annotations; while (p != NULL) { census_trace_annotation *next = p->next; gpr_free(p); p = next; } gpr_free(obj->method); gpr_free(obj); } static void delete_trace_obj(void *obj) { census_trace_obj_destroy((census_trace_obj *)obj); } static const census_ht_option ht_opt = { CENSUS_HT_UINT64 /* key type */, 571 /* n_of_buckets */, NULL /* hash */, NULL /* compare_keys */, delete_trace_obj /* delete data */, NULL /* delete key */ }; static gpr_once g_init_mutex_once = GPR_ONCE_INIT; static gpr_mu g_mu; /* Guards following two static variables. */ static census_ht *g_trace_store = NULL; static gpr_uint64 g_id = 0; static census_ht_key op_id_as_key(census_op_id *id) { return *(census_ht_key *)id; } static gpr_uint64 op_id_2_uint64(census_op_id *id) { gpr_uint64 ret; memcpy(&ret, id, sizeof(census_op_id)); return ret; } static void init_mutex(void) { gpr_mu_init(&g_mu); } static void init_mutex_once(void) { gpr_once_init(&g_init_mutex_once, init_mutex); } census_op_id census_tracing_start_op(void) { gpr_mu_lock(&g_mu); { census_trace_obj *ret = gpr_malloc(sizeof(census_trace_obj)); memset(ret, 0, sizeof(census_trace_obj)); g_id++; memcpy(&ret->id, &g_id, sizeof(census_op_id)); ret->rpc_stats.cnt = 1; ret->ts = gpr_now(GPR_CLOCK_REALTIME); census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void *)ret); gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id); gpr_mu_unlock(&g_mu); return ret->id; } } int census_add_method_tag(census_op_id op_id, const char *method) { int ret = 0; census_trace_obj *trace = NULL; gpr_mu_lock(&g_mu); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace == NULL) { ret = 1; } else { trace->method = gpr_strdup(method); } gpr_mu_unlock(&g_mu); return ret; } void census_tracing_print(census_op_id op_id, const char *anno_txt) { census_trace_obj *trace = NULL; gpr_mu_lock(&g_mu); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace != NULL) { census_trace_annotation *anno = gpr_malloc(sizeof(census_trace_annotation)); anno->ts = gpr_now(GPR_CLOCK_REALTIME); { char *d = anno->txt; const char *s = anno_txt; int n = 0; for (; n < CENSUS_MAX_ANNOTATION_LENGTH && *s != '\0'; ++n) { *d++ = *s++; } *d = '\0'; } anno->next = trace->annotations; trace->annotations = anno; } gpr_mu_unlock(&g_mu); } void census_tracing_end_op(census_op_id op_id) { census_trace_obj *trace = NULL; gpr_mu_lock(&g_mu); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); if (trace != NULL) { trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros( gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), trace->ts)); gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us", op_id_2_uint64(&op_id), trace->method, trace->rpc_stats.elapsed_time_ms); census_ht_erase(g_trace_store, op_id_as_key(&op_id)); } gpr_mu_unlock(&g_mu); } void census_tracing_init(void) { init_mutex_once(); gpr_mu_lock(&g_mu); if (g_trace_store == NULL) { g_id = 1; g_trace_store = census_ht_create(&ht_opt); } else { gpr_log(GPR_ERROR, "Census trace store already initialized."); } gpr_mu_unlock(&g_mu); } void census_tracing_shutdown(void) { gpr_mu_lock(&g_mu); if (g_trace_store != NULL) { census_ht_destroy(g_trace_store); g_trace_store = NULL; } else { gpr_log(GPR_ERROR, "Census trace store is not initialized."); } gpr_mu_unlock(&g_mu); } void census_internal_lock_trace_store(void) { gpr_mu_lock(&g_mu); } void census_internal_unlock_trace_store(void) { gpr_mu_unlock(&g_mu); } census_trace_obj *census_get_trace_obj_locked(census_op_id op_id) { if (g_trace_store == NULL) { gpr_log(GPR_ERROR, "Census trace store is not initialized."); return NULL; } return (census_trace_obj *)census_ht_find(g_trace_store, op_id_as_key(&op_id)); } const char *census_get_trace_method_name(const census_trace_obj *trace) { return trace->method; } static census_trace_annotation *dup_annotation_chain( census_trace_annotation *from) { census_trace_annotation *ret = NULL; census_trace_annotation **to = &ret; for (; from != NULL; from = from->next) { *to = gpr_malloc(sizeof(census_trace_annotation)); memcpy(*to, from, sizeof(census_trace_annotation)); to = &(*to)->next; } return ret; } static census_trace_obj *trace_obj_dup(census_trace_obj *from) { census_trace_obj *to = NULL; GPR_ASSERT(from != NULL); to = gpr_malloc(sizeof(census_trace_obj)); to->id = from->id; to->ts = from->ts; to->rpc_stats = from->rpc_stats; to->method = gpr_strdup(from->method); to->annotations = dup_annotation_chain(from->annotations); return to; } census_trace_obj **census_get_active_ops(int *num_active_ops) { census_trace_obj **ret = NULL; gpr_mu_lock(&g_mu); if (g_trace_store != NULL) { size_t n = 0; census_ht_kv *all_kvs = census_ht_get_all_elements(g_trace_store, &n); *num_active_ops = (int)n; if (n != 0) { size_t i = 0; ret = gpr_malloc(sizeof(census_trace_obj *) * n); for (i = 0; i < n; i++) { ret[i] = trace_obj_dup((census_trace_obj *)all_kvs[i].v); } } gpr_free(all_kvs); } gpr_mu_unlock(&g_mu); return ret; }