diff --git a/proc/libprocps.sym b/proc/libprocps.sym index bdbcf647..0b90bd3a 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -16,13 +16,10 @@ global: procps_linux_version; procps_loadavg; procps_meminfo_new; - procps_meminfo_read; procps_meminfo_ref; procps_meminfo_unref; procps_meminfo_get; - procps_meminfo_getstack; - procps_meminfo_stack_fill; - procps_meminfo_stack_alloc; + procps_meminfo_select; procps_ns_get_name; procps_ns_get_id; procps_ns_read_pid; diff --git a/proc/meminfo.c b/proc/meminfo.c index d2677e76..3828d66d 100644 --- a/proc/meminfo.c +++ b/proc/meminfo.c @@ -1,9 +1,5 @@ /* - * meminfo - Memory statistics part of procps - * - * Copyright (C) 1992-1998 by Michael K. Johnson - * Copyright (C) 1998-2003 Albert Cahalan - * Copyright (C) 2015 Craig Small + * libprocps - Library to read proc filesystem * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,92 +18,568 @@ #include #include +#include #include #include +#include #include #include #include +#include #include -#include "procps-private.h" + #define MEMINFO_FILE "/proc/meminfo" struct meminfo_data { - unsigned long active; - unsigned long inactive; - unsigned long high_free; - unsigned long high_total; - unsigned long low_free; - unsigned long low_total; - unsigned long available; - unsigned long buffers; - unsigned long cached; - unsigned long free; - unsigned long shared; - unsigned long total; - unsigned long used; - unsigned long slab; - unsigned long slab_reclaimable; - unsigned long swap_free; - unsigned long swap_total; - unsigned long swap_used; +/** == man 5 proc shows these as: to be documented, so the hell with 'em */ + unsigned long Active; + unsigned long Active_anon; // as: (anon) + unsigned long Active_file; // as: (file) + unsigned long AnonHugePages; + unsigned long AnonPages; + unsigned long Bounce; + unsigned long Buffers; + unsigned long Cached; +/** unsigned long CmaFree; ----------- */ +/** unsigned long CmaTotal; ---------- */ + unsigned long CommitLimit; + unsigned long Committed_AS; +/** unsigned long DirectMap1G; ------- */ +/** unsigned long DirectMap2M; ------- */ +/** unsigned long DirectMap4k; ------- */ + unsigned long Dirty; + unsigned long HardwareCorrupted; + unsigned long HighFree; + unsigned long HighTotal; + unsigned long HugePages_Free; + unsigned long HugePages_Rsvd; + unsigned long HugePages_Surp; + unsigned long HugePages_Total; + unsigned long Hugepagesize; + unsigned long Inactive; + unsigned long Inactive_anon; // as: (anon) + unsigned long Inactive_file; // as: (file) + unsigned long KernelStack; + unsigned long LowFree; + unsigned long LowTotal; + unsigned long Mapped; + unsigned long MemAvailable; + unsigned long MemFree; + unsigned long MemTotal; + unsigned long Mlocked; + unsigned long NFS_Unstable; + unsigned long PageTables; + unsigned long SReclaimable; + unsigned long SUnreclaim; + unsigned long Shmem; + unsigned long Slab; + unsigned long SwapCached; + unsigned long SwapFree; + unsigned long SwapTotal; + unsigned long Unevictable; + unsigned long VmallocChunk; + unsigned long VmallocTotal; + unsigned long VmallocUsed; + unsigned long Writeback; + unsigned long WritebackTmp; + + unsigned long derived_mem_hi_used; + unsigned long derived_mem_lo_used; + unsigned long derived_mem_used; + unsigned long derived_swap_used; +}; + +struct hist_mem { + struct meminfo_data new; + struct meminfo_data old; +}; + +struct stacks_extent { + int ext_numstacks; + struct stacks_extent *next; + struct meminfo_stack **stacks; }; struct procps_meminfo { int refcount; int meminfo_fd; - struct meminfo_data data; - struct stacks_anchor *stacked; -}; - -struct stack_vectors { - struct stacks_anchor *owner; - struct meminfo_stack **heads; -}; - -struct stacks_anchor { - int depth; - struct stack_vectors *vectors; - struct stacks_anchor *self; - struct stacks_anchor *next; + int meminfo_was_read; + int dirty_stacks; + struct hist_mem mem_hist; + int numitems; + enum meminfo_item *items; + struct stacks_extent *extents; }; -/* - * procps_meminfo_new(): - * - * Create a new container to hold the meminfo information - * - * The initial refcount is 1, and needs to be decremented - * to release the resources of the structure. - * - * Returns: a new meminfo info container - */ -PROCPS_EXPORT int procps_meminfo_new ( - struct procps_meminfo **info) +// ___ Results 'Set' Support |||||||||||||||||||||||||||||||||||||||||||||||||| + +#define setNAME(e) set_results_ ## e +#define setDECL(e) static void setNAME(e) \ + (struct meminfo_result *R, struct hist_mem *H) + +// regular assignment +#define MEM_set(e,t,x) setDECL(e) { R->result. t = H->new . x; } +// delta assignment +#define HST_set(e,t,x) setDECL(e) { R->result. t = ( H->new . x - H->old. x ); \ + if (R->result. t < 0) R->result. t = 0; } + +setDECL(noop) { (void)R; (void)H; } +setDECL(extra) { (void)R; (void)H; } + +MEM_set(MEM_ACTIVE, ul_int, Active) +MEM_set(MEM_ACTIVE_ANON, ul_int, Active_anon) +MEM_set(MEM_ACTIVE_FILE, ul_int, Active_file) +MEM_set(MEM_ANON, ul_int, AnonPages) +MEM_set(MEM_AVAILABLE, ul_int, MemAvailable) +MEM_set(MEM_BOUNCE, ul_int, Bounce) +MEM_set(MEM_BUFFERS, ul_int, Buffers) +MEM_set(MEM_CACHED, ul_int, Cached) +MEM_set(MEM_COMMIT_LIMIT, ul_int, CommitLimit) +MEM_set(MEM_COMMITTED_AS, ul_int, Committed_AS) +MEM_set(MEM_HARD_CORRUPTED, ul_int, HardwareCorrupted) +MEM_set(MEM_DIRTY, ul_int, Dirty) +MEM_set(MEM_FREE, ul_int, MemFree) +MEM_set(MEM_HUGE_ANON, ul_int, AnonHugePages) +MEM_set(MEM_HUGE_FREE, ul_int, HugePages_Free) +MEM_set(MEM_HUGE_RSVD, ul_int, HugePages_Rsvd) +MEM_set(MEM_HUGE_SIZE, ul_int, Hugepagesize) +MEM_set(MEM_HUGE_SURPLUS, ul_int, HugePages_Surp) +MEM_set(MEM_HUGE_TOTAL, ul_int, HugePages_Total) +MEM_set(MEM_INACTIVE, ul_int, Inactive) +MEM_set(MEM_INACTIVE_ANON, ul_int, Inactive_anon) +MEM_set(MEM_INACTIVE_FILE, ul_int, Inactive_file) +MEM_set(MEM_KERNEL_STACK, ul_int, KernelStack) +MEM_set(MEM_LOCKED, ul_int, Mlocked) +MEM_set(MEM_MAPPED, ul_int, Mapped) +MEM_set(MEM_NFS_UNSTABLE, ul_int, NFS_Unstable) +MEM_set(MEM_PAGE_TABLES, ul_int, PageTables) +MEM_set(MEM_SHARED, ul_int, Shmem) +MEM_set(MEM_SLAB, ul_int, Slab) +MEM_set(MEM_SLAB_RECLAIM, ul_int, SReclaimable) +MEM_set(MEM_SLAB_UNRECLAIM, ul_int, SUnreclaim) +MEM_set(MEM_TOTAL, ul_int, MemTotal) +MEM_set(MEM_UNEVICTABLE, ul_int, Unevictable) +MEM_set(MEM_USED, ul_int, derived_mem_used) +MEM_set(MEM_VM_ALLOC_CHUNK, ul_int, VmallocChunk) +MEM_set(MEM_VM_ALLOC_TOTAL, ull_int, VmallocTotal) +MEM_set(MEM_VM_ALLOC_USED, ul_int, VmallocUsed) +MEM_set(MEM_WRITEBACK, ul_int, Writeback) +MEM_set(MEM_WRITEBACK_TMP, ul_int, WritebackTmp) + +HST_set(DELTA_ACTIVE, s_int, Active) +HST_set(DELTA_ACTIVE_ANON, s_int, Active_anon) +HST_set(DELTA_ACTIVE_FILE, s_int, Active_file) +HST_set(DELTA_ANON, s_int, AnonPages) +HST_set(DELTA_AVAILABLE, s_int, MemAvailable) +HST_set(DELTA_BOUNCE, s_int, Bounce) +HST_set(DELTA_BUFFERS, s_int, Buffers) +HST_set(DELTA_CACHED, s_int, Cached) +HST_set(DELTA_COMMIT_LIMIT, s_int, CommitLimit) +HST_set(DELTA_COMMITTED_AS, s_int, Committed_AS) +HST_set(DELTA_HARD_CORRUPTED, s_int, HardwareCorrupted) +HST_set(DELTA_DIRTY, s_int, Dirty) +HST_set(DELTA_FREE, s_int, MemFree) +HST_set(DELTA_HUGE_ANON, s_int, AnonHugePages) +HST_set(DELTA_HUGE_FREE, s_int, HugePages_Free) +HST_set(DELTA_HUGE_RSVD, s_int, HugePages_Rsvd) +HST_set(DELTA_HUGE_SIZE, s_int, Hugepagesize) +HST_set(DELTA_HUGE_SURPLUS, s_int, HugePages_Surp) +HST_set(DELTA_HUGE_TOTAL, s_int, HugePages_Total) +HST_set(DELTA_INACTIVE, s_int, Inactive) +HST_set(DELTA_INACTIVE_ANON, s_int, Inactive_anon) +HST_set(DELTA_INACTIVE_FILE, s_int, Inactive_file) +HST_set(DELTA_KERNEL_STACK, s_int, KernelStack) +HST_set(DELTA_LOCKED, s_int, Mlocked) +HST_set(DELTA_MAPPED, s_int, Mapped) +HST_set(DELTA_NFS_UNSTABLE, s_int, NFS_Unstable) +HST_set(DELTA_PAGE_TABLES, s_int, PageTables) +HST_set(DELTA_SHARED, s_int, Shmem) +HST_set(DELTA_SLAB, s_int, Slab) +HST_set(DELTA_SLAB_RECLAIM, s_int, SReclaimable) +HST_set(DELTA_SLAB_UNRECLAIM, s_int, SUnreclaim) +HST_set(DELTA_TOTAL, s_int, MemTotal) +HST_set(DELTA_UNEVICTABLE, s_int, Unevictable) +HST_set(DELTA_USED, s_int, derived_mem_used) +HST_set(DELTA_VM_ALLOC_CHUNK, s_int, VmallocChunk) +HST_set(DELTA_VM_ALLOC_TOTAL, sl_int, VmallocTotal) +HST_set(DELTA_VM_ALLOC_USED, s_int, VmallocUsed) +HST_set(DELTA_WRITEBACK, s_int, Writeback) +HST_set(DELTA_WRITEBACK_TMP, s_int, WritebackTmp) + +MEM_set(MEMHI_FREE, ul_int, HighFree) +MEM_set(MEMHI_TOTAL, ul_int, HighTotal) +MEM_set(MEMHI_USED, ul_int, derived_mem_hi_used) + +MEM_set(MEMLO_FREE, ul_int, LowFree) +MEM_set(MEMLO_TOTAL, ul_int, LowTotal) +MEM_set(MEMLO_USED, ul_int, derived_mem_lo_used) + +MEM_set(SWAP_CACHED, ul_int, SwapCached) +MEM_set(SWAP_FREE, ul_int, SwapFree) +MEM_set(SWAP_TOTAL, ul_int, SwapTotal) +MEM_set(SWAP_USED, ul_int, derived_swap_used) + + +// ___ Results 'Get' Support |||||||||||||||||||||||||||||||||||||||||||||||||| + +#define getNAME(e) get_results_ ## e +#define getDECL(e) static signed long getNAME(e) \ + (struct procps_meminfo *I) + +// regular get +#define MEM_get(e,x) getDECL(e) { return I->mem_hist.new. x; } +// delta get +#define HST_get(e,x) getDECL(e) { int n = I->mem_hist.new. x - I->mem_hist.old. x; \ + return ( n < 0 ? 0 : n ); } + +getDECL(noop) { (void)I; return 0; } +getDECL(extra) { (void)I; return 0; } + +MEM_get(MEM_ACTIVE, Active) +MEM_get(MEM_ACTIVE_ANON, Active_anon) +MEM_get(MEM_ACTIVE_FILE, Active_file) +MEM_get(MEM_ANON, AnonPages) +MEM_get(MEM_AVAILABLE, MemAvailable) +MEM_get(MEM_BOUNCE, Bounce) +MEM_get(MEM_BUFFERS, Buffers) +MEM_get(MEM_CACHED, Cached) +MEM_get(MEM_COMMIT_LIMIT, CommitLimit) +MEM_get(MEM_COMMITTED_AS, Committed_AS) +MEM_get(MEM_HARD_CORRUPTED, HardwareCorrupted) +MEM_get(MEM_DIRTY, Dirty) +MEM_get(MEM_FREE, MemFree) +MEM_get(MEM_HUGE_ANON, AnonHugePages) +MEM_get(MEM_HUGE_FREE, HugePages_Free) +MEM_get(MEM_HUGE_RSVD, HugePages_Rsvd) +MEM_get(MEM_HUGE_SIZE, Hugepagesize) +MEM_get(MEM_HUGE_SURPLUS, HugePages_Surp) +MEM_get(MEM_HUGE_TOTAL, HugePages_Total) +MEM_get(MEM_INACTIVE, Inactive) +MEM_get(MEM_INACTIVE_ANON, Inactive_anon) +MEM_get(MEM_INACTIVE_FILE, Inactive_file) +MEM_get(MEM_KERNEL_STACK, KernelStack) +MEM_get(MEM_LOCKED, Mlocked) +MEM_get(MEM_MAPPED, Mapped) +MEM_get(MEM_NFS_UNSTABLE, NFS_Unstable) +MEM_get(MEM_PAGE_TABLES, PageTables) +MEM_get(MEM_SHARED, Shmem) +MEM_get(MEM_SLAB, Slab) +MEM_get(MEM_SLAB_RECLAIM, SReclaimable) +MEM_get(MEM_SLAB_UNRECLAIM, SUnreclaim) +MEM_get(MEM_TOTAL, MemTotal) +MEM_get(MEM_UNEVICTABLE, Unevictable) +MEM_get(MEM_USED, derived_mem_used) +MEM_get(MEM_VM_ALLOC_CHUNK, VmallocChunk) +MEM_get(MEM_VM_ALLOC_TOTAL, VmallocTotal) +MEM_get(MEM_VM_ALLOC_USED, VmallocUsed) +MEM_get(MEM_WRITEBACK, Writeback) +MEM_get(MEM_WRITEBACK_TMP, WritebackTmp) + +HST_get(DELTA_ACTIVE, Active) +HST_get(DELTA_ACTIVE_ANON, Active_anon) +HST_get(DELTA_ACTIVE_FILE, Active_file) +HST_get(DELTA_ANON, AnonPages) +HST_get(DELTA_AVAILABLE, MemAvailable) +HST_get(DELTA_BOUNCE, Bounce) +HST_get(DELTA_BUFFERS, Buffers) +HST_get(DELTA_CACHED, Cached) +HST_get(DELTA_COMMIT_LIMIT, CommitLimit) +HST_get(DELTA_COMMITTED_AS, Committed_AS) +HST_get(DELTA_HARD_CORRUPTED, HardwareCorrupted) +HST_get(DELTA_DIRTY, Dirty) +HST_get(DELTA_FREE, MemFree) +HST_get(DELTA_HUGE_ANON, AnonHugePages) +HST_get(DELTA_HUGE_FREE, HugePages_Free) +HST_get(DELTA_HUGE_RSVD, HugePages_Rsvd) +HST_get(DELTA_HUGE_SIZE, Hugepagesize) +HST_get(DELTA_HUGE_SURPLUS, HugePages_Surp) +HST_get(DELTA_HUGE_TOTAL, HugePages_Total) +HST_get(DELTA_INACTIVE, Inactive) +HST_get(DELTA_INACTIVE_ANON, Inactive_anon) +HST_get(DELTA_INACTIVE_FILE, Inactive_file) +HST_get(DELTA_KERNEL_STACK, KernelStack) +HST_get(DELTA_LOCKED, Mlocked) +HST_get(DELTA_MAPPED, Mapped) +HST_get(DELTA_NFS_UNSTABLE, NFS_Unstable) +HST_get(DELTA_PAGE_TABLES, PageTables) +HST_get(DELTA_SHARED, Shmem) +HST_get(DELTA_SLAB, Slab) +HST_get(DELTA_SLAB_RECLAIM, SReclaimable) +HST_get(DELTA_SLAB_UNRECLAIM, SUnreclaim) +HST_get(DELTA_TOTAL, MemTotal) +HST_get(DELTA_UNEVICTABLE, Unevictable) +HST_get(DELTA_USED, derived_mem_used) +HST_get(DELTA_VM_ALLOC_CHUNK, VmallocChunk) +HST_get(DELTA_VM_ALLOC_TOTAL, VmallocTotal) +HST_get(DELTA_VM_ALLOC_USED, VmallocUsed) +HST_get(DELTA_WRITEBACK, Writeback) +HST_get(DELTA_WRITEBACK_TMP, WritebackTmp) + +MEM_get(MEMHI_FREE, HighFree) +MEM_get(MEMHI_TOTAL, HighTotal) +MEM_get(MEMHI_USED, derived_mem_hi_used) + +MEM_get(MEMLO_FREE, LowFree) +MEM_get(MEMLO_TOTAL, LowTotal) +MEM_get(MEMLO_USED, derived_mem_lo_used) + +MEM_get(SWAP_CACHED, SwapCached) +MEM_get(SWAP_FREE, SwapFree) +MEM_get(SWAP_TOTAL, SwapTotal) +MEM_get(SWAP_USED, derived_swap_used) + + +// ___ Controlling Table |||||||||||||||||||||||||||||||||||||||||||||||||||||| + +typedef void (*SET_t)(struct meminfo_result *, struct hist_mem *); +#define RS(e) (SET_t)setNAME(e) + +typedef long (*GET_t)(struct procps_meminfo *); +#define RG(e) (GET_t)getNAME(e) + + + /* + * Need it be said? + * This table must be kept in the exact same order as + * those 'enum meminfo_item' guys ! */ +static struct { + SET_t setsfunc; // the actual result setting routine + GET_t getsfunc; // a routine to return single result +} Item_table[] = { +/* setsfunc getsfunc + --------------------------- --------------------------- */ + { RS(noop), RG(noop) }, + { RS(extra), RG(extra) }, + + { RS(MEM_ACTIVE), RG(MEM_ACTIVE) }, + { RS(MEM_ACTIVE_ANON), RG(MEM_ACTIVE_ANON) }, + { RS(MEM_ACTIVE_FILE), RG(MEM_ACTIVE_FILE) }, + { RS(MEM_ANON), RG(MEM_ANON) }, + { RS(MEM_AVAILABLE), RG(MEM_AVAILABLE) }, + { RS(MEM_BOUNCE), RG(MEM_BOUNCE) }, + { RS(MEM_BUFFERS), RG(MEM_BUFFERS) }, + { RS(MEM_CACHED), RG(MEM_CACHED) }, + { RS(MEM_COMMIT_LIMIT), RG(MEM_COMMIT_LIMIT) }, + { RS(MEM_COMMITTED_AS), RG(MEM_COMMITTED_AS) }, + { RS(MEM_HARD_CORRUPTED), RG(MEM_HARD_CORRUPTED) }, + { RS(MEM_DIRTY), RG(MEM_DIRTY) }, + { RS(MEM_FREE), RG(MEM_FREE) }, + { RS(MEM_HUGE_ANON), RG(MEM_HUGE_ANON) }, + { RS(MEM_HUGE_FREE), RG(MEM_HUGE_FREE) }, + { RS(MEM_HUGE_RSVD), RG(MEM_HUGE_RSVD) }, + { RS(MEM_HUGE_SIZE), RG(MEM_HUGE_SIZE) }, + { RS(MEM_HUGE_SURPLUS), RG(MEM_HUGE_SURPLUS) }, + { RS(MEM_HUGE_TOTAL), RG(MEM_HUGE_TOTAL) }, + { RS(MEM_INACTIVE), RG(MEM_INACTIVE) }, + { RS(MEM_INACTIVE_ANON), RG(MEM_INACTIVE_ANON) }, + { RS(MEM_INACTIVE_FILE), RG(MEM_INACTIVE_FILE) }, + { RS(MEM_KERNEL_STACK), RG(MEM_KERNEL_STACK) }, + { RS(MEM_LOCKED), RG(MEM_LOCKED) }, + { RS(MEM_MAPPED), RG(MEM_MAPPED) }, + { RS(MEM_NFS_UNSTABLE), RG(MEM_NFS_UNSTABLE) }, + { RS(MEM_PAGE_TABLES), RG(MEM_PAGE_TABLES) }, + { RS(MEM_SHARED), RG(MEM_SHARED) }, + { RS(MEM_SLAB), RG(MEM_SLAB) }, + { RS(MEM_SLAB_RECLAIM), RG(MEM_SLAB_RECLAIM) }, + { RS(MEM_SLAB_UNRECLAIM), RG(MEM_SLAB_UNRECLAIM) }, + { RS(MEM_TOTAL), RG(MEM_TOTAL) }, + { RS(MEM_UNEVICTABLE), RG(MEM_UNEVICTABLE) }, + { RS(MEM_USED), RG(MEM_USED) }, + { RS(MEM_VM_ALLOC_CHUNK), RG(MEM_VM_ALLOC_CHUNK) }, + { RS(MEM_VM_ALLOC_TOTAL), RG(MEM_VM_ALLOC_TOTAL) }, + { RS(MEM_VM_ALLOC_USED), RG(MEM_VM_ALLOC_USED) }, + { RS(MEM_WRITEBACK), RG(MEM_WRITEBACK) }, + { RS(MEM_WRITEBACK_TMP), RG(MEM_WRITEBACK_TMP) }, + + { RS(DELTA_ACTIVE), RG(DELTA_ACTIVE) }, + { RS(DELTA_ACTIVE_ANON), RG(DELTA_ACTIVE_ANON) }, + { RS(DELTA_ACTIVE_FILE), RG(DELTA_ACTIVE_FILE) }, + { RS(DELTA_ANON), RG(DELTA_ANON) }, + { RS(DELTA_AVAILABLE), RG(DELTA_AVAILABLE) }, + { RS(DELTA_BOUNCE), RG(DELTA_BOUNCE) }, + { RS(DELTA_BUFFERS), RG(DELTA_BUFFERS) }, + { RS(DELTA_CACHED), RG(DELTA_CACHED) }, + { RS(DELTA_COMMIT_LIMIT), RG(DELTA_COMMIT_LIMIT) }, + { RS(DELTA_COMMITTED_AS), RG(DELTA_COMMITTED_AS) }, + { RS(DELTA_HARD_CORRUPTED), RG(DELTA_HARD_CORRUPTED) }, + { RS(DELTA_DIRTY), RG(DELTA_DIRTY) }, + { RS(DELTA_FREE), RG(DELTA_FREE) }, + { RS(DELTA_HUGE_ANON), RG(DELTA_HUGE_ANON) }, + { RS(DELTA_HUGE_FREE), RG(DELTA_HUGE_FREE) }, + { RS(DELTA_HUGE_RSVD), RG(DELTA_HUGE_RSVD) }, + { RS(DELTA_HUGE_SIZE), RG(DELTA_HUGE_SIZE) }, + { RS(DELTA_HUGE_SURPLUS), RG(DELTA_HUGE_SURPLUS) }, + { RS(DELTA_HUGE_TOTAL), RG(DELTA_HUGE_TOTAL) }, + { RS(DELTA_INACTIVE), RG(DELTA_INACTIVE) }, + { RS(DELTA_INACTIVE_ANON), RG(DELTA_INACTIVE_ANON) }, + { RS(DELTA_INACTIVE_FILE), RG(DELTA_INACTIVE_FILE) }, + { RS(DELTA_KERNEL_STACK), RG(DELTA_KERNEL_STACK) }, + { RS(DELTA_LOCKED), RG(DELTA_LOCKED) }, + { RS(DELTA_MAPPED), RG(DELTA_MAPPED) }, + { RS(DELTA_NFS_UNSTABLE), RG(DELTA_NFS_UNSTABLE) }, + { RS(DELTA_PAGE_TABLES), RG(DELTA_PAGE_TABLES) }, + { RS(DELTA_SHARED), RG(DELTA_SHARED) }, + { RS(DELTA_SLAB), RG(DELTA_SLAB) }, + { RS(DELTA_SLAB_RECLAIM), RG(DELTA_SLAB_RECLAIM) }, + { RS(DELTA_SLAB_UNRECLAIM), RG(DELTA_SLAB_UNRECLAIM) }, + { RS(DELTA_TOTAL), RG(DELTA_TOTAL) }, + { RS(DELTA_UNEVICTABLE), RG(DELTA_UNEVICTABLE) }, + { RS(DELTA_USED), RG(DELTA_USED) }, + { RS(DELTA_VM_ALLOC_CHUNK), RG(DELTA_VM_ALLOC_CHUNK) }, + { RS(DELTA_VM_ALLOC_TOTAL), RG(DELTA_VM_ALLOC_TOTAL) }, + { RS(DELTA_VM_ALLOC_USED), RG(DELTA_VM_ALLOC_USED) }, + { RS(DELTA_WRITEBACK), RG(DELTA_WRITEBACK) }, + { RS(DELTA_WRITEBACK_TMP), RG(DELTA_WRITEBACK_TMP) }, + + { RS(MEMHI_FREE), RG(MEMHI_FREE) }, + { RS(MEMHI_TOTAL), RG(MEMHI_TOTAL) }, + { RS(MEMHI_USED), RG(MEMHI_USED) }, + { RS(MEMLO_FREE), RG(MEMLO_FREE) }, + { RS(MEMLO_TOTAL), RG(MEMLO_TOTAL) }, + { RS(MEMLO_USED), RG(MEMLO_USED) }, + + { RS(SWAP_CACHED), RG(SWAP_CACHED) }, + { RS(SWAP_FREE), RG(SWAP_FREE) }, + { RS(SWAP_TOTAL), RG(SWAP_TOTAL) }, + { RS(SWAP_USED), RG(SWAP_USED) }, + + { NULL, NULL } +}; + + /* please note, + * this enum MUST be 1 greater than the highest value of any enum */ +enum meminfo_item PROCPS_MEMINFO_logical_end = PROCPS_MEMINFO_SWAP_USED + 1; + +#undef setNAME +#undef setDECL +#undef MEM_set +#undef HST_set +#undef getNAME +#undef getDECL +#undef MEM_get +#undef HST_get + +// ___ Private Functions |||||||||||||||||||||||||||||||||||||||||||||||||||||| + +static inline void assign_results ( + struct meminfo_stack *stack, + struct hist_mem *mem_hist) { - struct procps_meminfo *m; - m = calloc(1, sizeof(struct procps_meminfo)); - if (!m) - return -ENOMEM; + struct meminfo_result *this = stack->head; + + for (;;) { + enum meminfo_item item = this->item; + if (item >= PROCPS_MEMINFO_logical_end) + break; + Item_table[item].setsfunc(this, mem_hist); + ++this; + } + return; +} // end: assign_results + + +static inline void cleanup_stack ( + struct meminfo_result *this) +{ + for (;;) { + if (this->item >= PROCPS_MEMINFO_logical_end) + break; + if (this->item > PROCPS_MEMINFO_noop) + this->result.ull_int = 0; + ++this; + } +} // end: cleanup_stack + + +static inline void cleanup_stacks_all ( + struct procps_meminfo *info) +{ + int i; + struct stacks_extent *ext = info->extents; + + while (ext) { + for (i = 0; ext->stacks[i]; i++) + cleanup_stack(ext->stacks[i]->head); + ext = ext->next; + }; + info->dirty_stacks = 0; +} // end: cleanup_stacks_all + + +static void extents_free_all ( + struct procps_meminfo *info) +{ + do { + struct stacks_extent *p = info->extents; + info->extents = info->extents->next; + free(p); + } while (info->extents); +} // end: extents_free_all + + +static inline struct meminfo_result *itemize_stack ( + struct meminfo_result *p, + int depth, + enum meminfo_item *items) +{ + struct meminfo_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: itemize_stack + + +static inline int items_check_failed ( + int numitems, + enum meminfo_item *items) +{ + 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 meminfo_item *' + * my_stack = procps_meminfo_select(info, PROCPS_MEMINFO_noop, num); + * ^~~~~~~~~~~~~~~~ + */ + if (numitems < 1 + || (void *)items < (void *)(unsigned long)(2 * PROCPS_MEMINFO_logical_end)) + return -1; + + for (i = 0; i < numitems; i++) { + // a meminfo_item is currently unsigned, but we'll protect our future + if (items[i] < 0) + return -1; + if (items[i] >= PROCPS_MEMINFO_logical_end) + return -1; + } - m->refcount = 1; - m->meminfo_fd = -1; - *info = m; return 0; -} +} // end: items_check_failed + /* - * procps_meminfo_read(): + * read_meminfo_failed(): * * Read the data out of /proc/meminfo putting the information * into the supplied info structure */ -PROCPS_EXPORT int procps_meminfo_read ( +PROCPS_EXPORT int read_meminfo_failed ( struct procps_meminfo *info) { + /* a 'memory history reference' macro for readability, + so we can focus the field names ... */ + #define mHr(f) info->mem_hist.new. f char buf[8192]; char *head, *tail; int size; @@ -117,15 +589,18 @@ PROCPS_EXPORT int procps_meminfo_read ( if (info == NULL) return -1; - memset(&(info->data), 0, sizeof(struct meminfo_data)); - /* read in the data */ + // remember history from last time around + memcpy(&info->mem_hist.old, &info->mem_hist.new, sizeof(struct meminfo_data)); + // clear out the soon to be 'current' values + memset(&info->mem_hist.new, 0, sizeof(struct meminfo_data)); - if (-1 == info->meminfo_fd && (info->meminfo_fd = open(MEMINFO_FILE, O_RDONLY)) == -1) { + if (-1 == info->meminfo_fd + && (info->meminfo_fd = open(MEMINFO_FILE, O_RDONLY)) == -1) return -errno; - } - if (lseek(info->meminfo_fd, 0L, SEEK_SET) == -1) { + + if (lseek(info->meminfo_fd, 0L, SEEK_SET) == -1) return -errno; - } + for (;;) { if ((size = read(info->meminfo_fd, buf, sizeof(buf)-1)) < 0) { if (errno == EINTR || errno == EAGAIN) @@ -149,51 +624,153 @@ PROCPS_EXPORT int procps_meminfo_read ( switch (*head) { case 'A': if (0 == strcmp(head, "Active:")) - valptr = &(info->data.active); + valptr = &(mHr(Active)); + else + if (0 == strcmp(head, "Active(anon):")) + valptr = &(mHr(Active_anon)); + else + if (0 == strcmp(head, "Active(file):")) + valptr = &(mHr(Active_file)); + else + if (0 == strcmp(head, "AnonHugePages:")) + valptr = &(mHr(AnonHugePages)); + else + if (0 == strcmp(head, "AnonPages:")) + valptr = &(mHr(AnonPages)); break; case 'B': + if (0 == strcmp(head, "Bounce:")) + valptr = &(mHr(Bounce)); + else if (0 == strcmp(head, "Buffers:")) - valptr = &(info->data.buffers); + valptr = &(mHr(Buffers)); break; case 'C': if (0 == strcmp(head, "Cached:")) - valptr = &(info->data.cached); + valptr = &(mHr(Cached)); + else + if (0 == strcmp(head, "CommitLimit:")) + valptr = &(mHr(CommitLimit)); + else + if (0 == strcmp(head, "Committed_AS:")) + valptr = &(mHr(Committed_AS)); + break; + case 'D': + if (0 == strcmp(head, "Dirty:")) + valptr = &(mHr(Dirty)); break; case 'H': + if (0 == strcmp(head, "HardwareCorrupted:")) + valptr = &(mHr(HardwareCorrupted)); + else if (0 == strcmp(head, "HighFree:")) - valptr = &(info->data.high_free); - else if (0 == strcmp(head, "HighTotal:")) - valptr = &(info->data.high_total); + valptr = &(mHr(HighFree)); + else + if (0 == strcmp(head, "HighTotal:")) + valptr = &(mHr(HighTotal)); + else + if (0 == strcmp(head, "HugePages_Free:")) + valptr = &(mHr(HugePages_Free)); + else + if (0 == strcmp(head, "HugePages_Rsvd:")) + valptr = &(mHr(HugePages_Rsvd)); + else + if (0 == strcmp(head, "HugePages_Surp:")) + valptr = &(mHr(HugePages_Surp)); + else + if (0 == strcmp(head, "HugePages_Total:")) + valptr = &(mHr(HugePages_Total)); + else + if (0 == strcmp(head, "Hugepagesize:")) + valptr = &(mHr(Hugepagesize)); break; case 'I': if (0 == strcmp(head, "Inactive:")) - valptr = &(info->data.inactive); + valptr = &(mHr(Inactive)); + else + if (0 == strcmp(head, "Inactive(anon):")) + valptr = &(mHr(Inactive_anon)); + else + if (0 == strcmp(head, "Inactive(file):")) + valptr = &(mHr(Inactive_file)); + break; + case 'K': + if (0 == strcmp(head, "KernelStack:")) + valptr = &(mHr(KernelStack)); break; case 'L': if (0 == strcmp(head, "LowFree:")) - valptr = &(info->data.low_free); - else if (0 == strcmp(head, "LowTotal:")) - valptr = &(info->data.low_total); + valptr = &(mHr(LowFree)); + else + if (0 == strcmp(head, "LowTotal:")) + valptr = &(mHr(LowTotal)); break; case 'M': + if (0 == strcmp(head, "Mapped:")) + valptr = &(mHr(Mapped)); + else if (0 == strcmp(head, "MemAvailable:")) - valptr = &(info->data.available); - else if (0 == strcmp(head, "MemFree:")) - valptr = &(info->data.free); - else if (0 == strcmp(head, "MemTotal:")) - valptr = &(info->data.total); + valptr = &(mHr(MemAvailable)); + else + if (0 == strcmp(head, "MemFree:")) + valptr = &(mHr(MemFree)); + else + if (0 == strcmp(head, "MemTotal:")) + valptr = &(mHr(MemTotal)); + else + if (0 == strcmp(head, "Mlocked:")) + valptr = &(mHr(Mlocked)); + break; + case 'N': + if (0 == strcmp(head, "NFS_Unstable:")) + valptr = &(mHr(NFS_Unstable)); + break; + case 'P': + if (0 == strcmp(head, "PageTables:")) + valptr = &(mHr(PageTables)); break; case 'S': - if (0 == strcmp(head, "Slab:")) - valptr = &(info->data.slab); if (0 == strcmp(head, "SReclaimable:")) - valptr = &(info->data.slab_reclaimable); - else if (0 == strcmp(head, "SwapFree:")) - valptr = &(info->data.swap_free); - else if (0 == strcmp(head, "SwapTotal:")) - valptr = &(info->data.swap_total); - else if (0 == strcmp(head, "Shmem:")) - valptr = &(info->data.shared); + valptr = &(mHr(SReclaimable)); + else + if (0 == strcmp(head, "SUnreclaim:")) + valptr = &(mHr(SUnreclaim)); + else + if (0 == strcmp(head, "Shmem:")) + valptr = &(mHr(Shmem)); + else + if (0 == strcmp(head, "Slab:")) + valptr = &(mHr(Slab)); + else + if (0 == strcmp(head, "SwapCached:")) + valptr = &(mHr(SwapCached)); + else + if (0 == strcmp(head, "SwapFree:")) + valptr = &(mHr(SwapFree)); + else + if (0 == strcmp(head, "SwapTotal:")) + valptr = &(mHr(SwapTotal)); + break; + case 'U': + if (0 == strcmp(head, "Unevictable:")) + valptr = &(mHr(Unevictable)); + break; + case 'V': + if (0 == strcmp(head, "VmallocChunk:")) + valptr = &(mHr(VmallocChunk)); + else + if (0 == strcmp(head, "VmallocTotal:")) + valptr = &(mHr(VmallocTotal)); + else + if (0 == strcmp(head, "VmallocUsed:")) + valptr = &(mHr(VmallocUsed)); + break; + case 'W': + if (0 == strcmp(head, "Writeback:")) + valptr = &(mHr(Writeback)); + else + if (0 == strcmp(head, "WritebackTmp:")) + valptr = &(mHr(WritebackTmp)); break; default: break; @@ -208,37 +785,145 @@ PROCPS_EXPORT int procps_meminfo_read ( head = tail + 1; } while(tail); - if (0 == info->data.low_total) { - info->data.low_total = info->data.total; - info->data.low_free = info->data.free; - } - if (0 == info->data.available) { - info->data.available = info->data.free; - } - info->data.cached += info->data.slab_reclaimable; - info->data.swap_used = info->data.swap_total - info->data.swap_free; - + if (0 == mHr(MemAvailable)) + mHr(MemAvailable) = mHr(MemFree); /* if 'available' is greater than 'total' or our calculation of mem_used overflows, that's symptomatic of running within a lxc container where such values will be dramatically distorted over those of the host. */ - if (info->data.available > info->data.total) - info->data.available = info->data.free; - mem_used = info->data.total - info->data.free - info->data.cached - info->data.buffers; + if (mHr(MemAvailable) > mHr(MemTotal)) + mHr(MemAvailable) = mHr(MemFree); + + mem_used = mHr(MemTotal) - mHr(MemFree) - mHr(Cached) - mHr(Buffers); if (mem_used < 0) - mem_used = info->data.total - info->data.free; - info->data.used = (unsigned long)mem_used; + mem_used = mHr(MemTotal) - mHr(MemFree); + mHr(derived_mem_used) = (unsigned long)mem_used; + + if (mHr(HighFree) < mHr(HighTotal)) + mHr(derived_mem_hi_used) = mHr(HighTotal) - mHr(HighFree); + + mHr(Cached) += mHr(SReclaimable); + + if (0 == mHr(LowTotal)) { + mHr(LowTotal) = mHr(MemTotal); + mHr(LowFree) = mHr(MemFree); + } + if (mHr(LowFree) < mHr(LowTotal)) + mHr(derived_mem_lo_used) = mHr(LowTotal) - mHr(LowFree); + + if (mHr(SwapFree) < mHr(SwapTotal)) + mHr(derived_swap_used) = mHr(SwapTotal) - mHr(SwapFree); + + // let's not distort the deltas the first time thru ... + if (!info->meminfo_was_read) + memcpy(&info->mem_hist.old, &info->mem_hist.new, sizeof(struct meminfo_data)); + info->meminfo_was_read = 1; return 0; -} + #undef mHr +} // end: read_meminfo_failed + + +/* + * stacks_alloc(): + * + * Allocate and initialize one or more stacks each of which is anchored in an + * associated meminfo_stack 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 *stacks_alloc ( + struct procps_meminfo *info, + int maxstacks) +{ + struct stacks_extent *p_blob; + struct meminfo_stack **p_vect; + struct meminfo_stack *p_head; + size_t vect_size, head_size, list_size, blob_size; + void *v_head, *v_list; + int i; + + if (info == NULL || info->items == NULL) + return NULL; + if (maxstacks < 1) + return NULL; + + vect_size = sizeof(void *) * maxstacks; // size of the addr vectors | + vect_size += sizeof(void *); // plus NULL addr delimiter | + head_size = sizeof(struct meminfo_stack); // size of that head struct | + list_size = sizeof(struct meminfo_result)*info->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 a single blob, facilitating a later free(). | + as a minimum, it is important that the result structures themselves always be | + contiguous for every stack since they are accessed through relative position. | */ + if (NULL == (p_blob = calloc(1, blob_size))) + return NULL; + + p_blob->next = info->extents; // push this extent onto... | + info->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 meminfo_stack *)v_head; + p_head->head = itemize_stack((struct meminfo_result *)v_list, info->numitems, info->items); + p_blob->stacks[i] = p_head; + v_list += list_size; + v_head += head_size; + } + p_blob->ext_numstacks = maxstacks; + return p_blob; +} // end: stacks_alloc + + +// ___ Public Functions ||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +/* + * procps_meminfo_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 pointer to a new meminfo struct + */ +PROCPS_EXPORT int procps_meminfo_new ( + struct procps_meminfo **info) +{ + struct procps_meminfo *p; + + if (info == NULL || *info != NULL) + return -EINVAL; + if (!(p = calloc(1, sizeof(struct procps_meminfo)))) + return -ENOMEM; + + p->refcount = 1; + p->meminfo_fd = -1; + + *info = p; + return 0; +} // end: procps_meminfo_new + PROCPS_EXPORT int procps_meminfo_ref ( struct procps_meminfo *info) { if (info == NULL) return -EINVAL; + info->refcount++; return info->refcount; -} +} // end: procps_meminfo_ref + PROCPS_EXPORT int procps_meminfo_unref ( struct procps_meminfo **info) @@ -246,321 +931,84 @@ PROCPS_EXPORT int procps_meminfo_unref ( if (info == NULL || *info == NULL) return -EINVAL; (*info)->refcount--; + if ((*info)->refcount == 0) { - if ((*info)->stacked) { - do { - struct stacks_anchor *p = (*info)->stacked; - (*info)->stacked = (*info)->stacked->next; - free(p); - } while((*info)->stacked); - } + if ((*info)->extents) + extents_free_all((*info)); + if ((*info)->items) + free((*info)->items); free(*info); *info = NULL; return 0; } return (*info)->refcount; -} +} // end: procps_meminfo_unref -/* - * Accessor functions - */ -PROCPS_EXPORT unsigned long procps_meminfo_get ( + +PROCPS_EXPORT signed long procps_meminfo_get ( struct procps_meminfo *info, enum meminfo_item item) { - switch (item) { - case PROCPS_MEM_ACTIVE: - return info->data.active; - case PROCPS_MEM_INACTIVE: - return info->data.inactive; - case PROCPS_MEMHI_FREE: - return info->data.high_free; - case PROCPS_MEMHI_TOTAL: - return info->data.high_total; - case PROCPS_MEMHI_USED: - if (info->data.high_free > info->data.high_total) - return 0; - return info->data.high_total - info->data.high_free; - case PROCPS_MEMLO_FREE: - return info->data.low_free; - case PROCPS_MEMLO_TOTAL: - return info->data.low_total; - case PROCPS_MEMLO_USED: - if (info->data.low_free > info->data.low_total) - return 0; - return info->data.low_total - info->data.low_free; - case PROCPS_MEM_AVAILABLE: - return info->data.available; - case PROCPS_MEM_BUFFERS: - return info->data.buffers; - case PROCPS_MEM_CACHED: - return info->data.cached; - case PROCPS_MEM_FREE: - return info->data.free; - case PROCPS_MEM_SHARED: - return info->data.shared; - case PROCPS_MEM_TOTAL: - return info->data.total; - case PROCPS_MEM_USED: - return info->data.used; - case PROCPS_SWAP_FREE: - return info->data.swap_free; - case PROCPS_SWAP_TOTAL: - return info->data.swap_total; - case PROCPS_SWAP_USED: - if (info->data.swap_free > info->data.swap_total) - return 0; - return info->data.swap_total - info->data.swap_free; - default: - return 0; - } -} - -PROCPS_EXPORT int procps_meminfo_getstack ( - struct procps_meminfo *info, - struct meminfo_result *these) -{ - if (info == NULL || these == NULL) - return -EINVAL; - - for (;;) { - switch (these->item) { - case PROCPS_MEM_ACTIVE: - these->result.ul_int = info->data.active; - break; - case PROCPS_MEM_INACTIVE: - these->result.ul_int = info->data.inactive; - break; - case PROCPS_MEMHI_FREE: - these->result.ul_int = info->data.high_free; - break; - case PROCPS_MEMHI_TOTAL: - these->result.ul_int = info->data.high_total; - break; - case PROCPS_MEMHI_USED: - if (info->data.high_free > info->data.high_total) - these->result.ul_int = 0; - else - these->result.ul_int = info->data.high_total - info->data.high_free; - break; - case PROCPS_MEMLO_FREE: - these->result.ul_int = info->data.low_free; - break; - case PROCPS_MEMLO_TOTAL: - these->result.ul_int = info->data.low_total; - break; - case PROCPS_MEMLO_USED: - if (info->data.low_free > info->data.low_total) - these->result.ul_int = 0; - else - these->result.ul_int = info->data.low_total - info->data.low_free; - break; - case PROCPS_MEM_AVAILABLE: - these->result.ul_int = info->data.available; - break; - case PROCPS_MEM_BUFFERS: - these->result.ul_int = info->data.buffers; - break; - case PROCPS_MEM_CACHED: - these->result.ul_int = info->data.cached; - break; - case PROCPS_MEM_FREE: - these->result.ul_int = info->data.free; - break; - case PROCPS_MEM_SHARED: - these->result.ul_int = info->data.shared; - break; - case PROCPS_MEM_TOTAL: - these->result.ul_int = info->data.total; - break; - case PROCPS_MEM_USED: - these->result.ul_int = info->data.used; - break; - case PROCPS_SWAP_FREE: - these->result.ul_int = info->data.swap_free; - break; - case PROCPS_SWAP_TOTAL: - these->result.ul_int = info->data.swap_total; - break; - case PROCPS_SWAP_USED: - if (info->data.swap_free > info->data.swap_total) - these->result.ul_int = 0; - else - these->result.ul_int = info->data.swap_total - info->data.swap_free; - break; - case PROCPS_MEM_noop: - // don't disturb potential user data in the result struct - break; - case PROCPS_MEM_stack_end: - return 0; - default: - return -EINVAL; - } - ++these; - } -} - -PROCPS_EXPORT int procps_meminfo_stack_fill ( - struct procps_meminfo *info, - struct meminfo_stack *stack) -{ + static time_t sav_secs; + time_t cur_secs; int rc; - if (info == NULL || stack == NULL || stack->head == NULL) - return -EINVAL; - if ((rc = procps_meminfo_read(info)) < 0) - return rc; - - return procps_meminfo_getstack(info, stack->head); -} - -static void stacks_validate (struct meminfo_stack **v, const char *who) -{ -#if 0 - #include - 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 meminfo_stack *h = v[x]; - struct meminfo_result *r = h->head; - fprintf(stderr, "%s: vector[%02d] = %p", __func__, x, h); - i = 0; - for (i = 0; r->item < PROCPS_MEM_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 meminfo_result) * t); - fprintf(stderr, "%s: sizeof(struct meminfo_stack) = %2d\n", __func__, (int)sizeof(struct meminfo_stack)); - fprintf(stderr, "%s: sizeof(struct meminfo_result) = %2d\n", __func__, (int)sizeof(struct meminfo_result)); - fputc('\n', stderr); - return; -#endif -} - -static struct meminfo_result *stack_make ( - struct meminfo_result *p, - int maxitems, - enum meminfo_item *items) -{ - struct meminfo_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; + /* we will NOT read the meminfo 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 ((rc = read_meminfo_failed(info))) + return rc; + sav_secs = cur_secs; } - return p_sav; -} - -static int stack_items_valid ( - int maxitems, - enum meminfo_item *items) -{ - int i; - - for (i = 0; i < maxitems; i++) { - if (items[i] < 0) - return 0; - if (items[i] > PROCPS_MEM_stack_end) - return 0; - } - if (items[maxitems -1] != PROCPS_MEM_stack_end) - return 0; - return 1; -} + if (item < PROCPS_MEMINFO_logical_end) + return Item_table[item].getsfunc(info); + return -EINVAL; +} // end: procps_meminfo_get -/* - * procps_meminfo_stacks_alloc(): +/* procps_meminfo_select(): * - * A local copy of code borrowed from slab.c to support the public version - * representing a single stack. Currently there is no conceivable need - * for multiple stacks in the 'memory' arena. + * Harvest all the requested MEM and/or SWAP information then return + * it in a results stack. + * + * Returns: pointer to a meminfo_stack struct on success, NULL on error. */ -static struct meminfo_stack **procps_meminfo_stacks_alloc ( +PROCPS_EXPORT struct meminfo_stack *procps_meminfo_select ( struct procps_meminfo *info, - int maxstacks, - int maxitems, - enum meminfo_item *items) + enum meminfo_item *items, + int numitems) { - struct stacks_anchor *p_blob; - struct stack_vectors *p_vect; - struct meminfo_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)) + if (items_check_failed(numitems, 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 meminfo_stack); // a head struct - list_size = sizeof(struct meminfo_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 - 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->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 meminfo_stack *)v_head; - p_head->head = stack_make((struct meminfo_result *)v_list, maxitems, items); - p_blob->vectors->heads[i] = p_head; - v_list += list_size; - v_head += head_size; + /* is this the first time or have things changed since we were last called? + if so, gotta' redo all of our stacks stuff ... */ + if (info->numitems != numitems + 1 + || memcmp(info->items, items, sizeof(enum meminfo_item) * numitems)) { + // allow for our PROCPS_MEMINFO_logical_end + if (!(info->items = realloc(info->items, sizeof(enum meminfo_item) * (numitems + 1)))) + return -ENOMEM; + memcpy(info->items, items, sizeof(enum meminfo_item) * numitems); + info->items[numitems] = PROCPS_MEMINFO_logical_end; + info->numitems = numitems + 1; + if (info->extents) + extents_free_all(info); } - p_blob->depth = maxstacks; - stacks_validate(p_blob->vectors->heads, __func__); - return p_blob->vectors->heads; -} + if (!info->extents + && !(info->extents = stacks_alloc(info, 1))) + return NULL; -/* - * procps_meminfo_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 meminfo_stack *procps_meminfo_stack_alloc ( - struct procps_meminfo *info, - int maxitems, - enum meminfo_item *items) -{ - struct meminfo_stack **v; + if (info->dirty_stacks) + cleanup_stacks_all(info); - v = procps_meminfo_stacks_alloc(info, 1, maxitems, items); - if (!v) + if (read_meminfo_failed(info)) return NULL; - stacks_validate(v, __func__); - return v[0]; -} + assign_results(info->extents->stacks[0], &info->mem_hist); + info->dirty_stacks = 1; + + return info->extents->stacks[0]; +} // end: procps_meminfo_select diff --git a/proc/meminfo.h b/proc/meminfo.h index 7300f70a..20f6e0ab 100644 --- a/proc/meminfo.h +++ b/proc/meminfo.h @@ -1,9 +1,5 @@ /* - * meminfo - Memory statistics part of procps - * - * Copyright (C) 1992-1998 by Michael K. Johnson - * Copyright (C) 1998-2003 Albert Cahalan - * Copyright (C) 2015 Craig Small + * libprocps - Library to read proc filesystem * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,31 +19,106 @@ #ifndef PROC_MEMINFO_H #define PROC_MEMINFO_H -#include - __BEGIN_DECLS enum meminfo_item { - PROCPS_MEMHI_FREE, // ul_int - PROCPS_MEMHI_TOTAL, // ul_int - PROCPS_MEMHI_USED, // ul_int - PROCPS_MEMLO_FREE, // ul_int - PROCPS_MEMLO_TOTAL, // ul_int - PROCPS_MEMLO_USED, // ul_int - PROCPS_MEM_ACTIVE, // ul_int - PROCPS_MEM_AVAILABLE, // ul_int - PROCPS_MEM_BUFFERS, // ul_int - PROCPS_MEM_CACHED, // ul_int - PROCPS_MEM_FREE, // ul_int - PROCPS_MEM_INACTIVE, // ul_int - PROCPS_MEM_SHARED, // ul_int - PROCPS_MEM_TOTAL, // ul_int - PROCPS_MEM_USED, // ul_int - PROCPS_SWAP_FREE, // ul_int - PROCPS_SWAP_TOTAL, // ul_int - PROCPS_SWAP_USED, // ul_int - PROCPS_MEM_noop, // n/a - PROCPS_MEM_stack_end // n/a + PROCPS_MEMINFO_noop, // n/a ( never altered ) + PROCPS_MEMINFO_extra, // n/a ( reset to zero ) + /* + note: all of the following values are exressed as KiB + */ + PROCPS_MEMINFO_MEM_ACTIVE, // ul_int + PROCPS_MEMINFO_MEM_ACTIVE_ANON, // ul_int + PROCPS_MEMINFO_MEM_ACTIVE_FILE, // ul_int + PROCPS_MEMINFO_MEM_ANON, // ul_int + PROCPS_MEMINFO_MEM_AVAILABLE, // ul_int + PROCPS_MEMINFO_MEM_BOUNCE, // ul_int + PROCPS_MEMINFO_MEM_BUFFERS, // ul_int + PROCPS_MEMINFO_MEM_CACHED, // ul_int + PROCPS_MEMINFO_MEM_COMMIT_LIMIT, // ul_int + PROCPS_MEMINFO_MEM_COMMITTED_AS, // ul_int + PROCPS_MEMINFO_MEM_HARD_CORRUPTED, // ul_int + PROCPS_MEMINFO_MEM_DIRTY, // ul_int + PROCPS_MEMINFO_MEM_FREE, // ul_int + PROCPS_MEMINFO_MEM_HUGE_ANON, // ul_int + PROCPS_MEMINFO_MEM_HUGE_FREE, // ul_int + PROCPS_MEMINFO_MEM_HUGE_RSVD, // ul_int + PROCPS_MEMINFO_MEM_HUGE_SIZE, // ul_int + PROCPS_MEMINFO_MEM_HUGE_SURPLUS, // ul_int + PROCPS_MEMINFO_MEM_HUGE_TOTAL, // ul_int + PROCPS_MEMINFO_MEM_INACTIVE, // ul_int + PROCPS_MEMINFO_MEM_INACTIVE_ANON, // ul_int + PROCPS_MEMINFO_MEM_INACTIVE_FILE, // ul_int + PROCPS_MEMINFO_MEM_KERNEL_STACK, // ul_int + PROCPS_MEMINFO_MEM_LOCKED, // ul_int + PROCPS_MEMINFO_MEM_MAPPED, // ul_int + PROCPS_MEMINFO_MEM_NFS_UNSTABLE, // ul_int + PROCPS_MEMINFO_MEM_PAGE_TABLES, // ul_int + PROCPS_MEMINFO_MEM_SHARED, // ul_int + PROCPS_MEMINFO_MEM_SLAB, // ul_int + PROCPS_MEMINFO_MEM_SLAB_RECLAIM, // ul_int + PROCPS_MEMINFO_MEM_SLAB_UNRECLAIM, // ul_int + PROCPS_MEMINFO_MEM_TOTAL, // ul_int + PROCPS_MEMINFO_MEM_UNEVICTABLE, // ul_int + PROCPS_MEMINFO_MEM_USED, // ul_int + PROCPS_MEMINFO_MEM_VM_ALLOC_CHUNK, // ul_int + PROCPS_MEMINFO_MEM_VM_ALLOC_TOTAL, // ull_int + PROCPS_MEMINFO_MEM_VM_ALLOC_USED, // ul_int + PROCPS_MEMINFO_MEM_WRITEBACK, // ul_int + PROCPS_MEMINFO_MEM_WRITEBACK_TMP, // ul_int + + PROCPS_MEMINFO_DELTA_ACTIVE, // s_int + PROCPS_MEMINFO_DELTA_ACTIVE_ANON, // s_int + PROCPS_MEMINFO_DELTA_ACTIVE_FILE, // s_int + PROCPS_MEMINFO_DELTA_ANON, // s_int + PROCPS_MEMINFO_DELTA_AVAILABLE, // s_int + PROCPS_MEMINFO_DELTA_BOUNCE, // s_int + PROCPS_MEMINFO_DELTA_BUFFERS, // s_int + PROCPS_MEMINFO_DELTA_CACHED, // s_int + PROCPS_MEMINFO_DELTA_COMMIT_LIMIT, // s_int + PROCPS_MEMINFO_DELTA_COMMITTED_AS, // s_int + PROCPS_MEMINFO_DELTA_HARD_CORRUPTED, // s_int + PROCPS_MEMINFO_DELTA_DIRTY, // s_int + PROCPS_MEMINFO_DELTA_FREE, // s_int + PROCPS_MEMINFO_DELTA_HUGE_ANON, // s_int + PROCPS_MEMINFO_DELTA_HUGE_FREE, // s_int + PROCPS_MEMINFO_DELTA_HUGE_RSVD, // s_int + PROCPS_MEMINFO_DELTA_HUGE_SIZE, // s_int + PROCPS_MEMINFO_DELTA_HUGE_SURPLUS, // s_int + PROCPS_MEMINFO_DELTA_HUGE_TOTAL, // s_int + PROCPS_MEMINFO_DELTA_INACTIVE, // s_int + PROCPS_MEMINFO_DELTA_INACTIVE_ANON, // s_int + PROCPS_MEMINFO_DELTA_INACTIVE_FILE, // s_int + PROCPS_MEMINFO_DELTA_KERNEL_STACK, // s_int + PROCPS_MEMINFO_DELTA_LOCKED, // s_int + PROCPS_MEMINFO_DELTA_MAPPED, // s_int + PROCPS_MEMINFO_DELTA_NFS_UNSTABLE, // s_int + PROCPS_MEMINFO_DELTA_PAGE_TABLES, // s_int + PROCPS_MEMINFO_DELTA_SHARED, // s_int + PROCPS_MEMINFO_DELTA_SLAB, // s_int + PROCPS_MEMINFO_DELTA_SLAB_RECLAIM, // s_int + PROCPS_MEMINFO_DELTA_SLAB_UNRECLAIM, // s_int + PROCPS_MEMINFO_DELTA_TOTAL, // s_int + PROCPS_MEMINFO_DELTA_UNEVICTABLE, // s_int + PROCPS_MEMINFO_DELTA_USED, // s_int + PROCPS_MEMINFO_DELTA_VM_ALLOC_CHUNK, // s_int + PROCPS_MEMINFO_DELTA_VM_ALLOC_TOTAL, // sl_int + PROCPS_MEMINFO_DELTA_VM_ALLOC_USED, // s_int + PROCPS_MEMINFO_DELTA_WRITEBACK, // s_int + PROCPS_MEMINFO_DELTA_WRITEBACK_TMP, // s_int + + PROCPS_MEMINFO_MEMHI_FREE, // ul_int + PROCPS_MEMINFO_MEMHI_TOTAL, // ul_int + PROCPS_MEMINFO_MEMHI_USED, // ul_int + + PROCPS_MEMINFO_MEMLO_FREE, // ul_int + PROCPS_MEMINFO_MEMLO_TOTAL, // ul_int + PROCPS_MEMINFO_MEMLO_USED, // ul_int + + PROCPS_MEMINFO_SWAP_CACHED, // ul_int + PROCPS_MEMINFO_SWAP_FREE, // ul_int + PROCPS_MEMINFO_SWAP_TOTAL, // ul_int + PROCPS_MEMINFO_SWAP_USED // ul_int }; struct procps_meminfo; @@ -55,7 +126,10 @@ struct procps_meminfo; struct meminfo_result { enum meminfo_item item; union { - unsigned long ul_int; + signed int s_int; + signed long sl_int; + unsigned long ul_int; + unsigned long long ull_int; } result; }; @@ -64,28 +138,25 @@ struct meminfo_stack { }; -int procps_meminfo_new (struct procps_meminfo **info); -int procps_meminfo_read (struct procps_meminfo *info); +#define PROCPS_MEMINFO_VAL(rel_enum,type,stack) \ + stack -> head [ rel_enum ] . result . type -int procps_meminfo_ref (struct procps_meminfo *info); + +struct procps_meminfo; + +int procps_meminfo_new (struct procps_meminfo **info); +int procps_meminfo_ref (struct procps_meminfo *info); int procps_meminfo_unref (struct procps_meminfo **info); -unsigned long procps_meminfo_get ( +signed long procps_meminfo_get ( struct procps_meminfo *info, enum meminfo_item item); -int procps_meminfo_getstack ( +struct meminfo_stack *procps_meminfo_select ( struct procps_meminfo *info, - struct meminfo_result *these); - -int procps_meminfo_stack_fill ( - struct procps_meminfo *info, - struct meminfo_stack *stack); - -struct meminfo_stack *procps_meminfo_stack_alloc ( - struct procps_meminfo *info, - int maxitems, - enum meminfo_item *items); + enum meminfo_item *items, + int numitems); __END_DECLS + #endif