diff --git a/Action.c b/Action.c index 1d3bccc51..ca9637dde 100644 --- a/Action.c +++ b/Action.c @@ -27,7 +27,9 @@ in the source distribution for its full text. #include "ListItem.h" #include "Macros.h" #include "MainPanel.h" +#include "MemoryMeter.h" #include "OpenFilesScreen.h" +#include "Platform.h" #include "Process.h" #include "ProcessLocksScreen.h" #include "ProvideCurses.h" @@ -750,16 +752,22 @@ static Htop_Reaction actionHelp(State* st) { attrset(CRT_colors[DEFAULT_COLOR]); mvaddstr(line++, 0, "Memory bar: "); addattrstr(CRT_colors[BAR_BORDER], "["); - addbartext(CRT_colors[MEMORY_USED], "", "used"); - addbartext(CRT_colors[MEMORY_SHARED], "/", "shared"); - addbartext(CRT_colors[MEMORY_COMPRESSED], "/", "compressed"); - if (st->host->settings->showCachedMemory) { - addbartext(CRT_colors[MEMORY_BUFFERS_TEXT], "/", "buffers"); - addbartext(CRT_colors[MEMORY_CACHE], "/", "cache"); - addbartext(CRT_colors[BAR_SHADOW], " ", "used"); - } else { - addbartext(CRT_colors[BAR_SHADOW], " ", "used"); + // memory classes are OS-specific and provided in their /Platform.c implementation + // ideal length of memory bar == 56 chars. Any length < 45 requires padding to 45. + // [0 1 2 3 4 5 ] + // [12345678901234567890123456789012345678901234567890123456] + // [ ^ 5 ] + // [class1/class2/class3/.../classN used/total] + int barTxtLen = 0; + for (unsigned int i = 0; i < Platform_numberOfMemoryClasses; i++) { + if (!st->host->settings->showCachedMemory && Platform_memoryClasses[i].countsAsCache) + continue; // skip reclaimable cache memory classes if "show cached memory" is not ticked + addbartext(CRT_colors[Platform_memoryClasses[i].color], (i == 0 ? "" : "/"), Platform_memoryClasses[i].label); + barTxtLen += (i == 0 ? 0 : 1) + strlen (Platform_memoryClasses[i].label); } + for (int i = barTxtLen; i < 45; i++) + addattrstr(CRT_colors[BAR_SHADOW], " "); // pad to 45 chars if necessary + addbartext(CRT_colors[BAR_SHADOW], " ", "used"); addbartext(CRT_colors[BAR_SHADOW], "/", "total"); addattrstr(CRT_colors[BAR_BORDER], "]"); diff --git a/CRT.c b/CRT.c index c92c1043d..22d7b9d6e 100644 --- a/CRT.c +++ b/CRT.c @@ -177,12 +177,12 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [SWAP_FRONTSWAP] = A_BOLD | ColorPairGrayBlack, [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), - [MEMORY_USED] = ColorPair(Green, Black), - [MEMORY_BUFFERS] = A_BOLD | ColorPair(Blue, Black), - [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), - [MEMORY_CACHE] = ColorPair(Yellow, Black), - [MEMORY_SHARED] = ColorPair(Magenta, Black), - [MEMORY_COMPRESSED] = A_BOLD | ColorPairGrayBlack, + [MEMORY_1] = ColorPair(Green, Black), + [MEMORY_2] = A_BOLD | ColorPair(Blue, Black), + [MEMORY_3] = ColorPair(Yellow, Black), + [MEMORY_4] = ColorPair(Magenta, Black), + [MEMORY_5] = A_BOLD | ColorPairGrayBlack, + [MEMORY_6] = ColorPair(Red, Black), [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), @@ -295,12 +295,12 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [SWAP_FRONTSWAP] = A_DIM, [GRAPH_1] = A_BOLD, [GRAPH_2] = A_NORMAL, - [MEMORY_USED] = A_BOLD, - [MEMORY_BUFFERS] = A_NORMAL, - [MEMORY_BUFFERS_TEXT] = A_NORMAL, - [MEMORY_CACHE] = A_NORMAL, - [MEMORY_SHARED] = A_NORMAL, - [MEMORY_COMPRESSED] = A_DIM, + [MEMORY_1] = A_BOLD, + [MEMORY_2] = A_NORMAL, + [MEMORY_3] = A_NORMAL, + [MEMORY_4] = A_NORMAL, + [MEMORY_5] = A_DIM, + [MEMORY_6] = A_NORMAL, [HUGEPAGE_1] = A_BOLD, [HUGEPAGE_2] = A_NORMAL, [HUGEPAGE_3] = A_REVERSE | A_BOLD, @@ -413,12 +413,12 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [SWAP_FRONTSWAP] = A_BOLD | ColorPair(Black, White), [GRAPH_1] = A_BOLD | ColorPair(Blue, White), [GRAPH_2] = ColorPair(Blue, White), - [MEMORY_USED] = ColorPair(Green, White), - [MEMORY_BUFFERS] = ColorPair(Cyan, White), - [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, White), - [MEMORY_CACHE] = ColorPair(Yellow, White), - [MEMORY_SHARED] = ColorPair(Magenta, White), - [MEMORY_COMPRESSED] = A_BOLD | ColorPair(Black, White), + [MEMORY_1] = ColorPair(Green, White), + [MEMORY_2] = ColorPair(Cyan, White), + [MEMORY_3] = ColorPair(Yellow, White), + [MEMORY_4] = ColorPair(Magenta, White), + [MEMORY_5] = A_BOLD | ColorPair(Black, White), + [MEMORY_6] = ColorPair(Red, White), [HUGEPAGE_1] = ColorPair(Green, White), [HUGEPAGE_2] = ColorPair(Yellow, White), [HUGEPAGE_3] = ColorPair(Red, White), @@ -531,12 +531,12 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [SWAP_FRONTSWAP] = ColorPairGrayBlack, [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), - [MEMORY_USED] = ColorPair(Green, Black), - [MEMORY_BUFFERS] = ColorPair(Cyan, Black), - [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black), - [MEMORY_CACHE] = ColorPair(Yellow, Black), - [MEMORY_SHARED] = ColorPair(Magenta, Black), - [MEMORY_COMPRESSED] = ColorPairGrayBlack, + [MEMORY_1] = ColorPair(Green, Black), + [MEMORY_2] = ColorPair(Cyan, Black), + [MEMORY_3] = ColorPair(Yellow, Black), + [MEMORY_4] = ColorPair(Magenta, Black), + [MEMORY_5] = ColorPairGrayBlack, + [MEMORY_6] = ColorPair(Red, Black), [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), @@ -649,12 +649,12 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [SWAP_FRONTSWAP] = A_BOLD | ColorPair(Black, Blue), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Blue), [GRAPH_2] = ColorPair(Cyan, Blue), - [MEMORY_USED] = A_BOLD | ColorPair(Green, Blue), - [MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan, Blue), - [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan, Blue), - [MEMORY_CACHE] = A_BOLD | ColorPair(Yellow, Blue), - [MEMORY_SHARED] = A_BOLD | ColorPair(Magenta, Blue), - [MEMORY_COMPRESSED] = A_BOLD | ColorPair(Black, Blue), + [MEMORY_1] = A_BOLD | ColorPair(Green, Blue), + [MEMORY_2] = A_BOLD | ColorPair(Cyan, Blue), + [MEMORY_3] = A_BOLD | ColorPair(Yellow, Blue), + [MEMORY_4] = A_BOLD | ColorPair(Magenta, Blue), + [MEMORY_5] = A_BOLD | ColorPair(Black, Blue), + [MEMORY_6] = A_BOLD | ColorPair(Red, Blue), [HUGEPAGE_1] = A_BOLD | ColorPair(Green, Blue), [HUGEPAGE_2] = A_BOLD | ColorPair(Yellow, Blue), [HUGEPAGE_3] = A_BOLD | ColorPair(Red, Blue), @@ -767,12 +767,12 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [SWAP_FRONTSWAP] = ColorPair(Yellow, Black), [GRAPH_1] = A_BOLD | ColorPair(Green, Black), [GRAPH_2] = ColorPair(Green, Black), - [MEMORY_USED] = ColorPair(Green, Black), - [MEMORY_BUFFERS] = ColorPair(Blue, Black), - [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), - [MEMORY_CACHE] = ColorPair(Yellow, Black), - [MEMORY_SHARED] = ColorPair(Magenta, Black), - [MEMORY_COMPRESSED] = ColorPair(Yellow, Black), + [MEMORY_1] = ColorPair(Green, Black), + [MEMORY_2] = ColorPair(Blue, Black), + [MEMORY_3] = ColorPair(Yellow, Black), + [MEMORY_4] = ColorPair(Magenta, Black), + [MEMORY_5] = ColorPair(Yellow, Black), + [MEMORY_6] = ColorPair(Red, Black), [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), @@ -880,12 +880,12 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [SWAP_FRONTSWAP] = A_BOLD | ColorPairGrayBlack, [GRAPH_1] = A_BOLD, [GRAPH_2] = A_NORMAL, - [MEMORY_USED] = A_BOLD | ColorPair(Yellow, Black), - [MEMORY_BUFFERS] = A_NORMAL, - [MEMORY_BUFFERS_TEXT] = A_NORMAL, - [MEMORY_CACHE] = A_NORMAL, - [MEMORY_SHARED] = A_NORMAL, - [MEMORY_COMPRESSED] = A_BOLD | ColorPairGrayBlack, + [MEMORY_1] = A_BOLD | ColorPair(Yellow, Black), + [MEMORY_2] = A_NORMAL, + [MEMORY_3] = A_NORMAL, + [MEMORY_4] = A_NORMAL, + [MEMORY_5] = A_BOLD | ColorPairGrayBlack, + [MEMORY_6] = A_NORMAL, [HUGEPAGE_1] = A_BOLD, [HUGEPAGE_2] = A_NORMAL, [HUGEPAGE_3] = A_BOLD | ColorPair(Cyan, Black), diff --git a/CRT.h b/CRT.h index 77ca62ba2..145a0cf60 100644 --- a/CRT.h +++ b/CRT.h @@ -93,12 +93,12 @@ typedef enum ColorElements_ { BAR_SHADOW, GRAPH_1, GRAPH_2, - MEMORY_USED, - MEMORY_BUFFERS, - MEMORY_BUFFERS_TEXT, - MEMORY_CACHE, - MEMORY_SHARED, - MEMORY_COMPRESSED, + MEMORY_1, + MEMORY_2, + MEMORY_3, + MEMORY_4, + MEMORY_5, + MEMORY_6, HUGEPAGE_1, HUGEPAGE_2, HUGEPAGE_3, diff --git a/Machine.h b/Machine.h index 814977053..98cac6019 100644 --- a/Machine.h +++ b/Machine.h @@ -51,11 +51,6 @@ typedef struct Machine_ { #endif memory_t totalMem; - memory_t usedMem; - memory_t buffersMem; - memory_t cachedMem; - memory_t sharedMem; - memory_t availableMem; memory_t totalSwap; memory_t usedSwap; diff --git a/MemoryMeter.c b/MemoryMeter.c index d555a1214..1a72b0f47 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -1,6 +1,7 @@ /* htop - MemoryMeter.c (C) 2004-2011 Hisham H. Muhammad +(C) 2025 htop dev team Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ @@ -21,11 +22,12 @@ in the source distribution for its full text. static const int MemoryMeter_attributes[] = { - MEMORY_USED, - MEMORY_SHARED, - MEMORY_COMPRESSED, - MEMORY_BUFFERS, - MEMORY_CACHE + MEMORY_1, + MEMORY_2, + MEMORY_3, + MEMORY_4, + MEMORY_5, + MEMORY_6 }; static void MemoryMeter_updateValues(Meter* this) { @@ -35,26 +37,28 @@ static void MemoryMeter_updateValues(Meter* this) { Settings *settings = this->host->settings; - /* shared, compressed and available memory are not supported on all platforms */ - this->values[MEMORY_METER_SHARED] = NAN; - this->values[MEMORY_METER_COMPRESSED] = NAN; - this->values[MEMORY_METER_AVAILABLE] = NAN; + /* not all memory classes are supported on all platforms */ + for (unsigned int memoryClassIdx = 0; memoryClassIdx < Platform_numberOfMemoryClasses; memoryClassIdx++) { + this->values[memoryClassIdx] = NAN; + } + Platform_setMemoryValues(this); + this->curItems = (uint8_t) Platform_numberOfMemoryClasses; + + /* compute the used memory */ + double used = 0.0; + for (unsigned int memoryClassIdx = 0; memoryClassIdx < Platform_numberOfMemoryClasses; memoryClassIdx++) { + if (Platform_memoryClasses[memoryClassIdx].countsAsUsed) + used += this->values[memoryClassIdx]; + } + + /* clear the values we don't want to see */ if ((this->mode == GRAPH_METERMODE || this->mode == BAR_METERMODE) && !settings->showCachedMemory) { - this->values[MEMORY_METER_BUFFERS] = 0; - this->values[MEMORY_METER_CACHE] = 0; + for (unsigned int memoryClassIdx = 0; memoryClassIdx < Platform_numberOfMemoryClasses; memoryClassIdx++) { + if (Platform_memoryClasses[memoryClassIdx].countsAsCache) + this->values[memoryClassIdx] = NAN; + } } - /* Do not print available memory in bar mode */ - static_assert(MEMORY_METER_AVAILABLE + 1 == MEMORY_METER_ITEMCOUNT, - "MEMORY_METER_AVAILABLE is not the last item in MemoryMeterValues"); - this->curItems = MEMORY_METER_AVAILABLE; - - /* we actually want to show "used + shared + compressed" */ - double used = this->values[MEMORY_METER_USED]; - if (isPositive(this->values[MEMORY_METER_SHARED])) - used += this->values[MEMORY_METER_SHARED]; - if (isPositive(this->values[MEMORY_METER_COMPRESSED])) - used += this->values[MEMORY_METER_COMPRESSED]; written = Meter_humanUnit(buffer, used, size); METER_BUFFER_CHECK(buffer, size, written); @@ -73,37 +77,15 @@ static void MemoryMeter_display(const Object* cast, RichString* out) { Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - Meter_humanUnit(buffer, this->values[MEMORY_METER_USED], sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); - RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer); - - /* shared memory is not supported on all platforms */ - if (isNonnegative(this->values[MEMORY_METER_SHARED])) { - Meter_humanUnit(buffer, this->values[MEMORY_METER_SHARED], sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_TEXT], " shared:"); - RichString_appendAscii(out, CRT_colors[MEMORY_SHARED], buffer); - } - - /* compressed memory is not supported on all platforms */ - if (isNonnegative(this->values[MEMORY_METER_COMPRESSED])) { - Meter_humanUnit(buffer, this->values[MEMORY_METER_COMPRESSED], sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_TEXT], " compressed:"); - RichString_appendAscii(out, CRT_colors[MEMORY_COMPRESSED], buffer); - } - - Meter_humanUnit(buffer, this->values[MEMORY_METER_BUFFERS], sizeof(buffer)); - RichString_appendAscii(out, settings->showCachedMemory ? CRT_colors[METER_TEXT] : CRT_colors[METER_SHADOW], " buffers:"); - RichString_appendAscii(out, settings->showCachedMemory ? CRT_colors[MEMORY_BUFFERS_TEXT] : CRT_colors[METER_SHADOW], buffer); - - Meter_humanUnit(buffer, this->values[MEMORY_METER_CACHE], sizeof(buffer)); - RichString_appendAscii(out, settings->showCachedMemory ? CRT_colors[METER_TEXT] : CRT_colors[METER_SHADOW], " cache:"); - RichString_appendAscii(out, settings->showCachedMemory ? CRT_colors[MEMORY_CACHE] : CRT_colors[METER_SHADOW], buffer); - - /* available memory is not supported on all platforms */ - if (isNonnegative(this->values[MEMORY_METER_AVAILABLE])) { - Meter_humanUnit(buffer, this->values[MEMORY_METER_AVAILABLE], sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_TEXT], " available:"); - RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); + /* print the memory classes in the order supplied (specific to each platform) */ + for (unsigned int memoryClassIdx = 0; memoryClassIdx < Platform_numberOfMemoryClasses; memoryClassIdx++) { + if (!settings->showCachedMemory && Platform_memoryClasses[memoryClassIdx].countsAsCache) + continue; // skip reclaimable cache memory classes if "show cached memory" is not ticked + Meter_humanUnit(buffer, this->values[memoryClassIdx], sizeof(buffer)); + RichString_appendAscii(out, CRT_colors[METER_TEXT], " "); + RichString_appendAscii(out, CRT_colors[METER_TEXT], Platform_memoryClasses[memoryClassIdx].label); + RichString_appendAscii(out, CRT_colors[METER_TEXT], ":"); + RichString_appendAscii(out, CRT_colors[Platform_memoryClasses[memoryClassIdx].color], buffer); } } @@ -116,7 +98,7 @@ const MeterClass MemoryMeter_class = { .updateValues = MemoryMeter_updateValues, .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, - .maxItems = MEMORY_METER_ITEMCOUNT, + .maxItems = 6, // maximum of MEMORY_N settings .isPercentChart = true, .total = 100.0, .attributes = MemoryMeter_attributes, diff --git a/MemoryMeter.h b/MemoryMeter.h index e39b3bd38..3a8b967bb 100644 --- a/MemoryMeter.h +++ b/MemoryMeter.h @@ -3,21 +3,19 @@ /* htop - MemoryMeter.h (C) 2004-2011 Hisham H. Muhammad +(C) 2025 htop dev team Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" -typedef enum { - MEMORY_METER_USED = 0, - MEMORY_METER_SHARED = 1, - MEMORY_METER_COMPRESSED = 2, - MEMORY_METER_BUFFERS = 3, - MEMORY_METER_CACHE = 4, - MEMORY_METER_AVAILABLE = 5, - MEMORY_METER_ITEMCOUNT = 6, // number of entries in this enum -} MemoryMeterValues; +typedef struct MemoryClass_s { + const char *label; // e.g. "wired", "shared", "compressed" - platform-specific memory classe names + bool countsAsUsed; // memory class counts as "used" memory + bool countsAsCache; // memory class reclaimed under pressure (displayed with "show cached memory") + ColorElements color; // one of the MEMORY CRT color values +} MemoryClass; extern const MeterClass MemoryMeter_class; diff --git a/darwin/DarwinMachine.c b/darwin/DarwinMachine.c index 4937be105..132cdd243 100644 --- a/darwin/DarwinMachine.c +++ b/darwin/DarwinMachine.c @@ -60,19 +60,11 @@ static unsigned int DarwinMachine_allocateCPULoadInfo(processor_cpu_load_info_t* } static void DarwinMachine_getVMStats(DarwinMachine* this) { -#ifdef HAVE_STRUCT_VM_STATISTICS64 mach_msg_type_number_t info_size = HOST_VM_INFO64_COUNT; if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info_t)&this->vm_stats, &info_size) != 0) { CRT_fatalError("Unable to retrieve VM statistics64"); } -#else - mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; - - if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&this->vm_stats, &info_size) != 0) { - CRT_fatalError("Unable to retrieve VM statistics"); - } -#endif } void Machine_scan(Machine* super) { diff --git a/darwin/DarwinMachine.h b/darwin/DarwinMachine.h index 13a385983..828dfa07f 100644 --- a/darwin/DarwinMachine.h +++ b/darwin/DarwinMachine.h @@ -19,11 +19,7 @@ typedef struct DarwinMachine_ { Machine super; host_basic_info_data_t host_info; -#ifdef HAVE_STRUCT_VM_STATISTICS64 vm_statistics64_data_t vm_stats; -#else - vm_statistics_data_t vm_stats; -#endif processor_cpu_load_info_t prev_load; processor_cpu_load_info_t curr_load; diff --git a/darwin/Platform.c b/darwin/Platform.c index 72d262590..fe552015c 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -114,6 +114,26 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +enum { + MEMORY_CLASS_WIRED = 0, + MEMORY_CLASS_SPECULATIVE, + MEMORY_CLASS_ACTIVE, + MEMORY_CLASS_PURGEABLE, + MEMORY_CLASS_COMPRESSED, + MEMORY_CLASS_INACTIVE, +}; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "wired", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, // pages wired down to physical memory (kernel) + { .label = "speculative", .countsAsUsed = true, .countsAsCache = true, .color = MEMORY_2 }, // readahead optimization caches + { .label = "active", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, // userland pages actively being used + { .label = "purgeable", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_4 }, // userland pages voluntarily marked "discardable" by apps + { .label = "compressed", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_5 }, // userland pages being compressed (means memory pressure++) + { .label = "inactive", .countsAsUsed = true, .countsAsCache = true, .color = MEMORY_6 }, // pages no longer used; macOS counts them as "used" anyway... +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -394,31 +414,24 @@ void Platform_setGPUValues(Meter* mtr, double* totalUsage, unsigned long long* t void Platform_setMemoryValues(Meter* mtr) { const DarwinMachine* dhost = (const DarwinMachine*) mtr->host; -#ifdef HAVE_STRUCT_VM_STATISTICS64 + const Settings* settings = mtr->host->settings; const struct vm_statistics64* vm = &dhost->vm_stats; -#else - const struct vm_statistics* vm = &dhost->vm_stats; -#endif double page_K = (double)vm_page_size / (double)1024; mtr->total = dhost->host_info.max_mem / 1024; -#ifdef HAVE_STRUCT_VM_STATISTICS64 - natural_t used = vm->active_count + vm->inactive_count + - vm->speculative_count + vm->wire_count + - vm->compressor_page_count - vm->purgeable_count - vm->external_page_count; - mtr->values[MEMORY_METER_USED] = (double)(used - vm->compressor_page_count) * page_K; -#else - mtr->values[MEMORY_METER_USED] = (double)(vm->active_count + vm->wire_count) * page_K; -#endif - // mtr->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" -#ifdef HAVE_STRUCT_VM_STATISTICS64 - mtr->values[MEMORY_METER_COMPRESSED] = (double)vm->compressor_page_count * page_K; -#else - // mtr->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" -#endif - mtr->values[MEMORY_METER_BUFFERS] = (double)vm->purgeable_count * page_K; - mtr->values[MEMORY_METER_CACHE] = (double)vm->inactive_count * page_K; - // mtr->values[MEMORY_METER_AVAILABLE] = "available memory" + mtr->values[MEMORY_CLASS_WIRED] = page_K * vm->wire_count; + if (settings->showCachedMemory) { + mtr->values[MEMORY_CLASS_SPECULATIVE] = page_K * vm->speculative_count; + mtr->values[MEMORY_CLASS_ACTIVE] = page_K * (vm->active_count - vm->purgeable_count - vm->external_page_count); // external pages are pages swapped out + mtr->values[MEMORY_CLASS_PURGEABLE] = page_K * vm->purgeable_count; // purgeable pages are flagged in the active pages + } + else { // if showCachedMemory is disabled, merge speculative and purgeable into the active pages + mtr->values[MEMORY_CLASS_SPECULATIVE] = 0; + mtr->values[MEMORY_CLASS_ACTIVE] = page_K * (vm->speculative_count + vm->active_count - vm->external_page_count); // external pages are pages swapped out + mtr->values[MEMORY_CLASS_PURGEABLE] = 0; + } + mtr->values[MEMORY_CLASS_COMPRESSED] = page_K * vm->compressor_page_count; + mtr->values[MEMORY_CLASS_INACTIVE] = page_K * vm->inactive_count; // for some reason macOS counts inactive pages in the "used" memory... } void Platform_setSwapValues(Meter* mtr) { @@ -429,8 +442,6 @@ void Platform_setSwapValues(Meter* mtr) { mtr->total = swapused.xsu_total / 1024; mtr->values[SWAP_METER_USED] = swapused.xsu_used / 1024; - // mtr->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" - // mtr->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { diff --git a/darwin/Platform.h b/darwin/Platform.h index bfe67e0a0..bb1672d16 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "CPUMeter.h" #include "DiskIOMeter.h" #include "Hashtable.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" @@ -34,6 +35,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/dragonflybsd/DragonFlyBSDMachine.c b/dragonflybsd/DragonFlyBSDMachine.c index 39b86ba48..fc6bb85e7 100644 --- a/dragonflybsd/DragonFlyBSDMachine.c +++ b/dragonflybsd/DragonFlyBSDMachine.c @@ -32,7 +32,6 @@ static int MIB_vm_stats_vm_v_wire_count[4]; static int MIB_vm_stats_vm_v_active_count[4]; static int MIB_vm_stats_vm_v_cache_count[4]; static int MIB_vm_stats_vm_v_inactive_count[4]; -static int MIB_vm_stats_vm_v_free_count[4]; static int MIB_vfs_bufspace[2]; @@ -65,7 +64,6 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); @@ -231,30 +229,57 @@ static void DragonFlyBSDMachine_scanMemoryInfo(Machine* super) { // htop_used (unavail to anybody) = active + wired // htop_cache (for cache meter) = buffers + cache // user_free (avail to procs) = buffers + inactive + cache + free - size_t len = sizeof(super->totalMem); - //disabled for now, as it is always smaller than phycal amount of memory... - //...to avoid "where is my memory?" questions - //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(this->totalMem), &len, NULL, 0); - //this->totalMem *= pageSizeKb; - sysctl(MIB_hw_physmem, 2, &(super->totalMem), &len, NULL, 0); - super->totalMem /= 1024; - - unsigned long long int memActive = 0; - sysctl(MIB_vm_stats_vm_v_active_count, 4, &memActive, &len, NULL, 0); - memActive *= this->pageSizeKb; - - unsigned long long int memWire = 0; - sysctl(MIB_vm_stats_vm_v_wire_count, 4, &memWire, &len, NULL, 0); - memWire *= this->pageSizeKb; - - sysctl(MIB_vfs_bufspace, 2, &(super->buffersMem), &len, NULL, 0); - super->buffersMem /= 1024; + u_long totalMem; + unsigned long long int memActive, memWire, memInactive, memCache; + long long int buffersMem; + size_t len; - sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(super->cachedMem), &len, NULL, 0); - super->cachedMem *= this->pageSizeKb; - super->usedMem = memActive + memWire; + // total memory + len = sizeof(totalMem); + if ((sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0) == 0) && (totalMem > 0)) + super->totalMem = totalMem / 1024; + else + super->totalMem = 0; + + // "active" pages + len = sizeof(memActive); + if ((sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0) == 0) && (memActive > 0)) + this->activeMem = memActive * this->pageSizeKb; + else + this->activeMem = 0; + + // "wired" pages + len = sizeof(memWire); + if ((sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0) == 0) && (memWire > 0)) + this->wiredMem = memWire * this->pageSizeKb; + else + this->wiredMem = 0; + + // "inactive" pages + len = sizeof(memInactive); + if ((sysctl(MIB_vm_stats_vm_v_inactive_count, 4, &(memInactive), &len, NULL, 0) == 0) && (memInactive > 0)) + this->inactiveMem = memInactive * this->pageSizeKb; + else + this->inactiveMem = 0; + + // "cache" pages + len = sizeof(memCache); + if ((sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(memCache), &len, NULL, 0) == 0) && (memCache > 0)) + this->cacheMem = memCache * this->pageSizeKb; + else + this->cacheMem = 0; + + // "buffers" pages (separate read, should be deducted from 'wired') + len = sizeof(buffersMem); + if ((sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0) == 0) && (buffersMem > 0)) { + this->buffersMem = buffersMem / 1024; + this->wiredMem -= this->buffersMem; // substract (NB: "buffers" can't be larger than "wired") + } + else + this->buffersMem = 0; + // swap struct kvm_swap swap[16]; int nswap = kvm_getswapinfo(this->kd, swap, ARRAYSIZE(swap), 0); super->totalSwap = 0; diff --git a/dragonflybsd/DragonFlyBSDMachine.h b/dragonflybsd/DragonFlyBSDMachine.h index b66ec60e9..84fc3ab78 100644 --- a/dragonflybsd/DragonFlyBSDMachine.h +++ b/dragonflybsd/DragonFlyBSDMachine.h @@ -42,6 +42,12 @@ typedef struct DragonFlyBSDMachine_ { int pageSizeKb; int kernelFScale; + memory_t wiredMem; + memory_t buffersMem; + memory_t activeMem; + memory_t inactiveMem; + memory_t cacheMem; + CPUData* cpus; unsigned long* cp_time_o; diff --git a/dragonflybsd/DragonFlyBSDProcessTable.c b/dragonflybsd/DragonFlyBSDProcessTable.c index dd49282c8..5c4647991 100644 --- a/dragonflybsd/DragonFlyBSDProcessTable.c +++ b/dragonflybsd/DragonFlyBSDProcessTable.c @@ -217,7 +217,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { proc->time = (kproc->kp_lwp.kl_uticks + kproc->kp_lwp.kl_sticks + kproc->kp_lwp.kl_iticks) / 10000; proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)dhost->kernelFScale); - proc->percent_mem = 100.0 * proc->m_resident / (double)(super->super.host->totalMem); + proc->percent_mem = 100.0 * proc->m_resident / (double)(host->totalMem); Process_updateCPUFieldWidths(proc->percent_cpu); if (proc->percent_cpu > 0.1) { diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 46c367b10..58c4b670b 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -93,6 +93,25 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +enum { + MEMORY_CLASS_WIRED = 0, + MEMORY_CLASS_BUFFERS, + MEMORY_CLASS_ACTIVE, + MEMORY_CLASS_CACHE, + MEMORY_CLASS_INACTIVE, +}; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "wired", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "buffers", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_2 }, + { .label = "active", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, + { .label = "cache", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_4 }, + { .label = "inactive", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_5 }, +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -222,22 +241,27 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; + const DragonFlyBSDMachine* fhost = (const DragonFlyBSDMachine*) host; + const Settings* settings = host->settings; this->total = host->totalMem; - this->values[MEMORY_METER_USED] = host->usedMem; - // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" - // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_BUFFERS] = host->buffersMem; - this->values[MEMORY_METER_CACHE] = host->cachedMem; - // this->values[MEMORY_METER_AVAILABLE] = "available memory" + if (settings->showCachedMemory) { + this->values[MEMORY_CLASS_WIRED] = fhost->wiredMem; + this->values[MEMORY_CLASS_BUFFERS] = fhost->buffersMem; + } + else { // if showCachedMemory is disabled, merge buffers into the wired pages + this->values[MEMORY_CLASS_WIRED] = fhost->wiredMem + fhost->buffersMem; + this->values[MEMORY_CLASS_BUFFERS] = 0; + } + this->values[MEMORY_CLASS_ACTIVE] = fhost->activeMem; + this->values[MEMORY_CLASS_CACHE] = fhost->cacheMem; + this->values[MEMORY_CLASS_INACTIVE] = fhost->inactiveMem; } void Platform_setSwapValues(Meter* this) { const Machine* host = this->host; this->total = host->totalSwap; this->values[SWAP_METER_USED] = host->usedSwap; - // mtr->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" - // mtr->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } char* Platform_getProcessEnv(pid_t pid) { diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index bc816598b..6d709fecb 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -18,7 +18,7 @@ in the source distribution for its full text. #include "DiskIOMeter.h" #include "Hashtable.h" #include "Macros.h" -#include "Meter.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "Process.h" #include "ProcessLocksScreen.h" @@ -37,6 +37,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/freebsd/FreeBSDMachine.c b/freebsd/FreeBSDMachine.c index c42ef705c..949d2c22b 100644 --- a/freebsd/FreeBSDMachine.c +++ b/freebsd/FreeBSDMachine.c @@ -42,10 +42,8 @@ static int MIB_vm_stats_vm_v_page_count[4]; static int MIB_vm_stats_vm_v_wire_count[4]; static int MIB_vm_stats_vm_v_active_count[4]; -static int MIB_vm_stats_vm_v_cache_count[4]; +static int MIB_vm_stats_vm_v_laundry_count[4]; static int MIB_vm_stats_vm_v_inactive_count[4]; -static int MIB_vm_stats_vm_v_free_count[4]; -static int MIB_vm_vmtotal[2]; static int MIB_vfs_bufspace[2]; @@ -76,10 +74,8 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_laundry_count", MIB_vm_stats_vm_v_laundry_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); - len = 2; sysctlnametomib("vm.vmtotal", MIB_vm_vmtotal, &len); len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); @@ -309,65 +305,76 @@ static inline void FreeBSDMachine_scanCPU(Machine* super) { static void FreeBSDMachine_scanMemoryInfo(Machine* super) { FreeBSDMachine* this = (FreeBSDMachine*) super; - // @etosan: - // memory counter relationships seem to be these: - // total = active + wired + inactive + cache + free - // htop_used (unavail to anybody) = active + wired + inactive - buffer - // htop_cache (for cache meter) = buffer + cache - // htop_user_free (avail to procs) = buffer + cache + free - // htop_buffers (disk write buffer) = 0 (not applicable to FreeBSD) + // comment by Pierre-Marie Baty // - // 'buffer' contain cache used by most file systems other than ZFS, and is - // included in 'wired' + // FreeBSD has the following memory classes: + // active: userland pages currently mapped to physical memory (i.e. in use) + // inactive: userland pages that are no longer active, can be (re)allocated to processes + // laundry: userland pages that were just released, now being flushed, will become inactive + // wired: kernel pages currently mapped to physical memory, cannot be paged out nor swapped + // buffers: subcategory of 'wired' corresponding to the filesystem caches + // free: pages that haven't been allocated yet, or have been released // - // with ZFS ARC situation becomes bit muddled, as ARC behaves like "user_free" - // and belongs into cache, but is reported as wired by kernel - // - // htop_used = active + (wired - arc) - // htop_cache = buffers + cache + arc + // With ZFS, the ARC area is NOT counted in the 'buffers' class, but is still counted in the 'wired' + // class. The ARC total must thus be substracted from the 'wired' class AND added to the 'buffer' class, + // so that the result (ARC being shown in buffersMem) is consistent with what ZFS users would expect. + // This adjustment is done in Platform_setMemoryValues() in freebsd/Platform.c. + u_long totalMem; - u_int memActive, memWire, memInactive, cachedMem; + u_int memActive, memWire, memInactive, memLaundry; long buffersMem; size_t len; - struct vmtotal vmtotal; - //disabled for now, as it is always smaller than phycal amount of memory... - //...to avoid "where is my memory?" questions - //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(super->totalMem), &len, NULL, 0); - //super->totalMem *= this->pageSizeKb; + // total memory len = sizeof(totalMem); - sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); - totalMem /= 1024; - super->totalMem = totalMem; + if ((sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0) == 0) && (totalMem > 0)) + super->totalMem = totalMem / 1024; + else + super->totalMem = 0; + // "active" pages len = sizeof(memActive); - sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); - memActive *= this->pageSizeKb; + if ((sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0) == 0) && (memActive > 0)) + this->activeMem = memActive * this->pageSizeKb; + else + this->activeMem = 0; + // "wired" pages len = sizeof(memWire); - sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0); - memWire *= this->pageSizeKb; + if ((sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0) == 0) && (memWire > 0)) + this->wiredMem = memWire * this->pageSizeKb; + else + this->wiredMem = 0; + // "inactive" pages len = sizeof(memInactive); - sysctl(MIB_vm_stats_vm_v_inactive_count, 4, &(memInactive), &len, NULL, 0); - memInactive *= this->pageSizeKb; - + if ((sysctl(MIB_vm_stats_vm_v_inactive_count, 4, &(memInactive), &len, NULL, 0) == 0) && (memInactive > 0)) + this->inactiveMem = memInactive * this->pageSizeKb; + else + this->inactiveMem = 0; + + // "laundry" pages + len = sizeof(memLaundry); + if ((sysctl(MIB_vm_stats_vm_v_laundry_count, 4, &(memLaundry), &len, NULL, 0) == 0) && (memLaundry > 0)) + this->laundryMem = memLaundry * this->pageSizeKb; + else + this->laundryMem = 0; + + // "buffers" pages (separate read, should be deducted from 'wired') len = sizeof(buffersMem); - sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); - buffersMem /= 1024; - super->cachedMem = buffersMem; - - len = sizeof(cachedMem); - sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); - cachedMem *= this->pageSizeKb; - super->cachedMem += cachedMem; - - len = sizeof(vmtotal); - sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); - super->sharedMem = vmtotal.t_rmshr * this->pageSizeKb; + if ((sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0) == 0) && (buffersMem > 0)) { + this->buffersMem = buffersMem / 1024; + this->wiredMem -= this->buffersMem; // substract (NB: "buffers" can't be larger than "wired") + } + else + this->buffersMem = 0; - super->usedMem = memActive + memWire + memInactive - buffersMem; + // NOTE: it is wrong in FreeBSD to represent the "shared" memory as a memory class by itself. + // The only page classes exposed by the kernel are "active", "inactive", "wired", "laundry" and "free". + // The "shared" memory can be obtained from another sysctl, but there is no simple way + // in FreeBSD to determine which page classe(s) this "shared" memory should be substracted from. + // swap struct kvm_swap swap[16]; int nswap = kvm_getswapinfo(this->kd, swap, ARRAYSIZE(swap), 0); super->totalSwap = 0; diff --git a/freebsd/FreeBSDMachine.h b/freebsd/FreeBSDMachine.h index 0f9c33f26..9cbc307db 100644 --- a/freebsd/FreeBSDMachine.h +++ b/freebsd/FreeBSDMachine.h @@ -36,6 +36,13 @@ typedef struct FreeBSDMachine_ { int pageSizeKb; int kernelFScale; + memory_t wiredMem; + memory_t buffersMem; + memory_t activeMem; + memory_t laundryMem; + memory_t inactiveMem; + memory_t arcMem; + ZfsArcStats zfs; CPUData* cpus; diff --git a/freebsd/Platform.c b/freebsd/Platform.c index fce57f2a9..98fa74e11 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -100,6 +100,26 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +enum { + MEMORY_CLASS_WIRED = 0, + MEMORY_CLASS_BUFFERS, + MEMORY_CLASS_ACTIVE, + MEMORY_CLASS_LAUNDRY, + MEMORY_CLASS_INACTIVE, + MEMORY_CLASS_ARC, +}; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "wired", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "buffers", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_2 }, + { .label = "active", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, + { .label = "laundry", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_4 }, + { .label = "inactive", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_5 }, + { .label = "ARC", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_6 }, +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -230,22 +250,27 @@ void Platform_setMemoryValues(Meter* this) { const FreeBSDMachine* fhost = (const FreeBSDMachine*) host; this->total = host->totalMem; - this->values[MEMORY_METER_USED] = host->usedMem; - this->values[MEMORY_METER_SHARED] = host->sharedMem; - // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_BUFFERS] = host->buffersMem; - this->values[MEMORY_METER_CACHE] = host->cachedMem; - // this->values[MEMORY_METER_AVAILABLE] = "available memory" + if (host->settings->showCachedMemory) { + this->values[MEMORY_CLASS_WIRED] = fhost->wiredMem; + this->values[MEMORY_CLASS_BUFFERS] = fhost->buffersMem; + } + else { // if showCachedMemory is disabled, merge buffers into the wired pages + this->values[MEMORY_CLASS_WIRED] = fhost->wiredMem + fhost->buffersMem; + this->values[MEMORY_CLASS_BUFFERS] = 0; + } + this->values[MEMORY_CLASS_ACTIVE] = fhost->activeMem; + this->values[MEMORY_CLASS_LAUNDRY] = fhost->laundryMem; + this->values[MEMORY_CLASS_INACTIVE] = fhost->inactiveMem; if (fhost->zfs.enabled) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (fhost->zfs.size > fhost->zfs.min) shrinkableSize = fhost->zfs.size - fhost->zfs.min; - this->values[MEMORY_METER_USED] -= shrinkableSize; - this->values[MEMORY_METER_CACHE] += shrinkableSize; - // this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; + this->values[MEMORY_CLASS_ARC] = shrinkableSize; } + else + this->values[MEMORY_CLASS_ARC] = 0; } void Platform_setSwapValues(Meter* this) { @@ -253,8 +278,6 @@ void Platform_setSwapValues(Meter* this) { this->total = host->totalSwap; this->values[SWAP_METER_USED] = host->usedSwap; - // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" - // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { diff --git a/freebsd/Platform.h b/freebsd/Platform.h index f9b3f4ef5..2ade5ff79 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -14,7 +14,7 @@ in the source distribution for its full text. #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "Hashtable.h" -#include "Meter.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "Process.h" #include "ProcessLocksScreen.h" @@ -33,6 +33,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c index d9bf6577b..1d1ea34c7 100644 --- a/linux/LinuxMachine.c +++ b/linux/LinuxMachine.c @@ -204,12 +204,12 @@ static void LinuxMachine_scanMemoryInfo(LinuxMachine* this) { * do not show twice by subtracting from Cached and do not subtract twice from used. */ host->totalMem = totalMem; - host->cachedMem = cachedMem + sreclaimableMem - sharedMem; - host->sharedMem = sharedMem; + this->cachedMem = cachedMem + sreclaimableMem - sharedMem; + this->sharedMem = sharedMem; const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem; - host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; - host->buffersMem = buffersMem; - host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; + this->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; + this->buffersMem = buffersMem; + this->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; host->totalSwap = swapTotalMem; host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; host->cachedSwap = swapCacheMem; diff --git a/linux/LinuxMachine.h b/linux/LinuxMachine.h index ffc37e1b4..c575a4fdb 100644 --- a/linux/LinuxMachine.h +++ b/linux/LinuxMachine.h @@ -77,6 +77,12 @@ typedef struct LinuxMachine_ { double period; + memory_t cachedMem; + memory_t sharedMem; + memory_t usedMem; + memory_t buffersMem; + memory_t availableMem; + CPUData* cpuData; #ifdef HAVE_SENSORS_SENSORS_H @@ -87,8 +93,6 @@ typedef struct LinuxMachine_ { memory_t totalHugePageMem; memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT]; - memory_t availableMem; - unsigned long long int prevGpuTime, curGpuTime; /* total absolute GPU time in nano seconds */ GPUEngineData* gpuEngineData; diff --git a/linux/Platform.c b/linux/Platform.c index 5320ca6b7..44dc60c8e 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -141,6 +141,26 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +enum { + MEMORY_CLASS_USED = 0, + MEMORY_CLASS_SHARED, + MEMORY_CLASS_COMPRESSED, + MEMORY_CLASS_BUFFERS, + MEMORY_CLASS_CACHE, + MEMORY_CLASS_AVAILABLE, +}; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "used", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "shared", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_2 }, + { .label = "compressed", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, + { .label = "buffers", .countsAsUsed = false, .countsAsCache = false, .color = MEMORY_4 }, + { .label = "cache", .countsAsUsed = false, .countsAsCache = false, .color = MEMORY_5 }, + { .label = "available", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_6 }, +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + static enum { BAT_PROC, BAT_SYS, BAT_ERR } Platform_Battery_method = BAT_PROC; static time_t Platform_Battery_cacheTime; static double Platform_Battery_cachePercent = NAN; @@ -421,26 +441,26 @@ void Platform_setMemoryValues(Meter* this) { const LinuxMachine* lhost = (const LinuxMachine*) host; this->total = host->totalMem; - this->values[MEMORY_METER_USED] = host->usedMem; - this->values[MEMORY_METER_SHARED] = host->sharedMem; - this->values[MEMORY_METER_COMPRESSED] = 0; /* compressed */ - this->values[MEMORY_METER_BUFFERS] = host->buffersMem; - this->values[MEMORY_METER_CACHE] = host->cachedMem; - this->values[MEMORY_METER_AVAILABLE] = host->availableMem; + this->values[MEMORY_CLASS_USED] = lhost->usedMem; + this->values[MEMORY_CLASS_SHARED] = lhost->sharedMem; + this->values[MEMORY_CLASS_COMPRESSED] = 0; /* compressed */ + this->values[MEMORY_CLASS_BUFFERS] = lhost->buffersMem; + this->values[MEMORY_CLASS_CACHE] = lhost->cachedMem; + this->values[MEMORY_CLASS_AVAILABLE] = lhost->availableMem; if (lhost->zfs.enabled != 0 && !Running_containerized) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (lhost->zfs.size > lhost->zfs.min) shrinkableSize = lhost->zfs.size - lhost->zfs.min; - this->values[MEMORY_METER_USED] -= shrinkableSize; - this->values[MEMORY_METER_CACHE] += shrinkableSize; - this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; + this->values[MEMORY_CLASS_USED] -= shrinkableSize; + this->values[MEMORY_CLASS_CACHE] += shrinkableSize; + this->values[MEMORY_CLASS_AVAILABLE] += shrinkableSize; } if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) { - this->values[MEMORY_METER_USED] -= lhost->zswap.usedZswapComp; - this->values[MEMORY_METER_COMPRESSED] += lhost->zswap.usedZswapComp; + this->values[MEMORY_CLASS_USED] -= lhost->zswap.usedZswapComp; + this->values[MEMORY_CLASS_COMPRESSED] += lhost->zswap.usedZswapComp; } } diff --git a/linux/Platform.h b/linux/Platform.h index 2c2057d97..98323fc9a 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -19,7 +19,7 @@ in the source distribution for its full text. #include "DiskIOMeter.h" #include "Hashtable.h" #include "Macros.h" -#include "Meter.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "Panel.h" #include "Process.h" @@ -47,6 +47,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/netbsd/NetBSDMachine.c b/netbsd/NetBSDMachine.c index d90093da8..7399e69f5 100644 --- a/netbsd/NetBSDMachine.c +++ b/netbsd/NetBSDMachine.c @@ -155,10 +155,16 @@ static void NetBSDMachine_scanMemoryInfo(NetBSDMachine* this) { CRT_fatalError("uvmexp sysctl call failed"); } - super->totalMem = uvmexp.npages * this->pageSizeKB; - super->buffersMem = 0; - super->cachedMem = (uvmexp.filepages + uvmexp.execpages) * this->pageSizeKB; - super->usedMem = (uvmexp.active + uvmexp.wired) * this->pageSizeKB; + // NOTE: it is wrong in NetBSD to represent the "cache" memory as a memory class by itself. + // The only page classes exposed by the kernel in the uvmexp struct are these. + // The "cached" memory can be obtained from another sysctl, but there is no simple way + // in NetBSD to determine which page classe(s) this "cached" memory should be substracted from. + this->wiredMem = this->pageSizeKB * uvmexp.wired; + this->activeMem = this->pageSizeKB * uvmexp.active; + this->pagedMem = this->pageSizeKB * uvmexp.paging; + this->inactiveMem = this->pageSizeKB * uvmexp.inactive; + + super->totalMem = this->pageSizeKB * uvmexp.npages; super->totalSwap = uvmexp.swpages * this->pageSizeKB; super->usedSwap = uvmexp.swpginuse * this->pageSizeKB; } diff --git a/netbsd/NetBSDMachine.h b/netbsd/NetBSDMachine.h index 1bfd9bbf4..e433401ec 100644 --- a/netbsd/NetBSDMachine.h +++ b/netbsd/NetBSDMachine.h @@ -48,6 +48,11 @@ typedef struct NetBSDMachine_ { size_t pageSize; size_t pageSizeKB; + memory_t wiredMem; + memory_t activeMem; + memory_t pagedMem; + memory_t inactiveMem; + CPUData* cpuData; } NetBSDMachine; diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 12e427cb5..3be73a9fc 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -152,6 +152,22 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +enum { + MEMORY_CLASS_WIRED = 0, + MEMORY_CLASS_ACTIVE, + MEMORY_CLASS_PAGED, + MEMORY_CLASS_INACTIVE, +}; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "wired", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "active", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_2 }, + { .label = "paged", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, + { .label = "inactive", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_4 }, +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -274,21 +290,18 @@ double Platform_setCPUValues(Meter* this, int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; + const NetBSDMachine* nhost = (const NetBSDMachine*) host; this->total = host->totalMem; - this->values[MEMORY_METER_USED] = host->usedMem; - // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" - // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_BUFFERS] = host->buffersMem; - this->values[MEMORY_METER_CACHE] = host->cachedMem; - // this->values[MEMORY_METER_AVAILABLE] = "available memory" + this->values[MEMORY_CLASS_WIRED] = nhost->wiredMem; + this->values[MEMORY_CLASS_ACTIVE] = nhost->activeMem; + this->values[MEMORY_CLASS_PAGED] = nhost->pagedMem; + this->values[MEMORY_CLASS_INACTIVE] = nhost->inactiveMem; } void Platform_setSwapValues(Meter* this) { const Machine* host = this->host; this->total = host->totalSwap; this->values[SWAP_METER_USED] = host->usedSwap; - // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" - // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } char* Platform_getProcessEnv(pid_t pid) { diff --git a/netbsd/Platform.h b/netbsd/Platform.h index 56d10d99c..6ca8cb987 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -19,7 +19,7 @@ in the source distribution for its full text. #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" -#include "Meter.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "Process.h" #include "ProcessLocksScreen.h" @@ -43,6 +43,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/openbsd/OpenBSDMachine.c b/openbsd/OpenBSDMachine.c index 8d9d6ee6b..80baf79e4 100644 --- a/openbsd/OpenBSDMachine.c +++ b/openbsd/OpenBSDMachine.c @@ -142,19 +142,21 @@ static void OpenBSDMachine_scanMemoryInfo(OpenBSDMachine* this) { CRT_fatalError("uvmexp sysctl call failed"); } - super->totalMem = uvmexp.npages * this->pageSizeKB; - super->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * this->pageSizeKB; - // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; struct bcachestats bcstats; size_t size_bcstats = sizeof(bcstats); - if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) { CRT_fatalError("cannot get vfs.bcachestat"); } - super->cachedMem = bcstats.numbufpages * this->pageSizeKB; + // NOTE: in OpenBSD the "cached" memory is a subset of the "wired" memory. + super->totalMem = this->pageSizeKB * uvmexp.npages; + this->wiredMem = this->pageSizeKB * (uvmexp.npages - uvmexp.free - uvmexp.active - uvmexp.paging - bcstats.numbufpages); // NB: uvmexp.wired == 0!? deduct it + this->cacheMem = this->pageSizeKB * bcstats.numbufpages; + this->activeMem = this->pageSizeKB * uvmexp.active; + this->pagingMem = this->pageSizeKB * uvmexp.paging; + this->inactiveMem = this->pageSizeKB * uvmexp.inactive; /* * Copyright (c) 1994 Thorsten Lockert diff --git a/openbsd/OpenBSDMachine.h b/openbsd/OpenBSDMachine.h index 4d599adcd..36946263b 100644 --- a/openbsd/OpenBSDMachine.h +++ b/openbsd/OpenBSDMachine.h @@ -41,6 +41,12 @@ typedef struct OpenBSDMachine_ { Machine super; kvm_t* kd; + memory_t wiredMem; + memory_t cacheMem; + memory_t activeMem; + memory_t pagingMem; + memory_t inactiveMem; + CPUData* cpuData; long fscale; diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 7dc5b4de6..def001b7e 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -100,6 +100,24 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +enum { + MEMORY_CLASS_WIRED = 0, + MEMORY_CLASS_CACHE, + MEMORY_CLASS_ACTIVE, + MEMORY_CLASS_PAGING, + MEMORY_CLASS_INACTIVE, +}; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "wired", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "cache", .countsAsUsed = true, .countsAsCache = true, .color = MEMORY_2 }, + { .label = "active", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, + { .label = "paging", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_4 }, + { .label = "inactive", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_5 }, +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -225,25 +243,25 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; - long int usedMem = host->usedMem; - long int buffersMem = host->buffersMem; - long int cachedMem = host->cachedMem; - usedMem -= buffersMem + cachedMem; + const OpenBSDMachine* ohost = (const OpenBSDMachine*) host; this->total = host->totalMem; - this->values[MEMORY_METER_USED] = usedMem; - // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" - // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_BUFFERS] = buffersMem; - this->values[MEMORY_METER_CACHE] = cachedMem; - // this->values[MEMORY_METER_AVAILABLE] = "available memory" + if (host->settings->showCachedMemory) { + this->values[MEMORY_CLASS_WIRED] = ohost->wiredMem; + this->values[MEMORY_CLASS_CACHE] = ohost->cacheMem; + } + else { // if showCachedMemory is disabled, merge cache into the wired pages + this->values[MEMORY_CLASS_WIRED] = ohost->wiredMem + ohost->cacheMem; + this->values[MEMORY_CLASS_CACHE] = 0; + } + this->values[MEMORY_CLASS_ACTIVE] = ohost->activeMem; + this->values[MEMORY_CLASS_PAGING] = ohost->pagingMem; + this->values[MEMORY_CLASS_INACTIVE] = ohost->inactiveMem; } void Platform_setSwapValues(Meter* this) { const Machine* host = this->host; this->total = host->totalSwap; this->values[SWAP_METER_USED] = host->usedSwap; - // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" - // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } char* Platform_getProcessEnv(pid_t pid) { diff --git a/openbsd/Platform.h b/openbsd/Platform.h index 9568d2bda..2149d103b 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -15,7 +15,7 @@ in the source distribution for its full text. #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "Hashtable.h" -#include "Meter.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "Process.h" #include "ProcessLocksScreen.h" @@ -35,6 +35,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/pcp/Metric.h b/pcp/Metric.h index 26b67a078..71702d252 100644 --- a/pcp/Metric.h +++ b/pcp/Metric.h @@ -58,11 +58,18 @@ typedef enum Metric_ { PCP_PERCPU_GUESTNICE, /* kernel.percpu.cpu.guest_nice */ PCP_MEM_TOTAL, /* mem.physmem */ PCP_MEM_FREE, /* mem.util.free */ + PCP_MEM_ACTIVE, /* mem.util.active */ + PCP_MEM_AVAILABLE, /* mem.util.available */ PCP_MEM_BUFFERS, /* mem.util.bufmem */ PCP_MEM_CACHED, /* mem.util.cached */ + PCP_MEM_COMPRESSED, /* mem.util.compressed */ + PCP_MEM_EXTERNAL, /* mem.util.external */ + PCP_MEM_INACTIVE, /* mem.util.inactive */ PCP_MEM_SHARED, /* mem.util.shared */ - PCP_MEM_AVAILABLE, /* mem.util.available */ + PCP_MEM_PURGEABLE, /* mem.util.purgeable */ + PCP_MEM_SPECULATIVE, /* mem.util.speculative */ PCP_MEM_SRECLAIM, /* mem.util.slabReclaimable */ + PCP_MEM_WIRED, /* mem.util.wired */ PCP_MEM_SWAPCACHED, /* mem.util.swapCached */ PCP_MEM_SWAPTOTAL, /* mem.util.swapTotal */ PCP_MEM_SWAPFREE, /* mem.util.swapFree */ diff --git a/pcp/PCPMachine.c b/pcp/PCPMachine.c index 2e8725346..c8152b587 100644 --- a/pcp/PCPMachine.c +++ b/pcp/PCPMachine.c @@ -50,40 +50,97 @@ static void PCPMachine_updateCPUcount(PCPMachine* this) { this->values = xCalloc(cpus, sizeof(pmAtomValue)); } -static void PCPMachine_updateMemoryInfo(Machine* host) { +static void PCPMachine_updateSystemName(PCPMachine* this) { + pmAtomValue sysname; + if (!Metric_values(PCP_UNAME_SYSNAME, &sysname, 1, PM_TYPE_STRING)) + sysname.cp = NULL; + else if (String_eq(sysname.cp, "Linux")) + this->sys = SYSTEM_NAME_LINUX; + else if (String_eq(sysname.cp, "Darwin")) + this->sys = SYSTEM_NAME_DARWIN; + free(sysname.cp); +} + +static void PCPMachine_updateLinuxMemoryInfo(PCPMachine* this) { + Machine* super = &this->super; unsigned long long int freeMem = 0; unsigned long long int swapFreeMem = 0; unsigned long long int sreclaimableMem = 0; - host->totalMem = host->usedMem = host->cachedMem = 0; - host->usedSwap = host->totalSwap = host->sharedMem = 0; pmAtomValue value; - if (Metric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL) - host->totalMem = value.ull; if (Metric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL) freeMem = value.ull; if (Metric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL) - host->buffersMem = value.ull; + this->memValue[MEMORY_CLASS_BUFFERS] = value.ull; if (Metric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL) sreclaimableMem = value.ull; if (Metric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL) - host->sharedMem = value.ull; + this->memValue[MEMORY_CLASS_SHARED] = value.ull; if (Metric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL) - host->cachedMem = value.ull + sreclaimableMem - host->sharedMem; - const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem; - host->usedMem = (host->totalMem >= usedDiff) ? - host->totalMem - usedDiff : host->totalMem - freeMem; + this->memValue[MEMORY_CLASS_CACHE] = value.ull + sreclaimableMem - this->memValue[MEMORY_CLASS_SHARED]; + const memory_t usedDiff = freeMem + this->memValue[MEMORY_CLASS_CACHE] + sreclaimableMem + this->memValue[MEMORY_CLASS_BUFFERS]; + this->memValue[MEMORY_CLASS_USED] = (super->totalMem >= usedDiff) ? + super->totalMem - usedDiff : super->totalMem - freeMem; if (Metric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL) - host->availableMem = MINIMUM(value.ull, host->totalMem); + this->memValue[MEMORY_CLASS_AVAILABLE] = MINIMUM(value.ull, super->totalMem); else - host->availableMem = freeMem; + this->memValue[MEMORY_CLASS_AVAILABLE] = freeMem; if (Metric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL) swapFreeMem = value.ull; if (Metric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL) - host->totalSwap = value.ull; + super->totalSwap = value.ull; if (Metric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL) - host->cachedSwap = value.ull; - host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap; + super->cachedSwap = value.ull; + super->usedSwap = super->totalSwap - swapFreeMem - super->cachedSwap; +} + +static void PCPMachine_updateDarwinMemoryInfo(PCPMachine* this, Settings* settings) { + unsigned long long int activeMem = 0; + unsigned long long int externalMem = 0; + unsigned long long int purgeableMem = 0; + unsigned long long int speculativeMem = 0; + + pmAtomValue value; + if (Metric_values(PCP_MEM_WIRED, &value, 1, PM_TYPE_U64) != NULL) + this->memValue[MEMORY_CLASS_WIRED] = value.ull; + if (Metric_values(PCP_MEM_ACTIVE, &value, 1, PM_TYPE_U64) != NULL) + activeMem = value.ull; + if (Metric_values(PCP_MEM_EXTERNAL, &value, 1, PM_TYPE_U64) != NULL) + externalMem = value.ull; + if (Metric_values(PCP_MEM_PURGEABLE, &value, 1, PM_TYPE_U64) != NULL) + purgeableMem = value.ull; + if (Metric_values(PCP_MEM_SPECULATIVE, &value, 1, PM_TYPE_U64) != NULL) + speculativeMem = value.ull; + if (settings->showCachedMemory) { + this->memValue[MEMORY_CLASS_SPECULATIVE] = speculativeMem; + this->memValue[MEMORY_CLASS_ACTIVE] = (activeMem - purgeableMem - externalMem); + this->memValue[MEMORY_CLASS_PURGEABLE] = purgeableMem; + } + else { + this->memValue[MEMORY_CLASS_SPECULATIVE] = 0; + this->memValue[MEMORY_CLASS_ACTIVE] = (speculativeMem + activeMem - externalMem); + this->memValue[MEMORY_CLASS_PURGEABLE] = 0; + } + if (Metric_values(PCP_MEM_COMPRESSED, &value, 1, PM_TYPE_U64) != NULL) + this->memValue[MEMORY_CLASS_COMPRESSED] = value.ull; + if (Metric_values(PCP_MEM_INACTIVE, &value, 1, PM_TYPE_U64) != NULL) + this->memValue[MEMORY_CLASS_INACTIVE] = value.ull; +} + +static void PCPMachine_updateMemoryInfo(Machine* super) { + PCPMachine* this = (PCPMachine*) super; + + pmAtomValue value; + if (Metric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL) + super->totalMem = value.ull; + else + super->totalMem = 0; + + memset(this->memValue, 0, sizeof(this->memValue)); + if (this->sys == SYSTEM_NAME_DARWIN) + PCPMachine_updateDarwinMemoryInfo(this, super->settings); + else if (this->sys == SYSTEM_NAME_LINUX) + PCPMachine_updateLinuxMemoryInfo(this); } /* make copies of previously sampled values to avoid overwrite */ @@ -315,6 +372,9 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { gettimeofday(×tamp, NULL); this->timestamp = pmtimevalToReal(×tamp); + this->sys = SYSTEM_NAME_UNKNOWN; + PCPMachine_updateSystemName(this); + this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); PCPMachine_updateCPUcount(this); diff --git a/pcp/PCPMachine.h b/pcp/PCPMachine.h index 6518bd49f..d128a439f 100644 --- a/pcp/PCPMachine.h +++ b/pcp/PCPMachine.h @@ -53,12 +53,39 @@ typedef enum CPUMetric_ { CPU_METRIC_COUNT } CPUMetric; +typedef enum MemoryMetric_ { + // Linux + MEMORY_CLASS_USED = 0, + MEMORY_CLASS_SHARED = 1, + MEMORY_CLASS_BUFFERS = 2, + MEMORY_CLASS_CACHE = 3, + MEMORY_CLASS_COMPRESSED = 4, + MEMORY_CLASS_AVAILABLE = 5, + // Darwin + MEMORY_CLASS_WIRED = 0, + MEMORY_CLASS_SPECULATIVE = 1, + MEMORY_CLASS_ACTIVE = 2, + MEMORY_CLASS_PURGEABLE = 3, + MEMORY_CLASS_INACTIVE = 5, + // Maximum + MEMORY_CLASS_LIMIT = 6 +} MemoryMetric; + +typedef enum SystemName_ { + SYSTEM_NAME_LINUX, + SYSTEM_NAME_DARWIN, + SYSTEM_NAME_UNKNOWN +} SystemName; + typedef struct PCPMachine_ { Machine super; + SystemName sys; int smaps_flag; double period; double timestamp; /* previous sample timestamp */ + memory_t memValue[MEMORY_CLASS_LIMIT]; + pmAtomValue* cpu; /* aggregate values for each metric */ pmAtomValue** percpu; /* per-processor values for each metric */ pmAtomValue* values; /* per-processor buffer for just one metric */ diff --git a/pcp/Platform.c b/pcp/Platform.c index 335ad3bf8..e191bcb58 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -79,6 +79,28 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +static const MemoryClass Linux_memoryClasses[] = { + { .label = "used", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "shared", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_2 }, + { .label = "compressed", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, + { .label = "buffers", .countsAsUsed = false, .countsAsCache = false, .color = MEMORY_4 }, + { .label = "cache", .countsAsUsed = false, .countsAsCache = false, .color = MEMORY_5 }, + { .label = "available", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_6 }, +}; + +static const MemoryClass Darwin_memoryClasses[] = { + { .label = "wired", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "speculative", .countsAsUsed = true, .countsAsCache = true, .color = MEMORY_2 }, + { .label = "active", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_3 }, + { .label = "purgeable", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_4 }, + { .label = "compressed", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_5 }, + { .label = "inactive", .countsAsUsed = true, .countsAsCache = true, .color = MEMORY_6 }, +}; + +MemoryClass Platform_memoryClasses[MEMORY_CLASS_LIMIT]; /* dynamically adjusted */ + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -161,11 +183,18 @@ static const char* Platform_metricNames[] = { [PCP_PERCPU_GUESTNICE] = "kernel.percpu.cpu.guest_nice", [PCP_MEM_TOTAL] = "mem.physmem", [PCP_MEM_FREE] = "mem.util.free", + [PCP_MEM_ACTIVE] = "mem.util.active", [PCP_MEM_AVAILABLE] = "mem.util.available", [PCP_MEM_BUFFERS] = "mem.util.bufmem", [PCP_MEM_CACHED] = "mem.util.cached", + [PCP_MEM_COMPRESSED] = "mem.util.compressed", + [PCP_MEM_EXTERNAL] = "mem.util.external", + [PCP_MEM_INACTIVE] = "mem.util.inactive", + [PCP_MEM_PURGEABLE] = "mem.util.purgeable", [PCP_MEM_SHARED] = "mem.util.shmem", + [PCP_MEM_SPECULATIVE] = "mem.util.speculative", [PCP_MEM_SRECLAIM] = "mem.util.slabReclaimable", + [PCP_MEM_WIRED] = "mem.util.wired", [PCP_MEM_SWAPCACHED] = "mem.util.swapCached", [PCP_MEM_SWAPTOTAL] = "mem.util.swapTotal", [PCP_MEM_SWAPFREE] = "mem.util.swapFree", @@ -558,34 +587,53 @@ double Platform_setCPUValues(Meter* this, int cpu) { return Platform_setOneCPUValues(this, settings, phost->percpu[cpu - 1]); } -void Platform_setMemoryValues(Meter* this) { - const Machine* host = this->host; - const PCPMachine* phost = (const PCPMachine*) host; +static void Platform_setLinuxMemoryValues(double* v, const PCPMachine *host) { + v[MEMORY_CLASS_USED] = host->memValue[MEMORY_CLASS_USED]; + v[MEMORY_CLASS_SHARED] = host->memValue[MEMORY_CLASS_SHARED]; + v[MEMORY_CLASS_BUFFERS] = host->memValue[MEMORY_CLASS_BUFFERS]; + v[MEMORY_CLASS_CACHE] = host->memValue[MEMORY_CLASS_CACHE]; + v[MEMORY_CLASS_AVAILABLE] = host->memValue[MEMORY_CLASS_AVAILABLE]; - this->total = host->totalMem; - this->values[MEMORY_METER_USED] = host->usedMem; - this->values[MEMORY_METER_SHARED] = host->sharedMem; - this->values[MEMORY_METER_COMPRESSED] = 0; - this->values[MEMORY_METER_BUFFERS] = host->buffersMem; - this->values[MEMORY_METER_CACHE] = host->cachedMem; - this->values[MEMORY_METER_AVAILABLE] = host->availableMem; - - if (phost->zfs.enabled != 0) { + if (host->zfs.enabled != 0) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (phost->zfs.size > phost->zfs.min) - shrinkableSize = phost->zfs.size - phost->zfs.min; - this->values[MEMORY_METER_USED] -= shrinkableSize; - this->values[MEMORY_METER_CACHE] += shrinkableSize; - this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; + if (host->zfs.size > host->zfs.min) + shrinkableSize = host->zfs.size - host->zfs.min; + v[MEMORY_CLASS_USED] -= shrinkableSize; + v[MEMORY_CLASS_CACHE] += shrinkableSize; + v[MEMORY_CLASS_AVAILABLE] += shrinkableSize; } - if (phost->zswap.usedZswapOrig > 0 || phost->zswap.usedZswapComp > 0) { - this->values[MEMORY_METER_USED] -= phost->zswap.usedZswapComp; - this->values[MEMORY_METER_COMPRESSED] += phost->zswap.usedZswapComp; + if (host->zswap.usedZswapOrig > 0 || host->zswap.usedZswapComp > 0) { + v[MEMORY_CLASS_USED] -= host->zswap.usedZswapComp; + v[MEMORY_CLASS_COMPRESSED] = host->zswap.usedZswapComp; + } else { + v[MEMORY_CLASS_COMPRESSED] = 0; } } +static void Platform_setDarwinMemoryValues(double* v, const PCPMachine *host) { + v[MEMORY_CLASS_WIRED] = host->memValue[MEMORY_CLASS_WIRED]; + v[MEMORY_CLASS_SPECULATIVE] = host->memValue[MEMORY_CLASS_SPECULATIVE]; + v[MEMORY_CLASS_ACTIVE] = host->memValue[MEMORY_CLASS_ACTIVE]; + v[MEMORY_CLASS_PURGEABLE] = host->memValue[MEMORY_CLASS_PURGEABLE]; + v[MEMORY_CLASS_COMPRESSED] = host->memValue[MEMORY_CLASS_COMPRESSED]; + v[MEMORY_CLASS_INACTIVE] = host->memValue[MEMORY_CLASS_INACTIVE]; +} + +void Platform_setMemoryValues(Meter* this) { + const Machine* host = this->host; + const PCPMachine* phost = (const PCPMachine*) host; + + this->total = host->totalMem; + if (phost->sys == SYSTEM_NAME_LINUX) + Platform_setLinuxMemoryValues(this->values, phost); + else if (phost->sys == SYSTEM_NAME_DARWIN) + Platform_setDarwinMemoryValues(this->values, phost); + else + memset(this->values, 0, sizeof(phost->memValue)); +} + void Platform_setSwapValues(Meter* this) { const Machine* host = this->host; const PCPMachine* phost = (const PCPMachine*) host; @@ -671,6 +719,12 @@ static void Platform_setRelease(void) { if (!Metric_values(PCP_UNAME_DISTRO, &distro, 1, PM_TYPE_STRING)) distro.cp = NULL; + /* set global memory class model using sysname */ + if (sysname.cp && String_eq(sysname.cp, "Darwin")) + memcpy(Platform_memoryClasses, Darwin_memoryClasses, sizeof(Darwin_memoryClasses)); + else /* default to the Linux memory categories */ + memcpy(Platform_memoryClasses, Linux_memoryClasses, sizeof(Linux_memoryClasses)); + size_t length = 16; /* padded for formatting characters */ if (sysname.cp) length += strlen(sysname.cp); @@ -705,6 +759,7 @@ static void Platform_setRelease(void) { strcat(pcp->release, " "); } + if (pcp->release) /* cull trailing space */ pcp->release[strlen(pcp->release)] = '\0'; diff --git a/pcp/Platform.h b/pcp/Platform.h index 0b4abd62b..7ec0d2be6 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -28,7 +28,7 @@ in the source distribution for its full text. #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "Hashtable.h" -#include "Meter.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "Process.h" #include "ProcessLocksScreen.h" @@ -69,6 +69,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/solaris/Platform.c b/solaris/Platform.c index 3934f7896..d206d3d2a 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -101,6 +101,18 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +enum { + MEMORY_CLASS_USED = 0, + MEMORY_CLASS_LOCKED, +}; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "used", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "locked", .countsAsUsed = true, .countsAsCache = true, .color = MEMORY_2 }, +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -240,21 +252,16 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; + const SolarisMachine* shost = (const SolarisMachine*) host; this->total = host->totalMem; - this->values[MEMORY_METER_USED] = host->usedMem; - // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" - // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_BUFFERS] = host->buffersMem; - this->values[MEMORY_METER_CACHE] = host->cachedMem; - // this->values[MEMORY_METER_AVAILABLE] = "available memory" + this->values[MEMORY_CLASS_USED] = shost->usedMem; + this->values[MEMORY_CLASS_LOCKED] = shost->lockedMem; } void Platform_setSwapValues(Meter* this) { const Machine* host = this->host; this->total = host->totalSwap; this->values[SWAP_METER_USED] = host->usedSwap; - // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" - // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { diff --git a/solaris/Platform.h b/solaris/Platform.h index 9bb09cd13..d58be10b5 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -31,6 +31,7 @@ in the source distribution for its full text. #include "CommandLine.h" #include "DiskIOMeter.h" #include "Hashtable.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" @@ -58,6 +59,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/solaris/SolarisMachine.c b/solaris/SolarisMachine.c index d8e8a0b2f..3c4a14df8 100644 --- a/solaris/SolarisMachine.c +++ b/solaris/SolarisMachine.c @@ -165,18 +165,15 @@ static void SolarisMachine_scanCPUTime(SolarisMachine* this) { static void SolarisMachine_scanMemoryInfo(SolarisMachine* this) { Machine* super = &this->super; - static kstat_t *meminfo = NULL; - int ksrphyserr = -1; - kstat_named_t *totalmem_pgs = NULL; - kstat_named_t *freemem_pgs = NULL; - kstat_named_t *pages = NULL; - struct swaptable *sl = NULL; - struct swapent *swapdev = NULL; + static kstat_t* meminfo = NULL; + struct swaptable* sl = NULL; + struct swapent* swapdev = NULL; uint64_t totalswap = 0; uint64_t totalfree = 0; + int ksrphyserr = -1; int nswap = 0; - char *spath = NULL; - char *spathbase = NULL; + char* spath = NULL; + char* spathbase = NULL; // Part 1 - physical memory if (this->kd != NULL && meminfo == NULL) { @@ -187,27 +184,19 @@ static void SolarisMachine_scanMemoryInfo(SolarisMachine* this) { ksrphyserr = kstat_read(this->kd, meminfo, NULL); } if (ksrphyserr != -1) { - totalmem_pgs = kstat_data_lookup_wrapper(meminfo, "physmem"); - freemem_pgs = kstat_data_lookup_wrapper(meminfo, "freemem"); - pages = kstat_data_lookup_wrapper(meminfo, "pagestotal"); - - super->totalMem = totalmem_pgs->value.ui64 * this->pageSizeKB; - if (super->totalMem > freemem_pgs->value.ui64 * this->pageSizeKB) { - super->usedMem = super->totalMem - freemem_pgs->value.ui64 * this->pageSizeKB; - } else { - super->usedMem = 0; // This can happen in non-global zone (in theory) - } - // Not sure how to implement this on Solaris - suggestions welcome! - super->cachedMem = 0; - // Not really "buffers" but the best Solaris analogue that I can find to - // "memory in use but not by programs or the kernel itself" - super->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * this->pageSizeKB; + kstat_named_t* physmem = kstat_data_lookup_wrapper(meminfo, "physmem"); + kstat_named_t* pagesfree = kstat_data_lookup_wrapper(meminfo, "pagesfree"); + kstat_named_t* pagestotal = kstat_data_lookup_wrapper(meminfo, "pagestotal"); + kstat_named_t* pageslocked = kstat_data_lookup_wrapper(meminfo, "pageslocked"); + + super->totalMem = physmem->value.ui64 * this->pageSizeKB; + this->usedMem = (pagestotal->value.ui64 - pageslocked->value.ui64 - pagesfree->value.ui64) * this->pageSizeKB; + this->lockedMem = pageslocked->value.ui64 * this->pageSizeKB; } else { // Fall back to basic sysconf if kstat isn't working super->totalMem = sysconf(_SC_PHYS_PAGES) * this->pageSize; - super->buffersMem = 0; - super->cachedMem = 0; - super->usedMem = super->totalMem - (sysconf(_SC_AVPHYS_PAGES) * this->pageSize); + this->usedMem = super->totalMem - (sysconf(_SC_AVPHYS_PAGES) * this->pageSize); + this->lockedMem = 0; } // Part 2 - swap diff --git a/solaris/SolarisMachine.h b/solaris/SolarisMachine.h index 00fc77ba3..a117e48cb 100644 --- a/solaris/SolarisMachine.h +++ b/solaris/SolarisMachine.h @@ -53,6 +53,9 @@ typedef struct SolarisMachine_ { size_t pageSize; size_t pageSizeKB; + memory_t usedMem; + memory_t lockedMem; + ZfsArcStats zfs; } SolarisMachine; diff --git a/unsupported/Platform.c b/unsupported/Platform.c index 2ab1ab6e9..94c8171c3 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -44,6 +44,18 @@ const SignalItem Platform_signals[] = { const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); +typedef enum { + MEMORY_CLASS_USED = 0, + MEMORY_CLASS_CACHED, +} MemoryClasses; + +const MemoryClass Platform_memoryClasses[] = { + { .label = "used", .countsAsUsed = true, .countsAsCache = false, .color = MEMORY_1 }, + { .label = "cached", .countsAsUsed = false, .countsAsCache = true, .color = MEMORY_2 }, +}; // N.B. the chart will display categories in this order + +const unsigned int Platform_numberOfMemoryClasses = ARRAYSIZE(Platform_memoryClasses); + const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, @@ -119,7 +131,11 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { } void Platform_setMemoryValues(Meter* this) { - (void) this; + double* v = this->values; + v[MEMORY_CLASS_USED] = NAN; + v[MEMORY_CLASS_CACHED] = NAN; + + this->curItems = 2; } void Platform_setSwapValues(Meter* this) { diff --git a/unsupported/Platform.h b/unsupported/Platform.h index d8eb740a5..7b03011f9 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -15,6 +15,7 @@ in the source distribution for its full text. #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "Hashtable.h" +#include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" @@ -31,6 +32,10 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; +extern const MemoryClass Platform_memoryClasses[]; + +extern const unsigned int Platform_numberOfMemoryClasses; + extern const MeterClass* const Platform_meterTypes[]; bool Platform_init(void); diff --git a/unsupported/UnsupportedMachine.c b/unsupported/UnsupportedMachine.c index fd5cc72f5..a13324a66 100644 --- a/unsupported/UnsupportedMachine.c +++ b/unsupported/UnsupportedMachine.c @@ -42,17 +42,13 @@ bool Machine_isCPUonline(const Machine* host, unsigned int id) { } void Machine_scan(Machine* super) { + UnsupportedMachine* this = (UnsupportedMachine*) super; super->existingCPUs = 1; super->activeCPUs = 1; - - super->totalMem = 0; - super->usedMem = 0; - super->buffersMem = 0; - super->cachedMem = 0; - super->sharedMem = 0; - super->availableMem = 0; - super->totalSwap = 0; super->usedSwap = 0; super->cachedSwap = 0; + super->totalMem = 0; + this->usedMem = 0; + this->cachedMem = 0; } diff --git a/unsupported/UnsupportedMachine.h b/unsupported/UnsupportedMachine.h index 4ec760f17..bc507268e 100644 --- a/unsupported/UnsupportedMachine.h +++ b/unsupported/UnsupportedMachine.h @@ -12,6 +12,8 @@ in the source distribution for its full text. typedef struct UnsupportedMachine_ { Machine super; + memory_t usedMem; + memory_t cachedMem; } UnsupportedMachine; #endif