aboutsummaryrefslogtreecommitdiffstats
path: root/pcp
diff options
context:
space:
mode:
authorDaniel Lange <DLange@git.local>2024-01-10 11:17:08 +0100
committerDaniel Lange <DLange@git.local>2024-01-10 11:17:08 +0100
commite7372d18a1a661d8c3dba9f51e1f17b5f94171a7 (patch)
treee8270dd60ec096bee8157dbadf029e15ed584592 /pcp
parent937052b231259a47d881d539ad5748245ef55b99 (diff)
downloaddebian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.tar.gz
debian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.tar.bz2
debian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.zip
New upstream version 3.3.0
Diffstat (limited to 'pcp')
-rw-r--r--pcp/InDomTable.c99
-rw-r--r--pcp/InDomTable.h34
-rw-r--r--pcp/Instance.c163
-rw-r--r--pcp/Instance.h37
-rw-r--r--pcp/Metric.c (renamed from pcp/PCPMetric.c)52
-rw-r--r--pcp/Metric.h (renamed from pcp/PCPMetric.h)41
-rw-r--r--pcp/PCPDynamicColumn.c275
-rw-r--r--pcp/PCPDynamicColumn.h21
-rw-r--r--pcp/PCPDynamicMeter.c47
-rw-r--r--pcp/PCPDynamicMeter.h6
-rw-r--r--pcp/PCPDynamicScreen.c407
-rw-r--r--pcp/PCPDynamicScreen.h56
-rw-r--r--pcp/PCPMachine.c345
-rw-r--r--pcp/PCPMachine.h (renamed from pcp/PCPProcessList.h)31
-rw-r--r--pcp/PCPProcess.c155
-rw-r--r--pcp/PCPProcess.h9
-rw-r--r--pcp/PCPProcessList.c727
-rw-r--r--pcp/PCPProcessTable.c486
-rw-r--r--pcp/PCPProcessTable.h24
-rw-r--r--pcp/Platform.c334
-rw-r--r--pcp/Platform.h26
-rw-r--r--pcp/ProcessField.h3
-rw-r--r--pcp/screens/biosnoop41
-rw-r--r--pcp/screens/cgroups45
-rw-r--r--pcp/screens/cgroupsio49
-rw-r--r--pcp/screens/cgroupsmem48
-rw-r--r--pcp/screens/devices114
-rw-r--r--pcp/screens/execsnoop37
-rw-r--r--pcp/screens/exitsnoop48
-rw-r--r--pcp/screens/filesystems50
-rw-r--r--pcp/screens/opensnoop27
31 files changed, 2798 insertions, 1039 deletions
diff --git a/pcp/InDomTable.c b/pcp/InDomTable.c
new file mode 100644
index 0000000..2f9a500
--- /dev/null
+++ b/pcp/InDomTable.c
@@ -0,0 +1,99 @@
+/*
+htop - InDomTable.c
+(C) 2023 htop dev team
+(C) 2022-2023 Sohaib Mohammed
+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/InDomTable.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "CRT.h"
+#include "DynamicColumn.h"
+#include "Hashtable.h"
+#include "Macros.h"
+#include "Platform.h"
+#include "Table.h"
+#include "Vector.h"
+#include "XUtils.h"
+
+#include "pcp/Instance.h"
+#include "pcp/Metric.h"
+#include "pcp/PCPDynamicColumn.h"
+
+
+InDomTable* InDomTable_new(Machine* host, pmInDom indom, int metricKey) {
+ InDomTable* this = xCalloc(1, sizeof(InDomTable));
+ Object_setClass(this, Class(InDomTable));
+ this->metricKey = metricKey;
+ this->id = indom;
+
+ Table* super = &this->super;
+ Table_init(super, Class(Row), host);
+
+ return this;
+}
+
+void InDomTable_done(InDomTable* this) {
+ Table_done(&this->super);
+}
+
+static void InDomTable_delete(Object* cast) {
+ InDomTable* this = (InDomTable*) cast;
+ InDomTable_done(this);
+ free(this);
+}
+
+static Instance* InDomTable_getInstance(InDomTable* this, int id, bool* preExisting) {
+ const Table* super = &this->super;
+ Instance* inst = (Instance*) Hashtable_get(super->table, id);
+ *preExisting = inst != NULL;
+ if (inst) {
+ assert(Vector_indexOf(super->rows, inst, Row_idEqualCompare) != -1);
+ assert(Instance_getId(inst) == id);
+ } else {
+ inst = Instance_new(super->host, this);
+ assert(inst->name == NULL);
+ Instance_setId(inst, id);
+ }
+ return inst;
+}
+
+static void InDomTable_goThroughEntries(InDomTable* this) {
+ Table* super = &this->super;
+
+ /* for every instance ... */
+ int instid = -1, offset = -1;
+ while (Metric_iterate(this->metricKey, &instid, &offset)) {
+ bool preExisting;
+ Instance* inst = InDomTable_getInstance(this, instid, &preExisting);
+ inst->offset = offset >= 0 ? offset : 0;
+
+ Row* row = (Row*) inst;
+ if (!preExisting)
+ Table_add(super, row);
+ row->updated = true;
+ row->show = true;
+ }
+}
+
+static void InDomTable_iterateEntries(Table* super) {
+ InDomTable* this = (InDomTable*) super;
+ InDomTable_goThroughEntries(this);
+}
+
+const TableClass InDomTable_class = {
+ .super = {
+ .extends = Class(Table),
+ .delete = InDomTable_delete,
+ },
+ .prepare = Table_prepareEntries,
+ .iterate = InDomTable_iterateEntries,
+ .cleanup = Table_cleanupEntries,
+};
diff --git a/pcp/InDomTable.h b/pcp/InDomTable.h
new file mode 100644
index 0000000..f44a39f
--- /dev/null
+++ b/pcp/InDomTable.h
@@ -0,0 +1,34 @@
+#ifndef HEADER_InDomTable
+#define HEADER_InDomTable
+/*
+htop - InDomTable.h
+(C) 2023 htop dev team
+(C) 2022-2023 Sohaib Mohammed
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "Platform.h"
+#include "Table.h"
+
+
+typedef struct InDomTable_ {
+ Table super;
+ pmInDom id; /* shared by metrics in the table */
+ unsigned int metricKey; /* representative metric using this indom */
+} InDomTable;
+
+extern const TableClass InDomTable_class;
+
+InDomTable* InDomTable_new(Machine* host, pmInDom indom, int metricKey);
+
+void InDomTable_done(InDomTable* this);
+
+RowField RowField_keyAt(const Settings* settings, int at);
+
+void InDomTable_scan(Table* super);
+
+#endif
diff --git a/pcp/Instance.c b/pcp/Instance.c
new file mode 100644
index 0000000..8ae9051
--- /dev/null
+++ b/pcp/Instance.c
@@ -0,0 +1,163 @@
+/*
+htop - Instance.c
+(C) 2022-2023 Sohaib Mohammed
+(C) 2022-2023 htop dev team
+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/Instance.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "CRT.h"
+#include "DynamicColumn.h"
+#include "DynamicScreen.h"
+#include "Hashtable.h"
+#include "Machine.h"
+#include "Macros.h"
+#include "Metric.h"
+#include "Platform.h"
+#include "PCPDynamicColumn.h"
+#include "PCPDynamicScreen.h"
+#include "Row.h"
+#include "RichString.h"
+#include "XUtils.h"
+
+#include "pcp/InDomTable.h"
+#include "pcp/Metric.h"
+
+
+Instance* Instance_new(const Machine* host, const InDomTable* indom) {
+ Instance* this = xCalloc(1, sizeof(Instance));
+ Object_setClass(this, Class(Instance));
+
+ Row* super = &this->super;
+ Row_init(super, host);
+
+ this->indom = indom;
+
+ return this;
+}
+
+void Instance_done(Instance* this) {
+ if (this->name)
+ free(this->name);
+ Row_done(&this->super);
+}
+
+static void Instance_delete(Object* cast) {
+ Instance* this = (Instance*) cast;
+ Instance_done(this);
+ free(this);
+}
+
+static void Instance_writeField(const Row* super, RichString* str, RowField field) {
+ const Instance* this = (const Instance*) super;
+ int instid = Instance_getId(this);
+
+ const Settings* settings = super->host->settings;
+ DynamicColumn* column = Hashtable_get(settings->dynamicColumns, field);
+ PCPDynamicColumn* cp = (PCPDynamicColumn*) column;
+ if (!cp)
+ return;
+
+ pmAtomValue atom;
+ pmAtomValue* ap = &atom;
+ const pmDesc* descp = Metric_desc(cp->id);
+ if (!Metric_instance(cp->id, instid, this->offset, ap, descp->type))
+ ap = NULL;
+
+ PCPDynamicColumn_writeAtomValue(cp, str, settings, cp->id, instid, descp, ap);
+
+ if (ap && descp->type == PM_TYPE_STRING)
+ free(ap->cp);
+}
+
+static const char* Instance_externalName(Row* super) {
+ Instance* this = (Instance*) super;
+
+ if (!this->name)
+ /* ignore any failure here - its safe and we try again next time */
+ (void)pmNameInDom(InDom_getId(this), Instance_getId(this), &this->name);
+ return this->name;
+}
+
+static int Instance_compareByKey(const Row* v1, const Row* v2, int key) {
+ const Instance* i1 = (const Instance*)v1;
+ const Instance* i2 = (const Instance*)v2;
+
+ if (key < 0)
+ return 0;
+
+ Hashtable* dc = Platform_dynamicColumns();
+ const PCPDynamicColumn* column = Hashtable_get(dc, key);
+ if (!column)
+ return -1;
+
+ size_t metric = column->id;
+ unsigned int type = Metric_type(metric);
+
+ pmAtomValue atom1 = {0}, atom2 = {0};
+ if (!Metric_instance(metric, i1->offset, i1->offset, &atom1, type) ||
+ !Metric_instance(metric, i2->offset, i2->offset, &atom2, type)) {
+ if (type == PM_TYPE_STRING) {
+ free(atom1.cp);
+ free(atom2.cp);
+ }
+ return -1;
+ }
+
+ switch (type) {
+ case PM_TYPE_STRING: {
+ int cmp = SPACESHIP_NULLSTR(atom2.cp, atom1.cp);
+ free(atom2.cp);
+ free(atom1.cp);
+ return cmp;
+ }
+ case PM_TYPE_32:
+ return SPACESHIP_NUMBER(atom2.l, atom1.l);
+ case PM_TYPE_U32:
+ return SPACESHIP_NUMBER(atom2.ul, atom1.ul);
+ case PM_TYPE_64:
+ return SPACESHIP_NUMBER(atom2.ll, atom1.ll);
+ case PM_TYPE_U64:
+ return SPACESHIP_NUMBER(atom2.ull, atom1.ull);
+ case PM_TYPE_FLOAT:
+ return SPACESHIP_NUMBER(atom2.f, atom1.f);
+ case PM_TYPE_DOUBLE:
+ return SPACESHIP_NUMBER(atom2.d, atom1.d);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int Instance_compare(const void* v1, const void* v2) {
+ const Instance* i1 = (const Instance*)v1;
+ const Instance* i2 = (const Instance*)v2;
+ const ScreenSettings* ss = i1->super.host->settings->ss;
+ RowField key = ScreenSettings_getActiveSortKey(ss);
+ int result = Instance_compareByKey(v1, v2, key);
+
+ // Implement tie-breaker (needed to make tree mode more stable)
+ if (!result)
+ return SPACESHIP_NUMBER(Instance_getId(i1), Instance_getId(i2));
+
+ return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result;
+}
+
+const RowClass Instance_class = {
+ .super = {
+ .extends = Class(Row),
+ .display = Row_display,
+ .delete = Instance_delete,
+ .compare = Instance_compare,
+ },
+ .sortKeyString = Instance_externalName,
+ .writeField = Instance_writeField,
+};
diff --git a/pcp/Instance.h b/pcp/Instance.h
new file mode 100644
index 0000000..aefd642
--- /dev/null
+++ b/pcp/Instance.h
@@ -0,0 +1,37 @@
+#ifndef HEADER_Instance
+#define HEADER_Instance
+/*
+htop - Instance.h
+(C) 2022-2023 htop dev team
+(C) 2022-2023 Sohaib Mohammed
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Hashtable.h"
+#include "Object.h"
+#include "Platform.h"
+#include "Row.h"
+
+
+typedef struct Instance_ {
+ Row super;
+
+ char* name; /* external instance name */
+ const struct InDomTable_* indom; /* instance domain */
+
+ /* default result offset to use for searching metrics with instances */
+ unsigned int offset;
+} Instance;
+
+#define InDom_getId(i_) ((i_)->indom->id)
+#define Instance_getId(i_) ((i_)->super.id)
+#define Instance_setId(i_, id_) ((i_)->super.id = (id_))
+
+extern const RowClass Instance_class;
+
+Instance* Instance_new(const Machine* host, const struct InDomTable_* indom);
+
+void Instance_done(Instance* this);
+
+#endif
diff --git a/pcp/PCPMetric.c b/pcp/Metric.c
index 606a5df..4a3c858 100644
--- a/pcp/PCPMetric.c
+++ b/pcp/Metric.c
@@ -1,5 +1,5 @@
/*
-htop - PCPMetric.c
+htop - Metric.c
(C) 2020-2021 htop dev team
(C) 2020-2021 Red Hat, Inc.
Released under the GNU GPLv2+, see the COPYING file
@@ -8,8 +8,9 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep
-#include "pcp/PCPMetric.h"
+#include "pcp/Metric.h"
+#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
@@ -21,15 +22,15 @@ in the source distribution for its full text.
extern Platform* pcp;
-const pmDesc* PCPMetric_desc(PCPMetric metric) {
+const pmDesc* Metric_desc(Metric metric) {
return &pcp->descs[metric];
}
-int PCPMetric_type(PCPMetric metric) {
+int Metric_type(Metric metric) {
return pcp->descs[metric].type;
}
-pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type) {
+pmAtomValue* Metric_values(Metric metric, pmAtomValue* atom, int count, int type) {
if (pcp->result == NULL)
return NULL;
@@ -54,14 +55,14 @@ pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, in
return atom;
}
-int PCPMetric_instanceCount(PCPMetric metric) {
+int Metric_instanceCount(Metric metric) {
pmValueSet* vset = pcp->result->vset[metric];
if (vset)
return vset->numval;
return 0;
}
-int PCPMetric_instanceOffset(PCPMetric metric, int inst) {
+int Metric_instanceOffset(Metric metric, int inst) {
pmValueSet* vset = pcp->result->vset[metric];
if (!vset || vset->numval <= 0)
return 0;
@@ -74,7 +75,7 @@ int PCPMetric_instanceOffset(PCPMetric metric, int inst) {
return 0;
}
-static pmAtomValue* PCPMetric_extract(PCPMetric metric, int inst, int offset, pmValueSet* vset, pmAtomValue* atom, int type) {
+static pmAtomValue* Metric_extract(Metric metric, int inst, int offset, pmValueSet* vset, pmAtomValue* atom, int type) {
/* extract value (using requested type) of given metric instance */
const pmDesc* desc = &pcp->descs[metric];
@@ -89,7 +90,7 @@ static pmAtomValue* PCPMetric_extract(PCPMetric metric, int inst, int offset, pm
return atom;
}
-pmAtomValue* PCPMetric_instance(PCPMetric metric, int inst, int offset, pmAtomValue* atom, int type) {
+pmAtomValue* Metric_instance(Metric metric, int inst, int offset, pmAtomValue* atom, int type) {
pmValueSet* vset = pcp->result->vset[metric];
if (!vset || vset->numval <= 0)
@@ -97,12 +98,12 @@ pmAtomValue* PCPMetric_instance(PCPMetric metric, int inst, int offset, pmAtomVa
/* fast-path using heuristic offset based on expected location */
if (offset >= 0 && offset < vset->numval && inst == vset->vlist[offset].inst)
- return PCPMetric_extract(metric, inst, offset, vset, atom, type);
+ return Metric_extract(metric, inst, offset, vset, atom, type);
/* slow-path using a linear search for the requested instance */
for (int i = 0; i < vset->numval; i++) {
if (inst == vset->vlist[i].inst)
- return PCPMetric_extract(metric, inst, i, vset, atom, type);
+ return Metric_extract(metric, inst, i, vset, atom, type);
}
return NULL;
}
@@ -113,7 +114,7 @@ pmAtomValue* PCPMetric_instance(PCPMetric metric, int inst, int offset, pmAtomVa
*
* Start it off by passing offset -1 into the routine.
*/
-bool PCPMetric_iterate(PCPMetric metric, int* instp, int* offsetp) {
+bool Metric_iterate(Metric metric, int* instp, int* offsetp) {
if (!pcp->result)
return false;
@@ -132,15 +133,15 @@ bool PCPMetric_iterate(PCPMetric metric, int* instp, int* offsetp) {
}
/* Switch on/off a metric for value fetching (sampling) */
-void PCPMetric_enable(PCPMetric metric, bool enable) {
+void Metric_enable(Metric metric, bool enable) {
pcp->fetch[metric] = enable ? pcp->pmids[metric] : PM_ID_NULL;
}
-bool PCPMetric_enabled(PCPMetric metric) {
+bool Metric_enabled(Metric metric) {
return pcp->fetch[metric] != PM_ID_NULL;
}
-void PCPMetric_enableThreads(void) {
+void Metric_enableThreads(void) {
pmValueSet* vset = xCalloc(1, sizeof(pmValueSet));
vset->vlist[0].inst = PM_IN_NULL;
vset->vlist[0].value.lval = 1;
@@ -159,7 +160,7 @@ void PCPMetric_enableThreads(void) {
pmFreeResult(result);
}
-bool PCPMetric_fetch(struct timeval* timestamp) {
+bool Metric_fetch(struct timeval* timestamp) {
if (pcp->result) {
pmFreeResult(pcp->result);
pcp->result = NULL;
@@ -178,3 +179,22 @@ bool PCPMetric_fetch(struct timeval* timestamp) {
*timestamp = pcp->result->timestamp;
return true;
}
+
+void Metric_externalName(Metric metric, int inst, char** externalName) {
+ const pmDesc* desc = &pcp->descs[metric];
+ /* ignore a failure here - its safe to do so */
+ (void)pmNameInDom(desc->indom, inst, externalName);
+}
+
+int Metric_lookupText(const char* metric, char** desc) {
+ pmID pmid;
+ int sts;
+
+ sts = pmLookupName(1, &metric, &pmid);
+ if (sts < 0)
+ return sts;
+
+ if (pmLookupText(pmid, PM_TEXT_ONELINE, desc) >= 0)
+ (*desc)[0] = toupper((*desc)[0]); /* UI consistency */
+ return 0;
+}
diff --git a/pcp/PCPMetric.h b/pcp/Metric.h
index 84ccbb9..e72f6e1 100644
--- a/pcp/PCPMetric.h
+++ b/pcp/Metric.h
@@ -1,7 +1,7 @@
-#ifndef HEADER_PCPMetric
-#define HEADER_PCPMetric
+#ifndef HEADER_Metric
+#define HEADER_Metric
/*
-htop - PCPMetric.h
+htop - Metric.h
(C) 2020-2021 htop dev team
(C) 2020-2021 Red Hat, Inc.
Released under the GNU GPLv2+, see the COPYING file
@@ -22,7 +22,7 @@ in the source distribution for its full text.
#undef PACKAGE_BUGREPORT
-typedef enum PCPMetric_ {
+typedef enum Metric_ {
PCP_CONTROL_THREADS, /* proc.control.perclient.threads */
PCP_HINV_NCPU, /* hinv.ncpu */
@@ -75,6 +75,7 @@ typedef enum PCPMetric_ {
PCP_PSI_CPUSOME, /* kernel.all.pressure.cpu.some.avg */
PCP_PSI_IOSOME, /* kernel.all.pressure.io.some.avg */
PCP_PSI_IOFULL, /* kernel.all.pressure.io.full.avg */
+ PCP_PSI_IRQFULL, /* kernel.all.pressure.irq.full.avg */
PCP_PSI_MEMSOME, /* kernel.all.pressure.memory.some.avg */
PCP_PSI_MEMFULL, /* kernel.all.pressure.memory.full.avg */
PCP_ZFS_ARC_ANON_SIZE, /* zfs.arc.anon_size */
@@ -92,6 +93,10 @@ typedef enum PCPMetric_ {
PCP_ZRAM_CAPACITY, /* zram.capacity */
PCP_ZRAM_ORIGINAL, /* zram.mm_stat.data_size.original */
PCP_ZRAM_COMPRESSED, /* zram.mm_stat.data_size.compressed */
+ PCP_MEM_ZSWAP, /* mem.util.zswap */
+ PCP_MEM_ZSWAPPED, /* mem.util.zswapped */
+ PCP_VFS_FILES_COUNT, /* vfs.files.count */
+ PCP_VFS_FILES_MAX, /* vfs.files.max */
PCP_PROC_PID, /* proc.psinfo.pid */
PCP_PROC_PPID, /* proc.psinfo.ppid */
@@ -153,28 +158,32 @@ typedef enum PCPMetric_ {
PCP_PROC_SMAPS_SWAPPSS, /* proc.smaps.swappss */
PCP_METRIC_COUNT /* total metric count */
-} PCPMetric;
+} Metric;
-void PCPMetric_enable(PCPMetric metric, bool enable);
+void Metric_enable(Metric metric, bool enable);
-bool PCPMetric_enabled(PCPMetric metric);
+bool Metric_enabled(Metric metric);
-void PCPMetric_enableThreads(void);
+void Metric_enableThreads(void);
-bool PCPMetric_fetch(struct timeval* timestamp);
+bool Metric_fetch(struct timeval* timestamp);
-bool PCPMetric_iterate(PCPMetric metric, int* instp, int* offsetp);
+bool Metric_iterate(Metric metric, int* instp, int* offsetp);
-pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type);
+pmAtomValue* Metric_values(Metric metric, pmAtomValue* atom, int count, int type);
-const pmDesc* PCPMetric_desc(PCPMetric metric);
+const pmDesc* Metric_desc(Metric metric);
-int PCPMetric_type(PCPMetric metric);
+int Metric_type(Metric metric);
-int PCPMetric_instanceCount(PCPMetric metric);
+int Metric_instanceCount(Metric metric);
-int PCPMetric_instanceOffset(PCPMetric metric, int inst);
+int Metric_instanceOffset(Metric metric, int inst);
-pmAtomValue* PCPMetric_instance(PCPMetric metric, int inst, int offset, pmAtomValue* atom, int type);
+pmAtomValue* Metric_instance(Metric metric, int inst, int offset, pmAtomValue* atom, int type);
+
+void Metric_externalName(Metric metric, int inst, char** externalName);
+
+int Metric_lookupText(const char* metric, char** desc);
#endif
diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c
index aab2525..b0bd03e 100644
--- a/pcp/PCPDynamicColumn.c
+++ b/pcp/PCPDynamicColumn.c
@@ -1,8 +1,7 @@
/*
htop - PCPDynamicColumn.c
-(C) 2021 Sohaib Mohammed
-(C) 2021 htop dev team
-(C) 2021 Red Hat, Inc.
+(C) 2021-2023 Sohaib Mohammed
+(C) 2021-2023 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@@ -14,6 +13,7 @@ in the source distribution for its full text.
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
+#include <pwd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -23,12 +23,13 @@ in the source distribution for its full text.
#include "Macros.h"
#include "Platform.h"
#include "Process.h"
-#include "ProcessList.h"
+#include "ProcessTable.h"
#include "RichString.h"
#include "XUtils.h"
+#include "linux/CGroupUtils.h"
+#include "pcp/Metric.h"
#include "pcp/PCPProcess.h"
-#include "pcp/PCPMetric.h"
static bool PCPDynamicColumn_addMetric(PCPDynamicColumns* columns, PCPDynamicColumn* column) {
@@ -48,6 +49,10 @@ static bool PCPDynamicColumn_addMetric(PCPDynamicColumns* columns, PCPDynamicCol
}
static void PCPDynamicColumn_parseMetric(PCPDynamicColumns* columns, PCPDynamicColumn* column, const char* path, unsigned int line, char* value) {
+ /* pmLookupText */
+ if (!column->super.description)
+ Metric_lookupText(value, &column->super.description);
+
/* lookup a dynamic metric with this name, else create */
if (PCPDynamicColumn_addMetric(columns, column) == false)
return;
@@ -107,6 +112,10 @@ static bool PCPDynamicColumn_uniqueName(char* key, PCPDynamicColumns* columns) {
static PCPDynamicColumn* PCPDynamicColumn_new(PCPDynamicColumns* columns, const char* name) {
PCPDynamicColumn* column = xCalloc(1, sizeof(*column));
String_safeStrncpy(column->super.name, name, sizeof(column->super.name));
+ column->super.enabled = false;
+ column->percent = false;
+ column->instances = false;
+ column->defaultEnabled = true;
size_t id = columns->count + LAST_PROCESSFIELD;
Hashtable_put(columns->table, id, column);
@@ -159,6 +168,14 @@ static void PCPDynamicColumn_parseFile(PCPDynamicColumns* columns, const char* p
free_and_xStrdup(&column->super.description, value);
} else if (value && column && String_eq(key, "width")) {
column->super.width = strtoul(value, NULL, 10);
+ } else if (value && column && String_eq(key, "format")) {
+ free_and_xStrdup(&column->format, value);
+ } else if (value && column && String_eq(key, "instances")) {
+ if (String_eq(value, "True") || String_eq(value, "true"))
+ column->instances = true;
+ } else if (value && column && (String_eq(key, "default") || String_eq(key, "enabled"))) {
+ if (String_eq(value, "False") || String_eq(value, "false"))
+ column->defaultEnabled = false;
} else if (value && column && String_eq(key, "metric")) {
PCPDynamicColumn_parseMetric(columns, column, path, lineno, value);
}
@@ -194,6 +211,12 @@ void PCPDynamicColumns_init(PCPDynamicColumns* columns) {
const char* home = getenv("HOME");
char* path;
+ if (!xdgConfigHome && !home) {
+ const struct passwd* pw = getpwuid(getuid());
+ if (pw)
+ home = pw->pw_dir;
+ }
+
columns->table = Hashtable_new(0, true);
/* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */
@@ -226,88 +249,239 @@ void PCPDynamicColumns_init(PCPDynamicColumns* columns) {
free(path);
}
+void PCPDynamicColumn_done(PCPDynamicColumn* this) {
+ DynamicColumn_done(&this->super);
+ free(this->metricName);
+ free(this->format);
+}
+
static void PCPDynamicColumns_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
PCPDynamicColumn* column = (PCPDynamicColumn*) value;
- free(column->metricName);
- free(column->super.heading);
- free(column->super.caption);
- free(column->super.description);
+ PCPDynamicColumn_done(column);
}
void PCPDynamicColumns_done(Hashtable* table) {
Hashtable_foreach(table, PCPDynamicColumns_free, NULL);
}
-void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str) {
- const PCPProcess* pp = (const PCPProcess*) proc;
- unsigned int type = PCPMetric_type(this->id);
+static void PCPDynamicColumn_setupWidth(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
+ PCPDynamicColumn* column = (PCPDynamicColumn*) value;
- pmAtomValue atom;
- if (!PCPMetric_instance(this->id, proc->pid, pp->offset, &atom, type)) {
- RichString_appendAscii(str, CRT_colors[METER_VALUE_ERROR], "no data");
+ /* calculate column size based on config file and metric units */
+ const pmDesc* desc = Metric_desc(column->id);
+
+ if (column->instances || desc->type == PM_TYPE_STRING) {
+ column->super.width = column->width;
+ if (column->super.width == 0)
+ column->super.width = -16;
return;
}
- int width = this->super.width;
- if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
- width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
- int abswidth = abs(width);
- if (abswidth > DYNAMIC_MAX_COLUMN_WIDTH) {
- abswidth = DYNAMIC_MAX_COLUMN_WIDTH;
- width = -abswidth;
+ if (column->format) {
+ if (strcmp(column->format, "percent") == 0) {
+ column->super.width = 5;
+ return;
+ }
+ if (strcmp(column->format, "process") == 0) {
+ column->super.width = Process_pidDigits;
+ return;
+ }
}
- char buffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
- int attr = CRT_colors[DEFAULT_COLOR];
+ if (column->width) {
+ column->super.width = column->width;
+ return;
+ }
+
+ pmUnits units = desc->units;
+ if (units.dimSpace && units.dimTime)
+ column->super.width = 11; // Row_printRate
+ else if (units.dimSpace)
+ column->super.width = 5; // Row_printBytes
+ else if (units.dimCount && units.dimTime)
+ column->super.width = 11; // Row_printCount
+ else if (units.dimTime)
+ column->super.width = 8; // Row_printTime
+ else
+ column->super.width = 11; // Row_printCount
+}
+
+void PCPDynamicColumns_setupWidths(PCPDynamicColumns* columns) {
+ Hashtable_foreach(columns->table, PCPDynamicColumn_setupWidth, NULL);
+}
+
+/* normalize output units to bytes and seconds */
+static int PCPDynamicColumn_normalize(const pmDesc* desc, const pmAtomValue* ap, double* value) {
+ /* form normalized units based on the original metric units */
+ pmUnits units = desc->units;
+ if (units.dimTime)
+ units.scaleTime = PM_TIME_SEC;
+ if (units.dimSpace)
+ units.scaleSpace = PM_SPACE_BYTE;
+ if (units.dimCount)
+ units.scaleCount = PM_COUNT_ONE;
+
+ pmAtomValue atom;
+ int sts, type = desc->type;
+ if ((sts = pmConvScale(type, ap, &desc->units, &atom, &units)) < 0)
+ return sts;
+
switch (type) {
- case PM_TYPE_STRING:
- attr = CRT_colors[PROCESS_SHADOW];
- Process_printLeftAlignedField(str, attr, atom.cp, abswidth);
- free(atom.cp);
- break;
case PM_TYPE_32:
- xSnprintf(buffer, sizeof(buffer), "%*d ", width, atom.l);
- RichString_appendAscii(str, attr, buffer);
+ *value = (double) atom.l;
break;
case PM_TYPE_U32:
- xSnprintf(buffer, sizeof(buffer), "%*u ", width, atom.ul);
- RichString_appendAscii(str, attr, buffer);
+ *value = (double) atom.ul;
break;
case PM_TYPE_64:
- xSnprintf(buffer, sizeof(buffer), "%*lld ", width, (long long) atom.ll);
- RichString_appendAscii(str, attr, buffer);
+ *value = (double) atom.ll;
break;
case PM_TYPE_U64:
- xSnprintf(buffer, sizeof(buffer), "%*llu ", width, (unsigned long long) atom.ull);
- RichString_appendAscii(str, attr, buffer);
+ *value = (double) atom.ull;
break;
case PM_TYPE_FLOAT:
- xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, (double) atom.f);
- RichString_appendAscii(str, attr, buffer);
+ *value = (double) atom.f;
break;
case PM_TYPE_DOUBLE:
- xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, atom.d);
- RichString_appendAscii(str, attr, buffer);
+ *value = atom.d;
break;
default:
- attr = CRT_colors[METER_VALUE_ERROR];
- RichString_appendAscii(str, attr, "no type");
- break;
+ return PM_ERR_CONV;
+ }
+
+ return 0;
+}
+
+void PCPDynamicColumn_writeAtomValue(PCPDynamicColumn* column, RichString* str, const struct Settings_* settings, int metric, int instance, const pmDesc* desc, const void* atom) {
+ const pmAtomValue* atomvalue = (const pmAtomValue*) atom;
+ char buffer[DYNAMIC_MAX_COLUMN_WIDTH + /*space*/ 1 + /*null*/ 1];
+ int attr = CRT_colors[DEFAULT_COLOR];
+ int width = column->super.width;
+ int n;
+
+ if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
+ width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
+ int abswidth = abs(width);
+ if (abswidth > DYNAMIC_MAX_COLUMN_WIDTH) {
+ abswidth = DYNAMIC_MAX_COLUMN_WIDTH;
+ width = -abswidth;
+ }
+
+ if (atomvalue == NULL) {
+ n = xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, abswidth, "N/A");
+ RichString_appendnAscii(str, CRT_colors[PROCESS_SHADOW], buffer, n);
+ return;
}
+
+ /* deal with instance names and metrics with string values first */
+ if (column->instances || desc->type == PM_TYPE_STRING) {
+ char* value = NULL;
+ char* dupd1 = NULL;
+ if (column->instances) {
+ attr = CRT_colors[DYNAMIC_GRAY];
+ Metric_externalName(metric, instance, &dupd1);
+ value = dupd1;
+ } else {
+ attr = CRT_colors[DYNAMIC_GREEN];
+ value = atomvalue->cp;
+ }
+ if (column->format && value) {
+ char* dupd2 = NULL;
+ if (strcmp(column->format, "command") == 0)
+ attr = CRT_colors[PROCESS_COMM];
+ else if (strcmp(column->format, "process") == 0)
+ attr = CRT_colors[PROCESS_SHADOW];
+ else if (strcmp(column->format, "device") == 0 && strncmp(value, "/dev/", 5) == 0)
+ value += 5;
+ else if (strcmp(column->format, "cgroup") == 0 && (dupd2 = CGroup_filterName(value)))
+ value = dupd2;
+ n = xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, abswidth, value);
+ if (dupd2)
+ free(dupd2);
+ } else if (value) {
+ n = xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, abswidth, value);
+ } else {
+ n = xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, abswidth, "N/A");
+ }
+ if (dupd1)
+ free(dupd1);
+ RichString_appendnAscii(str, attr, buffer, n);
+ return;
+ }
+
+ /* deal with any numeric value - first, normalize units to bytes/seconds */
+ double value;
+ if (PCPDynamicColumn_normalize(desc, atomvalue, &value) < 0) {
+ n = xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, abswidth, "no conv");
+ RichString_appendnAscii(str, CRT_colors[METER_VALUE_ERROR], buffer, n);
+ return;
+ }
+
+ if (column->format) {
+ if (strcmp(column->format, "percent") == 0) {
+ n = Row_printPercentage(value, buffer, sizeof(buffer), width, &attr);
+ RichString_appendnAscii(str, attr, buffer, n);
+ return;
+ }
+ if (strcmp(column->format, "process") == 0) {
+ n = xSnprintf(buffer, sizeof(buffer), "%*d ", Row_pidDigits, (int)value);
+ RichString_appendnAscii(str, attr, buffer, n);
+ return;
+ }
+ }
+
+ /* width overrides unit suffix and coloring; too complex for a corner case */
+ if (column->width) {
+ if (value - (unsigned long long)value > 0) /* display floating point */
+ n = xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, value);
+ else /* display as integer */
+ n = xSnprintf(buffer, sizeof(buffer), "%*llu ", width, (unsigned long long)value);
+ RichString_appendnAscii(str, CRT_colors[PROCESS], buffer, n);
+ return;
+ }
+
+ bool coloring = settings->highlightMegabytes;
+ pmUnits units = desc->units;
+ if (units.dimSpace && units.dimTime)
+ Row_printRate(str, value, coloring);
+ else if (units.dimSpace)
+ Row_printBytes(str, value, coloring);
+ else if (units.dimCount)
+ Row_printCount(str, value, coloring);
+ else if (units.dimTime)
+ Row_printTime(str, value / 10 /* hundreds of a second */, coloring);
+ else
+ Row_printCount(str, value, 0); /* e.g. PID */
+}
+
+void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str) {
+ const Settings* settings = proc->super.host->settings;
+ const PCPProcess* pp = (const PCPProcess*) proc;
+ const pmDesc* desc = Metric_desc(this->id);
+ pid_t pid = Process_getPid(proc);
+
+ pmAtomValue atom;
+ pmAtomValue* ap = &atom;
+ if (!Metric_instance(this->id, pid, pp->offset, ap, desc->type))
+ ap = NULL;
+
+ PCPDynamicColumn_writeAtomValue(this, str, settings, this->id, pid, desc, ap);
}
int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key) {
- const PCPDynamicColumn* column = Hashtable_get(p1->super.processList->dynamicColumns, key);
+ const Process* proc = &p1->super;
+ const Settings* settings = proc->super.host->settings;
+ const PCPDynamicColumn* column = Hashtable_get(settings->dynamicColumns, key);
if (!column)
return -1;
size_t metric = column->id;
- unsigned int type = PCPMetric_type(metric);
+ unsigned int type = Metric_type(metric);
pmAtomValue atom1 = {0}, atom2 = {0};
- if (!PCPMetric_instance(metric, p1->super.pid, p1->offset, &atom1, type) ||
- !PCPMetric_instance(metric, p2->super.pid, p2->offset, &atom2, type)) {
+ if (!Metric_instance(metric, Process_getPid(&p1->super), p1->offset, &atom1, type) ||
+ !Metric_instance(metric, Process_getPid(&p2->super), p2->offset, &atom2, type)) {
if (type == PM_TYPE_STRING) {
free(atom1.cp);
free(atom2.cp);
@@ -331,11 +505,12 @@ int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, Pr
case PM_TYPE_U64:
return SPACESHIP_NUMBER(atom2.ull, atom1.ull);
case PM_TYPE_FLOAT:
- return SPACESHIP_NUMBER(atom2.f, atom1.f);
+ return compareRealNumbers(atom2.f, atom1.f);
case PM_TYPE_DOUBLE:
- return SPACESHIP_NUMBER(atom2.d, atom1.d);
+ return compareRealNumbers(atom2.d, atom1.d);
default:
break;
}
+
return -1;
}
diff --git a/pcp/PCPDynamicColumn.h b/pcp/PCPDynamicColumn.h
index d0ffe71..ade782b 100644
--- a/pcp/PCPDynamicColumn.h
+++ b/pcp/PCPDynamicColumn.h
@@ -1,5 +1,11 @@
#ifndef HEADER_PCPDynamicColumn
#define HEADER_PCPDynamicColumn
+/*
+htop - PCPDynamicColumn.h
+(C) 2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
#include <stddef.h>
@@ -11,15 +17,22 @@
#include "pcp/PCPProcess.h"
+struct pmDesc;
+
typedef struct PCPDynamicColumn_ {
DynamicColumn super;
char* metricName;
+ char* format;
size_t id; /* identifier for metric array lookups */
+ int width; /* optional width from configuration file */
+ bool defaultEnabled; /* default enabled in dynamic screen */
+ bool percent;
+ bool instances; /* an instance *names* column, not values */
} PCPDynamicColumn;
typedef struct PCPDynamicColumns_ {
Hashtable* table;
- size_t count; /* count of dynamic meters discovered by scan */
+ size_t count; /* count of dynamic columns discovered by scan */
size_t offset; /* start offset into the Platform metric array */
size_t cursor; /* identifier allocator for each new metric used */
} PCPDynamicColumns;
@@ -28,8 +41,14 @@ void PCPDynamicColumns_init(PCPDynamicColumns* columns);
void PCPDynamicColumns_done(Hashtable* table);
+void PCPDynamicColumns_setupWidths(PCPDynamicColumns* columns);
+
void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str);
+void PCPDynamicColumn_writeAtomValue(PCPDynamicColumn* column, RichString* str, const struct Settings_* settings, int metric, int instance, const struct pmDesc* desc, const void* atomvalue);
+
int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key);
+void PCPDynamicColumn_done(PCPDynamicColumn* this);
+
#endif
diff --git a/pcp/PCPDynamicMeter.c b/pcp/PCPDynamicMeter.c
index 7c55e4b..11df5f0 100644
--- a/pcp/PCPDynamicMeter.c
+++ b/pcp/PCPDynamicMeter.c
@@ -5,6 +5,7 @@ htop - PCPDynamicMeter.c
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"
@@ -12,18 +13,20 @@ in the source distribution for its full text.
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
-#include <pcp/pmapi.h>
+#include <pwd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <pcp/pmapi.h>
+
#include "Macros.h"
#include "Platform.h"
#include "RichString.h"
#include "XUtils.h"
-#include "pcp/PCPMetric.h"
+#include "pcp/Metric.h"
static PCPDynamicMetric* PCPDynamicMeter_lookupMetric(PCPDynamicMeters* meters, PCPDynamicMeter* meter, const char* name) {
@@ -251,6 +254,12 @@ void PCPDynamicMeters_init(PCPDynamicMeters* meters) {
const char* home = getenv("HOME");
char* path;
+ if (!xdgConfigHome && !home) {
+ const struct passwd* pw = getpwuid(getuid());
+ if (pw)
+ home = pw->pw_dir;
+ }
+
meters->table = Hashtable_new(0, true);
/* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */
@@ -301,7 +310,7 @@ void PCPDynamicMeters_done(Hashtable* table) {
void PCPDynamicMeter_enable(PCPDynamicMeter* this) {
for (size_t i = 0; i < this->totalMetrics; i++)
- PCPMetric_enable(this->metrics[i].id, true);
+ Metric_enable(this->metrics[i].id, true);
}
void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter) {
@@ -314,10 +323,10 @@ void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter) {
buffer[bytes++] = '/'; /* separator */
PCPDynamicMetric* metric = &this->metrics[i];
- const pmDesc* desc = PCPMetric_desc(metric->id);
+ const pmDesc* desc = Metric_desc(metric->id);
pmAtomValue atom, raw;
- if (!PCPMetric_values(metric->id, &raw, 1, desc->type)) {
+ if (!Metric_values(metric->id, &raw, 1, desc->type)) {
bytes--; /* clear the separator */
continue;
}
@@ -342,27 +351,27 @@ void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter) {
break;
case PM_TYPE_32:
bytes += conv.dimSpace ?
- Meter_humanUnit(buffer + bytes, atom.l, size - bytes) :
+ Meter_humanUnit(buffer + bytes, (double) atom.l, size - bytes) :
xSnprintf(buffer + bytes, size - bytes, "%d", atom.l);
break;
case PM_TYPE_U32:
bytes += conv.dimSpace ?
- Meter_humanUnit(buffer + bytes, atom.ul, size - bytes) :
+ Meter_humanUnit(buffer + bytes, (double) atom.ul, size - bytes) :
xSnprintf(buffer + bytes, size - bytes, "%u", atom.ul);
break;
case PM_TYPE_64:
bytes += conv.dimSpace ?
- Meter_humanUnit(buffer + bytes, atom.ll, size - bytes) :
+ Meter_humanUnit(buffer + bytes, (double) atom.ll, size - bytes) :
xSnprintf(buffer + bytes, size - bytes, "%lld", (long long) atom.ll);
break;
case PM_TYPE_U64:
bytes += conv.dimSpace ?
- Meter_humanUnit(buffer + bytes, atom.ull, size - bytes) :
+ Meter_humanUnit(buffer + bytes, (double) atom.ull, size - bytes) :
xSnprintf(buffer + bytes, size - bytes, "%llu", (unsigned long long) atom.ull);
break;
case PM_TYPE_FLOAT:
bytes += conv.dimSpace ?
- Meter_humanUnit(buffer + bytes, atom.f, size - bytes) :
+ Meter_humanUnit(buffer + bytes, (double) atom.f, size - bytes) :
xSnprintf(buffer + bytes, size - bytes, "%.2f", (double) atom.f);
break;
case PM_TYPE_DOUBLE:
@@ -373,9 +382,11 @@ void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter) {
default:
break;
}
+
if (saved != bytes && metric->suffix)
bytes += xSnprintf(buffer + bytes, size - bytes, "%s", metric->suffix);
}
+
if (!bytes)
xSnprintf(buffer, size, "no data");
}
@@ -385,11 +396,11 @@ void PCPDynamicMeter_display(PCPDynamicMeter* this, ATTR_UNUSED const Meter* met
for (size_t i = 0; i < this->totalMetrics; i++) {
PCPDynamicMetric* metric = &this->metrics[i];
- const pmDesc* desc = PCPMetric_desc(metric->id);
+ const pmDesc* desc = Metric_desc(metric->id);
pmAtomValue atom, raw;
char buffer[64];
- if (!PCPMetric_values(metric->id, &raw, 1, desc->type))
+ if (!Metric_values(metric->id, &raw, 1, desc->type))
continue;
pmUnits conv = desc->units; /* convert to canonical units */
@@ -418,27 +429,27 @@ void PCPDynamicMeter_display(PCPDynamicMeter* this, ATTR_UNUSED const Meter* met
break;
case PM_TYPE_32:
len = conv.dimSpace ?
- Meter_humanUnit(buffer, atom.l, sizeof(buffer)) :
+ Meter_humanUnit(buffer, (double) atom.l, sizeof(buffer)) :
xSnprintf(buffer, sizeof(buffer), "%d", atom.l);
break;
case PM_TYPE_U32:
len = conv.dimSpace ?
- Meter_humanUnit(buffer, atom.ul, sizeof(buffer)) :
+ Meter_humanUnit(buffer, (double) atom.ul, sizeof(buffer)) :
xSnprintf(buffer, sizeof(buffer), "%u", atom.ul);
break;
case PM_TYPE_64:
len = conv.dimSpace ?
- Meter_humanUnit(buffer, atom.ll, sizeof(buffer)) :
+ Meter_humanUnit(buffer, (double) atom.ll, sizeof(buffer)) :
xSnprintf(buffer, sizeof(buffer), "%lld", (long long) atom.ll);
break;
case PM_TYPE_U64:
len = conv.dimSpace ?
- Meter_humanUnit(buffer, atom.ull, sizeof(buffer)) :
+ Meter_humanUnit(buffer, (double) atom.ull, sizeof(buffer)) :
xSnprintf(buffer, sizeof(buffer), "%llu", (unsigned long long) atom.ull);
break;
case PM_TYPE_FLOAT:
len = conv.dimSpace ?
- Meter_humanUnit(buffer, atom.f, sizeof(buffer)) :
+ Meter_humanUnit(buffer, (double) atom.f, sizeof(buffer)) :
xSnprintf(buffer, sizeof(buffer), "%.2f", (double) atom.f);
break;
case PM_TYPE_DOUBLE:
@@ -449,12 +460,14 @@ void PCPDynamicMeter_display(PCPDynamicMeter* this, ATTR_UNUSED const Meter* met
default:
break;
}
+
if (len) {
RichString_appendnAscii(out, CRT_colors[metric->color], buffer, len);
if (metric->suffix)
RichString_appendAscii(out, CRT_colors[METER_TEXT], metric->suffix);
}
}
+
if (nodata)
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
}
diff --git a/pcp/PCPDynamicMeter.h b/pcp/PCPDynamicMeter.h
index 0e5ddd2..3a72d13 100644
--- a/pcp/PCPDynamicMeter.h
+++ b/pcp/PCPDynamicMeter.h
@@ -1,5 +1,11 @@
#ifndef HEADER_PCPDynamicMeter
#define HEADER_PCPDynamicMeter
+/*
+htop - PCPDynamicMeter.h
+(C) 2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
#include <stddef.h>
diff --git a/pcp/PCPDynamicScreen.c b/pcp/PCPDynamicScreen.c
new file mode 100644
index 0000000..2222822
--- /dev/null
+++ b/pcp/PCPDynamicScreen.c
@@ -0,0 +1,407 @@
+/*
+htop - PCPDynamicScreen.c
+(C) 2022 Sohaib Mohammed
+(C) 2022-2023 htop dev team
+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/PCPDynamicScreen.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include <pcp/pmapi.h>
+
+#include "AvailableColumnsPanel.h"
+#include "Macros.h"
+#include "Platform.h"
+#include "Settings.h"
+#include "XUtils.h"
+
+#include "pcp/InDomTable.h"
+#include "pcp/PCPDynamicColumn.h"
+
+
+static char* formatFields(PCPDynamicScreen* screen) {
+ char* columns = strdup("");
+
+ for (size_t j = 0; j < screen->totalColumns; j++) {
+ const PCPDynamicColumn* column = screen->columns[j];
+ if (column->super.enabled == false)
+ continue;
+ char* prefix = columns;
+ xAsprintf(&columns, "%s Dynamic(%s)", prefix, column->super.name);
+ free(prefix);
+ }
+
+ return columns;
+}
+
+static void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynamicColumns* columns) {
+ for (size_t i = 0; i < screens->count; i++) {
+ PCPDynamicScreen* screen = Hashtable_get(screens->table, i);
+ if (!screen)
+ return;
+
+ /* setup default fields (columns) based on configuration */
+ for (size_t j = 0; j < screen->totalColumns; j++) {
+ PCPDynamicColumn* column = screen->columns[j];
+
+ column->id = columns->offset + columns->cursor;
+ columns->cursor++;
+ Platform_addMetric(column->id, column->metricName);
+
+ size_t id = columns->count + LAST_PROCESSFIELD;
+ Hashtable_put(columns->table, id, column);
+ columns->count++;
+
+ if (j == 0) {
+ const pmDesc* desc = Metric_desc(column->id);
+ assert(desc->indom != PM_INDOM_NULL);
+ screen->indom = desc->indom;
+ screen->key = column->id;
+ }
+ }
+ screen->super.columnKeys = formatFields(screen);
+ }
+}
+
+static PCPDynamicColumn* PCPDynamicScreen_lookupMetric(PCPDynamicScreen* screen, const char* name) {
+ PCPDynamicColumn* column = NULL;
+ size_t bytes = strlen(name) + strlen(screen->super.name) + 1; /* colon */
+ if (bytes >= sizeof(column->super.name))
+ return NULL;
+
+ bytes += 16; /* prefix, dots and terminator */
+ char* metricName = xMalloc(bytes);
+ xSnprintf(metricName, bytes, "htop.screen.%s.%s", screen->super.name, name);
+
+ for (size_t i = 0; i < screen->totalColumns; i++) {
+ column = screen->columns[i];
+ if (String_eq(column->metricName, metricName)) {
+ free(metricName);
+ return column;
+ }
+ }
+
+ /* not an existing column in this screen - create it and add to the list */
+ column = xCalloc(1, sizeof(PCPDynamicColumn));
+ xSnprintf(column->super.name, sizeof(column->super.name), "%s:%s", screen->super.name, name);
+ column->super.table = &screen->table->super;
+ column->metricName = metricName;
+ column->super.enabled = true;
+
+ size_t n = screen->totalColumns + 1;
+ screen->columns = xReallocArray(screen->columns, n, sizeof(PCPDynamicColumn*));
+ screen->columns[n - 1] = column;
+ screen->totalColumns = n;
+
+ return column;
+}
+
+static void PCPDynamicScreen_parseColumn(PCPDynamicScreen* screen, const char* path, unsigned int line, char* key, char* value) {
+ PCPDynamicColumn* column;
+ char* p;
+
+ if ((p = strchr(key, '.')) == NULL)
+ return;
+ *p++ = '\0'; /* end the name, p is now the attribute, e.g. 'label' */
+
+ /* lookup a dynamic column with this name, else create */
+ column = PCPDynamicScreen_lookupMetric(screen, key);
+
+ if (String_eq(p, "metric")) {
+ char* error;
+ if (pmRegisterDerivedMetric(column->metricName, value, &error) < 0) {
+ char* note;
+ xAsprintf(&note,
+ "%s: failed to parse expression in %s at line %u\n%s\n",
+ pmGetProgname(), path, line, error);
+ free(error);
+ errno = EINVAL;
+ CRT_fatalError(note);
+ free(note);
+ }
+
+ /* pmLookupText - add optional metric help text */
+ if (!column->super.description && !column->instances)
+ Metric_lookupText(value, &column->super.description);
+
+ } else {
+ /* this is a property of a dynamic column - the column expression */
+ /* may not have been observed yet; i.e. we allow for any ordering */
+
+ if (String_eq(p, "caption")) {
+ free_and_xStrdup(&column->super.caption, value);
+ } else if (String_eq(p, "heading")) {
+ free_and_xStrdup(&column->super.heading, value);
+ } else if (String_eq(p, "description")) {
+ free_and_xStrdup(&column->super.description, value);
+ } else if (String_eq(p, "width")) {
+ column->width = strtoul(value, NULL, 10);
+ } else if (String_eq(p, "format")) {
+ free_and_xStrdup(&column->format, value);
+ } else if (String_eq(p, "instances")) {
+ if (String_eq(value, "True") || String_eq(value, "true"))
+ column->instances = true;
+ free_and_xStrdup(&column->super.description, screen->super.caption);
+ } else if (String_eq(p, "default")) { /* displayed by default */
+ if (String_eq(value, "False") || String_eq(value, "false"))
+ column->defaultEnabled = column->super.enabled = false;
+ }
+ }
+}
+
+static bool PCPDynamicScreen_validateScreenName(char* key, const char* path, unsigned int line) {
+ char* p = key;
+ char* end = strrchr(key, ']');
+
+ if (end) {
+ *end = '\0';
+ } else {
+ fprintf(stderr,
+ "%s: no closing brace on screen name at %s line %u\n\"%s\"",
+ pmGetProgname(), path, line, key);
+ return false;
+ }
+
+ while (*p) {
+ if (p == key) {
+ if (!isalpha(*p) && *p != '_')
+ break;
+ } else {
+ if (!isalnum(*p) && *p != '_')
+ break;
+ }
+ p++;
+ }
+ if (*p != '\0') { /* badness */
+ fprintf(stderr,
+ "%s: invalid screen name at %s line %u\n\"%s\"",
+ pmGetProgname(), path, line, key);
+ return false;
+ }
+ return true;
+}
+
+/* Ensure a screen name has not been defined previously */
+static bool PCPDynamicScreen_uniqueName(char* key, PCPDynamicScreens* screens) {
+ return !DynamicScreen_search(screens->table, key, NULL);
+}
+
+static PCPDynamicScreen* PCPDynamicScreen_new(PCPDynamicScreens* screens, const char* name) {
+ PCPDynamicScreen* screen = xCalloc(1, sizeof(*screen));
+ String_safeStrncpy(screen->super.name, name, sizeof(screen->super.name));
+ screen->defaultEnabled = true;
+
+ size_t id = screens->count;
+ Hashtable_put(screens->table, id, screen);
+ screens->count++;
+
+ return screen;
+}
+
+static void PCPDynamicScreen_parseFile(PCPDynamicScreens* screens, const char* path) {
+ FILE* file = fopen(path, "r");
+ if (!file)
+ return;
+
+ PCPDynamicScreen* screen = NULL;
+ unsigned int lineno = 0;
+ bool ok = true;
+ for (;;) {
+ char* line = String_readLine(file);
+ if (!line)
+ break;
+ lineno++;
+
+ /* cleanup whitespace, skip comment lines */
+ char* trimmed = String_trim(line);
+ free(line);
+ if (!trimmed || !trimmed[0] || trimmed[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 name - i.e. new screen */
+ ok = PCPDynamicScreen_validateScreenName(key + 1, path, lineno);
+ if (ok)
+ ok = PCPDynamicScreen_uniqueName(key + 1, screens);
+ if (ok)
+ screen = PCPDynamicScreen_new(screens, key + 1);
+ if (pmDebugOptions.appl0)
+ fprintf(stderr, "[%s] screen: %s\n", path, key + 1);
+ } else if (!ok) {
+ ; /* skip this one, we're looking for a new header */
+ } else if (!value || !screen) {
+ ; /* skip this one as we always need value strings */
+ } else if (String_eq(key, "heading")) {
+ free_and_xStrdup(&screen->super.heading, value);
+ } else if (String_eq(key, "caption")) {
+ free_and_xStrdup(&screen->super.caption, value);
+ } else if (String_eq(key, "sortKey")) {
+ free_and_xStrdup(&screen->super.sortKey, value);
+ } else if (String_eq(key, "sortDirection")) {
+ screen->super.direction = strtoul(value, NULL, 10);
+ } else if (String_eq(key, "default") || String_eq(key, "enabled")) {
+ if (String_eq(value, "False") || String_eq(value, "false"))
+ screen->defaultEnabled = false;
+ else if (String_eq(value, "True") || String_eq(value, "true"))
+ screen->defaultEnabled = true; /* also default */
+ } else {
+ PCPDynamicScreen_parseColumn(screen, path, lineno, key, value);
+ }
+ String_freeArray(config);
+ free(value);
+ free(key);
+ }
+ fclose(file);
+}
+
+static void PCPDynamicScreen_scanDir(PCPDynamicScreens* screens, 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);
+ PCPDynamicScreen_parseFile(screens, file);
+ free(file);
+ }
+ closedir(dir);
+}
+
+void PCPDynamicScreens_init(PCPDynamicScreens* screens, PCPDynamicColumns* columns) {
+ const char* share = pmGetConfig("PCP_SHARE_DIR");
+ const char* sysconf = pmGetConfig("PCP_SYSCONF_DIR");
+ const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
+ const char* override = getenv("PCP_HTOP_DIR");
+ const char* home = getenv("HOME");
+ char* path;
+
+ screens->table = Hashtable_new(0, true);
+
+ /* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */
+ if (override) {
+ path = String_cat(override, "/screens/");
+ PCPDynamicScreen_scanDir(screens, path);
+ free(path);
+ }
+
+ /* next, search in home directory alongside htoprc */
+ if (xdgConfigHome)
+ path = String_cat(xdgConfigHome, "/htop/screens/");
+ else if (home)
+ path = String_cat(home, "/.config/htop/screens/");
+ else
+ path = NULL;
+ if (path) {
+ PCPDynamicScreen_scanDir(screens, path);
+ free(path);
+ }
+
+ /* next, search in the system screens directory */
+ path = String_cat(sysconf, "/htop/screens/");
+ PCPDynamicScreen_scanDir(screens, path);
+ free(path);
+
+ /* next, try the readonly system screens directory */
+ path = String_cat(share, "/htop/screens/");
+ PCPDynamicScreen_scanDir(screens, path);
+ free(path);
+
+ /* establish internal metric identifier mappings */
+ PCPDynamicScreens_appendDynamicColumns(screens, columns);
+}
+
+static void PCPDynamicScreen_done(PCPDynamicScreen* ds) {
+ DynamicScreen_done(&ds->super);
+ Object_delete(ds->table);
+ free(ds->columns);
+}
+
+static void PCPDynamicScreens_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
+ PCPDynamicScreen* ds = (PCPDynamicScreen*) value;
+ PCPDynamicScreen_done(ds);
+}
+
+void PCPDynamicScreens_done(Hashtable* table) {
+ Hashtable_foreach(table, PCPDynamicScreens_free, NULL);
+}
+
+void PCPDynamicScreen_appendTables(PCPDynamicScreens* screens, Machine* host) {
+ PCPDynamicScreen* ds;
+
+ for (size_t i = 0; i < screens->count; i++) {
+ if ((ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i)) == NULL)
+ continue;
+ ds->table = InDomTable_new(host, ds->indom, ds->key);
+ }
+}
+
+void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settings) {
+ PCPDynamicScreen* ds;
+
+ for (size_t i = 0; i < screens->count; i++) {
+ if ((ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i)) == NULL)
+ continue;
+ if (ds->defaultEnabled == false)
+ continue;
+ const char* tab = ds->super.heading;
+ Settings_newDynamicScreen(settings, tab, &ds->super, &ds->table->super);
+ }
+}
+
+/* called when htoprc .dynamic line is parsed for a dynamic screen */
+void PCPDynamicScreen_addDynamicScreen(PCPDynamicScreens* screens, ScreenSettings* ss) {
+ PCPDynamicScreen* ds;
+
+ for (size_t i = 0; i < screens->count; i++) {
+ if ((ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i)) == NULL)
+ continue;
+ if (String_eq(ss->dynamic, ds->super.name) == false)
+ continue;
+ ss->table = &ds->table->super;
+ }
+}
+
+void PCPDynamicScreens_addAvailableColumns(Panel* availableColumns, Hashtable* screens, const char* screen) {
+ Vector_prune(availableColumns->items);
+
+ bool success;
+ unsigned int key;
+ success = DynamicScreen_search(screens, screen, &key);
+ if (!success)
+ return;
+
+ PCPDynamicScreen* dynamicScreen = Hashtable_get(screens, key);
+ if (!screen)
+ return;
+
+ for (unsigned int j = 0; j < dynamicScreen->totalColumns; j++) {
+ PCPDynamicColumn* column = dynamicScreen->columns[j];
+ const char* title = column->super.heading ? column->super.heading : column->super.name;
+ const char* text = column->super.description ? column->super.description : column->super.caption;
+ char description[256];
+ if (text)
+ xSnprintf(description, sizeof(description), "%s - %s", title, text);
+ else
+ xSnprintf(description, sizeof(description), "%s", title);
+ Panel_add(availableColumns, (Object*) ListItem_new(description, j));
+ }
+}
diff --git a/pcp/PCPDynamicScreen.h b/pcp/PCPDynamicScreen.h
new file mode 100644
index 0000000..6248394
--- /dev/null
+++ b/pcp/PCPDynamicScreen.h
@@ -0,0 +1,56 @@
+#ifndef HEADER_PCPDynamicScreen
+#define HEADER_PCPDynamicScreen
+/*
+htop - PCPDynamicScreen.h
+(C) 2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "CRT.h"
+#include "DynamicScreen.h"
+#include "Hashtable.h"
+#include "Machine.h"
+#include "Panel.h"
+#include "Settings.h"
+
+
+struct InDomTable_;
+struct PCPDynamicColumn_;
+struct PCPDynamicColumns_;
+
+typedef struct PCPDynamicScreen_ {
+ DynamicScreen super;
+
+ struct InDomTable_* table;
+ struct PCPDynamicColumn_** columns;
+ size_t totalColumns;
+
+ unsigned int indom; /* instance domain number */
+ unsigned int key; /* PCPMetric identifier */
+
+ bool defaultEnabled; /* enabled setting from configuration file */
+ /* at runtime enabled screens have entries in settings->screens */
+} PCPDynamicScreen;
+
+typedef struct PCPDynamicScreens_ {
+ Hashtable* table;
+ size_t count; /* count of dynamic screens discovered from scan */
+} PCPDynamicScreens;
+
+void PCPDynamicScreens_init(PCPDynamicScreens* screens, struct PCPDynamicColumns_* columns);
+
+void PCPDynamicScreens_done(Hashtable* table);
+
+void PCPDynamicScreen_appendTables(PCPDynamicScreens* screens, Machine* host);
+
+void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settings);
+
+void PCPDynamicScreen_addDynamicScreen(PCPDynamicScreens* screens, ScreenSettings* ss);
+
+void PCPDynamicScreens_addAvailableColumns(Panel* availableColumns, Hashtable* screens, const char* screen);
+
+#endif
diff --git a/pcp/PCPMachine.c b/pcp/PCPMachine.c
new file mode 100644
index 0000000..2e87253
--- /dev/null
+++ b/pcp/PCPMachine.c
@@ -0,0 +1,345 @@
+/*
+htop - PCPProcessTable.c
+(C) 2014 Hisham H. Muhammad
+(C) 2020-2023 htop dev team
+(C) 2020-2023 Red Hat, Inc.
+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/PCPMachine.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "Machine.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Platform.h"
+#include "Settings.h"
+#include "XUtils.h"
+
+#include "pcp/Metric.h"
+#include "pcp/PCPProcess.h"
+
+
+static void PCPMachine_updateCPUcount(PCPMachine* this) {
+ Machine* super = &this->super;
+ super->activeCPUs = Metric_instanceCount(PCP_PERCPU_SYSTEM);
+ unsigned int cpus = Platform_getMaxCPU();
+ if (cpus == super->existingCPUs)
+ return;
+ if (cpus == 0)
+ cpus = super->activeCPUs;
+ if (cpus <= 1)
+ cpus = super->activeCPUs = 1;
+ super->existingCPUs = cpus;
+
+ free(this->percpu);
+ free(this->values);
+
+ this->percpu = xCalloc(cpus, sizeof(pmAtomValue*));
+ for (unsigned int i = 0; i < cpus; i++)
+ this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
+ this->values = xCalloc(cpus, sizeof(pmAtomValue));
+}
+
+static void PCPMachine_updateMemoryInfo(Machine* host) {
+ 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;
+ 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;
+ 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;
+ if (Metric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL)
+ host->availableMem = MINIMUM(value.ull, host->totalMem);
+ else
+ host->availableMem = 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;
+ if (Metric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL)
+ host->cachedSwap = value.ull;
+ host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap;
+}
+
+/* make copies of previously sampled values to avoid overwrite */
+static inline void PCPMachine_backupCPUTime(pmAtomValue* values) {
+ /* the PERIOD fields (must) mirror the TIME fields */
+ for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) {
+ values[metric + CPU_TOTAL_PERIOD] = values[metric];
+ }
+}
+
+static inline void PCPMachine_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) {
+ pmAtomValue* value;
+
+ /* new value for period */
+ value = &values[previous];
+ if (latest->ull > value->ull)
+ value->ull = latest->ull - value->ull;
+ else
+ value->ull = 0;
+
+ /* new value for time */
+ value = &values[previous - CPU_TOTAL_PERIOD];
+ value->ull = latest->ull;
+}
+
+/* using copied sampled values and new values, calculate derivations */
+static void PCPMachine_deriveCPUTime(pmAtomValue* values) {
+
+ pmAtomValue* usertime = &values[CPU_USER_TIME];
+ pmAtomValue* guesttime = &values[CPU_GUEST_TIME];
+ usertime->ull -= guesttime->ull;
+
+ pmAtomValue* nicetime = &values[CPU_NICE_TIME];
+ pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME];
+ nicetime->ull -= guestnicetime->ull;
+
+ pmAtomValue* idletime = &values[CPU_IDLE_TIME];
+ pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME];
+ pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME];
+ idlealltime->ull = idletime->ull + iowaittime->ull;
+
+ pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME];
+ pmAtomValue* irqtime = &values[CPU_IRQ_TIME];
+ pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME];
+ pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME];
+ systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull;
+
+ pmAtomValue* virtalltime = &values[CPU_GUEST_TIME];
+ virtalltime->ull = guesttime->ull + guestnicetime->ull;
+
+ pmAtomValue* stealtime = &values[CPU_STEAL_TIME];
+ pmAtomValue* totaltime = &values[CPU_TOTAL_TIME];
+ totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull +
+ idlealltime->ull + stealtime->ull + virtalltime->ull;
+
+ PCPMachine_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime);
+ PCPMachine_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime);
+}
+
+static void PCPMachine_updateAllCPUTime(PCPMachine* this, Metric metric, CPUMetric cpumetric)
+{
+ pmAtomValue* value = &this->cpu[cpumetric];
+ if (Metric_values(metric, value, 1, PM_TYPE_U64) == NULL)
+ memset(value, 0, sizeof(pmAtomValue));
+}
+
+static void PCPMachine_updatePerCPUTime(PCPMachine* this, Metric metric, CPUMetric cpumetric)
+{
+ int cpus = this->super.existingCPUs;
+ if (Metric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL)
+ memset(this->values, 0, cpus * sizeof(pmAtomValue));
+ for (int i = 0; i < cpus; i++)
+ this->percpu[i][cpumetric].ull = this->values[i].ull;
+}
+
+static void PCPMachine_updatePerCPUReal(PCPMachine* this, Metric metric, CPUMetric cpumetric)
+{
+ int cpus = this->super.existingCPUs;
+ if (Metric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL)
+ memset(this->values, 0, cpus * sizeof(pmAtomValue));
+ for (int i = 0; i < cpus; i++)
+ this->percpu[i][cpumetric].d = this->values[i].d;
+}
+
+static inline void PCPMachine_scanZswapInfo(PCPMachine* this) {
+ pmAtomValue value;
+
+ memset(&this->zswap, 0, sizeof(ZswapStats));
+ if (Metric_values(PCP_MEM_ZSWAP, &value, 1, PM_TYPE_U64))
+ this->zswap.usedZswapComp = value.ull;
+ if (Metric_values(PCP_MEM_ZSWAPPED, &value, 1, PM_TYPE_U64))
+ this->zswap.usedZswapOrig = value.ull;
+}
+
+static inline void PCPMachine_scanZfsArcstats(PCPMachine* this) {
+ unsigned long long int dbufSize = 0;
+ unsigned long long int dnodeSize = 0;
+ unsigned long long int bonusSize = 0;
+ pmAtomValue value;
+
+ memset(&this->zfs, 0, sizeof(ZfsArcStats));
+ if (Metric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
+ this->zfs.anon = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64))
+ this->zfs.min = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64))
+ this->zfs.max = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))
+ bonusSize = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64))
+ dbufSize = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64))
+ dnodeSize = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
+ this->zfs.compressed = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
+ this->zfs.uncompressed = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64))
+ this->zfs.header = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64))
+ this->zfs.MFU = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64))
+ this->zfs.MRU = value.ull / ONE_K;
+ if (Metric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64))
+ this->zfs.size = value.ull / ONE_K;
+
+ this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K;
+ this->zfs.enabled = (this->zfs.size > 0);
+ this->zfs.isCompressed = (this->zfs.compressed > 0);
+}
+
+static void PCPMachine_scan(PCPMachine* this) {
+ Machine* super = &this->super;
+
+ PCPMachine_updateMemoryInfo(super);
+ PCPMachine_updateCPUcount(this);
+
+ PCPMachine_backupCPUTime(this->cpu);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME);
+ PCPMachine_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME);
+ PCPMachine_deriveCPUTime(this->cpu);
+
+ for (unsigned int i = 0; i < super->existingCPUs; i++)
+ PCPMachine_backupCPUTime(this->percpu[i]);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME);
+ PCPMachine_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME);
+ for (unsigned int i = 0; i < super->existingCPUs; i++)
+ PCPMachine_deriveCPUTime(this->percpu[i]);
+
+ if (super->settings->showCPUFrequency)
+ PCPMachine_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY);
+
+ PCPMachine_scanZfsArcstats(this);
+ PCPMachine_scanZswapInfo(this);
+}
+
+void Machine_scan(Machine* super) {
+ PCPMachine* host = (PCPMachine*) super;
+ const Settings* settings = super->settings;
+ uint32_t flags = settings->ss->flags;
+ bool flagged;
+
+ for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++)
+ Metric_enable(metric, true);
+
+ flagged = settings->showCPUFrequency;
+ Metric_enable(PCP_HINV_CPUCLOCK, flagged);
+ flagged = flags & PROCESS_FLAG_LINUX_CGROUP;
+ Metric_enable(PCP_PROC_CGROUPS, flagged);
+ flagged = flags & PROCESS_FLAG_LINUX_OOM;
+ Metric_enable(PCP_PROC_OOMSCORE, flagged);
+ flagged = flags & PROCESS_FLAG_LINUX_CTXT;
+ Metric_enable(PCP_PROC_VCTXSW, flagged);
+ Metric_enable(PCP_PROC_NVCTXSW, flagged);
+ flagged = flags & PROCESS_FLAG_LINUX_SECATTR;
+ Metric_enable(PCP_PROC_LABELS, flagged);
+ flagged = flags & PROCESS_FLAG_LINUX_AUTOGROUP;
+ Metric_enable(PCP_PROC_AUTOGROUP_ID, flagged);
+ Metric_enable(PCP_PROC_AUTOGROUP_NICE, flagged);
+
+ /* Sample smaps metrics on every second pass to improve performance */
+ host->smaps_flag = !!host->smaps_flag;
+ Metric_enable(PCP_PROC_SMAPS_PSS, host->smaps_flag);
+ Metric_enable(PCP_PROC_SMAPS_SWAP, host->smaps_flag);
+ Metric_enable(PCP_PROC_SMAPS_SWAPPSS, host->smaps_flag);
+
+ struct timeval timestamp;
+ if (Metric_fetch(&timestamp) != true)
+ return;
+
+ double sample = host->timestamp;
+ host->timestamp = pmtimevalToReal(&timestamp);
+ host->period = (host->timestamp - sample) * 100;
+
+ PCPMachine_scan(host);
+}
+
+Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
+ PCPMachine* this = xCalloc(1, sizeof(PCPMachine));
+ Machine* super = &this->super;
+
+ Machine_init(super, usersTable, userId);
+
+ struct timeval timestamp;
+ gettimeofday(&timestamp, NULL);
+ this->timestamp = pmtimevalToReal(&timestamp);
+
+ this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
+ PCPMachine_updateCPUcount(this);
+
+ Platform_updateTables(super);
+
+ return super;
+}
+
+void Machine_delete(Machine* super) {
+ PCPMachine* this = (PCPMachine*) super;
+ Machine_done(super);
+ free(this->values);
+ for (unsigned int i = 0; i < super->existingCPUs; i++)
+ free(this->percpu[i]);
+ free(this->percpu);
+ free(this->cpu);
+ free(this);
+}
+
+bool Machine_isCPUonline(const Machine* host, unsigned int id) {
+ assert(id < host->existingCPUs);
+ (void) host;
+
+ pmAtomValue value;
+ if (Metric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32))
+ return true;
+ return false;
+}
diff --git a/pcp/PCPProcessList.h b/pcp/PCPMachine.h
index a3a7372..6518bd4 100644
--- a/pcp/PCPProcessList.h
+++ b/pcp/PCPMachine.h
@@ -1,22 +1,21 @@
-#ifndef HEADER_PCPProcessList
-#define HEADER_PCPProcessList
+#ifndef HEADER_PCPMachine
+#define HEADER_PCPMachine
/*
-htop - PCPProcessList.h
+htop - PCPMachine.h
(C) 2014 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h" // IWYU pragma: keep
-
#include <stdbool.h>
#include <sys/types.h>
#include "Hashtable.h"
-#include "ProcessList.h"
+#include "Machine.h"
#include "UsersTable.h"
#include "pcp/Platform.h"
+#include "linux/ZswapStats.h"
#include "zfs/ZfsArcStats.h"
@@ -54,21 +53,19 @@ typedef enum CPUMetric_ {
CPU_METRIC_COUNT
} CPUMetric;
-typedef struct PCPProcessList_ {
- ProcessList super;
+typedef struct PCPMachine_ {
+ Machine super;
+ int smaps_flag;
+ double period;
double timestamp; /* previous sample timestamp */
+
pmAtomValue* cpu; /* aggregate values for each metric */
pmAtomValue** percpu; /* per-processor values for each metric */
pmAtomValue* values; /* per-processor buffer for just one metric */
- ZfsArcStats zfs;
-} PCPProcessList;
-
-ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
-void ProcessList_delete(ProcessList* pl);
-
-void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
-
-bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id);
+ ZfsArcStats zfs;
+ /*ZramStats zram; -- not needed, calculated in-line in Platform.c */
+ ZswapStats zswap;
+} PCPMachine;
#endif
diff --git a/pcp/PCPProcess.c b/pcp/PCPProcess.c
index b8b87ca..69e2972 100644
--- a/pcp/PCPProcess.c
+++ b/pcp/PCPProcess.c
@@ -7,6 +7,8 @@ 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/PCPProcess.h"
#include <math.h>
@@ -49,6 +51,7 @@ const ProcessFieldData Process_fields[] = {
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[M_SHARE] = { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, .defaultSortDesc = true, },
+ [M_PRIV] = { .name = "M_PRIV", .title = " PRIV ", .description = "The private memory size of the process - resident set size minus shared memory", .flags = 0, .defaultSortDesc = true, },
[M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, .defaultSortDesc = true, },
[M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, },
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, },
@@ -71,7 +74,9 @@ const ProcessFieldData Process_fields[] = {
[IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
- [CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, },
+ [CGROUP] = { .name = "CGROUP", .title = "CGROUP (raw) ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, },
+ [CCGROUP] = { .name = "CCGROUP", .title = "CGROUP (compressed) ", .description = "Which cgroup the process is in (condensed to essentials)", .flags = PROCESS_FLAG_LINUX_CGROUP, },
+ [CONTAINER] = { .name = "CONTAINER", .title = "CONTAINER ", .description = "Name of the container the process is in (guessed by heuristics)", .flags = PROCESS_FLAG_LINUX_CGROUP, },
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, },
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, .defaultSortDesc = true, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = " IOD% ", .description = "Block I/O delay %", .flags = 0, .defaultSortDesc = true, },
@@ -88,73 +93,80 @@ const ProcessFieldData Process_fields[] = {
[AUTOGROUP_NICE] = { .name = "AUTOGROUP_NICE", .title = " ANI", .description = "Nice value (the higher the value, the more other processes take priority) associated with the process autogroup", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, },
};
-Process* PCPProcess_new(const Settings* settings) {
+Process* PCPProcess_new(const Machine* host) {
PCPProcess* this = xCalloc(1, sizeof(PCPProcess));
Object_setClass(this, Class(PCPProcess));
- Process_init(&this->super, settings);
+ Process_init(&this->super, host);
return &this->super;
}
void Process_delete(Object* cast) {
PCPProcess* this = (PCPProcess*) cast;
Process_done((Process*)cast);
+ free(this->cgroup_short);
free(this->cgroup);
free(this->secattr);
free(this);
}
-static void PCPProcess_printDelay(float delay_percent, char* buffer, int n) {
- if (isnan(delay_percent)) {
- xSnprintf(buffer, n, " N/A ");
- } else {
+static void PCPProcess_printDelay(float delay_percent, char* buffer, size_t n) {
+ if (isNonnegative(delay_percent)) {
xSnprintf(buffer, n, "%4.1f ", delay_percent);
+ } else {
+ xSnprintf(buffer, n, " N/A ");
+ }
+}
+
+static double PCPProcess_totalIORate(const PCPProcess* pp) {
+ double totalRate = NAN;
+ if (isNonnegative(pp->io_rate_read_bps)) {
+ totalRate = pp->io_rate_read_bps;
+ if (isNonnegative(pp->io_rate_write_bps)) {
+ totalRate += pp->io_rate_write_bps;
+ }
+ } else if (isNonnegative(pp->io_rate_write_bps)) {
+ totalRate = pp->io_rate_write_bps;
}
+ return totalRate;
}
-static void PCPProcess_writeField(const Process* this, RichString* str, ProcessField field) {
- const PCPProcess* pp = (const PCPProcess*) this;
- bool coloring = this->settings->highlightMegabytes;
+static void PCPProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
+ const PCPProcess* pp = (const PCPProcess*) super;
+
+ bool coloring = super->host->settings->highlightMegabytes;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
- int n = sizeof(buffer) - 1;
+ size_t n = sizeof(buffer) - 1;
+
switch ((int)field) {
- case CMINFLT: Process_printCount(str, pp->cminflt, coloring); return;
- case CMAJFLT: Process_printCount(str, pp->cmajflt, coloring); return;
- case M_DRS: Process_printBytes(str, pp->m_drs, coloring); return;
- case M_DT: Process_printBytes(str, pp->m_dt, coloring); return;
- case M_LRS: Process_printBytes(str, pp->m_lrs, coloring); return;
- case M_TRS: Process_printBytes(str, pp->m_trs, coloring); return;
- case M_SHARE: Process_printBytes(str, pp->m_share, coloring); return;
- case M_PSS: Process_printKBytes(str, pp->m_pss, coloring); return;
- case M_SWAP: Process_printKBytes(str, pp->m_swap, coloring); return;
- case M_PSSWP: Process_printKBytes(str, pp->m_psswp, coloring); return;
- case UTIME: Process_printTime(str, pp->utime, coloring); return;
- case STIME: Process_printTime(str, pp->stime, coloring); return;
- case CUTIME: Process_printTime(str, pp->cutime, coloring); return;
- case CSTIME: Process_printTime(str, pp->cstime, coloring); return;
- case RCHAR: Process_printBytes(str, pp->io_rchar, coloring); return;
- case WCHAR: Process_printBytes(str, pp->io_wchar, coloring); return;
- case SYSCR: Process_printCount(str, pp->io_syscr, coloring); return;
- case SYSCW: Process_printCount(str, pp->io_syscw, coloring); return;
- case RBYTES: Process_printBytes(str, pp->io_read_bytes, coloring); return;
- case WBYTES: Process_printBytes(str, pp->io_write_bytes, coloring); return;
- case CNCLWB: Process_printBytes(str, pp->io_cancelled_write_bytes, coloring); return;
- case IO_READ_RATE: Process_printRate(str, pp->io_rate_read_bps, coloring); return;
- case IO_WRITE_RATE: Process_printRate(str, pp->io_rate_write_bps, coloring); return;
- case IO_RATE: {
- double totalRate = NAN;
- if (!isnan(pp->io_rate_read_bps) && !isnan(pp->io_rate_write_bps))
- totalRate = pp->io_rate_read_bps + pp->io_rate_write_bps;
- else if (!isnan(pp->io_rate_read_bps))
- totalRate = pp->io_rate_read_bps;
- else if (!isnan(pp->io_rate_write_bps))
- totalRate = pp->io_rate_write_bps;
- else
- totalRate = NAN;
- Process_printRate(str, totalRate, coloring);
- return;
- }
- case CGROUP: xSnprintf(buffer, n, "%-10s ", pp->cgroup ? pp->cgroup : ""); break;
+ case CMINFLT: Row_printCount(str, pp->cminflt, coloring); return;
+ case CMAJFLT: Row_printCount(str, pp->cmajflt, coloring); return;
+ case M_DRS: Row_printBytes(str, pp->m_drs, coloring); return;
+ case M_DT: Row_printBytes(str, pp->m_dt, coloring); return;
+ case M_LRS: Row_printBytes(str, pp->m_lrs, coloring); return;
+ case M_TRS: Row_printBytes(str, pp->m_trs, coloring); return;
+ case M_SHARE: Row_printBytes(str, pp->m_share, coloring); return;
+ case M_PRIV: Row_printBytes(str, pp->m_priv, coloring); return;
+ case M_PSS: Row_printKBytes(str, pp->m_pss, coloring); return;
+ case M_SWAP: Row_printKBytes(str, pp->m_swap, coloring); return;
+ case M_PSSWP: Row_printKBytes(str, pp->m_psswp, coloring); return;
+ case UTIME: Row_printTime(str, pp->utime, coloring); return;
+ case STIME: Row_printTime(str, pp->stime, coloring); return;
+ case CUTIME: Row_printTime(str, pp->cutime, coloring); return;
+ case CSTIME: Row_printTime(str, pp->cstime, coloring); return;
+ case RCHAR: Row_printBytes(str, pp->io_rchar, coloring); return;
+ case WCHAR: Row_printBytes(str, pp->io_wchar, coloring); return;
+ case SYSCR: Row_printCount(str, pp->io_syscr, coloring); return;
+ case SYSCW: Row_printCount(str, pp->io_syscw, coloring); return;
+ case RBYTES: Row_printBytes(str, pp->io_read_bytes, coloring); return;
+ case WBYTES: Row_printBytes(str, pp->io_write_bytes, coloring); return;
+ case CNCLWB: Row_printBytes(str, pp->io_cancelled_write_bytes, coloring); return;
+ case IO_READ_RATE: Row_printRate(str, pp->io_rate_read_bps, coloring); return;
+ case IO_WRITE_RATE: Row_printRate(str, pp->io_rate_write_bps, coloring); return;
+ case IO_RATE: Row_printRate(str, PCPProcess_totalIORate(pp), coloring); return;
+ case CGROUP: xSnprintf(buffer, n, "%-35.35s ", pp->cgroup ? pp->cgroup : "N/A"); break;
+ case CCGROUP: xSnprintf(buffer, n, "%-35.35s ", pp->cgroup_short ? pp->cgroup_short : (pp->cgroup ? pp->cgroup : "N/A")); break;
+ case CONTAINER: xSnprintf(buffer, n, "%-35.35s ", pp->container_short ? pp->container_short : "N/A"); break;
case OOM: xSnprintf(buffer, n, "%4u ", pp->oom); break;
case PERCENT_CPU_DELAY:
PCPProcess_printDelay(pp->cpu_delay_percent, buffer, n);
@@ -192,17 +204,11 @@ static void PCPProcess_writeField(const Process* this, RichString* str, ProcessF
}
break;
default:
- Process_writeField(this, str, field);
+ Process_writeField(&pp->super, str, field);
return;
}
- RichString_appendWide(str, attr, buffer);
-}
-static double adjustNaN(double num) {
- if (isnan(num))
- return -0.0005;
-
- return num;
+ RichString_appendWide(str, attr, buffer);
}
static int PCPProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
@@ -220,6 +226,8 @@ static int PCPProcess_compareByKey(const Process* v1, const Process* v2, Process
return SPACESHIP_NUMBER(p1->m_trs, p2->m_trs);
case M_SHARE:
return SPACESHIP_NUMBER(p1->m_share, p2->m_share);
+ case M_PRIV:
+ return SPACESHIP_NUMBER(p1->m_priv, p2->m_priv);
case M_PSS:
return SPACESHIP_NUMBER(p1->m_pss, p2->m_pss);
case M_SWAP:
@@ -249,21 +257,25 @@ static int PCPProcess_compareByKey(const Process* v1, const Process* v2, Process
case CNCLWB:
return SPACESHIP_NUMBER(p1->io_cancelled_write_bytes, p2->io_cancelled_write_bytes);
case IO_READ_RATE:
- return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_read_bps), adjustNaN(p2->io_rate_read_bps));
+ return compareRealNumbers(p1->io_rate_read_bps, p2->io_rate_read_bps);
case IO_WRITE_RATE:
- return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_write_bps), adjustNaN(p2->io_rate_write_bps));
+ return compareRealNumbers(p1->io_rate_write_bps, p2->io_rate_write_bps);
case IO_RATE:
- return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_read_bps) + adjustNaN(p1->io_rate_write_bps), adjustNaN(p2->io_rate_read_bps) + adjustNaN(p2->io_rate_write_bps));
+ return compareRealNumbers(PCPProcess_totalIORate(p1), PCPProcess_totalIORate(p2));
case CGROUP:
return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup);
+ case CCGROUP:
+ return SPACESHIP_NULLSTR(p1->cgroup_short, p2->cgroup_short);
+ case CONTAINER:
+ return SPACESHIP_NULLSTR(p1->container_short, p2->container_short);
case OOM:
return SPACESHIP_NUMBER(p1->oom, p2->oom);
case PERCENT_CPU_DELAY:
- return SPACESHIP_NUMBER(p1->cpu_delay_percent, p2->cpu_delay_percent);
+ return compareRealNumbers(p1->cpu_delay_percent, p2->cpu_delay_percent);
case PERCENT_IO_DELAY:
- return SPACESHIP_NUMBER(p1->blkio_delay_percent, p2->blkio_delay_percent);
+ return compareRealNumbers(p1->blkio_delay_percent, p2->blkio_delay_percent);
case PERCENT_SWAP_DELAY:
- return SPACESHIP_NUMBER(p1->swapin_delay_percent, p2->swapin_delay_percent);
+ return compareRealNumbers(p1->swapin_delay_percent, p2->swapin_delay_percent);
case CTXT:
return SPACESHIP_NUMBER(p1->ctxt_diff, p2->ctxt_diff);
case SECATTR:
@@ -281,11 +293,18 @@ static int PCPProcess_compareByKey(const Process* v1, const Process* v2, Process
const ProcessClass PCPProcess_class = {
.super = {
- .extends = Class(Process),
- .display = Process_display,
- .delete = Process_delete,
- .compare = Process_compare
+ .super = {
+ .extends = Class(Process),
+ .display = Row_display,
+ .delete = Process_delete,
+ .compare = Process_compare
+ },
+ .isHighlighted = Process_rowIsHighlighted,
+ .isVisible = Process_rowIsVisible,
+ .matchesFilter = Process_rowMatchesFilter,
+ .compareByParent = Process_compareByParent,
+ .sortKeyString = Process_rowGetSortKey,
+ .writeField = PCPProcess_rowWriteField,
},
- .writeField = PCPProcess_writeField,
- .compareByKey = PCPProcess_compareByKey
+ .compareByKey = PCPProcess_compareByKey,
};
diff --git a/pcp/PCPProcess.h b/pcp/PCPProcess.h
index 46ba07f..e953757 100644
--- a/pcp/PCPProcess.h
+++ b/pcp/PCPProcess.h
@@ -9,13 +9,11 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h" // IWYU pragma: keep
-
#include <stdbool.h>
+#include "Machine.h"
#include "Object.h"
#include "Process.h"
-#include "Settings.h"
#define PROCESS_FLAG_LINUX_CGROUP 0x00000800
@@ -38,6 +36,7 @@ typedef struct PCPProcess_ {
unsigned long long int cutime;
unsigned long long int cstime;
long m_share;
+ long m_priv;
long m_pss;
long m_swap;
long m_psswp;
@@ -73,6 +72,8 @@ typedef struct PCPProcess_ {
double io_rate_read_bps;
double io_rate_write_bps;
char* cgroup;
+ char* cgroup_short;
+ char* container_short;
long int autogroup_id;
int autogroup_nice;
unsigned int oom;
@@ -93,7 +94,7 @@ extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern const ProcessClass PCPProcess_class;
-Process* PCPProcess_new(const Settings* settings);
+Process* PCPProcess_new(const Machine* host);
void Process_delete(Object* cast);
diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c
deleted file mode 100644
index 045f7ae..0000000
--- a/pcp/PCPProcessList.c
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
-htop - PCPProcessList.c
-(C) 2014 Hisham H. Muhammad
-(C) 2020-2021 htop dev team
-(C) 2020-2021 Red Hat, Inc.
-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/PCPProcessList.h"
-
-#include <assert.h>
-#include <limits.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include "Macros.h"
-#include "Object.h"
-#include "Platform.h"
-#include "Process.h"
-#include "Settings.h"
-#include "XUtils.h"
-
-#include "pcp/PCPMetric.h"
-#include "pcp/PCPProcess.h"
-
-
-static void PCPProcessList_updateCPUcount(PCPProcessList* this) {
- ProcessList* pl = &(this->super);
- pl->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM);
- unsigned int cpus = Platform_getMaxCPU();
- if (cpus == pl->existingCPUs)
- return;
- if (cpus == 0)
- cpus = pl->activeCPUs;
- if (cpus <= 1)
- cpus = pl->activeCPUs = 1;
- pl->existingCPUs = cpus;
-
- free(this->percpu);
- free(this->values);
-
- this->percpu = xCalloc(cpus, sizeof(pmAtomValue *));
- for (unsigned int i = 0; i < cpus; i++)
- this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
- this->values = xCalloc(cpus, sizeof(pmAtomValue));
-}
-
-static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) {
- char* name = Hashtable_get(this->users, uid);
- if (name)
- return name;
-
- pmAtomValue value;
- if (PCPMetric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) {
- Hashtable_put(this->users, uid, value.cp);
- name = value.cp;
- }
- return name;
-}
-
-ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
- PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList));
- ProcessList* super = &(this->super);
-
- ProcessList_init(super, Class(PCPProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId);
-
- struct timeval timestamp;
- gettimeofday(&timestamp, NULL);
- this->timestamp = pmtimevalToReal(&timestamp);
-
- this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
- PCPProcessList_updateCPUcount(this);
-
- return super;
-}
-
-void ProcessList_delete(ProcessList* pl) {
- PCPProcessList* this = (PCPProcessList*) pl;
- ProcessList_done(pl);
- free(this->values);
- for (unsigned int i = 0; i < pl->existingCPUs; i++)
- free(this->percpu[i]);
- free(this->percpu);
- free(this->cpu);
- free(this);
-}
-
-static inline long Metric_instance_s32(int metric, int pid, int offset, long fallback) {
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_32))
- return value.l;
- return fallback;
-}
-
-static inline long long Metric_instance_s64(int metric, int pid, int offset, long long fallback) {
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_64))
- return value.l;
- return fallback;
-}
-
-static inline unsigned long Metric_instance_u32(int metric, int pid, int offset, unsigned long fallback) {
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U32))
- return value.ul;
- return fallback;
-}
-
-static inline unsigned long long Metric_instance_u64(int metric, int pid, int offset, unsigned long long fallback) {
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U64))
- return value.ull;
- return fallback;
-}
-
-static inline unsigned long long Metric_instance_time(int metric, int pid, int offset) {
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U64))
- return value.ull / 10;
- return 0;
-}
-
-static inline unsigned long long Metric_instance_ONE_K(int metric, int pid, int offset) {
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U64))
- return value.ull / ONE_K;
- return ULLONG_MAX;
-}
-
-static inline char Metric_instance_char(int metric, int pid, int offset, char fallback) {
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_STRING)) {
- char uchar = value.cp[0];
- free(value.cp);
- return uchar;
- }
- return fallback;
-}
-
-static inline ProcessState PCPProcessList_getProcessState(char state) {
- switch (state) {
- case '?': return UNKNOWN;
- case 'R': return RUNNING;
- case 'W': return WAITING;
- case 'D': return UNINTERRUPTIBLE_WAIT;
- case 'P': return PAGING;
- case 'T': return STOPPED;
- case 't': return TRACED;
- case 'Z': return ZOMBIE;
- case 'X': return DEFUNCT;
- case 'I': return IDLE;
- case 'S': return SLEEPING;
- default: return UNKNOWN;
- }
-}
-
-static void PCPProcessList_updateID(Process* process, int pid, int offset) {
- process->tgid = Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1);
- process->ppid = Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1);
- process->state = PCPProcessList_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?'));
-}
-
-static void PCPProcessList_updateInfo(Process* process, int pid, int offset, char* command, size_t commLen) {
- PCPProcess* pp = (PCPProcess*) process;
- pmAtomValue value;
-
- if (!PCPMetric_instance(PCP_PROC_CMD, pid, offset, &value, PM_TYPE_STRING))
- value.cp = xStrdup("<unknown>");
- String_safeStrncpy(command, value.cp, commLen);
- free(value.cp);
-
- process->pgrp = Metric_instance_u32(PCP_PROC_PGRP, pid, offset, 0);
- process->session = Metric_instance_u32(PCP_PROC_SESSION, pid, offset, 0);
- process->tty_nr = Metric_instance_u32(PCP_PROC_TTY, pid, offset, 0);
- process->tpgid = Metric_instance_u32(PCP_PROC_TTYPGRP, pid, offset, 0);
- process->minflt = Metric_instance_u32(PCP_PROC_MINFLT, pid, offset, 0);
- pp->cminflt = Metric_instance_u32(PCP_PROC_CMINFLT, pid, offset, 0);
- process->majflt = Metric_instance_u32(PCP_PROC_MAJFLT, pid, offset, 0);
- pp->cmajflt = Metric_instance_u32(PCP_PROC_CMAJFLT, pid, offset, 0);
- pp->utime = Metric_instance_time(PCP_PROC_UTIME, pid, offset);
- pp->stime = Metric_instance_time(PCP_PROC_STIME, pid, offset);
- pp->cutime = Metric_instance_time(PCP_PROC_CUTIME, pid, offset);
- pp->cstime = Metric_instance_time(PCP_PROC_CSTIME, pid, offset);
- process->priority = Metric_instance_u32(PCP_PROC_PRIORITY, pid, offset, 0);
- process->nice = Metric_instance_s32(PCP_PROC_NICE, pid, offset, 0);
- process->nlwp = Metric_instance_u32(PCP_PROC_THREADS, pid, offset, 0);
- process->starttime_ctime = Metric_instance_time(PCP_PROC_STARTTIME, pid, offset);
- process->processor = Metric_instance_u32(PCP_PROC_PROCESSOR, pid, offset, 0);
-
- process->time = pp->utime + pp->stime;
-}
-
-static void PCPProcessList_updateIO(PCPProcess* pp, int pid, int offset, unsigned long long now) {
- pmAtomValue value;
-
- pp->io_rchar = Metric_instance_ONE_K(PCP_PROC_IO_RCHAR, pid, offset);
- pp->io_wchar = Metric_instance_ONE_K(PCP_PROC_IO_WCHAR, pid, offset);
- pp->io_syscr = Metric_instance_u64(PCP_PROC_IO_SYSCR, pid, offset, ULLONG_MAX);
- pp->io_syscw = Metric_instance_u64(PCP_PROC_IO_SYSCW, pid, offset, ULLONG_MAX);
- pp->io_cancelled_write_bytes = Metric_instance_ONE_K(PCP_PROC_IO_CANCELLED, pid, offset);
-
- if (PCPMetric_instance(PCP_PROC_IO_READB, pid, offset, &value, PM_TYPE_U64)) {
- unsigned long long last_read = pp->io_read_bytes;
- pp->io_read_bytes = value.ull / ONE_K;
- pp->io_rate_read_bps = ONE_K * (pp->io_read_bytes - last_read) /
- (now - pp->io_last_scan_time);
- } else {
- pp->io_read_bytes = ULLONG_MAX;
- pp->io_rate_read_bps = NAN;
- }
-
- if (PCPMetric_instance(PCP_PROC_IO_WRITEB, pid, offset, &value, PM_TYPE_U64)) {
- unsigned long long last_write = pp->io_write_bytes;
- pp->io_write_bytes = value.ull;
- pp->io_rate_write_bps = ONE_K * (pp->io_write_bytes - last_write) /
- (now - pp->io_last_scan_time);
- } else {
- pp->io_write_bytes = ULLONG_MAX;
- pp->io_rate_write_bps = NAN;
- }
-
- pp->io_last_scan_time = now;
-}
-
-static void PCPProcessList_updateMemory(PCPProcess* pp, int pid, int offset) {
- pp->super.m_virt = Metric_instance_u32(PCP_PROC_MEM_SIZE, pid, offset, 0);
- pp->super.m_resident = Metric_instance_u32(PCP_PROC_MEM_RSS, pid, offset, 0);
- pp->m_share = Metric_instance_u32(PCP_PROC_MEM_SHARE, pid, offset, 0);
- pp->m_trs = Metric_instance_u32(PCP_PROC_MEM_TEXTRS, pid, offset, 0);
- pp->m_lrs = Metric_instance_u32(PCP_PROC_MEM_LIBRS, pid, offset, 0);
- pp->m_drs = Metric_instance_u32(PCP_PROC_MEM_DATRS, pid, offset, 0);
- pp->m_dt = Metric_instance_u32(PCP_PROC_MEM_DIRTY, pid, offset, 0);
-}
-
-static void PCPProcessList_updateSmaps(PCPProcess* pp, pid_t pid, int offset) {
- pp->m_pss = Metric_instance_u64(PCP_PROC_SMAPS_PSS, pid, offset, 0);
- pp->m_swap = Metric_instance_u64(PCP_PROC_SMAPS_SWAP, pid, offset, 0);
- pp->m_psswp = Metric_instance_u64(PCP_PROC_SMAPS_SWAPPSS, pid, offset, 0);
-}
-
-static void PCPProcessList_readOomData(PCPProcess* pp, int pid, int offset) {
- pp->oom = Metric_instance_u32(PCP_PROC_OOMSCORE, pid, offset, 0);
-}
-
-static void PCPProcessList_readAutogroup(PCPProcess* pp, int pid, int offset) {
- pp->autogroup_id = Metric_instance_s64(PCP_PROC_AUTOGROUP_ID, pid, offset, -1);
- pp->autogroup_nice = Metric_instance_s32(PCP_PROC_AUTOGROUP_NICE, pid, offset, 0);
-}
-
-static void PCPProcessList_readCtxtData(PCPProcess* pp, int pid, int offset) {
- pmAtomValue value;
- unsigned long ctxt = 0;
-
- if (PCPMetric_instance(PCP_PROC_VCTXSW, pid, offset, &value, PM_TYPE_U32))
- ctxt += value.ul;
- if (PCPMetric_instance(PCP_PROC_NVCTXSW, pid, offset, &value, PM_TYPE_U32))
- ctxt += value.ul;
-
- pp->ctxt_diff = ctxt > pp->ctxt_total ? ctxt - pp->ctxt_total : 0;
- pp->ctxt_total = ctxt;
-}
-
-static char* setString(PCPMetric metric, int pid, int offset, char* string) {
- if (string)
- free(string);
- pmAtomValue value;
- if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_STRING))
- string = value.cp;
- else
- string = NULL;
- return string;
-}
-
-static void PCPProcessList_updateTTY(Process* process, int pid, int offset) {
- process->tty_name = setString(PCP_PROC_TTYNAME, pid, offset, process->tty_name);
-}
-
-static void PCPProcessList_readCGroups(PCPProcess* pp, int pid, int offset) {
- pp->cgroup = setString(PCP_PROC_CGROUPS, pid, offset, pp->cgroup);
-}
-
-static void PCPProcessList_readSecattrData(PCPProcess* pp, int pid, int offset) {
- pp->secattr = setString(PCP_PROC_LABELS, pid, offset, pp->secattr);
-}
-
-static void PCPProcessList_readCwd(PCPProcess* pp, int pid, int offset) {
- pp->super.procCwd = setString(PCP_PROC_CWD, pid, offset, pp->super.procCwd);
-}
-
-static void PCPProcessList_updateUsername(Process* process, int pid, int offset, UsersTable* users) {
- process->st_uid = Metric_instance_u32(PCP_PROC_ID_UID, pid, offset, 0);
- process->user = setUser(users, process->st_uid, pid, offset);
-}
-
-static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, const char* comm) {
- pmAtomValue value;
- if (!PCPMetric_instance(PCP_PROC_PSARGS, pid, offset, &value, PM_TYPE_STRING)) {
- if (process->state != ZOMBIE)
- process->isKernelThread = true;
- Process_updateCmdline(process, NULL, 0, 0);
- return;
- }
-
- char* command = value.cp;
- int length = strlen(command);
- if (command[0] != '(') {
- process->isKernelThread = false;
- } else {
- ++command;
- --length;
- if (command[length - 1] == ')')
- command[--length] = '\0';
- process->isKernelThread = true;
- }
-
- int tokenStart = 0;
- for (int i = 0; i < length; i++) {
- /* htop considers the next character after the last / that is before
- * basenameOffset, as the start of the basename in cmdline - see
- * Process_writeCommand */
- if (command[i] == '/')
- tokenStart = i + 1;
- }
- int tokenEnd = length;
-
- Process_updateCmdline(process, command, tokenStart, tokenEnd);
- free(value.cp);
-
- Process_updateComm(process, comm);
-
- if (PCPMetric_instance(PCP_PROC_EXE, pid, offset, &value, PM_TYPE_STRING)) {
- Process_updateExe(process, value.cp[0] ? value.cp : NULL);
- free(value.cp);
- }
-}
-
-static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, struct timeval* tv) {
- ProcessList* pl = (ProcessList*) this;
- const Settings* settings = pl->settings;
-
- bool hideKernelThreads = settings->hideKernelThreads;
- bool hideUserlandThreads = settings->hideUserlandThreads;
-
- unsigned long long now = tv->tv_sec * 1000LL + tv->tv_usec / 1000LL;
- int pid = -1, offset = -1;
-
- /* for every process ... */
- while (PCPMetric_iterate(PCP_PROC_PID, &pid, &offset)) {
-
- bool preExisting;
- Process* proc = ProcessList_getProcess(pl, pid, &preExisting, PCPProcess_new);
- PCPProcess* pp = (PCPProcess*) proc;
- PCPProcessList_updateID(proc, pid, offset);
- proc->isUserlandThread = proc->pid != proc->tgid;
- pp->offset = offset >= 0 ? offset : 0;
-
- /*
- * These conditions will not trigger on first occurrence, cause we need to
- * add the process to the ProcessList and do all one time scans
- * (e.g. parsing the cmdline to detect a kernel thread)
- * But it will short-circuit subsequent scans.
- */
- if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) {
- proc->updated = true;
- proc->show = false;
- if (proc->state == RUNNING)
- pl->runningTasks++;
- pl->kernelThreads++;
- pl->totalTasks++;
- continue;
- }
- if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
- proc->updated = true;
- proc->show = false;
- if (proc->state == RUNNING)
- pl->runningTasks++;
- pl->userlandThreads++;
- pl->totalTasks++;
- continue;
- }
-
- if (settings->ss->flags & PROCESS_FLAG_IO)
- PCPProcessList_updateIO(pp, pid, offset, now);
-
- PCPProcessList_updateMemory(pp, pid, offset);
-
- if ((settings->ss->flags & PROCESS_FLAG_LINUX_SMAPS) &&
- (Process_isKernelThread(proc) == false)) {
- if (PCPMetric_enabled(PCP_PROC_SMAPS_PSS))
- PCPProcessList_updateSmaps(pp, pid, offset);
- }
-
- char command[MAX_NAME + 1];
- unsigned int tty_nr = proc->tty_nr;
- unsigned long long int lasttimes = pp->utime + pp->stime;
-
- PCPProcessList_updateInfo(proc, pid, offset, command, sizeof(command));
- proc->starttime_ctime += Platform_getBootTime();
- if (tty_nr != proc->tty_nr)
- PCPProcessList_updateTTY(proc, pid, offset);
-
- float percent_cpu = (pp->utime + pp->stime - lasttimes) / period * 100.0;
- proc->percent_cpu = isnan(percent_cpu) ?
- 0.0 : CLAMP(percent_cpu, 0.0, pl->activeCPUs * 100.0);
- proc->percent_mem = proc->m_resident / (double)pl->totalMem * 100.0;
- Process_updateCPUFieldWidths(proc->percent_cpu);
-
- PCPProcessList_updateUsername(proc, pid, offset, pl->usersTable);
-
- if (!preExisting) {
- PCPProcessList_updateCmdline(proc, pid, offset, command);
- Process_fillStarttimeBuffer(proc);
- ProcessList_add(pl, proc);
- } else if (settings->updateProcessNames && proc->state != ZOMBIE) {
- PCPProcessList_updateCmdline(proc, pid, offset, command);
- }
-
- if (settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP)
- PCPProcessList_readCGroups(pp, pid, offset);
-
- if (settings->ss->flags & PROCESS_FLAG_LINUX_OOM)
- PCPProcessList_readOomData(pp, pid, offset);
-
- if (settings->ss->flags & PROCESS_FLAG_LINUX_CTXT)
- PCPProcessList_readCtxtData(pp, pid, offset);
-
- if (settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR)
- PCPProcessList_readSecattrData(pp, pid, offset);
-
- if (settings->ss->flags & PROCESS_FLAG_CWD)
- PCPProcessList_readCwd(pp, pid, offset);
-
- if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP)
- PCPProcessList_readAutogroup(pp, pid, offset);
-
- if (proc->state == ZOMBIE && !proc->cmdline && command[0]) {
- Process_updateCmdline(proc, command, 0, strlen(command));
- } else if (Process_isThread(proc)) {
- if ((settings->showThreadNames || Process_isKernelThread(proc)) && command[0]) {
- Process_updateCmdline(proc, command, 0, strlen(command));
- }
-
- if (Process_isKernelThread(proc)) {
- pl->kernelThreads++;
- } else {
- pl->userlandThreads++;
- }
- }
-
- /* Set at the end when we know if a new entry is a thread */
- proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) ||
- (hideUserlandThreads && Process_isUserlandThread(proc)));
-
- pl->totalTasks++;
- if (proc->state == RUNNING)
- pl->runningTasks++;
- proc->updated = true;
- }
- return true;
-}
-
-static void PCPProcessList_updateMemoryInfo(ProcessList* super) {
- unsigned long long int freeMem = 0;
- unsigned long long int swapFreeMem = 0;
- unsigned long long int sreclaimableMem = 0;
- super->totalMem = super->usedMem = super->cachedMem = 0;
- super->usedSwap = super->totalSwap = super->sharedMem = 0;
-
- pmAtomValue value;
- if (PCPMetric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL)
- super->totalMem = value.ull;
- if (PCPMetric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL)
- freeMem = value.ull;
- if (PCPMetric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL)
- super->buffersMem = value.ull;
- if (PCPMetric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL)
- sreclaimableMem = value.ull;
- if (PCPMetric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL)
- super->sharedMem = value.ull;
- if (PCPMetric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL)
- super->cachedMem = value.ull + sreclaimableMem - super->sharedMem;
- const memory_t usedDiff = freeMem + super->cachedMem + sreclaimableMem + super->buffersMem;
- super->usedMem = (super->totalMem >= usedDiff) ?
- super->totalMem - usedDiff : super->totalMem - freeMem;
- if (PCPMetric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL)
- super->availableMem = MINIMUM(value.ull, super->totalMem);
- else
- super->availableMem = freeMem;
- if (PCPMetric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL)
- swapFreeMem = value.ull;
- if (PCPMetric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL)
- super->totalSwap = value.ull;
- if (PCPMetric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL)
- super->cachedSwap = value.ull;
- super->usedSwap = super->totalSwap - swapFreeMem - super->cachedSwap;
-}
-
-/* make copies of previously sampled values to avoid overwrite */
-static inline void PCPProcessList_backupCPUTime(pmAtomValue* values) {
- /* the PERIOD fields (must) mirror the TIME fields */
- for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) {
- values[metric + CPU_TOTAL_PERIOD] = values[metric];
- }
-}
-
-static inline void PCPProcessList_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) {
- pmAtomValue* value;
-
- /* new value for period */
- value = &values[previous];
- if (latest->ull > value->ull)
- value->ull = latest->ull - value->ull;
- else
- value->ull = 0;
-
- /* new value for time */
- value = &values[previous - CPU_TOTAL_PERIOD];
- value->ull = latest->ull;
-}
-
-/* using copied sampled values and new values, calculate derivations */
-static void PCPProcessList_deriveCPUTime(pmAtomValue* values) {
-
- pmAtomValue* usertime = &values[CPU_USER_TIME];
- pmAtomValue* guesttime = &values[CPU_GUEST_TIME];
- usertime->ull -= guesttime->ull;
-
- pmAtomValue* nicetime = &values[CPU_NICE_TIME];
- pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME];
- nicetime->ull -= guestnicetime->ull;
-
- pmAtomValue* idletime = &values[CPU_IDLE_TIME];
- pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME];
- pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME];
- idlealltime->ull = idletime->ull + iowaittime->ull;
-
- pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME];
- pmAtomValue* irqtime = &values[CPU_IRQ_TIME];
- pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME];
- pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME];
- systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull;
-
- pmAtomValue* virtalltime = &values[CPU_GUEST_TIME];
- virtalltime->ull = guesttime->ull + guestnicetime->ull;
-
- pmAtomValue* stealtime = &values[CPU_STEAL_TIME];
- pmAtomValue* totaltime = &values[CPU_TOTAL_TIME];
- totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull +
- idlealltime->ull + stealtime->ull + virtalltime->ull;
-
- PCPProcessList_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime);
- PCPProcessList_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime);
-}
-
-static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric)
-{
- pmAtomValue* value = &this->cpu[cpumetric];
- if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL)
- memset(&value, 0, sizeof(pmAtomValue));
-}
-
-static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric)
-{
- int cpus = this->super.existingCPUs;
- if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL)
- memset(this->values, 0, cpus * sizeof(pmAtomValue));
- for (int i = 0; i < cpus; i++)
- this->percpu[i][cpumetric].ull = this->values[i].ull;
-}
-
-static void PCPProcessList_updatePerCPUReal(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric)
-{
- int cpus = this->super.existingCPUs;
- if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL)
- memset(this->values, 0, cpus * sizeof(pmAtomValue));
- for (int i = 0; i < cpus; i++)
- this->percpu[i][cpumetric].d = this->values[i].d;
-}
-
-static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) {
- unsigned long long int dbufSize = 0;
- unsigned long long int dnodeSize = 0;
- unsigned long long int bonusSize = 0;
- pmAtomValue value;
-
- memset(&this->zfs, 0, sizeof(ZfsArcStats));
- if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
- this->zfs.anon = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64))
- this->zfs.min = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64))
- this->zfs.max = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))
- bonusSize = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64))
- dbufSize = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64))
- dnodeSize = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
- this->zfs.compressed = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
- this->zfs.uncompressed = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64))
- this->zfs.header = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64))
- this->zfs.MFU = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64))
- this->zfs.MRU = value.ull / ONE_K;
- if (PCPMetric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64))
- this->zfs.size = value.ull / ONE_K;
-
- this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K;
- this->zfs.enabled = (this->zfs.size > 0);
- this->zfs.isCompressed = (this->zfs.compressed > 0);
-}
-
-static void PCPProcessList_updateHeader(ProcessList* super, const Settings* settings) {
- PCPProcessList_updateMemoryInfo(super);
-
- PCPProcessList* this = (PCPProcessList*) super;
- PCPProcessList_updateCPUcount(this);
-
- PCPProcessList_backupCPUTime(this->cpu);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME);
- PCPProcessList_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME);
- PCPProcessList_deriveCPUTime(this->cpu);
-
- for (unsigned int i = 0; i < super->existingCPUs; i++)
- PCPProcessList_backupCPUTime(this->percpu[i]);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME);
- PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME);
- for (unsigned int i = 0; i < super->existingCPUs; i++)
- PCPProcessList_deriveCPUTime(this->percpu[i]);
-
- if (settings->showCPUFrequency)
- PCPProcessList_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY);
-
- PCPProcessList_scanZfsArcstats(this);
-}
-
-void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
- PCPProcessList* this = (PCPProcessList*) super;
- const Settings* settings = super->settings;
- bool enabled = !pauseProcessUpdate;
-
- bool flagged = settings->showCPUFrequency;
- PCPMetric_enable(PCP_HINV_CPUCLOCK, flagged);
-
- /* In pause mode do not sample per-process metric values at all */
- for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++)
- PCPMetric_enable(metric, enabled);
-
- flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP;
- PCPMetric_enable(PCP_PROC_CGROUPS, flagged && enabled);
- flagged = settings->ss->flags & PROCESS_FLAG_LINUX_OOM;
- PCPMetric_enable(PCP_PROC_OOMSCORE, flagged && enabled);
- flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CTXT;
- PCPMetric_enable(PCP_PROC_VCTXSW, flagged && enabled);
- PCPMetric_enable(PCP_PROC_NVCTXSW, flagged && enabled);
- flagged = settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR;
- PCPMetric_enable(PCP_PROC_LABELS, flagged && enabled);
- flagged = settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP;
- PCPMetric_enable(PCP_PROC_AUTOGROUP_ID, flagged && enabled);
- PCPMetric_enable(PCP_PROC_AUTOGROUP_NICE, flagged && enabled);
-
- /* Sample smaps metrics on every second pass to improve performance */
- static int smaps_flag;
- smaps_flag = !!smaps_flag;
- PCPMetric_enable(PCP_PROC_SMAPS_PSS, smaps_flag && enabled);
- PCPMetric_enable(PCP_PROC_SMAPS_SWAP, smaps_flag && enabled);
- PCPMetric_enable(PCP_PROC_SMAPS_SWAPPSS, smaps_flag && enabled);
-
- struct timeval timestamp;
- if (PCPMetric_fetch(&timestamp) != true)
- return;
-
- double sample = this->timestamp;
- this->timestamp = pmtimevalToReal(&timestamp);
-
- PCPProcessList_updateHeader(super, settings);
-
- /* In pause mode only update global data for meters (CPU, memory, etc) */
- if (pauseProcessUpdate)
- return;
-
- double period = (this->timestamp - sample) * 100;
- PCPProcessList_updateProcesses(this, period, &timestamp);
-}
-
-bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) {
- assert(id < super->existingCPUs);
- (void) super;
-
- pmAtomValue value;
- if (PCPMetric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32))
- return true;
- return false;
-}
diff --git a/pcp/PCPProcessTable.c b/pcp/PCPProcessTable.c
new file mode 100644
index 0000000..4999bdc
--- /dev/null
+++ b/pcp/PCPProcessTable.c
@@ -0,0 +1,486 @@
+/*
+htop - PCPProcessTable.c
+(C) 2014 Hisham H. Muhammad
+(C) 2020-2021 htop dev team
+(C) 2020-2021 Red Hat, Inc.
+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/PCPProcessTable.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "Machine.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Platform.h"
+#include "Process.h"
+#include "Settings.h"
+#include "XUtils.h"
+
+#include "linux/CGroupUtils.h"
+#include "pcp/Metric.h"
+#include "pcp/PCPMachine.h"
+#include "pcp/PCPProcess.h"
+
+
+ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
+ PCPProcessTable* this = xCalloc(1, sizeof(PCPProcessTable));
+ Object_setClass(this, Class(ProcessTable));
+
+ ProcessTable* super = &this->super;
+ ProcessTable_init(super, Class(PCPProcess), host, pidMatchList);
+
+ return super;
+}
+
+void ProcessTable_delete(Object* cast) {
+ PCPProcessTable* this = (PCPProcessTable*) cast;
+ ProcessTable_done(&this->super);
+ free(this);
+}
+
+static inline long Metric_instance_s32(int metric, int pid, int offset, long fallback) {
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_32))
+ return value.l;
+ return fallback;
+}
+
+static inline long long Metric_instance_s64(int metric, int pid, int offset, long long fallback) {
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_64))
+ return value.l;
+ return fallback;
+}
+
+static inline unsigned long Metric_instance_u32(int metric, int pid, int offset, unsigned long fallback) {
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U32))
+ return value.ul;
+ return fallback;
+}
+
+static inline unsigned long long Metric_instance_u64(int metric, int pid, int offset, unsigned long long fallback) {
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U64))
+ return value.ull;
+ return fallback;
+}
+
+static inline unsigned long long Metric_instance_time(int metric, int pid, int offset) {
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U64))
+ return value.ull / 10;
+ return 0;
+}
+
+static inline unsigned long long Metric_instance_ONE_K(int metric, int pid, int offset) {
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U64))
+ return value.ull / ONE_K;
+ return ULLONG_MAX;
+}
+
+static inline char Metric_instance_char(int metric, int pid, int offset, char fallback) {
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_STRING)) {
+ char uchar = value.cp[0];
+ free(value.cp);
+ return uchar;
+ }
+ return fallback;
+}
+
+static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) {
+ char* name = Hashtable_get(this->users, uid);
+ if (name)
+ return name;
+
+ pmAtomValue value;
+ if (Metric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) {
+ Hashtable_put(this->users, uid, value.cp);
+ name = value.cp;
+ }
+ return name;
+}
+
+static inline ProcessState PCPProcessTable_getProcessState(char state) {
+ switch (state) {
+ case '?': return UNKNOWN;
+ case 'R': return RUNNING;
+ case 'W': return WAITING;
+ case 'D': return UNINTERRUPTIBLE_WAIT;
+ case 'P': return PAGING;
+ case 'T': return STOPPED;
+ case 't': return TRACED;
+ case 'Z': return ZOMBIE;
+ case 'X': return DEFUNCT;
+ case 'I': return IDLE;
+ case 'S': return SLEEPING;
+ default: return UNKNOWN;
+ }
+}
+
+static void PCPProcessTable_updateID(Process* process, int pid, int offset) {
+ Process_setThreadGroup(process, Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1));
+ Process_setParent(process, Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1));
+ process->state = PCPProcessTable_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?'));
+}
+
+static void PCPProcessTable_updateInfo(PCPProcess* pp, int pid, int offset, char* command, size_t commLen) {
+ Process* process = &pp->super;
+ pmAtomValue value;
+
+ if (!Metric_instance(PCP_PROC_CMD, pid, offset, &value, PM_TYPE_STRING))
+ value.cp = xStrdup("<unknown>");
+ String_safeStrncpy(command, value.cp, commLen);
+ free(value.cp);
+
+ process->pgrp = Metric_instance_u32(PCP_PROC_PGRP, pid, offset, 0);
+ process->session = Metric_instance_u32(PCP_PROC_SESSION, pid, offset, 0);
+ process->tty_nr = Metric_instance_u32(PCP_PROC_TTY, pid, offset, 0);
+ process->tpgid = Metric_instance_u32(PCP_PROC_TTYPGRP, pid, offset, 0);
+ process->minflt = Metric_instance_u32(PCP_PROC_MINFLT, pid, offset, 0);
+ pp->cminflt = Metric_instance_u32(PCP_PROC_CMINFLT, pid, offset, 0);
+ process->majflt = Metric_instance_u32(PCP_PROC_MAJFLT, pid, offset, 0);
+ pp->cmajflt = Metric_instance_u32(PCP_PROC_CMAJFLT, pid, offset, 0);
+ pp->utime = Metric_instance_time(PCP_PROC_UTIME, pid, offset);
+ pp->stime = Metric_instance_time(PCP_PROC_STIME, pid, offset);
+ pp->cutime = Metric_instance_time(PCP_PROC_CUTIME, pid, offset);
+ pp->cstime = Metric_instance_time(PCP_PROC_CSTIME, pid, offset);
+ process->priority = Metric_instance_u32(PCP_PROC_PRIORITY, pid, offset, 0);
+ process->nice = Metric_instance_s32(PCP_PROC_NICE, pid, offset, 0);
+ process->nlwp = Metric_instance_u32(PCP_PROC_THREADS, pid, offset, 0);
+ process->starttime_ctime = Metric_instance_time(PCP_PROC_STARTTIME, pid, offset);
+ process->processor = Metric_instance_u32(PCP_PROC_PROCESSOR, pid, offset, 0);
+
+ process->time = pp->utime + pp->stime;
+}
+
+static void PCPProcessTable_updateIO(PCPProcess* pp, int pid, int offset, unsigned long long now) {
+ pmAtomValue value;
+
+ pp->io_rchar = Metric_instance_ONE_K(PCP_PROC_IO_RCHAR, pid, offset);
+ pp->io_wchar = Metric_instance_ONE_K(PCP_PROC_IO_WCHAR, pid, offset);
+ pp->io_syscr = Metric_instance_u64(PCP_PROC_IO_SYSCR, pid, offset, ULLONG_MAX);
+ pp->io_syscw = Metric_instance_u64(PCP_PROC_IO_SYSCW, pid, offset, ULLONG_MAX);
+ pp->io_cancelled_write_bytes = Metric_instance_ONE_K(PCP_PROC_IO_CANCELLED, pid, offset);
+
+ if (Metric_instance(PCP_PROC_IO_READB, pid, offset, &value, PM_TYPE_U64)) {
+ unsigned long long last_read = pp->io_read_bytes;
+ pp->io_read_bytes = value.ull / ONE_K;
+ pp->io_rate_read_bps = ONE_K * (pp->io_read_bytes - last_read) /
+ (now - pp->io_last_scan_time);
+ } else {
+ pp->io_read_bytes = ULLONG_MAX;
+ pp->io_rate_read_bps = NAN;
+ }
+
+ if (Metric_instance(PCP_PROC_IO_WRITEB, pid, offset, &value, PM_TYPE_U64)) {
+ unsigned long long last_write = pp->io_write_bytes;
+ pp->io_write_bytes = value.ull;
+ pp->io_rate_write_bps = ONE_K * (pp->io_write_bytes - last_write) /
+ (now - pp->io_last_scan_time);
+ } else {
+ pp->io_write_bytes = ULLONG_MAX;
+ pp->io_rate_write_bps = NAN;
+ }
+
+ pp->io_last_scan_time = now;
+}
+
+static void PCPProcessTable_updateMemory(PCPProcess* pp, int pid, int offset) {
+ pp->super.m_virt = Metric_instance_u32(PCP_PROC_MEM_SIZE, pid, offset, 0);
+ pp->super.m_resident = Metric_instance_u32(PCP_PROC_MEM_RSS, pid, offset, 0);
+ pp->m_share = Metric_instance_u32(PCP_PROC_MEM_SHARE, pid, offset, 0);
+ pp->m_priv = pp->super.m_resident - pp->m_share;
+ pp->m_trs = Metric_instance_u32(PCP_PROC_MEM_TEXTRS, pid, offset, 0);
+ pp->m_lrs = Metric_instance_u32(PCP_PROC_MEM_LIBRS, pid, offset, 0);
+ pp->m_drs = Metric_instance_u32(PCP_PROC_MEM_DATRS, pid, offset, 0);
+ pp->m_dt = Metric_instance_u32(PCP_PROC_MEM_DIRTY, pid, offset, 0);
+}
+
+static void PCPProcessTable_updateSmaps(PCPProcess* pp, pid_t pid, int offset) {
+ pp->m_pss = Metric_instance_u64(PCP_PROC_SMAPS_PSS, pid, offset, 0);
+ pp->m_swap = Metric_instance_u64(PCP_PROC_SMAPS_SWAP, pid, offset, 0);
+ pp->m_psswp = Metric_instance_u64(PCP_PROC_SMAPS_SWAPPSS, pid, offset, 0);
+}
+
+static void PCPProcessTable_readOomData(PCPProcess* pp, int pid, int offset) {
+ pp->oom = Metric_instance_u32(PCP_PROC_OOMSCORE, pid, offset, 0);
+}
+
+static void PCPProcessTable_readAutogroup(PCPProcess* pp, int pid, int offset) {
+ pp->autogroup_id = Metric_instance_s64(PCP_PROC_AUTOGROUP_ID, pid, offset, -1);
+ pp->autogroup_nice = Metric_instance_s32(PCP_PROC_AUTOGROUP_NICE, pid, offset, 0);
+}
+
+static void PCPProcessTable_readCtxtData(PCPProcess* pp, int pid, int offset) {
+ pmAtomValue value;
+ unsigned long ctxt = 0;
+
+ if (Metric_instance(PCP_PROC_VCTXSW, pid, offset, &value, PM_TYPE_U32))
+ ctxt += value.ul;
+ if (Metric_instance(PCP_PROC_NVCTXSW, pid, offset, &value, PM_TYPE_U32))
+ ctxt += value.ul;
+
+ pp->ctxt_diff = ctxt > pp->ctxt_total ? ctxt - pp->ctxt_total : 0;
+ pp->ctxt_total = ctxt;
+}
+
+static char* setString(Metric metric, int pid, int offset, char* string) {
+ if (string)
+ free(string);
+ pmAtomValue value;
+ if (Metric_instance(metric, pid, offset, &value, PM_TYPE_STRING))
+ string = value.cp;
+ else
+ string = NULL;
+ return string;
+}
+
+static void PCPProcessTable_updateTTY(Process* process, int pid, int offset) {
+ process->tty_name = setString(PCP_PROC_TTYNAME, pid, offset, process->tty_name);
+}
+
+static void PCPProcessTable_readCGroups(PCPProcess* pp, int pid, int offset) {
+ pp->cgroup = setString(PCP_PROC_CGROUPS, pid, offset, pp->cgroup);
+
+ if (pp->cgroup) {
+ char* cgroup_short = CGroup_filterName(pp->cgroup);
+ if (cgroup_short) {
+ Row_updateFieldWidth(CCGROUP, strlen(cgroup_short));
+ free_and_xStrdup(&pp->cgroup_short, cgroup_short);
+ free(cgroup_short);
+ } else {
+ //CCGROUP is alias to normal CGROUP if shortening fails
+ Row_updateFieldWidth(CCGROUP, strlen(pp->cgroup));
+ free(pp->cgroup_short);
+ pp->cgroup_short = NULL;
+ }
+
+ char* container_short = CGroup_filterName(pp->cgroup);
+ if (container_short) {
+ Row_updateFieldWidth(CONTAINER, strlen(container_short));
+ free_and_xStrdup(&pp->container_short, container_short);
+ free(container_short);
+ } else {
+ Row_updateFieldWidth(CONTAINER, strlen("N/A"));
+ free(pp->container_short);
+ pp->container_short = NULL;
+ }
+ } else {
+ free(pp->cgroup_short);
+ pp->cgroup_short = NULL;
+
+ free(pp->container_short);
+ pp->container_short = NULL;
+ }
+}
+
+static void PCPProcessTable_readSecattrData(PCPProcess* pp, int pid, int offset) {
+ pp->secattr = setString(PCP_PROC_LABELS, pid, offset, pp->secattr);
+}
+
+static void PCPProcessTable_readCwd(PCPProcess* pp, int pid, int offset) {
+ pp->super.procCwd = setString(PCP_PROC_CWD, pid, offset, pp->super.procCwd);
+}
+
+static void PCPProcessTable_updateUsername(Process* process, int pid, int offset, UsersTable* users) {
+ process->st_uid = Metric_instance_u32(PCP_PROC_ID_UID, pid, offset, 0);
+ process->user = setUser(users, process->st_uid, pid, offset);
+}
+
+static void PCPProcessTable_updateCmdline(Process* process, int pid, int offset, const char* comm) {
+ pmAtomValue value;
+ if (!Metric_instance(PCP_PROC_PSARGS, pid, offset, &value, PM_TYPE_STRING)) {
+ if (process->state != ZOMBIE)
+ process->isKernelThread = true;
+ Process_updateCmdline(process, NULL, 0, 0);
+ return;
+ }
+
+ char* command = value.cp;
+ int length = strlen(command);
+ if (command[0] != '(') {
+ process->isKernelThread = false;
+ } else {
+ ++command;
+ --length;
+ if (command[length - 1] == ')')
+ command[--length] = '\0';
+ process->isKernelThread = true;
+ }
+
+ int tokenEnd = 0;
+ int tokenStart = 0;
+ bool argSepSpace = false;
+
+ for (int i = 0; i < length; i++) {
+ /* htop considers the next character after the last / that is before
+ * basenameOffset, as the start of the basename in cmdline - see
+ * Process_writeCommand */
+ if (command[i] == '/')
+ tokenStart = i + 1;
+ /* special-case arguments for problematic situations like "find /" */
+ if (command[i] <= ' ')
+ argSepSpace = true;
+ }
+ tokenEnd = length;
+ if (argSepSpace)
+ tokenStart = 0;
+
+ Process_updateCmdline(process, command, tokenStart, tokenEnd);
+ free(value.cp);
+
+ Process_updateComm(process, comm);
+
+ if (Metric_instance(PCP_PROC_EXE, pid, offset, &value, PM_TYPE_STRING)) {
+ Process_updateExe(process, value.cp[0] ? value.cp : NULL);
+ free(value.cp);
+ }
+}
+
+static bool PCPProcessTable_updateProcesses(PCPProcessTable* this) {
+ ProcessTable* pt = (ProcessTable*) this;
+ Machine* host = pt->super.host;
+ PCPMachine* phost = (PCPMachine*) host;
+
+ const Settings* settings = host->settings;
+ bool hideKernelThreads = settings->hideKernelThreads;
+ bool hideUserlandThreads = settings->hideUserlandThreads;
+ uint32_t flags = settings->ss->flags;
+
+ unsigned long long now = (unsigned long long)(phost->timestamp * 1000);
+ int pid = -1, offset = -1;
+
+ /* for every process ... */
+ while (Metric_iterate(PCP_PROC_PID, &pid, &offset)) {
+
+ bool preExisting;
+ Process* proc = ProcessTable_getProcess(pt, pid, &preExisting, PCPProcess_new);
+ PCPProcess* pp = (PCPProcess*) proc;
+ PCPProcessTable_updateID(proc, pid, offset);
+ proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc);
+ pp->offset = offset >= 0 ? offset : 0;
+
+ /*
+ * These conditions will not trigger on first occurrence, cause we need to
+ * add the process to the ProcessTable and do all one time scans
+ * (e.g. parsing the cmdline to detect a kernel thread)
+ * But it will short-circuit subsequent scans.
+ */
+ if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) {
+ proc->super.updated = true;
+ proc->super.show = false;
+ if (proc->state == RUNNING)
+ pt->runningTasks++;
+ pt->kernelThreads++;
+ pt->totalTasks++;
+ continue;
+ }
+ if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
+ proc->super.updated = true;
+ proc->super.show = false;
+ if (proc->state == RUNNING)
+ pt->runningTasks++;
+ pt->userlandThreads++;
+ pt->totalTasks++;
+ continue;
+ }
+
+ if (flags & PROCESS_FLAG_IO)
+ PCPProcessTable_updateIO(pp, pid, offset, now);
+
+ PCPProcessTable_updateMemory(pp, pid, offset);
+
+ if ((flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {
+ if (Metric_enabled(PCP_PROC_SMAPS_PSS)) {
+ PCPProcessTable_updateSmaps(pp, pid, offset);
+ }
+ }
+
+ char command[MAX_NAME + 1];
+ unsigned int tty_nr = proc->tty_nr;
+ unsigned long long int lasttimes = pp->utime + pp->stime;
+
+ PCPProcessTable_updateInfo(pp, pid, offset, command, sizeof(command));
+ proc->starttime_ctime += Platform_getBootTime();
+ if (tty_nr != proc->tty_nr)
+ PCPProcessTable_updateTTY(proc, pid, offset);
+
+ proc->percent_cpu = NAN;
+ if (phost->period > 0.0) {
+ float percent_cpu = saturatingSub(pp->utime + pp->stime, lasttimes) / phost->period * 100.0;
+ proc->percent_cpu = MINIMUM(percent_cpu, host->activeCPUs * 100.0F);
+ }
+ proc->percent_mem = proc->m_resident / (double) host->totalMem * 100.0;
+ Process_updateCPUFieldWidths(proc->percent_cpu);
+
+ PCPProcessTable_updateUsername(proc, pid, offset, host->usersTable);
+
+ if (!preExisting) {
+ PCPProcessTable_updateCmdline(proc, pid, offset, command);
+ Process_fillStarttimeBuffer(proc);
+ ProcessTable_add(pt, proc);
+ } else if (settings->updateProcessNames && proc->state != ZOMBIE) {
+ PCPProcessTable_updateCmdline(proc, pid, offset, command);
+ }
+
+ if (flags & PROCESS_FLAG_LINUX_CGROUP)
+ PCPProcessTable_readCGroups(pp, pid, offset);
+
+ if (flags & PROCESS_FLAG_LINUX_OOM)
+ PCPProcessTable_readOomData(pp, pid, offset);
+
+ if (flags & PROCESS_FLAG_LINUX_CTXT)
+ PCPProcessTable_readCtxtData(pp, pid, offset);
+
+ if (flags & PROCESS_FLAG_LINUX_SECATTR)
+ PCPProcessTable_readSecattrData(pp, pid, offset);
+
+ if (flags & PROCESS_FLAG_CWD)
+ PCPProcessTable_readCwd(pp, pid, offset);
+
+ if (flags & PROCESS_FLAG_LINUX_AUTOGROUP)
+ PCPProcessTable_readAutogroup(pp, pid, offset);
+
+ if (proc->state == ZOMBIE && !proc->cmdline && command[0]) {
+ Process_updateCmdline(proc, command, 0, strlen(command));
+ } else if (Process_isThread(proc)) {
+ if ((settings->showThreadNames || Process_isKernelThread(proc)) && command[0]) {
+ Process_updateCmdline(proc, command, 0, strlen(command));
+ }
+
+ if (Process_isKernelThread(proc)) {
+ pt->kernelThreads++;
+ } else {
+ pt->userlandThreads++;
+ }
+ }
+
+ /* Set at the end when we know if a new entry is a thread */
+ proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) ||
+ (hideUserlandThreads && Process_isUserlandThread(proc)));
+
+ pt->totalTasks++;
+ if (proc->state == RUNNING)
+ pt->runningTasks++;
+ proc->super.updated = true;
+ }
+ return true;
+}
+
+void ProcessTable_goThroughEntries(ProcessTable* super) {
+ PCPProcessTable* this = (PCPProcessTable*) super;
+ PCPProcessTable_updateProcesses(this);
+}
diff --git a/pcp/PCPProcessTable.h b/pcp/PCPProcessTable.h
new file mode 100644
index 0000000..e55c85b
--- /dev/null
+++ b/pcp/PCPProcessTable.h
@@ -0,0 +1,24 @@
+#ifndef HEADER_PCPProcessTable
+#define HEADER_PCPProcessTable
+/*
+htop - PCPProcessTable.h
+(C) 2014 Hisham H. Muhammad
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "Hashtable.h"
+#include "ProcessTable.h"
+#include "UsersTable.h"
+
+#include "pcp/Platform.h"
+
+
+typedef struct PCPProcessTable_ {
+ ProcessTable super;
+} PCPProcessTable;
+
+#endif
diff --git a/pcp/Platform.c b/pcp/Platform.c
index 342bf43..0b5f334 100644
--- a/pcp/Platform.c
+++ b/pcp/Platform.c
@@ -1,8 +1,8 @@
/*
htop - linux/Platform.c
(C) 2014 Hisham H. Muhammad
-(C) 2020-2021 htop dev team
-(C) 2020-2021 Red Hat, Inc.
+(C) 2020-2022 htop dev team
+(C) 2020-2022 Red Hat, Inc.
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@@ -25,6 +25,8 @@ in the source distribution for its full text.
#include "DiskIOMeter.h"
#include "DynamicColumn.h"
#include "DynamicMeter.h"
+#include "DynamicScreen.h"
+#include "FileDescriptorMeter.h"
#include "HostnameMeter.h"
#include "LoadAverageMeter.h"
#include "Macros.h"
@@ -32,7 +34,7 @@ in the source distribution for its full text.
#include "MemorySwapMeter.h"
#include "Meter.h"
#include "NetworkIOMeter.h"
-#include "ProcessList.h"
+#include "ProcessTable.h"
#include "Settings.h"
#include "SwapMeter.h"
#include "SysArchMeter.h"
@@ -43,10 +45,12 @@ in the source distribution for its full text.
#include "linux/PressureStallMeter.h"
#include "linux/ZramMeter.h"
#include "linux/ZramStats.h"
+#include "pcp/Metric.h"
#include "pcp/PCPDynamicColumn.h"
#include "pcp/PCPDynamicMeter.h"
-#include "pcp/PCPMetric.h"
-#include "pcp/PCPProcessList.h"
+#include "pcp/PCPDynamicScreen.h"
+#include "pcp/PCPMachine.h"
+#include "pcp/PCPProcessTable.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/ZfsCompressedArcMeter.h"
@@ -106,6 +110,7 @@ const MeterClass* const Platform_meterTypes[] = {
&PressureStallCPUSomeMeter_class,
&PressureStallIOSomeMeter_class,
&PressureStallIOFullMeter_class,
+ &PressureStallIRQFullMeter_class,
&PressureStallMemorySomeMeter_class,
&PressureStallMemoryFullMeter_class,
&ZfsArcMeter_class,
@@ -114,6 +119,7 @@ const MeterClass* const Platform_meterTypes[] = {
&DiskIOMeter_class,
&NetworkIOMeter_class,
&SysArchMeter_class,
+ &FileDescriptorMeter_class,
NULL
};
@@ -171,6 +177,7 @@ static const char* Platform_metricNames[] = {
[PCP_PSI_CPUSOME] = "kernel.all.pressure.cpu.some.avg",
[PCP_PSI_IOSOME] = "kernel.all.pressure.io.some.avg",
[PCP_PSI_IOFULL] = "kernel.all.pressure.io.full.avg",
+ [PCP_PSI_IRQFULL] = "kernel.all.pressure.irq.full.avg",
[PCP_PSI_MEMSOME] = "kernel.all.pressure.memory.some.avg",
[PCP_PSI_MEMFULL] = "kernel.all.pressure.memory.full.avg",
@@ -190,6 +197,10 @@ static const char* Platform_metricNames[] = {
[PCP_ZRAM_CAPACITY] = "zram.capacity",
[PCP_ZRAM_ORIGINAL] = "zram.mm_stat.data_size.original",
[PCP_ZRAM_COMPRESSED] = "zram.mm_stat.data_size.compressed",
+ [PCP_MEM_ZSWAP] = "mem.util.zswap",
+ [PCP_MEM_ZSWAPPED] = "mem.util.zswapped",
+ [PCP_VFS_FILES_COUNT] = "vfs.files.count",
+ [PCP_VFS_FILES_MAX] = "vfs.files.max",
[PCP_PROC_PID] = "proc.psinfo.pid",
[PCP_PROC_PPID] = "proc.psinfo.ppid",
@@ -248,7 +259,38 @@ static const char* Platform_metricNames[] = {
[PCP_METRIC_COUNT] = NULL
};
-size_t Platform_addMetric(PCPMetric id, const char* name) {
+#ifndef HAVE_PMLOOKUPDESCS
+/*
+ * pmLookupDescs(3) exists in latest versions of libpcp (5.3.6+),
+ * but for older versions we provide an implementation here. This
+ * involves multiple round trips to pmcd though, which the latest
+ * libpcp version avoids by using a protocol extension. In time,
+ * perhaps in a few years, we could remove this back-compat code.
+ */
+int pmLookupDescs(int numpmid, pmID* pmids, pmDesc* descs) {
+ int count = 0;
+
+ for (int i = 0; i < numpmid; i++) {
+ /* expect some metrics to be missing - e.g. PMDA not available */
+ if (pmids[i] == PM_ID_NULL)
+ continue;
+
+ int sts = pmLookupDesc(pmids[i], &descs[i]);
+ if (sts < 0) {
+ if (pmDebugOptions.appl0)
+ fprintf(stderr, "Error: cannot lookup metric %s(%s): %s\n",
+ pcp->names[i], pmIDStr(pcp->pmids[i]), pmErrStr(sts));
+ pmids[i] = PM_ID_NULL;
+ continue;
+ }
+
+ count++;
+ }
+ return count;
+}
+#endif
+
+size_t Platform_addMetric(Metric id, const char* name) {
unsigned int i = (unsigned int)id;
if (i >= PCP_METRIC_COUNT && i >= pcp->totalMetrics) {
@@ -317,6 +359,7 @@ bool Platform_init(void) {
pcp->columns.offset = PCP_METRIC_COUNT + pcp->meters.cursor;
PCPDynamicColumns_init(&pcp->columns);
+ PCPDynamicScreens_init(&pcp->screens, &pcp->columns);
sts = pmLookupName(pcp->totalMetrics, pcp->names, pcp->pmids);
if (sts < 0) {
@@ -325,49 +368,43 @@ bool Platform_init(void) {
return false;
}
- for (size_t 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 */
- if (pcp->pmids[i] == PM_ID_NULL)
- continue;
-
- sts = pmLookupDesc(pcp->pmids[i], &pcp->descs[i]);
- if (sts < 0) {
- if (pmDebugOptions.appl0)
- fprintf(stderr, "Error: cannot lookup metric %s(%s): %s\n",
- pcp->names[i], pmIDStr(pcp->pmids[i]), pmErrStr(sts));
- pcp->pmids[i] = PM_ID_NULL;
- continue;
- }
+ sts = pmLookupDescs(pcp->totalMetrics, pcp->pmids, pcp->descs);
+ if (sts < 1) {
+ if (sts < 0)
+ fprintf(stderr, "Error: cannot lookup descriptors: %s\n", pmErrStr(sts));
+ else /* ensure we have at least one valid metric to work with */
+ fprintf(stderr, "Error: cannot find a single valid metric, exiting\n");
+ Platform_done();
+ return false;
}
/* set proc.control.perclient.threads to 1 for live contexts */
- PCPMetric_enableThreads();
+ Metric_enableThreads();
/* extract values needed for setup - e.g. cpu count, pid_max */
- PCPMetric_enable(PCP_PID_MAX, true);
- PCPMetric_enable(PCP_BOOTTIME, true);
- PCPMetric_enable(PCP_HINV_NCPU, true);
- PCPMetric_enable(PCP_PERCPU_SYSTEM, true);
- PCPMetric_enable(PCP_UNAME_SYSNAME, true);
- PCPMetric_enable(PCP_UNAME_RELEASE, true);
- PCPMetric_enable(PCP_UNAME_MACHINE, true);
- PCPMetric_enable(PCP_UNAME_DISTRO, true);
-
+ Metric_enable(PCP_PID_MAX, true);
+ Metric_enable(PCP_BOOTTIME, true);
+ Metric_enable(PCP_HINV_NCPU, true);
+ Metric_enable(PCP_PERCPU_SYSTEM, true);
+ Metric_enable(PCP_UNAME_SYSNAME, true);
+ Metric_enable(PCP_UNAME_RELEASE, true);
+ Metric_enable(PCP_UNAME_MACHINE, true);
+ Metric_enable(PCP_UNAME_DISTRO, true);
+
+ /* enable metrics for all dynamic columns (including those from dynamic screens) */
for (size_t i = pcp->columns.offset; i < pcp->columns.offset + pcp->columns.count; i++)
- PCPMetric_enable(i, true);
+ Metric_enable(i, true);
- PCPMetric_fetch(NULL);
+ Metric_fetch(NULL);
- for (PCPMetric metric = 0; metric < PCP_PROC_PID; metric++)
- PCPMetric_enable(metric, true);
- PCPMetric_enable(PCP_PID_MAX, false); /* needed one time only */
- PCPMetric_enable(PCP_BOOTTIME, false);
- PCPMetric_enable(PCP_UNAME_SYSNAME, false);
- PCPMetric_enable(PCP_UNAME_RELEASE, false);
- PCPMetric_enable(PCP_UNAME_MACHINE, false);
- PCPMetric_enable(PCP_UNAME_DISTRO, false);
+ for (Metric metric = 0; metric < PCP_PROC_PID; metric++)
+ Metric_enable(metric, true);
+ Metric_enable(PCP_PID_MAX, false); /* needed one time only */
+ Metric_enable(PCP_BOOTTIME, false);
+ Metric_enable(PCP_UNAME_SYSNAME, false);
+ Metric_enable(PCP_UNAME_RELEASE, false);
+ Metric_enable(PCP_UNAME_MACHINE, false);
+ Metric_enable(PCP_UNAME_DISTRO, false);
/* first sample (fetch) performed above, save constants */
Platform_getBootTime();
@@ -386,6 +423,10 @@ void Platform_dynamicMetersDone(Hashtable* meters) {
PCPDynamicMeters_done(meters);
}
+void Platform_dynamicScreensDone(Hashtable* screens) {
+ PCPDynamicScreens_done(screens);
+}
+
void Platform_done(void) {
pmDestroyContext(pcp->context);
if (pcp->result)
@@ -405,7 +446,7 @@ void Platform_setBindings(Htop_Action* keys) {
int Platform_getUptime(void) {
pmAtomValue value;
- if (PCPMetric_values(PCP_UPTIME, &value, 1, PM_TYPE_32) == NULL)
+ if (Metric_values(PCP_UPTIME, &value, 1, PM_TYPE_32) == NULL)
return 0;
return value.l;
}
@@ -414,7 +455,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
*one = *five = *fifteen = 0.0;
pmAtomValue values[3] = {0};
- if (PCPMetric_values(PCP_LOAD_AVERAGE, values, 3, PM_TYPE_DOUBLE) != NULL) {
+ if (Metric_values(PCP_LOAD_AVERAGE, values, 3, PM_TYPE_DOUBLE) != NULL) {
*one = values[0].d;
*five = values[1].d;
*fifteen = values[2].d;
@@ -426,20 +467,20 @@ unsigned int Platform_getMaxCPU(void) {
return pcp->ncpu;
pmAtomValue value;
- if (PCPMetric_values(PCP_HINV_NCPU, &value, 1, PM_TYPE_U32) != NULL)
+ if (Metric_values(PCP_HINV_NCPU, &value, 1, PM_TYPE_U32) != NULL)
pcp->ncpu = value.ul;
else
pcp->ncpu = 1;
return pcp->ncpu;
}
-int Platform_getMaxPid(void) {
+pid_t Platform_getMaxPid(void) {
if (pcp->pidmax)
return pcp->pidmax;
pmAtomValue value;
- if (PCPMetric_values(PCP_PID_MAX, &value, 1, PM_TYPE_32) == NULL)
- return -1;
+ if (Metric_values(PCP_PID_MAX, &value, 1, PM_TYPE_32) == NULL)
+ return UINT_MAX;
pcp->pidmax = value.l;
return pcp->pidmax;
}
@@ -449,13 +490,12 @@ long long Platform_getBootTime(void) {
return pcp->btime;
pmAtomValue value;
- if (PCPMetric_values(PCP_BOOTTIME, &value, 1, PM_TYPE_64) != NULL)
+ if (Metric_values(PCP_BOOTTIME, &value, 1, PM_TYPE_64) != NULL)
pcp->btime = value.ll;
return pcp->btime;
}
-static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) {
-
+static double Platform_setOneCPUValues(Meter* this, const Settings* settings, pmAtomValue* values) {
unsigned long long value = values[CPU_TOTAL_PERIOD].ull;
double total = (double) (value == 0 ? 1 : value);
double percent;
@@ -463,28 +503,32 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) {
double* v = this->values;
v[CPU_METER_NICE] = values[CPU_NICE_PERIOD].ull / total * 100.0;
v[CPU_METER_NORMAL] = values[CPU_USER_PERIOD].ull / total * 100.0;
- if (this->pl->settings->detailedCPUTime) {
+ if (settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = values[CPU_SYSTEM_PERIOD].ull / total * 100.0;
v[CPU_METER_IRQ] = values[CPU_IRQ_PERIOD].ull / total * 100.0;
v[CPU_METER_SOFTIRQ] = values[CPU_SOFTIRQ_PERIOD].ull / total * 100.0;
+ this->curItems = 5;
+
v[CPU_METER_STEAL] = values[CPU_STEAL_PERIOD].ull / total * 100.0;
v[CPU_METER_GUEST] = values[CPU_GUEST_PERIOD].ull / total * 100.0;
+ if (settings->accountGuestInCPUMeter) {
+ this->curItems = 7;
+ }
+
v[CPU_METER_IOWAIT] = values[CPU_IOWAIT_PERIOD].ull / total * 100.0;
- this->curItems = 8;
- if (this->pl->settings->accountGuestInCPUMeter)
- percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6];
- else
- percent = v[0] + v[1] + v[2] + v[3] + v[4];
} else {
- v[2] = values[CPU_SYSTEM_ALL_PERIOD].ull / total * 100.0;
+ v[CPU_METER_KERNEL] = values[CPU_SYSTEM_ALL_PERIOD].ull / total * 100.0;
value = values[CPU_STEAL_PERIOD].ull + values[CPU_GUEST_PERIOD].ull;
- v[3] = value / total * 100.0;
+ v[CPU_METER_IRQ] = value / total * 100.0;
this->curItems = 4;
- percent = v[0] + v[1] + v[2] + v[3];
}
- percent = CLAMP(percent, 0.0, 100.0);
- if (isnan(percent))
- percent = 0.0;
+
+ percent = sumPositiveValues(v, this->curItems);
+ percent = MINIMUM(percent, 100.0);
+
+ if (settings->detailedCPUTime) {
+ this->curItems = 8;
+ }
v[CPU_METER_FREQUENCY] = values[CPU_FREQUENCY].d;
v[CPU_METER_TEMPERATURE] = NAN;
@@ -493,43 +537,65 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) {
}
double Platform_setCPUValues(Meter* this, int cpu) {
- const PCPProcessList* pl = (const PCPProcessList*) this->pl;
+ const PCPMachine* phost = (const PCPMachine*) this->host;
+ const Settings* settings = this->host->settings;
+
if (cpu <= 0) /* use aggregate values */
- return Platform_setOneCPUValues(this, pl->cpu);
- return Platform_setOneCPUValues(this, pl->percpu[cpu - 1]);
+ return Platform_setOneCPUValues(this, settings, phost->cpu);
+ return Platform_setOneCPUValues(this, settings, phost->percpu[cpu - 1]);
}
void Platform_setMemoryValues(Meter* this) {
- const ProcessList* pl = this->pl;
- const PCPProcessList* ppl = (const PCPProcessList*) pl;
-
- this->total = pl->totalMem;
- this->values[0] = pl->usedMem;
- this->values[1] = pl->buffersMem;
- this->values[2] = pl->sharedMem;
- this->values[3] = pl->cachedMem;
- this->values[4] = pl->availableMem;
-
- if (ppl->zfs.enabled != 0) {
+ const Machine* host = this->host;
+ const PCPMachine* phost = (const PCPMachine*) 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;
+ 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) {
// ZFS does not shrink below the value of zfs_arc_min.
unsigned long long int shrinkableSize = 0;
- if (ppl->zfs.size > ppl->zfs.min)
- shrinkableSize = ppl->zfs.size - ppl->zfs.min;
- this->values[0] -= shrinkableSize;
- this->values[3] += shrinkableSize;
- this->values[4] += shrinkableSize;
+ 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 (phost->zswap.usedZswapOrig > 0 || phost->zswap.usedZswapComp > 0) {
+ this->values[MEMORY_METER_USED] -= phost->zswap.usedZswapComp;
+ this->values[MEMORY_METER_COMPRESSED] += phost->zswap.usedZswapComp;
}
}
void Platform_setSwapValues(Meter* this) {
- const ProcessList* pl = this->pl;
- this->total = pl->totalSwap;
- this->values[0] = pl->usedSwap;
- this->values[1] = pl->cachedSwap;
+ const Machine* host = this->host;
+ const PCPMachine* phost = (const PCPMachine*) host;
+
+ this->total = host->totalSwap;
+ this->values[SWAP_METER_USED] = host->usedSwap;
+ this->values[SWAP_METER_CACHE] = host->cachedSwap;
+ this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */
+
+ if (phost->zswap.usedZswapOrig > 0 || phost->zswap.usedZswapComp > 0) {
+ /* refer to linux/Platform.c::Platform_setSwapValues for details */
+ this->values[SWAP_METER_USED] -= phost->zswap.usedZswapOrig;
+ if (this->values[SWAP_METER_USED] < 0) {
+ /* subtract the overflow from SwapCached */
+ this->values[SWAP_METER_CACHE] += this->values[SWAP_METER_USED];
+ this->values[SWAP_METER_USED] = 0;
+ }
+ this->values[SWAP_METER_FRONTSWAP] += phost->zswap.usedZswapOrig;
+ }
}
void Platform_setZramValues(Meter* this) {
- int i, count = PCPMetric_instanceCount(PCP_ZRAM_CAPACITY);
+ int i, count = Metric_instanceCount(PCP_ZRAM_CAPACITY);
if (!count) {
this->total = 0;
this->values[0] = 0;
@@ -540,36 +606,40 @@ void Platform_setZramValues(Meter* this) {
pmAtomValue* values = xCalloc(count, sizeof(pmAtomValue));
ZramStats stats = {0};
- if (PCPMetric_values(PCP_ZRAM_CAPACITY, values, count, PM_TYPE_U64)) {
+ if (Metric_values(PCP_ZRAM_CAPACITY, values, count, PM_TYPE_U64)) {
for (i = 0; i < count; i++)
stats.totalZram += values[i].ull;
}
- if (PCPMetric_values(PCP_ZRAM_ORIGINAL, values, count, PM_TYPE_U64)) {
+ if (Metric_values(PCP_ZRAM_ORIGINAL, values, count, PM_TYPE_U64)) {
for (i = 0; i < count; i++)
stats.usedZramOrig += values[i].ull;
}
- if (PCPMetric_values(PCP_ZRAM_COMPRESSED, values, count, PM_TYPE_U64)) {
+ if (Metric_values(PCP_ZRAM_COMPRESSED, values, count, PM_TYPE_U64)) {
for (i = 0; i < count; i++)
stats.usedZramComp += values[i].ull;
}
free(values);
+ if (stats.usedZramComp > stats.usedZramOrig) {
+ stats.usedZramComp = stats.usedZramOrig;
+ }
+
this->total = stats.totalZram;
this->values[0] = stats.usedZramComp;
- this->values[1] = stats.usedZramOrig;
+ this->values[1] = stats.usedZramOrig - stats.usedZramComp;
}
void Platform_setZfsArcValues(Meter* this) {
- const PCPProcessList* ppl = (const PCPProcessList*) this->pl;
+ const PCPMachine* phost = (const PCPMachine*) this->host;
- ZfsArcMeter_readStats(this, &(ppl->zfs));
+ ZfsArcMeter_readStats(this, &phost->zfs);
}
void Platform_setZfsCompressedArcValues(Meter* this) {
- const PCPProcessList* ppl = (const PCPProcessList*) this->pl;
+ const PCPMachine* phost = (const PCPMachine*) this->host;
- ZfsCompressedArcMeter_readStats(this, &(ppl->zfs));
+ ZfsCompressedArcMeter_readStats(this, &phost->zfs);
}
void Platform_getHostname(char* buffer, size_t size) {
@@ -586,13 +656,13 @@ void Platform_getRelease(char** string) {
/* first call, extract just-sampled values */
pmAtomValue sysname, release, machine, distro;
- if (!PCPMetric_values(PCP_UNAME_SYSNAME, &sysname, 1, PM_TYPE_STRING))
+ if (!Metric_values(PCP_UNAME_SYSNAME, &sysname, 1, PM_TYPE_STRING))
sysname.cp = NULL;
- if (!PCPMetric_values(PCP_UNAME_RELEASE, &release, 1, PM_TYPE_STRING))
+ if (!Metric_values(PCP_UNAME_RELEASE, &release, 1, PM_TYPE_STRING))
release.cp = NULL;
- if (!PCPMetric_values(PCP_UNAME_MACHINE, &machine, 1, PM_TYPE_STRING))
+ if (!Metric_values(PCP_UNAME_MACHINE, &machine, 1, PM_TYPE_STRING))
machine.cp = NULL;
- if (!PCPMetric_values(PCP_UNAME_DISTRO, &distro, 1, PM_TYPE_STRING))
+ if (!Metric_values(PCP_UNAME_DISTRO, &distro, 1, PM_TYPE_STRING))
distro.cp = NULL;
size_t length = 16; /* padded for formatting characters */
@@ -640,17 +710,11 @@ void Platform_getRelease(char** string) {
char* Platform_getProcessEnv(pid_t pid) {
pmAtomValue value;
- if (!PCPMetric_instance(PCP_PROC_ENVIRON, pid, 0, &value, PM_TYPE_STRING))
+ if (!Metric_instance(PCP_PROC_ENVIRON, pid, 0, &value, PM_TYPE_STRING))
return NULL;
return value.cp;
}
-char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
- (void)pid;
- (void)inode;
- return NULL;
-}
-
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
(void)pid;
return NULL;
@@ -659,18 +723,20 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred) {
*ten = *sixty = *threehundred = 0;
- PCPMetric metric;
+ Metric metric;
if (String_eq(file, "cpu"))
metric = PCP_PSI_CPUSOME;
else if (String_eq(file, "io"))
metric = some ? PCP_PSI_IOSOME : PCP_PSI_IOFULL;
+ else if (String_eq(file, "irq"))
+ metric = PCP_PSI_IRQFULL;
else if (String_eq(file, "mem"))
metric = some ? PCP_PSI_MEMSOME : PCP_PSI_MEMFULL;
else
return;
pmAtomValue values[3] = {0};
- if (PCPMetric_values(metric, values, 3, PM_TYPE_DOUBLE) != NULL) {
+ if (Metric_values(metric, values, 3, PM_TYPE_DOUBLE) != NULL) {
*ten = values[0].d;
*sixty = values[1].d;
*threehundred = values[2].d;
@@ -681,11 +747,11 @@ bool Platform_getDiskIO(DiskIOData* data) {
memset(data, 0, sizeof(*data));
pmAtomValue value;
- if (PCPMetric_values(PCP_DISK_READB, &value, 1, PM_TYPE_U64) != NULL)
+ if (Metric_values(PCP_DISK_READB, &value, 1, PM_TYPE_U64) != NULL)
data->totalBytesRead = value.ull;
- if (PCPMetric_values(PCP_DISK_WRITEB, &value, 1, PM_TYPE_U64) != NULL)
+ if (Metric_values(PCP_DISK_WRITEB, &value, 1, PM_TYPE_U64) != NULL)
data->totalBytesWritten = value.ull;
- if (PCPMetric_values(PCP_DISK_ACTIVE, &value, 1, PM_TYPE_U64) != NULL)
+ if (Metric_values(PCP_DISK_ACTIVE, &value, 1, PM_TYPE_U64) != NULL)
data->totalMsTimeSpend = value.ull;
return true;
}
@@ -694,17 +760,28 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
memset(data, 0, sizeof(*data));
pmAtomValue value;
- if (PCPMetric_values(PCP_NET_RECVB, &value, 1, PM_TYPE_U64) != NULL)
+ if (Metric_values(PCP_NET_RECVB, &value, 1, PM_TYPE_U64) != NULL)
data->bytesReceived = value.ull;
- if (PCPMetric_values(PCP_NET_SENDB, &value, 1, PM_TYPE_U64) != NULL)
+ if (Metric_values(PCP_NET_SENDB, &value, 1, PM_TYPE_U64) != NULL)
data->bytesTransmitted = value.ull;
- if (PCPMetric_values(PCP_NET_RECVP, &value, 1, PM_TYPE_U64) != NULL)
+ if (Metric_values(PCP_NET_RECVP, &value, 1, PM_TYPE_U64) != NULL)
data->packetsReceived = value.ull;
- if (PCPMetric_values(PCP_NET_SENDP, &value, 1, PM_TYPE_U64) != NULL)
+ if (Metric_values(PCP_NET_SENDP, &value, 1, PM_TYPE_U64) != NULL)
data->packetsTransmitted = value.ull;
return true;
}
+void Platform_getFileDescriptors(double* used, double* max) {
+ *used = NAN;
+ *max = 65536;
+
+ pmAtomValue value;
+ if (Metric_values(PCP_VFS_FILES_COUNT, &value, 1, PM_TYPE_32) != NULL)
+ *used = value.l;
+ if (Metric_values(PCP_VFS_FILES_MAX, &value, 1, PM_TYPE_32) != NULL)
+ *max = value.l;
+}
+
void Platform_getBattery(double* level, ACPresence* isOnAC) {
*level = NAN;
*isOnAC = AC_ERROR;
@@ -725,7 +802,7 @@ CommandLineStatus Platform_getLongOption(int opt, ATTR_UNUSED int argc, char** a
case PLATFORM_LONGOPT_HOST: /* --host=HOSTSPEC */
if (argv[optind][0] == '\0')
return STATUS_ERROR_EXIT;
- __pmAddOptHost(&opts, optarg);
+ __pmAddOptHost(&opts, optarg);
return STATUS_OK;
case PLATFORM_LONGOPT_HOSTZONE: /* --hostzone */
@@ -751,6 +828,7 @@ CommandLineStatus Platform_getLongOption(int opt, ATTR_UNUSED int argc, char** a
default:
break;
}
+
return STATUS_ERROR_EXIT;
}
@@ -801,10 +879,10 @@ Hashtable* Platform_dynamicColumns(void) {
return pcp->columns.table;
}
-const char* Platform_dynamicColumnInit(unsigned int key) {
+const char* Platform_dynamicColumnName(unsigned int key) {
PCPDynamicColumn* this = Hashtable_get(pcp->columns.table, key);
if (this) {
- PCPMetric_enable(this->id, true);
+ Metric_enable(this->id, true);
if (this->super.caption)
return this->super.caption;
if (this->super.heading)
@@ -822,3 +900,25 @@ bool Platform_dynamicColumnWriteField(const Process* proc, RichString* str, unsi
}
return false;
}
+
+Hashtable* Platform_dynamicScreens(void) {
+ return pcp->screens.table;
+}
+
+void Platform_defaultDynamicScreens(Settings* settings) {
+ PCPDynamicScreen_appendScreens(&pcp->screens, settings);
+}
+
+void Platform_addDynamicScreen(ScreenSettings* ss) {
+ PCPDynamicScreen_addDynamicScreen(&pcp->screens, ss);
+}
+
+void Platform_addDynamicScreenAvailableColumns(Panel* availableColumns, const char* screen) {
+ Hashtable* screens = pcp->screens.table;
+ PCPDynamicScreens_addAvailableColumns(availableColumns, screens, screen);
+}
+
+void Platform_updateTables(Machine* host) {
+ PCPDynamicScreen_appendTables(&pcp->screens, host);
+ PCPDynamicColumns_setupWidths(&pcp->columns);
+}
diff --git a/pcp/Platform.h b/pcp/Platform.h
index dc51c73..f43ed54 100644
--- a/pcp/Platform.h
+++ b/pcp/Platform.h
@@ -36,9 +36,10 @@ in the source distribution for its full text.
#include "SignalsPanel.h"
#include "CommandLine.h"
+#include "pcp/Metric.h"
#include "pcp/PCPDynamicColumn.h"
#include "pcp/PCPDynamicMeter.h"
-#include "pcp/PCPMetric.h"
+#include "pcp/PCPDynamicScreen.h"
typedef struct Platform_ {
@@ -51,6 +52,7 @@ typedef struct Platform_ {
pmResult* result; /* sample values result indexed by Metric */
PCPDynamicMeters meters; /* dynamic meters via configuration files */
PCPDynamicColumns columns; /* dynamic columns via configuration files */
+ PCPDynamicScreens screens; /* dynamic screens 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 */
@@ -82,7 +84,7 @@ long long Platform_getBootTime(void);
unsigned int Platform_getMaxCPU(void);
-int Platform_getMaxPid(void);
+pid_t Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, int cpu);
@@ -98,8 +100,6 @@ void Platform_setZfsCompressedArcValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
-char* Platform_getInodeFilename(pid_t pid, ino_t inode);
-
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred);
@@ -131,7 +131,9 @@ CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv);
extern pmOptions opts;
-size_t Platform_addMetric(PCPMetric id, const char* name);
+size_t Platform_addMetric(Metric id, const char* name);
+
+void Platform_getFileDescriptors(double* used, double* max);
void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec);
@@ -151,8 +153,20 @@ Hashtable* Platform_dynamicColumns(void);
void Platform_dynamicColumnsDone(Hashtable* columns);
-const char* Platform_dynamicColumnInit(unsigned int key);
+const char* Platform_dynamicColumnName(unsigned int key);
bool Platform_dynamicColumnWriteField(const Process* proc, RichString* str, unsigned int key);
+Hashtable* Platform_dynamicScreens(void);
+
+void Platform_defaultDynamicScreens(Settings* settings);
+
+void Platform_addDynamicScreen(ScreenSettings* ss);
+
+void Platform_addDynamicScreenAvailableColumns(Panel* availableColumns, const char* screen);
+
+void Platform_dynamicScreensDone(Hashtable* screens);
+
+void Platform_updateTables(Machine* host);
+
#endif
diff --git a/pcp/ProcessField.h b/pcp/ProcessField.h
index b3ba265..6342561 100644
--- a/pcp/ProcessField.h
+++ b/pcp/ProcessField.h
@@ -45,6 +45,9 @@ in the source distribution for its full text.
SECATTR = 123, \
AUTOGROUP_ID = 127, \
AUTOGROUP_NICE = 128, \
+ CCGROUP = 129, \
+ CONTAINER = 130, \
+ M_PRIV = 131, \
// End of list
diff --git a/pcp/screens/biosnoop b/pcp/screens/biosnoop
new file mode 100644
index 0000000..e6cdf89
--- /dev/null
+++ b/pcp/screens/biosnoop
@@ -0,0 +1,41 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[biosnoop]
+heading = BioSnoop
+caption = BPF block I/O snoop
+default = false
+
+pid.heading = PID
+pid.caption = Process identifier
+pid.metric = bpf.biosnoop.pid
+pid.format = process
+
+disk.heading = DISK
+disk.caption = Device name
+disk.width = -7
+disk.metric = bpf.biosnoop.disk
+
+rwbs.heading = TYPE
+rwbs.caption = I/O type string
+rwbs.width = -4
+rwbs.metric = bpf.biosnoop.rwbs
+
+bytes.heading = BYTES
+bytes.caption = I/O size in bytes
+bytes.metric = bpf.biosnoop.bytes
+
+lat.heading = LAT
+lat.caption = I/O latency
+lat.metric = bpf.biosnoop.lat
+
+sector.heading = SECTOR
+sector.caption = Device sector
+sector.metric = bpf.biosnoop.sector
+
+comm.heading = Command
+comm.caption = Process command name
+comm.width = -16
+comm.metric = bpf.biosnoop.comm
+comm.format = process
diff --git a/pcp/screens/cgroups b/pcp/screens/cgroups
new file mode 100644
index 0000000..0ddc65c
--- /dev/null
+++ b/pcp/screens/cgroups
@@ -0,0 +1,45 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[cgroups]
+heading = CGroups
+caption = Control Groups
+default = true
+
+user_cpu.heading = UTIME
+user_cpu.caption = User CPU Time
+user_cpu.metric = 1000 * rate(cgroup.cpu.stat.user)
+user_cpu.width = 6
+
+system_cpu.heading = STIME
+system_cpu.caption = Kernel CPU Time
+system_cpu.metric = 1000 * rate(cgroup.cpu.stat.system)
+system_cpu.width = 6
+
+cpu_usage.heading = CPU%
+cpu_usage.caption = CPU Utilization
+cpu_usage.metric = 100 * (rate(cgroup.cpu.stat.usage) / hinv.ncpu)
+cpu_usage.format = percent
+
+cpu_psi.heading = CPU-PSI
+cpu_psi.caption = CPU Pressure Stall Information
+cpu_psi.metric = 1000 * rate(cgroup.pressure.cpu.some.total)
+cpu_psi.width = 7
+
+mem_psi.heading = MEM-PSI
+mem_psi.caption = Memory Pressure Stall Information
+mem_psi.metric = 1000 * rate(cgroup.pressure.memory.some.total)
+mem_psi.width = 7
+
+io_psi.heading = I/O-PSI
+io_psi.caption = I/O Pressure Stall Information
+io_psi.metric = 1000 * rate(cgroup.pressure.io.some.total)
+io_psi.width = 7
+
+name.heading = Control group
+name.caption = Control group name
+name.width = -64
+name.metric = cgroup.cpu.stat.system
+name.instances = true
+name.format = cgroup
diff --git a/pcp/screens/cgroupsio b/pcp/screens/cgroupsio
new file mode 100644
index 0000000..3a431db
--- /dev/null
+++ b/pcp/screens/cgroupsio
@@ -0,0 +1,49 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[cgroupsio]
+heading = CGroupsIO
+caption = Control Groups I/O
+default = false
+
+iops.heading = IOPS
+iops.caption = I/O operations
+iops.metric = rate(cgroup.io.stat.rios) + rate(cgroup.io.stat.wios) + rate(cgroup.io.stat.dios)
+
+readops.heading = RDIO
+readops.caption = Read operations
+readops.metric = rate(cgroup.io.stat.rios)
+readops.default = false
+
+writeops.heading = WRIO
+writeops.caption = Write operations
+writeops.metric = rate(cgroup.io.stat.wios)
+writeops.default = false
+
+directops.heading = DIO
+directops.caption = Direct I/O operations
+directops.metric = rate(cgroup.io.stat.dios)
+directops.default = false
+
+totalbytes.heading = R/W/D
+totalbytes.caption = Disk throughput
+totalbytes.metric = rate(cgroup.io.stat.rbytes) + rate(cgroup.io.stat.wbytes) + rate(cgroup.io.stat.dbytes)
+
+readbytes.heading = RBYTE
+readbytes.caption = Disk read throughput
+readbytes.metric = rate(cgroup.io.stat.rbytes)
+
+writebytes.heading = WBYTE
+writebytes.caption = Disk throughput
+writebytes.metric = rate(cgroup.io.stat.wbytes)
+
+directio.heading = DBYTE
+directio.caption = Direct I/O throughput
+directio.metric = rate(cgroup.io.stat.dbytes)
+
+name.heading = Control group device
+name.caption = Control group device
+name.width = -64
+name.metric = cgroup.io.stat.rbytes
+name.instances = true
diff --git a/pcp/screens/cgroupsmem b/pcp/screens/cgroupsmem
new file mode 100644
index 0000000..17bc1e3
--- /dev/null
+++ b/pcp/screens/cgroupsmem
@@ -0,0 +1,48 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[cgroupsmem]
+heading = CGroupsMem
+caption = Control Groups Memory
+default = false
+
+current.heading = MEM
+current.caption = Current memory
+current.metric = cgroup.memory.current
+
+usage.heading = USAGE
+usage.caption = Memory usage
+usage.metric = cgroup.memory.usage
+
+container.heading = CONTAINER
+container.caption = Container Name
+container.metric = cgroup.memory.id.container
+
+resident.heading = RSS
+resident.metric = cgroup.memory.stat.rss
+
+cresident.heading = CRSS
+cresident.metric = cgroup.memory.stat.total.rss
+
+anonmem.heading = ANON
+anonmem.metric = cgroup.memory.stat.anon
+
+filemem.heading = FILE
+filemem.metric = cgroup.memory.stat.file
+
+shared.heading = SHMEM
+shared.metric = cgroup.memory.stat.shmem
+
+swap.heading = SWAP
+swap.metric = cgroup.memory.stat.swap
+
+pgfault.heading = FAULTS
+pgfault.metric = cgroup.memory.stat.pgfaults
+
+name.heading = Control group
+name.caption = Control group name
+name.width = -64
+name.metric = cgroup.memory.current
+name.instances = true
+name.format = cgroup
diff --git a/pcp/screens/devices b/pcp/screens/devices
new file mode 100644
index 0000000..7399f82
--- /dev/null
+++ b/pcp/screens/devices
@@ -0,0 +1,114 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[disks]
+heading = Disks
+caption = Disk devices
+
+diskdev.heading = Device
+diskdev.metric = disk.dev.read
+diskdev.instances = true
+diskdev.format = device
+diskdev.width = -8
+
+total.heading = TPS
+total.metric = rate(disk.dev.read) + rate(disk.dev.write) + rate(disk.dev.discard)
+total.caption = Rate of read requests
+
+read.heading = RR/S
+read.metric = rate(disk.dev.read)
+read.caption = Rate of read requests
+
+read_bytes.heading = RRB/S
+read_bytes.metric = rate(disk.dev.read_bytes)
+read_bytes.caption = Read throughput from the device
+
+read_merge.heading = RRQM/S
+read_merge.metric = rate(disk.dev.read_merge)
+read_merge.caption = Rate reads merged before queued
+read_merge.default = false
+
+read_merge_pct.heading = RRQM%
+read_merge_pct.metric = 100 * rate(disk.dev.read_merge) / rate(disk.dev.read)
+read_merge_pct.caption = Percentage reads merged before queued
+read_merge_pct.format = percent
+
+read_await.heading = RAWAIT
+read_await.metric = disk.dev.r_await
+read_await.default = false
+
+read_avqsz.heading = RARQSZ
+read_avqsz.metric = disk.dev.r_avg_rqsz
+read_avqsz.default = false
+
+write.heading = WR/S
+write.metric = rate(disk.dev.write)
+write.caption = Rate of write requests
+
+write_bytes.heading = WRB/S
+write_bytes.metric = rate(disk.dev.write_bytes)
+write_bytes.caption = Write throughput to the device
+
+write_merge.heading = WRQM/S
+write_merge.metric = rate(disk.dev.write_merge)
+write_merge.caption = Rate writes merged before queued
+write_merge.default = false
+
+write_merge_pct.heading = WRQM%
+write_merge_pct.metric = 100 * rate(disk.dev.write_merge) / rate(disk.dev.write)
+write_merge_pct.caption = Percentage writes merged before queued
+write_merge_pct.format = percent
+
+write_await.heading = WAWAIT
+write_await.metric = disk.dev.w_await
+write_await.default = false
+
+write_avqsz.heading = WARQSZ
+write_avqsz.metric = disk.dev.w_avg_rqsz
+write_avqsz.default = false
+
+discard.heading = DR/S
+discard.metric = rate(disk.dev.discard)
+discard.caption = Rate of discard requests
+
+discard_bytes.heading = DRB/S
+discard_bytes.metric = rate(disk.dev.discard_bytes)
+discard_bytes.caption = Discard request throughput
+discard_bytes.default = false
+
+discard_merge.heading = DRQM/S
+discard_merge.metric = rate(disk.dev.discard_merge)
+discard_merge.caption = Rate discards merged before queued
+discard_merge.default = false
+
+discard_merge_pct.heading = DRQM%
+discard_merge_pct.metric = 100 * rate(disk.dev.discard_merge) / rate(disk.dev.discard)
+discard_merge_pct.caption = Percentage discards merged before queued
+discard_merge_pct.format = percent
+discard_merge_pct.default = false
+
+discard_await.heading = DAWAIT
+discard_await.metric = disk.dev.d_await
+discard_await.default = false
+
+discard_avqsz.heading = DARQSZ
+discard_avqsz.metric = disk.dev.d_avg_rqsz
+discard_avqsz.default = false
+
+flush.heading = F/S
+flush.metric = rate(disk.dev.flush)
+flush.default = false
+flush.caption = Flushes per second
+
+flush_await.heading = FAWAIT
+flush_await.metric = disk.dev.f_await
+flush_await.default = false
+
+qlen.heading = AQU-SZ
+qlen.metric = disk.dev.avg_qlen
+
+util.heading = UTIL%
+util.metric = 100 * disk.dev.util
+util.caption = Perentage device utilization
+util.format = percent
diff --git a/pcp/screens/execsnoop b/pcp/screens/execsnoop
new file mode 100644
index 0000000..d706e76
--- /dev/null
+++ b/pcp/screens/execsnoop
@@ -0,0 +1,37 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[execsnoop]
+heading = ExecSnoop
+caption = BPF exec(2) syscall snoop
+default = false
+
+pid.heading = PID
+pid.caption = Process Identifier
+pid.metric = bpf.execsnoop.pid
+pid.format = process
+
+ppid.heading = PPID
+ppid.caption = Parent Process
+ppid.metric = bpf.execsnoop.ppid
+ppid.format = process
+
+uid.heading = UID
+uid.caption = User Identifier
+uid.metric = bpf.execsnoop.uid
+
+comm.heading = COMM
+comm.caption = Command
+comm.width = -16
+comm.metric = bpf.execsnoop.comm
+comm.format = command
+
+ret.heading = RET
+ret.caption = Return Code
+ret.metric = bpf.execsnoop.ret
+
+path.heading = Arguments
+path.caption = Arguments
+path.width = -12
+path.metric = bpf.execsnoop.args
diff --git a/pcp/screens/exitsnoop b/pcp/screens/exitsnoop
new file mode 100644
index 0000000..6c6b867
--- /dev/null
+++ b/pcp/screens/exitsnoop
@@ -0,0 +1,48 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[exitsnoop]
+heading = ExitSnoop
+caption = BPF process exit(2) snoop
+default = false
+
+pid.heading = PID
+pid.caption = Process Identifier
+pid.metric = bpf.exitsnoop.pid
+pid.format = process
+
+ppid.heading = PPID
+ppid.caption = Parent Process
+ppid.metric = bpf.exitsnoop.ppid
+ppid.format = process
+
+tid.heading = TID
+tid.caption = Task Identifier
+tid.metric = bpf.exitsnoop.tid
+tid.format = process
+tid.default = false
+
+signal.heading = SIG
+signal.caption = Signal number
+signal.metric = bpf.exitsnoop.sig
+
+exit.heading = EXIT
+exit.caption = Exit Code
+exit.metric = bpf.exitsnoop.exit_code
+
+core.heading = CORE
+core.caption = Dumped core
+core.metric = bpf.exitsnoop.coredump
+core.default = false
+
+age.heading = AGE
+age.caption = Process age
+age.metric = bpf.exitsnoop.age
+age.default = false
+
+comm.heading = Command
+comm.caption = COMM
+comm.width = -16
+comm.metric = bpf.exitsnoop.comm
+comm.format = command
diff --git a/pcp/screens/filesystems b/pcp/screens/filesystems
new file mode 100644
index 0000000..06f3bf2
--- /dev/null
+++ b/pcp/screens/filesystems
@@ -0,0 +1,50 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[filesystems]
+heading = Filesystems
+caption = Mounted block device filesystems
+
+blockdev.heading = Device
+blockdev.metric = filesys.mountdir
+blockdev.instances = true
+blockdev.width = -14
+
+blocksize.heading = BSIZE
+blocksize.metric = filesys.blocksize
+blocksize.default = false
+
+capacity.heading = SIZE
+capacity.metric = filesys.capacity
+
+used.heading = USED
+used.metric = filesys.used
+
+free.heading = FREE
+free.metric = filesys.free
+free.default = false
+
+avail.heading = AVAIL
+avail.metric = filesys.avail
+
+full.heading = USE%
+full.metric = filesys.full
+full.format = percent
+
+usedfiles.heading = USEDF
+usedfiles.metric = filesys.usedfiles
+usedfiles.default = false
+
+freefiles.heading = FREEF
+freefiles.metric = filesys.freefiles
+freefiles.default = false
+
+maxfiles.heading = MAXF
+maxfiles.metric = filesys.maxfiles
+maxfiles.default = false
+
+mountdir.heading = Mount point
+mountdir.metric = filesys.mountdir
+mountdir.format = path
+mountdir.width = -33
diff --git a/pcp/screens/opensnoop b/pcp/screens/opensnoop
new file mode 100644
index 0000000..ec209b0
--- /dev/null
+++ b/pcp/screens/opensnoop
@@ -0,0 +1,27 @@
+#
+# pcp-htop(1) configuration file - see pcp-htop(5)
+#
+
+[opensnoop]
+heading = OpenSnoop
+caption = BPF open(2) syscall snoop
+default = false
+
+pid.heading = PID
+pid.metric = bpf.opensnoop.pid
+pid.format = process
+
+comm.heading = COMM
+comm.metric = bpf.opensnoop.comm
+comm.format = command
+
+fd.heading = FD
+fd.metric = bpf.opensnoop.fd
+
+err.heading = ERR
+err.metric = bpf.opensnoop.err
+
+file.heading = File name
+file.width = -32
+file.metric = bpf.opensnoop.fname
+file.format = path

© 2014-2024 Faster IT GmbH | imprint | privacy policy