From 3d497a3760626d398fffc8f4594e8b9658f67d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Wed, 6 Jan 2021 18:11:24 +0100 Subject: [PATCH] Linux: overhaul memory partition Use similar calculation than procps. Show AvailableMemory in text mode. Use total minus available memory instead of manually computed used- memory as fraction part in bar mode (if available). --- MemoryMeter.c | 23 +++++++++- ProcessList.h | 18 +++++--- linux/HugePageMeter.c | 6 +-- linux/LinuxProcessList.c | 94 ++++++++++++++++++++++++---------------- linux/LinuxProcessList.h | 6 ++- linux/Platform.c | 13 +++--- linux/ZramStats.h | 6 +-- 7 files changed, 103 insertions(+), 63 deletions(-) diff --git a/MemoryMeter.c b/MemoryMeter.c index e4754427..dd219bc0 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -7,6 +7,8 @@ in the source distribution for its full text. #include "MemoryMeter.h" +#include + #include "CRT.h" #include "Object.h" #include "Platform.h" @@ -21,9 +23,15 @@ static const int MemoryMeter_attributes[] = { static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) { int written; + + /* available memory is not supported on all platforms */ + this->values[3] = NAN; Platform_setMemoryValues(this); - written = Meter_humanUnit(buffer, this->values[0], size); + /* Do not print available memory in bar mode */ + this->curItems = 3; + + written = Meter_humanUnit(buffer, isnan(this->values[3]) ? this->values[0] : this->total - this->values[3], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); @@ -34,18 +42,29 @@ static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) { static void MemoryMeter_display(const Object* cast, RichString* out) { char buffer[50]; const Meter* this = (const Meter*)cast; + RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); + Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer); + Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:"); RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); + Meter_humanUnit(buffer, this->values[2], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer); + + /* available memory is not supported on all platforms */ + if (!isnan(this->values[3])) { + Meter_humanUnit(buffer, this->values[3], sizeof(buffer)); + RichString_appendAscii(out, CRT_colors[METER_TEXT], " available:"); + RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); + } } const MeterClass MemoryMeter_class = { @@ -56,7 +75,7 @@ const MeterClass MemoryMeter_class = { }, .updateValues = MemoryMeter_updateValues, .defaultMode = BAR_METERMODE, - .maxItems = 3, + .maxItems = 4, .total = 100.0, .attributes = MemoryMeter_attributes, .name = "Memory", diff --git a/ProcessList.h b/ProcessList.h index cb17f0d7..3cd10a4e 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -34,6 +34,9 @@ in the source distribution for its full text. #define MAX_READ 2048 #endif +typedef unsigned long long int memory_t; +#define MEMORY_MAX ULLONG_MAX + typedef struct ProcessList_ { const Settings* settings; @@ -61,14 +64,15 @@ typedef struct ProcessList_ { int userlandThreads; int kernelThreads; - unsigned long long int totalMem; - unsigned long long int usedMem; - unsigned long long int buffersMem; - unsigned long long int cachedMem; + memory_t totalMem; + memory_t usedMem; + memory_t buffersMem; + memory_t cachedMem; + memory_t availableMem; - unsigned long long int totalSwap; - unsigned long long int usedSwap; - unsigned long long int cachedSwap; + memory_t totalSwap; + memory_t usedSwap; + memory_t cachedSwap; int cpuCount; diff --git a/linux/HugePageMeter.c b/linux/HugePageMeter.c index 7222c87a..242d2857 100644 --- a/linux/HugePageMeter.c +++ b/linux/HugePageMeter.c @@ -35,7 +35,7 @@ static void HugePageMeter_updateValues(Meter* this, char* buffer, size_t size) { assert(ARRAYSIZE(HugePageMeter_labels) == HTOP_HUGEPAGE_COUNT); int written; - unsigned long long int usedTotal = 0; + memory_t usedTotal = 0; unsigned nextUsed = 0; const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; @@ -47,8 +47,8 @@ static void HugePageMeter_updateValues(Meter* this, char* buffer, size_t size) { HugePageMeter_active_labels[i] = NULL; } for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { - unsigned long long int value = lpl->usedHugePageMem[i]; - if (value != ULLONG_MAX) { + memory_t value = lpl->usedHugePageMem[i]; + if (value != MEMORY_MAX) { this->values[nextUsed] = value; usedTotal += value; HugePageMeter_active_labels[nextUsed] = HugePageMeter_labels[i]; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 039308f5..3a0feae0 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -1525,64 +1525,82 @@ errorReadingProcess: } static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { - unsigned long long int freeMem = 0; - unsigned long long int swapFree = 0; - unsigned long long int shmem = 0; - unsigned long long int sreclaimable = 0; + memory_t availableMem = 0; + memory_t freeMem = 0; + memory_t totalMem = 0; + memory_t buffersMem = 0; + memory_t cachedMem = 0; + memory_t swapTotalMem = 0; + memory_t swapCacheMem = 0; + memory_t swapFreeMem = 0; + memory_t sreclaimableMem = 0; FILE* file = fopen(PROCMEMINFOFILE, "r"); - if (file == NULL) { + if (!file) CRT_fatalError("Cannot open " PROCMEMINFOFILE); - } - char buffer[128]; - while (fgets(buffer, 128, file)) { - #define tryRead(label, variable) \ - if (String_startsWith(buffer, label)) { \ - sscanf(buffer + strlen(label), " %32llu kB", variable); \ - break; \ + char buffer[128]; + while (fgets(buffer, sizeof(buffer), file)) { + + #define tryRead(label, variable) \ + if (String_startsWith(buffer, label)) { \ + memory_t parsed_; \ + if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \ + variable = parsed_; \ + } \ + break; \ } switch (buffer[0]) { case 'M': - tryRead("MemTotal:", &this->totalMem); - tryRead("MemFree:", &freeMem); + tryRead("MemAvailable:", availableMem); + tryRead("MemFree:", freeMem); + tryRead("MemTotal:", totalMem); break; case 'B': - tryRead("Buffers:", &this->buffersMem); + tryRead("Buffers:", buffersMem); break; case 'C': - tryRead("Cached:", &this->cachedMem); + tryRead("Cached:", cachedMem); break; case 'S': switch (buffer[1]) { case 'w': - tryRead("SwapTotal:", &this->totalSwap); - tryRead("SwapCached:", &this->cachedSwap); - tryRead("SwapFree:", &swapFree); - break; - case 'h': - tryRead("Shmem:", &shmem); + tryRead("SwapTotal:", swapTotalMem); + tryRead("SwapCached:", swapCacheMem); + tryRead("SwapFree:", swapFreeMem); break; case 'R': - tryRead("SReclaimable:", &sreclaimable); + tryRead("SReclaimable:", sreclaimableMem); break; } break; } + #undef tryRead } - this->usedMem = this->totalMem - freeMem; - this->cachedMem = this->cachedMem + sreclaimable - shmem; - this->usedSwap = this->totalSwap - swapFree - this->cachedSwap; fclose(file); + + /* + * Compute memory partition like procps(free) + * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c + */ + this->totalMem = totalMem; + this->cachedMem = cachedMem + sreclaimableMem; + const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem; + this->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; + this->buffersMem = buffersMem; + this->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; + this->totalSwap = swapTotalMem; + this->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; + this->cachedSwap = swapCacheMem; } static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { this->totalHugePageMem = 0; for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { - this->usedHugePageMem[i] = ULLONG_MAX; + this->usedHugePageMem[i] = MEMORY_MAX; } DIR* dir = opendir("/sys/kernel/mm/hugepages"); @@ -1614,7 +1632,7 @@ static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { if (r <= 0) continue; - unsigned long long int total = strtoull(content, NULL, 10); + memory_t total = strtoull(content, NULL, 10); if (total == 0) continue; @@ -1623,7 +1641,7 @@ static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { if (r <= 0) continue; - unsigned long long int free = strtoull(content, NULL, 10); + memory_t free = strtoull(content, NULL, 10); int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10); assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT); @@ -1636,9 +1654,9 @@ static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { } static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) { - unsigned long long int totalZram = 0; - unsigned long long int usedZramComp = 0; - unsigned long long int usedZramOrig = 0; + memory_t totalZram = 0; + memory_t usedZramComp = 0; + memory_t usedZramOrig = 0; char mm_stat[34]; char disksize[34]; @@ -1659,9 +1677,9 @@ static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) { } break; } - unsigned long long int size = 0; - unsigned long long int orig_data_size = 0; - unsigned long long int compr_data_size = 0; + memory_t size = 0; + memory_t orig_data_size = 0; + memory_t compr_data_size = 0; if (!fscanf(disksize_file, "%llu\n", &size) || !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) { @@ -1684,9 +1702,9 @@ static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) { } static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { - unsigned long long int dbufSize = 0; - unsigned long long int dnodeSize = 0; - unsigned long long int bonusSize = 0; + memory_t dbufSize = 0; + memory_t dnodeSize = 0; + memory_t bonusSize = 0; FILE* file = fopen(PROCARCSTATSFILE, "r"); if (file == NULL) { diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index d65119e8..72661ce2 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -74,8 +74,10 @@ typedef struct LinuxProcessList_ { int netlink_family; #endif - unsigned long long int totalHugePageMem; - unsigned long long int usedHugePageMem[HTOP_HUGEPAGE_COUNT]; + memory_t totalHugePageMem; + memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT]; + + memory_t availableMem; ZfsArcStats zfs; ZramStats zram; diff --git a/linux/Platform.c b/linux/Platform.c index 80b276ef..83c62d32 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -283,14 +283,11 @@ void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; const LinuxProcessList* lpl = (const LinuxProcessList*) pl; - long int usedMem = pl->usedMem; - long int buffersMem = pl->buffersMem; - long int cachedMem = pl->cachedMem; - usedMem -= buffersMem + cachedMem + lpl->totalHugePageMem; - this->total = pl->totalMem - lpl->totalHugePageMem; - this->values[0] = usedMem; - this->values[1] = buffersMem; - this->values[2] = cachedMem; + this->total = pl->totalMem; + this->values[0] = pl->usedMem; + this->values[1] = pl->buffersMem; + this->values[2] = pl->cachedMem; + this->values[3] = pl->availableMem; if (lpl->zfs.enabled != 0) { this->values[0] -= lpl->zfs.size; diff --git a/linux/ZramStats.h b/linux/ZramStats.h index 2305cfd2..67aadcc5 100644 --- a/linux/ZramStats.h +++ b/linux/ZramStats.h @@ -2,9 +2,9 @@ #define HEADER_ZramStats typedef struct ZramStats_ { - unsigned long long int totalZram; - unsigned long long int usedZramComp; - unsigned long long int usedZramOrig; + memory_t totalZram; + memory_t usedZramComp; + memory_t usedZramOrig; } ZramStats; #endif