141 lines
4.0 KiB
C
141 lines
4.0 KiB
C
|
#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 */
|