Browse Source
Add global counters mechanism to Trace2. The Trace2 counters mechanism adds the ability to create a set of global counter variables and an API to increment them efficiently. Counters can optionally report per-thread usage in addition to the sum across all threads. Counter events are emitted to the Trace2 logs when a thread exits and at process exit. Counters are an alternative to `data` and `data_json` events. Counters are useful when you want to measure something across the life of the process, when you don't want per-measurement events for performance reasons, when the data does not fit conveniently within a region, or when your control flow does not easily let you write the final total. For example, you might use this to report the number of calls to unzip() or the number of de-delta steps during a checkout. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
Jeff Hostetler
2 years ago
committed by
Junio C Hamano
13 changed files with 517 additions and 7 deletions
@ -0,0 +1,101 @@
@@ -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); |
||||
} |
@ -0,0 +1,104 @@
@@ -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 */ |
Loading…
Reference in new issue