#include "cache.h" #include "thread-utils.h" #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" #include "trace2/tr2_tmr.h" #define MY_MAX(a, b) ((a) > (b) ? (a) : (b)) #define MY_MIN(a, b) ((a) < (b) ? (a) : (b)) /* * A global timer block to aggregate values from the partial sums from * each thread. */ static struct tr2_timer_block final_timer_block; /* access under tr2tls_mutex */ /* * Define metadata for each stopwatch timer. * * This array must match "enum trace2_timer_id" and the values * in "struct tr2_timer_block.timer[*]". */ static struct tr2_timer_metadata tr2_timer_metadata[TRACE2_NUMBER_OF_TIMERS] = { [TRACE2_TIMER_ID_TEST1] = { .category = "test", .name = "test1", .want_per_thread_events = 0, }, [TRACE2_TIMER_ID_TEST2] = { .category = "test", .name = "test2", .want_per_thread_events = 1, }, /* Add additional metadata before here. */ }; void tr2_start_timer(enum trace2_timer_id tid) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); struct tr2_timer *t = &ctx->timer_block.timer[tid]; t->recursion_count++; if (t->recursion_count > 1) return; /* ignore recursive starts */ t->start_ns = getnanotime(); } void tr2_stop_timer(enum trace2_timer_id tid) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); struct tr2_timer *t = &ctx->timer_block.timer[tid]; uint64_t ns_now; uint64_t ns_interval; assert(t->recursion_count > 0); t->recursion_count--; if (t->recursion_count) return; /* still in recursive call(s) */ ns_now = getnanotime(); ns_interval = ns_now - t->start_ns; t->total_ns += ns_interval; /* * min_ns was initialized to zero (in the xcalloc()) rather * than UINT_MAX when the block of timers was allocated, * so we should always set both the min_ns and max_ns values * the first time that the timer is used. */ if (!t->interval_count) { t->min_ns = ns_interval; t->max_ns = ns_interval; } else { t->min_ns = MY_MIN(ns_interval, t->min_ns); t->max_ns = MY_MAX(ns_interval, t->max_ns); } t->interval_count++; ctx->used_any_timer = 1; if (tr2_timer_metadata[tid].want_per_thread_events) ctx->used_any_per_thread_timer = 1; } void tr2_update_final_timers(void) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); enum trace2_timer_id tid; if (!ctx->used_any_timer) return; /* * Accessing `final_timer_block` requires holding `tr2tls_mutex`. * We assume that our caller is holding the lock. */ for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) { struct tr2_timer *t_final = &final_timer_block.timer[tid]; struct tr2_timer *t = &ctx->timer_block.timer[tid]; if (t->recursion_count) { /* * The current thread is exiting with * timer[tid] still running. * * Technically, this is a bug, but I'm going * to ignore it. * * I don't think it is worth calling die() * for. I don't think it is worth killing the * process for this bookkeeping error. We * might want to call warning(), but I'm going * to wait on that. * * The downside here is that total_ns won't * include the current open interval (now - * start_ns). I can live with that. */ } if (!t->interval_count) continue; /* this timer was not used by this thread */ t_final->total_ns += t->total_ns; /* * final_timer_block.timer[tid].min_ns was initialized to * was initialized to zero rather than UINT_MAX, so we should * always set both the min_ns and max_ns values the first time * that we add a partial sum into it. */ if (!t_final->interval_count) { t_final->min_ns = t->min_ns; t_final->max_ns = t->max_ns; } else { t_final->min_ns = MY_MIN(t_final->min_ns, t->min_ns); t_final->max_ns = MY_MAX(t_final->max_ns, t->max_ns); } t_final->interval_count += t->interval_count; } } void tr2_emit_per_thread_timers(tr2_tgt_evt_timer_t *fn_apply) { struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); enum trace2_timer_id tid; if (!ctx->used_any_per_thread_timer) return; /* * For each timer, if the timer wants per-thread events and * this thread used it, emit it. */ for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) if (tr2_timer_metadata[tid].want_per_thread_events && ctx->timer_block.timer[tid].interval_count) fn_apply(&tr2_timer_metadata[tid], &ctx->timer_block.timer[tid], 0); } void tr2_emit_final_timers(tr2_tgt_evt_timer_t *fn_apply) { enum trace2_timer_id tid; /* * Accessing `final_timer_block` requires holding `tr2tls_mutex`. * We assume that our caller is holding the lock. */ for (tid = 0; tid < TRACE2_NUMBER_OF_TIMERS; tid++) if (final_timer_block.timer[tid].interval_count) fn_apply(&tr2_timer_metadata[tid], &final_timer_block.timer[tid], 1); }