blob: d5753576134aa8bc73d248c179f1201405351b86 [file] [log] [blame]
#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 */