From 6a27de7ef9dbaca0736019668307f7cbdc785a71 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 14 Feb 2023 11:51:54 +1100 Subject: [PATCH 1/9] trivial: rename changes: drop 'Process' 'actionTogglePauseProcessUpdate' -> 'actionTogglePauseUpdate' 'pauseProcessUpdate' -> 'pauseUpdate' 'hideProcessSelection' -> 'hideSelection' 'hideProcessSelection' -> 'hideSelection' Signed-off-by: Sohaib Mohamed --- Action.c | 6 +++--- Action.h | 6 +++--- CommandLine.c | 4 ++-- MainPanel.c | 6 +++--- ScreenManager.c | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Action.c b/Action.c index 5d33f6624..117defd38 100644 --- a/Action.c +++ b/Action.c @@ -565,8 +565,8 @@ static Htop_Reaction actionRedraw(ATTR_UNUSED State* st) { return HTOP_REFRESH | HTOP_REDRAW_BAR; } -static Htop_Reaction actionTogglePauseProcessUpdate(State* st) { - st->pauseProcessUpdate = !st->pauseProcessUpdate; +static Htop_Reaction actionTogglePauseUpdate(State* st) { + st->pauseUpdate = !st->pauseUpdate; return HTOP_REFRESH | HTOP_REDRAW_BAR; } @@ -831,7 +831,7 @@ void Action_setBindings(Htop_Action* keys) { #ifdef SCHEDULER_SUPPORT keys['Y'] = actionSetSchedPolicy; #endif - keys['Z'] = actionTogglePauseProcessUpdate; + keys['Z'] = actionTogglePauseUpdate; keys['['] = actionLowerPriority; keys['\014'] = actionRedraw; // Ctrl+L keys['\177'] = actionCollapseIntoParent; diff --git a/Action.h b/Action.h index 09b68bd72..04d090f8f 100644 --- a/Action.h +++ b/Action.h @@ -41,13 +41,13 @@ typedef struct State_ { ProcessList* pl; struct MainPanel_* mainPanel; Header* header; - bool pauseProcessUpdate; - bool hideProcessSelection; + bool pauseUpdate; + bool hideSelection; bool hideMeters; } State; static inline bool State_hideFunctionBar(const State* st) { - return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideProcessSelection); + return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideSelection); } typedef Htop_Reaction (*Htop_Action)(State* st); diff --git a/CommandLine.c b/CommandLine.c index 682e05421..3df79d430 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -372,8 +372,8 @@ int CommandLine_run(const char* name, int argc, char** argv) { .pl = pl, .mainPanel = panel, .header = header, - .pauseProcessUpdate = false, - .hideProcessSelection = false, + .pauseUpdate = false, + .hideSelection = false, .hideMeters = false, }; diff --git a/MainPanel.c b/MainPanel.c index 89b4e7d41..0889a5c20 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -70,7 +70,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { needReset = false; #endif if (needReset) - this->state->hideProcessSelection = false; + this->state->hideSelection = false; Settings* settings = this->state->settings; ScreenSettings* ss = settings->ss; @@ -107,7 +107,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } result = HANDLED; } else if (ch == 27) { - this->state->hideProcessSelection = true; + this->state->hideSelection = true; return HANDLED; } else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) { reaction |= (this->keys[ch])(this->state); @@ -190,7 +190,7 @@ static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) { return; IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); - if (this->state->pauseProcessUpdate) { + if (this->state->pauseUpdate) { FunctionBar_append("PAUSED", CRT_colors[PAUSED]); } } diff --git a/ScreenManager.c b/ScreenManager.c index 55cacd20a..a6f4b8a8c 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -131,12 +131,12 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi if (*rescan) { *oldTime = newTime; int oldUidDigits = Process_uidDigits; - if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->ss->treeView)) { + if (!this->state->pauseUpdate && (*sortTimeout == 0 || this->settings->ss->treeView)) { pl->needsSort = true; *sortTimeout = 1; } // scan processes first - some header values are calculated there - ProcessList_scan(pl, this->state->pauseProcessUpdate); + ProcessList_scan(pl, this->state->pauseUpdate); // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); // force redraw if the number of UID digits was changed @@ -206,7 +206,7 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_ Panel_draw(panel, force_redraw, i == focus, - panel != (Panel*)this->state->mainPanel || !this->state->hideProcessSelection, + panel != (Panel*)this->state->mainPanel || !this->state->hideSelection, State_hideFunctionBar(this->state)); mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0)); } From 6f4d75a73be914863c3a31efeeb0a19b8b2e9d94 Mon Sep 17 00:00:00 2001 From: Sohaib Mohamed Date: Sat, 29 Oct 2022 05:08:26 +0200 Subject: [PATCH 2/9] PCP: DynamicScreens Signed-off-by: Sohaib Mohamed --- Action.h | 2 + AvailableColumnsPanel.c | 16 +- AvailableColumnsPanel.h | 6 + CommandLine.c | 23 ++- DynamicColumn.h | 12 +- DynamicScreen.c | 62 +++++++ DynamicScreen.h | 28 +++ GenericData.c | 69 ++++++++ GenericData.h | 62 +++++++ GenericDataList.c | 82 +++++++++ GenericDataList.h | 80 +++++++++ Header.c | 3 +- Header.h | 6 +- Makefile.am | 12 ++ ProcessList.c | 4 +- ScreenManager.c | 17 +- ScreensPanel.c | 10 ++ Settings.c | 11 +- Settings.h | 1 + darwin/Platform.c | 9 + darwin/Platform.h | 8 + dragonflybsd/Platform.c | 9 + dragonflybsd/Platform.h | 8 + freebsd/Platform.c | 9 + freebsd/Platform.h | 8 + linux/Platform.c | 9 + linux/Platform.h | 8 + netbsd/Platform.c | 9 + netbsd/Platform.h | 8 + openbsd/Platform.c | 9 + openbsd/Platform.h | 8 + pcp/PCPDynamicColumn.c | 14 +- pcp/PCPDynamicColumn.h | 1 + pcp/PCPDynamicScreen.c | 369 +++++++++++++++++++++++++++++++++++++++ pcp/PCPDynamicScreen.h | 39 +++++ pcp/PCPGenericData.c | 218 +++++++++++++++++++++++ pcp/PCPGenericData.h | 61 +++++++ pcp/PCPGenericDataList.c | 207 ++++++++++++++++++++++ pcp/PCPGenericDataList.h | 27 +++ pcp/PCPMetric.c | 35 ++++ pcp/PCPMetric.h | 7 +- pcp/Platform.c | 21 +++ pcp/Platform.h | 11 ++ pcp/screens/biosnoop | 42 +++++ pcp/screens/cgroups | 28 +++ pcp/screens/execsnoop | 37 ++++ pcp/screens/exitsnoop | 50 ++++++ pcp/screens/filesys | 66 +++++++ pcp/screens/opensnoop | 32 ++++ solaris/Platform.c | 9 + solaris/Platform.h | 8 + unsupported/Platform.c | 9 + unsupported/Platform.h | 8 + 53 files changed, 1889 insertions(+), 18 deletions(-) create mode 100644 DynamicScreen.c create mode 100644 DynamicScreen.h create mode 100644 GenericData.c create mode 100644 GenericData.h create mode 100644 GenericDataList.c create mode 100644 GenericDataList.h create mode 100644 pcp/PCPDynamicScreen.c create mode 100644 pcp/PCPDynamicScreen.h create mode 100644 pcp/PCPGenericData.c create mode 100644 pcp/PCPGenericData.h create mode 100644 pcp/PCPGenericDataList.c create mode 100644 pcp/PCPGenericDataList.h create mode 100644 pcp/screens/biosnoop create mode 100644 pcp/screens/cgroups create mode 100644 pcp/screens/execsnoop create mode 100644 pcp/screens/exitsnoop create mode 100644 pcp/screens/filesys create mode 100644 pcp/screens/opensnoop diff --git a/Action.h b/Action.h index 04d090f8f..2feeba293 100644 --- a/Action.h +++ b/Action.h @@ -17,6 +17,7 @@ in the source distribution for its full text. #include "Panel.h" #include "Process.h" #include "ProcessList.h" +#include "GenericDataList.h" #include "Settings.h" #include "UsersTable.h" @@ -39,6 +40,7 @@ typedef struct State_ { Settings* settings; UsersTable* ut; ProcessList* pl; + GenericDataList* gl; struct MainPanel_* mainPanel; Header* header; bool pauseUpdate; diff --git a/AvailableColumnsPanel.c b/AvailableColumnsPanel.c index b8c09c74c..997229581 100644 --- a/AvailableColumnsPanel.c +++ b/AvailableColumnsPanel.c @@ -23,11 +23,15 @@ in the source distribution for its full text. #include "XUtils.h" +Panel* activeAvailableColumns; + static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL}; static void AvailableColumnsPanel_delete(Object* object) { Panel* super = (Panel*) object; AvailableColumnsPanel* this = (AvailableColumnsPanel*) object; + if (activeAvailableColumns == super) + activeAvailableColumns = NULL; Panel_done(super); free(this); } @@ -81,6 +85,9 @@ const PanelClass AvailableColumnsPanel_class = { static void AvailableColumnsPanel_addDynamicColumn(ht_key_t key, void* value, void* data) { const DynamicColumn* column = (const DynamicColumn*) value; + if (column->belongToDynamicScreen) + return; + Panel* super = (Panel*) data; const char* title = column->caption ? column->caption : column->heading; if (!title) @@ -91,13 +98,13 @@ static void AvailableColumnsPanel_addDynamicColumn(ht_key_t key, void* value, vo } // Handle DynamicColumns entries in the AvailableColumnsPanel -static void AvailableColumnsPanel_addDynamicColumns(Panel* super, Hashtable* dynamicColumns) { +void AvailableColumnsPanel_addDynamicColumns(Panel* super, Hashtable* dynamicColumns) { assert(dynamicColumns); Hashtable_foreach(dynamicColumns, AvailableColumnsPanel_addDynamicColumn, super); } // Handle remaining Platform Meter entries in the AvailableColumnsPanel -static void AvailableColumnsPanel_addPlatformColumn(Panel* super) { +void AvailableColumnsPanel_addPlatformColumn(Panel* super) { for (int i = 1; i < LAST_PROCESSFIELD; i++) { if (i != COMM && Process_fields[i].description) { char description[256]; @@ -117,6 +124,11 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dyna AvailableColumnsPanel_addPlatformColumn(super); AvailableColumnsPanel_addDynamicColumns(super, dynamicColumns); + activeAvailableColumns = super; this->columns = columns; return this; } + +Panel* AvailableColumnsPanel_get(void) { + return activeAvailableColumns; +} diff --git a/AvailableColumnsPanel.h b/AvailableColumnsPanel.h index aca59060d..cced9d4be 100644 --- a/AvailableColumnsPanel.h +++ b/AvailableColumnsPanel.h @@ -20,4 +20,10 @@ extern const PanelClass AvailableColumnsPanel_class; AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns); +void AvailableColumnsPanel_addPlatformColumn(Panel* super); + +void AvailableColumnsPanel_addDynamicColumns(Panel* super, Hashtable* dynamicColumns); + +Panel* AvailableColumnsPanel_get(void); + #endif diff --git a/CommandLine.c b/CommandLine.c index 3df79d430..068fb05cb 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -25,6 +25,8 @@ in the source distribution for its full text. #include "CRT.h" #include "DynamicColumn.h" #include "DynamicMeter.h" +#include "DynamicScreen.h" +#include "GenericDataList.h" #include "Hashtable.h" #include "Header.h" #include "IncSet.h" @@ -328,11 +330,16 @@ int CommandLine_run(const char* name, int argc, char** argv) { dc = Hashtable_new(0, true); ProcessList* pl = ProcessList_new(ut, dm, dc, flags.pidMatchList, flags.userId); - Settings* settings = Settings_new(pl->activeCPUs, dc); + + Hashtable* dt = DynamicScreens_new(settings); + GenericDataList* gl = GenericDataList_new(); + pl->settings = settings; + if (gl) + gl->settings = settings; - Header* header = Header_new(pl, settings, 2); + Header* header = Header_new(pl, gl, settings, 2); Header_populateFromSettings(header); @@ -362,7 +369,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { CRT_init(settings, flags.allowUnicode); MainPanel* panel = MainPanel_new(); - ProcessList_setPanel(pl, (Panel*) panel); + MainPanel* genericDataPanel = MainPanel_new(); MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter); @@ -377,7 +384,13 @@ int CommandLine_run(const char* name, int argc, char** argv) { .hideMeters = false, }; - MainPanel_setState(panel, &state); + panel->state = &state; + genericDataPanel->state = &state; + + ProcessList_setPanel(pl, (Panel*) panel); + if (gl) + GenericDataList_setPanel(gl, (Panel*) genericDataPanel); + if (flags.commFilter) setCommFilter(&state, &(flags.commFilter)); @@ -405,6 +418,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { Header_delete(header); ProcessList_delete(pl); + GenericDataList_delete(gl); ScreenManager_delete(scr); MetersPanel_cleanup(); @@ -420,6 +434,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { Settings_delete(settings); DynamicColumns_delete(dc); DynamicMeters_delete(dm); + DynamicScreens_delete(dt); return 0; } diff --git a/DynamicColumn.h b/DynamicColumn.h index 4760e6ea5..dca373c8d 100644 --- a/DynamicColumn.h +++ b/DynamicColumn.h @@ -12,11 +12,13 @@ #define DYNAMIC_DEFAULT_COLUMN_WIDTH -5 typedef struct DynamicColumn_ { - char name[32]; /* unique, internal-only name */ - char* heading; /* displayed in main screen */ - char* caption; /* displayed in setup menu (short name) */ - char* description; /* displayed in setup menu (detail) */ - int width; /* display width +/- for value alignment */ + char name[32]; /* unique, internal-only name */ + char* heading; /* displayed in main screen */ + char* caption; /* displayed in setup menu (short name) */ + char* description; /* displayed in setup menu (detail) */ + int width; /* display width +/- for value alignment */ + bool belongToDynamicScreen; /* belong to DynamicScreen or ProcessList screen? */ + bool enabled; /* false == ignore this column */ } DynamicColumn; Hashtable* DynamicColumns_new(void); diff --git a/DynamicScreen.c b/DynamicScreen.c new file mode 100644 index 000000000..43c8ef408 --- /dev/null +++ b/DynamicScreen.c @@ -0,0 +1,62 @@ +/* +htop - DynamicScreen.c +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. All Rights Reserved. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "DynamicScreen.h" + +#include +#include + +#include "Hashtable.h" +#include "Platform.h" +#include "XUtils.h" + + +Hashtable* DynamicScreens_new(Settings* settings) { + return Platform_dynamicScreens(settings); +} + +void DynamicScreens_delete(Hashtable* dynamics) { + if (dynamics) { + Platform_dynamicScreensDone(dynamics); + Hashtable_delete(dynamics); + } +} + +typedef struct { + ht_key_t key; + const char* name; + bool found; +} DynamicIterator; + +static void DynamicScreen_compare(ht_key_t key, void* value, void* data) { + const DynamicScreen* screen = (const DynamicScreen*)value; + DynamicIterator* iter = (DynamicIterator*)data; + if (String_eq(iter->name, screen->name)) { + iter->found = true; + iter->key = key; + } +} + +bool DynamicScreen_search(Hashtable* dynamics, const char* name, ht_key_t* key) { + DynamicIterator iter = { .key = 0, .name = name, .found = false }; + if (dynamics) + Hashtable_foreach(dynamics, DynamicScreen_compare, &iter); + if (key) + *key = iter.key; + return iter.found; +} + +const char* DynamicScreen_lookup(Hashtable* dynamics, ht_key_t key) { + const DynamicScreen* screen = Hashtable_get(dynamics, key); + return screen ? screen->name : NULL; +} + +void DynamicScreen_availableColumns(char* currentScreen) { + Platform_dynamicScreenAvailableColumns(currentScreen); +} diff --git a/DynamicScreen.h b/DynamicScreen.h new file mode 100644 index 000000000..76996a91e --- /dev/null +++ b/DynamicScreen.h @@ -0,0 +1,28 @@ +#ifndef HEADER_DynamicScreen +#define HEADER_DynamicScreen + +#include + +#include "Hashtable.h" +#include "Settings.h" + + +typedef struct DynamicScreen_ { + char name[32]; /* unique name, cannot contain spaces */ + char* caption; + char* fields; + char* sortKey; + int direction; +} DynamicScreen; + +Hashtable* DynamicScreens_new(Settings* settings); + +void DynamicScreens_delete(Hashtable* dynamics); + +const char* DynamicScreen_lookup(Hashtable* dynamics, unsigned int key); + +bool DynamicScreen_search(Hashtable* dynamics, const char* name, unsigned int* key); + +void DynamicScreen_availableColumns(char* currentScreen); + +#endif diff --git a/GenericData.c b/GenericData.c new file mode 100644 index 000000000..11dcfdff1 --- /dev/null +++ b/GenericData.c @@ -0,0 +1,69 @@ +/* +htop - GenericData.c +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "GenericData.h" + +#include + +#include "CRT.h" +#include "Macros.h" +#include "Process.h" +#include "RichString.h" +#include "Settings.h" + + +void GenericData_init(GenericData* this, const Settings* settings) { + this->settings = settings; +} + +void GenericData_done(ATTR_UNUSED GenericData* this) { + assert (this != NULL); +} + +void GenericData_writeField(ATTR_UNUSED const GenericData* this, ATTR_UNUSED RichString* str, ATTR_UNUSED int field) { + return; +} + +void GenericData_display(const Object* cast, RichString* out) { + const GenericData* this = (const GenericData*) cast; + const ProcessField* fields = this->settings->ss->fields; + for (int i = 0; fields[i]; i++) + As_GenericData(this)->writeField(this, out, i); +} + +int GenericData_compare(const void* v1, const void* v2) { + const GenericData* g1 = (const GenericData*)v1; + const GenericData* g2 = (const GenericData*)v2; + + const Settings* settings = g1->settings; + const ScreenSettings* ss = settings->ss; + + ProcessField key = ScreenSettings_getActiveSortKey(ss); + + int result = GenericData_compareByKey(g1, g2, key); + + return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result; +} + +int GenericData_compareByKey_Base(const GenericData* g1, const GenericData* g2, ATTR_UNUSED ProcessField key) { + // TODO + (void) g1; + (void) g2; + + return 0; +} + +const GenericDataClass GenericData_class = { + .super = { + .extends = Class(Object), + .display = GenericData_display, + .compare = GenericData_compare, + }, + .writeField = GenericData_writeField, +}; diff --git a/GenericData.h b/GenericData.h new file mode 100644 index 000000000..5cffecdde --- /dev/null +++ b/GenericData.h @@ -0,0 +1,62 @@ +#ifndef HEADER_GenericData +#define HEADER_GenericData +/* +htop - GenericData.h +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Object.h" +#include "ProcessField.h" +#include "RichString.h" +#include "Settings.h" + + +typedef struct GenericData_ { + /* Super object for emulated OOP */ + Object super; + + /* Pointer to quasi-global data structures */ + const Settings* settings; + +} GenericData; + + +// Implemented in platform-specific code: +void GenericData_writeField(const GenericData* this, RichString* str, int field); +int GenericData_compare(const void* v1, const void* v2); +void GenericData_delete(Object* cast); + +typedef GenericData* (*GenericData_New)(const Settings* settings); +typedef void (*GenericData_WriteField)(const GenericData*, RichString*, int field); +typedef int (*GenericData_CompareByKey)(const GenericData*, const GenericData*, int key); + +typedef struct GenericDataClass_ { + const ObjectClass super; + const GenericData_WriteField writeField; + const GenericData_CompareByKey compareByKey; +} GenericDataClass; + +#define As_GenericData(this_) ((const GenericDataClass*)((this_)->super.klass)) + +#define GenericData_compareByKey(g1_, g2_, key_) (As_GenericData(g1_)->compareByKey ? (As_GenericData(g1_)->compareByKey(g1_, g2_, key_)) : GenericData_compareByKey_Base(g1_, g2_, key_)) + +int GenericData_compareByKey_Base(const GenericData* g1, const GenericData* g2, ProcessField key); + +void GenericData_display(const Object* cast, RichString* out); +extern const GenericDataClass GenericData_class; + +void GenericData_init(GenericData* this, const Settings* settings); + +void GenericData_done(GenericData* this); + +void GenericData_addField(GenericData* this); + +void GenericData_removeField(GenericData* this); + +void GenericData_rebuildFields(GenericData* this); + +#endif diff --git a/GenericDataList.c b/GenericDataList.c new file mode 100644 index 000000000..3d89d23ec --- /dev/null +++ b/GenericDataList.c @@ -0,0 +1,82 @@ +/* +htop - GenericDataList.h +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "GenericDataList.h" + +#include "Object.h" + + +GenericDataList* GenericDataList_new(void) { + GenericDataList* gl = xCalloc(1, sizeof(GenericDataList)); + + gl = GenericDataList_addPlatformList(gl); + + return gl; +} + +void GenericDataList_delete(GenericDataList* this) { + GenericDataList_removePlatformList(this); +} + +void GenericDataList_addGenericData(GenericDataList* this, GenericData* g) { + Vector_add(this->genericDataRow, g); + Hashtable_put(this->genericDataTable, this->totalRows, g); + + this->totalRows++; +} + +void GenericDataList_removeGenericData(GenericDataList* this) { + int idx = this->totalRows - 1; + Object* last = Vector_get(this->genericDataRow, idx); + + GenericData_delete(last); + + Vector_remove(this->genericDataRow, idx); + Hashtable_remove(this->genericDataTable, idx); + + this->totalRows--; +} + +GenericData* GenericDataList_getGenericData(GenericDataList* this, GenericData_New constructor) { + GenericData* g = constructor(this->settings); + + return g; +} + +void GenericDataList_scan(GenericDataList* this, bool pauseUpdate) { + GenericDataList_goThroughEntries(this, pauseUpdate); +} + +void GenericDataList_setPanel(GenericDataList* this, Panel* panel) { + this->panel = panel; +} + +static void GenericDataList_updateDisplayList(GenericDataList* this) { + if (this->needsSort) + Vector_insertionSort(this->genericDataRow); + Vector_prune(this->displayList); + int size = Vector_size(this->genericDataRow); + for (int i = 0; i < size; i++) + Vector_add(this->displayList, Vector_get(this->genericDataRow, i)); + this->needsSort = false; +} + +void GenericDataList_rebuildPanel(GenericDataList* this) { + GenericDataList_updateDisplayList(this); + const int genericDataCount = Vector_size(this->displayList); + int idx = 0; + + for (int i = 0; i < genericDataCount; i++) { + GenericData* g = (GenericData*) Vector_get(this->displayList, i); + + Panel_set(this->panel, idx, (Object*)g); + + idx++; + } +} diff --git a/GenericDataList.h b/GenericDataList.h new file mode 100644 index 000000000..a03495e19 --- /dev/null +++ b/GenericDataList.h @@ -0,0 +1,80 @@ +#ifndef HEADER_GenericDataList +#define HEADER_GenericDataList +/* +htop - GenericDataList.h +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include + +#include "GenericData.h" +#include "Hashtable.h" +#include "Panel.h" +#include "RichString.h" +#include "Settings.h" +#include "Vector.h" + + +typedef struct GenericDataList_ { + const Settings* settings; + + Vector* displayList; + Vector* genericDataRow; /* each elem is struct GenericData */ + Hashtable* genericDataTable; + + bool needsSort; + + Panel* panel; + + Hashtable* fieldUI; + + bool rebuildFields; + + int offset; + + int id; /* GenericDataList id == offset */ + + size_t totalRows; +} GenericDataList; + +/* Implemented by platforms */ +GenericDataList* GenericDataList_addPlatformList(GenericDataList* super); +void GenericDataList_removePlatformList(GenericDataList* super); +void GenericDataList_goThroughEntries(GenericDataList* super, bool pauseUpdate); + + +/* GenericData Lists */ +GenericDataList* GenericDataList_new(void); + +void GenericDataList_delete(GenericDataList* gl); + +/* One GenericData List */ +void GenericDataList_addList(void); + +//void GenericDataList_removeList(GenericDataList* g); + +/* struct GenericData */ +GenericData* GenericDataList_getGenericData(GenericDataList* this, GenericData_New constructor); + +void GenericDataList_addGenericData(GenericDataList* this, GenericData* g); + +void GenericDataList_removeGenericData(GenericDataList* this); + +/* helpers functions */ +void GenericDataList_setPanel(GenericDataList* this, Panel* panel); + +void GenericDataList_printHeader(const GenericDataList* this, RichString* header); // TODO + +void GenericDataList_expandTree(GenericDataList* this); // TODO + +void GenericDataList_collapseAllBranches(GenericDataList* this); // TODO + +void GenericDataList_rebuildPanel(GenericDataList* this); + +void GenericDataList_scan(GenericDataList* this, bool pauseUpdate); + +#endif diff --git a/Header.c b/Header.c index 1953c0206..baed76444 100644 --- a/Header.c +++ b/Header.c @@ -25,11 +25,12 @@ in the source distribution for its full text. #include "XUtils.h" -Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout) { +Header* Header_new(ProcessList* pl, GenericDataList* gl, Settings* settings, HeaderLayout hLayout) { Header* this = xCalloc(1, sizeof(Header)); this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*)); this->settings = settings; this->pl = pl; + this->gl = gl; this->headerLayout = hLayout; Header_forEachColumn(this, i) { diff --git a/Header.h b/Header.h index 954d434c3..bac67b942 100644 --- a/Header.h +++ b/Header.h @@ -7,6 +7,9 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ +#include + +#include "GenericDataList.h" #include "HeaderLayout.h" #include "Meter.h" #include "ProcessList.h" @@ -18,6 +21,7 @@ typedef struct Header_ { Vector** columns; Settings* settings; ProcessList* pl; + GenericDataList* gl; HeaderLayout headerLayout; int pad; int height; @@ -25,7 +29,7 @@ typedef struct Header_ { #define Header_forEachColumn(this_, i_) for (size_t (i_)=0, H_fEC_numColumns_ = HeaderLayout_getColumns((this_)->headerLayout); (i_) < H_fEC_numColumns_; ++(i_)) -Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout); +Header* Header_new(ProcessList* pl, GenericDataList* gl, Settings* settings, HeaderLayout hLayout); void Header_delete(Header* this); diff --git a/Makefile.am b/Makefile.am index 1c685e4d3..2d0c87945 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,8 +49,11 @@ myhtopsources = \ DisplayOptionsPanel.c \ DynamicColumn.c \ DynamicMeter.c \ + DynamicScreen.c \ + GenericData.c \ EnvScreen.c \ FunctionBar.c \ + GenericDataList.c \ Hashtable.c \ Header.c \ HeaderOptionsPanel.c \ @@ -109,8 +112,11 @@ myhtopheaders = \ DisplayOptionsPanel.h \ DynamicColumn.h \ DynamicMeter.h \ + DynamicScreen.h \ + GenericData.h \ EnvScreen.h \ FunctionBar.h \ + GenericDataList.h \ Hashtable.h \ Header.h \ HeaderLayout.h \ @@ -380,6 +386,9 @@ pcp_platform_headers = \ linux/ZramStats.h \ pcp/PCPDynamicColumn.h \ pcp/PCPDynamicMeter.h \ + pcp/PCPDynamicScreen.h \ + pcp/PCPGenericData.h \ + pcp/PCPGenericDataList.h \ pcp/PCPMetric.h \ pcp/PCPProcess.h \ pcp/PCPProcessList.h \ @@ -394,6 +403,9 @@ pcp_platform_sources = \ linux/ZramMeter.c \ pcp/PCPDynamicColumn.c \ pcp/PCPDynamicMeter.c \ + pcp/PCPDynamicScreen.c \ + pcp/PCPGenericData.c \ + pcp/PCPGenericDataList.c \ pcp/PCPMetric.c \ pcp/PCPProcess.c \ pcp/PCPProcessList.c \ diff --git a/ProcessList.c b/ProcessList.c index d11567893..398f84dca 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -86,10 +86,12 @@ static const char* alignedDynamicColumnTitle(const ProcessList* this, int key, c const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key); if (column == NULL) return "- "; + if (!column->enabled) + return ""; int width = column->width; if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH) width = DYNAMIC_DEFAULT_COLUMN_WIDTH; - xSnprintf(titleBuffer, titleBufferSize, "%*s", width, column->heading); + xSnprintf(titleBuffer, titleBufferSize, "%*s ", width, column->heading); return titleBuffer; } diff --git a/ScreenManager.c b/ScreenManager.c index a6f4b8a8c..206ddf35c 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "CRT.h" #include "FunctionBar.h" +#include "GenericDataList.h" #include "Macros.h" #include "Object.h" #include "Platform.h" @@ -117,6 +118,7 @@ void ScreenManager_resize(ScreenManager* this) { static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool* force_redraw) { ProcessList* pl = this->header->pl; + GenericDataList* gl = this->header->gl; Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs); double newTime = ((double)pl->realtime.tv_sec * 10) + ((double)pl->realtime.tv_usec / 100000); @@ -137,6 +139,10 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi } // scan processes first - some header values are calculated there ProcessList_scan(pl, this->state->pauseUpdate); + + if (this->settings->ss->generic) + GenericDataList_scan(gl, this->state->pauseUpdate); + // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); // force redraw if the number of UID digits was changed @@ -146,7 +152,16 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi *redraw = true; } if (*redraw) { - ProcessList_rebuildPanel(pl); + if (this->settings->ss->generic) { + *force_redraw = true; + Vector_prune(pl->panel->items); + + GenericDataList_rebuildPanel(gl); + + pl->panel->items = gl->panel->items; // workaround + } else { + ProcessList_rebuildPanel(pl); + } if (!this->state->hideMeters) Header_draw(this->header); } diff --git a/ScreensPanel.c b/ScreensPanel.c index cb664ac45..ba1c32c99 100644 --- a/ScreensPanel.c +++ b/ScreensPanel.c @@ -12,7 +12,9 @@ in the source distribution for its full text. #include #include +#include "AvailableColumnsPanel.h" #include "CRT.h" +#include "DynamicScreen.h" #include "FunctionBar.h" #include "Hashtable.h" #include "ProvideCurses.h" @@ -273,6 +275,14 @@ static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super); if (newFocus && oldFocus != newFocus) { ColumnsPanel_fill(this->columns, newFocus->ss, this->settings->dynamicColumns); + if (newFocus->ss->generic) { + char* currentScreen = newFocus->ss->name; + DynamicScreen_availableColumns(currentScreen); + } else { + Panel* availableColumns = AvailableColumnsPanel_get(); + AvailableColumnsPanel_addPlatformColumn(availableColumns); + AvailableColumnsPanel_addDynamicColumns(availableColumns, this->settings->dynamicColumns); + } result = HANDLED; } if (shouldRebuildArray) diff --git a/Settings.c b/Settings.c index 8543b9e43..d45b73ecc 100644 --- a/Settings.c +++ b/Settings.c @@ -285,6 +285,7 @@ ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* default .treeView = false, .treeViewAlwaysByPID = false, .allBranchesCollapsed = false, + .generic = false, }; ScreenSettings_readFields(ss, this->dynamicColumns, defaults->columns); @@ -366,9 +367,11 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini screen = Settings_defaultScreens(this); screen->treeDirection = atoi(option[1]); } else if (String_eq(option[0], "tree_view") && this->config_version <= 2) { - // old (no screen) naming also supported for backwards compatibility screen = Settings_defaultScreens(this); screen->treeView = atoi(option[1]); + } else if (String_eq(option[0], "generic_screen") && this->config_version <= 2) { + screen = Settings_defaultScreens(this); + screen->generic = atoi(option[1]); } else if (String_eq(option[0], "tree_view_always_by_pid") && this->config_version <= 2) { // old (no screen) naming also supported for backwards compatibility screen = Settings_defaultScreens(this); @@ -499,6 +502,9 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } else if (String_eq(option[0], ".tree_view")) { if (screen) screen->treeView = atoi(option[1]); + } else if (String_eq(option[0], ".generic_screen")) { + if (screen) + screen->generic = atoi(option[1]); } else if (String_eq(option[0], ".tree_view_always_by_pid")) { if (screen) screen->treeViewAlwaysByPID = atoi(option[1]); @@ -638,6 +644,8 @@ int Settings_write(const Settings* this, bool onCrash) { printSettingInteger("all_branches_collapsed", this->screens[0]->allBranchesCollapsed); for (unsigned int i = 0; i < this->nScreens; i++) { + if (this->screens[i]->generic) + continue; ScreenSettings* ss = this->screens[i]; fprintf(fd, "screen:%s=", ss->name); writeFields(fd, ss->fields, this->dynamicColumns, true, separator); @@ -648,6 +656,7 @@ int Settings_write(const Settings* this, bool onCrash) { printSettingInteger(".sort_direction", ss->direction); printSettingInteger(".tree_sort_direction", ss->treeDirection); printSettingInteger(".all_branches_collapsed", ss->allBranchesCollapsed); + printSettingInteger(".generic_screen", ss->generic); } #undef printSettingString diff --git a/Settings.h b/Settings.h index baf05da3b..599b952d7 100644 --- a/Settings.h +++ b/Settings.h @@ -44,6 +44,7 @@ typedef struct { bool treeView; bool treeViewAlwaysByPID; bool allBranchesCollapsed; + bool generic; } ScreenSettings; typedef struct Settings_ { diff --git a/darwin/Platform.c b/darwin/Platform.c index 332752b26..62e880350 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -24,6 +24,7 @@ in the source distribution for its full text. #include "CRT.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -435,3 +436,11 @@ void Platform_gettime_monotonic(uint64_t* msec) { #endif } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/darwin/Platform.h b/darwin/Platform.h index 66362072e..548749ce8 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -124,4 +124,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 8a684d866..0e0547ee3 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -20,6 +20,7 @@ in the source distribution for its full text. #include "CPUMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "MemoryMeter.h" @@ -260,3 +261,11 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) { else *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT; } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index cf6935628..657af1aa1 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -117,4 +117,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 646163af1..9e851fb22 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -31,6 +31,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "DiskIOMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -381,3 +382,11 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) { else *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT; } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/freebsd/Platform.h b/freebsd/Platform.h index 51269c091..4ae894781 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -117,4 +117,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif diff --git a/linux/Platform.c b/linux/Platform.c index 12852e36b..012005baf 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -31,6 +31,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "DiskIOMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "HugePageMeter.h" #include "LoadAverageMeter.h" @@ -1032,3 +1033,11 @@ void Platform_done(void) { LibSensors_cleanup(); #endif } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/linux/Platform.h b/linux/Platform.h index f6ac18804..5c69a6287 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -138,4 +138,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif diff --git a/netbsd/Platform.c b/netbsd/Platform.c index ad6050c00..8a2e8434d 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -38,6 +38,7 @@ in the source distribution for its full text. #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -507,3 +508,11 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) { if (fd != -1) close(fd); } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/netbsd/Platform.h b/netbsd/Platform.h index 0e53b4570..7c1e4eccc 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -121,4 +121,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 1ce5ba194..a480d019e 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -28,6 +28,7 @@ in the source distribution for its full text. #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -369,3 +370,11 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) { *isOnAC = s.value; } } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/openbsd/Platform.h b/openbsd/Platform.h index 27d792e0d..dd4897634 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -115,4 +115,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index 33c6d72a8..dc69f2df6 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -49,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) + PCPMetric_lookupText(value, &column->super.description); + /* lookup a dynamic metric with this name, else create */ if (PCPDynamicColumn_addMetric(columns, column) == false) return; @@ -108,6 +112,8 @@ 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->instances = false; + column->super.enabled = true; size_t id = columns->count + LAST_PROCESSFIELD; Hashtable_put(columns->table, id, column); @@ -160,6 +166,12 @@ 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, "instances")) { + if (String_eq(value, "True") || String_eq(value, "true")) + column->instances = true; + } else if (value && column && String_eq(key, "enabled")) { + if (String_eq(value, "False") || String_eq(value, "false")) + column->super.enabled = false; } else if (value && column && String_eq(key, "metric")) { PCPDynamicColumn_parseMetric(columns, column, path, lineno, value); } @@ -201,7 +213,7 @@ void PCPDynamicColumns_init(PCPDynamicColumns* columns) { home = pw->pw_dir; } - columns->table = Hashtable_new(0, true); + columns->table = Hashtable_new(0, false); /* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */ if (override) { diff --git a/pcp/PCPDynamicColumn.h b/pcp/PCPDynamicColumn.h index d0ffe7192..341f19872 100644 --- a/pcp/PCPDynamicColumn.h +++ b/pcp/PCPDynamicColumn.h @@ -15,6 +15,7 @@ typedef struct PCPDynamicColumn_ { DynamicColumn super; char* metricName; size_t id; /* identifier for metric array lookups */ + bool instances; } PCPDynamicColumn; typedef struct PCPDynamicColumns_ { diff --git a/pcp/PCPDynamicScreen.c b/pcp/PCPDynamicScreen.c new file mode 100644 index 000000000..f7c52b31c --- /dev/null +++ b/pcp/PCPDynamicScreen.c @@ -0,0 +1,369 @@ +/* +htop - PCPDynamicScreen.c +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "pcp/PCPDynamicScreen.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "Macros.h" +#include "Platform.h" +#include "Settings.h" +#include "XUtils.h" +#include "AvailableColumnsPanel.h" + +#include "pcp/PCPDynamicColumn.h" + + +void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynamicColumns* columns) { + for (unsigned int i = 0; i < screens->count; i++) { + PCPDynamicScreen *screen = Hashtable_get(screens->table, i); + if (!screen) + return; + + for (unsigned int column = 0; column < screen->totalColumns; column++) { + screen->columns[column].id = columns->offset + columns->cursor; + columns->cursor++; + Platform_addMetric(screen->columns[column].id, screen->columns[column].metricName); + size_t id = columns->count + LAST_PROCESSFIELD; + Hashtable_put(columns->table, id, &screen->columns[column]); + columns->count++; + } + } +} + +static PCPDynamicColumn* PCPDynamicScreen_lookupMetric(PCPDynamicScreen* screen, const char* name) { + size_t bytes = 16 + strlen(screen->super.name) + strlen(name); + char* metricName = xMalloc(bytes); + xSnprintf(metricName, bytes, "htop.screen.%s.%s", screen->super.name, name); + + PCPDynamicColumn* column = NULL; + 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 - add it */ + size_t n = screen->totalColumns + 1; + screen->columns = xReallocArray(screen->columns, n, sizeof(PCPDynamicColumn)); + screen->totalColumns = n; + column = &screen->columns[n - 1]; + memset(column, 0, sizeof(PCPDynamicColumn)); + + column->metricName = metricName; + xSnprintf(column->super.name, bytes, "%s_%s", screen->super.name, name); + column->instances = false; + column->super.belongToDynamicScreen = true; + column->super.enabled = true; + + 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' */ + + if (String_eq(p, "metric")) { + /* lookup a dynamic column with this name, else create */ + column = PCPDynamicScreen_lookupMetric(screen, key); + + 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 */ + if (!column->super.description) + PCPMetric_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 */ + column = PCPDynamicScreen_lookupMetric(screen, key); + + 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->super.width = strtoul(value, NULL, 10); + } else if (String_eq(p, "instances")) { + if (String_eq(value, "True") || String_eq(value, "true")) + column->instances = true; + } else if (String_eq(p, "enabled")) { /* default is True */ + if (String_eq(value, "False") || String_eq(value, "false")) + 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)); + + 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 heading - 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); + } else if (!ok) { + ; /* skip this one, we're looking for a new header */ + } else if (value && screen && String_eq(key, "caption")) { + free_and_xStrdup(&screen->super.caption, value); + } else if (value && screen && String_eq(key, "columns")) { + free_and_xStrdup(&screen->super.fields, value); + } else if (value && screen && String_eq(key, "sortKey")) { + free_and_xStrdup(&screen->super.sortKey, value); + } else if (value && screen && String_eq(key, "sortDirection")) { + screen->super.direction = strtoul(value, NULL, 10); + } else if (value && screen && String_eq(key, "enabled")) { /* default is false */ + if (String_eq(value, "True") || String_eq(value, "true")) + screen->enabled = true; + } else if (value && screen) { + 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) { + 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); +} + +static void PCPDynamicScreens_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) { + PCPDynamicScreen* screen = (PCPDynamicScreen*) value; + free(screen->instances); + free(screen->super.caption); + free(screen->super.fields); + free(screen->super.sortKey); +} + +void PCPDynamicScreens_done(Hashtable* table) { + Hashtable_foreach(table, PCPDynamicScreens_free, NULL); +} + +static char* formatFields(PCPDynamicScreen* screen) { + char* columns = strdup(""); + + for (size_t j = 0; j < screen->totalColumns; j++) + xAsprintf(&columns, "%s Dynamic(%s)", columns, screen->columns[j].super.name); + + return columns; +} + +void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settings) { + PCPDynamicScreen* ds; + ScreenSettings* ss; + char* sortKey; + + for (size_t i = 0; i < screens->count; i++) { + ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i); + if (!ds) + continue; + if(!ds->enabled) + continue; + + char* columns = formatFields(ds); + + xAsprintf(&sortKey, "Dynamic(%s)", ds->super.sortKey); + + ScreenDefaults screen = { + .name = ds->super.name, + .columns = columns, + .sortKey = sortKey, + }; + + ss = Settings_newScreen(settings, &screen); + + ss->generic = true; + if (ds->super.direction) + ss->direction = ds->super.direction; + + free(columns); + } +} + +void PCPDynamicScreens_availableColumns(Hashtable* screens, char* currentScreen) { + Panel* availableColumns = AvailableColumnsPanel_get(); + Vector_prune(availableColumns->items); + + bool success; + unsigned int key; + success = DynamicScreen_search(screens, currentScreen, &key); + if (!success) + return; + + PCPDynamicScreen* screen = Hashtable_get(screens, key); + if (!screen) + return; + + for (unsigned int j = 0; j < screen->totalColumns; j++) { + PCPDynamicColumn* column = &screen->columns[j]; + const char* title = column->super.caption ? column->super.caption : column->super.heading; + if (!title) + title = column->super.name; // fallback to the only mandatory field + + char description[256]; + xSnprintf(description, sizeof(description), "%s - %s", title, column->super.description); + Panel_add(availableColumns, (Object*) ListItem_new(description, j)); + } +} diff --git a/pcp/PCPDynamicScreen.h b/pcp/PCPDynamicScreen.h new file mode 100644 index 000000000..0c6e6f2f2 --- /dev/null +++ b/pcp/PCPDynamicScreen.h @@ -0,0 +1,39 @@ +#ifndef HEADER_PCPDynamicScreen +#define HEADER_PCPDynamicScreen + +#include + +#include + +#include "CRT.h" +#include "DynamicScreen.h" +#include "Hashtable.h" +#include "Settings.h" + +#include "pcp/PCPDynamicColumn.h" + + +typedef struct PCPDynamicScreen_ { + DynamicScreen super; + PCPDynamicColumn* columns; + size_t totalColumns; + char* instances; + bool enabled; +} PCPDynamicScreen; + +typedef struct PCPDynamicScreens_ { + Hashtable* table; + size_t count; /* count of dynamic screens discovered by scan */ +} PCPDynamicScreens; + +void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynamicColumns* columns); + +void PCPDynamicScreens_init(PCPDynamicScreens* screens); + +void PCPDynamicScreens_done(Hashtable* table); + +void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settings); + +void PCPDynamicScreens_availableColumns(Hashtable* screens, char* currentScreen); + +#endif diff --git a/pcp/PCPGenericData.c b/pcp/PCPGenericData.c new file mode 100644 index 000000000..fd9a50b61 --- /dev/null +++ b/pcp/PCPGenericData.c @@ -0,0 +1,218 @@ +/* +htop - GenericData.c +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 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/PCPGenericData.h" + +#include +#include + +#include "CRT.h" +#include "DynamicColumn.h" +#include "DynamicScreen.h" +#include "GenericData.h" +#include "Hashtable.h" +#include "Macros.h" +#include "PCPDynamicColumn.h" +#include "PCPDynamicScreen.h" +#include "PCPMetric.h" +#include "Platform.h" +#include "Process.h" +#include "RichString.h" +#include "XUtils.h" + +#include "pcp/PCPMetric.h" + + +GenericData* PCPGenericData_new(const Settings* settings) { + PCPGenericData* this = xCalloc(1, sizeof(PCPGenericData)); + Object_setClass(this, Class(PCPGenericData)); + + this->fields = Hashtable_new(0, false); + + this->fieldsCount = 0; + + GenericData_init(&this->super, settings); + return &this->super; +} + +void GenericData_delete(Object* cast) { + PCPGenericData* this = (PCPGenericData*) cast; + + for (size_t i = 1; i <= this->fieldsCount; i++) + PCPGenericData_removeField(this); + + GenericData_done((GenericData*)cast); + free(this); +} + +PCPGenericDataField* PCPGenericData_addField(PCPGenericData* this) +{ + PCPGenericDataField* field = xCalloc(1, sizeof(PCPGenericDataField)); + pmAtomValue* atom = xCalloc(1, sizeof(pmAtomValue)); + + field->value = atom; + Hashtable_put(this->fields, this->fieldsCount, field); + + this->fieldsCount++; + + return field; +} + +void PCPGenericData_removeField(PCPGenericData* this) +{ + int idx = this->fieldsCount - 1; + + PCPGenericDataField* field = Hashtable_get(this->fields, idx); + free(field->value); + Hashtable_remove(this->fields, idx); + this->fieldsCount--; +} + +void PCPGenericData_removeAllFields(PCPGenericData* this) +{ + for (size_t i = this->fieldsCount; i > 0; i--) { + PCPGenericData_removeField(this); + } +} + +static void PCPGenericData_writeField(const GenericData* this, RichString* str, int field) { + const PCPGenericData* gg = (const PCPGenericData*) this; + PCPGenericDataField* gf = (PCPGenericDataField*)Hashtable_get(gg->fields, field); + if (!gf) + return; + + const ProcessField* fields = this->settings->ss->fields; + char buffer[256]; + int attr = CRT_colors[DEFAULT_COLOR]; + + DynamicColumn* dc = Hashtable_get(this->settings->dynamicColumns, fields[field]); + if (!dc || !dc->enabled) + return; + + PCPDynamicColumn* column = (PCPDynamicColumn*) dc; + bool instances = column->instances; + + int width = column->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 (instances) { + char* instName; + attr = CRT_colors[DYNAMIC_GRAY]; + + PCPMetric_externalName(gf->pmid, gf->interInst, &instName); + + xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, 250, instName); + RichString_appendAscii(str, attr, buffer); + } else { + switch (gf->type) { + case PM_TYPE_STRING: + attr = CRT_colors[DYNAMIC_GREEN]; + xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, 250, gf->value->cp); + RichString_appendAscii(str, attr, buffer); + break; + case PM_TYPE_32: + xSnprintf(buffer, sizeof(buffer), "%*d ", width, gf->value->l); + RichString_appendAscii(str, attr, buffer); + break; + case PM_TYPE_U32: + xSnprintf(buffer, sizeof(buffer), "%*u ", width, gf->value->ul); + RichString_appendAscii(str, attr, buffer); + break; + case PM_TYPE_64: + xSnprintf(buffer, sizeof(buffer), "%*lld ", width, (long long) gf->value->ll); + RichString_appendAscii(str, attr, buffer); + break; + case PM_TYPE_U64: + xSnprintf(buffer, sizeof(buffer), "%*llu ", width, (unsigned long long) gf->value->ull); + RichString_appendAscii(str, attr, buffer); + break; + case PM_TYPE_FLOAT: + xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, (double) gf->value->f); + RichString_appendAscii(str, attr, buffer); + break; + case PM_TYPE_DOUBLE: + xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, gf->value->d); + RichString_appendAscii(str, attr, buffer); + break; + default: + attr = CRT_colors[DYNAMIC_RED]; + RichString_appendAscii(str, attr, "no data"); + break; + } + } +} + +static int PCPGenericData_compareByKey(const GenericData* v1, const GenericData* v2, int key) { + const PCPGenericData* g1 = (const PCPGenericData*)v1; + const PCPGenericData* g2 = (const PCPGenericData*)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 = PCPMetric_type(metric); + + pmAtomValue atom1 = {0}, atom2 = {0}; + if (!PCPMetric_instance(metric, g1->offset, g1->offset, &atom1, type) || + !PCPMetric_instance(metric, g2->offset, g2->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; +} + +const GenericDataClass PCPGenericData_class = { + .super = { + .extends = Class(GenericData), + .display = GenericData_display, + .compare = GenericData_compare, + }, + .writeField = PCPGenericData_writeField, + .compareByKey = PCPGenericData_compareByKey, +}; diff --git a/pcp/PCPGenericData.h b/pcp/PCPGenericData.h new file mode 100644 index 000000000..2ed859e62 --- /dev/null +++ b/pcp/PCPGenericData.h @@ -0,0 +1,61 @@ +#ifndef HEADER_PCPGenericData +#define HEADER_PCPGenericData +/* +htop - PCPGenericData.h +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "GenericData.h" +#include "Hashtable.h" +#include "Object.h" +#include "Platform.h" +#include "Settings.h" + + +typedef struct PCPGenericDataField_ { + pmAtomValue* value; + + pmID pmid; + + int offset; + + int interInst; + + int type; +} PCPGenericDataField; + +typedef struct PCPGenericData_ { + GenericData super; + + /* default result offset to use for searching proc metrics */ + unsigned int offset; + + Hashtable* fields; /* PCPGenericDataFields */ + + size_t fieldsCount; + + int sortKey; +} PCPGenericData; + + +void PCPGenericData_delete(Object* cast); + +int PCPGenericData_compare(const void* v1, const void* v2); + +extern const GenericDataClass PCPGenericData_class; + +GenericData* PCPGenericData_new(const Settings* settings); + +void GenericData_delete(Object* cast); + +PCPGenericDataField* PCPGenericData_addField(PCPGenericData* this); + +void PCPGenericData_removeField(PCPGenericData* this); + +void PCPGenericData_removeAllFields(PCPGenericData* this); + +#endif diff --git a/pcp/PCPGenericDataList.c b/pcp/PCPGenericDataList.c new file mode 100644 index 000000000..7614e11a0 --- /dev/null +++ b/pcp/PCPGenericDataList.c @@ -0,0 +1,207 @@ +/* +htop - PCPGenericDataList.c +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "pcp/PCPGenericDataList.h" + +#include +#include + +#include "DynamicColumn.h" +#include "GenericData.h" +#include "GenericDataList.h" +#include "Hashtable.h" +#include "Object.h" +#include "Process.h" +#include "Settings.h" +#include "Vector.h" +#include "XUtils.h" + +#include "pcp/PCPDynamicColumn.h" +#include "pcp/PCPGenericData.h" +#include "pcp/PCPMetric.h" + + +static const PCPDynamicColumn* defineKeyMetric(const Settings* settings) { + const PCPDynamicColumn* column; + column = (PCPDynamicColumn*)Hashtable_get(settings->dynamicColumns, settings->ss->fields[0]); + if (!column) + return NULL; + + return column; +} + +static int getColumnCount(const ProcessField* fields) { + int count = 0; + for (unsigned int i = 0; fields[i]; i++) + count = i; + + return count + 1; +} + +static int getRowCount(int keyMetric) { + int count = PCPMetric_instanceCount(keyMetric); + return (count > 0) ? count : 0; +} + +static void allocRows(GenericDataList* gl, int requiredRows) { + GenericData* g; + int currentRowsCount = Vector_size(gl->genericDataRow); + + if (currentRowsCount != requiredRows) { + if (currentRowsCount > requiredRows) { + for (int r = currentRowsCount; r > requiredRows; r--) { + int idx = r - 1; + + PCPGenericData* gg = (PCPGenericData*)Vector_get(gl->genericDataRow, idx); + PCPGenericData_removeAllFields(gg); + GenericDataList_removeGenericData(gl); + } + } + if (currentRowsCount < requiredRows) { + for (int r = currentRowsCount; r < requiredRows; r++) { + g = GenericDataList_getGenericData(gl, PCPGenericData_new); + GenericDataList_addGenericData(gl, g); + } + } + } +} + +static void allocColumns(GenericDataList* gl, int requiredColumns, int requiredRows) { + PCPGenericData* firstrow = (PCPGenericData*)Vector_get(gl->genericDataRow, 0); + int currentColumns = firstrow->fieldsCount; + + if (currentColumns < requiredRows) { + for (int r = 0; r < Vector_size(gl->genericDataRow); r++) { + PCPGenericData* gg = (PCPGenericData*)Vector_get(gl->genericDataRow, r); + for (int c = gg->fieldsCount; c < requiredColumns; c++) + PCPGenericData_addField(gg); + } + } + if (currentColumns > requiredRows) { + for (int r = 0; r < Vector_size(gl->genericDataRow); r++) { + PCPGenericData* gg = (PCPGenericData*)Vector_get(gl->genericDataRow, r); + for (int c = gg->fieldsCount; c > requiredColumns; c--) + PCPGenericData_removeField(gg); + } + } +} + +static int PCPGenericDataList_updateGenericDataList(PCPGenericDataList* this) { + GenericDataList* gl = (GenericDataList*) this; + const Settings* settings = gl->settings; + const ProcessField* fields = settings->ss->fields; + const PCPDynamicColumn* keyMetric; + + keyMetric = defineKeyMetric(settings); + + if (!settings->ss->generic) + return 0; + + // one or more columns are added to pcp/screens and do not exist in + // pcp/columns, continue. + for (unsigned int i = 0 ; !fields[i]; i++) + return 0; + + // Instance Domain Validation + // check if all columns share the same InDom + pmInDom keyInDom = PCPMetric_InDom(keyMetric->id); + for (unsigned int i = 0 ; fields[i]; i++) { + PCPDynamicColumn* dc = Hashtable_get(settings->dynamicColumns, fields[i]); + if (!dc) + return -1; + + pmInDom indom = PCPMetric_InDom(dc->id); + if (keyInDom != indom) { + return 0; + } + } + + int requiredColumns = getColumnCount(fields); + int requiredRows = getRowCount(keyMetric->id); + + // alloc + allocRows(gl, requiredRows); + if (requiredRows == 0) + return 0; + + allocColumns(gl, requiredColumns, requiredRows); + + // fill + int interInst = -1, offset = -1; + while (PCPMetric_iterate(keyMetric->id, &interInst, &offset)) { + for (unsigned int i = 0; fields[i]; i++) { + int metricType; + + DynamicColumn* dc = Hashtable_get(settings->dynamicColumns, fields[i]); + if (!dc) + return -1; + + PCPDynamicColumn* column = (PCPDynamicColumn*) dc; + metricType = PCPMetric_type(column->id); + + pmAtomValue value; + if (PCPMetric_instance(column->id, interInst, offset, &value, metricType)) { + GenericData* g; + + g = Hashtable_get(gl->genericDataTable, offset); + if (!g) + return -1; + + PCPGenericData* gg = (PCPGenericData*) g; + + PCPGenericDataField* field = (PCPGenericDataField*)Hashtable_get(gg->fields, i); + if (!field) + return -1; + + gg->offset = offset; + *field->value = value; + field->type = metricType; + field->pmid = column->id; + field->offset = offset; + field->interInst = interInst; + } + } + } + + return 0; +} + +void GenericDataList_goThroughEntries(GenericDataList* super, bool pauseUpdate) +{ + bool enabled = !pauseUpdate; + PCPGenericDataList* this = (PCPGenericDataList*) super; + + if (enabled) + PCPGenericDataList_updateGenericDataList(this); +} + +GenericDataList* GenericDataList_addPlatformList(GenericDataList* super) +{ + PCPGenericDataList* this = xCalloc(1, sizeof(PCPGenericDataList)); + super = &(this->super); + + super->genericDataRow = Vector_new(Class(GenericData), false, DEFAULT_SIZE); + super->displayList = Vector_new(Class(GenericData), false, DEFAULT_SIZE); + super->genericDataTable = Hashtable_new(200, false); + + super->totalRows = 0; + super->needsSort = true; + + return super; +} + +void GenericDataList_removePlatformList(GenericDataList* gl) +{ + // SMA FIXME loop & GenericDataList_removeGenericData() + Hashtable_delete(gl->genericDataTable); + Vector_delete(gl->genericDataRow); + + PCPGenericDataList* this = (PCPGenericDataList*) gl; + free(this); +} diff --git a/pcp/PCPGenericDataList.h b/pcp/PCPGenericDataList.h new file mode 100644 index 000000000..e309f8a69 --- /dev/null +++ b/pcp/PCPGenericDataList.h @@ -0,0 +1,27 @@ +#ifndef HEADER_PCPGenericDataList +#define HEADER_PCPGenericDataList +/* +htop - PCPGenericDataList.h +(C) 2022 Sohaib Mohammed +(C) 2022 htop dev team +(C) 2022 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include + +#include "GenericDataList.h" + + +typedef struct PCPGenericDataList_ { + GenericDataList super; +} PCPGenericDataList; + +void GenericDataList_goThroughEntries(GenericDataList* super, bool pauseUpdate); + +GenericDataList* GenericDataList_addPlatformList(GenericDataList* super); + +void GenericDataList_removePlatformList(GenericDataList* super); + +#endif diff --git a/pcp/PCPMetric.c b/pcp/PCPMetric.c index 606a5df06..25a2196c1 100644 --- a/pcp/PCPMetric.c +++ b/pcp/PCPMetric.c @@ -178,3 +178,38 @@ bool PCPMetric_fetch(struct timeval* timestamp) { *timestamp = pcp->result->timestamp; return true; } + +pmInDom PCPMetric_InDom(PCPMetric metric) { + if (pcp->result == NULL) + return -1; + + pmValueSet* vset = pcp->result->vset[metric]; + if (!vset || vset->numval <= 0) + return -1; + + /* extract requested number of values as requested type */ + const pmDesc* desc = &pcp->descs[metric]; + return desc->indom; +} + +/* Note: it is the responsibility of the caller to free(3) the space when it is + * no longer required. + */ +void PCPMetric_externalName(PCPMetric metric, int inst, char** externalName) { + unsigned int indom; + + indom = PCPMetric_InDom(metric); + pmNameInDom(indom, inst, externalName); +} + +int PCPMetric_lookupText(const char* metric, char** desc) { + pmID pmid; + int sts; + + sts = pmLookupName(1, &metric, &pmid); + if (sts < 0) { + return false; + } + pmLookupText(pmid, PM_TEXT_ONELINE, desc); + return 0; +} diff --git a/pcp/PCPMetric.h b/pcp/PCPMetric.h index 84ccbb95c..3f7e9cc7e 100644 --- a/pcp/PCPMetric.h +++ b/pcp/PCPMetric.h @@ -8,7 +8,6 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include #include #include #include @@ -177,4 +176,10 @@ int PCPMetric_instanceOffset(PCPMetric metric, int inst); pmAtomValue* PCPMetric_instance(PCPMetric metric, int inst, int offset, pmAtomValue* atom, int type); +pmInDom PCPMetric_InDom(PCPMetric metric); + +void PCPMetric_externalName(PCPMetric metric, int inst, char** externalName); + +int PCPMetric_lookupText(const char* metric, char** desc); + #endif diff --git a/pcp/Platform.c b/pcp/Platform.c index 994cef3c6..6efc825a2 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -45,6 +45,7 @@ in the source distribution for its full text. #include "linux/ZramStats.h" #include "pcp/PCPDynamicColumn.h" #include "pcp/PCPDynamicMeter.h" +#include "pcp/PCPDynamicScreen.h" #include "pcp/PCPMetric.h" #include "pcp/PCPProcessList.h" #include "zfs/ZfsArcMeter.h" @@ -349,6 +350,9 @@ bool Platform_init(void) { pcp->columns.offset = PCP_METRIC_COUNT + pcp->meters.cursor; PCPDynamicColumns_init(&pcp->columns); + PCPDynamicScreens_init(&pcp->screens); + PCPDynamicScreens_appendDynamicColumns(&pcp->screens, &pcp->columns); + sts = pmLookupName(pcp->totalMetrics, pcp->names, pcp->pmids); if (sts < 0) { fprintf(stderr, "Error: cannot lookup metric names: %s\n", pmErrStr(sts)); @@ -840,3 +844,20 @@ bool Platform_dynamicColumnWriteField(const Process* proc, RichString* str, unsi } return false; } + +Hashtable* Platform_dynamicScreens(Settings* settings) { + PCPDynamicScreen_appendScreens(&pcp->screens, settings); + return pcp->screens.table; +} + +Hashtable* Platform_getDynamicScreens(void) { + return pcp->screens.table; +} + +void Platform_dynamicScreensDone(Hashtable* screens) { + PCPDynamicScreens_done(screens); +} + +void Platform_dynamicScreenAvailableColumns(char* currentScreen) { + PCPDynamicScreens_availableColumns(pcp->screens.table, currentScreen); +} diff --git a/pcp/Platform.h b/pcp/Platform.h index f06f22664..f8523982a 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -35,9 +35,11 @@ in the source distribution for its full text. #include "RichString.h" #include "SignalsPanel.h" #include "CommandLine.h" +#include "Settings.h" #include "pcp/PCPDynamicColumn.h" #include "pcp/PCPDynamicMeter.h" +#include "pcp/PCPDynamicScreen.h" #include "pcp/PCPMetric.h" @@ -51,6 +53,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 */ @@ -153,4 +156,12 @@ const char* Platform_dynamicColumnInit(unsigned int key); bool Platform_dynamicColumnWriteField(const Process* proc, RichString* str, unsigned int key); +Hashtable* Platform_dynamicScreens(Settings* settings); + +Hashtable* Platform_getDynamicScreens(void); + +void Platform_dynamicScreensDone(Hashtable* screens); + +void Platform_dynamicScreenAvailableColumns(char* currentScreen); + #endif diff --git a/pcp/screens/biosnoop b/pcp/screens/biosnoop new file mode 100644 index 000000000..9f8741429 --- /dev/null +++ b/pcp/screens/biosnoop @@ -0,0 +1,42 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[biosnoop] +caption = BioSnoop +enabled = true + +comm.heading = comm +comm.caption = Comm +comm.width = -7 +comm.metric = bpf.biosnoop.comm + +pid.heading = PID +pid.caption = PID +pid.width = -7 +pid.metric = bpf.biosnoop.pid + +disk.heading = disk +disk.caption = Disk +disk.width = -7 +disk.metric = bpf.biosnoop.disk + +rwbs.heading = rwbs +rwbs.caption = rwbs +rwbs.width = -7 +rwbs.metric = bpf.biosnoop.rwbs + +bytes.heading = bytes +bytes.caption = Bytes +bytes.width = -7 +bytes.metric = bpf.biosnoop.bytes + +lat.heading = lat +lat.caption = lat +lat.width = -7 +lat.metric = bpf.biosnoop.lat + +sector.heading = sector +sector.caption = sector +sector.width = -7 +sector.metric = bpf.biosnoop.sector diff --git a/pcp/screens/cgroups b/pcp/screens/cgroups new file mode 100644 index 000000000..57d194fbd --- /dev/null +++ b/pcp/screens/cgroups @@ -0,0 +1,28 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[cgroup] +caption = Cgroup +enabled = true + +cuser.heading = UTIME +cuser.caption = User CPU Time +cuser.width = -8 +cuser.metric = 1000 * rate(cgroup.cpu.stat.user) + +csystem.heading = STIME +csystem.caption = Kernel CPU Time +csystem.width = -8 +csystem.metric = 1000 * rate(cgroup.cpu.stat.system) + +cusage.heading = CPU% +cusage.caption = CPU Utilization +cusage.width = 6 +cusage.metric = 100 * (rate(cgroup.cpu.stat.usage) / hinv.ncpu) + +name.heading = Control group +name.caption = Control group name +name.width = -12 +name.metric = cgroup.cpu.stat.system +name.instances = true diff --git a/pcp/screens/execsnoop b/pcp/screens/execsnoop new file mode 100644 index 000000000..0cb775224 --- /dev/null +++ b/pcp/screens/execsnoop @@ -0,0 +1,37 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[execsnoop] +caption = ExecSnoop +enabled = true + +pid.heading = PID +pid.caption = Process Identifier +pid.width = -7 +pid.metric = bpf.execsnoop.pid + +ppid.heading = PPID +ppid.caption = Parent Process +ppid.width = -7 +ppid.metric = bpf.execsnoop.ppid + +uid.heading = UID +uid.caption = User Identifier +uid.width = -6 +uid.metric = bpf.execsnoop.uid + +comm.heading = COMM +comm.caption = Command +comm.width = -16 +comm.metric = bpf.execsnoop.comm + +ret.heading = RET +ret.caption = Return Code +ret.width = -4 +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 000000000..5b5c4acea --- /dev/null +++ b/pcp/screens/exitsnoop @@ -0,0 +1,50 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[exitsnoop] +caption = ExitSnoop +enabled = true + +pid.heading = PID +pid.caption = Process Identifier +pid.width = -7 +pid.metric = bpf.exitsnoop.pid + +ppid.heading = PPID +ppid.caption = Parent Process +ppid.width = -7 +ppid.metric = bpf.exitsnoop.ppid + +tid.heading = TID +tid.caption = Task Identifier +tid.width = -7 +tid.metric = bpf.exitsnoop.tid +tid.enabled = false + +signal.heading = SIG +signal.caption = Signal number +signal.width = -4 +signal.metric = bpf.exitsnoop.sig + +exit.heading = EXIT +exit.caption = Exit Code +exit.width = -4 +exit.metric = bpf.exitsnoop.exit_code + +core.heading = CORE +core.caption = Dumped core +core.width = -3 +core.metric = bpf.exitsnoop.coredump +core.enabled = false + +age.heading = AGE +age.caption = Process age +age.width = -6 +age.metric = bpf.exitsnoop.age +age.enabled = false + +comm.heading = Command +comm.caption = COMM +comm.width = -16 +comm.metric = bpf.exitsnoop.comm diff --git a/pcp/screens/filesys b/pcp/screens/filesys new file mode 100644 index 000000000..12b5854fb --- /dev/null +++ b/pcp/screens/filesys @@ -0,0 +1,66 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[filesys] +caption = Filesystem +enabled = true + +avail.heading = AVAIL +avail.caption = avail +avail.width = -12 +avail.metric = filesys.avail + +blocksize.heading = BSIZE +blocksize.caption = blocksize +blocksize.width = -12 +blocksize.metric = filesys.blocksize +blocksize.enabled = false + +capacity.heading = SIZE +capacity.caption = capacity +capacity.width = -12 +capacity.metric = filesys.capacity + +free.heading = FREE +free.caption = free +free.width = -12 +free.metric = filesys.free + +freefiles.heading = FREEF +freefiles.caption = freefiles +freefiles.width = -12 +freefiles.metric = filesys.freefiles + +full.heading = FULL +full.caption = full +full.width = -12 +full.metric = filesys.full +#full.percent = true + +maxfiles.heading = MAXF +maxfiles.caption = maxfiles +maxfiles.width = -12 +maxfiles.metric = filesys.maxfiles +maxfiles.enabled = false + +readonly.heading = RO +readonly.caption = readonly +readonly.width = -12 +readonly.metric = filesys.readonly +readonly.enabled = false + +used.heading = USED +used.caption = used +used.width = -12 +used.metric = filesys.used + +usedfiles.heading = USEDF +usedfiles.caption = usedfiles +usedfiles.metric = filesys.usedfiles +usedfiles.enabled = false + +mountdir.heading = Mount point +mountdir.caption = mountdir +mountdir.width = -12 +mountdir.metric = filesys.mountdir diff --git a/pcp/screens/opensnoop b/pcp/screens/opensnoop new file mode 100644 index 000000000..3bb0b4ec9 --- /dev/null +++ b/pcp/screens/opensnoop @@ -0,0 +1,32 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[opensnoop] +caption = OpenSnoop +enabled = true + +pid.heading = PID +pid.caption = PID +pid.width = -7 +pid.metric = bpf.opensnoop.pid + +comm.heading = COMM +comm.caption = COMM +comm.width = -16 +comm.metric = bpf.opensnoop.comm + +fd.heading = FD +fd.caption = FD +fd.width = -4 +fd.metric = bpf.opensnoop.fd + +err.heading = ERR +err.caption = ERR +err.width = -4 +err.metric = bpf.opensnoop.err + +path.heading = File name +path.caption = PATH +path.width = -12 +path.metric = bpf.opensnoop.fname diff --git a/solaris/Platform.c b/solaris/Platform.c index 96f35263d..1ddff72bf 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -23,6 +23,7 @@ in the source distribution for its full text. #include "Macros.h" #include "Meter.h" #include "CPUMeter.h" +#include "GenericDataList.h" #include "MemoryMeter.h" #include "MemorySwapMeter.h" #include "SwapMeter.h" @@ -329,3 +330,11 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) { *percent = NAN; *isOnAC = AC_ERROR; } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/solaris/Platform.h b/solaris/Platform.h index 14431e356..b00ef7546 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -156,4 +156,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif diff --git a/unsupported/Platform.c b/unsupported/Platform.c index 27bc560bd..f9d31f8d4 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -156,3 +157,11 @@ void Platform_getHostname(char* buffer, size_t size) { void Platform_getRelease(char** string) { *string = xStrdup(Platform_unsupported); } + +void GenericDataList_goThroughEntries(ATTR_UNUSED GenericDataList* super, ATTR_UNUSED bool pauseUpdate) { return; } + +void GenericDataList_removePlatformList(ATTR_UNUSED GenericDataList* gl) { return; } + +GenericDataList* GenericDataList_addPlatformList(ATTR_UNUSED GenericDataList* super) {return NULL; } + +void GenericData_delete(ATTR_UNUSED Object* cast) { return; } diff --git a/unsupported/Platform.h b/unsupported/Platform.h index f475dda4d..900ff07dc 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -104,4 +104,12 @@ static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* p return false; } +static inline Hashtable* Platform_dynamicScreens(ATTR_UNUSED Settings* settings) { return NULL; } + +static inline Hashtable* Platform_getDynamicScreens(void) { return NULL; } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { return; } + +static inline void Platform_dynamicScreenAvailableColumns(ATTR_UNUSED char* currentScreen) { } + #endif From 2069fb9f8f7f9ae58dd1eaac367e0fc758be8ea6 Mon Sep 17 00:00:00 2001 From: Sohaib Mohamed Date: Sat, 29 Oct 2022 05:31:40 +0200 Subject: [PATCH 3/9] normalized columns values Signed-off-by: Sohaib Mohamed --- pcp/PCPDynamicColumn.c | 8 ++- pcp/PCPDynamicColumn.h | 1 + pcp/PCPDynamicScreen.c | 17 +++-- pcp/PCPGenericData.c | 142 +++++++++++++++++++++++++++++++++------ pcp/PCPGenericData.h | 2 + pcp/PCPGenericDataList.c | 1 + pcp/PCPMetric.c | 4 ++ pcp/PCPMetric.h | 2 + pcp/screens/filesys | 11 +++ 9 files changed, 162 insertions(+), 26 deletions(-) diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index dc69f2df6..9940b4235 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -114,6 +114,7 @@ static PCPDynamicColumn* PCPDynamicColumn_new(PCPDynamicColumns* columns, const String_safeStrncpy(column->super.name, name, sizeof(column->super.name)); column->instances = false; column->super.enabled = true; + column->scale = false; size_t id = columns->count + LAST_PROCESSFIELD; Hashtable_put(columns->table, id, column); @@ -172,6 +173,9 @@ static void PCPDynamicColumn_parseFile(PCPDynamicColumns* columns, const char* p } else if (value && column && String_eq(key, "enabled")) { if (String_eq(value, "False") || String_eq(value, "false")) column->super.enabled = false; + } else if (value && column && String_eq(key, "scale")) { + if (String_eq(value, "True") || String_eq(value, "true")) + column->scale = true; } else if (value && column && String_eq(key, "metric")) { PCPDynamicColumn_parseMetric(columns, column, path, lineno, value); } @@ -263,7 +267,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri pmAtomValue atom; if (!PCPMetric_instance(this->id, proc->pid, pp->offset, &atom, type)) { - RichString_appendAscii(str, CRT_colors[METER_VALUE_ERROR], "no data"); + RichString_appendAscii(str, CRT_colors[DYNAMIC_RED], "no data"); return; } @@ -309,7 +313,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri RichString_appendAscii(str, attr, buffer); break; default: - attr = CRT_colors[METER_VALUE_ERROR]; + attr = CRT_colors[DYNAMIC_RED]; RichString_appendAscii(str, attr, "no type"); break; } diff --git a/pcp/PCPDynamicColumn.h b/pcp/PCPDynamicColumn.h index 341f19872..af4b56884 100644 --- a/pcp/PCPDynamicColumn.h +++ b/pcp/PCPDynamicColumn.h @@ -16,6 +16,7 @@ typedef struct PCPDynamicColumn_ { char* metricName; size_t id; /* identifier for metric array lookups */ bool instances; + bool scale; } PCPDynamicColumn; typedef struct PCPDynamicColumns_ { diff --git a/pcp/PCPDynamicScreen.c b/pcp/PCPDynamicScreen.c index f7c52b31c..75bdbea27 100644 --- a/pcp/PCPDynamicScreen.c +++ b/pcp/PCPDynamicScreen.c @@ -28,16 +28,19 @@ in the source distribution for its full text. void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynamicColumns* columns) { for (unsigned int i = 0; i < screens->count; i++) { - PCPDynamicScreen *screen = Hashtable_get(screens->table, i); + PCPDynamicScreen* screen = Hashtable_get(screens->table, i); if (!screen) return; - for (unsigned int column = 0; column < screen->totalColumns; column++) { - screen->columns[column].id = columns->offset + columns->cursor; + for (unsigned int j = 0; j < screen->totalColumns; j++) { + PCPDynamicColumn* column = &screen->columns[j]; + + column->id = columns->offset + columns->cursor; columns->cursor++; - Platform_addMetric(screen->columns[column].id, screen->columns[column].metricName); + + Platform_addMetric(column->id, column->metricName); size_t id = columns->count + LAST_PROCESSFIELD; - Hashtable_put(columns->table, id, &screen->columns[column]); + Hashtable_put(columns->table, id, column); columns->count++; } } @@ -70,6 +73,7 @@ static PCPDynamicColumn* PCPDynamicScreen_lookupMetric(PCPDynamicScreen* screen, column->instances = false; column->super.belongToDynamicScreen = true; column->super.enabled = true; + column->scale = false; return column; } @@ -121,6 +125,9 @@ static void PCPDynamicScreen_parseColumn(PCPDynamicScreen* screen, const char* p } else if (String_eq(p, "enabled")) { /* default is True */ if (String_eq(value, "False") || String_eq(value, "false")) column->super.enabled = false; + } else if (String_eq(p, "scale")) { + if (String_eq(value, "True") || String_eq(value, "true")) + column->scale = true; } } } diff --git a/pcp/PCPGenericData.c b/pcp/PCPGenericData.c index fd9a50b61..68bf8140c 100644 --- a/pcp/PCPGenericData.c +++ b/pcp/PCPGenericData.c @@ -83,6 +83,22 @@ void PCPGenericData_removeAllFields(PCPGenericData* this) } } +#define PrintField(format, value) \ + xSnprintf(buffer, sizeof(buffer), format, width, value);\ + RichString_appendAscii(str, attr, buffer); + +#define KBytesField(format, val) \ + pmConvScale(format, gf->value, &gf->units, &atom, &kbyte_scale);\ + Process_printKBytes(str, val, coloring); + +#define TimeField(format, val) \ + pmConvScale(format, gf->value, &gf->units, &atom, &sec_scale);\ + Process_printTime(str, val, coloring); + +#define CountField(format, val) \ + pmConvScale(format, gf->value, &gf->units, &atom, &count_scale);\ + Process_printCount(str, val, coloring); + static void PCPGenericData_writeField(const GenericData* this, RichString* str, int field) { const PCPGenericData* gg = (const PCPGenericData*) this; PCPGenericDataField* gf = (PCPGenericDataField*)Hashtable_get(gg->fields, field); @@ -92,6 +108,7 @@ static void PCPGenericData_writeField(const GenericData* this, RichString* str, const ProcessField* fields = this->settings->ss->fields; char buffer[256]; int attr = CRT_colors[DEFAULT_COLOR]; + bool coloring = this->settings->highlightMegabytes; DynamicColumn* dc = Hashtable_get(this->settings->dynamicColumns, fields[field]); if (!dc || !dc->enabled) @@ -100,6 +117,21 @@ static void PCPGenericData_writeField(const GenericData* this, RichString* str, PCPDynamicColumn* column = (PCPDynamicColumn*) dc; bool instances = column->instances; + bool scaled = column->scale; + pmAtomValue atom; + pmUnits kbyte_scale; + + kbyte_scale.dimSpace = 1; + kbyte_scale.scaleSpace = PM_SPACE_KBYTE; + + pmUnits sec_scale; + sec_scale.dimTime = 1; + sec_scale.scaleTime = PM_TIME_SEC; + + pmUnits count_scale; + count_scale.dimCount = 1; + count_scale.scaleCount = PM_COUNT_ONE; + int width = column->super.width; if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH) width = DYNAMIC_DEFAULT_COLUMN_WIDTH; @@ -125,32 +157,104 @@ static void PCPGenericData_writeField(const GenericData* this, RichString* str, RichString_appendAscii(str, attr, buffer); break; case PM_TYPE_32: - xSnprintf(buffer, sizeof(buffer), "%*d ", width, gf->value->l); - RichString_appendAscii(str, attr, buffer); - break; + if (scaled) { + if (gf->units.dimSpace) { + KBytesField(PM_TYPE_32, atom.l); + break; + } else if (gf->units.dimTime) { + TimeField(PM_TYPE_32, atom.l); + break; + } else { + CountField(PM_TYPE_32, atom.l); + break; + } + } else { + PrintField("%*d ", gf->value->l); + break; + } case PM_TYPE_U32: - xSnprintf(buffer, sizeof(buffer), "%*u ", width, gf->value->ul); - RichString_appendAscii(str, attr, buffer); - break; + if (scaled) { + if (gf->units.dimSpace) { + KBytesField(PM_TYPE_U32, atom.ul); + break; + } else if (gf->units.dimTime) { + TimeField(PM_TYPE_U32, atom.ul); + break; + } else { + CountField(PM_TYPE_U32, atom.ul); + break; + } + } else { + PrintField("%*u ", gf->value->ul); + break; + } case PM_TYPE_64: - xSnprintf(buffer, sizeof(buffer), "%*lld ", width, (long long) gf->value->ll); - RichString_appendAscii(str, attr, buffer); - break; + if (scaled) { + if (gf->units.dimSpace) { + KBytesField(PM_TYPE_64, atom.ll); + break; + } else if (gf->units.dimTime) { + TimeField(PM_TYPE_64, atom.ll); + break; + } else { + CountField(PM_TYPE_64, atom.ll); + break; + } + } else { + PrintField("%*lld ", (long long)gf->value->ll); + break; + } case PM_TYPE_U64: - xSnprintf(buffer, sizeof(buffer), "%*llu ", width, (unsigned long long) gf->value->ull); - RichString_appendAscii(str, attr, buffer); - break; + if (scaled) { + if (gf->units.dimSpace) { + KBytesField(PM_TYPE_U64, atom.ull); + break; + } else if (gf->units.dimTime) { + TimeField(PM_TYPE_U64, atom.ull); + break; + } else { + CountField(PM_TYPE_U64, atom.ull); + break; + } + } else { + PrintField("%*llu ", (unsigned long long)gf->value->ull); + break; + } case PM_TYPE_FLOAT: - xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, (double) gf->value->f); - RichString_appendAscii(str, attr, buffer); - break; + if (scaled) { + if (gf->units.dimSpace) { + KBytesField(PM_TYPE_FLOAT, atom.f); + break; + } else if (gf->units.dimTime) { + TimeField(PM_TYPE_FLOAT, atom.f); + break; + } else { + CountField(PM_TYPE_FLOAT, atom.f); + break; + } + } else { + PrintField("%*.2f ", (double)gf->value->f); + break; + } case PM_TYPE_DOUBLE: - xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, gf->value->d); - RichString_appendAscii(str, attr, buffer); - break; + if (scaled) { + if (gf->units.dimSpace) { + KBytesField(PM_TYPE_DOUBLE, atom.d); + break; + } else if (gf->units.dimTime) { + TimeField(PM_TYPE_DOUBLE, atom.d); + break; + } else { + CountField(PM_TYPE_DOUBLE, atom.d); + break; + } + } else { + PrintField("%*.2f ", gf->value->d); + break; + } default: attr = CRT_colors[DYNAMIC_RED]; - RichString_appendAscii(str, attr, "no data"); + RichString_appendAscii(str, attr, "no data "); break; } } diff --git a/pcp/PCPGenericData.h b/pcp/PCPGenericData.h index 2ed859e62..e038921a4 100644 --- a/pcp/PCPGenericData.h +++ b/pcp/PCPGenericData.h @@ -26,6 +26,8 @@ typedef struct PCPGenericDataField_ { int interInst; int type; + + pmUnits units; } PCPGenericDataField; typedef struct PCPGenericData_ { diff --git a/pcp/PCPGenericDataList.c b/pcp/PCPGenericDataList.c index 7614e11a0..9618d448f 100644 --- a/pcp/PCPGenericDataList.c +++ b/pcp/PCPGenericDataList.c @@ -165,6 +165,7 @@ static int PCPGenericDataList_updateGenericDataList(PCPGenericDataList* this) { field->pmid = column->id; field->offset = offset; field->interInst = interInst; + field->units = PCPMetric_units(column->id); } } } diff --git a/pcp/PCPMetric.c b/pcp/PCPMetric.c index 25a2196c1..8fa4083f0 100644 --- a/pcp/PCPMetric.c +++ b/pcp/PCPMetric.c @@ -29,6 +29,10 @@ int PCPMetric_type(PCPMetric metric) { return pcp->descs[metric].type; } +pmUnits PCPMetric_units(PCPMetric metric) { + return pcp->descs[metric].units; +} + pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type) { if (pcp->result == NULL) return NULL; diff --git a/pcp/PCPMetric.h b/pcp/PCPMetric.h index 3f7e9cc7e..999458311 100644 --- a/pcp/PCPMetric.h +++ b/pcp/PCPMetric.h @@ -166,6 +166,8 @@ bool PCPMetric_iterate(PCPMetric metric, int* instp, int* offsetp); pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type); +pmUnits PCPMetric_units(PCPMetric metric); + const pmDesc* PCPMetric_desc(PCPMetric metric); int PCPMetric_type(PCPMetric metric); diff --git a/pcp/screens/filesys b/pcp/screens/filesys index 12b5854fb..d1f757a8e 100644 --- a/pcp/screens/filesys +++ b/pcp/screens/filesys @@ -10,57 +10,68 @@ avail.heading = AVAIL avail.caption = avail avail.width = -12 avail.metric = filesys.avail +avail.scale = true blocksize.heading = BSIZE blocksize.caption = blocksize blocksize.width = -12 blocksize.metric = filesys.blocksize blocksize.enabled = false +blocksize.scale = true capacity.heading = SIZE capacity.caption = capacity capacity.width = -12 capacity.metric = filesys.capacity +capacity.scale = true free.heading = FREE free.caption = free free.width = -12 free.metric = filesys.free +free.scale = true freefiles.heading = FREEF freefiles.caption = freefiles freefiles.width = -12 freefiles.metric = filesys.freefiles +freefiles.scale = true full.heading = FULL full.caption = full full.width = -12 full.metric = filesys.full #full.percent = true +full.scale = true maxfiles.heading = MAXF maxfiles.caption = maxfiles maxfiles.width = -12 maxfiles.metric = filesys.maxfiles maxfiles.enabled = false +maxfiles.scale = true readonly.heading = RO readonly.caption = readonly readonly.width = -12 readonly.metric = filesys.readonly readonly.enabled = false +readonly.scale = true used.heading = USED used.caption = used used.width = -12 used.metric = filesys.used +used.scale = true usedfiles.heading = USEDF usedfiles.caption = usedfiles usedfiles.metric = filesys.usedfiles usedfiles.enabled = false +usedfiles.scale = true mountdir.heading = Mount point mountdir.caption = mountdir mountdir.width = -12 mountdir.metric = filesys.mountdir +mountdir.scale = true From 383dfcfe2ef2053611c47c179df9278d2389d665 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Fri, 11 Nov 2022 17:07:20 +1100 Subject: [PATCH 4/9] Updates to biosnoop and cgroups screen configs --- pcp/screens/biosnoop | 38 ++++++++++++------------ pcp/screens/cgroups | 70 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 21 deletions(-) diff --git a/pcp/screens/biosnoop b/pcp/screens/biosnoop index 9f8741429..ad26db94f 100644 --- a/pcp/screens/biosnoop +++ b/pcp/screens/biosnoop @@ -4,39 +4,39 @@ [biosnoop] caption = BioSnoop -enabled = true - -comm.heading = comm -comm.caption = Comm -comm.width = -7 -comm.metric = bpf.biosnoop.comm +enabled = false pid.heading = PID -pid.caption = PID +pid.caption = Process identifier pid.width = -7 pid.metric = bpf.biosnoop.pid -disk.heading = disk -disk.caption = Disk +disk.heading = DISK +disk.caption = Device name disk.width = -7 disk.metric = bpf.biosnoop.disk -rwbs.heading = rwbs -rwbs.caption = rwbs -rwbs.width = -7 +rwbs.heading = TYPE +rwbs.caption = I/O type string +rwbs.width = -4 rwbs.metric = bpf.biosnoop.rwbs -bytes.heading = bytes -bytes.caption = Bytes +bytes.heading = BYTES +bytes.caption = I/O size in bytes bytes.width = -7 bytes.metric = bpf.biosnoop.bytes -lat.heading = lat -lat.caption = lat +lat.heading = LAT +lat.caption = I/O latency lat.width = -7 lat.metric = bpf.biosnoop.lat -sector.heading = sector -sector.caption = sector -sector.width = -7 +sector.heading = SECTOR +sector.caption = Device sector +sector.width = -8 sector.metric = bpf.biosnoop.sector + +comm.heading = Command +comm.caption = Process command name +comm.width = -16 +comm.metric = bpf.biosnoop.comm diff --git a/pcp/screens/cgroups b/pcp/screens/cgroups index 57d194fbd..29eedc548 100644 --- a/pcp/screens/cgroups +++ b/pcp/screens/cgroups @@ -3,7 +3,7 @@ # [cgroup] -caption = Cgroup +caption = CGroups enabled = true cuser.heading = UTIME @@ -21,8 +21,74 @@ cusage.caption = CPU Utilization cusage.width = 6 cusage.metric = 100 * (rate(cgroup.cpu.stat.usage) / hinv.ncpu) +memory.heading = MEM +memory.caption = Used memory +memory.width = -6 +memory.metric = cgroup.memory.current + +iops.heading = IOPS +iops.caption = I/O operations +iops.width = -6 +iops.metric = cgroup.io.stat.rios + cgroup.io.stat.wios + cgroup.io.stat.dios + +readops.heading = RDIO +readops.caption = Read operations +readops.width = -6 +readops.metric = cgroup.io.stat.rios +readops.enabled = false + +writeops.heading = WRIO +writeops.caption = Write operations +writeops.width = -6 +writeops.metric = cgroup.io.stat.wios +writeops.enabled = false + +directops.heading = DIO +directops.caption = Direct I/O operations +directops.width = -6 +directops.metric = cgroup.io.stat.dios +directops.enabled = false + +totalbytes.heading = DISK R/W +totalbytes.caption = Disk throughput +totalbytes.width = -8 +totalbytes.metric = cgroup.io.stat.rbytes + cgroup.io.stat.wbytes + cgroup.io.stat.dbytes + +readbytes.heading = RBYTES +readbytes.caption = Disk read throughput +readbytes.width = -8 +readbytes.metric = cgroup.io.stat.rbytes +readbytes.enabled = false + +writebytes.heading = WBYTES +writebytes.caption = Disk throughput +writebytes.width = -8 +writebytes.metric = cgroup.io.stat.wbytes +writeytes.enabled = false + +directio.heading = DIRECT +directio.caption = Direct I/O throughput +directio.width = -8 +directio.metric = cgroup.io.stat.dbytes +directio.enabled = false + +cpupressure.heading = CPU PSI +cpupressure.caption = CPU Pressure Stall Information +cpupressure.width = -6 +cpupressure.metric = cgroup.pressure.cpu.some.total + +mempressure.heading = MEM PSI +mempressure.caption = Memory Pressure Stall Information +mempressure.width = -6 +mempressure.metric = cgroup.pressure.memory.some.total + +iopressure.heading = I/O PSI +iopressure.caption = I/O Pressure Stall Information +iopressure.width = -6 +iopressure.metric = cgroup.pressure.io.some.total + name.heading = Control group name.caption = Control group name -name.width = -12 +name.width = -22 name.metric = cgroup.cpu.stat.system name.instances = true From 2dc6d1fce9e20b360abdeb426233cfd5a8e3bb9c Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Fri, 11 Nov 2022 19:27:45 +1100 Subject: [PATCH 5/9] Split cgroups into two configs (for each indom) --- pcp/screens/cgroups | 46 --------------------------------- pcp/screens/cgroupsio | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 46 deletions(-) create mode 100644 pcp/screens/cgroupsio diff --git a/pcp/screens/cgroups b/pcp/screens/cgroups index 29eedc548..a031b25a1 100644 --- a/pcp/screens/cgroups +++ b/pcp/screens/cgroups @@ -26,52 +26,6 @@ memory.caption = Used memory memory.width = -6 memory.metric = cgroup.memory.current -iops.heading = IOPS -iops.caption = I/O operations -iops.width = -6 -iops.metric = cgroup.io.stat.rios + cgroup.io.stat.wios + cgroup.io.stat.dios - -readops.heading = RDIO -readops.caption = Read operations -readops.width = -6 -readops.metric = cgroup.io.stat.rios -readops.enabled = false - -writeops.heading = WRIO -writeops.caption = Write operations -writeops.width = -6 -writeops.metric = cgroup.io.stat.wios -writeops.enabled = false - -directops.heading = DIO -directops.caption = Direct I/O operations -directops.width = -6 -directops.metric = cgroup.io.stat.dios -directops.enabled = false - -totalbytes.heading = DISK R/W -totalbytes.caption = Disk throughput -totalbytes.width = -8 -totalbytes.metric = cgroup.io.stat.rbytes + cgroup.io.stat.wbytes + cgroup.io.stat.dbytes - -readbytes.heading = RBYTES -readbytes.caption = Disk read throughput -readbytes.width = -8 -readbytes.metric = cgroup.io.stat.rbytes -readbytes.enabled = false - -writebytes.heading = WBYTES -writebytes.caption = Disk throughput -writebytes.width = -8 -writebytes.metric = cgroup.io.stat.wbytes -writeytes.enabled = false - -directio.heading = DIRECT -directio.caption = Direct I/O throughput -directio.width = -8 -directio.metric = cgroup.io.stat.dbytes -directio.enabled = false - cpupressure.heading = CPU PSI cpupressure.caption = CPU Pressure Stall Information cpupressure.width = -6 diff --git a/pcp/screens/cgroupsio b/pcp/screens/cgroupsio new file mode 100644 index 000000000..a186975a2 --- /dev/null +++ b/pcp/screens/cgroupsio @@ -0,0 +1,59 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[cgroupio] +caption = CGroups IO +enabled = false + +iops.heading = IOPS +iops.caption = I/O operations +iops.width = -6 +iops.metric = cgroup.io.stat.rios + cgroup.io.stat.wios + cgroup.io.stat.dios + +readops.heading = RDIO +readops.caption = Read operations +readops.width = -6 +readops.metric = cgroup.io.stat.rios +readops.enabled = false + +writeops.heading = WRIO +writeops.caption = Write operations +writeops.width = -6 +writeops.metric = cgroup.io.stat.wios +writeops.enabled = false + +directops.heading = DIO +directops.caption = Direct I/O operations +directops.width = -6 +directops.metric = cgroup.io.stat.dios +directops.enabled = false + +totalbytes.heading = DISK R/W +totalbytes.caption = Disk throughput +totalbytes.width = -8 +totalbytes.metric = cgroup.io.stat.rbytes + cgroup.io.stat.wbytes + cgroup.io.stat.dbytes + +readbytes.heading = RBYTES +readbytes.caption = Disk read throughput +readbytes.width = -8 +readbytes.metric = cgroup.io.stat.rbytes +readbytes.enabled = false + +writebytes.heading = WBYTES +writebytes.caption = Disk throughput +writebytes.width = -8 +writebytes.metric = cgroup.io.stat.wbytes +writeytes.enabled = false + +directio.heading = DIRECT +directio.caption = Direct I/O throughput +directio.width = -8 +directio.metric = cgroup.io.stat.dbytes +directio.enabled = false + +name.heading = Control group device +name.caption = Control group device +name.width = -22 +name.metric = cgroup.io.stat.rbytes +name.instances = true From fbc824221ec38241cf635edc24696fd31812ad04 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Fri, 11 Nov 2022 20:14:52 +1100 Subject: [PATCH 6/9] Drop cgroup memory for now, incompatible indom --- pcp/screens/cgroups | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pcp/screens/cgroups b/pcp/screens/cgroups index a031b25a1..b85e5c1f2 100644 --- a/pcp/screens/cgroups +++ b/pcp/screens/cgroups @@ -21,10 +21,11 @@ cusage.caption = CPU Utilization cusage.width = 6 cusage.metric = 100 * (rate(cgroup.cpu.stat.usage) / hinv.ncpu) -memory.heading = MEM -memory.caption = Used memory -memory.width = -6 -memory.metric = cgroup.memory.current +## different pmInDom, argh +#memory.heading = MEM +#memory.caption = Used memory +#memory.width = -6 +#memory.metric = cgroup.memory.current cpupressure.heading = CPU PSI cpupressure.caption = CPU Pressure Stall Information From a0b143914664be04be61d025e0ee2ab8684bc0bc Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Fri, 11 Nov 2022 20:21:59 +1100 Subject: [PATCH 7/9] Fix derived metric calcs for cgroup PSI --- pcp/screens/cgroups | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pcp/screens/cgroups b/pcp/screens/cgroups index b85e5c1f2..bdd3e4264 100644 --- a/pcp/screens/cgroups +++ b/pcp/screens/cgroups @@ -27,20 +27,20 @@ cusage.metric = 100 * (rate(cgroup.cpu.stat.usage) / hinv.ncpu) #memory.width = -6 #memory.metric = cgroup.memory.current -cpupressure.heading = CPU PSI +cpupressure.heading = CPU-PSI cpupressure.caption = CPU Pressure Stall Information cpupressure.width = -6 -cpupressure.metric = cgroup.pressure.cpu.some.total +cpupressure.metric = 1000 * rate (cgroup.pressure.cpu.some.total) -mempressure.heading = MEM PSI +mempressure.heading = MEM-PSI mempressure.caption = Memory Pressure Stall Information mempressure.width = -6 -mempressure.metric = cgroup.pressure.memory.some.total +mempressure.metric = 1000 * rate (cgroup.cgroup.pressure.memory.some.total) -iopressure.heading = I/O PSI +iopressure.heading = I/O-PSI iopressure.caption = I/O Pressure Stall Information iopressure.width = -6 -iopressure.metric = cgroup.pressure.io.some.total +iopressure.metric = 1000 * rate (cgroup.cgroup.pressure.io.some.total) name.heading = Control group name.caption = Control group name From 1e585f3590c9112e359cdde21ef0e40a404821c8 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Wed, 23 Nov 2022 10:27:26 +1100 Subject: [PATCH 8/9] Revert "normalized columns values" This reverts commit 821252abc6197e531dd62d5e90a172b62003fdb6. --- pcp/PCPDynamicColumn.c | 8 +-- pcp/PCPDynamicColumn.h | 1 - pcp/PCPDynamicScreen.c | 17 ++--- pcp/PCPGenericData.c | 142 ++++++--------------------------------- pcp/PCPGenericData.h | 2 - pcp/PCPGenericDataList.c | 1 - pcp/PCPMetric.c | 4 -- pcp/PCPMetric.h | 2 - pcp/screens/filesys | 11 --- 9 files changed, 26 insertions(+), 162 deletions(-) diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index 9940b4235..dc69f2df6 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -114,7 +114,6 @@ static PCPDynamicColumn* PCPDynamicColumn_new(PCPDynamicColumns* columns, const String_safeStrncpy(column->super.name, name, sizeof(column->super.name)); column->instances = false; column->super.enabled = true; - column->scale = false; size_t id = columns->count + LAST_PROCESSFIELD; Hashtable_put(columns->table, id, column); @@ -173,9 +172,6 @@ static void PCPDynamicColumn_parseFile(PCPDynamicColumns* columns, const char* p } else if (value && column && String_eq(key, "enabled")) { if (String_eq(value, "False") || String_eq(value, "false")) column->super.enabled = false; - } else if (value && column && String_eq(key, "scale")) { - if (String_eq(value, "True") || String_eq(value, "true")) - column->scale = true; } else if (value && column && String_eq(key, "metric")) { PCPDynamicColumn_parseMetric(columns, column, path, lineno, value); } @@ -267,7 +263,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri pmAtomValue atom; if (!PCPMetric_instance(this->id, proc->pid, pp->offset, &atom, type)) { - RichString_appendAscii(str, CRT_colors[DYNAMIC_RED], "no data"); + RichString_appendAscii(str, CRT_colors[METER_VALUE_ERROR], "no data"); return; } @@ -313,7 +309,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri RichString_appendAscii(str, attr, buffer); break; default: - attr = CRT_colors[DYNAMIC_RED]; + attr = CRT_colors[METER_VALUE_ERROR]; RichString_appendAscii(str, attr, "no type"); break; } diff --git a/pcp/PCPDynamicColumn.h b/pcp/PCPDynamicColumn.h index af4b56884..341f19872 100644 --- a/pcp/PCPDynamicColumn.h +++ b/pcp/PCPDynamicColumn.h @@ -16,7 +16,6 @@ typedef struct PCPDynamicColumn_ { char* metricName; size_t id; /* identifier for metric array lookups */ bool instances; - bool scale; } PCPDynamicColumn; typedef struct PCPDynamicColumns_ { diff --git a/pcp/PCPDynamicScreen.c b/pcp/PCPDynamicScreen.c index 75bdbea27..f7c52b31c 100644 --- a/pcp/PCPDynamicScreen.c +++ b/pcp/PCPDynamicScreen.c @@ -28,19 +28,16 @@ in the source distribution for its full text. void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynamicColumns* columns) { for (unsigned int i = 0; i < screens->count; i++) { - PCPDynamicScreen* screen = Hashtable_get(screens->table, i); + PCPDynamicScreen *screen = Hashtable_get(screens->table, i); if (!screen) return; - for (unsigned int j = 0; j < screen->totalColumns; j++) { - PCPDynamicColumn* column = &screen->columns[j]; - - column->id = columns->offset + columns->cursor; + for (unsigned int column = 0; column < screen->totalColumns; column++) { + screen->columns[column].id = columns->offset + columns->cursor; columns->cursor++; - - Platform_addMetric(column->id, column->metricName); + Platform_addMetric(screen->columns[column].id, screen->columns[column].metricName); size_t id = columns->count + LAST_PROCESSFIELD; - Hashtable_put(columns->table, id, column); + Hashtable_put(columns->table, id, &screen->columns[column]); columns->count++; } } @@ -73,7 +70,6 @@ static PCPDynamicColumn* PCPDynamicScreen_lookupMetric(PCPDynamicScreen* screen, column->instances = false; column->super.belongToDynamicScreen = true; column->super.enabled = true; - column->scale = false; return column; } @@ -125,9 +121,6 @@ static void PCPDynamicScreen_parseColumn(PCPDynamicScreen* screen, const char* p } else if (String_eq(p, "enabled")) { /* default is True */ if (String_eq(value, "False") || String_eq(value, "false")) column->super.enabled = false; - } else if (String_eq(p, "scale")) { - if (String_eq(value, "True") || String_eq(value, "true")) - column->scale = true; } } } diff --git a/pcp/PCPGenericData.c b/pcp/PCPGenericData.c index 68bf8140c..fd9a50b61 100644 --- a/pcp/PCPGenericData.c +++ b/pcp/PCPGenericData.c @@ -83,22 +83,6 @@ void PCPGenericData_removeAllFields(PCPGenericData* this) } } -#define PrintField(format, value) \ - xSnprintf(buffer, sizeof(buffer), format, width, value);\ - RichString_appendAscii(str, attr, buffer); - -#define KBytesField(format, val) \ - pmConvScale(format, gf->value, &gf->units, &atom, &kbyte_scale);\ - Process_printKBytes(str, val, coloring); - -#define TimeField(format, val) \ - pmConvScale(format, gf->value, &gf->units, &atom, &sec_scale);\ - Process_printTime(str, val, coloring); - -#define CountField(format, val) \ - pmConvScale(format, gf->value, &gf->units, &atom, &count_scale);\ - Process_printCount(str, val, coloring); - static void PCPGenericData_writeField(const GenericData* this, RichString* str, int field) { const PCPGenericData* gg = (const PCPGenericData*) this; PCPGenericDataField* gf = (PCPGenericDataField*)Hashtable_get(gg->fields, field); @@ -108,7 +92,6 @@ static void PCPGenericData_writeField(const GenericData* this, RichString* str, const ProcessField* fields = this->settings->ss->fields; char buffer[256]; int attr = CRT_colors[DEFAULT_COLOR]; - bool coloring = this->settings->highlightMegabytes; DynamicColumn* dc = Hashtable_get(this->settings->dynamicColumns, fields[field]); if (!dc || !dc->enabled) @@ -117,21 +100,6 @@ static void PCPGenericData_writeField(const GenericData* this, RichString* str, PCPDynamicColumn* column = (PCPDynamicColumn*) dc; bool instances = column->instances; - bool scaled = column->scale; - pmAtomValue atom; - pmUnits kbyte_scale; - - kbyte_scale.dimSpace = 1; - kbyte_scale.scaleSpace = PM_SPACE_KBYTE; - - pmUnits sec_scale; - sec_scale.dimTime = 1; - sec_scale.scaleTime = PM_TIME_SEC; - - pmUnits count_scale; - count_scale.dimCount = 1; - count_scale.scaleCount = PM_COUNT_ONE; - int width = column->super.width; if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH) width = DYNAMIC_DEFAULT_COLUMN_WIDTH; @@ -157,104 +125,32 @@ static void PCPGenericData_writeField(const GenericData* this, RichString* str, RichString_appendAscii(str, attr, buffer); break; case PM_TYPE_32: - if (scaled) { - if (gf->units.dimSpace) { - KBytesField(PM_TYPE_32, atom.l); - break; - } else if (gf->units.dimTime) { - TimeField(PM_TYPE_32, atom.l); - break; - } else { - CountField(PM_TYPE_32, atom.l); - break; - } - } else { - PrintField("%*d ", gf->value->l); - break; - } + xSnprintf(buffer, sizeof(buffer), "%*d ", width, gf->value->l); + RichString_appendAscii(str, attr, buffer); + break; case PM_TYPE_U32: - if (scaled) { - if (gf->units.dimSpace) { - KBytesField(PM_TYPE_U32, atom.ul); - break; - } else if (gf->units.dimTime) { - TimeField(PM_TYPE_U32, atom.ul); - break; - } else { - CountField(PM_TYPE_U32, atom.ul); - break; - } - } else { - PrintField("%*u ", gf->value->ul); - break; - } + xSnprintf(buffer, sizeof(buffer), "%*u ", width, gf->value->ul); + RichString_appendAscii(str, attr, buffer); + break; case PM_TYPE_64: - if (scaled) { - if (gf->units.dimSpace) { - KBytesField(PM_TYPE_64, atom.ll); - break; - } else if (gf->units.dimTime) { - TimeField(PM_TYPE_64, atom.ll); - break; - } else { - CountField(PM_TYPE_64, atom.ll); - break; - } - } else { - PrintField("%*lld ", (long long)gf->value->ll); - break; - } + xSnprintf(buffer, sizeof(buffer), "%*lld ", width, (long long) gf->value->ll); + RichString_appendAscii(str, attr, buffer); + break; case PM_TYPE_U64: - if (scaled) { - if (gf->units.dimSpace) { - KBytesField(PM_TYPE_U64, atom.ull); - break; - } else if (gf->units.dimTime) { - TimeField(PM_TYPE_U64, atom.ull); - break; - } else { - CountField(PM_TYPE_U64, atom.ull); - break; - } - } else { - PrintField("%*llu ", (unsigned long long)gf->value->ull); - break; - } + xSnprintf(buffer, sizeof(buffer), "%*llu ", width, (unsigned long long) gf->value->ull); + RichString_appendAscii(str, attr, buffer); + break; case PM_TYPE_FLOAT: - if (scaled) { - if (gf->units.dimSpace) { - KBytesField(PM_TYPE_FLOAT, atom.f); - break; - } else if (gf->units.dimTime) { - TimeField(PM_TYPE_FLOAT, atom.f); - break; - } else { - CountField(PM_TYPE_FLOAT, atom.f); - break; - } - } else { - PrintField("%*.2f ", (double)gf->value->f); - break; - } + xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, (double) gf->value->f); + RichString_appendAscii(str, attr, buffer); + break; case PM_TYPE_DOUBLE: - if (scaled) { - if (gf->units.dimSpace) { - KBytesField(PM_TYPE_DOUBLE, atom.d); - break; - } else if (gf->units.dimTime) { - TimeField(PM_TYPE_DOUBLE, atom.d); - break; - } else { - CountField(PM_TYPE_DOUBLE, atom.d); - break; - } - } else { - PrintField("%*.2f ", gf->value->d); - break; - } + xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, gf->value->d); + RichString_appendAscii(str, attr, buffer); + break; default: attr = CRT_colors[DYNAMIC_RED]; - RichString_appendAscii(str, attr, "no data "); + RichString_appendAscii(str, attr, "no data"); break; } } diff --git a/pcp/PCPGenericData.h b/pcp/PCPGenericData.h index e038921a4..2ed859e62 100644 --- a/pcp/PCPGenericData.h +++ b/pcp/PCPGenericData.h @@ -26,8 +26,6 @@ typedef struct PCPGenericDataField_ { int interInst; int type; - - pmUnits units; } PCPGenericDataField; typedef struct PCPGenericData_ { diff --git a/pcp/PCPGenericDataList.c b/pcp/PCPGenericDataList.c index 9618d448f..7614e11a0 100644 --- a/pcp/PCPGenericDataList.c +++ b/pcp/PCPGenericDataList.c @@ -165,7 +165,6 @@ static int PCPGenericDataList_updateGenericDataList(PCPGenericDataList* this) { field->pmid = column->id; field->offset = offset; field->interInst = interInst; - field->units = PCPMetric_units(column->id); } } } diff --git a/pcp/PCPMetric.c b/pcp/PCPMetric.c index 8fa4083f0..25a2196c1 100644 --- a/pcp/PCPMetric.c +++ b/pcp/PCPMetric.c @@ -29,10 +29,6 @@ int PCPMetric_type(PCPMetric metric) { return pcp->descs[metric].type; } -pmUnits PCPMetric_units(PCPMetric metric) { - return pcp->descs[metric].units; -} - pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type) { if (pcp->result == NULL) return NULL; diff --git a/pcp/PCPMetric.h b/pcp/PCPMetric.h index 999458311..3f7e9cc7e 100644 --- a/pcp/PCPMetric.h +++ b/pcp/PCPMetric.h @@ -166,8 +166,6 @@ bool PCPMetric_iterate(PCPMetric metric, int* instp, int* offsetp); pmAtomValue* PCPMetric_values(PCPMetric metric, pmAtomValue* atom, int count, int type); -pmUnits PCPMetric_units(PCPMetric metric); - const pmDesc* PCPMetric_desc(PCPMetric metric); int PCPMetric_type(PCPMetric metric); diff --git a/pcp/screens/filesys b/pcp/screens/filesys index d1f757a8e..12b5854fb 100644 --- a/pcp/screens/filesys +++ b/pcp/screens/filesys @@ -10,68 +10,57 @@ avail.heading = AVAIL avail.caption = avail avail.width = -12 avail.metric = filesys.avail -avail.scale = true blocksize.heading = BSIZE blocksize.caption = blocksize blocksize.width = -12 blocksize.metric = filesys.blocksize blocksize.enabled = false -blocksize.scale = true capacity.heading = SIZE capacity.caption = capacity capacity.width = -12 capacity.metric = filesys.capacity -capacity.scale = true free.heading = FREE free.caption = free free.width = -12 free.metric = filesys.free -free.scale = true freefiles.heading = FREEF freefiles.caption = freefiles freefiles.width = -12 freefiles.metric = filesys.freefiles -freefiles.scale = true full.heading = FULL full.caption = full full.width = -12 full.metric = filesys.full #full.percent = true -full.scale = true maxfiles.heading = MAXF maxfiles.caption = maxfiles maxfiles.width = -12 maxfiles.metric = filesys.maxfiles maxfiles.enabled = false -maxfiles.scale = true readonly.heading = RO readonly.caption = readonly readonly.width = -12 readonly.metric = filesys.readonly readonly.enabled = false -readonly.scale = true used.heading = USED used.caption = used used.width = -12 used.metric = filesys.used -used.scale = true usedfiles.heading = USEDF usedfiles.caption = usedfiles usedfiles.metric = filesys.usedfiles usedfiles.enabled = false -usedfiles.scale = true mountdir.heading = Mount point mountdir.caption = mountdir mountdir.width = -12 mountdir.metric = filesys.mountdir -mountdir.scale = true From f65ef79beb4dfd0391471ded38a97618ff028f30 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Fri, 11 Nov 2022 20:21:59 +1100 Subject: [PATCH 9/9] Correctly handling PCP metric units and column widths. Adds support for new 'format' keyword, updates to screens config files, and some minor cleanup here & there. --- AvailableColumnsPanel.c | 2 +- CommandLine.c | 4 +- DynamicColumn.h | 16 +-- GenericDataList.h | 10 +- Makefile.am | 2 + ProcessList.c | 2 +- ScreenManager.c | 4 +- ScreensPanel.c | 2 +- Settings.c | 33 +++--- Settings.h | 2 +- pcp/PCPDynamicColumn.c | 228 +++++++++++++++++++++++++++++++-------- pcp/PCPDynamicColumn.h | 16 +++ pcp/PCPDynamicScreen.c | 63 +++++++---- pcp/PCPDynamicScreen.h | 10 +- pcp/PCPGenericData.c | 77 ++----------- pcp/PCPGenericData.h | 19 ++-- pcp/PCPGenericDataList.c | 49 ++++----- pcp/Platform.c | 3 + pcp/screens/biosnoop | 10 +- pcp/screens/cgroups | 69 ++++++------ pcp/screens/cgroupsio | 30 ++---- pcp/screens/cgroupsmem | 47 ++++++++ pcp/screens/execsnoop | 9 +- pcp/screens/exitsnoop | 21 ++-- pcp/screens/filesys | 65 +++++------ pcp/screens/opensnoop | 21 ++-- 26 files changed, 456 insertions(+), 358 deletions(-) create mode 100644 pcp/screens/cgroupsmem diff --git a/AvailableColumnsPanel.c b/AvailableColumnsPanel.c index 997229581..979fd54d6 100644 --- a/AvailableColumnsPanel.c +++ b/AvailableColumnsPanel.c @@ -85,7 +85,7 @@ const PanelClass AvailableColumnsPanel_class = { static void AvailableColumnsPanel_addDynamicColumn(ht_key_t key, void* value, void* data) { const DynamicColumn* column = (const DynamicColumn*) value; - if (column->belongToDynamicScreen) + if (column->hasDynamicScreen) return; Panel* super = (Panel*) data; diff --git a/CommandLine.c b/CommandLine.c index 068fb05cb..e94f315b0 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -331,9 +331,11 @@ int CommandLine_run(const char* name, int argc, char** argv) { ProcessList* pl = ProcessList_new(ut, dm, dc, flags.pidMatchList, flags.userId); Settings* settings = Settings_new(pl->activeCPUs, dc); - +fprintf(stderr, "Settings created\n"); Hashtable* dt = DynamicScreens_new(settings); +fprintf(stderr, "DynamicScreens created\n"); GenericDataList* gl = GenericDataList_new(); +fprintf(stderr, "GenericDataList created\n"); pl->settings = settings; if (gl) diff --git a/DynamicColumn.h b/DynamicColumn.h index dca373c8d..9b472843e 100644 --- a/DynamicColumn.h +++ b/DynamicColumn.h @@ -8,17 +8,17 @@ #include "RichString.h" -#define DYNAMIC_MAX_COLUMN_WIDTH 28 +#define DYNAMIC_MAX_COLUMN_WIDTH 64 #define DYNAMIC_DEFAULT_COLUMN_WIDTH -5 typedef struct DynamicColumn_ { - char name[32]; /* unique, internal-only name */ - char* heading; /* displayed in main screen */ - char* caption; /* displayed in setup menu (short name) */ - char* description; /* displayed in setup menu (detail) */ - int width; /* display width +/- for value alignment */ - bool belongToDynamicScreen; /* belong to DynamicScreen or ProcessList screen? */ - bool enabled; /* false == ignore this column */ + char name[32]; /* unique, internal-only name */ + char* heading; /* displayed in main screen */ + char* caption; /* displayed in setup menu (short name) */ + char* description; /* displayed in setup menu (detail) */ + int width; /* display width +/- for value alignment */ + bool hasDynamicScreen; /* from a DynamicScreen or ProcessList screen? */ + bool enabled; /* false == ignore this column */ } DynamicColumn; Hashtable* DynamicColumns_new(void); diff --git a/GenericDataList.h b/GenericDataList.h index a03495e19..9b1787109 100644 --- a/GenericDataList.h +++ b/GenericDataList.h @@ -55,8 +55,6 @@ void GenericDataList_delete(GenericDataList* gl); /* One GenericData List */ void GenericDataList_addList(void); -//void GenericDataList_removeList(GenericDataList* g); - /* struct GenericData */ GenericData* GenericDataList_getGenericData(GenericDataList* this, GenericData_New constructor); @@ -64,15 +62,9 @@ void GenericDataList_addGenericData(GenericDataList* this, GenericData* g); void GenericDataList_removeGenericData(GenericDataList* this); -/* helpers functions */ +/* helper functions */ void GenericDataList_setPanel(GenericDataList* this, Panel* panel); -void GenericDataList_printHeader(const GenericDataList* this, RichString* header); // TODO - -void GenericDataList_expandTree(GenericDataList* this); // TODO - -void GenericDataList_collapseAllBranches(GenericDataList* this); // TODO - void GenericDataList_rebuildPanel(GenericDataList* this); void GenericDataList_scan(GenericDataList* this, bool pauseUpdate); diff --git a/Makefile.am b/Makefile.am index 2d0c87945..7a914601e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -381,6 +381,7 @@ endif # -------------------------- pcp_platform_headers = \ + linux/CGroupUtils.h \ linux/PressureStallMeter.h \ linux/ZramMeter.h \ linux/ZramStats.h \ @@ -399,6 +400,7 @@ pcp_platform_headers = \ zfs/ZfsCompressedArcMeter.h pcp_platform_sources = \ + linux/CGroupUtils.c \ linux/PressureStallMeter.c \ linux/ZramMeter.c \ pcp/PCPDynamicColumn.c \ diff --git a/ProcessList.c b/ProcessList.c index 398f84dca..624f01d42 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -86,7 +86,7 @@ static const char* alignedDynamicColumnTitle(const ProcessList* this, int key, c const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key); if (column == NULL) return "- "; - if (!column->enabled) + if (column->enabled == false) return ""; int width = column->width; if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH) diff --git a/ScreenManager.c b/ScreenManager.c index 206ddf35c..1fcd755fb 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -140,7 +140,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi // scan processes first - some header values are calculated there ProcessList_scan(pl, this->state->pauseUpdate); - if (this->settings->ss->generic) + if (this->settings->ss->dynamic) GenericDataList_scan(gl, this->state->pauseUpdate); // always update header, especially to avoid gaps in graph meters @@ -152,7 +152,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi *redraw = true; } if (*redraw) { - if (this->settings->ss->generic) { + if (this->settings->ss->dynamic) { *force_redraw = true; Vector_prune(pl->panel->items); diff --git a/ScreensPanel.c b/ScreensPanel.c index ba1c32c99..a2324784b 100644 --- a/ScreensPanel.c +++ b/ScreensPanel.c @@ -275,7 +275,7 @@ static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super); if (newFocus && oldFocus != newFocus) { ColumnsPanel_fill(this->columns, newFocus->ss, this->settings->dynamicColumns); - if (newFocus->ss->generic) { + if (newFocus->ss->dynamic) { char* currentScreen = newFocus->ss->name; DynamicScreen_availableColumns(currentScreen); } else { diff --git a/Settings.c b/Settings.c index d45b73ecc..0f8db18ff 100644 --- a/Settings.c +++ b/Settings.c @@ -285,7 +285,7 @@ ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* default .treeView = false, .treeViewAlwaysByPID = false, .allBranchesCollapsed = false, - .generic = false, + .dynamic = false, }; ScreenSettings_readFields(ss, this->dynamicColumns, defaults->columns); @@ -369,9 +369,9 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } else if (String_eq(option[0], "tree_view") && this->config_version <= 2) { screen = Settings_defaultScreens(this); screen->treeView = atoi(option[1]); - } else if (String_eq(option[0], "generic_screen") && this->config_version <= 2) { + } else if (String_eq(option[0], "dynamic_screen") && this->config_version <= 2) { screen = Settings_defaultScreens(this); - screen->generic = atoi(option[1]); + screen->dynamic = atoi(option[1]); } else if (String_eq(option[0], "tree_view_always_by_pid") && this->config_version <= 2) { // old (no screen) naming also supported for backwards compatibility screen = Settings_defaultScreens(this); @@ -502,9 +502,9 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } else if (String_eq(option[0], ".tree_view")) { if (screen) screen->treeView = atoi(option[1]); - } else if (String_eq(option[0], ".generic_screen")) { + } else if (String_eq(option[0], ".dynamic_screen")) { if (screen) - screen->generic = atoi(option[1]); + screen->dynamic = atoi(option[1]); } else if (String_eq(option[0], ".tree_view_always_by_pid")) { if (screen) screen->treeViewAlwaysByPID = atoi(option[1]); @@ -530,7 +530,7 @@ static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns fprintf(fd, "%s%s", sep, pName); } else if (fields[i] >= LAST_PROCESSFIELD && byName) { const char* pName = toFieldName(columns, fields[i]); - fprintf(fd, " Dynamic(%s)", pName); + fprintf(fd, "%sDynamic(%s)", sep, pName); } else { // This "-1" is for compatibility with the older enum format. fprintf(fd, "%s%d", sep, (int) fields[i] - 1); @@ -643,20 +643,21 @@ int Settings_write(const Settings* this, bool onCrash) { printSettingInteger("tree_view_always_by_pid", this->screens[0]->treeViewAlwaysByPID); printSettingInteger("all_branches_collapsed", this->screens[0]->allBranchesCollapsed); +fprintf(stderr, "writing %d screens\n", (int)this->nScreens); for (unsigned int i = 0; i < this->nScreens; i++) { - if (this->screens[i]->generic) - continue; ScreenSettings* ss = this->screens[i]; fprintf(fd, "screen:%s=", ss->name); writeFields(fd, ss->fields, this->dynamicColumns, true, separator); - printSettingString(".sort_key", toFieldName(this->dynamicColumns, ss->sortKey)); - printSettingString(".tree_sort_key", toFieldName(this->dynamicColumns, ss->treeSortKey)); - printSettingInteger(".tree_view", ss->treeView); - printSettingInteger(".tree_view_always_by_pid", ss->treeViewAlwaysByPID); - printSettingInteger(".sort_direction", ss->direction); - printSettingInteger(".tree_sort_direction", ss->treeDirection); - printSettingInteger(".all_branches_collapsed", ss->allBranchesCollapsed); - printSettingInteger(".generic_screen", ss->generic); + if (ss->dynamic == false) { + printSettingString(".sort_key", toFieldName(this->dynamicColumns, ss->sortKey)); + printSettingString(".tree_sort_key", toFieldName(this->dynamicColumns, ss->treeSortKey)); + printSettingInteger(".tree_view", ss->treeView); + printSettingInteger(".tree_view_always_by_pid", ss->treeViewAlwaysByPID); + printSettingInteger(".sort_direction", ss->direction); + printSettingInteger(".tree_sort_direction", ss->treeDirection); + printSettingInteger(".all_branches_collapsed", ss->allBranchesCollapsed); + } + printSettingInteger(".dynamic_screen", ss->dynamic); } #undef printSettingString diff --git a/Settings.h b/Settings.h index 599b952d7..4dfe6fe41 100644 --- a/Settings.h +++ b/Settings.h @@ -44,7 +44,7 @@ typedef struct { bool treeView; bool treeViewAlwaysByPID; bool allBranchesCollapsed; - bool generic; + bool dynamic; } ScreenSettings; typedef struct Settings_ { diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index dc69f2df6..27e63b527 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -1,8 +1,8 @@ /* htop - PCPDynamicColumn.c -(C) 2021 Sohaib Mohammed -(C) 2021 htop dev team -(C) 2021 Red Hat, Inc. +(C) 2021-2022 Sohaib Mohammed +(C) 2021-2022 htop dev team +(C) 2021-2022 Red Hat Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ @@ -14,6 +14,8 @@ in the source distribution for its full text. #include #include #include +#include +#include #include #include #include @@ -28,6 +30,7 @@ in the source distribution for its full text. #include "RichString.h" #include "XUtils.h" +#include "linux/CGroupUtils.h" #include "pcp/PCPProcess.h" #include "pcp/PCPMetric.h" @@ -112,6 +115,7 @@ 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->percent = false; column->instances = false; column->super.enabled = true; @@ -165,11 +169,13 @@ static void PCPDynamicColumn_parseFile(PCPDynamicColumns* columns, const char* p } else if (value && column && String_eq(key, "description")) { free_and_xStrdup(&column->super.description, value); } else if (value && column && String_eq(key, "width")) { - column->super.width = strtoul(value, NULL, 10); + column->width = strtol(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, "enabled")) { + } else if (value && column && String_eq(key, "default")) { if (String_eq(value, "False") || String_eq(value, "false")) column->super.enabled = false; } else if (value && column && String_eq(key, "metric")) { @@ -245,7 +251,7 @@ void PCPDynamicColumns_init(PCPDynamicColumns* columns) { free(path); } -static void PCPDynamicColumns_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) { +static void PCPDynamicColumn_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) { PCPDynamicColumn* column = (PCPDynamicColumn*) value; free(column->metricName); free(column->super.heading); @@ -254,65 +260,203 @@ static void PCPDynamicColumns_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_U } void PCPDynamicColumns_done(Hashtable* table) { - Hashtable_foreach(table, PCPDynamicColumns_free, NULL); + Hashtable_foreach(table, PCPDynamicColumn_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 = PCPMetric_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; // Process_printRate + else if (units.dimSpace) + column->super.width = 5; // Process_printBytes + else if (units.dimTime) + column->super.width = 8; // Process_printTime + else + column->super.width = 11; // Process_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 pmAtomValue* atomvalue) { + 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, "no data"); + RichString_appendnAscii(str, CRT_colors[METER_VALUE_ERROR], 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]; + PCPMetric_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) { + Process_printPercentage(value, buffer, sizeof(buffer), width, &attr); + return; + } + if (strcmp(column->format, "process") == 0) { + n = xSnprintf(buffer, sizeof(buffer), "%*d ", Process_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) + Process_printRate(str, value, coloring); + else if (units.dimSpace) + Process_printBytes(str, value, coloring); + else if (units.dimTime) + Process_printTime(str, value / 10 /* hundreds of a second */, coloring); + else if (units.dimCount) + Process_printCount(str, value, coloring); + else + Process_printCount(str, value, 0); /* e.g. PID */ +} + +void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str) { + const PCPProcess* pp = (const PCPProcess*) proc; + const pmDesc* desc = PCPMetric_desc(this->id); + pmAtomValue atom; + pmAtomValue* ap = &atom; + if (!PCPMetric_instance(this->id, proc->pid, pp->offset, ap, desc->type)) + ap = NULL; + + PCPDynamicColumn_writeAtomValue(this, str, proc->settings, this->id, proc->pid, desc, &atom); } int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key) { diff --git a/pcp/PCPDynamicColumn.h b/pcp/PCPDynamicColumn.h index 341f19872..bd0ddabb5 100644 --- a/pcp/PCPDynamicColumn.h +++ b/pcp/PCPDynamicColumn.h @@ -1,8 +1,17 @@ #ifndef HEADER_PCPDynamicColumn #define HEADER_PCPDynamicColumn +#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 + #include "DynamicColumn.h" #include "Hashtable.h" #include "Process.h" @@ -14,7 +23,10 @@ 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 percent; bool instances; } PCPDynamicColumn; @@ -29,8 +41,12 @@ 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 pmDesc* desc, const pmAtomValue* atomvalue); + int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key); #endif diff --git a/pcp/PCPDynamicScreen.c b/pcp/PCPDynamicScreen.c index f7c52b31c..513d338c5 100644 --- a/pcp/PCPDynamicScreen.c +++ b/pcp/PCPDynamicScreen.c @@ -13,9 +13,6 @@ in the source distribution for its full text. #include #include #include -#include -#include -#include #include "Macros.h" #include "Platform.h" @@ -32,12 +29,14 @@ void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynam if (!screen) return; - for (unsigned int column = 0; column < screen->totalColumns; column++) { - screen->columns[column].id = columns->offset + columns->cursor; + for (unsigned int j = 0; j < screen->totalColumns; j++) { + PCPDynamicColumn* column = &screen->columns[j]; + + column->id = columns->offset + columns->cursor; columns->cursor++; - Platform_addMetric(screen->columns[column].id, screen->columns[column].metricName); + Platform_addMetric(column->id, column->metricName); size_t id = columns->count + LAST_PROCESSFIELD; - Hashtable_put(columns->table, id, &screen->columns[column]); + Hashtable_put(columns->table, id, column); columns->count++; } } @@ -67,8 +66,7 @@ static PCPDynamicColumn* PCPDynamicScreen_lookupMetric(PCPDynamicScreen* screen, column->metricName = metricName; xSnprintf(column->super.name, bytes, "%s_%s", screen->super.name, name); - column->instances = false; - column->super.belongToDynamicScreen = true; + column->super.hasDynamicScreen = true; column->super.enabled = true; return column; @@ -118,7 +116,7 @@ static void PCPDynamicScreen_parseColumn(PCPDynamicScreen* screen, const char* p } else if (String_eq(p, "instances")) { if (String_eq(value, "True") || String_eq(value, "true")) column->instances = true; - } else if (String_eq(p, "enabled")) { /* default is True */ + } else if (String_eq(p, "default")) { /* displayed by default */ if (String_eq(value, "False") || String_eq(value, "false")) column->super.enabled = false; } @@ -165,6 +163,7 @@ static bool PCPDynamicScreen_uniqueName(char* key, PCPDynamicScreens* screens) { 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); @@ -209,6 +208,8 @@ static void PCPDynamicScreen_parseFile(PCPDynamicScreens* screens, const char* p 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 && String_eq(key, "caption")) { @@ -219,9 +220,9 @@ static void PCPDynamicScreen_parseFile(PCPDynamicScreens* screens, const char* p free_and_xStrdup(&screen->super.sortKey, value); } else if (value && screen && String_eq(key, "sortDirection")) { screen->super.direction = strtoul(value, NULL, 10); - } else if (value && screen && String_eq(key, "enabled")) { /* default is false */ - if (String_eq(value, "True") || String_eq(value, "true")) - screen->enabled = true; + } else if (value && screen && String_eq(key, "default")) { /* default is true */ + if (String_eq(value, "False") || String_eq(value, "false")) + screen->defaultEnabled = false; } else if (value && screen) { PCPDynamicScreen_parseColumn(screen, path, lineno, key, value); } @@ -291,7 +292,6 @@ void PCPDynamicScreens_init(PCPDynamicScreens* screens) { static void PCPDynamicScreens_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) { PCPDynamicScreen* screen = (PCPDynamicScreen*) value; - free(screen->instances); free(screen->super.caption); free(screen->super.fields); free(screen->super.sortKey); @@ -315,12 +315,37 @@ void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settin ScreenSettings* ss; char* sortKey; + /* determine whether htoprc contains dynamic screens */ + bool firstTime = true; + for (size_t i = 0; i < settings->nScreens; i++) { + if (settings->screens[i]->dynamic) + firstTime = false; + } + for (size_t i = 0; i < screens->count; i++) { ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i); if (!ds) continue; - if(!ds->enabled) + + if (pmDebugOptions.appl0) { + fprintf(stderr, "screen[%zu] %s ", i, ds->super.name); + fprintf(stderr, "enabled=%d default=%d\n", ds->enabled, ds->defaultEnabled); + } + + if (firstTime == false) { + bool found = false; + for (size_t j = 0; j < settings->nScreens; j++) { + if (strcmp(settings->screens[j]->name, ds->super.name) == 0) { + found = true; + break; + } + } + if (found == true) + continue; + } + else if (ds->defaultEnabled == false) { continue; + } char* columns = formatFields(ds); @@ -333,12 +358,8 @@ void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settin }; ss = Settings_newScreen(settings, &screen); - - ss->generic = true; - if (ds->super.direction) - ss->direction = ds->super.direction; - - free(columns); + ss->direction = ds->super.direction; + ss->dynamic = true; } } diff --git a/pcp/PCPDynamicScreen.h b/pcp/PCPDynamicScreen.h index 0c6e6f2f2..2b68dc5d3 100644 --- a/pcp/PCPDynamicScreen.h +++ b/pcp/PCPDynamicScreen.h @@ -10,14 +10,16 @@ #include "Hashtable.h" #include "Settings.h" -#include "pcp/PCPDynamicColumn.h" +struct PCPDynamicColumn_; +struct PCPDynamicColumns_; typedef struct PCPDynamicScreen_ { DynamicScreen super; - PCPDynamicColumn* columns; + struct PCPDynamicColumn_* columns; + char* displayInstances; size_t totalColumns; - char* instances; + bool defaultEnabled; bool enabled; } PCPDynamicScreen; @@ -26,7 +28,7 @@ typedef struct PCPDynamicScreens_ { size_t count; /* count of dynamic screens discovered by scan */ } PCPDynamicScreens; -void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynamicColumns* columns); +void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, struct PCPDynamicColumns_* columns); void PCPDynamicScreens_init(PCPDynamicScreens* screens); diff --git a/pcp/PCPGenericData.c b/pcp/PCPGenericData.c index fd9a50b61..031ce07fd 100644 --- a/pcp/PCPGenericData.c +++ b/pcp/PCPGenericData.c @@ -56,11 +56,8 @@ void GenericData_delete(Object* cast) { PCPGenericDataField* PCPGenericData_addField(PCPGenericData* this) { PCPGenericDataField* field = xCalloc(1, sizeof(PCPGenericDataField)); - pmAtomValue* atom = xCalloc(1, sizeof(pmAtomValue)); - field->value = atom; Hashtable_put(this->fields, this->fieldsCount, field); - this->fieldsCount++; return field; @@ -70,90 +67,32 @@ void PCPGenericData_removeField(PCPGenericData* this) { int idx = this->fieldsCount - 1; - PCPGenericDataField* field = Hashtable_get(this->fields, idx); - free(field->value); Hashtable_remove(this->fields, idx); this->fieldsCount--; } void PCPGenericData_removeAllFields(PCPGenericData* this) { - for (size_t i = this->fieldsCount; i > 0; i--) { + for (size_t i = this->fieldsCount; i > 0; i--) PCPGenericData_removeField(this); - } } static void PCPGenericData_writeField(const GenericData* this, RichString* str, int field) { const PCPGenericData* gg = (const PCPGenericData*) this; + if (!gg) + return; PCPGenericDataField* gf = (PCPGenericDataField*)Hashtable_get(gg->fields, field); if (!gf) return; - const ProcessField* fields = this->settings->ss->fields; - char buffer[256]; - int attr = CRT_colors[DEFAULT_COLOR]; - - DynamicColumn* dc = Hashtable_get(this->settings->dynamicColumns, fields[field]); + const Settings* settings = this->settings; + const ProcessField* fields = settings->ss->fields; + DynamicColumn* dc = Hashtable_get(settings->dynamicColumns, fields[field]); if (!dc || !dc->enabled) return; - PCPDynamicColumn* column = (PCPDynamicColumn*) dc; - bool instances = column->instances; - - int width = column->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 (instances) { - char* instName; - attr = CRT_colors[DYNAMIC_GRAY]; - - PCPMetric_externalName(gf->pmid, gf->interInst, &instName); - - xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, 250, instName); - RichString_appendAscii(str, attr, buffer); - } else { - switch (gf->type) { - case PM_TYPE_STRING: - attr = CRT_colors[DYNAMIC_GREEN]; - xSnprintf(buffer, sizeof(buffer), "%*.*s ", width, 250, gf->value->cp); - RichString_appendAscii(str, attr, buffer); - break; - case PM_TYPE_32: - xSnprintf(buffer, sizeof(buffer), "%*d ", width, gf->value->l); - RichString_appendAscii(str, attr, buffer); - break; - case PM_TYPE_U32: - xSnprintf(buffer, sizeof(buffer), "%*u ", width, gf->value->ul); - RichString_appendAscii(str, attr, buffer); - break; - case PM_TYPE_64: - xSnprintf(buffer, sizeof(buffer), "%*lld ", width, (long long) gf->value->ll); - RichString_appendAscii(str, attr, buffer); - break; - case PM_TYPE_U64: - xSnprintf(buffer, sizeof(buffer), "%*llu ", width, (unsigned long long) gf->value->ull); - RichString_appendAscii(str, attr, buffer); - break; - case PM_TYPE_FLOAT: - xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, (double) gf->value->f); - RichString_appendAscii(str, attr, buffer); - break; - case PM_TYPE_DOUBLE: - xSnprintf(buffer, sizeof(buffer), "%*.2f ", width, gf->value->d); - RichString_appendAscii(str, attr, buffer); - break; - default: - attr = CRT_colors[DYNAMIC_RED]; - RichString_appendAscii(str, attr, "no data"); - break; - } - } + PCPDynamicColumn* pc = (PCPDynamicColumn*) dc; + PCPDynamicColumn_writeAtomValue(pc, str, settings, gf->id, gf->instance, gf->desc, &gf->value); } static int PCPGenericData_compareByKey(const GenericData* v1, const GenericData* v2, int key) { diff --git a/pcp/PCPGenericData.h b/pcp/PCPGenericData.h index 2ed859e62..80262b0d3 100644 --- a/pcp/PCPGenericData.h +++ b/pcp/PCPGenericData.h @@ -17,27 +17,20 @@ in the source distribution for its full text. typedef struct PCPGenericDataField_ { - pmAtomValue* value; - - pmID pmid; - + pmAtomValue value; + size_t id; /* identifier for metric array lookups */ int offset; - - int interInst; - - int type; + int instance; + const pmDesc* desc; } PCPGenericDataField; typedef struct PCPGenericData_ { GenericData super; - - /* default result offset to use for searching proc metrics */ - unsigned int offset; - Hashtable* fields; /* PCPGenericDataFields */ + /* default result offset to use for searching metrics with instances */ + unsigned int offset; size_t fieldsCount; - int sortKey; } PCPGenericData; diff --git a/pcp/PCPGenericDataList.c b/pcp/PCPGenericDataList.c index 7614e11a0..62b6520f5 100644 --- a/pcp/PCPGenericDataList.c +++ b/pcp/PCPGenericDataList.c @@ -100,12 +100,7 @@ static int PCPGenericDataList_updateGenericDataList(PCPGenericDataList* this) { keyMetric = defineKeyMetric(settings); - if (!settings->ss->generic) - return 0; - - // one or more columns are added to pcp/screens and do not exist in - // pcp/columns, continue. - for (unsigned int i = 0 ; !fields[i]; i++) + if (!settings->ss->dynamic) return 0; // Instance Domain Validation @@ -116,10 +111,8 @@ static int PCPGenericDataList_updateGenericDataList(PCPGenericDataList* this) { if (!dc) return -1; - pmInDom indom = PCPMetric_InDom(dc->id); - if (keyInDom != indom) { + if (keyInDom != PCPMetric_InDom(dc->id)) return 0; - } } int requiredColumns = getColumnCount(fields); @@ -133,38 +126,32 @@ static int PCPGenericDataList_updateGenericDataList(PCPGenericDataList* this) { allocColumns(gl, requiredColumns, requiredRows); // fill - int interInst = -1, offset = -1; - while (PCPMetric_iterate(keyMetric->id, &interInst, &offset)) { + int instance = -1, offset = -1; + while (PCPMetric_iterate(keyMetric->id, &instance, &offset)) { for (unsigned int i = 0; fields[i]; i++) { - int metricType; - DynamicColumn* dc = Hashtable_get(settings->dynamicColumns, fields[i]); - if (!dc) + PCPDynamicColumn* column = (PCPDynamicColumn*) dc; + if (!column) return -1; - PCPDynamicColumn* column = (PCPDynamicColumn*) dc; - metricType = PCPMetric_type(column->id); + const pmDesc* desc = PCPMetric_desc(column->id); pmAtomValue value; - if (PCPMetric_instance(column->id, interInst, offset, &value, metricType)) { - GenericData* g; - - g = Hashtable_get(gl->genericDataTable, offset); - if (!g) - return -1; - + if (PCPMetric_instance(column->id, instance, offset, &value, desc->type)) { + GenericData* g = Hashtable_get(gl->genericDataTable, offset); PCPGenericData* gg = (PCPGenericData*) g; + if (!gg) + return -1; + gg->offset = offset; - PCPGenericDataField* field = (PCPGenericDataField*)Hashtable_get(gg->fields, i); + PCPGenericDataField* field = (PCPGenericDataField*) Hashtable_get(gg->fields, i); if (!field) return -1; - - gg->offset = offset; - *field->value = value; - field->type = metricType; - field->pmid = column->id; - field->offset = offset; - field->interInst = interInst; + field->id = column->id; + field->desc = desc; + field->value = value; + field->offset = offset; + field->instance = instance; } } } diff --git a/pcp/Platform.c b/pcp/Platform.c index 6efc825a2..95b79ba12 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -403,6 +403,9 @@ bool Platform_init(void) { Platform_getMaxCPU(); Platform_getMaxPid(); + Process_setupColumnWidths(); + PCPDynamicColumns_setupWidths(&pcp->columns); + return true; } diff --git a/pcp/screens/biosnoop b/pcp/screens/biosnoop index ad26db94f..0e1eb7231 100644 --- a/pcp/screens/biosnoop +++ b/pcp/screens/biosnoop @@ -3,13 +3,13 @@ # [biosnoop] -caption = BioSnoop -enabled = false +caption = Block I/O Snoop +default = false pid.heading = PID pid.caption = Process identifier -pid.width = -7 pid.metric = bpf.biosnoop.pid +pid.format = process disk.heading = DISK disk.caption = Device name @@ -23,20 +23,18 @@ rwbs.metric = bpf.biosnoop.rwbs bytes.heading = BYTES bytes.caption = I/O size in bytes -bytes.width = -7 bytes.metric = bpf.biosnoop.bytes lat.heading = LAT lat.caption = I/O latency -lat.width = -7 lat.metric = bpf.biosnoop.lat sector.heading = SECTOR sector.caption = Device sector -sector.width = -8 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 index bdd3e4264..b39ea7831 100644 --- a/pcp/screens/cgroups +++ b/pcp/screens/cgroups @@ -2,48 +2,39 @@ # pcp-htop(1) configuration file - see pcp-htop(5) # -[cgroup] -caption = CGroups -enabled = true - -cuser.heading = UTIME -cuser.caption = User CPU Time -cuser.width = -8 -cuser.metric = 1000 * rate(cgroup.cpu.stat.user) - -csystem.heading = STIME -csystem.caption = Kernel CPU Time -csystem.width = -8 -csystem.metric = 1000 * rate(cgroup.cpu.stat.system) - -cusage.heading = CPU% -cusage.caption = CPU Utilization -cusage.width = 6 -cusage.metric = 100 * (rate(cgroup.cpu.stat.usage) / hinv.ncpu) - -## different pmInDom, argh -#memory.heading = MEM -#memory.caption = Used memory -#memory.width = -6 -#memory.metric = cgroup.memory.current - -cpupressure.heading = CPU-PSI -cpupressure.caption = CPU Pressure Stall Information -cpupressure.width = -6 -cpupressure.metric = 1000 * rate (cgroup.pressure.cpu.some.total) - -mempressure.heading = MEM-PSI -mempressure.caption = Memory Pressure Stall Information -mempressure.width = -6 -mempressure.metric = 1000 * rate (cgroup.cgroup.pressure.memory.some.total) - -iopressure.heading = I/O-PSI -iopressure.caption = I/O Pressure Stall Information -iopressure.width = -6 -iopressure.metric = 1000 * rate (cgroup.cgroup.pressure.io.some.total) +[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) + +system_cpu.heading = STIME +system_cpu.caption = Kernel CPU Time +system_cpu.metric = 1000 * rate(cgroup.cpu.stat.system) + +cpu_usage.heading = CPU% +cpu_usage.caption = CPU Utilization +cpu_usage.width = 5 +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) + +mem_psi.heading = MEM-PSI +mem_psi.caption = Memory Pressure Stall Information +mem_psi.metric = 1000 * rate(cgroup.pressure.memory.some.total) + +io_psi.heading = I/O-PSI +io_psi.caption = I/O Pressure Stall Information +io_psi.metric = 1000 * rate(cgroup.pressure.io.some.total) name.heading = Control group name.caption = Control group name name.width = -22 name.metric = cgroup.cpu.stat.system name.instances = true +name.format = cgroup diff --git a/pcp/screens/cgroupsio b/pcp/screens/cgroupsio index a186975a2..862d1a459 100644 --- a/pcp/screens/cgroupsio +++ b/pcp/screens/cgroupsio @@ -2,55 +2,43 @@ # pcp-htop(1) configuration file - see pcp-htop(5) # -[cgroupio] -caption = CGroups IO -enabled = false +[cgroupsio] +caption = CGroups I/O iops.heading = IOPS iops.caption = I/O operations -iops.width = -6 iops.metric = cgroup.io.stat.rios + cgroup.io.stat.wios + cgroup.io.stat.dios readops.heading = RDIO readops.caption = Read operations -readops.width = -6 readops.metric = cgroup.io.stat.rios -readops.enabled = false +readops.default = false writeops.heading = WRIO writeops.caption = Write operations -writeops.width = -6 writeops.metric = cgroup.io.stat.wios -writeops.enabled = false +writeops.default = false directops.heading = DIO directops.caption = Direct I/O operations -directops.width = -6 directops.metric = cgroup.io.stat.dios -directops.enabled = false +directops.default = false -totalbytes.heading = DISK R/W +totalbytes.heading = R/W/D totalbytes.caption = Disk throughput -totalbytes.width = -8 totalbytes.metric = cgroup.io.stat.rbytes + cgroup.io.stat.wbytes + cgroup.io.stat.dbytes -readbytes.heading = RBYTES +readbytes.heading = RBYTE readbytes.caption = Disk read throughput -readbytes.width = -8 readbytes.metric = cgroup.io.stat.rbytes -readbytes.enabled = false -writebytes.heading = WBYTES +writebytes.heading = WBYTE writebytes.caption = Disk throughput -writebytes.width = -8 writebytes.metric = cgroup.io.stat.wbytes -writeytes.enabled = false -directio.heading = DIRECT +directio.heading = DBYTE directio.caption = Direct I/O throughput -directio.width = -8 directio.metric = cgroup.io.stat.dbytes -directio.enabled = false name.heading = Control group device name.caption = Control group device diff --git a/pcp/screens/cgroupsmem b/pcp/screens/cgroupsmem new file mode 100644 index 000000000..0eea30546 --- /dev/null +++ b/pcp/screens/cgroupsmem @@ -0,0 +1,47 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[cgroupsmem] +caption = CGroups Memory +default = true + +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 = -22 +name.metric = cgroup.memory.current +name.instances = true +name.format = cgroup diff --git a/pcp/screens/execsnoop b/pcp/screens/execsnoop index 0cb775224..00e383c8d 100644 --- a/pcp/screens/execsnoop +++ b/pcp/screens/execsnoop @@ -3,32 +3,31 @@ # [execsnoop] -caption = ExecSnoop +caption = Exec syscall snoop enabled = true pid.heading = PID pid.caption = Process Identifier -pid.width = -7 pid.metric = bpf.execsnoop.pid +pid.format = process ppid.heading = PPID ppid.caption = Parent Process -ppid.width = -7 ppid.metric = bpf.execsnoop.ppid +ppid.format = process uid.heading = UID uid.caption = User Identifier -uid.width = -6 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.width = -4 ret.metric = bpf.execsnoop.ret path.heading = Arguments diff --git a/pcp/screens/exitsnoop b/pcp/screens/exitsnoop index 5b5c4acea..d93ade12a 100644 --- a/pcp/screens/exitsnoop +++ b/pcp/screens/exitsnoop @@ -3,48 +3,45 @@ # [exitsnoop] -caption = ExitSnoop -enabled = true +caption = Process exit snoop +default = true pid.heading = PID pid.caption = Process Identifier -pid.width = -7 pid.metric = bpf.exitsnoop.pid +pid.format = process ppid.heading = PPID ppid.caption = Parent Process -ppid.width = -7 ppid.metric = bpf.exitsnoop.ppid +ppid.format = process tid.heading = TID tid.caption = Task Identifier -tid.width = -7 tid.metric = bpf.exitsnoop.tid -tid.enabled = false +tid.format = process +tid.default = false signal.heading = SIG signal.caption = Signal number -signal.width = -4 signal.metric = bpf.exitsnoop.sig exit.heading = EXIT exit.caption = Exit Code -exit.width = -4 exit.metric = bpf.exitsnoop.exit_code core.heading = CORE core.caption = Dumped core -core.width = -3 core.metric = bpf.exitsnoop.coredump -core.enabled = false +core.default = false age.heading = AGE age.caption = Process age -age.width = -6 age.metric = bpf.exitsnoop.age -age.enabled = false +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/filesys b/pcp/screens/filesys index 12b5854fb..0b02bf14f 100644 --- a/pcp/screens/filesys +++ b/pcp/screens/filesys @@ -4,63 +4,46 @@ [filesys] caption = Filesystem -enabled = true -avail.heading = AVAIL -avail.caption = avail -avail.width = -12 -avail.metric = filesys.avail +blockdev.heading = Filesystem +blockdev.metric = filesys.mountdir +blockdev.instances = true +blockdev.width = -14 blocksize.heading = BSIZE -blocksize.caption = blocksize -blocksize.width = -12 blocksize.metric = filesys.blocksize -blocksize.enabled = false +blocksize.default = false capacity.heading = SIZE -capacity.caption = capacity -capacity.width = -12 capacity.metric = filesys.capacity +used.heading = USED +used.metric = filesys.used + free.heading = FREE -free.caption = free -free.width = -12 free.metric = filesys.free +free.default = false -freefiles.heading = FREEF -freefiles.caption = freefiles -freefiles.width = -12 -freefiles.metric = filesys.freefiles +avail.heading = AVAIL +avail.metric = filesys.avail -full.heading = FULL -full.caption = full -full.width = -12 +full.heading = USE% full.metric = filesys.full -#full.percent = true - -maxfiles.heading = MAXF -maxfiles.caption = maxfiles -maxfiles.width = -12 -maxfiles.metric = filesys.maxfiles -maxfiles.enabled = false - -readonly.heading = RO -readonly.caption = readonly -readonly.width = -12 -readonly.metric = filesys.readonly -readonly.enabled = false - -used.heading = USED -used.caption = used -used.width = -12 -used.metric = filesys.used +full.format = percent usedfiles.heading = USEDF -usedfiles.caption = usedfiles usedfiles.metric = filesys.usedfiles -usedfiles.enabled = false +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.caption = mountdir -mountdir.width = -12 mountdir.metric = filesys.mountdir +mountdir.format = path +mountdir.width = -22 diff --git a/pcp/screens/opensnoop b/pcp/screens/opensnoop index 3bb0b4ec9..6c6adb7a3 100644 --- a/pcp/screens/opensnoop +++ b/pcp/screens/opensnoop @@ -3,30 +3,23 @@ # [opensnoop] -caption = OpenSnoop -enabled = true +caption = Open syscall snoop pid.heading = PID -pid.caption = PID -pid.width = -7 pid.metric = bpf.opensnoop.pid +pid.format = process comm.heading = COMM -comm.caption = COMM -comm.width = -16 comm.metric = bpf.opensnoop.comm +comm.format = command fd.heading = FD -fd.caption = FD -fd.width = -4 fd.metric = bpf.opensnoop.fd err.heading = ERR -err.caption = ERR -err.width = -4 err.metric = bpf.opensnoop.err -path.heading = File name -path.caption = PATH -path.width = -12 -path.metric = bpf.opensnoop.fname +file.heading = File name +file.width = -32 +file.metric = bpf.opensnoop.fname +file.format = path