diff options
Diffstat (limited to 'trace2')
-rw-r--r-- | trace2/tr2_ctr.c | 101 | ||||
-rw-r--r-- | trace2/tr2_ctr.h | 104 | ||||
-rw-r--r-- | trace2/tr2_tgt.h | 7 | ||||
-rw-r--r-- | trace2/tr2_tgt_event.c | 19 | ||||
-rw-r--r-- | trace2/tr2_tgt_normal.c | 16 | ||||
-rw-r--r-- | trace2/tr2_tgt_perf.c | 17 | ||||
-rw-r--r-- | trace2/tr2_tls.h | 4 |
7 files changed, 268 insertions, 0 deletions
diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c new file mode 100644 index 0000000000..483ca7c308 --- /dev/null +++ b/trace2/tr2_ctr.c @@ -0,0 +1,101 @@ +#include "cache.h" +#include "thread-utils.h" +#include "trace2/tr2_tgt.h" +#include "trace2/tr2_tls.h" +#include "trace2/tr2_ctr.h" + +/* + * A global counter block to aggregrate values from the partial sums + * from each thread. + */ +static struct tr2_counter_block final_counter_block; /* access under tr2tls_mutex */ + +/* + * Define metadata for each global counter. + * + * This array must match the "enum trace2_counter_id" and the values + * in "struct tr2_counter_block.counter[*]". + */ +static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTERS] = { + [TRACE2_COUNTER_ID_TEST1] = { + .category = "test", + .name = "test1", + .want_per_thread_events = 0, + }, + [TRACE2_COUNTER_ID_TEST2] = { + .category = "test", + .name = "test2", + .want_per_thread_events = 1, + }, + + /* Add additional metadata before here. */ +}; + +void tr2_counter_increment(enum trace2_counter_id cid, uint64_t value) +{ + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); + struct tr2_counter *c = &ctx->counter_block.counter[cid]; + + c->value += value; + + ctx->used_any_counter = 1; + if (tr2_counter_metadata[cid].want_per_thread_events) + ctx->used_any_per_thread_counter = 1; +} + +void tr2_update_final_counters(void) +{ + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); + enum trace2_counter_id cid; + + if (!ctx->used_any_counter) + return; + + /* + * Access `final_counter_block` requires holding `tr2tls_mutex`. + * We assume that our caller is holding the lock. + */ + + for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++) { + struct tr2_counter *c_final = &final_counter_block.counter[cid]; + const struct tr2_counter *c = &ctx->counter_block.counter[cid]; + + c_final->value += c->value; + } +} + +void tr2_emit_per_thread_counters(tr2_tgt_evt_counter_t *fn_apply) +{ + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); + enum trace2_counter_id cid; + + if (!ctx->used_any_per_thread_counter) + return; + + /* + * For each counter, if the counter wants per-thread events + * and this thread used it (the value is non-zero), emit it. + */ + for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++) + if (tr2_counter_metadata[cid].want_per_thread_events && + ctx->counter_block.counter[cid].value) + fn_apply(&tr2_counter_metadata[cid], + &ctx->counter_block.counter[cid], + 0); +} + +void tr2_emit_final_counters(tr2_tgt_evt_counter_t *fn_apply) +{ + enum trace2_counter_id cid; + + /* + * Access `final_counter_block` requires holding `tr2tls_mutex`. + * We assume that our caller is holding the lock. + */ + + for (cid = 0; cid < TRACE2_NUMBER_OF_COUNTERS; cid++) + if (final_counter_block.counter[cid].value) + fn_apply(&tr2_counter_metadata[cid], + &final_counter_block.counter[cid], + 1); +} diff --git a/trace2/tr2_ctr.h b/trace2/tr2_ctr.h new file mode 100644 index 0000000000..a2267ee990 --- /dev/null +++ b/trace2/tr2_ctr.h @@ -0,0 +1,104 @@ +#ifndef TR2_CTR_H +#define TR2_CTR_H + +#include "trace2.h" +#include "trace2/tr2_tgt.h" + +/* + * Define a mechanism to allow global "counters". + * + * Counters can be used count interesting activity that does not fit + * the "region and data" model, such as code called from many + * different regions and/or where you want to count a number of items, + * but don't have control of when the last item will be processed, + * such as counter the number of calls to `lstat()`. + * + * Counters differ from Trace2 "data" events. Data events are emitted + * immediately and are appropriate for documenting loop counters at + * the end of a region, for example. Counter values are accumulated + * during the program and final counter values are emitted at program + * exit. + * + * To make this model efficient, we define a compile-time fixed set of + * counters and counter ids using a fixed size "counter block" array + * in thread-local storage. This gives us constant time, lock-free + * access to each counter within each thread. This lets us avoid the + * complexities of dynamically allocating a counter and sharing that + * definition with other threads. + * + * Each thread uses the counter block in its thread-local storage to + * increment partial sums for each counter (without locking). When a + * thread exits, those partial sums are (under lock) added to the + * global final sum. + * + * Partial sums for each counter are optionally emitted when a thread + * exits. + * + * Final sums for each counter are emitted between the "exit" and + * "atexit" events. + * + * A parallel "counter metadata" table contains the "category" and + * "name" fields for each counter. This eliminates the need to + * include those args in the various counter APIs. + */ + +/* + * The definition of an individual counter as used by an individual + * thread (and later in aggregation). + */ +struct tr2_counter { + uint64_t value; +}; + +/* + * Metadata for a counter. + */ +struct tr2_counter_metadata { + const char *category; + const char *name; + + /* + * True if we should emit per-thread events for this counter + * when individual threads exit. + */ + unsigned int want_per_thread_events:1; +}; + +/* + * A compile-time fixed block of counters to insert into thread-local + * storage. This wrapper is used to avoid quirks of C and the usual + * need to pass an array size argument. + */ +struct tr2_counter_block { + struct tr2_counter counter[TRACE2_NUMBER_OF_COUNTERS]; +}; + +/* + * Private routines used by trace2.c to increment a counter for the + * current thread. + */ +void tr2_counter_increment(enum trace2_counter_id cid, uint64_t value); + +/* + * Add the current thread's counter data to the global totals. + * This is called during thread-exit. + * + * Caller must be holding the tr2tls_mutex. + */ +void tr2_update_final_counters(void); + +/* + * Emit per-thread counter data for the current thread. + * This is called during thread-exit. + */ +void tr2_emit_per_thread_counters(tr2_tgt_evt_counter_t *fn_apply); + +/* + * Emit global counter values. + * This is called during atexit handling. + * + * Caller must be holding the tr2tls_mutex. + */ +void tr2_emit_final_counters(tr2_tgt_evt_counter_t *fn_apply); + +#endif /* TR2_CTR_H */ diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h index 85c8d2d7f5..bf8745c4f0 100644 --- a/trace2/tr2_tgt.h +++ b/trace2/tr2_tgt.h @@ -6,6 +6,8 @@ struct repository; struct json_writer; struct tr2_timer_metadata; struct tr2_timer; +struct tr2_counter_metadata; +struct tr2_counter; #define NS_TO_SEC(ns) ((double)(ns) / 1.0e9) @@ -104,6 +106,10 @@ typedef void(tr2_tgt_evt_timer_t)(const struct tr2_timer_metadata *meta, const struct tr2_timer *timer, int is_final_data); +typedef void(tr2_tgt_evt_counter_t)(const struct tr2_counter_metadata *meta, + const struct tr2_counter *counter, + int is_final_data); + /* * "vtable" for a TRACE2 target. Use NULL if a target does not want * to emit that message. @@ -141,6 +147,7 @@ struct tr2_tgt { tr2_tgt_evt_data_json_fl_t *pfn_data_json_fl; tr2_tgt_evt_printf_va_fl_t *pfn_printf_va_fl; tr2_tgt_evt_timer_t *pfn_timer; + tr2_tgt_evt_counter_t *pfn_counter; }; /* clang-format on */ diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index af5a8edb47..16f6332755 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -642,6 +642,24 @@ static void fn_timer(const struct tr2_timer_metadata *meta, jw_release(&jw); } +static void fn_counter(const struct tr2_counter_metadata *meta, + const struct tr2_counter *counter, + int is_final_data) +{ + const char *event_name = is_final_data ? "counter" : "th_counter"; + struct json_writer jw = JSON_WRITER_INIT; + + jw_object_begin(&jw, 0); + event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); + jw_object_string(&jw, "category", meta->category); + jw_object_string(&jw, "name", meta->name); + jw_object_intmax(&jw, "count", counter->value); + jw_end(&jw); + + tr2_dst_write_line(&tr2dst_event, &jw.json); + jw_release(&jw); +} + struct tr2_tgt tr2_tgt_event = { .pdst = &tr2dst_event, @@ -674,4 +692,5 @@ struct tr2_tgt tr2_tgt_event = { .pfn_data_json_fl = fn_data_json_fl, .pfn_printf_va_fl = NULL, .pfn_timer = fn_timer, + .pfn_counter = fn_counter, }; diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index b079baf100..fbbef68dfc 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -351,6 +351,21 @@ static void fn_timer(const struct tr2_timer_metadata *meta, strbuf_release(&buf_payload); } +static void fn_counter(const struct tr2_counter_metadata *meta, + const struct tr2_counter *counter, + int is_final_data) +{ + const char *event_name = is_final_data ? "counter" : "th_counter"; + struct strbuf buf_payload = STRBUF_INIT; + + strbuf_addf(&buf_payload, "%s %s/%s value:%"PRIu64, + event_name, meta->category, meta->name, + counter->value); + + normal_io_write_fl(__FILE__, __LINE__, &buf_payload); + strbuf_release(&buf_payload); +} + struct tr2_tgt tr2_tgt_normal = { .pdst = &tr2dst_normal, @@ -383,4 +398,5 @@ struct tr2_tgt tr2_tgt_normal = { .pfn_data_json_fl = NULL, .pfn_printf_va_fl = fn_printf_va_fl, .pfn_timer = fn_timer, + .pfn_counter = fn_counter, }; diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index e69375e979..adae803263 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -578,6 +578,22 @@ static void fn_timer(const struct tr2_timer_metadata *meta, strbuf_release(&buf_payload); } +static void fn_counter(const struct tr2_counter_metadata *meta, + const struct tr2_counter *counter, + int is_final_data) +{ + const char *event_name = is_final_data ? "counter" : "th_counter"; + struct strbuf buf_payload = STRBUF_INIT; + + strbuf_addf(&buf_payload, "name:%s value:%"PRIu64, + meta->name, + counter->value); + + perf_io_write_fl(__FILE__, __LINE__, event_name, NULL, NULL, NULL, + meta->category, &buf_payload); + strbuf_release(&buf_payload); +} + struct tr2_tgt tr2_tgt_perf = { .pdst = &tr2dst_perf, @@ -610,4 +626,5 @@ struct tr2_tgt tr2_tgt_perf = { .pfn_data_json_fl = fn_data_json_fl, .pfn_printf_va_fl = fn_printf_va_fl, .pfn_timer = fn_timer, + .pfn_counter = fn_counter, }; diff --git a/trace2/tr2_tls.h b/trace2/tr2_tls.h index a064b66e4c..f9049805d4 100644 --- a/trace2/tr2_tls.h +++ b/trace2/tr2_tls.h @@ -2,6 +2,7 @@ #define TR2_TLS_H #include "strbuf.h" +#include "trace2/tr2_ctr.h" #include "trace2/tr2_tmr.h" /* @@ -22,8 +23,11 @@ struct tr2tls_thread_ctx { size_t nr_open_regions; /* plays role of "nr" in ALLOC_GROW */ int thread_id; struct tr2_timer_block timer_block; + struct tr2_counter_block counter_block; unsigned int used_any_timer:1; unsigned int used_any_per_thread_timer:1; + unsigned int used_any_counter:1; + unsigned int used_any_per_thread_counter:1; }; /* |