runtime/stat.c - systemtap

Functions defined

Macros defined

Source code

/* -*- linux-c -*-
* Statistics Aggregation
* Copyright (C) 2005-2008, 2012 Red Hat Inc.
* Copyright (C) 2006 Intel Corporation
*
* This file is part of systemtap, and is free software.  You can
* redistribute it and/or modify it under the terms of the GNU General
* Public License (GPL); either version 2, or (at your option) any
* later version.
*/
#ifndef _STAT_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
@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"


/** 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.
*/
static Stat _stp_stat_init (int type, ...)
{
    int size, buckets=0, start=0, stop=0, interval=0;
    Stat st;

    if (type != HIST_NONE) {
        va_list ap;
        va_start (ap, type);

        if (type == HIST_LOG) {
            buckets = HIST_LOG_BUCKETS;
        } else {
            start = va_arg(ap, int);
            stop = va_arg(ap, int);
            interval = va_arg(ap, int);

            buckets = _stp_stat_calc_buckets(stop, start, interval);
            if (!buckets)
                return NULL;
        }
        va_end (ap);
    }

    size = buckets * sizeof(int64_t) + sizeof(stat_data);
    st = _stp_stat_alloc (size);
    if (st == NULL)
        return NULL;

    if (_stp_stat_initialize_locks(st) != 0) {
        _stp_stat_free(st);
        return NULL;
    }

    st->hist.type = type;
    st->hist.start = start;
    st->hist.stop = stop;
    st->hist.interval = interval;
    st->hist.buckets = buckets;
    return st;
}

/** Delete Stat.
* Call this to free all memory allocated during initialization.
*
* @param st Stat
*/
static void _stp_stat_del (Stat st)
{
    if (st) {
        _stp_stat_destroy_locks(st);
        _stp_stat_free(st);
    }
}

/** Add to a Stat.
* Add an int64 to a Stat.
*
* @param st Stat
* @param val Value to add
*/
static void _stp_stat_add (Stat st, int64_t val)
{
    stat_data *sd = _stp_stat_per_cpu_ptr (st, STAT_GET_CPU());
    STAT_LOCK(sd);
    __stp_stat_add (&st->hist, sd, val);
    STAT_UNLOCK(sd);
    STAT_PUT_CPU();
}


static void _stp_stat_clear_data (Stat st, 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.
*
* @param st Stat
* @param clear Set if you want the data cleared after the read. Useful
* for polling.
* @returns A pointer to a stat.
*/
static stat_data *_stp_stat_get (Stat st, int clear)
{
    int i, j;
    stat_data *agg = _stp_stat_get_agg(st);
    stat_data *sd;
    STAT_LOCK(agg);
    _stp_stat_clear_data (st, agg);

    for_each_possible_cpu(i) {
        stat_data *sd = _stp_stat_per_cpu_ptr (st, 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);
    }

    /*
     * Originally this function returned the aggregate still
     * locked and it was the caller's responsibility to unlock the
     * aggregate. However the translator generated code that called
     * this function wasn't unlocking it...
     *
     * But, the translator generates its own locks for global
     * variables (like stats), so we don't need to return the
     * aggregate still locked.
     *
     * It is possible we could even skip locking the aggregate in
     * this function, but to be a bit paranoid lets keep the
     * locking.
     */
    STAT_UNLOCK(agg);
    return agg;
}


/** Clear Stats.
* Clears the Stats.
*
* @param st Stat
*/
static void _stp_stat_clear (Stat st)
{
    int i;

    for_each_possible_cpu(i) {
        stat_data *sd = _stp_stat_per_cpu_ptr (st, i);
        STAT_LOCK(sd);
        _stp_stat_clear_data (st, sd);
        STAT_UNLOCK(sd);
    }
}
/** @} */
#endif /* _STAT_C_ */