const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
uint64_t region_id);
+typedef enum {
+ DM_STATS_OBJECT_TYPE_NONE,
+ DM_STATS_OBJECT_TYPE_AREA,
+ DM_STATS_OBJECT_TYPE_REGION,
+ DM_STATS_OBJECT_TYPE_GROUP
+} dm_stats_obj_type_t;
+
/*
* Statistics cursor
*
* A dm_stats handle maintains an optional cursor into the statistics
- * regions and areas that it stores. Iterators are provided to visit
- * each region, or each area in a handle and accessor methods are
- * provided to obtain properties and values for the region or area
- * at the current cursor position.
+ * tables that it stores. Iterators are provided to visit each region,
+ * area, or group in a handle and accessor methods are provided to
+ * obtain properties and values for the object at the current cursor
+ * position.
+ *
+ * Using the cursor simplifies walking all regions or groups when
+ * the tables are sparse (i.e. contains some present and some
+ * non-present region_id or group_id values either due to program_id
+ * filtering or the ordering of region and group creation and deletion).
+ *
+ * Simple macros are provided to visit each area, region, or group,
+ * contained in a handle and applications are encouraged to use these
+ * where possible.
+ */
+
+/*
+ * Walk flags are used to initialise a dm_stats handle's cursor control
+ * and to select region or group aggregation when calling a metric or
+ * counter property method with immediate group, region, and area ID
+ * values.
*
- * Using the cursor simplifies walking all regions or areas when the
- * region table is sparse (i.e. contains some present and some
- * non-present region_id values either due to program_id filtering
- * or the ordering of region creation and deletion).
+ * Walk flags are stored in the uppermost word of a uint64_t so that
+ * a region_id or group_id may be encoded in the lower bits. This
+ * allows an aggregate region_id or group_id to be specified when
+ * retrieving counter or metric values.
+ *
+ * Flags may be ORred together when used to initialise a dm_stats_walk:
+ * the resulting walk will visit instance of each type specified by
+ * the flag combination.
+ */
+#define DM_STATS_WALK_AREA 0x1000000000000
+#define DM_STATS_WALK_REGION 0x2000000000000
+#define DM_STATS_WALK_GROUP 0x4000000000000
+
+#define DM_STATS_WALK_ALL 0x7000000000000
+#define DM_STATS_WALK_DEFAULT (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)
+
+/*
+ * Skip regions from a DM_STATS_WALK_REGION that contain only a single
+ * area: in this case the region's aggregate values are identical to
+ * the values of the single contained area. Setting this flag will
+ * suppress these duplicate entries during a dm_stats_walk_* with the
+ * DM_STATS_WALK_REGION flag set.
+ */
+#define DM_STATS_WALK_SKIP_SINGLE_AREA 0x8000000000000
+
+/*
+ * Initialise the cursor control of a dm_stats handle for the specified
+ * walk type(s). Including a walk flag in the flags argument will cause
+ * any subsequent walk to visit that type of object (until the next
+ * call to dm_stats_walk_init()).
*/
+int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags);
/*
- * Initialise the cursor of a dm_stats handle to address the first
- * present region. It is valid to attempt to walk a NULL stats handle
- * or a handle containing no present regions; in this case any call to
- * dm_stats_walk_next() becomes a no-op and all calls to
- * dm_stats_walk_end() return true.
+ * Set the cursor of a dm_stats handle to address the first present
+ * group, region, or area of the currently configured walk. It is
+ * valid to attempt to walk a NULL stats handle or a handle containing
+ * no present regions; in this case any call to dm_stats_walk_next()
+ * becomes a no-op and all calls to dm_stats_walk_end() return true.
*/
void dm_stats_walk_start(struct dm_stats *dms);
/*
* Advance the statistics cursor to the next area, or to the next
- * present region if at the end of the current region.
+ * present region if at the end of the current region. If the end of
+ * the region, area, or group tables is reached a subsequent call to
+ * dm_stats_walk_end() will return 1 and dm_stats_object_type() called
+ * on the location will return DM_STATS_OBJECT_TYPE_NONE,
*/
void dm_stats_walk_next(struct dm_stats *dms);
/*
- * Advance the statistics cursor to the next region.
+ * Force the statistics cursor to advance to the next region. This will
+ * stop any in-progress area walk (by clearing DM_STATS_WALK_AREA) and
+ * advance the cursor to the next present region, the first present
+ * group (if DM_STATS_GROUP_WALK is set), or to the end. In this case a
+ * subsequent call to dm_stats_walk_end() will return 1 and a call to
+ * dm_stats_object_type() for the location will return
+ * DM_STATS_OBJECT_TYPE_NONE.
*/
void dm_stats_walk_next_region(struct dm_stats *dms);
*/
int dm_stats_walk_end(struct dm_stats *dms);
+/*
+ * Return the type of object at the location specified by region_id
+ * and area_id. If either region_id or area_id uses one of the special
+ * values DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT the
+ * corresponding region or area identifier will be taken from the
+ * current cursor location. If the cursor location or the value encoded
+ * by region_id and area_id indicates an aggregate region or group,
+ * this will be reflected in the value returned.
+ */
+dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
+ uint64_t region_id,
+ uint64_t area_id);
+
+/*
+ * Return the type of object at the current stats cursor location.
+ */
+dm_stats_obj_type_t dm_stats_current_object_type(const struct dm_stats *dms);
+
/*
* Stats iterators
*
* executed.
*/
#define dm_stats_foreach_region(dms) \
-for (dm_stats_walk_start((dms)); \
+for (dm_stats_walk_init((dms), DM_STATS_WALK_REGION), \
+ dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next_region((dms)))
/*
* be executed.
*/
#define dm_stats_foreach_area(dms) \
-for (dm_stats_walk_start((dms)); \
+for (dm_stats_walk_init((dms), DM_STATS_WALK_AREA), \
+ dm_stats_walk_start((dms)); \
!dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
+/*
+ * Iterate over the regions table visiting each group. Metric and
+ * counter methods will return values for the group.
+ *
+ * If the group table is empty or unpopulated the loop body will not
+ * be executed.
+ */
+#define dm_stats_foreach_group(dms) \
+for (dm_stats_walk_init((dms), DM_STATS_WALK_GROUP), \
+ dm_stats_group_walk_start(dms); \
+ !dm_stats_group_walk_end(dms); \
+ dm_stats_group_walk_next(dms))
+
/*
* Start a walk iterating over the regions contained in dm_stats handle
* 'dms'.
*/
const char *dm_stats_get_alias(const struct dm_stats *dms, uint64_t id);
+#define DM_STATS_GROUP_NONE UINT64_MAX
/*
* Return the group_id that the specified region_id belongs to, or the
* special value DM_STATS_GROUP_NONE if the region does not belong
#include "math.h" /* log10() */
#define DM_STATS_REGION_NOT_PRESENT UINT64_MAX
-#define DM_STATS_GROUP_NOT_PRESENT UINT64_MAX
+#define DM_STATS_GROUP_NOT_PRESENT DM_STATS_GROUP_NONE
#define NSEC_PER_USEC 1000L
#define NSEC_PER_MSEC 1000000L
struct dm_stats_region *regions;
struct dm_stats_group *groups;
/* statistics cursor */
+ uint64_t walk_flags; /* walk control flags */
+ uint64_t cur_flags;
+ uint64_t cur_group;
uint64_t cur_region;
uint64_t cur_area;
};
dms->max_region = DM_STATS_REGION_NOT_PRESENT;
dms->regions = NULL;
+ /* maintain compatibility with earlier walk version */
+ dms->walk_flags = dms->cur_flags = DM_STATS_WALK_DEFAULT;
+
return dms;
bad:
return 0;
}
-static void _stats_walk_next(const struct dm_stats *dms, int skip_region,
- uint64_t *cur_r, uint64_t *cur_a)
+static void _stats_walk_next_present(const struct dm_stats *dms,
+ uint64_t *flags,
+ uint64_t *cur_r, uint64_t *cur_a,
+ uint64_t *cur_g)
{
- struct dm_stats_region *cur;
- int present;
+ struct dm_stats_region *cur = NULL;
+
+ /* start of walk: region loop advances *cur_r to 0. */
+ if (*cur_r != DM_STATS_REGION_NOT_PRESENT)
+ cur = &dms->regions[*cur_r];
+ /* within current region? */
+ if (cur && (*flags & DM_STATS_WALK_AREA)) {
+ if (++(*cur_a) < _nr_areas_region(cur))
+ return;
+ else
+ *cur_a = 0;
+ }
+
+ /* advance to next present, non-skipped region or end */
+ while (++(*cur_r) <= dms->max_region) {
+ cur = &dms->regions[*cur_r];
+ if (!_stats_region_present(cur))
+ continue;
+ if ((*flags & DM_STATS_WALK_SKIP_SINGLE_AREA))
+ if (!(*flags & DM_STATS_WALK_AREA))
+ if (_nr_areas_region(cur) < 2)
+ continue;
+ /* matching region found */
+ break;
+ }
+ return;
+}
+
+static void _stats_walk_next(const struct dm_stats *dms, uint64_t *flags,
+ uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
+{
if (!dms || !dms->regions)
return;
- cur = dms->regions + *cur_r;
- present = _stats_region_present(cur);
+ if (*flags & DM_STATS_WALK_AREA) {
+ /* advance to next area, region, or end */
+ _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
+ return;
+ }
- if (skip_region && present)
- *cur_a = _nr_areas_region(cur);
+ if (*flags & DM_STATS_WALK_REGION) {
+ /* enable region aggregation */
+ *cur_a = DM_STATS_WALK_REGION;
+ _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
+ return;
+ }
- if (skip_region || !present || ++(*cur_a) == _nr_areas_region(cur)) {
- *cur_a = 0;
- while(!dm_stats_region_present(dms, ++(*cur_r))
- && *cur_r < dms->max_region)
- ; /* keep walking until a present region is found
- * or the end of the table is reached. */
+ if (*flags & DM_STATS_WALK_GROUP) {
+ /* enable group aggregation */
+ *cur_r = *cur_a = DM_STATS_WALK_GROUP;
+ while (!_stats_group_id_present(dms, ++(*cur_g))
+ && (*cur_g) < dms->max_region + 1)
+ ; /* advance to next present group or end */
+ return;
}
+
+ log_error("stats_walk_next called with empty walk flags");
}
-static void _stats_walk_start(const struct dm_stats *dms,
- uint64_t *cur_r, uint64_t *cur_a)
+static void _group_walk_start(const struct dm_stats *dms, uint64_t *flags,
+ uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
{
- if (!dms || !dms->regions)
+ if (!(*flags & DM_STATS_WALK_GROUP))
+ return;
+
+ *cur_a = *cur_r = DM_STATS_WALK_GROUP;
+ *cur_g = 0;
+
+ /* advance to next present group or end */
+ while ((*cur_g) <= dms->max_region) {
+ if (_stats_region_is_grouped(dms, *cur_g))
+ break;
+ (*cur_g)++;
+ }
+
+ if (*cur_g > dms->max_region)
+ /* no groups to walk */
+ *flags &= ~DM_STATS_WALK_GROUP;
+}
+
+static void _stats_walk_start(const struct dm_stats *dms, uint64_t *flags,
+ uint64_t *cur_r, uint64_t *cur_a,
+ uint64_t *cur_g)
+{
+ log_debug("starting stats walk with %s %s %s %s",
+ (*flags & DM_STATS_WALK_AREA) ? "AREA" : "",
+ (*flags & DM_STATS_WALK_REGION) ? "REGION" : "",
+ (*flags & DM_STATS_WALK_GROUP) ? "GROUP" : "",
+ (*flags & DM_STATS_WALK_SKIP_SINGLE_AREA) ? "SKIP" : "");
+
+ if (!dms->regions)
return;
- *cur_r = 0;
+ if (!(*flags & (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)))
+ return _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
+
+ /* initialise cursor state */
*cur_a = 0;
+ *cur_r = DM_STATS_REGION_NOT_PRESENT;
+ *cur_g = DM_STATS_GROUP_NOT_PRESENT;
+
+ if (!(*flags & DM_STATS_WALK_AREA))
+ *cur_a = DM_STATS_WALK_REGION;
- /* advance to the first present region */
- if (!dm_stats_region_present(dms, dms->cur_region))
- _stats_walk_next(dms, 0, cur_r, cur_a);
+ /* advance to first present, non-skipped region */
+ _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
+}
+
+#define DM_STATS_WALK_MASK (DM_STATS_WALK_AREA \
+ | DM_STATS_WALK_REGION \
+ | DM_STATS_WALK_GROUP \
+ | DM_STATS_WALK_SKIP_SINGLE_AREA)
+
+int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags)
+{
+ if (!dms)
+ return_0;
+
+ if (flags & ~DM_STATS_WALK_MASK) {
+ log_error("Unknown value in walk flags: 0x" FMTx64,
+ flags & ~DM_STATS_WALK_MASK);
+ return 0;
+ }
+ dms->walk_flags = flags;
+ log_debug("dm_stats_walk_init: initialised flags to " FMTx64, flags);
+ return 1;
}
void dm_stats_walk_start(struct dm_stats *dms)
{
- _stats_walk_start(dms, &dms->cur_region, &dms->cur_area);
+ if (!dms || !dms->regions)
+ return;
+
+ dms->cur_flags = dms->walk_flags;
+
+ _stats_walk_start(dms, &dms->cur_flags,
+ &dms->cur_region, &dms->cur_area,
+ &dms->cur_group);
}
void dm_stats_walk_next(struct dm_stats *dms)
{
- _stats_walk_next(dms, 0, &dms->cur_region, &dms->cur_area);
+ _stats_walk_next(dms, &dms->cur_flags,
+ &dms->cur_region, &dms->cur_area,
+ &dms->cur_group);
}
void dm_stats_walk_next_region(struct dm_stats *dms)
{
- _stats_walk_next(dms, 1, &dms->cur_region, &dms->cur_area);
+ dms->cur_flags &= ~DM_STATS_WALK_AREA;
+ _stats_walk_next(dms, &dms->cur_flags,
+ &dms->cur_region, &dms->cur_area,
+ &dms->cur_group);
}
-static int _stats_walk_end(const struct dm_stats *dms,
- uint64_t *cur_r, uint64_t *cur_a)
+/*
+ * Return 1 if any regions remain that are present and not skipped
+ * by the current walk flags or 0 otherwise.
+ */
+static uint64_t _stats_walk_any_unskipped(const struct dm_stats *dms,
+ uint64_t *flags,
+ uint64_t *cur_r, uint64_t *cur_a)
{
- struct dm_stats_region *region = NULL;
- int end = 0;
+ struct dm_stats_region *region;
+ uint64_t i;
+
+ if (*cur_r > dms->max_region)
+ return 0;
+ for (i = *cur_r; i <= dms->max_region; i++) {
+ region = &dms->regions[i];
+ if (!_stats_region_present(region))
+ continue;
+ if ((*flags & DM_STATS_WALK_SKIP_SINGLE_AREA)
+ && !(*flags & DM_STATS_WALK_AREA))
+ if (_nr_areas_region(region) < 2)
+ continue;
+ return 1;
+ }
+ return 0;
+}
+
+static void _stats_walk_end_areas(const struct dm_stats *dms, uint64_t *flags,
+ uint64_t *cur_r, uint64_t *cur_a,
+ uint64_t *cur_g)
+{
+ int end = !_stats_walk_any_unskipped(dms, flags, cur_r, cur_a);
+
+ if (!(*flags & DM_STATS_WALK_AREA))
+ return;
+
+ if (!end)
+ return;
+
+ *flags &= ~DM_STATS_WALK_AREA;
+ if (*flags & DM_STATS_WALK_REGION) {
+ /* start region walk */
+ *cur_a = DM_STATS_WALK_REGION;
+ *cur_r = DM_STATS_REGION_NOT_PRESENT;
+ _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
+ if (!_stats_walk_any_unskipped(dms, flags, cur_r, cur_a))
+ /* no more regions */
+ *flags &= ~DM_STATS_WALK_REGION;
+ if (!(*flags & DM_STATS_WALK_GROUP))
+ *cur_r = dms->max_region;
+ }
+
+ if (*flags & DM_STATS_WALK_REGION)
+ return;
+
+ if (*flags & DM_STATS_WALK_GROUP)
+ _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
+}
+
+static int _stats_walk_end(const struct dm_stats *dms, uint64_t *flags,
+ uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
+{
if (!dms || !dms->regions)
return 1;
- region = &dms->regions[*cur_r];
- end = (*cur_r > dms->max_region
- || (*cur_r == dms->max_region
- && *cur_a >= _nr_areas_region(region)));
+ if (*flags & DM_STATS_WALK_AREA) {
+ _stats_walk_end_areas(dms, flags, cur_r, cur_a, cur_g);
+ goto out;
+ }
+
+ if (*flags & DM_STATS_WALK_REGION) {
+ if (!_stats_walk_any_unskipped(dms, flags, cur_r, cur_a)) {
+ *flags &= ~DM_STATS_WALK_REGION;
+ _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
+ }
+ goto out;
+ }
- return end;
+ if (*flags & DM_STATS_WALK_GROUP) {
+ if (*cur_g < dms->max_region)
+ goto out;
+ *flags &= ~DM_STATS_WALK_GROUP;
+ }
+out:
+ return !(*flags & ~DM_STATS_WALK_SKIP_SINGLE_AREA);
}
int dm_stats_walk_end(struct dm_stats *dms)
{
- return _stats_walk_end(dms, &dms->cur_region, &dms->cur_area);
+ if (_stats_walk_end(dms, &dms->cur_flags,
+ &dms->cur_region, &dms->cur_area,
+ &dms->cur_group)) {
+ dms->cur_flags = dms->walk_flags;
+ return 1;
+ }
+ return 0;
+}
+
+dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
+ uint64_t region_id,
+ uint64_t area_id)
+{
+ uint64_t group_id;
+
+ region_id = (region_id == DM_STATS_REGION_CURRENT)
+ ? dms->cur_region : region_id ;
+ area_id = (area_id == DM_STATS_AREA_CURRENT)
+ ? dms->cur_area : area_id ;
+
+ if (region_id == DM_STATS_REGION_NOT_PRESENT)
+ /* no region */
+ return DM_STATS_OBJECT_TYPE_NONE;
+
+ if (region_id & DM_STATS_WALK_GROUP) {
+ if (region_id == DM_STATS_WALK_GROUP)
+ /* indirect group_id from cursor */
+ group_id = dms->cur_group;
+ else
+ /* immediate group_id encoded in region_id */
+ group_id = region_id & ~DM_STATS_WALK_GROUP;
+ if (!_stats_group_id_present(dms, group_id))
+ return DM_STATS_OBJECT_TYPE_NONE;
+ return DM_STATS_OBJECT_TYPE_GROUP;
+ }
+
+ if (region_id > dms->max_region)
+ /* end of table */
+ return DM_STATS_OBJECT_TYPE_NONE;
+
+ if (area_id & DM_STATS_WALK_REGION)
+ /* aggregate region */
+ return DM_STATS_OBJECT_TYPE_REGION;
+
+ /* plain region_id and area_id */
+ return DM_STATS_OBJECT_TYPE_AREA;
+}
+
+dm_stats_obj_type_t dm_stats_current_object_type(const struct dm_stats *dms)
+{
+ /* dm_stats_object_type will decode region/area */
+ return dm_stats_object_type(dms,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT);
}
uint64_t dm_stats_get_region_nr_areas(const struct dm_stats *dms,
uint64_t region_id)
{
- struct dm_stats_region *region = &dms->regions[region_id];
+ struct dm_stats_region *region = NULL;
+
+ /* groups or aggregate regions cannot be subdivided */
+ if (region_id & DM_STATS_WALK_GROUP)
+ return 1;
+
+ region = &dms->regions[region_id];
return _nr_areas_region(region);
}
uint64_t dm_stats_get_current_nr_areas(const struct dm_stats *dms)
{
+ /* groups or aggregate regions cannot be subdivided */
+ if (dms->cur_region & DM_STATS_WALK_GROUP)
+ return 1;
+
return dm_stats_get_region_nr_areas(dms, dms->cur_region);
}
uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms)
{
- uint64_t nr_areas = 0;
+ uint64_t nr_areas = 0, flags = DM_STATS_WALK_AREA;
/* use a separate cursor */
- uint64_t cur_region, cur_area;
+ uint64_t cur_region = 0, cur_area = 0, cur_group = 0;
+
+ /* no regions to visit? */
+ if (!dms->regions)
+ return 0;
- _stats_walk_start(dms, &cur_region, &cur_area);
+ flags = DM_STATS_WALK_AREA;
+ _stats_walk_start(dms, &flags, &cur_region, &cur_area, &cur_group);
do {
nr_areas += dm_stats_get_current_nr_areas(dms);
- _stats_walk_next(dms, 1, &cur_region, &cur_area);
- } while (!_stats_walk_end(dms, &cur_region, &cur_area));
+ _stats_walk_next(dms, &flags,
+ &cur_region, &cur_area,
+ &cur_group);
+ } while (!_stats_walk_end(dms, &flags,
+ &cur_region, &cur_area,
+ &cur_group));
return nr_areas;
}
region_id = (region_id == DM_STATS_REGION_CURRENT)
? dms->cur_region : region_id ;
+ /* FIXME: support group histograms if all region bounds match */
+ if (region_id & DM_STATS_WALK_GROUP)
+ return 0;
+
if (!dms->regions[region_id].bounds)
- return_0;
+ return 0;
return dms->regions[region_id].bounds->nr_bins;
}
if (!_stats_bound(dms))
return_0;
+ /*
+ * FIXME: 'print' can be emulated for groups or aggregate regions
+ * by populating the handle and emitting aggregate counter data
+ * in the kernel print format.
+ */
+ if (region_id == DM_STATS_WALK_GROUP)
+ return_0;
+
dmt = _stats_print_region(dms, region_id,
start_line, num_lines, clear);
}
/**
- * Test whether region_id is present in this set of stats data
+ * Test whether region_id is present in this set of stats data.
*/
int dm_stats_region_present(const struct dm_stats *dms, uint64_t region_id)
{
{
int all_regions = (region_id == DM_STATS_REGIONS_ALL);
struct dm_task *dmt = NULL; /* @stats_print task */
+ uint64_t saved_flags; /* saved walk flags */
const char *resp;
+ /*
+ * We are about do destroy and re-create the region table, so it
+ * is safe to use the cursor embedded in the stats handle: just
+ * save a copy of the current walk_flags to restore later.
+ */
+ saved_flags = dms->walk_flags;
+
if (!_stats_bound(dms))
return_0;
+ if ((!all_regions) && (region_id & DM_STATS_WALK_GROUP)) {
+ log_error("Invalid region_id for dm_stats_populate: "
+ "DM_STATS_WALK_GROUP");
+ return 0;
+ }
+
/* allow zero-length program_id for populate */
if (!program_id)
program_id = dms->program_id;
if (!dms->nr_regions)
return_0;
+ dms->walk_flags = DM_STATS_WALK_REGION;
dm_stats_walk_start(dms);
do {
region_id = (all_regions)
}
dm_task_destroy(dmt);
- dm_stats_walk_next_region(dms);
+ dm_stats_walk_next(dms);
} while (all_regions && !dm_stats_walk_end(dms));
+ dms->walk_flags = saved_flags;
return 1;
bad:
+ dms->walk_flags = saved_flags;
_stats_regions_destroy(dms);
dms->regions = NULL;
return 0;
dm_free(dms);
}
+/*
+ * Walk each area that is a member of region_id rid.
+ * i is a variable of type int that holds the current area_id.
+ */
+#define _foreach_region_area(dms, rid, i) \
+for ((i) = 0; (i) < _nr_areas_region(&dms->regions[(rid)]); (i)++) \
+
+/*
+ * Walk each region that is a member of group_id gid.
+ * i is a variable of type int that holds the current region_id.
+ */
+#define _foreach_group_region(dms, gid, i) \
+for ((i) = dm_bit_get_first((dms)->groups[(gid)].regions); \
+ (i) != DM_STATS_GROUP_NOT_PRESENT; \
+ (i) = dm_bit_get_next((dms)->groups[(gid)].regions, (i))) \
+
/*
* Walk each region that is a member of group_id gid visiting each
* area within the region.
+ * i is a variable of type int that holds the current region_id.
+ * j is a variable of type int variable that holds the current area_id.
*/
-#define _foreach_group_member(dms, gid, i, j) \
-for ((i) = dm_bit_get_first((dms)->groups[(gid)].regions); \
- (i) != DM_STATS_GROUP_NOT_PRESENT; \
- (i) = dm_bit_get_next((dms)->groups[(gid)].regions, (i))) \
- for ((j) = 0; (j) < _nr_areas_region(&(dms)->regions[(i)]); (j)++) \
+#define _foreach_group_area(dms, gid, i, j) \
+_foreach_group_region(dms, gid, i) \
+ _foreach_region_area(dms, i, j)
static uint64_t _stats_get_counter(const struct dm_stats *dms,
const struct dm_stats_counters *area,
dm_stats_counter_t counter,
uint64_t region_id, uint64_t area_id)
{
+ uint64_t i, j, sum = 0; /* aggregation */
+ int sum_regions = 0;
struct dm_stats_region *region;
struct dm_stats_counters *area;
area_id = (area_id == DM_STATS_REGION_CURRENT)
? dms->cur_area : area_id ;
+ sum_regions = !!(region_id & DM_STATS_WALK_GROUP);
+
+ if (region_id == DM_STATS_WALK_GROUP)
+ /* group walk using the cursor */
+ region_id = dms->cur_group;
+ else if (region_id & DM_STATS_WALK_GROUP)
+ /* group walk using immediate group_id */
+ region_id &= ~DM_STATS_WALK_GROUP;
region = &dms->regions[region_id];
- area = ®ion->counters[area_id];
+ /*
+ * All statistics aggregation takes place here: aggregate metrics
+ * are calculated as normal using the aggregated counter values
+ * returned for the region or group specified.
+ */
- return _stats_get_counter(dms, area, counter);
+ if (_stats_region_is_grouped(dms, region_id) && (sum_regions)) {
+ /* group */
+ if (area_id & DM_STATS_WALK_GROUP)
+ _foreach_group_area(dms, region->group_id, i, j) {
+ area = &dms->regions[i].counters[j];
+ sum += _stats_get_counter(dms, area, counter);
+ }
+ else
+ _foreach_group_region(dms, region->group_id, i) {
+ area = &dms->regions[i].counters[area_id];
+ sum += _stats_get_counter(dms, area, counter);
+ }
+ } else if (area_id == DM_STATS_WALK_REGION) {
+ /* aggregate region */
+ _foreach_region_area(dms, region_id, j) {
+ area = &dms->regions[region_id].counters[j];
+ sum += _stats_get_counter(dms, area, counter);
+ }
+ } else {
+ /* plain region / area */
+ area = ®ion->counters[area_id];
+ sum = _stats_get_counter(dms, area, counter);
+ }
+
+ return sum;
}
-/**
+/*
* Methods for accessing named counter fields. All methods share the
* following naming scheme and prototype:
*
if (!dms->interval_ns)
return_0;
+ /*
+ * Decode DM_STATS_{REGION,AREA}_CURRENT here; counters will then
+ * be returned for the actual current region and area.
+ *
+ * DM_STATS_WALK_GROUP is passed through to the counter methods -
+ * aggregates for the group are returned and used to calculate
+ * the metric for the group totals.
+ */
region_id = (region_id == DM_STATS_REGION_CURRENT)
? dms->cur_region : region_id ;
area_id = (area_id == DM_STATS_REGION_CURRENT)
uint64_t dm_stats_get_current_area(const struct dm_stats *dms)
{
- return dms->cur_area;
+ return dms->cur_area & ~DM_STATS_WALK_ALL;
}
int dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
{
if (!dms || !dms->regions)
return_0;
+
+ /* start is unchanged when aggregating areas */
+ if (region_id & DM_STATS_WALK_REGION)
+ region_id &= ~DM_STATS_WALK_REGION;
+
+ /* use start of first region as group start */
+ if (region_id & DM_STATS_WALK_GROUP) {
+ if (region_id == DM_STATS_WALK_GROUP)
+ region_id = dms->cur_group;
+ else
+ region_id &= ~DM_STATS_WALK_GROUP;
+ }
+
*start = dms->regions[region_id].start;
return 1;
}
int dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
uint64_t region_id)
{
+ uint64_t i;
if (!dms || !dms->regions)
return_0;
- *len = dms->regions[region_id].len;
+
+ *len = 0;
+
+ /* length is unchanged when aggregating areas */
+ if (region_id & DM_STATS_WALK_REGION)
+ region_id &= ~DM_STATS_WALK_REGION;
+
+ if (region_id & DM_STATS_WALK_GROUP) {
+ /* decode region / group ID */
+ if (region_id == DM_STATS_WALK_GROUP)
+ region_id = dms->cur_group;
+ else
+ region_id &= ~DM_STATS_WALK_GROUP;
+
+ /* use sum of region sizes as group size */
+ if (_stats_region_is_grouped(dms, region_id))
+ _foreach_group_region(dms, dms->cur_group, i)
+ *len += dms->regions[i].len;
+ else {
+ log_error("Group ID " FMTu64 " does not exist",
+ region_id);
+ return 0;
+ }
+ } else
+ *len = dms->regions[region_id].len;
+
return 1;
}
{
if (!dms || !dms->regions)
return_0;
+
+ /* groups are not subdivided - area size equals group size */
+ if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
+ /* get_region_len will decode region_id */
+ return dm_stats_get_region_len(dms, len, region_id);
+
*len = dms->regions[region_id].step;
return 1;
}
struct dm_stats_region *region;
if (!dms || !dms->regions)
return_0;
+
+ /* group or region area start equals region start */
+ if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
+ return dm_stats_get_region_start(dms, start, region_id);
+
region = &dms->regions[region_id];
*start = region->start + region->step * area_id;
return 1;
{
if (!dms || !dms->regions)
return_0;
- *offset = dms->regions[region_id].step * area_id;
+
+ /* no areas for groups or aggregate regions */
+ if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
+ *offset = 0;
+ else
+ *offset = dms->regions[region_id].step * area_id;
+
return 1;
}
const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
uint64_t region_id)
{
- const char *program_id = dms->regions[region_id].program_id;
+ const char *program_id = NULL;
+
+ if (region_id & DM_STATS_WALK_GROUP)
+ return dms->program_id;
+
+ if (region_id & DM_STATS_WALK_REGION)
+ region_id &= ~DM_STATS_WALK_REGION;
+
+ program_id = dms->regions[region_id].program_id;
return (program_id) ? program_id : "";
}
const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
uint64_t region_id)
{
- const char *aux_data = dms->regions[region_id].aux_data;
+ const char *aux_data = NULL;
+
+ if (region_id & DM_STATS_WALK_GROUP)
+ return "";
+
+ if (region_id & DM_STATS_WALK_REGION)
+ region_id &= ~DM_STATS_WALK_REGION;
+
+ aux_data = dms->regions[region_id].aux_data;
return (aux_data) ? aux_data : "" ;
}
return 0;
}
+ if (group_id & DM_STATS_WALK_GROUP) {
+ if (group_id == DM_STATS_WALK_GROUP)
+ group_id = dms->cur_group;
+ else
+ group_id &= ~DM_STATS_WALK_GROUP;
+ }
+
if (group_id != dms->regions[group_id].group_id) {
/* dm_stats_set_alias() must be called on the group ID. */
log_error("Cannot set alias for group member " FMTu64 ".",
id = (id == DM_STATS_REGION_CURRENT) ? dms->cur_region : id;
- region = &dms->regions[id];
+ if (id & DM_STATS_WALK_GROUP) {
+ if (id == DM_STATS_WALK_GROUP)
+ id = dms->cur_group;
+ else
+ id &= ~DM_STATS_WALK_GROUP;
+ }
+ region = &dms->regions[id];
if (!_stats_region_is_grouped(dms, id)
|| !dms->groups[region->group_id].alias)
return dms->name;
int dm_stats_get_region_precise_timestamps(const struct dm_stats *dms,
uint64_t region_id)
{
- struct dm_stats_region *region = &dms->regions[region_id];
+ struct dm_stats_region *region;
+
+ if (region_id == DM_STATS_REGION_CURRENT)
+ region_id = dms->cur_region;
+
+ if (region_id == DM_STATS_WALK_GROUP)
+ region_id = dms->cur_group;
+ else if (region_id & DM_STATS_WALK_GROUP)
+ region_id &= ~DM_STATS_WALK_GROUP;
+
+ region = &dms->regions[region_id];
return region->timescale == 1;
}
int dm_stats_get_current_region_precise_timestamps(const struct dm_stats *dms)
{
- return dm_stats_get_region_precise_timestamps(dms, dms->cur_region);
+ return dm_stats_get_region_precise_timestamps(dms,
+ DM_STATS_REGION_CURRENT);
}
/*
area_id = (area_id == DM_STATS_AREA_CURRENT)
? dms->cur_area : area_id ;
+ /* FIXME return histogram sum? Requires bounds check at group time */
+ if (region_id & DM_STATS_WALK_GROUP) {
+ log_warn("Group histogram data is not supported");
+ return NULL;
+ }
+
+ if (region_id & DM_STATS_WALK_REGION)
+ region_id &= ~DM_STATS_WALK_REGION;
+
if (!dms->regions[region_id].counters)
return dms->regions[region_id].bounds;
uint64_t dm_stats_get_group_id(const struct dm_stats *dms, uint64_t region_id)
{
+ region_id = (region_id == DM_STATS_REGION_CURRENT)
+ ? dms->cur_region : region_id;
+
+ if (region_id & DM_STATS_WALK_GROUP) {
+ if (region_id == DM_STATS_WALK_GROUP)
+ return dms->cur_group;
+ else
+ return region_id & ~DM_STATS_WALK_GROUP;
+ }
+
+ if (region_id & DM_STATS_WALK_REGION)
+ region_id &= ~DM_STATS_WALK_REGION;
+
return dms->regions[region_id].group_id;
}