summaryrefslogtreecommitdiffstats
path: root/runtime/stat.c
diff options
context:
space:
mode:
authorhunt <hunt>2005-06-18 07:24:42 +0000
committerhunt <hunt>2005-06-18 07:24:42 +0000
commite5d2abb56ca57a613e4ef9398a6499190b2265be (patch)
tree083465047d73bc498f2278d2eb3dc8514b455ad3 /runtime/stat.c
parentef0e92b0335077884edd5ac9997a203bec1839b0 (diff)
downloadsystemtap-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.c254
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_ */
+