]> granicus.if.org Git - procps-ng/commitdiff
library: improve/normalize an interface, <MEMINFO> api
authorJim Warner <james.warner@comcast.net>
Wed, 11 May 2016 17:00:00 +0000 (12:00 -0500)
committerCraig Small <csmall@enc.com.au>
Wed, 11 May 2016 21:51:10 +0000 (07:51 +1000)
This represents the refinement of this interface after
the <stat> API was redesigned. We now follow a pattern
of 'get' for single item retrieval & 'select' for when
multiple items are desired, with just 1 function call.

And again following the <stat> lead this interface now
provides for delta values encompassing most items. The
reason I went cuckoo nuts with those deltas is because
they are essentially free. At the cost of a little RAM
and just one memcpy there's no other price to be paid.

Signed-off-by: Jim Warner <james.warner@comcast.net>
proc/libprocps.sym
proc/meminfo.c
proc/meminfo.h

index bdbcf6474b95115ec6d0ef6bae3ef4fec764a7b3..0b90bd3af089513b23365bf5be93195a505e8ebd 100644 (file)
@@ -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;
index d2677e768f98e39537cf0e0ee55d3841b0920e2b..3828d66d6abb174096ddf36731ecba22446a06ca 100644 (file)
@@ -1,9 +1,5 @@
 /*
- * meminfo - Memory statistics part of procps
- *
- * Copyright (C) 1992-1998 by Michael K. Johnson <johnsonm@redhat.com>
- * Copyright (C) 1998-2003 Albert Cahalan
- * Copyright (C) 2015 Craig Small <csmall@enc.com.au>
+ * 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
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <proc/procps-private.h>
 #include <proc/meminfo.h>
-#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;
+    int meminfo_was_read;
+    int dirty_stacks;
+    struct hist_mem mem_hist;
+    int numitems;
+    enum meminfo_item *items;
+    struct stacks_extent *extents;
 };
 
-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;
+// ___ 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;
 
-/*
- * 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)
+#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;
-}
-
-/*
- * Accessor functions
- */
-PROCPS_EXPORT unsigned 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;
+} // end: procps_meminfo_unref
 
-    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 (
+PROCPS_EXPORT signed long procps_meminfo_get (
         struct procps_meminfo *info,
-        struct meminfo_stack *stack)
+        enum meminfo_item item)
 {
+    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 <stdio.h>
-    int i, t, x, n = 0;
-    struct stack_vectors *p = (struct stack_vectors *)v - 1;
-
-    fprintf(stderr, "%s: called by '%s'\n", __func__, who);
-    fprintf(stderr, "%s: owned by %p (whose self = %p)\n", __func__, p->owner, p->owner->self);
-    for (x = 0; v[x]; x++) {
-        struct 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))
-        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)))
+    if (items_check_failed(numitems, items))
         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
index 7300f70a69f38cef4aece76a523acef686d8ead4..20f6e0ab3847c69d5faa63c587b052fc89a7445a 100644 (file)
@@ -1,9 +1,5 @@
 /*
- * meminfo - Memory statistics part of procps
- *
- * Copyright (C) 1992-1998 by Michael K. Johnson <johnsonm@redhat.com>
- * Copyright (C) 1998-2003 Albert Cahalan
- * Copyright (C) 2015 Craig Small <csmall@enc.com.au>
+ * 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
 #ifndef PROC_MEMINFO_H
 #define PROC_MEMINFO_H
 
-#include <features.h>
-
 __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);
-int procps_meminfo_unref (struct procps_meminfo **info);
 
-unsigned long procps_meminfo_get (
-    struct procps_meminfo *info,
-    enum meminfo_item item);
+struct procps_meminfo;
 
-int procps_meminfo_getstack (
-    struct procps_meminfo *info,
-    struct meminfo_result *these);
+int procps_meminfo_new   (struct procps_meminfo **info);
+int procps_meminfo_ref   (struct procps_meminfo  *info);
+int procps_meminfo_unref (struct procps_meminfo **info);
 
-int procps_meminfo_stack_fill (
+signed long procps_meminfo_get (
     struct procps_meminfo *info,
-    struct meminfo_stack *stack);
+    enum meminfo_item item);
 
-struct meminfo_stack *procps_meminfo_stack_alloc (
+struct meminfo_stack *procps_meminfo_select (
     struct procps_meminfo *info,
-    int maxitems,
-    enum meminfo_item *items);
+    enum meminfo_item *items,
+    int numitems);
 
 __END_DECLS
+
 #endif