From f0ed0fdafb9ecefc9d103ffb8f5d91bf723518f6 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Wed, 23 Jun 2021 17:44:56 +1000 Subject: [PATCH] Add a new DynamicMeter class for runtime Meter extension This commit is based on exploratory work by Sohaib Mohamed. The end goal is two-fold - to support addition of Meters we build via configuration files for both the PCP platform and for scripts ( https://github.com/htop-dev/htop/issues/526 ) Here, we focus on generic code and the PCP support. A new class DynamicMeter is introduced - it uses the special case 'param' field handling that previously was used only by the CPUMeter, such that every runtime-configured Meter is given a unique identifier. Unlike with the CPUMeter this is used internally only. When reading/writing to htoprc instead of CPU(N) - where N is an integer param (CPU number) - we use the string name for each meter. For example, if we have a configuration for a DynamicMeter for some Redis metrics, we might read and write "Dynamic(redis)". This identifier is subsequently matched (back) up to the configuration file so we're able to re-create arbitrary user configurations. The PCP platform configuration file format is fairly simple. We expand configs from several directories, including the users homedir alongside htoprc (below htop/meters/) and also /etc/pcp/htop/meters. The format will be described via a new pcp-htop(5) man page, but its basically ini-style and each Meter has one or more metric expressions associated, as well as specifications for labels, color and so on via a dot separated notation for individual metrics within the Meter. A few initial sample configuration files are provided below ./pcp/meters that give the general idea. The PCP "derived" metric specification - see pmRegisterDerived(3) - is used as the syntax for specifying metrics in PCP DynamicMeters. --- AvailableMetersPanel.c | 73 +++-- CPUMeter.c | 9 + CRT.c | 56 +++- CRT.h | 9 + CommandLine.c | 4 +- DynamicMeter.c | 98 +++++++ DynamicMeter.h | 25 ++ Header.c | 17 +- Makefile.am | 4 + Meter.c | 13 +- Meter.h | 6 +- ProcessList.c | 3 +- ProcessList.h | 6 +- darwin/DarwinProcessList.c | 4 +- darwin/DarwinProcessList.h | 2 +- darwin/Platform.h | 8 + dragonflybsd/DragonFlyBSDProcessList.c | 4 +- dragonflybsd/Platform.h | 8 + freebsd/FreeBSDProcessList.c | 4 +- freebsd/FreeBSDProcessList.h | 2 +- freebsd/Platform.h | 8 + linux/LinuxProcessList.c | 4 +- linux/LinuxProcessList.h | 2 +- linux/Platform.h | 8 + openbsd/OpenBSDProcessList.c | 4 +- openbsd/OpenBSDProcessList.h | 2 +- openbsd/Platform.h | 8 + pcp/PCPDynamicMeter.c | 368 +++++++++++++++++++++++++ pcp/PCPDynamicMeter.h | 36 +++ pcp/PCPProcessList.c | 4 +- pcp/PCPProcessList.h | 2 +- pcp/Platform.c | 52 +++- pcp/Platform.h | 15 +- pcp/meters/entropy | 9 + pcp/meters/freespace | 11 + pcp/meters/ipc | 13 + pcp/meters/locks | 15 + pcp/meters/memcache | 11 + pcp/meters/mysql | 73 +++++ pcp/meters/postfix | 21 ++ pcp/meters/redis | 41 +++ pcp/meters/tcp | 22 ++ solaris/Platform.h | 8 + solaris/SolarisProcessList.c | 4 +- solaris/SolarisProcessList.h | 2 +- unsupported/Platform.h | 8 + unsupported/UnsupportedProcessList.c | 4 +- unsupported/UnsupportedProcessList.h | 2 +- 48 files changed, 1044 insertions(+), 68 deletions(-) create mode 100644 DynamicMeter.c create mode 100644 DynamicMeter.h create mode 100644 pcp/PCPDynamicMeter.c create mode 100644 pcp/PCPDynamicMeter.h create mode 100644 pcp/meters/entropy create mode 100644 pcp/meters/freespace create mode 100644 pcp/meters/ipc create mode 100644 pcp/meters/locks create mode 100644 pcp/meters/memcache create mode 100644 pcp/meters/mysql create mode 100644 pcp/meters/postfix create mode 100644 pcp/meters/redis create mode 100644 pcp/meters/tcp diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index 22f2c5ef..048c7175 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -12,6 +12,7 @@ in the source distribution for its full text. #include #include "CPUMeter.h" +#include "DynamicMeter.h" #include "FunctionBar.h" #include "Header.h" #include "ListItem.h" @@ -91,6 +92,49 @@ const PanelClass AvailableMetersPanel_class = { .eventHandler = AvailableMetersPanel_eventHandler }; +// Handle (&CPUMeter_class) entries in the AvailableMetersPanel +static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const ProcessList* pl) { + if (pl->cpuCount > 1) { + Panel_add(super, (Object*) ListItem_new("CPU average", 0)); + for (unsigned int i = 1; i <= pl->cpuCount; i++) { + char buffer[50]; + xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(pl->settings, i - 1)); + Panel_add(super, (Object*) ListItem_new(buffer, i)); + } + } else { + Panel_add(super, (Object*) ListItem_new(type->uiName, 1)); + } +} + +typedef struct { + Panel* super; + unsigned int id; + unsigned int offset; +} DynamicIterator; + +static void AvailableMetersPanel_addDynamicMeter(ATTR_UNUSED ht_key_t key, void* value, void* data) { + const DynamicMeter* meter = (const DynamicMeter*)value; + DynamicIterator* iter = (DynamicIterator*)data; + unsigned int identifier = (iter->offset << 16) | iter->id; + const char* label = meter->description ? meter->description : meter->caption; + if (!label) label = meter->name; /* last fallback to name, guaranteed set */ + Panel_add(iter->super, (Object*) ListItem_new(label, identifier)); + iter->id++; +} + +// Handle (&DynamicMeter_class) entries in the AvailableMetersPanel +static void AvailableMetersPanel_addDynamicMeters(Panel* super, const ProcessList *pl, unsigned int offset) { + DynamicIterator iter = { .super = super, .id = 1, .offset = offset }; + assert(pl->dynamicMeters != NULL); + Hashtable_foreach(pl->dynamicMeters, AvailableMetersPanel_addDynamicMeter, &iter); +} + +// Handle remaining Platform Meter entries in the AvailableMetersPanel +static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass* type, unsigned int offset) { + const char* label = type->description ? type->description : type->uiName; + Panel_add(super, (Object*) ListItem_new(label, offset << 16)); +} + AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, const ProcessList* pl) { AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); Panel* super = (Panel*) this; @@ -104,26 +148,19 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade this->scr = scr; Panel_setHeader(super, "Available meters"); - // Platform_meterTypes[0] should be always (&CPUMeter_class), which we will - // handle separately in the code below. - for (int i = 1; Platform_meterTypes[i]; i++) { + // Platform_meterTypes[0] should be always (&CPUMeter_class) which we will + // handle separately in the code below. Likewise, identifiers for Dynamic + // Meters are handled separately - similar to CPUs, this allows generation + // of multiple different Meters (also using 'param' to distinguish them). + for (unsigned int i = 1; Platform_meterTypes[i]; i++) { const MeterClass* type = Platform_meterTypes[i]; assert(type != &CPUMeter_class); - const char* label = type->description ? type->description : type->uiName; - Panel_add(super, (Object*) ListItem_new(label, i << 16)); - } - // Handle (&CPUMeter_class) - const MeterClass* type = &CPUMeter_class; - unsigned int cpus = pl->cpuCount; - if (cpus > 1) { - Panel_add(super, (Object*) ListItem_new("CPU average", 0)); - for (unsigned int i = 1; i <= cpus; i++) { - char buffer[50]; - xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1)); - Panel_add(super, (Object*) ListItem_new(buffer, i)); - } - } else { - Panel_add(super, (Object*) ListItem_new("CPU", 1)); + if (type == &DynamicMeter_class) + AvailableMetersPanel_addDynamicMeters(super, pl, i); + else + AvailableMetersPanel_addPlatformMeter(super, type, i); } + AvailableMetersPanel_addCPUMeters(super, &CPUMeter_class, pl); + return this; } diff --git a/CPUMeter.c b/CPUMeter.c index 1e30ce34..0bea79fd 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -50,6 +50,14 @@ static void CPUMeter_init(Meter* this) { } } +// Custom uiName runtime logic to include the param (processor) +static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) { + if (this->param > 0) + xSnprintf(buffer, sizeof(length), "%s %u", Meter_uiName(this), this->param); + else + xSnprintf(buffer, sizeof(length), "%s", Meter_uiName(this)); +} + static void CPUMeter_updateValues(Meter* this) { unsigned int cpu = this->param; if (cpu > this->pl->cpuCount) { @@ -326,6 +334,7 @@ const MeterClass CPUMeter_class = { .display = CPUMeter_display }, .updateValues = CPUMeter_updateValues, + .getUiName = CPUMeter_getUiName, .defaultMode = BAR_METERMODE, .maxItems = CPU_METER_ITEMCOUNT, .total = 100.0, diff --git a/CRT.c b/CRT.c index 5353c7b7..6418f9ad 100644 --- a/CRT.c +++ b/CRT.c @@ -195,6 +195,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_COMPRESSED] = ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), + [DYNAMIC_GRAY] = ColorPairGrayBlack, + [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, + [DYNAMIC_RED] = ColorPair(Red, Black), + [DYNAMIC_GREEN] = ColorPair(Green, Black), + [DYNAMIC_BLUE] = ColorPair(Blue, Black), + [DYNAMIC_CYAN] = ColorPair(Cyan, Black), + [DYNAMIC_MAGENTA] = ColorPair(Magenta, Black), + [DYNAMIC_YELLOW] = ColorPair(Yellow, Black), + [DYNAMIC_WHITE] = ColorPair(White, Black), }, [COLORSCHEME_MONOCHROME] = { [RESET_COLOR] = A_NORMAL, @@ -288,6 +297,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_COMPRESSED] = A_BOLD, [ZFS_RATIO] = A_BOLD, [ZRAM] = A_NORMAL, + [DYNAMIC_GRAY] = A_DIM, + [DYNAMIC_DARKGRAY] = A_DIM, + [DYNAMIC_RED] = A_BOLD, + [DYNAMIC_GREEN] = A_NORMAL, + [DYNAMIC_BLUE] = A_NORMAL, + [DYNAMIC_CYAN] = A_BOLD, + [DYNAMIC_MAGENTA] = A_NORMAL, + [DYNAMIC_YELLOW] = A_NORMAL, + [DYNAMIC_WHITE] = A_BOLD, }, [COLORSCHEME_BLACKONWHITE] = { [RESET_COLOR] = ColorPair(Black, White), @@ -380,7 +398,16 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = ColorPair(Magenta, White), [ZFS_COMPRESSED] = ColorPair(Cyan, White), [ZFS_RATIO] = ColorPair(Magenta, White), - [ZRAM] = ColorPair(Yellow, White) + [ZRAM] = ColorPair(Yellow, White), + [DYNAMIC_GRAY] = ColorPair(Black, White), + [DYNAMIC_DARKGRAY] = A_BOLD | ColorPair(Black, White), + [DYNAMIC_RED] = ColorPair(Red, White), + [DYNAMIC_GREEN] = ColorPair(Green, White), + [DYNAMIC_BLUE] = ColorPair(Blue, White), + [DYNAMIC_CYAN] = ColorPair(Yellow, White), + [DYNAMIC_MAGENTA] = ColorPair(Magenta, White), + [DYNAMIC_YELLOW] = ColorPair(Yellow, White), + [DYNAMIC_WHITE] = A_BOLD | ColorPair(Black, White), }, [COLORSCHEME_LIGHTTERMINAL] = { [RESET_COLOR] = ColorPair(Black, Black), @@ -474,6 +501,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_COMPRESSED] = ColorPair(Cyan, Black), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), + [DYNAMIC_GRAY] = ColorPairGrayBlack, + [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, + [DYNAMIC_RED] = ColorPair(Red, Black), + [DYNAMIC_GREEN] = ColorPair(Green, Black), + [DYNAMIC_BLUE] = ColorPair(Blue, Black), + [DYNAMIC_CYAN] = ColorPair(Cyan, Black), + [DYNAMIC_MAGENTA] = ColorPair(Magenta, Black), + [DYNAMIC_YELLOW] = ColorPair(Yellow, Black), + [DYNAMIC_WHITE] = ColorPairWhiteDefault, }, [COLORSCHEME_MIDNIGHT] = { [RESET_COLOR] = ColorPair(White, Blue), @@ -567,6 +603,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue), [ZRAM] = A_BOLD | ColorPair(Yellow, Blue), + [DYNAMIC_GRAY] = ColorPairGrayBlack, + [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, + [DYNAMIC_RED] = ColorPair(Red, Blue), + [DYNAMIC_GREEN] = ColorPair(Green, Blue), + [DYNAMIC_BLUE] = ColorPair(Black, Blue), + [DYNAMIC_CYAN] = ColorPair(Cyan, Blue), + [DYNAMIC_MAGENTA] = ColorPair(Magenta, Blue), + [DYNAMIC_YELLOW] = ColorPair(Yellow, Blue), + [DYNAMIC_WHITE] = ColorPair(White, Blue), }, [COLORSCHEME_BLACKNIGHT] = { [RESET_COLOR] = ColorPair(Cyan, Black), @@ -658,6 +703,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_COMPRESSED] = ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), + [DYNAMIC_GRAY] = ColorPairGrayBlack, + [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, + [DYNAMIC_RED] = ColorPair(Red, Black), + [DYNAMIC_GREEN] = ColorPair(Green, Black), + [DYNAMIC_BLUE] = ColorPair(Blue, Black), + [DYNAMIC_CYAN] = ColorPair(Cyan, Black), + [DYNAMIC_MAGENTA] = ColorPair(Magenta, Black), + [DYNAMIC_YELLOW] = ColorPair(Yellow, Black), + [DYNAMIC_WHITE] = ColorPair(White, Black), }, [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. }; diff --git a/CRT.h b/CRT.h index e85247bd..9f3ecb44 100644 --- a/CRT.h +++ b/CRT.h @@ -131,6 +131,15 @@ typedef enum ColorElements_ { ZFS_COMPRESSED, ZFS_RATIO, ZRAM, + DYNAMIC_GRAY, + DYNAMIC_DARKGRAY, + DYNAMIC_RED, + DYNAMIC_GREEN, + DYNAMIC_BLUE, + DYNAMIC_CYAN, + DYNAMIC_MAGENTA, + DYNAMIC_YELLOW, + DYNAMIC_WHITE, LAST_COLORELEMENT } ColorElements; diff --git a/CommandLine.c b/CommandLine.c index 60582dcf..4a721a3d 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -22,6 +22,7 @@ in the source distribution for its full text. #include "Action.h" #include "CRT.h" +#include "DynamicMeter.h" #include "Hashtable.h" #include "Header.h" #include "IncSet.h" @@ -288,7 +289,8 @@ int CommandLine_run(const char* name, int argc, char** argv) { Process_setupColumnWidths(); UsersTable* ut = UsersTable_new(); - ProcessList* pl = ProcessList_new(ut, flags.pidMatchList, flags.userId); + Hashtable* dm = DynamicMeters_new(); + ProcessList* pl = ProcessList_new(ut, dm, flags.pidMatchList, flags.userId); Settings* settings = Settings_new(pl->cpuCount); pl->settings = settings; diff --git a/DynamicMeter.c b/DynamicMeter.c new file mode 100644 index 00000000..7b39fdaa --- /dev/null +++ b/DynamicMeter.c @@ -0,0 +1,98 @@ +/* +htop - DynamicMeter.c +(C) 2021 htop dev team +(C) 2021 Red Hat, Inc. All Rights Reserved. +Released under the GNU GPLv2, see the COPYING file +in the source distribution for its full text. +*/ +#include "config.h" // IWYU pragma: keep + +#include "DynamicMeter.h" + +#include "CRT.h" +#include "Object.h" +#include "Platform.h" +#include "ProcessList.h" +#include "RichString.h" +#include "XUtils.h" + + +static const int DynamicMeter_attributes[] = { + DYNAMIC_GRAY, + DYNAMIC_DARKGRAY, + DYNAMIC_RED, + DYNAMIC_GREEN, + DYNAMIC_BLUE, + DYNAMIC_CYAN, + DYNAMIC_MAGENTA, + DYNAMIC_YELLOW, + DYNAMIC_WHITE +}; + +Hashtable* DynamicMeters_new(void) { + return Platform_dynamicMeters(); +} + +typedef struct { + unsigned int key; + const char* name; +} DynamicIterator; + +static void DynamicMeter_compare(ht_key_t key, void* value, void* data) { + const DynamicMeter* meter = (const DynamicMeter*)value; + DynamicIterator* iter = (DynamicIterator*)data; + if (String_eq(iter->name, meter->name)) + iter->key = key; +} + +unsigned int DynamicMeter_search(const ProcessList* pl, const char* name) { + DynamicIterator iter = { .key = 0, .name = name }; + if (pl->dynamicMeters) + Hashtable_foreach(pl->dynamicMeters, DynamicMeter_compare, &iter); + return iter.key; +} + +const char* DynamicMeter_lookup(const ProcessList* pl, unsigned int key) { + const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, key); + return meter ? meter->name : NULL; +} + +static void DynamicMeter_init(Meter* meter) { + Platform_dynamicMeterInit(meter); +} + +static void DynamicMeter_updateValues(Meter* meter) { + Platform_dynamicMeterUpdateValues(meter); +} + +static void DynamicMeter_display(const Object* cast, RichString* out) { + const Meter* meter = (const Meter*)cast; + Platform_dynamicMeterDisplay(meter, out); +} + +static void DynamicMeter_getUiName(const Meter* this, char* name, size_t length) { + const ProcessList* pl = this->pl; + const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, this->param); + if (meter) { + const char* uiName = meter->caption ? meter->caption : meter->name; + xSnprintf(name, length, "%s", uiName); + } +} + +const MeterClass DynamicMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = DynamicMeter_display + }, + .init = DynamicMeter_init, + .updateValues = DynamicMeter_updateValues, + .getUiName = DynamicMeter_getUiName, + .defaultMode = TEXT_METERMODE, + .maxItems = 0, + .total = 100.0, + .attributes = DynamicMeter_attributes, + .name = "Dynamic", + .uiName = "Dynamic", + .caption = "", +}; diff --git a/DynamicMeter.h b/DynamicMeter.h new file mode 100644 index 00000000..3d1bc9f2 --- /dev/null +++ b/DynamicMeter.h @@ -0,0 +1,25 @@ +#ifndef HEADER_DynamicMeter +#define HEADER_DynamicMeter + +#include "Meter.h" + + +typedef struct DynamicMeter_ { + char name[32]; /* unique name, cannot contain spaces */ + char* caption; + char* description; + unsigned int type; + double maximum; + + void* dynamicData; /* platform-specific meter data */ +} DynamicMeter; + +Hashtable* DynamicMeters_new(void); + +const char* DynamicMeter_lookup(const ProcessList* pl, unsigned int param); + +unsigned int DynamicMeter_search(const ProcessList* pl, const char* name); + +extern const MeterClass DynamicMeter_class; + +#endif diff --git a/Header.c b/Header.c index d2d7f694..0fdafea5 100644 --- a/Header.c +++ b/Header.c @@ -13,6 +13,8 @@ in the source distribution for its full text. #include #include "CRT.h" +#include "CPUMeter.h" +#include "DynamicMeter.h" #include "Macros.h" #include "Object.h" #include "Platform.h" @@ -70,7 +72,10 @@ void Header_writeBackToSettings(const Header* this) { for (int i = 0; i < len; i++) { const Meter* meter = (Meter*) Vector_get(vec, i); char* name; - if (meter->param) { + if (meter->param && As_Meter(meter) == &DynamicMeter_class) { + const char* dynamic = DynamicMeter_lookup(this->pl, meter->param); + xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic); + } else if (meter->param && As_Meter(meter) == &CPUMeter_class) { xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param); } else { xAsprintf(&name, "%s", As_Meter(meter)->name); @@ -87,9 +92,13 @@ MeterModeId Header_addMeterByName(Header* this, const char* name, int column) { char* paren = strchr(name, '('); unsigned int param = 0; if (paren) { - int ok = sscanf(paren, "(%10u)", ¶m); - if (!ok) - param = 0; + char* end, dynamic[32] = {0}; + int ok = sscanf(paren, "(%10u)", ¶m); // CPUMeter + if (!ok) { + ok = sscanf(paren, "(%30s)", dynamic); // DynamicMeter + if (ok && (end = strrchr(dynamic, ')'))) *end = '\0'; + param = ok ? DynamicMeter_search(this->pl, dynamic) : 0; + } *paren = '\0'; } MeterModeId mode = TEXT_METERMODE; diff --git a/Makefile.am b/Makefile.am index 079bcd86..ce50d0f7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,6 +39,7 @@ myhtopsources = \ DateTimeMeter.c \ DiskIOMeter.c \ DisplayOptionsPanel.c \ + DynamicMeter.c \ EnvScreen.c \ FunctionBar.c \ Hashtable.c \ @@ -93,6 +94,7 @@ myhtopheaders = \ DateTimeMeter.h \ DiskIOMeter.h \ DisplayOptionsPanel.h \ + DynamicMeter.h \ EnvScreen.h \ FunctionBar.h \ Hashtable.h \ @@ -357,6 +359,7 @@ endif # -------------------------- pcp_platform_headers = \ + pcp/PCPDynamicMeter.h \ pcp/PCPProcess.h \ pcp/PCPProcessList.h \ pcp/Platform.h \ @@ -369,6 +372,7 @@ pcp_platform_headers = \ zfs/ZfsCompressedArcMeter.h pcp_platform_sources = \ + pcp/PCPDynamicMeter.c \ pcp/PCPProcess.c \ pcp/PCPProcessList.c \ pcp/Platform.c \ diff --git a/Meter.c b/Meter.c index ae867e1c..0052773c 100644 --- a/Meter.c +++ b/Meter.c @@ -138,14 +138,13 @@ ListItem* Meter_toListItem(const Meter* this, bool moving) { } else { mode[0] = '\0'; } - char number[10]; - if (this->param > 0) { - xSnprintf(number, sizeof(number), " %u", this->param); - } else { - number[0] = '\0'; - } + char name[32]; + if (Meter_getUiNameFn(this)) + Meter_getUiName(this, name, sizeof(name)); + else + xSnprintf(name, sizeof(name), "%s", Meter_uiName(this)); char buffer[50]; - xSnprintf(buffer, sizeof(buffer), "%s%s%s", Meter_uiName(this), number, mode); + xSnprintf(buffer, sizeof(buffer), "%s%s", name, mode); ListItem* li = ListItem_new(buffer, 0); li->moving = moving; return li; diff --git a/Meter.h b/Meter.h index cd9c9b8e..40cad332 100644 --- a/Meter.h +++ b/Meter.h @@ -53,14 +53,16 @@ typedef void(*Meter_Done)(Meter*); typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*Meter_UpdateValues)(Meter*); typedef void(*Meter_Draw)(Meter*, int, int, int); +typedef void(*Meter_GetUiName)(const Meter*, char*, size_t); typedef struct MeterClass_ { const ObjectClass super; const Meter_Init init; const Meter_Done done; const Meter_UpdateMode updateMode; - const Meter_Draw draw; const Meter_UpdateValues updateValues; + const Meter_Draw draw; + const Meter_GetUiName getUiName; const int defaultMode; const double total; const int* const attributes; @@ -80,6 +82,8 @@ typedef struct MeterClass_ { #define Meter_drawFn(this_) As_Meter(this_)->draw #define Meter_doneFn(this_) As_Meter(this_)->done #define Meter_updateValues(this_) As_Meter(this_)->updateValues((Meter*)(this_)) +#define Meter_getUiNameFn(this_) As_Meter(this_)->getUiName +#define Meter_getUiName(this_,n_,l_) As_Meter(this_)->getUiName((const Meter*)(this_),n_,l_) #define Meter_defaultMode(this_) As_Meter(this_)->defaultMode #define Meter_attributes(this_) As_Meter(this_)->attributes #define Meter_name(this_) As_Meter(this_)->name diff --git a/ProcessList.c b/ProcessList.c index da5d7a91..82f719f8 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -19,7 +19,7 @@ in the source distribution for its full text. #include "XUtils.h" -ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { this->processes = Vector_new(klass, true, DEFAULT_SIZE); this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer @@ -29,6 +29,7 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users this->usersTable = usersTable; this->pidMatchList = pidMatchList; + this->dynamicMeters = dynamicMeters; this->userId = userId; diff --git a/ProcessList.h b/ProcessList.h index 32bdfa18..da4a50da 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -50,6 +50,8 @@ typedef struct ProcessList_ { Hashtable* displayTreeSet; Hashtable* draftingTreeSet; + Hashtable* dynamicMeters; /* runtime-discovered meters */ + struct timeval realtime; /* time of the current sample */ uint64_t realtimeMs; /* current time in milliseconds */ uint64_t monotonicMs; /* same, but from monotonic clock */ @@ -84,12 +86,12 @@ typedef struct ProcessList_ { unsigned int cpuCount; } ProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_done(ProcessList* this); diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 900677ae..91d62c4a 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -128,10 +128,10 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { CRT_fatalError("Unable to get kinfo_procs"); } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); - ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId); + ProcessList_init(&this->super, Class(DarwinProcess), usersTable, dynamicMeters, pidMatchList, userId); /* Initialize the CPU information */ this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load); diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h index 1ae2f2b9..bd39ab0e 100644 --- a/darwin/DarwinProcessList.h +++ b/darwin/DarwinProcessList.h @@ -28,7 +28,7 @@ typedef struct DarwinProcessList_ { ZfsArcStats zfs; } DarwinProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/darwin/Platform.h b/darwin/Platform.h index 694fd7dc..c0743f1c 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -92,4 +92,12 @@ static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) void Platform_gettime_monotonic(uint64_t* msec); +static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + #endif diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index 200491ec..9bf6ffef 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -42,12 +42,12 @@ static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { size_t len; char errbuf[_POSIX2_LINE_MAX]; DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList)); ProcessList* pl = (ProcessList*) dfpl; - ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, dynamicMeters, pidMatchList, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index e5402c00..04c54555 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -89,4 +89,12 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } +static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + #endif diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 999e85ef..60afe074 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -56,12 +56,12 @@ static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { size_t len; char errbuf[_POSIX2_LINE_MAX]; FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList)); ProcessList* pl = (ProcessList*) fpl; - ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(FreeBSDProcess), usersTable, dynamicMeters, pidMatchList, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize diff --git a/freebsd/FreeBSDProcessList.h b/freebsd/FreeBSDProcessList.h index 882345e8..ac6bcd0a 100644 --- a/freebsd/FreeBSDProcessList.h +++ b/freebsd/FreeBSDProcessList.h @@ -47,7 +47,7 @@ typedef struct FreeBSDProcessList_ { } FreeBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/freebsd/Platform.h b/freebsd/Platform.h index a0ec6abe..09adfc18 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -89,4 +89,12 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } +static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + #endif diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 7452c199..6f86d021 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -184,11 +184,11 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super, FILE* stream) { } } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); ProcessList* pl = &(this->super); - ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(LinuxProcess), usersTable, dynamicMeters, pidMatchList, userId); LinuxProcessList_initTtyDrivers(this); // Initialize page size diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 72661ce2..555b5c41 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -111,7 +111,7 @@ typedef struct LinuxProcessList_ { #define PROC_LINE_LENGTH 4096 #endif -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); diff --git a/linux/Platform.h b/linux/Platform.h index ff7e954b..d44daa08 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -105,4 +105,12 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } +static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + #endif diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index c9b1db7a..8dc4ac94 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -36,7 +36,7 @@ static long fscale; static int pageSize; static int pageSizeKB; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { const int nmib[] = { CTL_HW, HW_NCPU }; const int mib[] = { CTL_HW, HW_NCPUONLINE }; const int fmib[] = { CTL_KERN, KERN_FSCALE }; @@ -48,7 +48,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList)); ProcessList* pl = (ProcessList*) opl; - ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(OpenBSDProcess), usersTable, dynamicMeters, pidMatchList, userId); size = sizeof(pl->cpuCount); r = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0); diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h index 0ef6a894..5734d3ab 100644 --- a/openbsd/OpenBSDProcessList.h +++ b/openbsd/OpenBSDProcessList.h @@ -49,7 +49,7 @@ typedef struct OpenBSDProcessList_ { } OpenBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/openbsd/Platform.h b/openbsd/Platform.h index 18e1a1c0..1c5e01c9 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -87,4 +87,12 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } +static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + #endif diff --git a/pcp/PCPDynamicMeter.c b/pcp/PCPDynamicMeter.c new file mode 100644 index 00000000..0dad1ec3 --- /dev/null +++ b/pcp/PCPDynamicMeter.c @@ -0,0 +1,368 @@ +/* +htop - PCPDynamicMeter.c +(C) 2021 htop dev team +(C) 2021 Red Hat, Inc. All Rights Reserved. +Released under the GNU GPLv2, see the COPYING file +in the source distribution for its full text. +*/ +#include "config.h" // IWYU pragma: keep + +#include "pcp/PCPDynamicMeter.h" + +#include +#include +#include +#include + +#include "Object.h" +#include "Platform.h" +#include "ProcessList.h" +#include "RichString.h" +#include "Settings.h" +#include "XUtils.h" + +static PCPDynamicMetric* PCPDynamicMeter_lookupMetric(PCPDynamicMeters* meters, PCPDynamicMeter* meter, const char* name) { + size_t bytes = 8 + strlen(meter->super.name) + strlen(name); + char* metricName = xMalloc(bytes); + xSnprintf(metricName, bytes, "htop.%s.%s", meter->super.name, name); + + PCPDynamicMetric* metric; + for (unsigned int i = 0; i < meter->totalMetrics; i++) { + metric = &meter->metrics[i]; + if (String_eq(metric->name, metricName)) { + free(metricName); + return metric; + } + } + + /* not an existing metric in this meter - add it */ + unsigned int n = meter->totalMetrics + 1; + meter->metrics = xReallocArray(meter->metrics, n, sizeof(PCPDynamicMetric)); + meter->totalMetrics = n; + metric = &meter->metrics[n-1]; + memset(metric, 0, sizeof(PCPDynamicMetric)); + metric->name = metricName; + metric->id = meters->offset + meters->cursor; + meters->cursor++; + + Platform_addMetric(metric->id, metricName); + + return metric; +} + +static void PCPDynamicMeter_parseMetric(PCPDynamicMeters* meters, PCPDynamicMeter* meter, const char *path, unsigned int line, char* key, char* value) { + PCPDynamicMetric *metric; + char* p; + + if ((p = strchr(key, '.')) == NULL) + return; + *p++ = '\0'; /* end the name, p is now the attribute, e.g. 'label' */ + + if (String_eq(p, "metric")) { + /* lookup a dynamic metric with this name, else create */ + metric = PCPDynamicMeter_lookupMetric(meters, meter, key); + + /* use derived metrics in dynamic meters for simplicity */ + char* error; + if (pmRegisterDerivedMetric(metric->name, value, &error) < 0) { + char note[1024]; + xSnprintf(note, sizeof(note), + "failed to parse expression in %s at line %u\n%s\n%s", + path, line, error, pmGetProgname()); + free(error); + errno = EINVAL; + CRT_fatalError(note); + } + } else { + /* this is a property of a dynamic metric - the metric expression */ + /* may not have been observed yet - i.e. we allow for any ordering */ + metric = PCPDynamicMeter_lookupMetric(meters, meter, key); + if (String_eq(p, "color")) { + if (String_eq(value, "gray")) + metric->color = DYNAMIC_GRAY; + else if (String_eq(value, "darkgray")) + metric->color = DYNAMIC_DARKGRAY; + else if (String_eq(value, "red")) + metric->color = DYNAMIC_RED; + else if (String_eq(value, "green")) + metric->color = DYNAMIC_GREEN; + else if (String_eq(value, "blue")) + metric->color = DYNAMIC_BLUE; + else if (String_eq(value, "cyan")) + metric->color = DYNAMIC_CYAN; + else if (String_eq(value, "magenta")) + metric->color = DYNAMIC_MAGENTA; + else if (String_eq(value, "yellow")) + metric->color = DYNAMIC_YELLOW; + else if (String_eq(value, "white")) + metric->color = DYNAMIC_WHITE; + } else if (String_eq(p, "label")) { + free_and_xStrdup(&metric->label, value); + } else if (String_eq(p, "suffix")) { + free_and_xStrdup(&metric->suffix, value); + } + } +} + +// Ensure a valid name for use in a PCP metric name and in htoprc +static void PCPDynamicMeter_validateMeterName(char* key, const char *path, unsigned int line) { + char* p = key; + char* end = strrchr(key, ']'); + + if (end) { + *end = '\0'; + } else { + char note[1024]; + xSnprintf(note, sizeof(note), + "No closing brace on meter name at %s line %u\n\"%s\"", + path, line, key); + errno = EINVAL; + CRT_fatalError(note); + } + + while (*p) { + if (p == key) { + if (!isalpha(*p) && *p != '_') + break; + } else { + if (!isalnum(*p) && *p != '_') + break; + } + p++; + } + if (*p != '\0') { /* badness */ + char note[1024]; + xSnprintf(note, sizeof(note), + "Invalid meter name at %s line %u\n\"%s\"", + path, line, key); + errno = EINVAL; + CRT_fatalError(note); + } else { /* overwrite closing brace */ + *p = '\0'; + } +} + +static PCPDynamicMeter* PCPDynamicMeter_new(PCPDynamicMeters* meters, const char* name) { + PCPDynamicMeter* meter = xCalloc(1, sizeof(*meter)); + String_safeStrncpy(meter->super.name, name, sizeof(meter->super.name)); + Hashtable_put(meters->table, ++meters->count, meter); + return meter; +} + +static void PCPDynamicMeter_parseFile(PCPDynamicMeters* meters, const char* path) { + FILE* file = fopen(path, "r"); + if (!file) + return; + + PCPDynamicMeter* meter = NULL; + unsigned int lineno = 0; + for (;;) { + char* line = String_readLine(file); + if (!line) + break; + lineno++; + + /* cleanup whitespace, skip comment lines */ + char* trimmed = String_trim(line); + free(line); + if (trimmed[0] == '#' || trimmed[0] == '\0') { + free(trimmed); + continue; + } + + size_t n; + char** config = String_split(trimmed, '=', &n); + free(trimmed); + if (config == NULL) + continue; + + char* key = String_trim(config[0]); + char* value = n > 1 ? String_trim(config[1]) : NULL; + if (key[0] == '[') { /* new section heading - i.e. new meter */ + PCPDynamicMeter_validateMeterName(key+1, path, lineno); + meter = PCPDynamicMeter_new(meters, key+1); + } else if (value && String_eq(key, "caption")) { + free_and_xStrdup(&meter->super.caption, value); + } else if (value && String_eq(key, "description")) { + free_and_xStrdup(&meter->super.description, value); + } else if (value && String_eq(key, "type")) { + if (String_eq(config[1], "bar")) + meter->super.type = BAR_METERMODE; + else if (String_eq(config[1], "text")) + meter->super.type = TEXT_METERMODE; + else if (String_eq(config[1], "graph")) + meter->super.type = GRAPH_METERMODE; + else if (String_eq(config[1], "led")) + meter->super.type = LED_METERMODE; + } else if (value && String_eq(key, "maximum")) { + meter->super.maximum = strtod(value, NULL); + } else if (value) { + PCPDynamicMeter_parseMetric(meters, meter, path, lineno, key, value); + } + String_freeArray(config); + free(value); + free(key); + } + fclose(file); +} + +static void PCPDynamicMeter_scanDir(PCPDynamicMeters* meters, char* path) { + DIR* dir = opendir(path); + if (!dir) + return; + + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL) { + if (dirent->d_name[0] == '.') + continue; + + char *file = String_cat(path, dirent->d_name); + PCPDynamicMeter_parseFile(meters, file); + free(file); + } + closedir(dir); +} + +void PCPDynamicMeters_init(PCPDynamicMeters* meters) { + const char* sysconf = pmGetConfig("PCP_SYSCONF_DIR"); + const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); + const char* home = getenv("HOME"); + char* path; + + meters->table = Hashtable_new(0, true); + + /* search in the users home directory first of all */ + if (xdgConfigHome) { + path = String_cat(xdgConfigHome, "/htop/meters/"); + } else { + if (!home) + home = ""; + path = String_cat(home, "/.config/htop/meters/"); + } + PCPDynamicMeter_scanDir(meters, path); + free(path); + + /* secondly search in the system meters directory */ + path = String_cat(sysconf, "/htop/meters/"); + PCPDynamicMeter_scanDir(meters, path); + free(path); + + /* check the working directory, as a final option */ + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + path = String_cat(cwd, "/pcp/meters/"); + PCPDynamicMeter_scanDir(meters, path); + free(path); + } +} + +void PCPDynamicMeter_enable(PCPDynamicMeter* this) { + for (unsigned int i = 0; i < this->totalMetrics; i++) + Metric_enable(this->metrics[i].id, true); +} + +void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter) { + char* buffer = meter->txtBuffer; + size_t size = sizeof(meter->txtBuffer); + size_t bytes = 0; + + for (unsigned int i = 0; i < this->totalMetrics; i++) { + if (i > 0 && bytes < size - 1) + buffer[bytes++] = '/'; /* separator */ + + PCPDynamicMetric* metric = &this->metrics[i]; + const pmDesc* desc = Metric_desc(metric->id); + pmAtomValue atom; + + if (!Metric_values(metric->id, &atom, 1, desc->type)) { + bytes--; /* clear the separator */ + continue; + } + /* TODO: pretty-print the values - pmConvScale, etc */ + switch (desc->type) { + case PM_TYPE_STRING: + bytes += xSnprintf(buffer + bytes, size - bytes, "%s", atom.cp); + free(atom.cp); + break; + case PM_TYPE_32: + bytes += xSnprintf(buffer + bytes, size - bytes, "%d", atom.l); + break; + case PM_TYPE_U32: + bytes += xSnprintf(buffer + bytes, size - bytes, "%u", atom.ul); + break; + case PM_TYPE_64: + bytes += xSnprintf(buffer + bytes, size - bytes, "%lld", (long long) atom.ll); + break; + case PM_TYPE_U64: + bytes += xSnprintf(buffer + bytes, size - bytes, "%llu", (unsigned long long) atom.ull); + break; + case PM_TYPE_FLOAT: + bytes += xSnprintf(buffer + bytes, size - bytes, "%f", (double) atom.f); + break; + case PM_TYPE_DOUBLE: + bytes += xSnprintf(buffer + bytes, size - bytes, "%f", atom.d); + break; + default: + break; + } + } + if (!bytes) + xSnprintf(buffer, size, "no data"); +} + +void PCPDynamicMeter_display(PCPDynamicMeter* this, ATTR_UNUSED const Meter* meter, RichString* out) { + int nodata = 1; + + for (unsigned int i = 0; i < this->totalMetrics; i++) { + PCPDynamicMetric* metric = &this->metrics[i]; + const pmDesc* desc = Metric_desc(metric->id); + pmAtomValue atom; + char buffer[64]; + int len; + + if (!Metric_values(metric->id, &atom, 1, desc->type)) + continue; + nodata = 0; + + if (i > 0) + RichString_appendnAscii(out, CRT_colors[metric->color], " ", 1); + + if (metric->label) { + len = xSnprintf(buffer, sizeof(buffer), "%s ", metric->label); + RichString_appendnAscii(out, CRT_colors[metric->color], buffer, len); + } + + /* TODO: pretty-print the values - pmConvScale, etc */ + len = 0; + switch (desc->type) { + case PM_TYPE_STRING: + len = xSnprintf(buffer, sizeof(buffer), "%s", atom.cp); + free(atom.cp); + break; + case PM_TYPE_32: + len = xSnprintf(buffer, sizeof(buffer), "%d", atom.l); + break; + case PM_TYPE_U32: + len = xSnprintf(buffer, sizeof(buffer), "%u", atom.ul); + break; + case PM_TYPE_64: + len = xSnprintf(buffer, sizeof(buffer), "%lld", (long long) atom.ll); + break; + case PM_TYPE_U64: + len = xSnprintf(buffer, sizeof(buffer), "%llu", (unsigned long long) atom.ull); + break; + case PM_TYPE_FLOAT: + len = xSnprintf(buffer, sizeof(buffer), "%f", (double)atom.f); + break; + case PM_TYPE_DOUBLE: + len = xSnprintf(buffer, sizeof(buffer), "%f", atom.d); + break; + default: + break; + } + if (len) + RichString_appendnAscii(out, CRT_colors[metric->color], buffer, len); + } + if (nodata) + RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); +} diff --git a/pcp/PCPDynamicMeter.h b/pcp/PCPDynamicMeter.h new file mode 100644 index 00000000..e73a1bca --- /dev/null +++ b/pcp/PCPDynamicMeter.h @@ -0,0 +1,36 @@ +#ifndef HEADER_PCPDynamicMeter +#define HEADER_PCPDynamicMeter + +#include "CRT.h" +#include "DynamicMeter.h" + +typedef struct { + unsigned int id; /* index into metric array */ + ColorElements color; + char* name; /* derived metric name */ + char* label; + char* suffix; +} PCPDynamicMetric; + +typedef struct { + DynamicMeter super; + PCPDynamicMetric *metrics; + unsigned int totalMetrics; +} PCPDynamicMeter; + +typedef struct { + Hashtable* table; + unsigned int count; /* count of dynamic meters discovered by scan */ + unsigned int offset; /* start offset into the Platform metric array */ + unsigned int cursor; /* identifier allocator for each new metric used */ +} PCPDynamicMeters; + +void PCPDynamicMeters_init(PCPDynamicMeters* meters); + +void PCPDynamicMeter_enable(PCPDynamicMeter* this); + +void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter); + +void PCPDynamicMeter_display(PCPDynamicMeter* this, const Meter* meter, RichString* out); + +#endif diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index ef37da2d..bece854d 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -59,11 +59,11 @@ static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) { return name; } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList)); ProcessList* super = &(this->super); - ProcessList_init(super, Class(PCPProcess), usersTable, pidMatchList, userId); + ProcessList_init(super, Class(PCPProcess), usersTable, dynamicMeters, pidMatchList, userId); struct timeval timestamp; gettimeofday(×tamp, NULL); diff --git a/pcp/PCPProcessList.h b/pcp/PCPProcessList.h index 23da6637..f1784904 100644 --- a/pcp/PCPProcessList.h +++ b/pcp/PCPProcessList.h @@ -63,7 +63,7 @@ typedef struct PCPProcessList_ { ZfsArcStats zfs; } PCPProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); diff --git a/pcp/Platform.c b/pcp/Platform.c index 42db6be1..f10b128b 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -20,6 +20,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "DiskIOMeter.h" +#include "DynamicMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -41,6 +42,7 @@ in the source distribution for its full text. #include "linux/PressureStallMeter.h" #include "linux/ZramMeter.h" #include "linux/ZramStats.h" +#include "pcp/PCPDynamicMeter.h" #include "pcp/PCPProcess.h" #include "pcp/PCPProcessList.h" #include "zfs/ZfsArcMeter.h" @@ -50,14 +52,14 @@ in the source distribution for its full text. typedef struct Platform_ { int context; /* PMAPI(3) context identifier */ - unsigned int total; /* total number of all metrics */ + unsigned int totalMetrics; /* total number of all metrics */ const char** names; /* name array indexed by Metric */ pmID* pmids; /* all known metric identifiers */ pmID* fetch; /* enabled identifiers for sampling */ pmDesc* descs; /* metric desc array indexed by Metric */ pmResult* result; /* sample values result indexed by Metric */ + PCPDynamicMeters meters; /* dynamic meters via configuration files */ struct timeval offset; /* time offset used in archive mode only */ - long long btime; /* boottime in seconds since the epoch */ char* release; /* uname and distro from this context */ int pidmax; /* maximum platform process identifier */ @@ -78,6 +80,7 @@ const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, + &DynamicMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, @@ -244,7 +247,13 @@ static const char *Platform_metricNames[] = { [PCP_METRIC_COUNT] = NULL }; +const pmDesc* Metric_desc(Metric metric) { + return &pcp->descs[metric]; +} + pmAtomValue* Metric_values(Metric metric, pmAtomValue *atom, int count, int type) { + if (pcp->result == NULL) + return NULL; pmValueSet* vset = pcp->result->vset[metric]; if (!vset || vset->numval <= 0) @@ -374,7 +383,7 @@ bool Metric_fetch(struct timeval *timestamp) { pmFreeResult(pcp->result); pcp->result = NULL; } - int sts = pmFetch(pcp->total, pcp->fetch, &pcp->result); + int sts = pmFetch(pcp->totalMetrics, pcp->fetch, &pcp->result); if (sts < 0) { if (pmDebugOptions.appl0) fprintf(stderr, "Error: cannot fetch metric values: %s\n", @@ -386,12 +395,12 @@ bool Metric_fetch(struct timeval *timestamp) { return true; } -static int Platform_addMetric(Metric id, const char *name) { +int Platform_addMetric(Metric id, const char *name) { unsigned int i = (unsigned int)id; - if (i >= PCP_METRIC_COUNT && i >= pcp->total) { + if (i >= PCP_METRIC_COUNT && i >= pcp->totalMetrics) { /* added via configuration files */ - unsigned int j = pcp->total + 1; + unsigned int j = pcp->totalMetrics + 1; pcp->fetch = xRealloc(pcp->fetch, j * sizeof(pmID)); pcp->pmids = xRealloc(pcp->pmids, j * sizeof(pmID)); pcp->names = xRealloc(pcp->names, j * sizeof(char*)); @@ -401,7 +410,7 @@ static int Platform_addMetric(Metric id, const char *name) { pcp->pmids[i] = pcp->fetch[i] = PM_ID_NULL; pcp->names[i] = name; - return ++pcp->total; + return ++pcp->totalMetrics; } /* global state from the environment and command line arguments */ @@ -449,14 +458,17 @@ void Platform_init(void) { for (unsigned int i = 0; i < PCP_METRIC_COUNT; i++) Platform_addMetric(i, Platform_metricNames[i]); + pcp->meters.offset = PCP_METRIC_COUNT; - sts = pmLookupName(pcp->total, pcp->names, pcp->pmids); + PCPDynamicMeters_init(&pcp->meters); + + sts = pmLookupName(pcp->totalMetrics, pcp->names, pcp->pmids); if (sts < 0) { fprintf(stderr, "Error: cannot lookup metric names: %s\n", pmErrStr(sts)); exit(1); } - for (unsigned int i = 0; i < pcp->total; i++) { + for (unsigned int i = 0; i < pcp->totalMetrics; i++) { pcp->fetch[i] = PM_ID_NULL; /* default is to not sample */ /* expect some metrics to be missing - e.g. PMDA not available */ @@ -883,3 +895,25 @@ void Platform_gettime_monotonic(uint64_t* msec) { struct timeval* tv = &pcp->result->timestamp; *msec = ((uint64_t)tv->tv_sec * 1000) + ((uint64_t)tv->tv_usec / 1000); } + +Hashtable* Platform_dynamicMeters(void) { + return pcp->meters.table; +} + +void Platform_dynamicMeterInit(Meter* meter) { + PCPDynamicMeter* this = Hashtable_get(pcp->meters.table, meter->param); + if (this) + PCPDynamicMeter_enable(this); +} + +void Platform_dynamicMeterUpdateValues(Meter* meter) { + PCPDynamicMeter* this = Hashtable_get(pcp->meters.table, meter->param); + if (this) + PCPDynamicMeter_updateValues(this, meter); +} + +void Platform_dynamicMeterDisplay(const Meter* meter, RichString* out) { + PCPDynamicMeter* this = Hashtable_get(pcp->meters.table, meter->param); + if (this) + PCPDynamicMeter_display(this, meter, out); +} diff --git a/pcp/Platform.h b/pcp/Platform.h index cd90bc00..7cf3722b 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -24,6 +24,7 @@ in the source distribution for its full text. #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" +#include "Hashtable.h" #include "Meter.h" #include "NetworkIOMeter.h" #include "Process.h" @@ -247,14 +248,26 @@ bool Metric_iterate(Metric metric, int* instp, int* offsetp); pmAtomValue* Metric_values(Metric metric, pmAtomValue *atom, int count, int type); +const pmDesc* Metric_desc(Metric metric); + int Metric_instanceCount(Metric metric); int Metric_instanceOffset(Metric metric, int inst); -pmAtomValue *Metric_instance(Metric metric, int inst, int offset, pmAtomValue *atom, int type); +pmAtomValue* Metric_instance(Metric metric, int inst, int offset, pmAtomValue *atom, int type); + +int Platform_addMetric(Metric id, const char *name); void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec); void Platform_gettime_monotonic(uint64_t* msec); +Hashtable* Platform_dynamicMeters(void); + +void Platform_dynamicMeterInit(Meter* meter); + +void Platform_dynamicMeterUpdateValues(Meter* meter); + +void Platform_dynamicMeterDisplay(const Meter* meter, RichString* out); + #endif diff --git a/pcp/meters/entropy b/pcp/meters/entropy new file mode 100644 index 00000000..0bef0cfc --- /dev/null +++ b/pcp/meters/entropy @@ -0,0 +1,9 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[entropy] +caption = Entropy +avail.metric = kernel.all.entropy.avail / kernel.all.entropy.poolsize * 100 +avail.label = avail +avail.suffix = % diff --git a/pcp/meters/freespace b/pcp/meters/freespace new file mode 100644 index 00000000..074af6d6 --- /dev/null +++ b/pcp/meters/freespace @@ -0,0 +1,11 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[freespace] +caption = Freespace +description = Filesystem space +used.metric = sum(filesys.used) +used.color = blue +free.metric = sum(filesys.free) +free.color = green diff --git a/pcp/meters/ipc b/pcp/meters/ipc new file mode 100644 index 00000000..4162f392 --- /dev/null +++ b/pcp/meters/ipc @@ -0,0 +1,13 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[ipc] +caption = SysV IPC +description = SysV IPC counts +msg.metric = ipc.msg.used_queues +msg.color = blue +sem.metric = ipc.sem.used_sem +sem.color = green +shm.metric = ipc.shm.used_ids +shm.color = cyan diff --git a/pcp/meters/locks b/pcp/meters/locks new file mode 100644 index 00000000..5d095106 --- /dev/null +++ b/pcp/meters/locks @@ -0,0 +1,15 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[locks] +caption = File locks +description = VFS file locks +posix.metric = vfs.locks.posix.count +posix.color = blue +flock.metric = vfs.locks.flock.count +flock.color = green +readlock.metric = vfs.locks.posix.read + vfs.locks.flock.read +readlock.color = red +writelock.metric = vfs.locks.posix.write + vfs.locks.flock.write +writelock.color = yellow diff --git a/pcp/meters/memcache b/pcp/meters/memcache new file mode 100644 index 00000000..0f2fac23 --- /dev/null +++ b/pcp/meters/memcache @@ -0,0 +1,11 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[memcache] +caption = Memcache +description = Memcache Hits +hit.metric = sum(memcache.hits) +hit.color = green +miss.metric = sum(memcache.misses) +miss.color = blue diff --git a/pcp/meters/mysql b/pcp/meters/mysql new file mode 100644 index 00000000..07ae11e9 --- /dev/null +++ b/pcp/meters/mysql @@ -0,0 +1,73 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[mysql_io] +caption = MySQL I/O +recv.metric = mysql.status.bytes_received +recv.color = green +recv.label = recv +sent.metric = mysql.status.bytes_sent +sent.color = blue +sent.label = sent + +[mysql_keys] +caption = MySQL keys +description = MySQL key status +key_blocks_used.metric = mysql.status.key_blocks_used +key_blocks_used.label = color +key_blocks_used.label = used +key_reads.metric = mysql.status.key_reads +key_reads.label = read +key_reads.color = green +key_writes.metric = mysql.status.key_writes +key_writes.label = writ +key_writes.color = blue +key_read_requests.metric = mysql.status.key_read_requests +key_read_requests.label = rreq +key_read_requests.color = green +key_write_requests.metric = mysql.status.key_write_requests +key_write_requests.label = wreq +key_write_requests.color = blue + +[innodb_buffer] +caption = InnoDB pool +description = InnoDB buffer pool +created.metric = mysql.status.innodb_pages_created +created.label = cr +created.color = yellow +read.metric = mysql.status.innodb_pages_read +read.label = rd +read.color = greed +written.metric = mysql.status.innodb_pages_written +written.label = wr +written.color = red + +[innodb_io] +caption = InnoDB I/O +description = InnoDB I/O operations +read.metric = mysql.status.innodb_data_read +read.label = rd +read.color = green +written.metric = mysql.status.innodb_data.writes +written.label = wr +written.color = blue +sync.metric = mysql.status.innodb_data_fsyncs +sync.label = sync +sync.color = cyan + +[innodb_ops] +caption = InnoDB ops +description = InnoDB operations +inserted.metric = mysql.status.innodb_rows_inserted +inserted.label = ins +inserted.color = blue +updated.metric = mysql.status.innodb_rows_updated +updated.label = upd +updated.color = cyan +deleted.metric = mysql.status.innodb_rows_deleted +deleted.label = del +deleted.color = red +read.metric = mysql.status.innodb_rows_read +read.label = rd +read.color = green diff --git a/pcp/meters/postfix b/pcp/meters/postfix new file mode 100644 index 00000000..66876c5a --- /dev/null +++ b/pcp/meters/postfix @@ -0,0 +1,21 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[postfix] +caption = Postfix +incoming.metric = sum(postfix.queues.incoming) +incoming.color = green +incoming.label = in +active.metric = sum(postfix.queues.active) +active.color = blue +active.label = act +deferred.metric = sum(postfix.queues.deferred) +deferred.color = cyan +deferred.label = dfr +bounce.metric = sum(postfix.queues.maildrop) +bounce.color = red +bounce.label = bnc +hold.metric = sum(postfix.queues.hold) +hold.color = yellow +hold.label = hold diff --git a/pcp/meters/redis b/pcp/meters/redis new file mode 100644 index 00000000..b9c084be --- /dev/null +++ b/pcp/meters/redis @@ -0,0 +1,41 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[redisxact] +caption = Redis xact +description = Redis transactions +tps.metric = redis.instantaneous_ops_per_sec +tps.color = green + +[redismem] +caption = Redis mem +description = Redis memory +lua.metric = redis.used_memory_lua +lua.color = magenta +lua.label = lua: +used.metric = redis.used_memory +used.color = blue +used.label = used: + +[redisclient] +caption = Redis clients +description = Redis clients +type = bar +blocked.metric = redis.blocked_clients +blocked.color = blue +blocked.label = blk +clients.metric = redis.connected_clients +clients.color = green +clients.label = conn + +[redisconn] +caption = Redis conn +description = Redis connections +type = bar +reject.metric = redis.rejected_connections +reject.color = magenta +reject.label = fail/s +total.metric = redis.total_connections_received +total.color = blue +total.label = conn/s diff --git a/pcp/meters/tcp b/pcp/meters/tcp new file mode 100644 index 00000000..cebc658d --- /dev/null +++ b/pcp/meters/tcp @@ -0,0 +1,22 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[tcp] +caption = TCP +description = TCP sockets +listen.metric = network.tcpconn.listen +listen.color = green +listen.label = lis +active.metric = network.tcpconn.established +active.color = blue +active.label = act +syn.metric = network.tcpconn.syn_sent + network.tcpconn.syn_recv + network.tcpconn.last_ack +syn.color = cyan +syn.label = syn +wait.metric = network.tcpconn.time_wait +wait.color = red +wait.label = tim +close.metric = network.tcpconn.fin_wait1 + network.tcpconn.fin_wait2 + network.tcpconn.close + network.tcpconn.close_wait + network.tcpconn.closing +close.color = yellow +close.label = clo diff --git a/solaris/Platform.h b/solaris/Platform.h index f83c4d1c..b32d60af 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -128,4 +128,12 @@ IGNORE_WCASTQUAL_BEGIN IGNORE_WCASTQUAL_END } +static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + #endif diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index 716436e7..b9849e57 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -47,10 +47,10 @@ static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sp return zname; } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); ProcessList* pl = (ProcessList*) spl; - ProcessList_init(pl, Class(SolarisProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(SolarisProcess), usersTable, dynamicMeters, pidMatchList, userId); spl->kd = kstat_open(); diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h index c262ba4c..e97d5cff 100644 --- a/solaris/SolarisProcessList.h +++ b/solaris/SolarisProcessList.h @@ -53,7 +53,7 @@ typedef struct SolarisProcessList_ { ZfsArcStats zfs; } SolarisProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); diff --git a/unsupported/Platform.h b/unsupported/Platform.h index 582accb2..f628230f 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -78,4 +78,12 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } +static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + #endif diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index 1ab5bd3b..3318453f 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -14,9 +14,9 @@ in the source distribution for its full text. #include "UnsupportedProcess.h" -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId) { ProcessList* this = xCalloc(1, sizeof(ProcessList)); - ProcessList_init(this, Class(Process), usersTable, pidMatchList, userId); + ProcessList_init(this, Class(Process), usersTable, dynamicMeters, pidMatchList, userId); this->cpuCount = 1; diff --git a/unsupported/UnsupportedProcessList.h b/unsupported/UnsupportedProcessList.h index 83796899..10432758 100644 --- a/unsupported/UnsupportedProcessList.h +++ b/unsupported/UnsupportedProcessList.h @@ -10,7 +10,7 @@ in the source distribution for its full text. #include "ProcessList.h" -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this);