procps/proc/slabinfo.c
Jim Warner 6671a3a8b7 library: refactored some header file items and origins
This commit is intended as a refinement of the patches
mentioned below, where origins/sources of newlib items
were added to the header files for user documentation.

However, if those additions are to be truly effective,
along with kernel documentation (where available), the
following prerequisites must also have been satisfied:
. our identifiers closely align with linux field names
. our derived items are documented or self-documenting

Satisfying those prerequisites prompted this patch and
for these changes, kernel sources were emphasized over
available documentation (shame on me, it should always
have been so). And, while some 'new' fields were found
to be conditional, they were included unconditionally.

These changes appear more extensive than they actually
need be since I have attempted to enforce some spacing
conventions. So, I've summarize the significant things
in the sections that follow. For a proper perspective,
use: 'git diff --ignore-space-change' (good as alias).

___________________________________________ <PIDS> api
This api is unique in that there exists many different
file/directory origins subordinate to /proc/<pid>. And
our item identifiers are sometimes coerced so as to be
able to group related or similar enumerators together.

So, users needed more help relating our identifiers to
an actual documented field. Thus, we will now also add
the field names as with 'stat: delayacct_blkio_ticks'.

Each item ending with a '_C' now consistently includes
both the parent's count/time plus waited for children.

That 'RTPRIO' guy was renamed/relocated as PRIORITY_RT
since its original name is an implementation artifact.

___________________________________________ <STAT> api
The only api change was to correct a typo ('dervied').

_________________________________________ <VMSTAT> api
Even ignoring white space, this interface received the
largest number of changes. Mostly, this was because of
deficiencies in the proc(5) documentation. Recall that
this documentation already sorely lacks any substance.
Usually, just kernel releases are noted, not contents.

When compared to kernel source, that proc(5) contained
many non-existent fields and also omitted many others.

________________________________________ <MEMINFO> api
Sadly, with this api many of the changes were simply a
correction of some earlier 'human error' where several
fields where hashed then tracked but never represented
with an item enumerator in this meminfo.h header file.

_______________________________________ <SLABINFO> api
The 'SLABS' (summary) & 'SLABNODE' items were reversed
since the former are derived from the separate caches.

More significantly, those 'SLABNODE' guys were renamed
to 'SLAB' since they concern individual caches and the
concept of 'nodes' is really an implementation detail.

Also, several enumerators were changed to more closely
agree with official slabinfo(5) documentation referred
to in what we're treating as a base document: proc(5).

Lastly, while those 'SLABS' items are solely a product
of our library and not represented in slabinfo(5), the
names attempt to parallel those found as 'SLAB' items.

______________________________________ <DISKSTATS> api
One enumeration identifier was changed so as to better
reflect its relationship to that actual documentation:
'Documentation/iostats.txt', as referenced in proc(5).

Reference(s):
. 12/2018, item origins added (and commit msg history)
commit 96d59cbf46
. 01/2019, <stat> origins tweaked
commit 201e816b26

Signed-off-by: Jim Warner <james.warner@comcast.net>
2019-03-26 19:55:30 +11:00

1076 lines
37 KiB
C

/*
* slabinfo.c - slabinfo related functions for libproc
*
* Chris Rivera <cmrivera@ufl.edu>
* Robert Love <rml@tech9.net>
*
* Copyright (C) 2003 Chris Rivera
* Copyright (C) 2004 Albert Cahalan
* Copyright (C) 2015 Craig Small <csmall@enc.com.au>
* Copyright (C) 2016 Jim Warnerl <james.warner@comcast.net>
*
* 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 <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <proc/procps-private.h>
#include <proc/slabinfo.h>
#define SLABINFO_FILE "/proc/slabinfo"
#define SLABINFO_LINE_LEN 2048
#define SLABINFO_NAME_LEN 128
#define STACKS_INCR 128 // amount reap stack allocations grow
/*
Because 'select' could, at most, return only node[0] values and since 'reap' |
would be forced to duplicate global slabs stuff in every node results stack, |
the following #define can be used to enforce strictly logical return values. |
select: allow only SLABINFO & SLABS items
reap: allow only SLABINFO & SLAB items
Without the #define, these functions always return something even if just 0. |
get: return only SLABS results, else 0
select: return only SLABINFO & SLABS results, else zero
reap: return any requested, even when duplicated in each cache's stack */
//#define ENFORCE_LOGICAL // ensure only logical items accepted by select/reap
struct slabs_summ {
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
unsigned long active_size; // size of all active objects
unsigned long total_size; // size of all objects
};
struct slabs_node {
char name[SLABINFO_NAME_LEN+1]; // 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 slabs_hist {
struct slabs_summ new;
struct slabs_summ old;
};
struct stacks_extent {
int ext_numstacks;
struct stacks_extent *next;
struct slabinfo_stack **stacks;
};
struct ext_support {
int numitems; // includes 'logical_end' delimiter
enum slabinfo_item *items; // includes 'logical_end' delimiter
struct stacks_extent *extents; // anchor for these extents
#ifdef ENFORCE_LOGICAL
enum slabinfo_item lowest; // range of allowable enums
enum slabinfo_item highest;
#endif
int dirty_stacks;
};
struct fetch_support {
struct slabinfo_stack **anchor; // fetch consolidated extents
int n_alloc; // number of above pointers allocated
int n_inuse; // number of above pointers occupied
int n_alloc_save; // last known reap.stacks allocation
struct slabinfo_reap results; // count + stacks for return to caller
};
struct slabinfo_info {
int refcount;
FILE *slabinfo_fp;
int nodes_alloc; // nodes alloc()ed
int nodes_used; // nodes using alloced memory
struct slabs_node *nodes; // first slabnode of this list
struct slabs_hist slabs; // new/old slabs_summ data
struct ext_support select_ext; // supports concurrent select/reap
struct ext_support fetch_ext; // supports concurrent select/reap
struct fetch_support fetch; // support for procps_slabinfo_reap
struct slabs_node nul_node; // used by slabinfo_get/select
struct slabinfo_result get_this; // used by slabinfo_get
};
// ___ Results 'Set' Support ||||||||||||||||||||||||||||||||||||||||||||||||||
#define setNAME(e) set_slabinfo_ ## e
#define setDECL(e) static void setNAME(e) \
(struct slabinfo_result *R, struct slabs_hist *S, struct slabs_node *N)
// regular assignment
#define REG_set(e,t,x) setDECL(e) { (void)N; R->result. t = S->new . x; }
#define NOD_set(e,t,x) setDECL(e) { (void)S; R->result. t = N-> x; }
// delta assignment
#define HST_set(e,t,x) setDECL(e) { (void)N; R->result. t = (signed long)S->new . x - S->old. x; }
setDECL(noop) { (void)R; (void)S; (void)N; }
setDECL(extra) { (void)R; (void)S; (void)N; }
NOD_set(SLAB_NAME, str, name)
NOD_set(SLAB_NUM_OBJS, u_int, nr_objs)
NOD_set(SLAB_ACTIVE_OBJS, u_int, nr_active_objs)
NOD_set(SLAB_OBJ_SIZE, u_int, obj_size)
NOD_set(SLAB_OBJ_PER_SLAB, u_int, objs_per_slab)
NOD_set(SLAB_NUMS_SLABS, u_int, nr_slabs)
NOD_set(SLAB_ACTIVE_SLABS, u_int, nr_active_slabs)
NOD_set(SLAB_PAGES_PER_SLAB, u_int, pages_per_slab)
NOD_set(SLAB_PERCENT_USED, u_int, use)
NOD_set(SLAB_SIZE_TOTAL, ul_int, cache_size)
REG_set(SLABS_CACHES_TOTAL, u_int, nr_caches)
REG_set(SLABS_CACHES_ACTIVE, u_int, nr_active_caches)
REG_set(SLABS_NUM_OBJS, u_int, nr_objs)
REG_set(SLABS_ACTIVE_OBJS, u_int, nr_active_objs)
REG_set(SLABS_OBJ_SIZE_AVG, u_int, avg_obj_size)
REG_set(SLABS_OBJ_SIZE_MIN, u_int, min_obj_size)
REG_set(SLABS_OBJ_SIZE_MAX, u_int, max_obj_size)
REG_set(SLABS_NUMS_SLABS, u_int, nr_slabs)
REG_set(SLABS_ACTIVE_SLABS, u_int, nr_active_slabs)
REG_set(SLABS_PAGES_TOTAL, u_int, nr_pages)
REG_set(SLABS_SIZE_ACTIVE, ul_int, active_size)
REG_set(SLABS_SIZE_TOTAL, ul_int, total_size)
HST_set(SLABS_DELTA_CACHES_TOTAL, s_int, nr_caches)
HST_set(SLABS_DELTA_CACHES_ACTIVE, s_int, nr_active_caches)
HST_set(SLABS_DELTA_NUM_OBJS, s_int, nr_objs)
HST_set(SLABS_DELTA_ACTIVE_OBJS, s_int, nr_active_objs)
HST_set(SLABS_DELTA_OBJ_SIZE_AVG, s_int, avg_obj_size)
HST_set(SLABS_DELTA_OBJ_SIZE_MIN, s_int, min_obj_size)
HST_set(SLABS_DELTA_OBJ_SIZE_MAX, s_int, max_obj_size)
HST_set(SLABS_DELTA_NUMS_SLABS, s_int, nr_slabs)
HST_set(SLABS_DELTA_ACTIVE_SLABS, s_int, nr_active_slabs)
HST_set(SLABS_DELTA_PAGES_TOTAL, s_int, nr_pages)
HST_set(SLABS_DELTA_SIZE_ACTIVE, s_int, active_size)
HST_set(SLABS_DELTA_SIZE_TOTAL, s_int, total_size)
#undef setDECL
#undef REG_set
#undef NOD_set
#undef HST_set
// ___ Sorting Support ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct sort_parms {
int offset;
enum slabinfo_sort_order order;
};
#define srtNAME(t) sort_slabinfo_ ## t
#define srtDECL(t) static int srtNAME(t) \
(const struct slabinfo_stack **A, const struct slabinfo_stack **B, struct sort_parms *P)
srtDECL(u_int) {
const struct slabinfo_result *a = (*A)->head + P->offset; \
const struct slabinfo_result *b = (*B)->head + P->offset; \
if ( a->result.u_int > b->result.u_int ) return P->order > 0 ? 1 : -1; \
if ( a->result.u_int < b->result.u_int ) return P->order > 0 ? -1 : 1; \
return 0;
}
srtDECL(ul_int) {
const struct slabinfo_result *a = (*A)->head + P->offset; \
const struct slabinfo_result *b = (*B)->head + P->offset; \
if ( a->result.ul_int > b->result.ul_int ) return P->order > 0 ? 1 : -1; \
if ( a->result.ul_int < b->result.ul_int ) return P->order > 0 ? -1 : 1; \
return 0;
}
srtDECL(str) {
const struct slabinfo_result *a = (*A)->head + P->offset;
const struct slabinfo_result *b = (*B)->head + P->offset;
return P->order * strcoll(a->result.str, b->result.str);
}
srtDECL(noop) { \
(void)A; (void)B; (void)P; \
return 0;
}
#undef srtDECL
// ___ Controlling Table ||||||||||||||||||||||||||||||||||||||||||||||||||||||
typedef void (*SET_t)(struct slabinfo_result *, struct slabs_hist *, struct slabs_node *);
#define RS(e) (SET_t)setNAME(e)
typedef int (*QSR_t)(const void *, const void *, void *);
#define QS(t) (QSR_t)srtNAME(t)
#define TS(t) STRINGIFY(t)
#define TS_noop ""
/*
* Need it be said?
* This table must be kept in the exact same order as
* those *enum slabinfo_item* guys ! */
static struct {
SET_t setsfunc; // the actual result setting routine
QSR_t sortfunc; // sort cmp func for a specific type
char *type2str; // the result type as a string value
} Item_table[] = {
/* setsfunc sortfunc type2str
------------------------------ ----------- ---------- */
{ RS(noop), QS(noop), TS_noop },
{ RS(extra), QS(ul_int), TS_noop },
{ RS(SLAB_NAME), QS(str), TS(str) },
{ RS(SLAB_NUM_OBJS), QS(u_int), TS(u_int) },
{ RS(SLAB_ACTIVE_OBJS), QS(u_int), TS(u_int) },
{ RS(SLAB_OBJ_SIZE), QS(u_int), TS(u_int) },
{ RS(SLAB_OBJ_PER_SLAB), QS(u_int), TS(u_int) },
{ RS(SLAB_NUMS_SLABS), QS(u_int), TS(u_int) },
{ RS(SLAB_ACTIVE_SLABS), QS(u_int), TS(u_int) },
{ RS(SLAB_PAGES_PER_SLAB), QS(u_int), TS(u_int) },
{ RS(SLAB_PERCENT_USED), QS(u_int), TS(u_int) },
{ RS(SLAB_SIZE_TOTAL), QS(ul_int), TS(ul_int) },
{ RS(SLABS_CACHES_TOTAL), QS(noop), TS(u_int) },
{ RS(SLABS_CACHES_ACTIVE), QS(noop), TS(u_int) },
{ RS(SLABS_NUM_OBJS), QS(noop), TS(u_int) },
{ RS(SLABS_ACTIVE_OBJS), QS(noop), TS(u_int) },
{ RS(SLABS_OBJ_SIZE_AVG), QS(noop), TS(u_int) },
{ RS(SLABS_OBJ_SIZE_MIN), QS(noop), TS(u_int) },
{ RS(SLABS_OBJ_SIZE_MAX), QS(noop), TS(u_int) },
{ RS(SLABS_NUMS_SLABS), QS(noop), TS(u_int) },
{ RS(SLABS_ACTIVE_SLABS), QS(noop), TS(u_int) },
{ RS(SLABS_PAGES_TOTAL), QS(noop), TS(u_int) },
{ RS(SLABS_SIZE_ACTIVE), QS(noop), TS(ul_int) },
{ RS(SLABS_SIZE_TOTAL), QS(noop), TS(ul_int) },
{ RS(SLABS_DELTA_CACHES_TOTAL), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_CACHES_ACTIVE), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_NUM_OBJS), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_ACTIVE_OBJS), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_OBJ_SIZE_AVG), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_OBJ_SIZE_MIN), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_OBJ_SIZE_MAX), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_NUMS_SLABS), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_ACTIVE_SLABS), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_PAGES_TOTAL), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_SIZE_ACTIVE), QS(noop), TS(s_int) },
{ RS(SLABS_DELTA_SIZE_TOTAL), QS(noop), TS(s_int) },
// dummy entry corresponding to SLABINFO_logical_end ...
{ NULL, NULL, NULL }
};
/* please note,
* this enum MUST be 1 greater than the highest value of any enum */
enum slabinfo_item SLABINFO_logical_end = SLABS_DELTA_SIZE_TOTAL + 1;
#undef setNAME
#undef srtNAME
#undef RS
#undef QS
// ___ Private Functions ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// --- slabnode specific support ----------------------------------------------
/* Alloc up more slabnode memory, if required
*/
static int alloc_slabnodes (
struct slabinfo_info *info)
{
struct slabs_node *new_nodes;
int new_count;
if (info->nodes_used < info->nodes_alloc)
return 1;
/* Increment the allocated number of slabs */
new_count = info->nodes_alloc * 5/4+30;
new_nodes = realloc(info->nodes, sizeof(struct slabs_node) * new_count);
if (!new_nodes)
return 0;
info->nodes = new_nodes;
info->nodes_alloc = new_count;
return 1;
} // end: alloc_slabnodes
/*
* 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 slabinfo_info *info,
struct slabs_node **node)
{
if (info->nodes_used == info->nodes_alloc) {
if (!alloc_slabnodes(info))
return 0; // here, errno was set to ENOMEM
}
*node = &(info->nodes[info->nodes_used++]);
return 1;
} // end: get_slabnode
/* parse_slabinfo20:
*
* Actual 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 slabinfo_info *info)
{
struct slabs_node *node;
char buffer[SLABINFO_LINE_LEN];
int page_size = getpagesize();
struct slabs_summ *slabs = &(info->slabs.new);
slabs->min_obj_size = INT_MAX;
slabs->max_obj_size = 0;
while (fgets(buffer, SLABINFO_LINE_LEN, info->slabinfo_fp )) {
if (buffer[0] == '#')
continue;
if (!get_slabnode(info, &node))
return 1; // here, errno was set to ENOMEM
if (sscanf(buffer,
"%" STRINGIFY(SLABINFO_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) {
errno = ERANGE;
return 1;
}
if (!node->name[0])
snprintf(node->name, sizeof(node->name), "%s", "unknown");
if (node->obj_size < slabs->min_obj_size)
slabs->min_obj_size = node->obj_size;
if (node->obj_size > slabs->max_obj_size)
slabs->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 * ((float)node->nr_active_objs / node->nr_objs));
slabs->nr_active_caches++;
} else
node->use = 0;
slabs->nr_objs += node->nr_objs;
slabs->nr_active_objs += node->nr_active_objs;
slabs->total_size += (unsigned long)node->nr_objs * node->obj_size;
slabs->active_size += (unsigned long)node->nr_active_objs * node->obj_size;
slabs->nr_pages += node->nr_slabs * node->pages_per_slab;
slabs->nr_slabs += node->nr_slabs;
slabs->nr_active_slabs += node->nr_active_slabs;
slabs->nr_caches++;
}
if (slabs->nr_objs)
slabs->avg_obj_size = slabs->total_size / slabs->nr_objs;
return 0;
} // end: parse_slabinfo20
/* slabinfo_read_failed():
*
* Read the data out of /proc/slabinfo putting the information
* into the supplied info container
*
* Returns: 0 on success, 1 on error
*/
static int slabinfo_read_failed (
struct slabinfo_info *info)
{
char line[SLABINFO_LINE_LEN];
int major, minor;
memcpy(&info->slabs.old, &info->slabs.new, sizeof(struct slabs_summ));
memset(&(info->slabs.new), 0, sizeof(struct slabs_summ));
if (!alloc_slabnodes(info))
return 1; // here, errno was set to ENOMEM
memset(info->nodes, 0, sizeof(struct slabs_node)*info->nodes_alloc);
info->nodes_used = 0;
if (NULL == info->slabinfo_fp
&& (info->slabinfo_fp = fopen(SLABINFO_FILE, "r")) == NULL)
return 1;
if (fseek(info->slabinfo_fp, 0L, SEEK_SET) < 0)
return 1;
/* Parse the version string */
if (!fgets(line, SLABINFO_LINE_LEN, info->slabinfo_fp))
return 1;
if (2 != sscanf(line, "slabinfo - version: %d.%d", &major, &minor)
|| (major != 2)) {
errno = ERANGE;
return 1;
}
return parse_slabinfo20(info);
} // end: slabinfo_read_failed
// ___ Private Functions ||||||||||||||||||||||||||||||||||||||||||||||||||||||
// --- generalized support ----------------------------------------------------
static inline void slabinfo_assign_results (
struct slabinfo_stack *stack,
struct slabs_hist *summ,
struct slabs_node *node)
{
struct slabinfo_result *this = stack->head;
for (;;) {
enum slabinfo_item item = this->item;
if (item >= SLABINFO_logical_end)
break;
Item_table[item].setsfunc(this, summ, node);
++this;
}
return;
} // end: slabinfo_assign_results
static inline void slabinfo_cleanup_stack (
struct slabinfo_result *this)
{
for (;;) {
if (this->item >= SLABINFO_logical_end)
break;
if (this->item > SLABINFO_noop)
this->result.ul_int = 0;
++this;
}
} // end: slabinfo_cleanup_stack
static inline void slabinfo_cleanup_stacks_all (
struct ext_support *this)
{
struct stacks_extent *ext = this->extents;
int i;
while (ext) {
for (i = 0; ext->stacks[i]; i++)
slabinfo_cleanup_stack(ext->stacks[i]->head);
ext = ext->next;
};
this->dirty_stacks = 0;
} // end: slabinfo_cleanup_stacks_all
static void slabinfo_extents_free_all (
struct ext_support *this)
{
while (this->extents) {
struct stacks_extent *p = this->extents;
this->extents = this->extents->next;
free(p);
};
} // end: slabinfo_extents_free_all
static inline struct slabinfo_result *slabinfo_itemize_stack (
struct slabinfo_result *p,
int depth,
enum slabinfo_item *items)
{
struct slabinfo_result *p_sav = p;
int i;
for (i = 0; i < depth; i++) {
p->item = items[i];
p->result.ul_int = 0;
++p;
}
return p_sav;
} // end: slabinfo_itemize_stack
static void slabinfo_itemize_stacks_all (
struct ext_support *this)
{
struct stacks_extent *ext = this->extents;
while (ext) {
int i;
for (i = 0; ext->stacks[i]; i++)
slabinfo_itemize_stack(ext->stacks[i]->head, this->numitems, this->items);
ext = ext->next;
};
this->dirty_stacks = 0;
} // end: slabinfo_itemize_stacks_all
static inline int slabinfo_items_check_failed (
struct ext_support *this,
enum slabinfo_item *items,
int numitems)
{
int i;
/* if an enum is passed instead of an address of one or more enums, ol' gcc
* will silently convert it to an address (possibly NULL). only clang will
* offer any sort of warning like the following:
*
* warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'enum slabinfo_item *'
* my_stack = procps_slabinfo_select(info, SLABINFO_noop, num);
* ^~~~~~~~~~~~~~~~
*/
if (numitems < 1
|| (void *)items < (void *)(unsigned long)(2 * SLABINFO_logical_end))
return 1;
for (i = 0; i < numitems; i++) {
#ifdef ENFORCE_LOGICAL
if (items[i] == SLABINFO_noop
|| (items[i] == SLABINFO_extra))
continue;
if (items[i] < this->lowest
|| (items[i] > this->highest))
return 1;
#else
// a slabinfo_item is currently unsigned, but we'll protect our future
if (items[i] < 0)
return 1;
if (items[i] >= SLABINFO_logical_end)
return 1;
(void)this;
#endif
}
return 0;
} // end: slabinfo_items_check_failed
/*
* slabinfo_stacks_alloc():
*
* Allocate and initialize one or more stacks each of which is anchored in an
* associated context structure.
*
* All such stacks will have their result structures properly primed with
* 'items', while the result itself will be zeroed.
*
* Returns a stacks_extent struct anchoring the 'heads' of each new stack.
*/
static struct stacks_extent *slabinfo_stacks_alloc (
struct ext_support *this,
int maxstacks)
{
struct stacks_extent *p_blob;
struct slabinfo_stack **p_vect;
struct slabinfo_stack *p_head;
size_t vect_size, head_size, list_size, blob_size;
void *v_head, *v_list;
int i;
vect_size = sizeof(void *) * maxstacks; // size of the addr vectors |
vect_size += sizeof(void *); // plus NULL addr delimiter |
head_size = sizeof(struct slabinfo_stack); // size of that head struct |
list_size = sizeof(struct slabinfo_result)*this->numitems; // any single results stack |
blob_size = sizeof(struct stacks_extent); // the extent anchor itself |
blob_size += vect_size; // plus room for addr vects |
blob_size += head_size * maxstacks; // plus room for head thing |
blob_size += list_size * maxstacks; // plus room for our stacks |
/* note: all of our memory is allocated in one single blob, facilitating a later free(). |
as a minimum, it is important that those result structures themselves always be |
contiguous within each stack since they are accessed through relative position. | */
if (NULL == (p_blob = calloc(1, blob_size)))
return NULL;
p_blob->next = this->extents; // push this extent onto... |
this->extents = p_blob; // ...some existing extents |
p_vect = (void *)p_blob + sizeof(struct stacks_extent); // prime our vector pointer |
p_blob->stacks = p_vect; // set actual vectors start |
v_head = (void *)p_vect + vect_size; // prime head pointer start |
v_list = v_head + (head_size * maxstacks); // prime our stacks pointer |
for (i = 0; i < maxstacks; i++) {
p_head = (struct slabinfo_stack *)v_head;
p_head->head = slabinfo_itemize_stack((struct slabinfo_result *)v_list, this->numitems, this->items);
p_blob->stacks[i] = p_head;
v_list += list_size;
v_head += head_size;
}
p_blob->ext_numstacks = maxstacks;
return p_blob;
} // end: slabinfo_stacks_alloc
static int slabinfo_stacks_fetch (
struct slabinfo_info *info)
{
#define n_alloc info->fetch.n_alloc
#define n_inuse info->fetch.n_inuse
#define n_saved info->fetch.n_alloc_save
struct stacks_extent *ext;
// initialize stuff -----------------------------------
if (!info->fetch.anchor) {
if (!(info->fetch.anchor = calloc(sizeof(void *), STACKS_INCR)))
return -1;
n_alloc = STACKS_INCR;
}
if (!info->fetch_ext.extents) {
if (!(ext = slabinfo_stacks_alloc(&info->fetch_ext, n_alloc)))
return -1; // here, errno was set to ENOMEM
memset(info->fetch.anchor, 0, sizeof(void *) * n_alloc);
memcpy(info->fetch.anchor, ext->stacks, sizeof(void *) * n_alloc);
slabinfo_itemize_stacks_all(&info->fetch_ext);
}
slabinfo_cleanup_stacks_all(&info->fetch_ext);
// iterate stuff --------------------------------------
n_inuse = 0;
while (n_inuse < info->nodes_used) {
if (!(n_inuse < n_alloc)) {
n_alloc += STACKS_INCR;
if ((!(info->fetch.anchor = realloc(info->fetch.anchor, sizeof(void *) * n_alloc)))
|| (!(ext = slabinfo_stacks_alloc(&info->fetch_ext, STACKS_INCR))))
return -1; // here, errno was set to ENOMEM
memcpy(info->fetch.anchor + n_inuse, ext->stacks, sizeof(void *) * STACKS_INCR);
}
slabinfo_assign_results(info->fetch.anchor[n_inuse], &info->slabs, &info->nodes[n_inuse]);
++n_inuse;
}
// finalize stuff -------------------------------------
/* note: we go to this trouble of maintaining a duplicate of the consolidated |
extent stacks addresses represented as our 'anchor' since these ptrs |
are exposed to a user (um, not that we don't trust 'em or anything). |
plus, we can NULL delimit these ptrs which we couldn't do otherwise. | */
if (n_saved < n_inuse + 1) {
n_saved = n_inuse + 1;
if (!(info->fetch.results.stacks = realloc(info->fetch.results.stacks, sizeof(void *) * n_saved)))
return -1;
}
memcpy(info->fetch.results.stacks, info->fetch.anchor, sizeof(void *) * n_inuse);
info->fetch.results.stacks[n_inuse] = NULL;
info->fetch.results.total = n_inuse;
return n_inuse;
#undef n_alloc
#undef n_inuse
#undef n_saved
} // end: slabinfo_stacks_fetch
static int slabinfo_stacks_reconfig_maybe (
struct ext_support *this,
enum slabinfo_item *items,
int numitems)
{
if (slabinfo_items_check_failed(this, items, numitems))
return -1;
/* is this the first time or have things changed since we were last called?
if so, gotta' redo all of our stacks stuff ... */
if (this->numitems != numitems + 1
|| memcmp(this->items, items, sizeof(enum slabinfo_item) * numitems)) {
// allow for our SLABINFO_logical_end
if (!(this->items = realloc(this->items, sizeof(enum slabinfo_item) * (numitems + 1))))
return -1;
memcpy(this->items, items, sizeof(enum slabinfo_item) * numitems);
this->items[numitems] = SLABINFO_logical_end;
this->numitems = numitems + 1;
if (this->extents)
slabinfo_extents_free_all(this);
return 1;
}
return 0;
} // end: slabinfo_stacks_reconfig_maybe
// ___ Public Functions |||||||||||||||||||||||||||||||||||||||||||||||||||||||
// --- standard required functions --------------------------------------------
/*
* procps_slabinfo_new():
*
* @info: location of returned new structure
*
* Returns: < 0 on failure, 0 on success along with
* a pointer to a new context struct
*/
PROCPS_EXPORT int procps_slabinfo_new (
struct slabinfo_info **info)
{
struct slabinfo_info *p;
if (info == NULL || *info != NULL)
return -EINVAL;
if (!(p = calloc(1, sizeof(struct slabinfo_info))))
return -ENOMEM;
#ifdef ENFORCE_LOGICAL
p->select_ext.lowest = SLABS_CACHES_TOTAL;
p->select_ext.highest = SLABS_DELTA_SIZE_TOTAL;
p->fetch_ext.lowest = SLAB_NAME;
p->fetch_ext.highest = SLAB_SIZE_TOTAL;
#endif
p->refcount = 1;
/* do a priming read here for the following potential benefits: |
1) see if that caller's permissions were sufficient (root) |
2) make delta results potentially useful, even if 1st time |
3) elimnate need for history distortions 1st time 'switch' | */
if (slabinfo_read_failed(p)) {
procps_slabinfo_unref(&p);
return -errno;
}
*info = p;
return 0;
} // end: procps_slabinfo_new
PROCPS_EXPORT int procps_slabinfo_ref (
struct slabinfo_info *info)
{
if (info == NULL)
return -EINVAL;
info->refcount++;
return info->refcount;
} // end: procps_slabinfo_ref
PROCPS_EXPORT int procps_slabinfo_unref (
struct slabinfo_info **info)
{
if (info == NULL || *info == NULL)
return -EINVAL;
(*info)->refcount--;
if ((*info)->refcount < 1) {
int errno_sav = errno;
if ((*info)->slabinfo_fp) {
fclose((*info)->slabinfo_fp);
(*info)->slabinfo_fp = NULL;
}
if ((*info)->select_ext.extents)
slabinfo_extents_free_all((&(*info)->select_ext));
if ((*info)->select_ext.items)
free((*info)->select_ext.items);
if ((*info)->fetch.anchor)
free((*info)->fetch.anchor);
if ((*info)->fetch.results.stacks)
free((*info)->fetch.results.stacks);
if ((*info)->fetch_ext.extents)
slabinfo_extents_free_all(&(*info)->fetch_ext);
if ((*info)->fetch_ext.items)
free((*info)->fetch_ext.items);
free((*info)->nodes);
free(*info);
*info = NULL;
errno = errno_sav;
return 0;
}
return (*info)->refcount;
} // end: procps_slabinfo_unref
// --- variable interface functions -------------------------------------------
PROCPS_EXPORT struct slabinfo_result *procps_slabinfo_get (
struct slabinfo_info *info,
enum slabinfo_item item)
{
static time_t sav_secs;
time_t cur_secs;
errno = EINVAL;
if (info == NULL)
return NULL;
if (item < 0 || item >= SLABINFO_logical_end)
return NULL;
errno = 0;
/* we will NOT read the slabinfo file with every call - rather, we'll offer
a granularity of 1 second between reads ... */
cur_secs = time(NULL);
if (1 <= cur_secs - sav_secs) {
if (slabinfo_read_failed(info))
return NULL;
sav_secs = cur_secs;
}
info->get_this.item = item;
// with 'get', we must NOT honor the usual 'noop' guarantee
info->get_this.result.ul_int = 0;
Item_table[item].setsfunc(&info->get_this, &info->slabs, &info->nul_node);
return &info->get_this;
} // end: procps_slabinfo_get
/* procps_slabinfo_reap():
*
* Harvest all the requested SLAB (individual nodes) information
* providing the result stacks along with the total number of nodes.
*
* Returns: pointer to a slabinfo_reap struct on success, NULL on error.
*/
PROCPS_EXPORT struct slabinfo_reap *procps_slabinfo_reap (
struct slabinfo_info *info,
enum slabinfo_item *items,
int numitems)
{
errno = EINVAL;
if (info == NULL || items == NULL)
return NULL;
if (0 > slabinfo_stacks_reconfig_maybe(&info->fetch_ext, items, numitems))
return NULL; // here, errno may be overridden with ENOMEM
errno = 0;
if (info->fetch_ext.dirty_stacks)
slabinfo_cleanup_stacks_all(&info->fetch_ext);
if (slabinfo_read_failed(info))
return NULL;
if (0 > slabinfo_stacks_fetch(info))
return NULL;
info->fetch_ext.dirty_stacks = 1;
return &info->fetch.results;
} // end: procps_slabinfo_reap
/* procps_slabinfo_select():
*
* Obtain all the requested SLABS (global) information then return
* it in a single library provided results stack.
*
* Returns: pointer to a slabinfo_stack struct on success, NULL on error.
*/
PROCPS_EXPORT struct slabinfo_stack *procps_slabinfo_select (
struct slabinfo_info *info,
enum slabinfo_item *items,
int numitems)
{
errno = EINVAL;
if (info == NULL || items == NULL)
return NULL;
if (0 > slabinfo_stacks_reconfig_maybe(&info->select_ext, items, numitems))
return NULL; // here, errno may be overridden with ENOMEM
errno = 0;
if (!info->select_ext.extents
&& (!slabinfo_stacks_alloc(&info->select_ext, 1)))
return NULL;
if (info->select_ext.dirty_stacks)
slabinfo_cleanup_stacks_all(&info->select_ext);
if (slabinfo_read_failed(info))
return NULL;
slabinfo_assign_results(info->select_ext.extents->stacks[0], &info->slabs, &info->nul_node);
info->select_ext.dirty_stacks = 1;
return info->select_ext.extents->stacks[0];
} // end: procps_slabinfo_select
/*
* procps_slabinfo_sort():
*
* Sort stacks anchored in the passed stack pointers array
* based on the designated sort enumerator and specified order.
*
* Returns those same addresses sorted.
*
* Note: all of the stacks must be homogeneous (of equal length and content).
*/
PROCPS_EXPORT struct slabinfo_stack **procps_slabinfo_sort (
struct slabinfo_info *info,
struct slabinfo_stack *stacks[],
int numstacked,
enum slabinfo_item sortitem,
enum slabinfo_sort_order order)
{
struct slabinfo_result *p;
struct sort_parms parms;
int offset;
errno = EINVAL;
if (info == NULL || stacks == NULL)
return NULL;
// a slabinfo_item is currently unsigned, but we'll protect our future
if (sortitem < 0 || sortitem >= SLABINFO_logical_end)
return NULL;
if (order != SLABINFO_SORT_ASCEND && order != SLABINFO_SORT_DESCEND)
return NULL;
if (numstacked < 2)
return stacks;
offset = 0;
p = stacks[0]->head;
for (;;) {
if (p->item == sortitem)
break;
++offset;
if (p->item >= SLABINFO_logical_end)
return NULL;
++p;
}
errno = 0;
parms.offset = offset;
parms.order = order;
qsort_r(stacks, numstacked, sizeof(void *), (QSR_t)Item_table[p->item].sortfunc, &parms);
return stacks;
} // end: procps_slabinfo_sort
// --- special debugging function(s) ------------------------------------------
/*
* The following isn't part of the normal programming interface. Rather,
* it exists to validate result types referenced in application programs.
*
* It's used only when:
* 1) the 'XTRA_PROCPS_DEBUG' has been defined, or
* 2) the '#include <proc/xtra-procps-debug.h>' used
*/
PROCPS_EXPORT struct slabinfo_result *xtra_slabinfo_get (
struct slabinfo_info *info,
enum slabinfo_item actual_enum,
const char *typestr,
const char *file,
int lineno)
{
struct slabinfo_result *r = procps_slabinfo_get(info, actual_enum);
if (actual_enum < 0 || actual_enum >= SLABINFO_logical_end) {
fprintf(stderr, "%s line %d: invalid item = %d, type = %s\n"
, file, lineno, actual_enum, typestr);
}
if (r) {
char *str = Item_table[r->item].type2str;
if (str[0]
&& (strcmp(typestr, str)))
fprintf(stderr, "%s line %d: was %s, expected %s\n", file, lineno, typestr, str);
}
return r;
} // end: xtra_slabinfo_get_
PROCPS_EXPORT struct slabinfo_result *xtra_slabinfo_val (
int relative_enum,
const char *typestr,
const struct slabinfo_stack *stack,
struct slabinfo_info *info,
const char *file,
int lineno)
{
char *str;
int i;
for (i = 0; stack->head[i].item < SLABINFO_logical_end; i++)
;
if (relative_enum < 0 || relative_enum >= i) {
fprintf(stderr, "%s line %d: invalid relative_enum = %d, type = %s\n"
, file, lineno, relative_enum, typestr);
return NULL;
}
str = Item_table[stack->head[relative_enum].item].type2str;
if (str[0]
&& (strcmp(typestr, str))) {
fprintf(stderr, "%s line %d: was %s, expected %s\n", file, lineno, typestr, str);
}
return &stack->head[relative_enum];
(void)info;
} // end: xtra_slabinfo_val