/* * This file is part of Xfce (https://gitlab.xfce.org). * * Copyright (c) 2003 Riccardo Persichetti * Copyright (c) 2010 Florian Rivoal * Copyright (c) 2022 Jan Ziak <0xe2.0x9a.0x9b@xfce.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "memswap.h" #include "zram.h" #if defined(__linux__) || defined(__FreeBSD_kernel__) #include #include #include #include #include #include #include #define MEMINFOBUFSIZE (2 * 1024) static char MemInfoBuf[MEMINFOBUFSIZE]; static unsigned long MTotal = 0; static unsigned long MFree = 0; static unsigned long MBuffers = 0; static unsigned long MCached = 0; static unsigned long MAvail = 0; static unsigned long MUsed = 0; static unsigned long STotal = 0; static unsigned long SFree = 0; static unsigned long SUsed = 0; gint read_memswap(gulong *mem, gulong *swap, gulong *MT, gulong *MU, gulong *ST, gulong *SU) { char *b_MTotal, *b_MFree, *b_MBuffers, *b_MCached, *b_MAvail, *b_STotal, *b_SFree; const char *filepath = "/proc/meminfo"; int fd; if ((fd = open(filepath, O_RDONLY)) < 0) { g_warning ("Cannot open '%s'", filepath); return -1; } ssize_t n = read(fd, MemInfoBuf, MEMINFOBUFSIZE - 1); close(fd); if (n < 0) { g_warning ("Cannot read '%s'", filepath); return -1; } if (n == MEMINFOBUFSIZE - 1) { g_warning ("Internal buffer too small to read '%s'", filepath); return -1; } MemInfoBuf[n] = '\0'; b_MTotal = strstr(MemInfoBuf, "MemTotal"); if (!b_MTotal || !sscanf(b_MTotal + strlen("MemTotal"), ": %lu", &MTotal)) return -1; b_MFree = strstr(MemInfoBuf, "MemFree"); if (!b_MFree || !sscanf(b_MFree + strlen("MemFree"), ": %lu", &MFree)) return -1; b_MBuffers = strstr(MemInfoBuf, "Buffers"); if (!b_MBuffers || !sscanf(b_MBuffers + strlen("Buffers"), ": %lu", &MBuffers)) return -1; b_MCached = strstr(MemInfoBuf, "Cached"); if (!b_MCached || !sscanf(b_MCached + strlen("Cached"), ": %lu", &MCached)) return -1; /* In Linux 3.14+, use MemAvailable instead */ b_MAvail = strstr(MemInfoBuf, "MemAvailable"); if (b_MAvail && sscanf(b_MAvail + strlen("MemAvailable"), ": %lu", &MAvail)) { MFree = MAvail; MBuffers = 0; MCached = 0; } b_STotal = strstr(MemInfoBuf, "SwapTotal"); if (!b_STotal || !sscanf(b_STotal + strlen("SwapTotal"), ": %lu", &STotal)) return -1; b_SFree = strstr(MemInfoBuf, "SwapFree"); if (!b_SFree || !sscanf(b_SFree + strlen("SwapFree"), ": %lu", &SFree)) return -1; MFree += MCached + MBuffers; MUsed = MTotal - MFree; /* * MUsed deliberately still includes the RAM held by zram: that memory is * in fact occupied and unavailable to anyone. Whether to subtract it is a * presentation choice, made by the caller through the "exclude zram from * memory usage" setting - see update_monitors() in systemload.cc. */ /* * SwapTotal/SwapFree lump zram together with disk swap, and for zram they * report the UNCOMPRESSED size - a figure that corresponds to nothing * physical. Ask /proc/swaps instead and count only the disk-backed areas: * anything landing there is a genuine spill out of RAM. * * If /proc/swaps is unreadable, fall back to the meminfo numbers. */ { gulong DTotal = 0, DUsed = 0; if (read_disk_swap(&DTotal, &DUsed) == 0) { STotal = DTotal; SUsed = DUsed; } else { SUsed = STotal - SFree; } } *mem = MUsed * 100 / MTotal; if(STotal) *swap = SUsed * 100 / STotal; else *swap = 0; *MT = MTotal; *MU = MUsed; *ST = STotal; *SU = SUsed; return 0; } #elif defined(__FreeBSD__) || defined(__DragonFly__) /* * This is inspired by /usr/src/usr.bin/top/machine.c * * Adapted by Thorsten Greiner * * Original authors: Christos Zoulas * Steven Wallace * Wolfram Schneider * Thomas Moestl */ /* * FreeBSD defines MAX and MIN in sys/param.h, so undef the glib macros first */ #ifdef MAX #undef MAX #endif #ifdef MIN #undef MIN #endif #include #include #include #include #include #include #include #include #include #include #include #include #define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) static int getsysctl (char *name, void *ptr, size_t len) { size_t nlen = len; if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { return -1; } if (nlen != len) { return -1; } return 0; } static kvm_t *kd = NULL; static int swapmode(int *retavail, int *retfree) { int n; int pagesize = getpagesize(); struct kvm_swap swapary[1]; static int kd_init = TRUE; if(kd_init) { kd_init = FALSE; if ((kd = kvm_open("/dev/null", "/dev/null", "/dev/null", O_RDONLY, "kvm_open")) == NULL) { g_warning("Cannot read kvm."); return -1; } } if(kd == NULL) { return -1; } *retavail = 0; *retfree = 0; #define CONVERT(v) ((quad_t)(v) * pagesize / 1024) n = kvm_getswapinfo(kd, swapary, 1, 0); if (n < 0 || swapary[0].ksw_total == 0) return(0); *retavail = CONVERT(swapary[0].ksw_total); *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used); n = (int)((double)swapary[0].ksw_used * 100.0 / (double)swapary[0].ksw_total); return(n); } gint read_memswap(gulong *mem, gulong *swap, gulong *MT, gulong *MU, gulong *ST, gulong *SU) { int total_pages; int free_pages; int inactive_pages; int pagesize = getpagesize(); int swap_avail; int swap_free; if(GETSYSCTL("vm.stats.vm.v_page_count", total_pages)) { g_warning("Cannot read sysctl \"vm.stats.vm.v_page_count\""); return -1; } if(GETSYSCTL("vm.stats.vm.v_free_count", free_pages)) { g_warning("Cannot read sysctl \"vm.stats.vm.v_free_count\""); return -1; } if(GETSYSCTL("vm.stats.vm.v_inactive_count", inactive_pages)) { g_warning("Cannot read sysctl \"vm.stats.vm.v_inactive_count\""); return -1; } *MT = CONVERT(total_pages); *MU = CONVERT(total_pages-free_pages-inactive_pages); *mem = *MU * 100 / *MT; if((*swap = swapmode(&swap_avail, &swap_free)) >= 0) { *ST = swap_avail; *SU = (swap_avail - swap_free); } else { *swap = 0; *ST = 0; *SU = 0; } return 0; } #elif defined(__NetBSD__) /* * NetBSD defines MAX and MIN in sys/param.h, so undef the glib macros first */ #ifdef MAX #undef MAX #endif #ifdef MIN #undef MIN #endif #include #include #include #include #include #include #include #include #include #include /* NetBSD-current post 1.6U uses swapctl */ #if __NetBSD_Version__ > 106210000 #include #elif __NetBSD_Version__ >= 105010000 /* Everything post 1.5.x uses uvm/uvm_* includes */ #include #else #include #endif static size_t MTotal = 0; static size_t MFree = 0; static size_t MUsed = 0; static size_t STotal = 0; static size_t SFree = 0; static size_t SUsed = 0; gint read_memswap(gulong *mem, gulong *swap, gulong *MT, gulong *MU, gulong *ST, gulong *SU) { int pagesize; size_t len; #define ARRLEN(X) (sizeof(X)/sizeof(X[0])) { static int mib[] = { CTL_HW, HW_PHYSMEM64 }; int64_t x; len = sizeof(x); sysctl(mib, ARRLEN(mib), &x, &len, NULL, 0); MTotal = x >> 10; } { static int mib[] = {CTL_HW, HW_PAGESIZE}; len = sizeof(pagesize); sysctl(mib, ARRLEN(mib), &pagesize, &len, NULL, 0); } #if __NetBSD_Version__ > 106210000 { struct swapent* swap; int nswap, n; STotal = SUsed = SFree = 0; if ((nswap = swapctl(SWAP_NSWAP, NULL, 0)) > 0) { swap = (struct swapent*)malloc(nswap * sizeof(*swap)); if (swapctl(SWAP_STATS, (void*)swap, nswap) == nswap) { for (n = 0; n < nswap; n++) { STotal += swap[n].se_nblks; SUsed += swap[n].se_inuse; } STotal = dbtob(STotal >> 10); SUsed = dbtob(SUsed >> 10); SFree = STotal - SUsed; } free(swap); } } #else { struct uvmexp x; static int mib[] = { CTL_VM, VM_UVMEXP }; len = sizeof(x); STotal = SUsed = SFree = -1; pagesize = 1; if (-1 < sysctl(mib, ARRLEN(mib), &x, &len, NULL, 0)) { //pagesize = x.pagesize; STotal = (pagesize*x.swpages) >> 10; SUsed = (pagesize*x.swpginuse) >> 10; SFree = STotal - SUsed; } } #endif { static int mib[]={ CTL_VM, VM_METER }; struct vmtotal x; len = sizeof(x); MFree = MUsed = -1; if (sysctl(mib, ARRLEN(mib), &x, &len, NULL, 0) > -1) { MFree = (x.t_free * pagesize) >> 10; MUsed = (x.t_rm * pagesize) >> 10; } } *mem = MUsed * 100 / MTotal; if(STotal) *swap = SUsed * 100 / STotal; else *swap = 0; *MT = MTotal; *MU = MUsed; *ST = STotal; *SU = SUsed; return 0; } #elif defined(__OpenBSD__) /* * OpenBSD defines MAX and MIN in sys/param.h, so undef the glib macros first */ #ifdef MAX #undef MAX #endif #ifdef MIN #undef MIN #endif #include #include #include #include #include #include #include #include #include #include #include static size_t MTotal = 0; static size_t MFree = 0; static size_t MUsed = 0; static size_t STotal = 0; static size_t SFree = 0; static size_t SUsed = 0; gint read_memswap(gulong *mem, gulong *swap, gulong *MT, gulong *MU, gulong *ST, gulong *SU) { long pagesize; size_t len; #define ARRLEN(X) (sizeof(X)/sizeof(X[0])) { static int mib[] = { CTL_HW, HW_PHYSMEM64 }; int64_t x; len = sizeof(x); sysctl(mib, ARRLEN(mib), &x, &len, NULL, 0); MTotal = x >> 10; } { struct uvmexp x; static int mib[] = { CTL_VM, VM_UVMEXP }; len = sizeof(x); STotal = SUsed = SFree = -1; pagesize = 1; if (-1 < sysctl(mib, ARRLEN(mib), &x, &len, NULL, 0)) { pagesize = x.pagesize; STotal = (pagesize*x.swpages) >> 10; SUsed = (pagesize*x.swpginuse) >> 10; SFree = STotal - SUsed; } } { static int mib[]={ CTL_VM, VM_METER }; struct vmtotal x; len = sizeof(x); MFree = MUsed = -1; if (sysctl(mib, ARRLEN(mib), &x, &len, NULL, 0) > -1) { MFree = (x.t_free * pagesize) >> 10; MUsed = (x.t_rm * pagesize) >> 10; } } *mem = MUsed * 100 / MTotal; if(STotal) *swap = SUsed * 100 / STotal; else *swap = 0; *MT = MTotal; *MU = MUsed; *ST = STotal; *SU = SUsed; return 0; } #elif defined (__sun__) #include #include #include kstat_ctl_t *kc; static size_t MTotal = 0; static size_t MFree = 0; static size_t MUsed = 0; static size_t STotal = 0; static size_t SFree = 0; static size_t SUsed = 0; void mem_init_stats() { kc = kstat_open(); } gint read_memswap(gulong *mem, gulong *swap, gulong *MT, gulong *MU, gulong *ST, gulong *SU) { long pagesize; struct anoninfo swapinfo; kstat_t *ksp; kstat_named_t *knp; pagesize = (long)(sysconf(_SC_PAGESIZE)); /* FIXME use real numbers, not fake data */ if (!kc) { mem_init_stats(); } if (ksp = kstat_lookup(kc, "unix", 0, "system_pages")) { kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "physmem"); MTotal = (pagesize * knp->value.ui64) >> 10; knp = kstat_data_lookup(ksp, "freemem"); MUsed = MTotal - ((pagesize * knp->value.ui64) >> 10); } if (swapctl(SC_AINFO, &swapinfo) == 0) { STotal = (swapinfo.ani_max * pagesize) >> 10; SUsed = ((swapinfo.ani_max - swapinfo.ani_free) * pagesize) >> 10; *swap = SUsed * 100 / STotal; } else { STotal = 0; SUsed = 0; *swap = 0; } *mem = MUsed * 100 / MTotal; *MT = MTotal; *MU = MUsed; *ST = STotal; *SU = SUsed; return 0; } #else #error "Your platform is not yet supported" #endif