summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Scott <nathans@redhat.com>2021-06-23 17:44:56 +1000
committerNathan Scott <nathans@redhat.com>2021-07-07 10:59:36 +1000
commitf0ed0fdafb9ecefc9d103ffb8f5d91bf723518f6 (patch)
tree8f82bf54c96a07d8efa5c9a8bd2c50865bd6757a
parent865b85eb2d31321e9c37334838fa514ac348d61a (diff)
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.
-rw-r--r--AvailableMetersPanel.c73
-rw-r--r--CPUMeter.c9
-rw-r--r--CRT.c56
-rw-r--r--CRT.h9
-rw-r--r--CommandLine.c4
-rw-r--r--DynamicMeter.c98
-rw-r--r--DynamicMeter.h25
-rw-r--r--Header.c17
-rw-r--r--Makefile.am4
-rw-r--r--Meter.c13
-rw-r--r--Meter.h6
-rw-r--r--ProcessList.c3
-rw-r--r--ProcessList.h6
-rw-r--r--darwin/DarwinProcessList.c4
-rw-r--r--darwin/DarwinProcessList.h2
-rw-r--r--darwin/Platform.h8
-rw-r--r--dragonflybsd/DragonFlyBSDProcessList.c4
-rw-r--r--dragonflybsd/Platform.h8
-rw-r--r--freebsd/FreeBSDProcessList.c4
-rw-r--r--freebsd/FreeBSDProcessList.h2
-rw-r--r--freebsd/Platform.h8
-rw-r--r--linux/LinuxProcessList.c4
-rw-r--r--linux/LinuxProcessList.h2
-rw-r--r--linux/Platform.h8
-rw-r--r--openbsd/OpenBSDProcessList.c4
-rw-r--r--openbsd/OpenBSDProcessList.h2
-rw-r--r--openbsd/Platform.h8
-rw-r--r--pcp/PCPDynamicMeter.c368
-rw-r--r--pcp/PCPDynamicMeter.h36
-rw-r--r--pcp/PCPProcessList.c4
-rw-r--r--pcp/PCPProcessList.h2
-rw-r--r--pcp/Platform.c52
-rw-r--r--pcp/Platform.h15
-rw-r--r--pcp/meters/entropy9
-rw-r--r--pcp/meters/freespace11
-rw-r--r--pcp/meters/ipc13
-rw-r--r--pcp/meters/locks15
-rw-r--r--pcp/meters/memcache11
-rw-r--r--pcp/meters/mysql73
-rw-r--r--pcp/meters/postfix21
-rw-r--r--pcp/meters/redis41
-rw-r--r--pcp/meters/tcp22
-rw-r--r--solaris/Platform.h8
-rw-r--r--solaris/SolarisProcessList.c4
-rw-r--r--solaris/SolarisProcessList.h2
-rw-r--r--unsupported/Platform.h8
-rw-r--r--unsupported/UnsupportedProcessList.c4
-rw-r--r--unsupported/UnsupportedProcessList.h2
48 files changed, 1044 insertions, 68 deletions
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 <stdlib.h>
#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 <string.h>
#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)", &param);
- if (!ok)
- param = 0;
+ char* end, dynamic[32] = {0};
+ int ok = sscanf(paren, "(%10u)", &param); // 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/Platf