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..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,15 +40,16 @@ typedef struct State_ { Settings* settings; UsersTable* ut; ProcessList* pl; + GenericDataList* gl; 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/AvailableColumnsPanel.c b/AvailableColumnsPanel.c index b8c09c74c..979fd54d6 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->hasDynamicScreen) + 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 682e05421..e94f315b0 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,18 @@ 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); +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) + gl->settings = settings; - Header* header = Header_new(pl, settings, 2); + Header* header = Header_new(pl, gl, settings, 2); Header_populateFromSettings(header); @@ -362,7 +371,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); @@ -372,12 +381,18 @@ 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, }; - 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 +420,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 +436,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..9b472843e 100644 --- a/DynamicColumn.h +++ b/DynamicColumn.h @@ -8,15 +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 */ + 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/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..9b1787109 --- /dev/null +++ b/GenericDataList.h @@ -0,0 +1,72 @@ +#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); + +/* struct GenericData */ +GenericData* GenericDataList_getGenericData(GenericDataList* this, GenericData_New constructor); + +void GenericDataList_addGenericData(GenericDataList* this, GenericData* g); + +void GenericDataList_removeGenericData(GenericDataList* this); + +/* helper functions */ +void GenericDataList_setPanel(GenericDataList* this, Panel* panel); + +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/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/Makefile.am b/Makefile.am index 2a6db44d4..8634a8ee1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,9 +49,12 @@ myhtopsources = \ DisplayOptionsPanel.c \ DynamicColumn.c \ DynamicMeter.c \ + DynamicScreen.c \ + GenericData.c \ EnvScreen.c \ FileDescriptorMeter.c \ FunctionBar.c \ + GenericDataList.c \ Hashtable.c \ Header.c \ HeaderOptionsPanel.c \ @@ -110,9 +113,12 @@ myhtopheaders = \ DisplayOptionsPanel.h \ DynamicColumn.h \ DynamicMeter.h \ + DynamicScreen.h \ + GenericData.h \ EnvScreen.h \ FileDescriptorMeter.h \ FunctionBar.h \ + GenericDataList.h \ Hashtable.h \ Header.h \ HeaderLayout.h \ @@ -385,11 +391,15 @@ endif # -------------------------- pcp_platform_headers = \ + linux/CGroupUtils.h \ linux/PressureStallMeter.h \ linux/ZramMeter.h \ 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 \ @@ -400,10 +410,14 @@ pcp_platform_headers = \ zfs/ZfsCompressedArcMeter.h pcp_platform_sources = \ + linux/CGroupUtils.c \ linux/PressureStallMeter.c \ 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..624f01d42 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 == false) + 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 87e718926..1c8c322df 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); @@ -131,12 +133,16 @@ 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); + + if (this->settings->ss->dynamic) + 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->dynamic) { + *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); } @@ -206,7 +221,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)); } diff --git a/ScreensPanel.c b/ScreensPanel.c index cb664ac45..a2324784b 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->dynamic) { + 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..0f8db18ff 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, + .dynamic = 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], "dynamic_screen") && this->config_version <= 2) { + screen = Settings_defaultScreens(this); + 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); @@ -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], ".dynamic_screen")) { + if (screen) + screen->dynamic = atoi(option[1]); } else if (String_eq(option[0], ".tree_view_always_by_pid")) { if (screen) screen->treeViewAlwaysByPID = atoi(option[1]); @@ -524,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); @@ -637,17 +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++) { 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); + 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 baf05da3b..4dfe6fe41 100644 --- a/Settings.h +++ b/Settings.h @@ -44,6 +44,7 @@ typedef struct { bool treeView; bool treeViewAlwaysByPID; bool allBranchesCollapsed; + bool dynamic; } ScreenSettings; typedef struct Settings_ { diff --git a/darwin/Platform.c b/darwin/Platform.c index 27bce5510..2c4d821c8 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -25,6 +25,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "FileDescriptorMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -442,3 +443,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 5cd672979..0bf3ad132 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -126,4 +126,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 277f1876f..f4ac329fe 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -21,6 +21,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "FileDescriptorMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "MemoryMeter.h" @@ -268,3 +269,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 b37cea2a1..31d5c6883 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -119,4 +119,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 061de70bc..83d32c22c 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -32,6 +32,7 @@ in the source distribution for its full text. #include "DateTimeMeter.h" #include "DiskIOMeter.h" #include "FileDescriptorMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -388,3 +389,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 849f7ddf3..416133416 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -119,4 +119,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 005d8316b..2f78e7919 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -32,6 +32,7 @@ in the source distribution for its full text. #include "DateTimeMeter.h" #include "DiskIOMeter.h" #include "FileDescriptorMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "HugePageMeter.h" #include "LoadAverageMeter.h" @@ -1052,3 +1053,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 1621d5628..966b1af2f 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -140,4 +140,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 c5c42d0ab..32812bb6e 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -39,6 +39,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "FileDescriptorMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -514,3 +515,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 a75c766cf..b15fe7c4a 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -123,4 +123,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 71419f8eb..120357191 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -29,6 +29,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "FileDescriptorMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -395,3 +396,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 f357006cc..47525a733 100644 --- a/openbsd/Platform.h +++ b/openbsd/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/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index 33c6d72a8..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" @@ -49,6 +52,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 +115,9 @@ 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; size_t id = columns->count + LAST_PROCESSFIELD; Hashtable_put(columns->table, id, column); @@ -159,7 +169,15 @@ 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, "default")) { + 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 +219,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) { @@ -233,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); @@ -242,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 d0ffe7192..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,11 @@ 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; typedef struct PCPDynamicColumns_ { @@ -28,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 new file mode 100644 index 000000000..513d338c5 --- /dev/null +++ b/pcp/PCPDynamicScreen.c @@ -0,0 +1,390 @@ +/* +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 "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 j = 0; j < screen->totalColumns; j++) { + PCPDynamicColumn* column = &screen->columns[j]; + + column->id = columns->offset + columns->cursor; + columns->cursor++; + Platform_addMetric(column->id, column->metricName); + size_t id = columns->count + LAST_PROCESSFIELD; + Hashtable_put(columns->table, id, column); + columns->count++; + } + } +} + +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->super.hasDynamicScreen = 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, "default")) { /* displayed by default */ + 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)); + screen->defaultEnabled = true; + + size_t id = screens->count; + Hashtable_put(screens->table, id, screen); + screens->count++; + + return screen; +} + +static void PCPDynamicScreen_parseFile(PCPDynamicScreens* screens, const char* path) { + FILE* file = fopen(path, "r"); + if (!file) + return; + + PCPDynamicScreen* screen = NULL; + unsigned int lineno = 0; + bool ok = true; + for (;;) { + char* line = String_readLine(file); + if (!line) + break; + lineno++; + + /* cleanup whitespace, skip comment lines */ + char* trimmed = String_trim(line); + free(line); + if (!trimmed || !trimmed[0] || trimmed[0] == '#') { + free(trimmed); + continue; + } + + size_t n; + char** config = String_split(trimmed, '=', &n); + free(trimmed); + if (config == NULL) + continue; + + char* key = String_trim(config[0]); + char* value = n > 1 ? String_trim(config[1]) : NULL; + if (key[0] == '[') { /* new section 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); + 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")) { + 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, "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); + } + 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->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; + + /* 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 (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); + + xAsprintf(&sortKey, "Dynamic(%s)", ds->super.sortKey); + + ScreenDefaults screen = { + .name = ds->super.name, + .columns = columns, + .sortKey = sortKey, + }; + + ss = Settings_newScreen(settings, &screen); + ss->direction = ds->super.direction; + ss->dynamic = true; + } +} + +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..2b68dc5d3 --- /dev/null +++ b/pcp/PCPDynamicScreen.h @@ -0,0 +1,41 @@ +#ifndef HEADER_PCPDynamicScreen +#define HEADER_PCPDynamicScreen + +#include + +#include + +#include "CRT.h" +#include "DynamicScreen.h" +#include "Hashtable.h" +#include "Settings.h" + + +struct PCPDynamicColumn_; +struct PCPDynamicColumns_; + +typedef struct PCPDynamicScreen_ { + DynamicScreen super; + struct PCPDynamicColumn_* columns; + char* displayInstances; + size_t totalColumns; + bool defaultEnabled; + bool enabled; +} PCPDynamicScreen; + +typedef struct PCPDynamicScreens_ { + Hashtable* table; + size_t count; /* count of dynamic screens discovered by scan */ +} PCPDynamicScreens; + +void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, struct 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..031ce07fd --- /dev/null +++ b/pcp/PCPGenericData.c @@ -0,0 +1,157 @@ +/* +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)); + + Hashtable_put(this->fields, this->fieldsCount, field); + this->fieldsCount++; + + return field; +} + +void PCPGenericData_removeField(PCPGenericData* this) +{ + int idx = this->fieldsCount - 1; + + 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; + if (!gg) + return; + PCPGenericDataField* gf = (PCPGenericDataField*)Hashtable_get(gg->fields, field); + if (!gf) + return; + + 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* 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) { + 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..80262b0d3 --- /dev/null +++ b/pcp/PCPGenericData.h @@ -0,0 +1,54 @@ +#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; + size_t id; /* identifier for metric array lookups */ + int offset; + int instance; + const pmDesc* desc; +} PCPGenericDataField; + +typedef struct PCPGenericData_ { + GenericData super; + Hashtable* fields; /* PCPGenericDataFields */ + + /* default result offset to use for searching metrics with instances */ + unsigned int offset; + 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..62b6520f5 --- /dev/null +++ b/pcp/PCPGenericDataList.c @@ -0,0 +1,194 @@ +/* +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->dynamic) + 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; + + if (keyInDom != PCPMetric_InDom(dc->id)) + 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 instance = -1, offset = -1; + while (PCPMetric_iterate(keyMetric->id, &instance, &offset)) { + for (unsigned int i = 0; fields[i]; i++) { + DynamicColumn* dc = Hashtable_get(settings->dynamicColumns, fields[i]); + PCPDynamicColumn* column = (PCPDynamicColumn*) dc; + if (!column) + return -1; + + const pmDesc* desc = PCPMetric_desc(column->id); + + pmAtomValue value; + 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); + if (!field) + return -1; + field->id = column->id; + field->desc = desc; + field->value = value; + field->offset = offset; + field->instance = instance; + } + } + } + + 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 e89a0a4c7..5d52104f8 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 @@ -180,4 +179,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 a76c0288a..0a0739d02 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -46,6 +46,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" @@ -355,6 +356,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)); @@ -405,6 +409,9 @@ bool Platform_init(void) { Platform_getMaxCPU(); Platform_getMaxPid(); + Process_setupColumnWidths(); + PCPDynamicColumns_setupWidths(&pcp->columns); + return true; } @@ -859,3 +866,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 f90e28135..b79e9b7a4 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 */ @@ -155,4 +158,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..0e1eb7231 --- /dev/null +++ b/pcp/screens/biosnoop @@ -0,0 +1,40 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[biosnoop] +caption = Block I/O Snoop +default = false + +pid.heading = PID +pid.caption = Process identifier +pid.metric = bpf.biosnoop.pid +pid.format = process + +disk.heading = DISK +disk.caption = Device name +disk.width = -7 +disk.metric = bpf.biosnoop.disk + +rwbs.heading = TYPE +rwbs.caption = I/O type string +rwbs.width = -4 +rwbs.metric = bpf.biosnoop.rwbs + +bytes.heading = BYTES +bytes.caption = I/O size in bytes +bytes.metric = bpf.biosnoop.bytes + +lat.heading = LAT +lat.caption = I/O latency +lat.metric = bpf.biosnoop.lat + +sector.heading = SECTOR +sector.caption = Device sector +sector.metric = bpf.biosnoop.sector + +comm.heading = Command +comm.caption = Process command name +comm.width = -16 +comm.metric = bpf.biosnoop.comm +comm.format = process diff --git a/pcp/screens/cgroups b/pcp/screens/cgroups new file mode 100644 index 000000000..b39ea7831 --- /dev/null +++ b/pcp/screens/cgroups @@ -0,0 +1,40 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[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 new file mode 100644 index 000000000..862d1a459 --- /dev/null +++ b/pcp/screens/cgroupsio @@ -0,0 +1,47 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[cgroupsio] +caption = CGroups I/O + +iops.heading = IOPS +iops.caption = I/O operations +iops.metric = cgroup.io.stat.rios + cgroup.io.stat.wios + cgroup.io.stat.dios + +readops.heading = RDIO +readops.caption = Read operations +readops.metric = cgroup.io.stat.rios +readops.default = false + +writeops.heading = WRIO +writeops.caption = Write operations +writeops.metric = cgroup.io.stat.wios +writeops.default = false + +directops.heading = DIO +directops.caption = Direct I/O operations +directops.metric = cgroup.io.stat.dios +directops.default = false + +totalbytes.heading = R/W/D +totalbytes.caption = Disk throughput +totalbytes.metric = cgroup.io.stat.rbytes + cgroup.io.stat.wbytes + cgroup.io.stat.dbytes + +readbytes.heading = RBYTE +readbytes.caption = Disk read throughput +readbytes.metric = cgroup.io.stat.rbytes + +writebytes.heading = WBYTE +writebytes.caption = Disk throughput +writebytes.metric = cgroup.io.stat.wbytes + +directio.heading = DBYTE +directio.caption = Direct I/O throughput +directio.metric = cgroup.io.stat.dbytes + +name.heading = Control group device +name.caption = Control group device +name.width = -22 +name.metric = cgroup.io.stat.rbytes +name.instances = true 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 new file mode 100644 index 000000000..00e383c8d --- /dev/null +++ b/pcp/screens/execsnoop @@ -0,0 +1,36 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[execsnoop] +caption = Exec syscall snoop +enabled = true + +pid.heading = PID +pid.caption = Process Identifier +pid.metric = bpf.execsnoop.pid +pid.format = process + +ppid.heading = PPID +ppid.caption = Parent Process +ppid.metric = bpf.execsnoop.ppid +ppid.format = process + +uid.heading = UID +uid.caption = User Identifier +uid.metric = bpf.execsnoop.uid + +comm.heading = COMM +comm.caption = Command +comm.width = -16 +comm.metric = bpf.execsnoop.comm +comm.format = command + +ret.heading = RET +ret.caption = Return Code +ret.metric = bpf.execsnoop.ret + +path.heading = Arguments +path.caption = Arguments +path.width = -12 +path.metric = bpf.execsnoop.args diff --git a/pcp/screens/exitsnoop b/pcp/screens/exitsnoop new file mode 100644 index 000000000..d93ade12a --- /dev/null +++ b/pcp/screens/exitsnoop @@ -0,0 +1,47 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[exitsnoop] +caption = Process exit snoop +default = true + +pid.heading = PID +pid.caption = Process Identifier +pid.metric = bpf.exitsnoop.pid +pid.format = process + +ppid.heading = PPID +ppid.caption = Parent Process +ppid.metric = bpf.exitsnoop.ppid +ppid.format = process + +tid.heading = TID +tid.caption = Task Identifier +tid.metric = bpf.exitsnoop.tid +tid.format = process +tid.default = false + +signal.heading = SIG +signal.caption = Signal number +signal.metric = bpf.exitsnoop.sig + +exit.heading = EXIT +exit.caption = Exit Code +exit.metric = bpf.exitsnoop.exit_code + +core.heading = CORE +core.caption = Dumped core +core.metric = bpf.exitsnoop.coredump +core.default = false + +age.heading = AGE +age.caption = Process age +age.metric = bpf.exitsnoop.age +age.default = false + +comm.heading = Command +comm.caption = COMM +comm.width = -16 +comm.metric = bpf.exitsnoop.comm +comm.format = command diff --git a/pcp/screens/filesys b/pcp/screens/filesys new file mode 100644 index 000000000..0b02bf14f --- /dev/null +++ b/pcp/screens/filesys @@ -0,0 +1,49 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[filesys] +caption = Filesystem + +blockdev.heading = Filesystem +blockdev.metric = filesys.mountdir +blockdev.instances = true +blockdev.width = -14 + +blocksize.heading = BSIZE +blocksize.metric = filesys.blocksize +blocksize.default = false + +capacity.heading = SIZE +capacity.metric = filesys.capacity + +used.heading = USED +used.metric = filesys.used + +free.heading = FREE +free.metric = filesys.free +free.default = false + +avail.heading = AVAIL +avail.metric = filesys.avail + +full.heading = USE% +full.metric = filesys.full +full.format = percent + +usedfiles.heading = USEDF +usedfiles.metric = filesys.usedfiles +usedfiles.default = false + +freefiles.heading = FREEF +freefiles.metric = filesys.freefiles +freefiles.default = false + +maxfiles.heading = MAXF +maxfiles.metric = filesys.maxfiles +maxfiles.default = false + +mountdir.heading = Mount point +mountdir.metric = filesys.mountdir +mountdir.format = path +mountdir.width = -22 diff --git a/pcp/screens/opensnoop b/pcp/screens/opensnoop new file mode 100644 index 000000000..6c6adb7a3 --- /dev/null +++ b/pcp/screens/opensnoop @@ -0,0 +1,25 @@ +# +# pcp-htop(1) configuration file - see pcp-htop(5) +# + +[opensnoop] +caption = Open syscall snoop + +pid.heading = PID +pid.metric = bpf.opensnoop.pid +pid.format = process + +comm.heading = COMM +comm.metric = bpf.opensnoop.comm +comm.format = command + +fd.heading = FD +fd.metric = bpf.opensnoop.fd + +err.heading = ERR +err.metric = bpf.opensnoop.err + +file.heading = File name +file.width = -32 +file.metric = bpf.opensnoop.fname +file.format = path diff --git a/solaris/Platform.c b/solaris/Platform.c index 4cadf1a00..e3ac5609e 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" @@ -334,3 +335,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 3dc6e3b59..79e6ca10d 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -158,4 +158,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 df7f3600a..035c8094e 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -17,6 +17,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "FileDescriptorMeter.h" +#include "GenericDataList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -163,3 +164,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 a718ca09e..f41281fbc 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -106,4 +106,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