diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c index 3c4ba66f5f4..ddf925cc47b 100644 --- a/src/core/statistics/census_tracing.c +++ b/src/core/statistics/census_tracing.c @@ -32,35 +32,19 @@ */ #include "src/core/statistics/census_interface.h" +#include "src/core/statistics/census_tracing.h" #include #include -#include "src/core/statistics/census_rpc_stats.h" #include "src/core/statistics/hash_table.h" #include "src/core/support/string.h" #include #include #include #include -#include - -/* Struct for a trace annotation. */ -typedef struct annotation { - gpr_timespec ts; /* timestamp of the annotation */ - char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */ - struct annotation* next; -} annotation; - -typedef struct trace_obj { - census_op_id id; - gpr_timespec ts; - census_rpc_stats rpc_stats; - char* method; - annotation* annotations; -} trace_obj; - -static void trace_obj_destroy(trace_obj* obj) { + +void trace_obj_destroy(trace_obj* obj) { annotation* p = obj->annotations; while (p != NULL) { annotation* next = p->next; @@ -207,3 +191,45 @@ trace_obj* census_get_trace_obj_locked(census_op_id op_id) { const char* census_get_trace_method_name(const trace_obj* trace) { return (const char*)trace->method; } + +static annotation* dup_annotation_chain(annotation* from) { + annotation* to = NULL; + if (from != NULL) { + to = gpr_malloc(sizeof(annotation)); + memcpy(to, from, sizeof(annotation)); + to->next = dup_annotation_chain(from->next); + } + return to; +} + +static trace_obj* trace_obj_dup(trace_obj* from) { + trace_obj* to = NULL; + GPR_ASSERT(from != NULL); + to = gpr_malloc(sizeof(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; +} + +trace_obj** census_get_active_ops(int* num_active_ops) { + 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 = n; + if (n != 0 ) { + size_t i = 0; + ret = gpr_malloc(sizeof(trace_obj *) * n); + for (i = 0; i < n; i++) { + ret[i] = trace_obj_dup((trace_obj*)all_kvs[i].v); + } + } + gpr_free(all_kvs); + } + gpr_mu_unlock(&g_mu); + return ret; +} diff --git a/src/core/statistics/census_tracing.h b/src/core/statistics/census_tracing.h index f356c9424d5..58333f7f6b8 100644 --- a/src/core/statistics/census_tracing.h +++ b/src/core/statistics/census_tracing.h @@ -34,12 +34,35 @@ #ifndef __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ #define __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ +#include +#include "src/core/statistics/census_rpc_stats.h" + +/* WARNING: The data structures and APIs provided by this file are for GRPC + library's internal use ONLY. They might be changed in backward-incompatible + ways and are not subject to any deprecation policy. + They are not recommended for external use. + */ #ifdef __cplusplus extern "C" { #endif -/* Opaque structure for trace object */ -typedef struct trace_obj trace_obj; +/* Struct for a trace annotation. */ +typedef struct annotation { + gpr_timespec ts; /* timestamp of the annotation */ + char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */ + struct annotation* next; +} annotation; + +typedef struct trace_obj { + census_op_id id; + gpr_timespec ts; + census_rpc_stats rpc_stats; + char* method; + annotation* annotations; +} trace_obj; + +/* Deletes trace object. */ +void trace_obj_destroy(trace_obj* obj); /* Initializes trace store. This function is thread safe. */ void census_tracing_init(void); @@ -60,6 +83,12 @@ void census_internal_unlock_trace_store(void); /* Gets method tag name associated with the input trace object. */ const char* census_get_trace_method_name(const trace_obj* trace); +/* Returns an array of pointers to trace objects of currently active operations + and fills in number of active operations. Returns NULL if there's no active + operations. + Caller owns the returned objects. */ +trace_obj** census_get_active_ops(int* num_active_ops); + #ifdef __cplusplus } #endif diff --git a/test/core/statistics/trace_test.c b/test/core/statistics/trace_test.c index 6eafcf14568..18edfb433aa 100644 --- a/test/core/statistics/trace_test.c +++ b/test/core/statistics/trace_test.c @@ -32,10 +32,12 @@ */ #include +#include #include "src/core/statistics/census_interface.h" #include "src/core/statistics/census_tracing.h" #include "src/core/statistics/census_tracing.h" +#include #include #include #include @@ -172,6 +174,74 @@ static void test_trace_print(void) { census_tracing_shutdown(); } +/* Returns 1 if two ids are equal, otherwise returns 0. */ +static int ids_equal(census_op_id id1, census_op_id id2) { + return (id1.upper == id2.upper) && (id1.lower == id2.lower); +} + +static void test_get_active_ops(void) { + census_op_id id_1, id_2, id_3; + trace_obj** active_ops; + const char* annotation_txt[] = {"annotation 1", "a2"}; + int i = 0; + int n = 0; + + gpr_log(GPR_INFO, "test_get_active_ops"); + census_tracing_init(); + /* No active ops before calling start_op(). */ + active_ops = census_get_active_ops(&n); + GPR_ASSERT(active_ops == NULL); + GPR_ASSERT(n == 0); + + /* Starts one op */ + id_1 = census_tracing_start_op(); + census_add_method_tag(id_1, "foo_1"); + active_ops = census_get_active_ops(&n); + GPR_ASSERT(active_ops != NULL); + GPR_ASSERT(n == 1); + GPR_ASSERT(ids_equal(active_ops[0]->id, id_1)); + trace_obj_destroy(active_ops[0]); + gpr_free(active_ops); + active_ops = NULL; + + /* Start the second and the third ops */ + id_2 = census_tracing_start_op(); + census_add_method_tag(id_2, "foo_2"); + id_3 = census_tracing_start_op(); + census_add_method_tag(id_3, "foo_3"); + + active_ops = census_get_active_ops(&n); + GPR_ASSERT(n == 3); + for (i = 0; i < 3; i++) { + trace_obj_destroy(active_ops[i]); + } + gpr_free(active_ops); + active_ops = NULL; + + /* End the second op and add annotations to the third ops*/ + census_tracing_end_op(id_2); + census_tracing_print(id_3, annotation_txt[0]); + census_tracing_print(id_3, annotation_txt[1]); + + active_ops = census_get_active_ops(&n); + GPR_ASSERT(active_ops != NULL); + GPR_ASSERT(n == 2); + for (i = 0; i < 2; i++) { + trace_obj_destroy(active_ops[i]); + } + gpr_free(active_ops); + active_ops = NULL; + + /* End all ops. */ + census_tracing_end_op(id_1); + census_tracing_end_op(id_3); + active_ops = census_get_active_ops(&n); + GPR_ASSERT(active_ops == NULL); + GPR_ASSERT(n == 0); + + census_tracing_shutdown(); +} + int main(int argc, char** argv) { grpc_test_init(argc, argv); test_init_shutdown(); @@ -180,5 +250,6 @@ int main(int argc, char** argv) { test_concurrency(); test_add_method_tag_to_unknown_op_id(); test_trace_print(); + test_get_active_ops(); return 0; }