procps/proc/slab.c
Jim Warner 6aa36717c4 library: slab is redesigned to use 'stack' vs. 'chain'
In addition to that text shown below the line which is
common to several commit messages, this patch contains
several minor changes with lessor impact upon the API:

. A 'read' was added to function procps_slabnode_count
(but only when necessary, i.e. info->nodes_used == 0).

. The #include header files are ordered alphabetically
now, with all those <sys/??> types separately grouped.

------------------------------------------------------
. The former 'chains' have now become 'stacks' without
the 'next' pointer in each result struct. The pointers
initially seemed to offer some flexibility with memory
allocations and benefits for the library access logic.
However, user access was always via displacement and a
a statically allocated chain was cumbersome to define.

. An enumerator ending in '_noop' will no longer serve
as a fencepost delimiter. Rather, it has become a much
more important and flexible user oriented tool. Adding
one or more such 'items' in any items list passed into
the library becomes the means of extending the 'stack'
to also include user (not just library) data. Any such
data is guaranteed to never be altered by the library.

. Anticipating PID support, where many different types
must be represented in a result structure, we'll adopt
a common naming standard. And, while not every results
structure currently needs to reflect disparate types a
union will be employed so the same dot qualifier ('.')
can be used consistently when accessing all such data.

Signed-off-by: Jim Warner <james.warner@comcast.net>
2015-07-23 22:31:32 +10:00

870 lines
27 KiB
C

/*
* slab.c - slab related functions for libproc
*
* Chris Rivera <cmrivera@ufl.edu>
* Robert Love <rml@tech9.net>
*
* Copyright (C) 2003 Chris Rivera
* Copyright 2004, Albert Cahalan
* Copyright (C) 2015 Craig Small <csmall@enc.com.au>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <proc/slab.h>
#include "procps-private.h"
#define SLABINFO_FILE "/proc/slabinfo"
#define SLABINFO_LINE_LEN 2048
#define SLAB_INFO_NAME_LEN 128
struct slabinfo_stats {
unsigned long total_size; /* size of all objects */
unsigned long active_size; /* size of all active objects */
unsigned int nr_objs; /* number of objects, among all caches */
unsigned int nr_active_objs; /* number of active objects, among all caches */
unsigned int nr_pages; /* number of pages consumed by all objects */
unsigned int nr_slabs; /* number of slabs, among all caches */
unsigned int nr_active_slabs; /* number of active slabs, among all caches */
unsigned int nr_caches; /* number of caches */
unsigned int nr_active_caches; /* number of active caches */
unsigned int avg_obj_size; /* average object size */
unsigned int min_obj_size; /* size of smallest object */
unsigned int max_obj_size; /* size of largest object */
};
struct slabinfo_node {
char name[SLAB_INFO_NAME_LEN]; /* name of this cache */
unsigned long cache_size; /* size of entire cache */
unsigned int nr_objs; /* number of objects in this cache */
unsigned int nr_active_objs; /* number of active objects */
unsigned int obj_size; /* size of each object */
unsigned int objs_per_slab; /* number of objects per slab */
unsigned int pages_per_slab; /* number of pages per slab */
unsigned int nr_slabs; /* number of slabs in this cache */
unsigned int nr_active_slabs; /* number of active slabs */
unsigned int use; /* percent full: total / active */
};
struct procps_slabinfo {
int refcount;
FILE *slabinfo_fp;
struct slabinfo_stats stats;
struct slabinfo_node *nodes; /* first slabnode of this list */
int nodes_alloc; /* nodes alloc()ed */
int nodes_used; /* nodes using alloced memory */
struct stacks_anchor *stacked;
};
struct stack_vectors {
struct stacks_anchor *owner;
struct slabnode_stack **heads;
};
struct stacks_anchor {
int depth;
int inuse;
int header_size;
struct stack_vectors *vectors;
struct stacks_anchor *self;
struct stacks_anchor *next;
};
/*
* Zero out the slabnode data, keeping the memory allocated.
*/
static void slabnodes_clear (
struct procps_slabinfo *info)
{
if (info == NULL || info->nodes == NULL || info->nodes_alloc < 1)
return;
memset(info->nodes, 0, sizeof(struct slabinfo_node)*info->nodes_alloc);
info->nodes_used = 0;
}
/* Alloc up more slabnode memory, if required
*/
static int slabnodes_alloc (
struct procps_slabinfo *info)
{
struct slabinfo_node *new_nodes;
int new_count;
if (info == NULL)
return -EINVAL;
if (info->nodes_used < info->nodes_alloc)
return 0;
/* Increment the allocated number of slabs */
new_count = info->nodes_alloc * 5/4+30;
new_nodes = realloc(info->nodes, sizeof(struct slabinfo_node) * new_count);
if (!new_nodes)
return -ENOMEM;
info->nodes = new_nodes;
info->nodes_alloc = new_count;
return 0;
}
/*
* get_slabnode - allocate slab_info structures using a free list
*
* In the fast path, we simply return a node off the free list. In the slow
* list, we malloc() a new node. The free list is never automatically reaped,
* both for simplicity and because the number of slab caches is fairly
* constant.
*/
static int get_slabnode (
struct procps_slabinfo *info,
struct slabinfo_node **node)
{
int retval;
if (!info)
return -EINVAL;
if (info->nodes_used == info->nodes_alloc) {
if ((retval = slabnodes_alloc(info)) < 0)
return retval;
}
*node = &(info->nodes[info->nodes_used++]);
return 0;
}
/* parse_slabinfo20:
*
* sactual parse routine for slabinfo 2.x (2.6 kernels)
* Note: difference between 2.0 and 2.1 is in the ": globalstat" part where version 2.1
* has extra column <nodeallocs>. We don't use ": globalstat" part in both versions.
*
* Formats (we don't use "statistics" extensions)
*
* slabinfo - version: 2.1
* # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
* : tunables <batchcount> <limit> <sharedfactor> \
* : slabdata <active_slabs> <num_slabs> <sharedavail>
*
* slabinfo - version: 2.1 (statistics)
* # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
* : tunables <batchcount> <limit> <sharedfactor> \
* : slabdata <active_slabs> <num_slabs> <sharedavail> \
* : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> <nodeallocs> \
* : cpustat <allochit> <allocmiss> <freehit> <freemiss>
*
* slabinfo - version: 2.0
* # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
* : tunables <batchcount> <limit> <sharedfactor> \
* : slabdata <active_slabs> <num_slabs> <sharedavail>
*
* slabinfo - version: 2.0 (statistics)
* # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
* : tunables <batchcount> <limit> <sharedfactor> \
* : slabdata <active_slabs> <num_slabs> <sharedavail> \
* : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> \
* : cpustat <allochit> <allocmiss> <freehit> <freemiss>
*/
static int parse_slabinfo20 (
struct procps_slabinfo *info)
{
struct slabinfo_node *node;
char buffer[SLABINFO_LINE_LEN];
int retval;
int page_size = getpagesize();
struct slabinfo_stats *stats = &(info->stats);
stats->min_obj_size = INT_MAX;
stats->max_obj_size = 0;
while (fgets(buffer, SLABINFO_LINE_LEN, info->slabinfo_fp )) {
if (buffer[0] == '#')
continue;
if ((retval = get_slabnode(info, &node)) < 0)
return retval;
if (sscanf(buffer,
"%" STRINGIFY(SLAB_INFO_NAME_LEN) "s" \
"%u %u %u %u %u : tunables %*u %*u %*u : slabdata %u %u %*u",
node->name,
&node->nr_active_objs, &node->nr_objs,
&node->obj_size, &node->objs_per_slab,
&node->pages_per_slab, &node->nr_active_slabs,
&node->nr_slabs) < 8) {
if (errno != 0)
return -errno;
return -EINVAL;
}
if (!node->name[0])
snprintf(node->name, sizeof(node->name), "%s", "unknown");
if (node->obj_size < stats->min_obj_size)
stats->min_obj_size = node->obj_size;
if (node->obj_size > stats->max_obj_size)
stats->max_obj_size = node->obj_size;
node->cache_size = (unsigned long)node->nr_slabs * node->pages_per_slab
* page_size;
if (node->nr_objs) {
node->use = (unsigned int)100 * node->nr_active_objs / node->nr_objs;
stats->nr_active_caches++;
} else
node->use = 0;
stats->nr_objs += node->nr_objs;
stats->nr_active_objs += node->nr_active_objs;
stats->total_size += (unsigned long)node->nr_objs * node->obj_size;
stats->active_size += (unsigned long)node->nr_active_objs * node->obj_size;
stats->nr_pages += node->nr_slabs * node->pages_per_slab;
stats->nr_slabs += node->nr_slabs;
stats->nr_active_slabs += node->nr_active_slabs;
stats->nr_caches++;
}
if (stats->nr_objs)
stats->avg_obj_size = stats->total_size / stats->nr_objs;
return 0;
}
/*
* procps_slabinfo_new():
*
* @info: location of returned new structure
*
* Returns: 0 on success <0 on failure
*/
PROCPS_EXPORT int procps_slabinfo_new (
struct procps_slabinfo **info)
{
struct procps_slabinfo *si;
if (info == NULL)
return -EINVAL;
si = calloc(1, sizeof(struct procps_slabinfo));
if (!si)
return -ENOMEM;
si->refcount = 1;
si->slabinfo_fp = NULL;
si->nodes_alloc = 0;
si->nodes_used = 0;
si->nodes = NULL;
*info = si;
return 0;
}
/* procps_slabinfo_read():
*
* Read the data out of /proc/slabinfo putting the information
* into the supplied info container
*
* Returns: 0 on success, negative on error
*/
PROCPS_EXPORT int procps_slabinfo_read (
struct procps_slabinfo *info)
{
char line[SLABINFO_LINE_LEN];
int retval, major, minor;
if (info == NULL)
return -1;
memset(&(info->stats), 0, sizeof(struct slabinfo_stats));
if ((retval = slabnodes_alloc(info)) < 0)
return retval;
slabnodes_clear(info);
if (NULL == info->slabinfo_fp &&
(info->slabinfo_fp = fopen(SLABINFO_FILE, "r")) == NULL)
return -errno;
if (fseek(info->slabinfo_fp, 0L, SEEK_SET) < 0)
return -errno;
/* Parse the version string */
if (!fgets(line, SLABINFO_LINE_LEN, info->slabinfo_fp))
return -errno;
if (sscanf(line, "slabinfo - version: %d.%d", &major, &minor) != 2)
return -EINVAL;
if (major == 2)
retval = parse_slabinfo20(info);
else
return -ERANGE;
return retval;
}
PROCPS_EXPORT int procps_slabinfo_ref (
struct procps_slabinfo *info)
{
if (info == NULL)
return -EINVAL;
info->refcount++;
return info->refcount;
}
PROCPS_EXPORT int procps_slabinfo_unref (
struct procps_slabinfo **info)
{
if (info == NULL || *info == NULL)
return -EINVAL;
(*info)->refcount--;
if ((*info)->refcount == 0) {
if ((*info)->slabinfo_fp) {
fclose((*info)->slabinfo_fp);
(*info)->slabinfo_fp = NULL;
}
if ((*info)->stacked) {
do {
struct stacks_anchor *p = (*info)->stacked;
(*info)->stacked = (*info)->stacked->next;
free(p);
} while((*info)->stacked);
}
free((*info)->nodes);
free(*info);
*info = NULL;
return 0;
}
return (*info)->refcount;
}
PROCPS_EXPORT unsigned long procps_slabs_get (
struct procps_slabinfo *info,
enum slabs_item item)
{
/* note: most of the results we might return are actually just
unsigned int, but we must accommodate the largest potential
result and so return an unsigned long */
if (info == NULL)
return -EINVAL;
switch (item) {
case PROCPS_SLABS_OBJS:
return info->stats.nr_objs;
case PROCPS_SLABS_AOBJS:
return info->stats.nr_active_objs;
case PROCPS_SLABS_PAGES:
return info->stats.nr_pages;
case PROCPS_SLABS_SLABS:
return info->stats.nr_slabs;
case PROCPS_SLABS_ASLABS:
return info->stats.nr_active_slabs;
case PROCPS_SLABS_CACHES:
return info->stats.nr_caches;
case PROCPS_SLABS_ACACHES:
return info->stats.nr_active_caches;
case PROCPS_SLABS_SIZE_AVG:
return info->stats.avg_obj_size;
case PROCPS_SLABS_SIZE_MIN:
return info->stats.min_obj_size;
case PROCPS_SLABS_SIZE_MAX:
return info->stats.max_obj_size;
case PROCPS_SLABS_SIZE_TOTAL:
return info->stats.total_size;
case PROCPS_SLABS_SIZE_ACTIVE:
return info->stats.active_size;
default:
return 0;
}
}
PROCPS_EXPORT int procps_slabs_getstack (
struct procps_slabinfo *info,
struct slab_result *these)
{
if (info == NULL || these == NULL)
return -EINVAL;
for (;;) {
switch (these->item) {
case PROCPS_SLABS_OBJS:
these->result.u_int = info->stats.nr_objs;
break;
case PROCPS_SLABS_AOBJS:
these->result.u_int = info->stats.nr_active_objs;
break;
case PROCPS_SLABS_PAGES:
these->result.u_int = info->stats.nr_pages;
break;
case PROCPS_SLABS_SLABS:
these->result.u_int = info->stats.nr_slabs;
break;
case PROCPS_SLABS_ASLABS:
these->result.u_int = info->stats.nr_active_slabs;
break;
case PROCPS_SLABS_CACHES:
these->result.u_int = info->stats.nr_caches;
break;
case PROCPS_SLABS_ACACHES:
these->result.u_int = info->stats.nr_active_caches;
break;
case PROCPS_SLABS_SIZE_AVG:
these->result.u_int = info->stats.avg_obj_size;
break;
case PROCPS_SLABS_SIZE_MIN:
these->result.u_int = info->stats.min_obj_size;
break;
case PROCPS_SLABS_SIZE_MAX:
these->result.u_int = info->stats.max_obj_size;
break;
case PROCPS_SLABS_SIZE_TOTAL:
these->result.ul_int = info->stats.total_size;
break;
case PROCPS_SLABS_SIZE_ACTIVE:
these->result.ul_int = info->stats.active_size;
break;
case PROCPS_SLABS_noop:
// don't disturb potential user data in the result struct
break;
case PROCPS_SLABS_stack_end:
return 0;
default:
return -EINVAL;
}
++these;
}
}
/*
* procps_slabnode_getname():
*
* @info: slabinfo structure with data read in
* @nodeid: number of node we want the name for
*
* Find the name of the given node
*
* Returns: name or NULL on error
*/
PROCPS_EXPORT const char *procps_slabnode_getname (
struct procps_slabinfo *info,
int nodeid)
{
if (info == NULL)
return NULL;
if (nodeid > info->nodes_used)
return NULL;
return info->nodes[nodeid].name;
}
PROCPS_EXPORT unsigned long procps_slabnode_get (
struct procps_slabinfo *info,
enum slabnode_item item,
int nodeid)
{
/* note: most of the results we might return are actually just
unsigned int, but we must accommodate the largest potential
result and so return an unsigned long */
if (info == NULL)
return -EINVAL;
switch (item) {
case PROCPS_SLABNODE_SIZE:
return info->nodes[nodeid].cache_size;
case PROCPS_SLABNODE_OBJS:
return info->nodes[nodeid].nr_objs;
case PROCPS_SLABNODE_AOBJS:
return info->nodes[nodeid].nr_active_objs;
case PROCPS_SLABNODE_OBJ_SIZE:
return info->nodes[nodeid].obj_size;
case PROCPS_SLABNODE_OBJS_PER_SLAB:
return info->nodes[nodeid].objs_per_slab;
case PROCPS_SLABNODE_PAGES_PER_SLAB:
return info->nodes[nodeid].pages_per_slab;
case PROCPS_SLABNODE_SLABS:
return info->nodes[nodeid].nr_slabs;
case PROCPS_SLABNODE_ASLABS:
return info->nodes[nodeid].nr_active_slabs;
case PROCPS_SLABNODE_USE:
return info->nodes[nodeid].use;
default:
return 0;
}
}
PROCPS_EXPORT int procps_slabnode_getstack (
struct procps_slabinfo *info,
struct slab_result *these,
int nodeid)
{
if (info == NULL || these == NULL)
return -EINVAL;
if (nodeid > info->nodes_used)
return -EINVAL;
for (;;) {
switch (these->item) {
case PROCPS_SLABNODE_SIZE:
these->result.ul_int = info->nodes[nodeid].cache_size;
break;
case PROCPS_SLABNODE_OBJS:
these->result.u_int = info->nodes[nodeid].nr_objs;
break;
case PROCPS_SLABNODE_AOBJS:
these->result.u_int = info->nodes[nodeid].nr_active_objs;
break;
case PROCPS_SLABNODE_OBJ_SIZE:
these->result.u_int = info->nodes[nodeid].obj_size;
break;
case PROCPS_SLABNODE_OBJS_PER_SLAB:
these->result.u_int = info->nodes[nodeid].objs_per_slab;
break;
case PROCPS_SLABNODE_PAGES_PER_SLAB:
these->result.u_int = info->nodes[nodeid].pages_per_slab;
break;
case PROCPS_SLABNODE_SLABS:
these->result.u_int = info->nodes[nodeid].nr_slabs;
break;
case PROCPS_SLABNODE_ASLABS:
these->result.u_int = info->nodes[nodeid].nr_active_slabs;
break;
case PROCPS_SLABNODE_USE:
these->result.u_int = info->nodes[nodeid].use;
break;
case PROCPS_SLABNODE_NAME:
these->result.str = info->nodes[nodeid].name;
break;
case PROCPS_SLABNODE_noop:
// don't disturb potential user data in the result struct
break;
case PROCPS_SLABNODE_stack_end:
return 0;
default:
return -EINVAL;
}
++these;
}
}
PROCPS_EXPORT int procps_slabnode_stack_fill (
struct procps_slabinfo *info,
struct slabnode_stack *stack,
int nodeid)
{
int rc;
if (info == NULL || stack == NULL || stack->head == NULL)
return -EINVAL;
if ((rc = procps_slabinfo_read(info)) < 0)
return rc;
return procps_slabnode_getstack(info, stack->head, nodeid);
}
/*
* procps_slabnode_count():
*
* @info: read in slabinfo structure
*
* Returns: number of nodes in @info or <0 on error
*/
PROCPS_EXPORT int procps_slabnode_count (
struct procps_slabinfo *info)
{
int rc = 0;
if (!info)
return -EINVAL;
if (!info->nodes_used)
rc = procps_slabinfo_read(info);
if (rc < 0)
return rc;
return info->nodes_used;
}
PROCPS_EXPORT int procps_slabnode_stacks_fill (
struct procps_slabinfo *info,
struct slabnode_stack **stacks,
int maxstacks)
{
int i, rc;
if (info == NULL || *stacks == NULL)
return -EINVAL;
if (maxstacks < 1)
return -EINVAL;
if ((rc = procps_slabinfo_read(info)) < 0)
return rc;
if (maxstacks > info->stacked->depth)
maxstacks = info->stacked->depth;
if (maxstacks > info->nodes_used)
maxstacks = info->nodes_used;
for (i = 0; i < maxstacks; i++) {
if (stacks[i] == NULL)
break;
if ((rc = procps_slabnode_getstack(info, stacks[i]->head, i) < 0))
return rc;
}
info->stacked->inuse = i;
return info->stacked->inuse;
}
static void stacks_validate (struct slabnode_stack **v, const char *who)
{
#if 0
#include <stdio.h>
int i, t, x, n = 0;
struct stack_vectors *p = (struct stack_vectors *)v - 1;
fprintf(stderr, "%s: called by '%s'\n", __func__, who);
fprintf(stderr, "%s: owned by %p (whose self = %p)\n", __func__, p->owner, p->owner->self);
for (x = 0; v[x]; x++) {
struct slabnode_stack *h = v[x];
struct slab_result *r = h->head;
fprintf(stderr, "%s: vector[%02d] = %p", __func__, x, h);
for (i = 0; r->item < PROCPS_SLABNODE_stack_end; i++, r++)
;
t = i + 1;
fprintf(stderr, ", stack %d found %d elements\n", n, i);
++n;
}
fprintf(stderr, "%s: found %d stack(s), each %d bytes (including eos)\n", __func__, x, (int)sizeof(struct slab_result) * t);
fprintf(stderr, "%s: found %d stack(s)\n", __func__, x);
fprintf(stderr, "%s: this header size = %2d\n", __func__, (int)p->owner->header_size);
fprintf(stderr, "%s: sizeof(struct slabnode_stack) = %2d\n", __func__, (int)sizeof(struct slabnode_stack));
fprintf(stderr, "%s: sizeof(struct slab_result) = %2d\n", __func__, (int)sizeof(struct slab_result));
fputc('\n', stderr);
return;
#endif
}
static struct slab_result *stack_make (
struct slab_result *p,
int maxitems,
enum slabnode_item *items)
{
struct slab_result *p_sav = p;
int i;
for (i = 0; i < maxitems; i++) {
p->item = items[i];
// note: we rely on calloc to initialize actual result
++p;
}
return p_sav;
}
static int stack_items_valid (
int maxitems,
enum slabnode_item *items)
{
int i;
for (i = 0; i < maxitems; i++) {
if (items[i] < PROCPS_SLABNODE_SIZE)
return 0;
if (items[i] > PROCPS_SLABNODE_stack_end)
return 0;
}
if (items[maxitems -1] != PROCPS_SLABNODE_stack_end)
return 0;
return 1;
}
/*
* procps_slabnode_stacks_alloc():
*
* Allocate and initialize one or more stacks each of which is anchored in an
* associated slabnode_stack structure (which may include extra user space).
*
* All such stacks will will have their result structures properly primed with
* 'items', while the result itself will be zeroed.
*
* Returns an array of pointers representing the 'heads' of each new stack.
*/
PROCPS_EXPORT struct slabnode_stack **procps_slabnode_stacks_alloc (
struct procps_slabinfo *info,
int maxstacks,
int stack_extra,
int maxitems,
enum slabnode_item *items)
{
struct stacks_anchor *p_blob;
struct stack_vectors *p_vect;
struct slabnode_stack *p_head;
size_t vect_size, head_size, list_size, blob_size;
void *v_head, *v_list;
int i;
if (info == NULL || items == NULL)
return NULL;
if (maxstacks < 1 || maxitems < 1)
return NULL;
if (!stack_items_valid(maxitems, items))
return NULL;
vect_size = sizeof(struct stack_vectors); // address vector struct
vect_size += sizeof(void *) * maxstacks; // plus vectors themselves
vect_size += sizeof(void *); // plus NULL delimiter
head_size = sizeof(struct slabnode_stack) + stack_extra; // a head struct + user stuff
list_size = sizeof(struct slab_result) * maxitems; // a results stack
blob_size = sizeof(struct stacks_anchor); // the anchor itself
blob_size += vect_size; // all vectors + delims
blob_size += head_size * maxstacks; // all head structs + user stuff
blob_size += list_size * maxstacks; // all results stacks
/* note: all memory is allocated in a single blob, facilitating a later free().
as a minimum, it's important that the result structures themselves always be
contiguous for any given stack (just as they are when defined statically). */
if (NULL == (p_blob = calloc(1, blob_size)))
return NULL;
p_blob->next = info->stacked;
info->stacked = p_blob;
p_blob->self = p_blob;
p_blob->header_size = head_size;
p_blob->vectors = (void *)p_blob + sizeof(struct stacks_anchor);
p_vect = p_blob->vectors;
p_vect->owner = p_blob->self;
p_vect->heads = (void *)p_vect + sizeof(struct stack_vectors);
v_head = (void *)p_vect + vect_size;
v_list = v_head + (head_size * maxstacks);
for (i = 0; i < maxstacks; i++) {
p_head = (struct slabnode_stack *)v_head;
p_head->head = stack_make((struct slab_result *)v_list, maxitems, items);
p_blob->vectors->heads[i] = p_head;
v_list += list_size;
v_head += head_size;
}
p_blob->depth = maxstacks;
stacks_validate(p_blob->vectors->heads, __func__);
return p_blob->vectors->heads;
}
/*
* procps_slabnode_stack_alloc():
*
* Allocate and initialize a single result stack under a simplified interface.
*
* Such a stack will will have its result structures properly primed with
* 'items', while the result itself will be zeroed.
*
*/
PROCPS_EXPORT struct slabnode_stack *procps_slabnode_stack_alloc (
struct procps_slabinfo *info,
int maxitems,
enum slabnode_item *items)
{
struct slabnode_stack **v;
if (info == NULL || items == NULL || maxitems < 1)
return NULL;
v = procps_slabnode_stacks_alloc(info, 1, 0, maxitems, items);
if (!v)
return NULL;
stacks_validate(v, __func__);
return v[0];
}
static int stacks_sort (
const struct slabnode_stack **A,
const struct slabnode_stack **B,
enum slabnode_item *offset)
{
const struct slab_result *a = (*A)->head + *offset;
const struct slab_result *b = (*B)->head + *offset;
// note: everything will be sorted high-to-low
switch (a->item) {
case PROCPS_SLABNODE_noop:
case PROCPS_SLABNODE_stack_end:
break;
case PROCPS_SLABNODE_NAME:
return strcoll(a->result.str, b->result.str);
case PROCPS_SLABNODE_SIZE:
if ( a->result.ul_int > b->result.ul_int ) return -1;
if ( a->result.ul_int < b->result.ul_int ) return +1;
break;
default:
if ( a->result.u_int > b->result.u_int ) return -1;
if ( a->result.u_int < b->result.u_int ) return +1;
break;
}
return 0;
}
/*
* procps_slabnode_stacks_sort():
*
* Sort stacks anchored as 'heads' in the passed slabnode_stack pointers
* array based on the designated sort enumerator.
*
* Returns those same addresses sorted.
*
* Note: all of the stacks must be homogeneous (of equal length and content).
*/
PROCPS_EXPORT struct slabnode_stack **procps_slabnode_stacks_sort (
struct procps_slabinfo *info,
struct slabnode_stack **stacks,
int numstacked,
enum slabnode_item sort)
{
#define QSORT_r int (*)(const void *, const void *, void *)
struct slab_result *p = stacks[0]->head;
int offset = 0;;
if (info == NULL || stacks == NULL)
return NULL;
if (sort < 0 || sort > PROCPS_SLABNODE_noop)
return NULL;
if (numstacked > info->stacked->depth)
return NULL;
if (numstacked < 2)
return stacks;
if (numstacked > info->stacked->inuse)
numstacked = info->stacked->inuse;
for (;;) {
if (p->item == sort)
break;
++offset;
if (p->item == PROCPS_SLABNODE_stack_end)
return NULL;
++p;
}
qsort_r(stacks, numstacked, sizeof(void *), (QSORT_r)stacks_sort, &offset);
return stacks;
#undef QSORT_r
}