From abc71a46ada71b790eb526d6cddb91e0d0aed4cb Mon Sep 17 00:00:00 2001 From: Jim Warner Date: Fri, 6 May 2016 00:00:00 -0500 Subject: [PATCH] library: improve/standardize one interface, api This commit represents a complete redesign of the stat interface. Gone are the confusing 8 separate accessors along with their 2 additional read functions. In their place we have just 3 accessors, with no read required. That old interface also suffered an inflexibility with respect to structures. Now we deal with an unchanging standard 'result' struct enabling future changes where the binary interface will no longer need to be broken. And gone is that former unnecessary typedef, used when dealing with jiffies. Now the standard C type is used. Our new API also adds some brand new functionality. If a caller plans to employ successive 'select' or 'reap' invocations, then delta values are available (which is actually only what that top program is interested in). At some future point a 'sort' function could be easily introduced to complement the 'reap' function. However, I saw no need for it at present and so it was omitted. There were several design decisions which everyone may not agree with. In support I'll offer these rationals: . The 'get' function returns a signed long long result which means a potential loss of some significance. But I felt the ability to distinguish actual errors (minus values) from true zero results were worth such a risk. . The DELTA item enumerators were also made signed and smaller than their parents. And they are intentionally grouped as last so as to emphasize those distinctions. . The SYS type items were excluded from the new 'reap' function. It would not make sense to duplicate them in each results stack. They're limited to 'get'/'select'. . By the same token, some items (DELTA, etc.) will not be allowed under that 'get' routine. That function was already open to significant internal overhead (through subsequent calls like in vmstat.c). That is why it has been limited via 1 second between reads of /proc/stat. Lastly, when we finally get around to documenting this interface there's a real potential toe stubber when it comes to the numa node portion. The libnuma.so doesn't really provide any means to retrieve the active nodes. Thus, any total reported by is just the highest node number plus one, as reported by the numa library. Any unused/inactive nodes are identified through these . PROCPS_STAT_TIC_ID shows as PROCPS_STAT_NODE_INVALID By the same token after the STAT_REAP_CPUS_ONLY 'reap' . PROCPS_STAT_TIC_NUMA_NODE = PROCPS_STAT_NODE_INVALID Reference(s): http://www.freelists.org/post/procps/newlib-stat-interface Signed-off-by: Jim Warner --- proc/libprocps.sym | 13 +- proc/stat.c | 1244 +++++++++++++++++++++++++++++--------------- proc/stat.h | 151 +++--- 3 files changed, 916 insertions(+), 492 deletions(-) diff --git a/proc/libprocps.sym b/proc/libprocps.sym index b6537eb7..bdbcf647 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -53,18 +53,11 @@ global: procps_slabnode_stacks_sort; procps_slabnode_stacks_alloc; procps_stat_new; - procps_stat_read; - procps_stat_read_jiffs; procps_stat_ref; procps_stat_unref; - procps_stat_cpu_get; - procps_stat_cpu_getstack; - procps_stat_jiffs_get; - procps_stat_jiffs_hist_get; - procps_stat_jiffs_fill; - procps_stat_jiffs_hist_fill; - procps_stat_sys_get; - procps_stat_sys_getstack; + procps_stat_get; + procps_stat_reap; + procps_stat_select; procps_uptime; procps_uptime_sprint; procps_uptime_sprint_short; diff --git a/proc/stat.c b/proc/stat.c index 07cf142b..4aa1a330 100644 --- a/proc/stat.c +++ b/proc/stat.c @@ -16,363 +16,437 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include #include #include +#include #include #include #include #include +#include #include "procps-private.h" + +/* ------------------------------------------------------------------------- + strictly development define(s), largely for the top program + ( next has no affect if ./configure --disable-numa has been specified ) */ +//#define PRETEND_NUMA // pretend there are 3 'discontiguous' nodes +// ------------------------------------------------------------------------- + #define STAT_FILE "/proc/stat" +#define STACKS_INCR 64 // amount reap stack allocations grow +#define NEWOLD_INCR 32 // amount jiffs hist allocations grow + + +struct stat_jifs { + unsigned long long user, nice, system, idle, iowait, irq, sirq, stolen, guest, gnice; +}; + struct stat_data { - struct procps_jiffs cpu; - unsigned int intr; - unsigned int ctxt; - unsigned int btime; - unsigned int procs; - unsigned int procs_blocked; - unsigned int procs_running; + unsigned long intr; + unsigned long ctxt; + unsigned long btime; + unsigned long procs_created; + unsigned long procs_blocked; + unsigned long procs_running; +}; + +struct hist_sys { + struct stat_data new; + struct stat_data old; }; -struct procps_jiffs_private { - struct procps_jiffs_hist cpu; - // future additions go here, please +struct hist_tic { + int id; + int numa_node; + struct stat_jifs new; + struct stat_jifs old; }; -struct procps_stat { +struct stacks_extent { + struct stat_stack **stacks; + int ext_numitems; // includes 'logical_end' delimiter + int ext_numstacks; + struct stacks_extent *next; +}; + +struct fetch_support { + int numitems; // includes 'logical_end' delimiter + enum stat_item *items; // includes 'logical_end' delimiter + struct stacks_extent *extents; // anchor for these extents + int dirty_stacks; +}; + +struct tic_support { + int n_alloc; // number of below structs allocated + int n_inuse; // number of below structs occupied + struct hist_tic *tics; // actual new/old jiffies +}; + +struct reap_support { + int total; // independently obtained # of cpus/nodes + struct fetch_support fetch; // extents plus items details + struct tic_support hist; // cpu and node jiffies management + int n_anchor_alloc; // last known anchor pointers allocation + struct stat_stack **anchor; // reapable stacks (consolidated extents) + struct stat_reap result; // summary + stacks returned to caller +}; + +struct procps_statinfo { int refcount; int stat_fd; - struct stat_data data; - int jiff_hists_alloc; - int jiff_hists_inuse; - struct procps_jiffs_private cpu_summary; - struct procps_jiffs_private *jiff_hists; + int stat_was_read; // is stat file history valid? + struct hist_sys sys_hist; // SYS type management + struct hist_tic cpu_hist; // TIC type management for cpu summary + struct reap_support cpus; // TIC type management for real cpus + struct reap_support nodes; // TIC type management for numa nodes + struct fetch_support cpu_summary; // supports /proc/stat line #1 results + struct fetch_support select; // support for 'procps_stat_select()' + struct stat_reaped results; // for return to caller after a reap +#ifndef NUMA_DISABLE + void *libnuma_handle; // if dlopen() for libnuma succeessful + int (*our_max_node)(void); // a libnuma function call via dlsym() + int (*our_node_of_cpu)(int); // a libnuma function call via dlsym() +#endif }; -/* - * procps_stat_new: - * - * Create a new container to hold the stat information - * - * The initial refcount is 1, and needs to be decremented - * to release the resources of the structure. - * - * Returns: a new stat info container - */ -PROCPS_EXPORT int procps_stat_new ( - struct procps_stat **info) -{ - struct procps_stat *v; - v = calloc(1, sizeof(struct procps_stat)); - if (!v) - return -ENOMEM; +// ___ Results 'Set' Support |||||||||||||||||||||||||||||||||||||||||||||||||| + +#define setNAME(e) set_results_ ## e +#define setDECL(e) static void setNAME(e) \ + (struct stat_result *R, struct hist_sys *S, struct hist_tic *T) + +// regular assignment +#define TIC_set(e,t,x) setDECL(e) { \ + (void)S; R->result. t = T->new . x; } +#define SYS_set(e,t,x) setDECL(e) { \ + (void)T; R->result. t = S->new . x; } +// delta assignment +#define TIC_hst(e,t,x) setDECL(e) { \ + (void)S; R->result. t = ( T->new . x - T->old. x ); \ + if (R->result. t < 0) R->result. t = 0; } +#define SYS_hst(e,t,x) setDECL(e) { \ + (void)T; R->result. t = ( S->new . x - S->old. x ); \ + if (R->result. t < 0) R->result. t = 0; } + +setDECL(noop) { (void)R; (void)S; (void)T; } +setDECL(extra) { (void)R; (void)S; (void)T; } + +setDECL(TIC_ID) { (void)S; R->result.s_int = T->id; } +setDECL(TIC_NUMA_NODE) { (void)S; R->result.s_int = T->numa_node; } +TIC_set(TIC_USER, ull_int, user) +TIC_set(TIC_NICE, ull_int, nice) +TIC_set(TIC_SYSTEM, ull_int, system) +TIC_set(TIC_IDLE, ull_int, idle) +TIC_set(TIC_IOWAIT, ull_int, iowait) +TIC_set(TIC_IRQ, ull_int, irq) +TIC_set(TIC_SOFTIRQ, ull_int, sirq) +TIC_set(TIC_STOLEN, ull_int, stolen) +TIC_set(TIC_GUEST, ull_int, guest) +TIC_set(TIC_GUEST_NICE, ull_int, gnice) +TIC_hst(TIC_DELTA_USER, sl_int, user) +TIC_hst(TIC_DELTA_NICE, sl_int, nice) +TIC_hst(TIC_DELTA_SYSTEM, sl_int, system) +TIC_hst(TIC_DELTA_IDLE, sl_int, idle) +TIC_hst(TIC_DELTA_IOWAIT, sl_int, iowait) +TIC_hst(TIC_DELTA_IRQ, sl_int, irq) +TIC_hst(TIC_DELTA_SOFTIRQ, sl_int, sirq) +TIC_hst(TIC_DELTA_STOLEN, sl_int, stolen) +TIC_hst(TIC_DELTA_GUEST, sl_int, guest) +TIC_hst(TIC_DELTA_GUEST_NICE, sl_int, gnice) + +SYS_set(SYS_CTX_SWITCHES, ul_int, ctxt) +SYS_set(SYS_INTERRUPTS, ul_int, intr) +SYS_set(SYS_PROC_BLOCKED, ul_int, procs_blocked) +SYS_set(SYS_PROC_CREATED, ul_int, procs_created) +SYS_set(SYS_PROC_RUNNING, ul_int, procs_running) +SYS_set(SYS_TIME_OF_BOOT, ul_int, btime) +SYS_hst(SYS_DELTA_CTX_SWITCHES, s_int, ctxt) +SYS_hst(SYS_DELTA_INTERRUPTS, s_int, intr) +SYS_hst(SYS_DELTA_PROC_BLOCKED, s_int, procs_blocked) +SYS_hst(SYS_DELTA_PROC_CREATED, s_int, procs_created) +SYS_hst(SYS_DELTA_PROC_RUNNING, s_int, procs_running) + + +typedef void (*SET_t)(struct stat_result *, struct hist_sys *, struct hist_tic *); +#define RS(e) (SET_t)setNAME(e) + + +// ___ Controlling Table |||||||||||||||||||||||||||||||||||||||||||||||||||||| + + /* + * Need it be said? + * This table must be kept in the exact same order as + * those 'enum stat_item' guys ! */ +static struct { + SET_t setsfunc; // the actual result setting routine +} Item_table[] = { +/* setsfunc + + --------------------------- */ + { RS(noop), }, + { RS(extra), }, + + { RS(TIC_ID), }, + { RS(TIC_NUMA_NODE), }, + { RS(TIC_USER), }, + { RS(TIC_NICE), }, + { RS(TIC_SYSTEM), }, + { RS(TIC_IDLE), }, + { RS(TIC_IOWAIT), }, + { RS(TIC_IRQ), }, + { RS(TIC_SOFTIRQ), }, + { RS(TIC_STOLEN), }, + { RS(TIC_GUEST), }, + { RS(TIC_GUEST_NICE), }, + { RS(TIC_DELTA_USER), }, + { RS(TIC_DELTA_NICE), }, + { RS(TIC_DELTA_SYSTEM), }, + { RS(TIC_DELTA_IDLE), }, + { RS(TIC_DELTA_IOWAIT), }, + { RS(TIC_DELTA_IRQ), }, + { RS(TIC_DELTA_SOFTIRQ), }, + { RS(TIC_DELTA_STOLEN), }, + { RS(TIC_DELTA_GUEST), }, + { RS(TIC_DELTA_GUEST_NICE), }, + + { RS(SYS_CTX_SWITCHES), }, + { RS(SYS_INTERRUPTS), }, + { RS(SYS_PROC_BLOCKED), }, + { RS(SYS_PROC_CREATED), }, + { RS(SYS_PROC_RUNNING), }, + { RS(SYS_TIME_OF_BOOT), }, + { RS(SYS_DELTA_CTX_SWITCHES), }, + { RS(SYS_DELTA_INTERRUPTS), }, + { RS(SYS_DELTA_PROC_BLOCKED), }, + { RS(SYS_DELTA_PROC_CREATED), }, + { RS(SYS_DELTA_PROC_RUNNING), }, + + { NULL, } +}; - v->refcount = 1; - v->stat_fd = -1; -/* v->jiff_hists_alloc = 0; unnecessary with calloc, */ -/* v->jiff_hists_inuse = 0; but serves as a reminder */ - *info = v; - return 0; -} + /* please note, + * 1st enum MUST be kept in sync with highest TIC type + * 2nd enum MUST be 1 greater than the highest value of any enum */ +enum stat_item PROCPS_STAT_TIC_highest = PROCPS_STAT_TIC_DELTA_GUEST_NICE; +enum stat_item PROCPS_STAT_logical_end = PROCPS_STAT_SYS_DELTA_PROC_RUNNING + 1; -/* - * procps_stat_read: - * - * Read the data out of /proc/stat putting the information - * into the supplied info structure. - * - * If CPU stats only needed, set cpu_only to non-zero - */ -PROCPS_EXPORT int procps_stat_read ( - struct procps_stat *info, - const int cpu_only) -{ - char buf[8192]; - char *head, *tail; - int size; - if (info == NULL) - return -EINVAL; +// ___ Private Functions |||||||||||||||||||||||||||||||||||||||||||||||||||||| - memset(&(info->data), 0, sizeof(struct stat_data)); - /* read in the data */ +#ifndef NUMA_DISABLE + #ifdef PRETEND_NUMA +static int fake_max_node (void) { return 3; } +static int fake_node_of_cpu (int n) { return (1 == (n % 4)) ? 0 : (n % 4); } + #endif +#endif + + +static inline void assign_results ( + struct stat_stack *stack, + struct hist_sys *sys_hist, + struct hist_tic *tic_hist) +{ + struct stat_result *this = stack->head; - if (-1 == info->stat_fd && (info->stat_fd = open(STAT_FILE, O_RDONLY)) == -1) { - return -errno; - } - if (lseek(info->stat_fd, 0L, SEEK_SET) == -1) { - return -errno; - } for (;;) { - if ((size = read(info->stat_fd, buf, sizeof(buf)-1)) < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return -errno; - } - break; + enum stat_item item = this->item; + if (item >= PROCPS_STAT_logical_end) + break; + Item_table[item].setsfunc(this, sys_hist, tic_hist); + ++this; } - if (size == 0) - return 0; - buf[size] = '\0'; + return; +} // end: assign_results - /* Scan the file */ - head = buf; - do { - tail = strchr(head, ' '); - if (!tail) - break; - *tail = '\0'; - if (0 == strcmp(head, "cpu")) { - if (sscanf(tail+1, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", - &(info->data.cpu.user), - &(info->data.cpu.nice), - &(info->data.cpu.system), - &(info->data.cpu.idle), - &(info->data.cpu.iowait), - &(info->data.cpu.irq), - &(info->data.cpu.sirq), - &(info->data.cpu.stolen), - &(info->data.cpu.guest), - &(info->data.cpu.nice) - ) != 10) - return -1; - - if (cpu_only) - return 0; // we got what we need - } else if (0 == strcmp(head, "intr")) { - info->data.intr = strtoul(tail+1, &tail, 10); - } else if (0 == strcmp(head, "ctxt")) { - info->data.ctxt = strtoul(tail+1, &tail, 10); - } else if (0 == strcmp(head, "btime")) { - info->data.btime = strtoul(tail+1, &tail, 10); - } else if (0 == strcmp(head, "processes")) { - info->data.procs = strtoul(tail+1, &tail, 10); - } else if (0 == strcmp(head, "procs_blocked")) { - info->data.procs_blocked = strtoul(tail+1, &tail, 10); - } else if (0 == strcmp(head, "procs_running")) { - info->data.procs_running = strtoul(tail+1, &tail, 10); - } - if (tail[0] != '\n') - tail = strchr(tail+1, '\n'); - if (!tail) + +static inline void cleanup_stack ( + struct stat_result *p, + int depth) +{ + int i; + + for (i = 0; i < depth; i++) { + if (p->item >= PROCPS_STAT_logical_end) break; - head = tail + 1; - } while (tail); - if (info->data.procs) - info->data.procs--; // exclude this process - return 0; -} + if (p->item > PROCPS_STAT_noop) + p->result.ull_int = 0; + ++p; + } +} // end: cleanup_stack -PROCPS_EXPORT int procps_stat_ref ( - struct procps_stat *info) + +static inline void cleanup_stacks_all ( + struct fetch_support *this) { - if (info == NULL) - return -EINVAL; - info->refcount++; - return info->refcount; -} + struct stacks_extent *ext = this->extents; + int i; -PROCPS_EXPORT int procps_stat_unref ( - struct procps_stat **info) + while (ext) { + for (i = 0; ext->stacks[i]; i++) + cleanup_stack(ext->stacks[i]->head, this->numitems); + ext = ext->next; + }; + this->dirty_stacks = 0; +} // end: cleanup_stacks_all + + +static void extents_free_all ( + struct fetch_support *this) { - if (info == NULL || *info == NULL) - return -EINVAL; - (*info)->refcount--; - if ((*info)->refcount == 0) { - if ((*info)->jiff_hists != NULL) - free((*info)->jiff_hists); - free(*info); - *info = NULL; - return 0; - } - return (*info)->refcount; -} + do { + struct stacks_extent *p = this->extents; + this->extents = this->extents->next; + free(p); + } while (this->extents); +} // end: extents_free_all -PROCPS_EXPORT jiff procps_stat_cpu_get ( - struct procps_stat *info, - enum procps_cpu_item item) + +static inline struct stat_result *itemize_stack ( + struct stat_result *p, + int depth, + enum stat_item *items) { - switch (item) { - case PROCPS_CPU_USER: - return info->data.cpu.user; - case PROCPS_CPU_NICE: - return info->data.cpu.nice; - case PROCPS_CPU_SYSTEM: - return info->data.cpu.system; - case PROCPS_CPU_IDLE: - return info->data.cpu.idle; - case PROCPS_CPU_IOWAIT: - return info->data.cpu.iowait; - case PROCPS_CPU_IRQ: - return info->data.cpu.irq; - case PROCPS_CPU_SIRQ: - return info->data.cpu.sirq; - case PROCPS_CPU_STOLEN: - return info->data.cpu.stolen; - case PROCPS_CPU_GUEST: - return info->data.cpu.guest; - case PROCPS_CPU_GNICE: - return info->data.cpu.gnice; - default: - return 0; + struct stat_result *p_sav = p; + int i; + + for (i = 0; i < depth; i++) { + p->item = items[i]; + p->result.ull_int = 0; + ++p; } -} + return p_sav; +} // end: itemize_stack + -PROCPS_EXPORT int procps_stat_cpu_getstack ( - struct procps_stat *info, - struct stat_result *these) +static inline int items_check_failed ( + int numitems, + enum stat_item *items) { - if (these == NULL) - return -EINVAL; + int i; - for (;;) { - switch (these->item) { - case PROCPS_CPU_USER: - these->result.jiff = info->data.cpu.user; - break; - case PROCPS_CPU_NICE: - these->result.jiff = info->data.cpu.nice; - break; - case PROCPS_CPU_SYSTEM: - these->result.jiff = info->data.cpu.system; - break; - case PROCPS_CPU_IDLE: - these->result.jiff = info->data.cpu.idle; - break; - case PROCPS_CPU_IOWAIT: - these->result.jiff = info->data.cpu.iowait; - break; - case PROCPS_CPU_IRQ: - these->result.jiff = info->data.cpu.irq; - break; - case PROCPS_CPU_SIRQ: - these->result.jiff = info->data.cpu.sirq; - break; - case PROCPS_CPU_STOLEN: - these->result.jiff = info->data.cpu.stolen; - break; - case PROCPS_CPU_GUEST: - these->result.jiff = info->data.cpu.guest; - break; - case PROCPS_CPU_GNICE: - these->result.jiff = info->data.cpu.gnice; - break; - case PROCPS_CPU_noop: - // don't disturb potential user data in the result struct - break; - case PROCPS_CPU_stack_end: - return 0; - default: - return -EINVAL; + /* 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 stat_item *' + * my_stack = procps_stat_select(info, PROCPS_STAT_noop, num); + * ^~~~~~~~~~~~~~~~ + */ + if (numitems < 1 + || (void *)items < (void *)(unsigned long)(2 * PROCPS_STAT_logical_end)) + return -1; + + for (i = 0; i < numitems; i++) { + // a stat_item is currently unsigned, but we'll protect our future + if (items[i] < 0) + return -1; + if (items[i] >= PROCPS_STAT_logical_end) { + return -1; } - ++these; } -} + return 0; +} // end: items_check_failed -PROCPS_EXPORT unsigned int procps_stat_sys_get ( - struct procps_stat *info, - enum procps_stat_item item) + +static int make_numa_hist ( + struct procps_statinfo *info) { - switch (item) { - case PROCPS_STAT_INTR: - return info->data.intr; - case PROCPS_STAT_CTXT: - return info->data.ctxt; - case PROCPS_STAT_BTIME: - return info->data.btime; - case PROCPS_STAT_PROCS: - return info->data.procs; - case PROCPS_STAT_PROCS_BLK: - return info->data.procs_blocked; - case PROCPS_STAT_PROCS_RUN: - return info->data.procs_running; - default: - return 0; +#ifndef NUMA_DISABLE + struct hist_tic *cpu_ptr, *nod_ptr; + int i, node; + + if (info->libnuma_handle == NULL + || (!info->nodes.total)) { + return 0; } -} -PROCPS_EXPORT int procps_stat_sys_getstack ( - struct procps_stat *info, - struct stat_result *these) -{ - if (these == NULL) - return -EINVAL; + /* are numa nodes dynamic like online cpus can be? + ( and be careful, this libnuma call returns the highest node id in use, ) + ( NOT an actual number of nodes - some of those 'slots' might be unused ) */ + info->nodes.total = info->our_max_node() + 1; + if (!info->nodes.hist.n_alloc + || !(info->nodes.total < info->nodes.hist.n_alloc)) { + info->nodes.hist.n_alloc = info->nodes.total + NEWOLD_INCR; + info->nodes.hist.tics = realloc(info->nodes.hist.tics, info->nodes.hist.n_alloc * sizeof(struct hist_tic)); + if (!(info->nodes.hist.tics)) + return -ENOMEM; + } - for (;;) { - switch (these->item) { - case PROCPS_STAT_INTR: - these->result.u_int = info->data.intr; - break; - case PROCPS_STAT_CTXT: - these->result.u_int = info->data.ctxt; - break; - case PROCPS_STAT_BTIME: - these->result.u_int = info->data.btime; - break; - case PROCPS_STAT_PROCS: - these->result.u_int = info->data.procs; - break; - case PROCPS_STAT_PROCS_BLK: - these->result.u_int = info->data.procs_blocked; - break; - case PROCPS_STAT_PROCS_RUN: - these->result.u_int = info->data.procs_running; - break; - case PROCPS_STAT_noop: - // don't disturb potential user data in the result struct - break; - case PROCPS_STAT_stack_end: - return 0; - default: - return -EINVAL; + // forget all of the prior node statistics & anticipate unassigned slots + memset(info->nodes.hist.tics, 0, info->nodes.hist.n_alloc * sizeof(struct hist_tic)); + nod_ptr = info->nodes.hist.tics; + for (i = 0; i < info->cpus.hist.n_alloc; i++) { + nod_ptr->id = nod_ptr->numa_node = PROCPS_STAT_NODE_INVALID; + ++nod_ptr; + } + + // spin thru each cpu and value the jiffs for it's numa node + for (i = 0; i < info->cpus.hist.n_inuse; i++) { + cpu_ptr = info->cpus.hist.tics + i; + if (-1 < (node = info->our_node_of_cpu(cpu_ptr->id))) { + nod_ptr = info->nodes.hist.tics + node; + nod_ptr->new.user += cpu_ptr->new.user; nod_ptr->old.user += cpu_ptr->old.user; + nod_ptr->new.nice += cpu_ptr->new.nice; nod_ptr->old.nice += cpu_ptr->old.nice; + nod_ptr->new.system += cpu_ptr->new.system; nod_ptr->old.system += cpu_ptr->old.system; + nod_ptr->new.idle += cpu_ptr->new.idle; nod_ptr->old.idle += cpu_ptr->old.idle; + nod_ptr->new.iowait += cpu_ptr->new.iowait; nod_ptr->old.iowait += cpu_ptr->old.iowait; + nod_ptr->new.irq += cpu_ptr->new.irq; nod_ptr->old.irq += cpu_ptr->old.irq; + nod_ptr->new.sirq += cpu_ptr->new.sirq; nod_ptr->old.sirq += cpu_ptr->old.sirq; + nod_ptr->new.stolen += cpu_ptr->new.stolen; nod_ptr->old.stolen += cpu_ptr->old.stolen; + /* + * note: the above call to 'our_node_of_cpu' will produce a modest + * memory leak summarized as: + * ==1234== LEAK SUMMARY: + * ==1234== definitely lost: 512 bytes in 1 blocks + * ==1234== indirectly lost: 48 bytes in 2 blocks + * ==1234== ... + * [ thanks very much libnuma, for all the pain you've caused us ] + */ + cpu_ptr->numa_node = node; + nod_ptr->id = node; } - ++these; } -} + info->nodes.hist.n_inuse = info->nodes.total; + return info->nodes.hist.n_inuse; +#else + return 0; +#endif +} // end: make_numa_hist -/* - * procps_stat_read_jiffs: - * - * Read the cpu data out of /proc/stat putting the information - * into the dynamically acquired 'jiff_hists' hanging off the - * info structure. Along the way we gather historical stats and - * and embed the cpu summary line in the info structure as well - * ( since we had to read that first /proc/stat line anyway ). - * - * This is all private information but can be copied to caller - * supplied structures upon request. - */ -PROCPS_EXPORT int procps_stat_read_jiffs ( - struct procps_stat *info) + +static int read_stat_failed ( + struct procps_statinfo *info) { - #define ALLOCincr 32 - struct procps_jiffs_private *sum_ptr, *cpu_ptr; - char buf[8192], *bp; + struct hist_tic *sum_ptr, *cpu_ptr; + char buf[8192], *bp, *b; int i, rc, size; + unsigned long long llnum = 0; if (info == NULL) return -EINVAL; - if (!info->jiff_hists_alloc) { - info->jiff_hists = calloc(ALLOCincr, sizeof(struct procps_jiffs_private)); - if (!(info->jiff_hists)) + if (!info->cpus.hist.n_alloc) { + info->cpus.hist.tics = calloc(NEWOLD_INCR, sizeof(struct hist_tic)); + if (!(info->cpus.hist.tics)) return -ENOMEM; - info->jiff_hists_alloc = ALLOCincr; - info->jiff_hists_inuse = 0; + info->cpus.hist.n_alloc = NEWOLD_INCR; + info->cpus.hist.n_inuse = 0; } if (-1 == info->stat_fd && (info->stat_fd = open(STAT_FILE, O_RDONLY)) == -1) return -errno; if (lseek(info->stat_fd, 0L, SEEK_SET) == -1) return -errno; + for (;;) { if ((size = read(info->stat_fd, buf, sizeof(buf)-1)) < 0) { if (errno == EINTR || errno == EAGAIN) @@ -381,183 +455,531 @@ PROCPS_EXPORT int procps_stat_read_jiffs ( } break; } - if (size == 0) - return 0; + if (size == 0) { + return -EIO; + } buf[size] = '\0'; bp = buf; - sum_ptr = &info->cpu_summary; - // remember from last time around - memcpy(&sum_ptr->cpu.old, &sum_ptr->cpu.new, sizeof(struct procps_jiffs)); - // then value the summary line + sum_ptr = &info->cpu_hist; + // remember summary from last time around + memcpy(&sum_ptr->old, &sum_ptr->new, sizeof(struct stat_jifs)); + + sum_ptr->id = PROCPS_STAT_SUMMARY_ID; // mark as summary + sum_ptr->numa_node = PROCPS_STAT_NODE_INVALID; // mark as invalid + + // now value the cpu summary tics from line #1 if (8 > sscanf(bp, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu" - , &sum_ptr->cpu.new.user, &sum_ptr->cpu.new.nice, &sum_ptr->cpu.new.system - , &sum_ptr->cpu.new.idle, &sum_ptr->cpu.new.iowait, &sum_ptr->cpu.new.irq - , &sum_ptr->cpu.new.sirq, &sum_ptr->cpu.new.stolen - , &sum_ptr->cpu.new.guest, &sum_ptr->cpu.new.gnice)) + , &sum_ptr->new.user, &sum_ptr->new.nice, &sum_ptr->new.system + , &sum_ptr->new.idle, &sum_ptr->new.iowait, &sum_ptr->new.irq + , &sum_ptr->new.sirq, &sum_ptr->new.stolen + , &sum_ptr->new.guest, &sum_ptr->new.gnice)) return -1; - sum_ptr->cpu.id = -1; // mark as summary + // let's not distort the deltas the first time thru ... + if (!info->stat_was_read) + memcpy(&sum_ptr->old, &sum_ptr->new, sizeof(struct stat_jifs)); i = 0; reap_em_again: - cpu_ptr = info->jiff_hists + i; // adapt to relocated if reap_em_again + cpu_ptr = info->cpus.hist.tics + i; // adapt to relocated if reap_em_again + do { bp = 1 + strchr(bp, '\n'); - // remember from last time around - memcpy(&cpu_ptr->cpu.old, &cpu_ptr->cpu.new, sizeof(struct procps_jiffs)); + // remember this cpu from last time around + memcpy(&cpu_ptr->old, &cpu_ptr->new, sizeof(struct stat_jifs)); + // next can be overridden under 'make_numa_hist' + cpu_ptr->numa_node = PROCPS_STAT_NODE_INVALID; + if (8 > (rc = sscanf(bp, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu" - , &cpu_ptr->cpu.id - , &cpu_ptr->cpu.new.user, &cpu_ptr->cpu.new.nice, &cpu_ptr->cpu.new.system - , &cpu_ptr->cpu.new.idle, &cpu_ptr->cpu.new.iowait, &cpu_ptr->cpu.new.irq - , &cpu_ptr->cpu.new.sirq, &cpu_ptr->cpu.new.stolen - , &cpu_ptr->cpu.new.guest, &cpu_ptr->cpu.new.gnice))) { - memmove(cpu_ptr, sum_ptr, sizeof(struct procps_jiffs_hist)); - break; // we must tolerate cpus taken offline + , &cpu_ptr->id + , &cpu_ptr->new.user, &cpu_ptr->new.nice, &cpu_ptr->new.system + , &cpu_ptr->new.idle, &cpu_ptr->new.iowait, &cpu_ptr->new.irq + , &cpu_ptr->new.sirq, &cpu_ptr->new.stolen + , &cpu_ptr->new.guest, &cpu_ptr->new.gnice))) { + int id_sav = cpu_ptr->id; + memmove(cpu_ptr, sum_ptr, sizeof(struct hist_tic)); + cpu_ptr->id = id_sav; + break; // we must tolerate cpus taken offline } + // let's not distort the deltas the first time thru ... + if (!info->stat_was_read) + memcpy(&cpu_ptr->old, &cpu_ptr->new, sizeof(struct stat_jifs)); ++i; ++cpu_ptr; - } while (i < info->jiff_hists_alloc); + } while (i < info->cpus.hist.n_alloc); - if (i == info->jiff_hists_alloc && rc >= 8) { - info->jiff_hists_alloc += ALLOCincr; - info->jiff_hists = realloc(info->jiff_hists, info->jiff_hists_alloc * sizeof(struct procps_jiffs_private)); - if (!(info->jiff_hists)) + if (i == info->cpus.hist.n_alloc && rc >= 8) { + info->cpus.hist.n_alloc += NEWOLD_INCR; + info->cpus.hist.tics = realloc(info->cpus.hist.tics, info->cpus.hist.n_alloc * sizeof(struct hist_tic)); + if (!(info->cpus.hist.tics)) return -ENOMEM; goto reap_em_again; } - info->jiff_hists_inuse = i; - return i; - #undef ALLOCincr -} + info->cpus.total = info->cpus.hist.n_inuse = i; + + // remember sys_hist stuff from last time around + memcpy(&info->sys_hist.old, &info->sys_hist.new, sizeof(struct stat_data)); + + llnum = 0; + b = strstr(bp, "intr "); + if(b) sscanf(b, "intr %llu", &llnum); + info->sys_hist.new.intr = llnum; + + llnum = 0; + b = strstr(bp, "ctxt "); + if(b) sscanf(b, "ctxt %llu", &llnum); + info->sys_hist.new.ctxt = llnum; + + llnum = 0; + b = strstr(bp, "btime "); + if(b) sscanf(b, "btime %llu", &llnum); + info->sys_hist.new.btime = llnum; + + llnum = 0; + b = strstr(bp, "processes "); + if(b) sscanf(b, "processes %llu", &llnum); + info->sys_hist.new.procs_created = llnum; + + llnum = 0; + b = strstr(bp, "procs_blocked "); + if(b) sscanf(b, "procs_blocked %llu", &llnum); + info->sys_hist.new.procs_blocked = llnum; + + llnum = 0; + b = strstr(bp, "procs_rnning "); + if(b) sscanf(b, "procs_rnning %llu", &llnum); + info->sys_hist.new.procs_running = llnum; + + // let's not distort the deltas the first time thru ... + if (!info->stat_was_read) + memcpy(&info->sys_hist.old, &info->sys_hist.new, sizeof(struct stat_data)); + + info->stat_was_read = 1; + return 0; +} // end: read_stat_failed + /* - * procps_stat_jiffs_get: + * stacks_alloc(): * - * Return the designated cpu data in the caller supplied structure. - * A negative 'which' denotes the cpu_summary, not a real cpu. + * Allocate and initialize one or more stacks each of which is anchored in an + * associated stat_stack structure. * - * This function deals only with the 'current' jiffs counts. + * All such stacks will have their result structures properly primed with + * 'items', while the result itself will be zeroed. + * + * Returns a stack_extent struct anchoring the 'heads' of each new stack. */ -PROCPS_EXPORT int procps_stat_jiffs_get ( - struct procps_stat *info, - struct procps_jiffs *dest, - int which) +static struct stacks_extent *stacks_alloc ( + struct fetch_support *this, + int maxstacks) +{ + struct stacks_extent *p_blob; + struct stat_stack **p_vect; + struct stat_stack *p_head; + size_t vect_size, head_size, list_size, blob_size; + void *v_head, *v_list; + int i; + + if (this == NULL || this->items == NULL) + return NULL; + if (maxstacks < 1) + return NULL; + + vect_size = sizeof(void *) * maxstacks; // address vectors themselves + vect_size += sizeof(void *); // plus NULL delimiter + head_size = sizeof(struct stat_stack); // a head struct + list_size = sizeof(struct stat_result) * this->numitems; // a results stack + blob_size = sizeof(struct stacks_extent); // the extent anchor itself + blob_size += vect_size; // all vectors + delim + blob_size += head_size * maxstacks; // all head structs + 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 each stack since they're accessed through relative position). */ + if (NULL == (p_blob = calloc(1, blob_size))) + return NULL; + + p_blob->next = this->extents; + this->extents = p_blob; + p_blob->stacks = (void *)p_blob + sizeof(struct stacks_extent); + p_vect = p_blob->stacks; + v_head = (void *)p_vect + vect_size; + v_list = v_head + (head_size * maxstacks); + + for (i = 0; i < maxstacks; i++) { + p_head = (struct stat_stack *)v_head; + p_head->head = itemize_stack((struct stat_result *)v_list, this->numitems, this->items); + p_blob->stacks[i] = p_head; + v_list += list_size; + v_head += head_size; + } + p_blob->ext_numitems = this->numitems; + p_blob->ext_numstacks = maxstacks; + return p_blob; +} // end: stacks_alloc + + +static int stacks_fetch_tics ( + struct procps_statinfo *info, + struct reap_support *this) { - struct procps_jiffs_private *p; + struct stacks_extent *ext; int i; - if (info == NULL || dest == NULL) + if (this == NULL) return -EINVAL; - if (which < 0) { - // note, we're just copying the 'new' portion of our procps_jiffs_private - memcpy(dest, &info->cpu_summary, sizeof(struct procps_jiffs)); - return 0; + + // initialize stuff ----------------------------------- + if (!this->anchor) { + if (!(this->anchor = calloc(sizeof(void *), STACKS_INCR))) + return -ENOMEM; + this->n_anchor_alloc = STACKS_INCR; } - p = info->jiff_hists; - for (i = 0; i < info->jiff_hists_inuse; i++) { - if (p->cpu.id == which) { - // note, we're just copying the 'new' portion of our procps_jiffs_private - memcpy(dest, p, sizeof(struct procps_jiffs)); - return 0; + if (!this->fetch.extents) { + if (!(ext = stacks_alloc(&this->fetch, this->n_anchor_alloc))) + return -ENOMEM; + memcpy(this->anchor, ext->stacks, sizeof(void *) * this->n_anchor_alloc); + } + if (this->fetch.dirty_stacks) + cleanup_stacks_all(&this->fetch); + + // iterate stuff -------------------------------------- + for (i = 0; i < this->hist.n_inuse; i++) { + if (!(i < this->n_anchor_alloc)) { + this->n_anchor_alloc += STACKS_INCR; + if ((!(this->anchor = realloc(this->anchor, sizeof(void *) * this->n_anchor_alloc))) + || (!(ext = stacks_alloc(&this->fetch, STACKS_INCR)))) { + return -ENOMEM; + } + memcpy(this->anchor + i, ext->stacks, sizeof(void *) * STACKS_INCR); } - ++p; + assign_results(this->anchor[i], &info->sys_hist, &this->hist.tics[i]); } - return -1; -} + + // finalize stuff ------------------------------------- + this->result.total = i; + this->result.stacks = this->anchor; + this->fetch.dirty_stacks = 1; + + return this->result.total; +} // end: stacks_fetch_tics + + +static int stacks_reconfig_maybe ( + struct fetch_support *this, + enum stat_item *items, + int numitems) +{ + if (items_check_failed(numitems, items)) + return -EINVAL; + + /* 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 stat_item) * numitems)) { + // allow for our PROCPS_STAT_logical_end + if (!(this->items = realloc(this->items, sizeof(enum stat_item) * (numitems + 1)))) + return -ENOMEM; + memcpy(this->items, items, sizeof(enum stat_item) * numitems); + this->items[numitems] = PROCPS_STAT_logical_end; + this->numitems = numitems + 1; + if (this->extents) + extents_free_all(this); + return 1; + } + return 0; +} // end: stacks_reconfig_maybe + + +static struct stat_stack *update_single_stack ( + struct procps_statinfo *info, + struct fetch_support *this, + enum stat_item *items, + int numitems) +{ + if (0 > stacks_reconfig_maybe(this, items, numitems)) + return NULL; + + if (!this->extents + && !(this->extents = stacks_alloc(this, 1))) + return NULL; + + if (this->dirty_stacks) + cleanup_stacks_all(this); + + assign_results(this->extents->stacks[0], &info->sys_hist, &info->cpu_hist); + this->dirty_stacks = 1; + + return this->extents->stacks[0]; +} // end: update_single_stack + + +#if defined(PRETEND_NUMA) && defined(NUMA_DISABLE) +# warning 'PRETEND_NUMA' ignored, 'NUMA_DISABLE' is active +#endif + + +// ___ Public Functions ||||||||||||||||||||||||||||||||||||||||||||||||||||||| /* - * procps_stat_jiffs_fill: + * procps_stat_new: * - * Refresh available cpu data, then return all cpu data in the caller - * supplied structures, up to the lesser of maxdests or total available. + * Create a new container to hold the stat information * - * We tolerate a maxdests greater than the total available, and - * the caller had better tolerate fewer returned than requested. + * The initial refcount is 1, and needs to be decremented + * to release the resources of the structure. * - * This function deals only with the 'current' jiffs counts. + * Returns: a new stat info container */ -PROCPS_EXPORT int procps_stat_jiffs_fill ( - struct procps_stat *info, - struct procps_jiffs *dests, - int maxdests) +PROCPS_EXPORT int procps_stat_new ( + struct procps_statinfo **info) { - int i, rc; + struct procps_statinfo *p; - if (info == NULL || dests == NULL) + if (info == NULL || *info != NULL) return -EINVAL; - if ((rc = procps_stat_read_jiffs(info)) < 0) - return rc; - if (!info->jiff_hists_inuse) - return -1; + if (!(p = calloc(1, sizeof(struct procps_statinfo)))) + return -ENOMEM; - for (i = 0; i < info->jiff_hists_inuse && i < maxdests; i++) { - // note, we're just copying the 'new' portion of our procps_jiffs_private - memcpy(dests + i, info->jiff_hists + i, sizeof(struct procps_jiffs)); + p->refcount = 1; + p->stat_fd = -1; + p->results.cpus = &p->cpus.result; + p->results.nodes = &p->nodes.result; + p->cpus.total = procps_cpu_count(); + +#ifndef NUMA_DISABLE + #ifndef PRETEND_NUMA + // we'll try for the most recent version, then a version we know works... + if ((p->libnuma_handle = dlopen("libnuma.so", RTLD_LAZY)) + || (p->libnuma_handle = dlopen("libnuma.so.1", RTLD_LAZY))) { + p->our_max_node = dlsym(p->libnuma_handle, "numa_max_node"); + p->our_node_of_cpu = dlsym(p->libnuma_handle, "numa_node_of_cpu"); + if (p->our_max_node && p->our_node_of_cpu) + p->nodes.total = p->our_max_node() + 1; + else { + dlclose(p->libnuma_handle); + p->libnuma_handle = NULL; + } } - return i; -} + #else + p->libnuma_handle = (void *)-1; + p->our_max_node = fake_max_node; + p->our_node_of_cpu = fake_node_of_cpu; + p->nodes.total = fake_max_node() + 1; + #endif +#endif + + *info = p; + return 0; +} // end :procps_stat_new -/* - * procps_stat_jiffs_hist_get: - * - * Return the designated cpu data in the caller supplied structure. - * A negative 'which' denotes the cpu_summary, not a real cpu. - * - * This function provides both 'new' and 'old' jiffs counts. - */ -PROCPS_EXPORT int procps_stat_jiffs_hist_get ( - struct procps_stat *info, - struct procps_jiffs_hist *dest, - int which) + +PROCPS_EXPORT int procps_stat_ref ( + struct procps_statinfo *info) { - struct procps_jiffs_private *p; - int i; + if (info == NULL) + return -EINVAL; - if (info == NULL || dest == NULL) + info->refcount++; + return info->refcount; +} // end: procps_stat_ref + + +PROCPS_EXPORT int procps_stat_unref ( + struct procps_statinfo **info) +{ + if (info == NULL || *info == NULL) return -EINVAL; - if (which < 0) { - memcpy(dest, &info->cpu_summary, sizeof(struct procps_jiffs_hist)); + (*info)->refcount--; + + if ((*info)->refcount == 0) { + if ((*info)->cpus.anchor) + free((*info)->cpus.anchor); + if ((*info)->cpus.hist.tics) + free((*info)->cpus.hist.tics); + if ((*info)->cpus.fetch.items) + free((*info)->cpus.fetch.items); + if ((*info)->cpus.fetch.extents) + extents_free_all(&(*info)->cpus.fetch); + + if ((*info)->nodes.anchor) + free((*info)->nodes.anchor); + if ((*info)->nodes.hist.tics) + free((*info)->nodes.hist.tics); + if ((*info)->nodes.fetch.items) + free((*info)->nodes.fetch.items); + if ((*info)->nodes.fetch.extents) + extents_free_all(&(*info)->nodes.fetch); + + if ((*info)->cpu_summary.items) + free((*info)->cpu_summary.items); + if ((*info)->cpu_summary.extents) + extents_free_all(&(*info)->cpu_summary); + + if ((*info)->select.items) + free((*info)->select.items); + if ((*info)->select.extents) + extents_free_all(&(*info)->select); + +#ifndef NUMA_DISABLE + #ifndef PRETEND_NUMA + if ((*info)->libnuma_handle) + dlclose((*info)->libnuma_handle); + #endif +#endif + free(*info); + *info = NULL; return 0; } - p = info->jiff_hists; - for (i = 0; i < info->jiff_hists_inuse; i++) { - if (p->cpu.id == which) { - memcpy(dest, p, sizeof(struct procps_jiffs_hist)); - return 0; - } - ++p; + return (*info)->refcount; +} // end: procps_stat_unref + + +PROCPS_EXPORT signed long long procps_stat_get ( + struct procps_statinfo *info, + enum stat_item item) +{ + static time_t sav_secs; + time_t cur_secs; + int rc; + + /* no sense reading the stat with every call from a program like vmstat + who chooses not to use the much more efficient 'select' function ... */ + cur_secs = time(NULL); + if (!info->stat_was_read || 1 <= cur_secs - sav_secs) { + if ((rc = read_stat_failed(info))) + return rc; + sav_secs = cur_secs; + } + switch (item) { + case PROCPS_STAT_TIC_ID: + return info->cpu_hist.id; + case PROCPS_STAT_TIC_NUMA_NODE: + return info->cpu_hist.numa_node; + case PROCPS_STAT_TIC_USER: + return info->cpu_hist.new.user; + case PROCPS_STAT_TIC_NICE: + return info->cpu_hist.new.nice; + case PROCPS_STAT_TIC_SYSTEM: + return info->cpu_hist.new.system; + case PROCPS_STAT_TIC_IDLE: + return info->cpu_hist.new.idle; + case PROCPS_STAT_TIC_IOWAIT: + return info->cpu_hist.new.iowait; + case PROCPS_STAT_TIC_IRQ: + return info->cpu_hist.new.irq; + case PROCPS_STAT_TIC_SOFTIRQ: + return info->cpu_hist.new.sirq; + case PROCPS_STAT_TIC_STOLEN: + return info->cpu_hist.new.stolen; + case PROCPS_STAT_TIC_GUEST: + return info->cpu_hist.new.guest; + case PROCPS_STAT_TIC_GUEST_NICE: + return info->cpu_hist.new.gnice; + case PROCPS_STAT_SYS_CTX_SWITCHES: + return info->sys_hist.new.ctxt; + case PROCPS_STAT_SYS_INTERRUPTS: + return info->sys_hist.new.intr; + case PROCPS_STAT_SYS_PROC_BLOCKED: + return info->sys_hist.new.procs_blocked; + case PROCPS_STAT_SYS_PROC_CREATED: + return info->sys_hist.new.procs_created; + case PROCPS_STAT_SYS_PROC_RUNNING: + return info->sys_hist.new.procs_running; + case PROCPS_STAT_SYS_TIME_OF_BOOT: + return info->sys_hist.new.btime; + default: + /* the other enumerators make no sense in this context + ( TIC_DELTA_, _noop, _extra ) */ + return -EINVAL; } - return -1; -} +} // end: procps_stat_get -/* - * procps_stat_jiffs_hist_fill: + +/* procps_stat_select(): * - * Refresh available cpu data, then return all cpu data in the caller - * supplied structures, up to the lesser of maxdests or total available. + * Harvest all the requested TIC and/or SYS information then return + * it in a results stack. + * + * Returns: pointer to a stat_stack struct on success, NULL on error. + */ +PROCPS_EXPORT struct stat_stack *procps_stat_select ( + struct procps_statinfo *info, + enum stat_item *items, + int numitems) +{ + if (info == NULL || items == NULL) + return NULL; + + if (read_stat_failed(info)) + return NULL; + + return update_single_stack(info, &info->select, items, numitems); +} // end: procps_stat_select + + +/* procps_stat_reap(): * - * We tolerate a maxdests greater than the total available, and - * the caller had better tolerate fewer returned than requested. + * Harvest all the requested NUMA NODE and/or CPU information providing the + * result stacks along with totals and the cpu summary. * - * This function provides both 'new' and 'old' jiffs counts. + * Returns: pointer to a stat_reaped struct on success, NULL on error. */ -PROCPS_EXPORT int procps_stat_jiffs_hist_fill ( - struct procps_stat *info, - struct procps_jiffs_hist *dests, - int maxdests) +PROCPS_EXPORT struct stat_reaped *procps_stat_reap ( + struct procps_statinfo *info, + enum stat_reap_type what, + enum stat_item *items, + int numitems) { int i, rc; - if (info == NULL || dests == NULL) - return -EINVAL; - if ((rc = procps_stat_read_jiffs(info)) < 0) - return rc; - if (!info->jiff_hists_inuse) - return -1; + if (info == NULL || items == NULL) + return NULL; + + info->results.summary = NULL; + info->cpus.result.total = info->nodes.result.total = 0; + info->cpus.result.stacks = info->nodes.result.stacks = NULL; + + if (what != STAT_REAP_CPUS_ONLY && what != STAT_REAP_CPUS_AND_NODES) + return NULL; + + // those PROCPS_STAT_SYS_type enum's make sense only to 'select' ... + for (i = 0; i < numitems; i++) { + if (items[i] > PROCPS_STAT_TIC_highest) + return NULL; + } - for (i = 0; i < info->jiff_hists_inuse && i < maxdests; i++) { - memcpy(dests + i, info->jiff_hists + i, sizeof(struct procps_jiffs_hist)); + if ((rc = stacks_reconfig_maybe(&info->cpu_summary, items, numitems)) < 0) + return NULL; + if (rc) { + if ((rc = stacks_reconfig_maybe(&info->cpus.fetch, items, numitems)) < 0 + || ((rc = stacks_reconfig_maybe(&info->nodes.fetch, items, numitems)) < 0)) + return NULL; } - return i; -} + + if (read_stat_failed(info)) + return NULL; + info->results.summary = update_single_stack(info, &info->cpu_summary, items, numitems); + + switch (what) { + case STAT_REAP_CPUS_ONLY: + if (!stacks_fetch_tics(info, &info->cpus)) + return NULL; + break; + case STAT_REAP_CPUS_AND_NODES: + if (0 > make_numa_hist(info)) + return NULL; + if (!stacks_fetch_tics(info, &info->cpus)) + return NULL; + if (!stacks_fetch_tics(info, &info->nodes)) + return NULL; + break; + default: + return NULL; + }; + + return &info->results; +} // end: procps_stat_reap diff --git a/proc/stat.h b/proc/stat.h index 58fed3ab..f86c1ce3 100644 --- a/proc/stat.h +++ b/proc/stat.h @@ -22,96 +22,105 @@ __BEGIN_DECLS -enum procps_cpu_item { - PROCPS_CPU_USER, // jiff - PROCPS_CPU_NICE, // jiff - PROCPS_CPU_SYSTEM, // jiff - PROCPS_CPU_IDLE, // jiff - PROCPS_CPU_IOWAIT, // jiff - PROCPS_CPU_IRQ, // jiff - PROCPS_CPU_SIRQ, // jiff - PROCPS_CPU_STOLEN, // jiff - PROCPS_CPU_GUEST, // jiff - PROCPS_CPU_GNICE, // jiff - PROCPS_CPU_noop, // n/a - PROCPS_CPU_stack_end // n/a +enum stat_item { + PROCPS_STAT_noop, // ( never altered ) + PROCPS_STAT_extra, // ( reset to zero ) + + PROCPS_STAT_TIC_ID, // s_int + PROCPS_STAT_TIC_NUMA_NODE, // s_int + PROCPS_STAT_TIC_USER, // ull_int + PROCPS_STAT_TIC_NICE, // ull_int + PROCPS_STAT_TIC_SYSTEM, // ull_int + PROCPS_STAT_TIC_IDLE, // ull_int + PROCPS_STAT_TIC_IOWAIT, // ull_int + PROCPS_STAT_TIC_IRQ, // ull_int + PROCPS_STAT_TIC_SOFTIRQ, // ull_int + PROCPS_STAT_TIC_STOLEN, // ull_int + PROCPS_STAT_TIC_GUEST, // ull_int + PROCPS_STAT_TIC_GUEST_NICE, // ull_int + PROCPS_STAT_TIC_DELTA_USER, // sl_int + PROCPS_STAT_TIC_DELTA_NICE, // sl_int + PROCPS_STAT_TIC_DELTA_SYSTEM, // sl_int + PROCPS_STAT_TIC_DELTA_IDLE, // sl_int + PROCPS_STAT_TIC_DELTA_IOWAIT, // sl_int + PROCPS_STAT_TIC_DELTA_IRQ, // sl_int + PROCPS_STAT_TIC_DELTA_SOFTIRQ, // sl_int + PROCPS_STAT_TIC_DELTA_STOLEN, // sl_int + PROCPS_STAT_TIC_DELTA_GUEST, // sl_int + PROCPS_STAT_TIC_DELTA_GUEST_NICE, // sl_int + + PROCPS_STAT_SYS_CTX_SWITCHES, // ul_int + PROCPS_STAT_SYS_INTERRUPTS, // ul_int + PROCPS_STAT_SYS_PROC_BLOCKED, // ul_int + PROCPS_STAT_SYS_PROC_CREATED, // ul_int + PROCPS_STAT_SYS_PROC_RUNNING, // ul_int + PROCPS_STAT_SYS_TIME_OF_BOOT, // ul_int + PROCPS_STAT_SYS_DELTA_CTX_SWITCHES, // s_int + PROCPS_STAT_SYS_DELTA_INTERRUPTS, // s_int + PROCPS_STAT_SYS_DELTA_PROC_BLOCKED, // s_int + PROCPS_STAT_SYS_DELTA_PROC_CREATED, // s_int + PROCPS_STAT_SYS_DELTA_PROC_RUNNING, // s_int }; -enum procps_stat_item { - PROCPS_STAT_INTR, // u_int - PROCPS_STAT_CTXT, // u_int - PROCPS_STAT_BTIME, // u_int - PROCPS_STAT_PROCS, // u_int - PROCPS_STAT_PROCS_BLK, // u_int - PROCPS_STAT_PROCS_RUN, // u_int - PROCPS_STAT_noop, // n/a - PROCPS_STAT_stack_end // n/a -}; - -typedef unsigned long long jiff; - -struct procps_jiffs { - jiff user, nice, system, idle, iowait, irq, sirq, stolen, guest, gnice; -}; - -struct procps_jiffs_hist { - struct procps_jiffs new; - struct procps_jiffs old; - int id; +enum stat_reap_type { + STAT_REAP_CPUS_ONLY, + STAT_REAP_CPUS_AND_NODES }; struct stat_result { - int item; + enum stat_item item; union { - unsigned int u_int; - jiff jiff; + int s_int; + long sl_int; + unsigned long ul_int; + unsigned long long ull_int; } result; }; -struct procps_stat; +struct stat_stack { + struct stat_result *head; +}; -int procps_stat_new (struct procps_stat **info); -int procps_stat_read (struct procps_stat *info, const int cpu_only); -int procps_stat_read_jiffs (struct procps_stat *info); +struct stat_reap { + int total; + struct stat_stack **stacks; +}; -int procps_stat_ref (struct procps_stat *info); -int procps_stat_unref (struct procps_stat **info); +struct stat_reaped { + struct stat_stack *summary; + struct stat_reap *cpus; + struct stat_reap *nodes; +}; -jiff procps_stat_cpu_get ( - struct procps_stat *info, - enum procps_cpu_item item); -int procps_stat_cpu_getstack ( - struct procps_stat *info, - struct stat_result *these); +#define PROCPS_STAT_SUMMARY_ID -11111 +#define PROCPS_STAT_NODE_INVALID -22222 -int procps_stat_jiffs_get ( - struct procps_stat *info, - struct procps_jiffs *dest, - int which); +#define PROCPS_STAT_VAL(rel_enum,type,stack) \ + stack -> head [ rel_enum ] . result . type -int procps_stat_jiffs_hist_get ( - struct procps_stat *info, - struct procps_jiffs_hist *dest, - int which); -int procps_stat_jiffs_fill ( - struct procps_stat *info, - struct procps_jiffs *dests, - int maxdests); +struct procps_statinfo; -int procps_stat_jiffs_hist_fill ( - struct procps_stat *info, - struct procps_jiffs_hist *dests, - int maxdests); +int procps_stat_new (struct procps_statinfo **info); +int procps_stat_ref (struct procps_statinfo *info); +int procps_stat_unref (struct procps_statinfo **info); -unsigned int procps_stat_sys_get ( - struct procps_stat *info, - enum procps_stat_item item); +signed long long procps_stat_get ( + struct procps_statinfo *info, + enum stat_item item); -int procps_stat_sys_getstack ( - struct procps_stat *info, - struct stat_result *these); +struct stat_stack *procps_stat_select ( + struct procps_statinfo *info, + enum stat_item *items, + int numitems); + +struct stat_reaped *procps_stat_reap ( + struct procps_statinfo *info, + enum stat_reap_type what, + enum stat_item *items, + int numitems); __END_DECLS + #endif -- 2.40.0