Sindbad~EG File Manager
/* -*- linux-c -*-
* Map Functions
* Copyright (C) 2005-2018 Red Hat Inc.
*
* 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 _MAP_C_
#define _MAP_C_
/** @file map.c
* @brief Implements maps (associative arrays) and lists
*/
#include "stat-common.c"
#include "map-stat.c"
static int int64_eq_p (int64_t key1, int64_t key2)
{
return key1 == key2;
}
static void str_copy(char *dest, char *src)
{
if (src)
strlcpy(dest, src, MAP_STRING_LENGTH);
else
*dest = 0;
}
static void str_add(void *dest, char *val)
{
char *dst = (char *)dest;
strlcat(dst, val, MAP_STRING_LENGTH);
}
static int str_eq_p (char *key1, char *key2)
{
return strncmp(key1, key2, MAP_STRING_LENGTH - 1) == 0;
}
/** @addtogroup maps
* Implements maps (associative arrays) and lists
* @{
*/
/** Get the first element in a map.
* @param map
* @returns a pointer to the first element.
* This is typically used with _stp_map_iter(). See the foreach() macro
* for typical usage. It probably does what you want anyway.
* @sa foreach
*/
static struct map_node *_stp_map_start(MAP map)
{
//dbug ("%lx\n", (long)mlist_next(&map->head));
if (mlist_empty(&map->head))
return NULL;
return mlist_map_node(mlist_next(&map->head));
}
/** Get the next element in a map.
* @param map
* @param m a pointer to the current element, returned from _stp_map_start()
* or _stp_map_iter().
* @returns a pointer to the next element.
* This is typically used with _stp_map_start(). See the foreach() macro
* for typical usage. It probably does what you want anyway.
* @sa foreach
*/
static struct map_node *_stp_map_iter(MAP map, struct map_node *m)
{
if (mlist_next(&m->lnode) == &map->head)
return NULL;
return mlist_map_node(mlist_next(&m->lnode));
}
static struct map_node *_stp_map_iterdel(MAP map, struct map_node *m)
{
struct map_node *r_map;
r_map = _stp_map_iter(map, m);
_new_map_del_node(map, m);
return r_map;
}
/** Clears all the elements in a map.
* @param map
*/
static void _stp_map_clear(MAP map)
{
struct map_node *m;
map->num = 0;
while (!mlist_empty(&map->head)) {
m = mlist_map_node(mlist_next(&map->head));
/* remove node from old hash list */
mhlist_del_init(&m->hnode);
/* remove from entry list */
mlist_del(&m->lnode);
/* add to free pool */
mlist_add(&m->lnode, &map->pool);
}
}
static void _stp_pmap_clear(PMAP pmap)
{
int i;
for_each_possible_cpu(i) {
MAP m = _stp_pmap_get_map (pmap, i);
if (likely(m != NULL))
_stp_map_clear(m);
}
_stp_map_clear(_stp_pmap_get_agg(pmap));
}
/* sort keynum values */
#define SORT_COUNT -5 /* see also translate.cxx:visit_foreach_loop */
#define SORT_SUM -4
#define SORT_MIN -3
#define SORT_MAX -2
#define SORT_AVG -1
/* comparison function for sorts. */
static int _stp_cmp (struct mlist_head *h1, struct mlist_head *h2,
int keynum, int dir, map_get_key_fn get_key)
{
int64_t a = 0, b = 0;
int type = END;
key_data k1 = (*get_key)(mlist_map_node(h1), keynum, &type);
key_data k2 = (*get_key)(mlist_map_node(h2), keynum, NULL);
if (type == INT64) {
a = k1.val;
b = k2.val;
} else if (type == STRING) {
a = strcmp(k1.strp, k2.strp);
b = 0;
} else if (type == STAT) {
stat_data *sd1 = k1.statp;
stat_data *sd2 = k2.statp;
switch (keynum) {
case SORT_COUNT:
a = sd1->count;
b = sd2->count;
break;
case SORT_SUM:
a = sd1->sum;
b = sd2->sum;
break;
case SORT_MIN:
a = sd1->min;
b = sd2->min;
break;
case SORT_MAX:
a = sd1->max;
b = sd2->max;
break;
case SORT_AVG:
a = _stp_div64 (NULL, sd1->sum, sd1->count);
b = _stp_div64 (NULL, sd2->sum, sd2->count);
break;
default:
/* should never happen */
a = b = 0;
}
}
if ((a < b && dir > 0) || (a > b && dir < 0))
return 1;
return 0;
}
/* swap function for bubble sort */
static inline void _stp_swap (struct mlist_head *a, struct mlist_head *b)
{
mlist_del(a);
mlist_add(a, b);
}
/** Sort an entire array.
* Sorts an entire array using merge sort.
*
* @param map Map
* @param keynum 0 for the value, or a positive number for the key number to sort on.
* @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low.
* @sa _stp_map_sortn()
*/
static void _stp_map_sort (MAP map, int keynum, int dir,
map_get_key_fn get_key)
{
struct mlist_head *p, *q, *e, *tail;
int nmerges, psize, qsize, i, insize = 1;
struct mlist_head *head = &map->head;
if (mlist_empty(head))
return;
do {
tail = head;
p = mlist_next(head);
nmerges = 0;
while (p) {
nmerges++;
q = p;
psize = 0;
for (i = 0; i < insize; i++) {
psize++;
q = mlist_next(q) == head ? NULL : mlist_next(q);
if (!q)
break;
}
qsize = insize;
while (psize > 0 || (qsize > 0 && q)) {
if (psize && (!qsize || !q ||
!_stp_cmp(p, q, keynum, dir, get_key))) {
e = p;
p = mlist_next(p) == head ? NULL : mlist_next(p);
psize--;
} else {
e = q;
q = mlist_next(q) == head ? NULL : mlist_next(q);
qsize--;
}
/* now put 'e' on tail of list and make it our new tail */
mlist_del(e);
mlist_add(e, tail);
tail = e;
}
p = q;
}
insize += insize;
} while (nmerges > 1);
}
/** Get the top values from an array.
* Sorts an array such that the start of the array contains the top
* or bottom 'n' values. Use this when sorting the entire array
* would be too time-consuming and you are only interested in the
* highest or lowest values.
*
* @param map Map
* @param n Top (or bottom) number of elements. 0 sorts the entire array.
* @param keynum 0 for the value, or a positive number for the key number to sort on.
* @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low.
* @sa _stp_map_sort()
*/
static void _stp_map_sortn(MAP map, int n, int keynum, int dir,
map_get_key_fn get_key)
{
if (n == 0 || n > 30) {
_stp_map_sort(map, keynum, dir, get_key);
} else {
struct mlist_head *head = &map->head;
struct mlist_head *c, *a = 0, *last, *tmp;
int num, swaps = 1;
if (mlist_empty(head))
return;
/* start off with a modified bubble sort of the first n elements */
while (swaps) {
num = n;
swaps = 0;
a = mlist_next(head);
c = mlist_next(mlist_next(a));
while ((mlist_next(a) != head) && (--num > 0)) {
if (_stp_cmp(a, mlist_next(a), keynum, dir, get_key)) {
swaps++;
_stp_swap(a, mlist_next(a));
}
a = mlist_prev(c);
c = mlist_next(c);
}
}
/* Now use a kind of insertion sort for the rest of the array. */
/* Each element is tested to see if it should be be in the top 'n' */
last = a;
a = mlist_next(a);
while (a != head) {
tmp = mlist_next(a);
c = last;
while (c != head && _stp_cmp(c, a, keynum, dir, get_key))
c = mlist_prev(c);
if (c != last) {
mlist_del(a);
mlist_add(a, c);
last = mlist_prev(last);
}
a = tmp;
}
}
}
static struct map_node *_stp_new_agg(MAP agg, struct mhlist_head *ahead,
struct map_node *ptr, map_update_fn update)
{
struct map_node *aptr;
/* copy keys and aggregate */
aptr = _new_map_create(agg, ahead);
if (aptr == NULL)
return NULL;
(*update)(agg, aptr, ptr, 0);
return aptr;
}
/** Aggregate per-cpu maps.
* This function aggregates the per-cpu maps into an aggregated
* map. A pointer to that aggregated map is returned.
*
* A write lock must be held on the map during this function.
*
* @param map A pointer to a pmap.
* @returns a pointer to the aggregated map. Null on failure.
*/
static MAP _stp_pmap_agg (PMAP pmap, map_update_fn update, map_cmp_fn cmp)
{
int i, hash;
MAP m, agg;
struct map_node *ptr, *aptr = NULL;
struct mhlist_head *head, *ahead;
struct mhlist_node *e, *f;
int quit = 0;
agg = _stp_pmap_get_agg(pmap);
/* FIXME. we either clear the aggregation map or clear each local map */
/* every time we aggregate. which would be best? */
_stp_map_clear (agg);
for_each_possible_cpu(i) {
m = _stp_pmap_get_map (pmap, i);
if (unlikely(m == NULL)) {
/* offline CPU or a newly-added online CPU */
continue;
}
/* walk the hash chains. */
for (hash = 0; hash <= m->hash_table_mask; hash++) {
head = &m->hashes[hash];
ahead = &agg->hashes[hash];
mhlist_for_each_entry(ptr, e, head, hnode) {
int match = 0;
mhlist_for_each_entry(aptr, f, ahead, hnode) {
if ((*cmp)(ptr, aptr)) {
match = 1;
break;
}
}
if (match)
(*update)(agg, aptr, ptr, 1);
else {
if (!_stp_new_agg(agg, ahead, ptr, update)) {
agg = NULL;
goto out;
// NB: break would head out to the for (hash...)
// loop, which behaves badly with an agg==NULL.
}
}
}
}
}
out:
return agg;
}
static struct map_node *_new_map_create (MAP map, struct mhlist_head *head)
{
struct map_node *m;
if (mlist_empty(&map->pool)) {
if (!map->wrap) {
/* ERROR. no space left */
return NULL;
}
m = mlist_map_node(mlist_next(&map->head));
mhlist_del_init(&m->hnode);
} else {
m = mlist_map_node(mlist_next(&map->pool));
map->num++;
}
mlist_move_tail(&m->lnode, &map->head);
/* add node to new hash list */
mhlist_add_head(&m->hnode, head);
return m;
}
static void _new_map_del_node (MAP map, struct map_node *n)
{
/* remove node from old hash list */
mhlist_del_init(&n->hnode);
/* remove from entry list */
mlist_del(&n->lnode);
/* add it back to the pool */
mlist_add(&n->lnode, &map->pool);
map->num--;
}
static int _new_map_set_int64 (MAP map, int64_t *dst, int64_t val, int add)
{
if (add)
*dst += val;
else
*dst = val;
return 0;
}
static int _new_map_set_str (MAP map, char *dst, char *val, int add)
{
if (add)
str_add(dst, val);
else
str_copy(dst, val);
return 0;
}
static int _new_map_set_stat (MAP map, struct stat_data *sd, int64_t val, int add, int s1, int s2, int s3, int s4, int s5)
{
if (!add) {
Hist st = &map->hist;
sd->count = 0;
if (st->type != HIST_NONE) {
int j;
for (j = 0; j < st->buckets; j++)
sd->histogram[j] = 0;
}
}
(&map->hist)->bit_shift = map->bit_shift;
(&map->hist)->stat_ops = map->stat_ops;
__stp_stat_add (&map->hist, sd, val, s1, s2, s3, s4, s5);
return 0;
}
static int _new_map_copy_stat (MAP map, struct stat_data *sd1, struct stat_data *sd2, int add)
{
Hist st = &map->hist;
int64_t S11, S12, S21, S22;
int64_t sd1_count, sd1_avg_s;
S11 = S12 = S21 = S22 = 0;
sd1_count = sd1_avg_s = 0;
if (sd2 == NULL) {
sd1->count = 0;
if (st->type != HIST_NONE) {
int j;
for (j = 0; j < st->buckets; j++)
sd1->histogram[j] = 0;
}
} else if (add && sd1->count > 0 && sd2->count > 0) {
sd1_count = sd1->count;
sd1_avg_s = sd1->avg_s;
sd1->count += sd2->count;
sd1->sum += sd2->sum;
if (sd2->min < sd1->min)
sd1->min = sd2->min;
if (sd2->max > sd1->max)
sd1->max = sd2->max;
if (sd2->stat_ops & STAT_OP_VARIANCE) {
sd1->shift = sd2->shift;
sd1->avg_s = _stp_div64(NULL, sd1->sum << sd2->shift, sd1->count);
/*
* For aggregating variance over available CPUs, the Total Variance
* formula is being used. This formula is mentioned in following
* paper: Niranjan Kamat, Arnab Nandi: A Closer Look at Variance
* Implementations In Modern Database Systems: SIGMOD Record 2015.
* Available at: http://web.cse.ohio-state.edu/~kamatn/variance.pdf
*/
S11 = sd1_count * (sd1_avg_s - sd1->avg_s) * (sd1_avg_s - sd1->avg_s);
S12 = (sd1_count - 1) * sd1->variance_s;
S21 = sd2->count * (sd2->avg_s - sd1->avg_s) * (sd2->avg_s - sd1->avg_s);
S22 = (sd2->count - 1) * sd2->variance_s;
sd1->variance_s = _stp_div64(NULL, (S11 + S12 + S21 + S22), (sd1->count - 1));
sd1->variance = sd1->variance_s >> (2 * sd2->shift);
}
if (st->type != HIST_NONE) {
int j;
for (j = 0; j < st->buckets; j++)
sd1->histogram[j] += sd2->histogram[j];
}
} else {
sd1->count = sd2->count;
sd1->sum = sd2->sum;
sd1->min = sd2->min;
sd1->max = sd2->max;
if (sd2->stat_ops & STAT_OP_VARIANCE) {
sd1->shift = sd2->shift;
sd1->avg_s = sd2->avg_s;
sd1->variance_s = sd2->variance_s;
sd1->variance = sd2->variance_s >> (2 * sd2->shift);
}
if (st->type != HIST_NONE) {
int j;
for (j = 0; j < st->buckets; j++)
sd1->histogram[j] = sd2->histogram[j];
}
}
return 0;
}
/** Return the number of elements in a map
* This function will return the number of active elements
* in a map.
* @param map
* @returns an int
*/
#define _stp_map_size(map) (map->num)
/** Return the number of elements in a pmap
* This function will return the number of active elements
* in all the per-cpu maps in a pmap. This is a quick sum and is
* not the same as the number of unique elements that would
* be in the aggragated map.
* @param pmap
* @returns an int
*/
static int _stp_pmap_size (PMAP pmap)
{
int i, num = 0;
for_each_possible_cpu(i) {
MAP m = _stp_pmap_get_map (pmap, i);
if (unlikely(m == NULL)) {
/* offline CPU or a newly-added online CPU */
continue;
}
num += m->num;
}
return num;
}
#endif /* _MAP_C_ */
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists