/*
- * 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;
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)
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;
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)
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