#ifndef TR2_TMR_H #define TR2_TMR_H #include "trace2.h" #include "trace2/tr2_tgt.h" /* * Define a mechanism to allow "stopwatch" timers. * * Timers can be used to measure "interesting" activity that does not * fit the "region" model, such as code called from many different * regions (like zlib) and/or where data for individual calls are not * interesting or are too numerous to be efficiently logged. * * Timer values are accumulated during program execution and emitted * to the Trace2 logs at program exit. * * To make this model efficient, we define a compile-time fixed set of * timers and timer ids using a "timer block" array in thread-local * storage. This gives us constant time access to each timer within * each thread, since we want start/stop operations to be as fast as * possible. This lets us avoid the complexities of dynamically * allocating a timer on the first use by a thread and/or possibly * sharing that timer definition with other concurrent threads. * However, this does require that we define time the set of timers at * compile time. * * Each thread uses the timer block in its thread-local storage to * compute partial sums for each timer (without locking). When a * thread exits, those partial sums are (under lock) added to the * global final sum. * * Using this "timer block" model costs ~48 bytes per timer per thread * (we have about six uint64 fields per timer). This does increase * the size of the thread-local storage block, but it is allocated (at * thread create time) and not on the thread stack, so I'm not worried * about the size. * * Partial sums for each timer are optionally emitted when a thread * exits. * * Final sums for each timer are emitted between the "exit" and * "atexit" events. * * A parallel "timer metadata" table contains the "category" and "name" * fields for each timer. This eliminates the need to include those * args in the various timer APIs. */ /* * The definition of an individual timer and used by an individual * thread. */ struct tr2_timer { /* * Total elapsed time for this timer in this thread in nanoseconds. */ uint64_t total_ns; /* * The maximum and minimum interval values observed for this * timer in this thread. */ uint64_t min_ns; uint64_t max_ns; /* * The value of the clock when this timer was started in this * thread. (Undefined when the timer is not active in this * thread.) */ uint64_t start_ns; /* * Number of times that this timer has been started and stopped * in this thread. (Recursive starts are ignored.) */ uint64_t interval_count; /* * Number of nested starts on the stack in this thread. (We * ignore recursive starts and use this to track the recursive * calls.) */ unsigned int recursion_count; }; /* * Metadata for a timer. */ struct tr2_timer_metadata { const char *category; const char *name; /* * True if we should emit per-thread events for this timer * when individual threads exit. */ unsigned int want_per_thread_events:1; }; /* * A compile-time fixed-size block of timers 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_timer_block { struct tr2_timer timer[TRACE2_NUMBER_OF_TIMERS]; }; /* * Private routines used by trace2.c to actually start/stop an * individual timer in the current thread. */ void tr2_start_timer(enum trace2_timer_id tid); void tr2_stop_timer(enum trace2_timer_id tid); /* * Add the current thread's timer data to the global totals. * This is called during thread-exit. * * Caller must be holding the tr2tls_mutex. */ void tr2_update_final_timers(void); /* * Emit per-thread timer data for the current thread. * This is called during thread-exit. */ void tr2_emit_per_thread_timers(tr2_tgt_evt_timer_t *fn_apply); /* * Emit global total timer values. * This is called during atexit handling. * * Caller must be holding the tr2tls_mutex. */ void tr2_emit_final_timers(tr2_tgt_evt_timer_t *fn_apply); #endif /* TR2_TMR_H */