diff options
author | hunt <hunt> | 2005-06-18 07:24:42 +0000 |
---|---|---|
committer | hunt <hunt> | 2005-06-18 07:24:42 +0000 |
commit | e5d2abb56ca57a613e4ef9398a6499190b2265be (patch) | |
tree | 083465047d73bc498f2278d2eb3dc8514b455ad3 /runtime/stat.c | |
parent | ef0e92b0335077884edd5ac9997a203bec1839b0 (diff) | |
download | systemtap-steved-e5d2abb56ca57a613e4ef9398a6499190b2265be.tar.gz systemtap-steved-e5d2abb56ca57a613e4ef9398a6499190b2265be.tar.xz systemtap-steved-e5d2abb56ca57a613e4ef9398a6499190b2265be.zip |
2005-06-18 Martin Hunt <hunt@redhat.com>
* counter.c: New file. Counter aggregations.
* stat.c: New file. Stat aggregations.
* stat.h: Header file for stats.
* map-int.c: New file. Support for int64 values.
* map-stat.c: New file. Support for stat values.
* map-str.c: New file. Support for string values.
* map-values.c: Now just includes the necessary map-*.c files.
* stat-common.c: New file. Stats stuff common to Stats and maps
containing stats.
* Doxyfile: Bumped version to 0.6.
* README: Renamed README.doc and reorganized.
Diffstat (limited to 'runtime/stat.c')
-rw-r--r-- | runtime/stat.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/runtime/stat.c b/runtime/stat.c new file mode 100644 index 00000000..f57dea65 --- /dev/null +++ b/runtime/stat.c @@ -0,0 +1,254 @@ +#ifndef _STAT_C_ /* -*- linux-c -*- */ +#define _STAT_C_ + +/** @file stat.c + * @brief Statistics Aggregation + */ +/** @addtogroup stat Statistics Aggregation + * The Statistics aggregations keep per-cpu statistics. You + * must create all aggregations at probe initialization and it is + * best to not read them until probe exit. If you must read them + * while probes are running, the values may be slightly off due + * to a probe updating the statistics of one cpu while another cpu attempts + * to read the same data. This will also negatively impact performance. + * + * If you have a need to poll Stat data while probes are running, and + * you want to be sure the data is accurate, you can do + * @verbatim +#define NEED_STAT_LOCKS 1 +@endverbatim + * This will insert per-cpu spinlocks around all accesses to Stat data, + * which will reduce performance some. + * + * Stats keep track of count, sum, min and max. Average is computed + * from the sum and count when required. Histograms are optional. + * If you want a histogram, you must set "type" to HIST_LOG + * or HIST_LINEAR when you call _stp_stat_init(). + * + * @{ + */ + +#include "stat-common.c" + +/* for the paranoid. */ +#if NEED_STAT_LOCKS == 1 +#define STAT_LOCK(st) spin_lock(&st->lock) +#define STAT_UNLOCK(st) spin_unlock(&st->lock) +#else +#define STAT_LOCK(st) ; +#define STAT_UNLOCK(st) ; +#endif + +/** Initialize a Stat. + * Call this during probe initialization to create a Stat. + * + * @param type HIST_NONE, HIST_LOG, or HIST_LINEAR + * + * For HIST_LOG, the following additional parametrs are required: + * @param buckets - An integer specifying the number of buckets. + * + * For HIST_LINEAR, the following additional parametrs are required: + * @param start - An integer. The start of the histogram. + * @param stop - An integer. The stopping value. Should be > start. + * @param interval - An integer. The interval. + */ +Stat _stp_stat_init (int type, ...) +{ + int size, buckets=0, start=0, stop=0, interval=0; + struct stat_data *sd, *agg; + Stat st; + + if (type != HIST_NONE) { + va_list ap; + va_start (ap, type); + + if (type == HIST_LOG) { + buckets = va_arg(ap, int); + } else { + start = va_arg(ap, int); + stop = va_arg(ap, int); + interval = va_arg(ap, int); + /* FIXME. check interval != 0 and not too large */ + buckets = (stop - start) / interval; + if ((stop - start) % interval) buckets++; + } + va_end (ap); + } + st = (Stat) kmalloc (sizeof(struct _Stat), GFP_KERNEL); + if (st == NULL) + return NULL; + + size = buckets * sizeof(int64_t) + sizeof(struct stat_data); + sd = (struct stat_data *) __alloc_percpu (size, 8); + if (sd == NULL) + goto exit1; + +#if NEED_STAT_LOCKS == 1 + { + int i; + for_each_cpu(i) { + struct stat_data *sdp = per_cpu_ptr (sd, i); + sdp->lock = SPIN_LOCK_UNLOCKED; + } + } +#endif + + agg = (struct stat_data *)kmalloc (size, GFP_KERNEL); + if (agg == NULL) + goto exit2; + + st->hist_type = type; + st->hist_start = start; + st->hist_stop = stop; + st->hist_int = interval; + st->hist_buckets = buckets; + st->sd = sd; + st->agg = agg; + return st; + +exit2: + kfree (sd); +exit1: + kfree (st); + return NULL; +} + +/** Add to a Stat. + * Add an int64 to a Stat. + * + * @param st Stat + * @param val Value to add + */ +void _stp_stat_add (Stat st, int64_t val) +{ + struct stat_data *sd = per_cpu_ptr (st->sd, get_cpu()); + STAT_LOCK(sd); + __stp_stat_add (st, sd, val); + STAT_UNLOCK(sd); + put_cpu(); +} + +/** Get per-cpu Stats. + * Gets the Stats for a specific CPU. + * + * If NEED_STAT_LOCKS is set, you MUST call STAT_UNLOCK() + * when you are finished with the returned pointer. + * + * @param st Stat + * @param cpu CPU number + * @returns A pointer to a struct stat_data. + */ +struct stat_data *_stp_stat_get_cpu (Stat st, int cpu) +{ + struct stat_data *sd = per_cpu_ptr (st->sd, cpu); + STAT_LOCK(sd); + return sd; +} + +static void _stp_stat_clear_data (Stat st, struct stat_data *sd) +{ + int j; + sd->count = sd->sum = sd->min = sd->max = 0; + if (st->hist_type != HIST_NONE) { + for (j = 0; j < st->hist_buckets; j++) + sd->histogram[j] = 0; + } +} + +/** Get Stats. + * Gets the aggregated Stats for all CPUs. + * + * If NEED_STAT_LOCKS is set, you MUST call STAT_UNLOCK() + * when you are finished with the returned pointer. + * + * @param st Stat + * @param clear Set if you want the data cleared after the read. Useful + * for polling. + * @returns A pointer to a struct stat_data. + */ +struct stat_data *_stp_stat_get (Stat st, int clear) +{ + int i, j; + struct stat_data *agg = st->agg; + STAT_LOCK(agg); + _stp_stat_clear_data (st, agg); + + for_each_cpu(i) { + struct stat_data *sd = per_cpu_ptr (st->sd, i); + STAT_LOCK(sd); + if (sd->count) { + if (agg->count == 0) { + agg->min = sd->min; + agg->max = sd->max; + } + agg->count += sd->count; + agg->sum += sd->sum; + if (sd->max > agg->max) + agg->max = sd->max; + if (sd->min < agg->min) + agg->min = sd->min; + if (st->hist_type != HIST_NONE) { + for (j = 0; j < st->hist_buckets; j++) + agg->histogram[j] += sd->histogram[j]; + } + if (clear) + _stp_stat_clear_data (st, sd); + } + STAT_UNLOCK(sd); + } + return agg; +} + + +static void __stp_stat_print (char *fmt, Stat st, struct stat_data *sd, int cpu) +{ + int num; + char *f = (char *)fmt; + while (*f) { + f = next_fmt (f, &num); + _stp_stat_print_valtype (f, st, sd, cpu); + if (*f) + f++; + } + _stp_print_cstr ("\n"); + _stp_print_flush(); +} + +/** Print per-cpu Stats. + * Prints the Stats for each CPU. + * + * @param st Stat + * @param fmt @ref format_string + * @param clear Set if you want the data cleared after the read. Useful + * for polling. + */ +void _stp_stat_print_cpu (Stat st, char *fmt, int clear) +{ + int i; + for_each_cpu(i) { + struct stat_data *sd = per_cpu_ptr (st->sd, i); + STAT_LOCK(sd); + __stp_stat_print (fmt, st, sd, i); + if (clear) + _stp_stat_clear_data (st, sd); + STAT_UNLOCK(sd); + } +} + +/** Print Stats. + * Prints the Stats. + * + * @param st Stat + * @param fmt @ref format_string + * @param clear Set if you want the data cleared after the read. Useful + * for polling. + */ +void _stp_stat_print (Stat st, char *fmt, int clear) +{ + stat *agg = _stp_stat_get(st, clear); + __stp_stat_print (fmt, st, agg, 0); + STAT_UNLOCK(agg); +} +/** @} */ +#endif /* _STAT_C_ */ + |