From e7372d18a1a661d8c3dba9f51e1f17b5f94171a7 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Wed, 10 Jan 2024 11:17:08 +0100 Subject: New upstream version 3.3.0 --- pcp/InDomTable.c | 99 +++++++ pcp/InDomTable.h | 34 +++ pcp/Instance.c | 163 +++++++++++ pcp/Instance.h | 37 +++ pcp/Metric.c | 200 +++++++++++++ pcp/Metric.h | 189 +++++++++++++ pcp/PCPDynamicColumn.c | 275 ++++++++++++++---- pcp/PCPDynamicColumn.h | 21 +- pcp/PCPDynamicMeter.c | 47 ++-- pcp/PCPDynamicMeter.h | 6 + pcp/PCPDynamicScreen.c | 407 +++++++++++++++++++++++++++ pcp/PCPDynamicScreen.h | 56 ++++ pcp/PCPMachine.c | 345 +++++++++++++++++++++++ pcp/PCPMachine.h | 71 +++++ pcp/PCPMetric.c | 180 ------------ pcp/PCPMetric.h | 180 ------------ pcp/PCPProcess.c | 155 ++++++----- pcp/PCPProcess.h | 9 +- pcp/PCPProcessList.c | 727 ------------------------------------------------ pcp/PCPProcessList.h | 74 ----- pcp/PCPProcessTable.c | 486 ++++++++++++++++++++++++++++++++ pcp/PCPProcessTable.h | 24 ++ pcp/Platform.c | 334 ++++++++++++++-------- pcp/Platform.h | 26 +- pcp/ProcessField.h | 3 + pcp/screens/biosnoop | 41 +++ pcp/screens/cgroups | 45 +++ pcp/screens/cgroupsio | 49 ++++ pcp/screens/cgroupsmem | 48 ++++ pcp/screens/devices | 114 ++++++++ pcp/screens/execsnoop | 37 +++ pcp/screens/exitsnoop | 48 ++++ pcp/screens/filesystems | 50 ++++ pcp/screens/opensnoop | 27 ++ 34 files changed, 3183 insertions(+), 1424 deletions(-) create mode 100644 pcp/InDomTable.c create mode 100644 pcp/InDomTable.h create mode 100644 pcp/Instance.c create mode 100644 pcp/Instance.h create mode 100644 pcp/Metric.c create mode 100644 pcp/Metric.h create mode 100644 pcp/PCPDynamicScreen.c create mode 100644 pcp/PCPDynamicScreen.h create mode 100644 pcp/PCPMachine.c create mode 100644 pcp/PCPMachine.h delete mode 100644 pcp/PCPMetric.c delete mode 100644 pcp/PCPMetric.h delete mode 100644 pcp/PCPProcessList.c delete mode 100644 pcp/PCPProcessList.h create mode 100644 pcp/PCPProcessTable.c create mode 100644 pcp/PCPProcessTable.h create mode 100644 pcp/screens/biosnoop create mode 100644 pcp/screens/cgroups create mode 100644 pcp/screens/cgroupsio create mode 100644 pcp/screens/cgroupsmem create mode 100644 pcp/screens/devices create mode 100644 pcp/screens/execsnoop create mode 100644 pcp/screens/exitsnoop create mode 100644 pcp/screens/filesystems create mode 100644 pcp/screens/opensnoop (limited to 'pcp') 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 +#include +#include + +#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 +#include + +#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 +#include + +#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/Metric.c b/pcp/Metric.c new file mode 100644 index 0000000..4a3c858 --- /dev/null +++ b/pcp/Metric.c @@ -0,0 +1,200 @@ +/* +htop - Metric.c +(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/Metric.h" + +#include +#include +#include +#include + +#include "XUtils.h" + +#include "pcp/Platform.h" + + +extern Platform* pcp; + +const pmDesc* Metric_desc(Metric metric) { + return &pcp->descs[metric]; +} + +int Metric_type(Metric metric) { + return pcp->descs[metric].type; +} + +pmAtomValue* Metric_values(Metric metric, pmAtomValue* atom, int count, int type) { + if (pcp->result == NULL) + return NULL; + + pmValueSet* vset = pcp->result->vset[metric]; + if (!vset || vset->numval <= 0) + return NULL; + + /* extract requested number of values as requested type */ + const pmDesc* desc = &pcp->descs[metric]; + for (int i = 0; i < vset->numval; i++) { + if (i == count) + break; + const pmValue* value = &vset->vlist[i]; + int sts = pmExtractValue(vset->valfmt, value, desc->type, &atom[i], type); + if (sts < 0) { + if (pmDebugOptions.appl0) + fprintf(stderr, "Error: cannot extract metric value: %s\n", + pmErrStr(sts)); + memset(&atom[i], 0, sizeof(pmAtomValue)); + } + } + return atom; +} + +int Metric_instanceCount(Metric metric) { + pmValueSet* vset = pcp->result->vset[metric]; + if (vset) + return vset->numval; + return 0; +} + +int Metric_instanceOffset(Metric metric, int inst) { + pmValueSet* vset = pcp->result->vset[metric]; + if (!vset || vset->numval <= 0) + return 0; + + /* search for optimal offset for subsequent inst lookups to begin */ + for (int i = 0; i < vset->numval; i++) { + if (inst == vset->vlist[i].inst) + return i; + } + return 0; +} + +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]; + const pmValue* value = &vset->vlist[offset]; + int sts = pmExtractValue(vset->valfmt, value, desc->type, atom, type); + if (sts < 0) { + if (pmDebugOptions.appl0) + fprintf(stderr, "Error: cannot extract %s instance %d value: %s\n", + pcp->names[metric], inst, pmErrStr(sts)); + memset(atom, 0, sizeof(pmAtomValue)); + } + return atom; +} + +pmAtomValue* Metric_instance(Metric metric, int inst, int offset, pmAtomValue* atom, int type) { + + pmValueSet* vset = pcp->result->vset[metric]; + if (!vset || vset->numval <= 0) + return NULL; + + /* fast-path using heuristic offset based on expected location */ + if (offset >= 0 && offset < vset->numval && inst == vset->vlist[offset].inst) + 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 Metric_extract(metric, inst, i, vset, atom, type); + } + return NULL; +} + +/* + * Iterate over a set of instances (incl PM_IN_NULL) + * returning the next instance identifier and offset. + * + * Start it off by passing offset -1 into the routine. + */ +bool Metric_iterate(Metric metric, int* instp, int* offsetp) { + if (!pcp->result) + return false; + + pmValueSet* vset = pcp->result->vset[metric]; + if (!vset || vset->numval <= 0) + return false; + + int offset = *offsetp; + offset = (offset < 0) ? 0 : offset + 1; + if (offset > vset->numval - 1) + return false; + + *offsetp = offset; + *instp = vset->vlist[offset].inst; + return true; +} + +/* Switch on/off a metric for value fetching (sampling) */ +void Metric_enable(Metric metric, bool enable) { + pcp->fetch[metric] = enable ? pcp->pmids[metric] : PM_ID_NULL; +} + +bool Metric_enabled(Metric metric) { + return pcp->fetch[metric] != PM_ID_NULL; +} + +void Metric_enableThreads(void) { + pmValueSet* vset = xCalloc(1, sizeof(pmValueSet)); + vset->vlist[0].inst = PM_IN_NULL; + vset->vlist[0].value.lval = 1; + vset->valfmt = PM_VAL_INSITU; + vset->numval = 1; + vset->pmid = pcp->pmids[PCP_CONTROL_THREADS]; + + pmResult* result = xCalloc(1, sizeof(pmResult)); + result->vset[0] = vset; + result->numpmid = 1; + + int sts = pmStore(result); + if (sts < 0 && pmDebugOptions.appl0) + fprintf(stderr, "Error: cannot enable threads: %s\n", pmErrStr(sts)); + + pmFreeResult(result); +} + +bool Metric_fetch(struct timeval* timestamp) { + if (pcp->result) { + pmFreeResult(pcp->result); + pcp->result = NULL; + } + int sts, count = 0; + do { + sts = pmFetch(pcp->totalMetrics, pcp->fetch, &pcp->result); + } while (sts == PM_ERR_IPC && ++count < 3); + if (sts < 0) { + if (pmDebugOptions.appl0) + fprintf(stderr, "Error: cannot fetch metric values: %s\n", + pmErrStr(sts)); + return false; + } + if (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/Metric.h b/pcp/Metric.h new file mode 100644 index 0000000..e72f6e1 --- /dev/null +++ b/pcp/Metric.h @@ -0,0 +1,189 @@ +#ifndef HEADER_Metric +#define HEADER_Metric +/* +htop - Metric.h +(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 +#include +#include +#include + +/* use htop config.h values for these macros, not pcp values */ +#undef PACKAGE_URL +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#undef PACKAGE_BUGREPORT + + +typedef enum Metric_ { + PCP_CONTROL_THREADS, /* proc.control.perclient.threads */ + + PCP_HINV_NCPU, /* hinv.ncpu */ + PCP_HINV_CPUCLOCK, /* hinv.cpu.clock */ + PCP_UNAME_SYSNAME, /* kernel.uname.sysname */ + PCP_UNAME_RELEASE, /* kernel.uname.release */ + PCP_UNAME_MACHINE, /* kernel.uname.machine */ + PCP_UNAME_DISTRO, /* kernel.uname.distro */ + PCP_LOAD_AVERAGE, /* kernel.all.load */ + PCP_PID_MAX, /* kernel.all.pid_max */ + PCP_UPTIME, /* kernel.all.uptime */ + PCP_BOOTTIME, /* kernel.all.boottime */ + PCP_CPU_USER, /* kernel.all.cpu.user */ + PCP_CPU_NICE, /* kernel.all.cpu.nice */ + PCP_CPU_SYSTEM, /* kernel.all.cpu.sys */ + PCP_CPU_IDLE, /* kernel.all.cpu.idle */ + PCP_CPU_IOWAIT, /* kernel.all.cpu.wait.total */ + PCP_CPU_IRQ, /* kernel.all.cpu.intr */ + PCP_CPU_SOFTIRQ, /* kernel.all.cpu.irq.soft */ + PCP_CPU_STEAL, /* kernel.all.cpu.steal */ + PCP_CPU_GUEST, /* kernel.all.cpu.guest */ + PCP_CPU_GUESTNICE, /* kernel.all.cpu.guest_nice */ + PCP_PERCPU_USER, /* kernel.percpu.cpu.user */ + PCP_PERCPU_NICE, /* kernel.percpu.cpu.nice */ + PCP_PERCPU_SYSTEM, /* kernel.percpu.cpu.sys */ + PCP_PERCPU_IDLE, /* kernel.percpu.cpu.idle */ + PCP_PERCPU_IOWAIT, /* kernel.percpu.cpu.wait.total */ + PCP_PERCPU_IRQ, /* kernel.percpu.cpu.intr */ + PCP_PERCPU_SOFTIRQ, /* kernel.percpu.cpu.irq.soft */ + PCP_PERCPU_STEAL, /* kernel.percpu.cpu.steal */ + PCP_PERCPU_GUEST, /* kernel.percpu.cpu.guest */ + PCP_PERCPU_GUESTNICE, /* kernel.percpu.cpu.guest_nice */ + PCP_MEM_TOTAL, /* mem.physmem */ + PCP_MEM_FREE, /* mem.util.free */ + PCP_MEM_BUFFERS, /* mem.util.bufmem */ + PCP_MEM_CACHED, /* mem.util.cached */ + PCP_MEM_SHARED, /* mem.util.shared */ + PCP_MEM_AVAILABLE, /* mem.util.available */ + PCP_MEM_SRECLAIM, /* mem.util.slabReclaimable */ + PCP_MEM_SWAPCACHED, /* mem.util.swapCached */ + PCP_MEM_SWAPTOTAL, /* mem.util.swapTotal */ + PCP_MEM_SWAPFREE, /* mem.util.swapFree */ + PCP_DISK_READB, /* disk.all.read_bytes */ + PCP_DISK_WRITEB, /* disk.all.write_bytes */ + PCP_DISK_ACTIVE, /* disk.all.avactive */ + PCP_NET_RECVB, /* network.all.in.bytes */ + PCP_NET_SENDB, /* network.all.out.bytes */ + PCP_NET_RECVP, /* network.all.in.packets */ + PCP_NET_SENDP, /* network.all.out.packets */ + 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 */ + PCP_ZFS_ARC_BONUS_SIZE, /* zfs.arc.bonus_size */ + PCP_ZFS_ARC_COMPRESSED_SIZE, /* zfs.arc.compressed_size */ + PCP_ZFS_ARC_UNCOMPRESSED_SIZE, /* zfs.arc.uncompressed_size */ + PCP_ZFS_ARC_C_MIN, /* zfs.arc.c_min */ + PCP_ZFS_ARC_C_MAX, /* zfs.arc.c_max */ + PCP_ZFS_ARC_DBUF_SIZE, /* zfs.arc.dbuf_size */ + PCP_ZFS_ARC_DNODE_SIZE, /* zfs.arc.dnode_size */ + PCP_ZFS_ARC_HDR_SIZE, /* zfs.arc.hdr_size */ + PCP_ZFS_ARC_MFU_SIZE, /* zfs.arc.mfu_size */ + PCP_ZFS_ARC_MRU_SIZE, /* zfs.arc.mru_size */ + PCP_ZFS_ARC_SIZE, /* zfs.arc.size */ + 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 */ + PCP_PROC_TGID, /* proc.psinfo.tgid */ + PCP_PROC_PGRP, /* proc.psinfo.pgrp */ + PCP_PROC_SESSION, /* proc.psinfo.session */ + PCP_PROC_STATE, /* proc.psinfo.sname */ + PCP_PROC_TTY, /* proc.psinfo.tty */ + PCP_PROC_TTYPGRP, /* proc.psinfo.tty_pgrp */ + PCP_PROC_MINFLT, /* proc.psinfo.minflt */ + PCP_PROC_MAJFLT, /* proc.psinfo.maj_flt */ + PCP_PROC_CMINFLT, /* proc.psinfo.cmin_flt */ + PCP_PROC_CMAJFLT, /* proc.psinfo.cmaj_flt */ + PCP_PROC_UTIME, /* proc.psinfo.utime */ + PCP_PROC_STIME, /* proc.psinfo.stime */ + PCP_PROC_CUTIME, /* proc.psinfo.cutime */ + PCP_PROC_CSTIME, /* proc.psinfo.cstime */ + PCP_PROC_PRIORITY, /* proc.psinfo.priority */ + PCP_PROC_NICE, /* proc.psinfo.nice */ + PCP_PROC_THREADS, /* proc.psinfo.threads */ + PCP_PROC_STARTTIME, /* proc.psinfo.start_time */ + PCP_PROC_PROCESSOR, /* proc.psinfo.processor */ + PCP_PROC_CMD, /* proc.psinfo.cmd */ + PCP_PROC_PSARGS, /* proc.psinfo.psargs */ + PCP_PROC_CGROUPS, /* proc.psinfo.cgroups */ + PCP_PROC_OOMSCORE, /* proc.psinfo.oom_score */ + PCP_PROC_VCTXSW, /* proc.psinfo.vctxsw */ + PCP_PROC_NVCTXSW, /* proc.psinfo.nvctxsw */ + PCP_PROC_LABELS, /* proc.psinfo.labels */ + PCP_PROC_ENVIRON, /* proc.psinfo.environ */ + PCP_PROC_TTYNAME, /* proc.psinfo.ttyname */ + PCP_PROC_EXE, /* proc.psinfo.exe */ + PCP_PROC_CWD, /* proc.psinfo.cwd */ + + PCP_PROC_AUTOGROUP_ID, /* proc.autogroup.id */ + PCP_PROC_AUTOGROUP_NICE, /* proc.autogroup.nice */ + + PCP_PROC_ID_UID, /* proc.id.uid */ + PCP_PROC_ID_USER, /* proc.id.uid_nm */ + + PCP_PROC_IO_RCHAR, /* proc.io.rchar */ + PCP_PROC_IO_WCHAR, /* proc.io.wchar */ + PCP_PROC_IO_SYSCR, /* proc.io.syscr */ + PCP_PROC_IO_SYSCW, /* proc.io.syscw */ + PCP_PROC_IO_READB, /* proc.io.read_bytes */ + PCP_PROC_IO_WRITEB, /* proc.io.write_bytes */ + PCP_PROC_IO_CANCELLED, /* proc.io.cancelled_write_bytes */ + + PCP_PROC_MEM_SIZE, /* proc.memory.size */ + PCP_PROC_MEM_RSS, /* proc.memory.rss */ + PCP_PROC_MEM_SHARE, /* proc.memory.share */ + PCP_PROC_MEM_TEXTRS, /* proc.memory.textrss */ + PCP_PROC_MEM_LIBRS, /* proc.memory.librss */ + PCP_PROC_MEM_DATRS, /* proc.memory.datrss */ + PCP_PROC_MEM_DIRTY, /* proc.memory.dirty */ + + PCP_PROC_SMAPS_PSS, /* proc.smaps.pss */ + PCP_PROC_SMAPS_SWAP, /* proc.smaps.swap */ + PCP_PROC_SMAPS_SWAPPSS, /* proc.smaps.swappss */ + + PCP_METRIC_COUNT /* total metric count */ +} Metric; + +void Metric_enable(Metric metric, bool enable); + +bool Metric_enabled(Metric metric); + +void Metric_enableThreads(void); + +bool Metric_fetch(struct timeval* timestamp); + +bool Metric_iterate(Metric metric, int* instp, int* offsetp); + +pmAtomValue* Metric_values(Metric metric, pmAtomValue* atom, int count, int type); + +const pmDesc* Metric_desc(Metric metric); + +int Metric_type(Metric metric); + +int Metric_instanceCount(Metric metric); + +int Metric_instanceOffset(Metric metric, int inst); + +pmAtomValue* Metric_instance(Metric metric, int inst, int offset, pmAtomValue* atom, int type); + +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 #include #include +#include #include #include #include @@ -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 @@ -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 #include #include -#include +#include #include #include #include #include +#include + #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 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 +#include +#include +#include + +#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(¬e, + "%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 +#include + +#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 +#include +#include +#include +#include +#include + +#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(×tamp) != true) + return; + + double sample = host->timestamp; + host->timestamp = pmtimevalToReal(×tamp); + 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(×tamp, NULL); + this->timestamp = pmtimevalToReal(×tamp); + + 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/PCPMachine.h b/pcp/PCPMachine.h new file mode 100644 index 0000000..6518bd4 --- /dev/null +++ b/pcp/PCPMachine.h @@ -0,0 +1,71 @@ +#ifndef HEADER_PCPMachine +#define HEADER_PCPMachine +/* +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 +#include + +#include "Hashtable.h" +#include "Machine.h" +#include "UsersTable.h" + +#include "pcp/Platform.h" +#include "linux/ZswapStats.h" +#include "zfs/ZfsArcStats.h" + + +typedef enum CPUMetric_ { + CPU_TOTAL_TIME, + CPU_USER_TIME, + CPU_SYSTEM_TIME, + CPU_SYSTEM_ALL_TIME, + CPU_IDLE_ALL_TIME, + CPU_IDLE_TIME, + CPU_NICE_TIME, + CPU_IOWAIT_TIME, + CPU_IRQ_TIME, + CPU_SOFTIRQ_TIME, + CPU_STEAL_TIME, + CPU_GUEST_TIME, + CPU_GUESTNICE_TIME, + + CPU_TOTAL_PERIOD, + CPU_USER_PERIOD, + CPU_SYSTEM_PERIOD, + CPU_SYSTEM_ALL_PERIOD, + CPU_IDLE_ALL_PERIOD, + CPU_IDLE_PERIOD, + CPU_NICE_PERIOD, + CPU_IOWAIT_PERIOD, + CPU_IRQ_PERIOD, + CPU_SOFTIRQ_PERIOD, + CPU_STEAL_PERIOD, + CPU_GUEST_PERIOD, + CPU_GUESTNICE_PERIOD, + + CPU_FREQUENCY, + + CPU_METRIC_COUNT +} CPUMetric; + +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; + /*ZramStats zram; -- not needed, calculated in-line in Platform.c */ + ZswapStats zswap; +} PCPMachine; + +#endif diff --git a/pcp/PCPMetric.c b/pcp/PCPMetric.c deleted file mode 100644 index 606a5df..0000000 --- a/pcp/PCPMetric.c +++ /dev/null @@ -1,180 +0,0 @@ -/* -htop - PCPMetric.c -(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/PCPMetric.h" - -#include -#include -#include - -#include "XUtils.h" - -#include "pcp/Platform.h" - - -extern Platform* pcp; - -const pmDesc* PCPMetric_desc(PCPMetric metric) { - return &pcp->descs[metric]; -} - -int PCPMetric_type(PCPMetric metric) { - return pcp->descs[metric].type; -} - -pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type) { - if (pcp->result == NULL) - return NULL; - - pmValueSet* vset = pcp->result->vset[metric]; - if (!vset || vset->numval <= 0) - return NULL; - - /* extract requested number of values as requested type */ - const pmDesc* desc = &pcp->descs[metric]; - for (int i = 0; i < vset->numval; i++) { - if (i == count) - break; - const pmValue* value = &vset->vlist[i]; - int sts = pmExtractValue(vset->valfmt, value, desc->type, &atom[i], type); - if (sts < 0) { - if (pmDebugOptions.appl0) - fprintf(stderr, "Error: cannot extract metric value: %s\n", - pmErrStr(sts)); - memset(&atom[i], 0, sizeof(pmAtomValue)); - } - } - return atom; -} - -int PCPMetric_instanceCount(PCPMetric metric) { - pmValueSet* vset = pcp->result->vset[metric]; - if (vset) - return vset->numval; - return 0; -} - -int PCPMetric_instanceOffset(PCPMetric metric, int inst) { - pmValueSet* vset = pcp->result->vset[metric]; - if (!vset || vset->numval <= 0) - return 0; - - /* search for optimal offset for subsequent inst lookups to begin */ - for (int i = 0; i < vset->numval; i++) { - if (inst == vset->vlist[i].inst) - return i; - } - return 0; -} - -static pmAtomValue* PCPMetric_extract(PCPMetric 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]; - const pmValue* value = &vset->vlist[offset]; - int sts = pmExtractValue(vset->valfmt, value, desc->type, atom, type); - if (sts < 0) { - if (pmDebugOptions.appl0) - fprintf(stderr, "Error: cannot extract %s instance %d value: %s\n", - pcp->names[metric], inst, pmErrStr(sts)); - memset(atom, 0, sizeof(pmAtomValue)); - } - return atom; -} - -pmAtomValue* PCPMetric_instance(PCPMetric metric, int inst, int offset, pmAtomValue* atom, int type) { - - pmValueSet* vset = pcp->result->vset[metric]; - if (!vset || vset->numval <= 0) - return NULL; - - /* 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); - - /* 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 NULL; -} - -/* - * Iterate over a set of instances (incl PM_IN_NULL) - * returning the next instance identifier and offset. - * - * Start it off by passing offset -1 into the routine. - */ -bool PCPMetric_iterate(PCPMetric metric, int* instp, int* offsetp) { - if (!pcp->result) - return false; - - pmValueSet* vset = pcp->result->vset[metric]; - if (!vset || vset->numval <= 0) - return false; - - int offset = *offsetp; - offset = (offset < 0) ? 0 : offset + 1; - if (offset > vset->numval - 1) - return false; - - *offsetp = offset; - *instp = vset->vlist[offset].inst; - return true; -} - -/* Switch on/off a metric for value fetching (sampling) */ -void PCPMetric_enable(PCPMetric metric, bool enable) { - pcp->fetch[metric] = enable ? pcp->pmids[metric] : PM_ID_NULL; -} - -bool PCPMetric_enabled(PCPMetric metric) { - return pcp->fetch[metric] != PM_ID_NULL; -} - -void PCPMetric_enableThreads(void) { - pmValueSet* vset = xCalloc(1, sizeof(pmValueSet)); - vset->vlist[0].inst = PM_IN_NULL; - vset->vlist[0].value.lval = 1; - vset->valfmt = PM_VAL_INSITU; - vset->numval = 1; - vset->pmid = pcp->pmids[PCP_CONTROL_THREADS]; - - pmResult* result = xCalloc(1, sizeof(pmResult)); - result->vset[0] = vset; - result->numpmid = 1; - - int sts = pmStore(result); - if (sts < 0 && pmDebugOptions.appl0) - fprintf(stderr, "Error: cannot enable threads: %s\n", pmErrStr(sts)); - - pmFreeResult(result); -} - -bool PCPMetric_fetch(struct timeval* timestamp) { - if (pcp->result) { - pmFreeResult(pcp->result); - pcp->result = NULL; - } - int sts, count = 0; - do { - sts = pmFetch(pcp->totalMetrics, pcp->fetch, &pcp->result); - } while (sts == PM_ERR_IPC && ++count < 3); - if (sts < 0) { - if (pmDebugOptions.appl0) - fprintf(stderr, "Error: cannot fetch metric values: %s\n", - pmErrStr(sts)); - return false; - } - if (timestamp) - *timestamp = pcp->result->timestamp; - return true; -} diff --git a/pcp/PCPMetric.h b/pcp/PCPMetric.h deleted file mode 100644 index 84ccbb9..0000000 --- a/pcp/PCPMetric.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef HEADER_PCPMetric -#define HEADER_PCPMetric -/* -htop - PCPMetric.h -(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 -#include -#include -#include - -/* use htop config.h values for these macros, not pcp values */ -#undef PACKAGE_URL -#undef PACKAGE_NAME -#undef PACKAGE_STRING -#undef PACKAGE_TARNAME -#undef PACKAGE_VERSION -#undef PACKAGE_BUGREPORT - - -typedef enum PCPMetric_ { - PCP_CONTROL_THREADS, /* proc.control.perclient.threads */ - - PCP_HINV_NCPU, /* hinv.ncpu */ - PCP_HINV_CPUCLOCK, /* hinv.cpu.clock */ - PCP_UNAME_SYSNAME, /* kernel.uname.sysname */ - PCP_UNAME_RELEASE, /* kernel.uname.release */ - PCP_UNAME_MACHINE, /* kernel.uname.machine */ - PCP_UNAME_DISTRO, /* kernel.uname.distro */ - PCP_LOAD_AVERAGE, /* kernel.all.load */ - PCP_PID_MAX, /* kernel.all.pid_max */ - PCP_UPTIME, /* kernel.all.uptime */ - PCP_BOOTTIME, /* kernel.all.boottime */ - PCP_CPU_USER, /* kernel.all.cpu.user */ - PCP_CPU_NICE, /* kernel.all.cpu.nice */ - PCP_CPU_SYSTEM, /* kernel.all.cpu.sys */ - PCP_CPU_IDLE, /* kernel.all.cpu.idle */ - PCP_CPU_IOWAIT, /* kernel.all.cpu.wait.total */ - PCP_CPU_IRQ, /* kernel.all.cpu.intr */ - PCP_CPU_SOFTIRQ, /* kernel.all.cpu.irq.soft */ - PCP_CPU_STEAL, /* kernel.all.cpu.steal */ - PCP_CPU_GUEST, /* kernel.all.cpu.guest */ - PCP_CPU_GUESTNICE, /* kernel.all.cpu.guest_nice */ - PCP_PERCPU_USER, /* kernel.percpu.cpu.user */ - PCP_PERCPU_NICE, /* kernel.percpu.cpu.nice */ - PCP_PERCPU_SYSTEM, /* kernel.percpu.cpu.sys */ - PCP_PERCPU_IDLE, /* kernel.percpu.cpu.idle */ - PCP_PERCPU_IOWAIT, /* kernel.percpu.cpu.wait.total */ - PCP_PERCPU_IRQ, /* kernel.percpu.cpu.intr */ - PCP_PERCPU_SOFTIRQ, /* kernel.percpu.cpu.irq.soft */ - PCP_PERCPU_STEAL, /* kernel.percpu.cpu.steal */ - PCP_PERCPU_GUEST, /* kernel.percpu.cpu.guest */ - PCP_PERCPU_GUESTNICE, /* kernel.percpu.cpu.guest_nice */ - PCP_MEM_TOTAL, /* mem.physmem */ - PCP_MEM_FREE, /* mem.util.free */ - PCP_MEM_BUFFERS, /* mem.util.bufmem */ - PCP_MEM_CACHED, /* mem.util.cached */ - PCP_MEM_SHARED, /* mem.util.shared */ - PCP_MEM_AVAILABLE, /* mem.util.available */ - PCP_MEM_SRECLAIM, /* mem.util.slabReclaimable */ - PCP_MEM_SWAPCACHED, /* mem.util.swapCached */ - PCP_MEM_SWAPTOTAL, /* mem.util.swapTotal */ - PCP_MEM_SWAPFREE, /* mem.util.swapFree */ - PCP_DISK_READB, /* disk.all.read_bytes */ - PCP_DISK_WRITEB, /* disk.all.write_bytes */ - PCP_DISK_ACTIVE, /* disk.all.avactive */ - PCP_NET_RECVB, /* network.all.in.bytes */ - PCP_NET_SENDB, /* network.all.out.bytes */ - PCP_NET_RECVP, /* network.all.in.packets */ - PCP_NET_SENDP, /* network.all.out.packets */ - 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_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 */ - PCP_ZFS_ARC_BONUS_SIZE, /* zfs.arc.bonus_size */ - PCP_ZFS_ARC_COMPRESSED_SIZE, /* zfs.arc.compressed_size */ - PCP_ZFS_ARC_UNCOMPRESSED_SIZE, /* zfs.arc.uncompressed_size */ - PCP_ZFS_ARC_C_MIN, /* zfs.arc.c_min */ - PCP_ZFS_ARC_C_MAX, /* zfs.arc.c_max */ - PCP_ZFS_ARC_DBUF_SIZE, /* zfs.arc.dbuf_size */ - PCP_ZFS_ARC_DNODE_SIZE, /* zfs.arc.dnode_size */ - PCP_ZFS_ARC_HDR_SIZE, /* zfs.arc.hdr_size */ - PCP_ZFS_ARC_MFU_SIZE, /* zfs.arc.mfu_size */ - PCP_ZFS_ARC_MRU_SIZE, /* zfs.arc.mru_size */ - PCP_ZFS_ARC_SIZE, /* zfs.arc.size */ - PCP_ZRAM_CAPACITY, /* zram.capacity */ - PCP_ZRAM_ORIGINAL, /* zram.mm_stat.data_size.original */ - PCP_ZRAM_COMPRESSED, /* zram.mm_stat.data_size.compressed */ - - PCP_PROC_PID, /* proc.psinfo.pid */ - PCP_PROC_PPID, /* proc.psinfo.ppid */ - PCP_PROC_TGID, /* proc.psinfo.tgid */ - PCP_PROC_PGRP, /* proc.psinfo.pgrp */ - PCP_PROC_SESSION, /* proc.psinfo.session */ - PCP_PROC_STATE, /* proc.psinfo.sname */ - PCP_PROC_TTY, /* proc.psinfo.tty */ - PCP_PROC_TTYPGRP, /* proc.psinfo.tty_pgrp */ - PCP_PROC_MINFLT, /* proc.psinfo.minflt */ - PCP_PROC_MAJFLT, /* proc.psinfo.maj_flt */ - PCP_PROC_CMINFLT, /* proc.psinfo.cmin_flt */ - PCP_PROC_CMAJFLT, /* proc.psinfo.cmaj_flt */ - PCP_PROC_UTIME, /* proc.psinfo.utime */ - PCP_PROC_STIME, /* proc.psinfo.stime */ - PCP_PROC_CUTIME, /* proc.psinfo.cutime */ - PCP_PROC_CSTIME, /* proc.psinfo.cstime */ - PCP_PROC_PRIORITY, /* proc.psinfo.priority */ - PCP_PROC_NICE, /* proc.psinfo.nice */ - PCP_PROC_THREADS, /* proc.psinfo.threads */ - PCP_PROC_STARTTIME, /* proc.psinfo.start_time */ - PCP_PROC_PROCESSOR, /* proc.psinfo.processor */ - PCP_PROC_CMD, /* proc.psinfo.cmd */ - PCP_PROC_PSARGS, /* proc.psinfo.psargs */ - PCP_PROC_CGROUPS, /* proc.psinfo.cgroups */ - PCP_PROC_OOMSCORE, /* proc.psinfo.oom_score */ - PCP_PROC_VCTXSW, /* proc.psinfo.vctxsw */ - PCP_PROC_NVCTXSW, /* proc.psinfo.nvctxsw */ - PCP_PROC_LABELS, /* proc.psinfo.labels */ - PCP_PROC_ENVIRON, /* proc.psinfo.environ */ - PCP_PROC_TTYNAME, /* proc.psinfo.ttyname */ - PCP_PROC_EXE, /* proc.psinfo.exe */ - PCP_PROC_CWD, /* proc.psinfo.cwd */ - - PCP_PROC_AUTOGROUP_ID, /* proc.autogroup.id */ - PCP_PROC_AUTOGROUP_NICE, /* proc.autogroup.nice */ - - PCP_PROC_ID_UID, /* proc.id.uid */ - PCP_PROC_ID_USER, /* proc.id.uid_nm */ - - PCP_PROC_IO_RCHAR, /* proc.io.rchar */ - PCP_PROC_IO_WCHAR, /* proc.io.wchar */ - PCP_PROC_IO_SYSCR, /* proc.io.syscr */ - PCP_PROC_IO_SYSCW, /* proc.io.syscw */ - PCP_PROC_IO_READB, /* proc.io.read_bytes */ - PCP_PROC_IO_WRITEB, /* proc.io.write_bytes */ - PCP_PROC_IO_CANCELLED, /* proc.io.cancelled_write_bytes */ - - PCP_PROC_MEM_SIZE, /* proc.memory.size */ - PCP_PROC_MEM_RSS, /* proc.memory.rss */ - PCP_PROC_MEM_SHARE, /* proc.memory.share */ - PCP_PROC_MEM_TEXTRS, /* proc.memory.textrss */ - PCP_PROC_MEM_LIBRS, /* proc.memory.librss */ - PCP_PROC_MEM_DATRS, /* proc.memory.datrss */ - PCP_PROC_MEM_DIRTY, /* proc.memory.dirty */ - - PCP_PROC_SMAPS_PSS, /* proc.smaps.pss */ - PCP_PROC_SMAPS_SWAP, /* proc.smaps.swap */ - PCP_PROC_SMAPS_SWAPPSS, /* proc.smaps.swappss */ - - PCP_METRIC_COUNT /* total metric count */ -} PCPMetric; - -void PCPMetric_enable(PCPMetric metric, bool enable); - -bool PCPMetric_enabled(PCPMetric metric); - -void PCPMetric_enableThreads(void); - -bool PCPMetric_fetch(struct timeval* timestamp); - -bool PCPMetric_iterate(PCPMetric metric, int* instp, int* offsetp); - -pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type); - -const pmDesc* PCPMetric_desc(PCPMetric metric); - -int PCPMetric_type(PCPMetric metric); - -int PCPMetric_instanceCount(PCPMetric metric); - -int PCPMetric_instanceOffset(PCPMetric metric, int inst); - -pmAtomValue* PCPMetric_instance(PCPMetric metric, int inst, int offset, pmAtomValue* atom, int type); - -#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 @@ -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 +#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 -#include -#include -#include -#include -#include - -#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(×tamp, NULL); - this->timestamp = pmtimevalToReal(×tamp); - - 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(""); - 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(×tamp) != true) - return; - - double sample = this->timestamp; - this->timestamp = pmtimevalToReal(×tamp); - - 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, ×tamp); -} - -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/PCPProcessList.h b/pcp/PCPProcessList.h deleted file mode 100644 index a3a7372..0000000 --- a/pcp/PCPProcessList.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef HEADER_PCPProcessList -#define HEADER_PCPProcessList -/* -htop - PCPProcessList.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 -#include - -#include "Hashtable.h" -#include "ProcessList.h" -#include "UsersTable.h" - -#include "pcp/Platform.h" -#include "zfs/ZfsArcStats.h" - - -typedef enum CPUMetric_ { - CPU_TOTAL_TIME, - CPU_USER_TIME, - CPU_SYSTEM_TIME, - CPU_SYSTEM_ALL_TIME, - CPU_IDLE_ALL_TIME, - CPU_IDLE_TIME, - CPU_NICE_TIME, - CPU_IOWAIT_TIME, - CPU_IRQ_TIME, - CPU_SOFTIRQ_TIME, - CPU_STEAL_TIME, - CPU_GUEST_TIME, - CPU_GUESTNICE_TIME, - - CPU_TOTAL_PERIOD, - CPU_USER_PERIOD, - CPU_SYSTEM_PERIOD, - CPU_SYSTEM_ALL_PERIOD, - CPU_IDLE_ALL_PERIOD, - CPU_IDLE_PERIOD, - CPU_NICE_PERIOD, - CPU_IOWAIT_PERIOD, - CPU_IRQ_PERIOD, - CPU_SOFTIRQ_PERIOD, - CPU_STEAL_PERIOD, - CPU_GUEST_PERIOD, - CPU_GUESTNICE_PERIOD, - - CPU_FREQUENCY, - - CPU_METRIC_COUNT -} CPUMetric; - -typedef struct PCPProcessList_ { - ProcessList super; - 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); - -#endif 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 +#include +#include +#include +#include +#include + +#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(""); + 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 +#include + +#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 -- cgit v1.2.3