diff --git a/Action.c b/Action.c index 62308da72..f7fc4a438 100644 --- a/Action.c +++ b/Action.c @@ -56,22 +56,22 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool follow) { Panel* panelFocus; int ch; bool unfollow = false; - int pid = follow ? MainPanel_selectedPid(mainPanel) : -1; - if (follow && host->pl->following == -1) { - host->pl->following = pid; + int row = follow ? MainPanel_selectedRow(mainPanel) : -1; + if (follow && host->activeTable->following == -1) { + host->activeTable->following = row; unfollow = true; } ScreenManager_run(scr, &panelFocus, &ch, NULL); if (unfollow) { - host->pl->following = -1; + host->activeTable->following = -1; } ScreenManager_delete(scr); Panel_move((Panel*)mainPanel, 0, y); Panel_resize((Panel*)mainPanel, COLS, LINES - y - 1); if (panelFocus == list && ch == 13) { if (follow) { - const Process* selected = (const Process*)Panel_getSelected((Panel*)mainPanel); - if (selected && selected->pid == pid) + const Row* selected = (const Row*)Panel_getSelected((Panel*)mainPanel); + if (selected && selected->id == row) return Panel_getSelected(list); beep(); @@ -99,7 +99,7 @@ static void Action_runSetup(State* st) { static bool changePriority(MainPanel* panel, int delta) { bool anyTagged; - bool ok = MainPanel_foreachProcess(panel, Process_changePriorityBy, (Arg) { .i = delta }, &anyTagged); + bool ok = MainPanel_foreachRow(panel, Process_rowChangePriorityBy, (Arg) { .i = delta }, &anyTagged); if (!ok) beep(); return anyTagged; @@ -121,36 +121,36 @@ bool Action_setUserOnly(const char* userName, uid_t* userId) { return false; } -static void tagAllChildren(Panel* panel, Process* parent) { +static void tagAllChildren(Panel* panel, Row* parent) { parent->tag = true; - pid_t ppid = parent->pid; + int parent_id = parent->id; for (int i = 0; i < Panel_size(panel); i++) { - Process* p = (Process*) Panel_get(panel, i); - if (!p->tag && Process_isChildOf(p, ppid)) { - tagAllChildren(panel, p); + Row* row = (Row*) Panel_get(panel, i); + if (!row->tag && Row_isChildOf(row, parent_id)) { + tagAllChildren(panel, row); } } } static bool expandCollapse(Panel* panel) { - Process* p = (Process*) Panel_getSelected(panel); - if (!p) + Row* row = (Row*) Panel_getSelected(panel); + if (!row) return false; - p->showChildren = !p->showChildren; + row->showChildren = !row->showChildren; return true; } static bool collapseIntoParent(Panel* panel) { - const Process* p = (Process*) Panel_getSelected(panel); - if (!p) + const Row* r = (Row*) Panel_getSelected(panel); + if (!r) return false; - pid_t ppid = Process_getParentPid(p); + int parent_id = Row_getGroupOrParent(r); for (int i = 0; i < Panel_size(panel); i++) { - Process* q = (Process*) Panel_get(panel, i); - if (q->pid == ppid) { - q->showChildren = false; + Row* row = (Row*) Panel_get(panel, i); + if (row->id == parent_id) { + row->showChildren = false; Panel_setSelected(panel, i); return true; } @@ -159,7 +159,7 @@ static bool collapseIntoParent(Panel* panel) { } Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { - ScreenSettings_setSortKey(settings->ss, sortKey); + ScreenSettings_setSortKey(settings->ss, (RowField) sortKey); return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; } @@ -171,11 +171,11 @@ static Htop_Reaction actionSetSortColumn(State* st) { Panel_setHeader(sortPanel, "Sort by"); Machine* host = st->host; Settings* settings = host->settings; - const ProcessField* fields = settings->ss->fields; + const RowField* fields = settings->ss->fields; Hashtable* dynamicColumns = settings->dynamicColumns; for (int i = 0; fields[i]; i++) { char* name = NULL; - if (fields[i] >= LAST_PROCESSFIELD) { + if (fields[i] >= ROW_DYNAMIC_FIELDS) { DynamicColumn* column = Hashtable_get(dynamicColumns, fields[i]); if (!column) continue; @@ -195,7 +195,7 @@ static Htop_Reaction actionSetSortColumn(State* st) { } Object_delete(sortPanel); - host->pl->needsSort = true; + host->activeTable->needsSort = true; return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } @@ -262,9 +262,9 @@ static Htop_Reaction actionToggleTreeView(State* st) { ss->treeView = !ss->treeView; if (!ss->allBranchesCollapsed) - ProcessList_expandTree(host->pl); + Table_expandTree(host->activeTable); - host->pl->needsSort = true; + host->activeTable->needsSort = true; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } @@ -282,9 +282,9 @@ static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) { } ss->allBranchesCollapsed = !ss->allBranchesCollapsed; if (ss->allBranchesCollapsed) - ProcessList_collapseAllBranches(host->pl); + Table_collapseAllBranches(host->activeTable); else - ProcessList_expandTree(host->pl); + Table_expandTree(host->activeTable); return HTOP_REFRESH | HTOP_SAVE_SETTINGS; } @@ -292,7 +292,7 @@ static Htop_Reaction actionIncFilter(State* st) { Machine* host = st->host; IncSet* inc = (st->mainPanel)->inc; IncSet_activate(inc, INC_FILTER, (Panel*)st->mainPanel); - host->pl->incFilter = IncSet_filter(inc); + host->activeTable->incFilter = IncSet_filter(inc); return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; } @@ -321,7 +321,7 @@ static Htop_Reaction actionLowerPriority(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) { Machine* host = st->host; ScreenSettings_invertSortOrder(host->settings->ss); - host->pl->needsSort = true; + host->activeTable->needsSort = true; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } @@ -397,11 +397,11 @@ static Htop_Reaction actionSetAffinity(State* st) { return HTOP_OK; #if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)) - const Process* p = (const Process*) Panel_getSelected((Panel*)st->mainPanel); - if (!p) + const Row* row = (const Row*) Panel_getSelected((Panel*)st->mainPanel); + if (!row) return HTOP_OK; - Affinity* affinity1 = Affinity_get(p, host); + Affinity* affinity1 = Affinity_rowGet(row, host); if (!affinity1) return HTOP_OK; @@ -412,7 +412,7 @@ static Htop_Reaction actionSetAffinity(State* st) { const void* set = Action_pickFromVector(st, affinityPanel, width, true); if (set) { Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, host); - bool ok = MainPanel_foreachProcess(st->mainPanel, Affinity_set, (Arg) { .v = affinity2 }, NULL); + bool ok = MainPanel_foreachRow(st->mainPanel, Affinity_rowSet, (Arg) { .v = affinity2 }, NULL); if (!ok) beep(); Affinity_delete(affinity2); @@ -422,7 +422,6 @@ static Htop_Reaction actionSetAffinity(State* st) { #else return HTOP_OK; #endif - } #ifdef SCHEDULER_SUPPORT @@ -459,7 +458,7 @@ static Htop_Reaction actionSetSchedPolicy(State* st) { SchedulingArg v = { .policy = preSelectedPolicy, .priority = preSelectedPriority }; - bool ok = MainPanel_foreachProcess(st->mainPanel, Scheduling_setPolicy, (Arg) { .v = &v }, NULL); + bool ok = MainPanel_foreachRow(st->mainPanel, Scheduling_rowSetPolicy, (Arg) { .v = &v }, NULL); if (!ok) beep(); } @@ -483,7 +482,7 @@ static Htop_Reaction actionKill(State* st) { Panel_setHeader((Panel*)st->mainPanel, "Sending..."); Panel_draw((Panel*)st->mainPanel, false, true, true, State_hideFunctionBar(st)); refresh(); - MainPanel_foreachProcess(st->mainPanel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL); + MainPanel_foreachRow(st->mainPanel, Process_rowSendSignal, (Arg) { .i = sgn->key }, NULL); napms(500); } Panel_delete((Object*)signalsPanel); @@ -511,7 +510,7 @@ static Htop_Reaction actionFilterByUser(State* st) { } Htop_Reaction Action_follow(State* st) { - st->host->pl->following = MainPanel_selectedPid(st->mainPanel); + st->host->activeTable->following = MainPanel_selectedRow(st->mainPanel); Panel_setSelectionColor((Panel*)st->mainPanel, PANEL_SELECTION_FOLLOW); return HTOP_KEEP_FOLLOWING; } @@ -529,6 +528,8 @@ static Htop_Reaction actionLsof(State* st) { if (!p) return HTOP_OK; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + OpenFilesScreen* ofs = OpenFilesScreen_new(p); InfoScreen_run((InfoScreen*)ofs); OpenFilesScreen_delete((Object*)ofs); @@ -541,6 +542,9 @@ static Htop_Reaction actionShowLocks(State* st) { const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel); if (!p) return HTOP_OK; + + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + ProcessLocksScreen* pls = ProcessLocksScreen_new(p); InfoScreen_run((InfoScreen*)pls); ProcessLocksScreen_delete((Object*)pls); @@ -557,6 +561,8 @@ static Htop_Reaction actionStrace(State* st) { if (!p) return HTOP_OK; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + TraceScreen* ts = TraceScreen_new(p); bool ok = TraceScreen_forkTracer(ts); if (ok) { @@ -569,11 +575,11 @@ static Htop_Reaction actionStrace(State* st) { } static Htop_Reaction actionTag(State* st) { - Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel); - if (!p) + Row* r = (Row*) Panel_getSelected((Panel*)st->mainPanel); + if (!r) return HTOP_OK; - Process_toggleTag(p); + Row_toggleTag(r); Panel_onKey((Panel*)st->mainPanel, KEY_DOWN); return HTOP_OK; } @@ -783,18 +789,18 @@ static Htop_Reaction actionHelp(State* st) { static Htop_Reaction actionUntagAll(State* st) { for (int i = 0; i < Panel_size((Panel*)st->mainPanel); i++) { - Process* p = (Process*) Panel_get((Panel*)st->mainPanel, i); - p->tag = false; + Row* row = (Row*) Panel_get((Panel*)st->mainPanel, i); + row->tag = false; } return HTOP_REFRESH; } static Htop_Reaction actionTagAllChildren(State* st) { - Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel); - if (!p) + Row* row = (Row*) Panel_getSelected((Panel*)st->mainPanel); + if (!row) return HTOP_OK; - tagAllChildren((Panel*)st->mainPanel, p); + tagAllChildren((Panel*)st->mainPanel, row); return HTOP_OK; } @@ -803,6 +809,8 @@ static Htop_Reaction actionShowEnvScreen(State* st) { if (!p) return HTOP_OK; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + EnvScreen* es = EnvScreen_new(p); InfoScreen_run((InfoScreen*)es); EnvScreen_delete((Object*)es); @@ -816,6 +824,8 @@ static Htop_Reaction actionShowCommandScreen(State* st) { if (!p) return HTOP_OK; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + CommandScreen* cmdScr = CommandScreen_new(p); InfoScreen_run((InfoScreen*)cmdScr); CommandScreen_delete((Object*)cmdScr); diff --git a/Affinity.c b/Affinity.c index f7c597bfc..546975d5d 100644 --- a/Affinity.c +++ b/Affinity.c @@ -12,6 +12,7 @@ in the source distribution for its full text. #include +#include "Process.h" #include "XUtils.h" #if defined(HAVE_LIBHWLOC) @@ -49,12 +50,11 @@ void Affinity_add(Affinity* this, unsigned int id) { this->used++; } - #if defined(HAVE_LIBHWLOC) -Affinity* Affinity_get(const Process* proc, Machine* host) { +static Affinity* Affinity_get(const Process* p, Machine* host) { hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); - bool ok = (hwloc_get_proc_cpubind(host->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); + bool ok = (hwloc_get_proc_cpubind(host->topology, Process_getPid(p), cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); Affinity* affinity = NULL; if (ok) { affinity = Affinity_new(host); @@ -73,22 +73,22 @@ Affinity* Affinity_get(const Process* proc, Machine* host) { return affinity; } -bool Affinity_set(Process* proc, Arg arg) { +static bool Affinity_set(Process* p, Arg arg) { Affinity* this = arg.v; hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); for (unsigned int i = 0; i < this->used; i++) { hwloc_bitmap_set(cpuset, this->cpus[i]); } - bool ok = (hwloc_set_proc_cpubind(this->host->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); + bool ok = (hwloc_set_proc_cpubind(this->host->topology, Process_getPid(p), cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); hwloc_bitmap_free(cpuset); return ok; } #elif defined(HAVE_AFFINITY) -Affinity* Affinity_get(const Process* proc, Machine* host) { +static Affinity* Affinity_get(const Process* p, Machine* host) { cpu_set_t cpuset; - bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0); + bool ok = (sched_getaffinity(Process_getPid(p), sizeof(cpu_set_t), &cpuset) == 0); if (!ok) return NULL; @@ -101,15 +101,31 @@ Affinity* Affinity_get(const Process* proc, Machine* host) { return affinity; } -bool Affinity_set(Process* proc, Arg arg) { +static bool Affinity_set(Process* p, Arg arg) { Affinity* this = arg.v; cpu_set_t cpuset; CPU_ZERO(&cpuset); for (unsigned int i = 0; i < this->used; i++) { CPU_SET(this->cpus[i], &cpuset); } - bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0); + bool ok = (sched_setaffinity(Process_getPid(p), sizeof(unsigned long), &cpuset) == 0); return ok; } #endif + +#if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY) + +bool Affinity_rowSet(Row* row, Arg arg) { + Process* p = (Process*) row; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + return Affinity_set(p, arg); +} + +Affinity* Affinity_rowGet(const Row* row, Machine* host) { + const Process* p = (const Process*) row; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + return Affinity_get(p, host); +} + +#endif /* HAVE_LIBHWLOC || HAVE_AFFINITY */ diff --git a/Affinity.h b/Affinity.h index 58d9bd733..341b0c04f 100644 --- a/Affinity.h +++ b/Affinity.h @@ -16,7 +16,7 @@ in the source distribution for its full text. #include #include "Object.h" -#include "Process.h" +#include "Row.h" #endif @@ -40,9 +40,9 @@ void Affinity_add(Affinity* this, unsigned int id); #if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY) -Affinity* Affinity_get(const Process* proc, Machine* host); +Affinity* Affinity_rowGet(const Row* row, Machine* host); -bool Affinity_set(Process* proc, Arg arg); +bool Affinity_rowSet(Row* row, Arg arg); #endif /* HAVE_LIBHWLOC || HAVE_AFFINITY */ diff --git a/AvailableColumnsPanel.c b/AvailableColumnsPanel.c index b8c09c74c..f3c408430 100644 --- a/AvailableColumnsPanel.c +++ b/AvailableColumnsPanel.c @@ -34,7 +34,7 @@ static void AvailableColumnsPanel_delete(Object* object) { static void AvailableColumnsPanel_insert(AvailableColumnsPanel* this, int at, int key) { const char* name; - if (key >= LAST_PROCESSFIELD) + if (key >= ROW_DYNAMIC_FIELDS) name = DynamicColumn_init(key); else name = Process_fields[key].name; diff --git a/ColumnsPanel.c b/ColumnsPanel.c index d53fff258..ecae36f7a 100644 --- a/ColumnsPanel.c +++ b/ColumnsPanel.c @@ -141,7 +141,7 @@ static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns) { Panel* super = (Panel*) this; Panel_prune(super); - for (const ProcessField* fields = ss->fields; *fields; fields++) + for (const RowField* fields = ss->fields; *fields; fields++) ColumnsPanel_add(super, *fields, columns); this->ss = ss; } diff --git a/CommandLine.c b/CommandLine.c index 3d1f953fc..848c7ae74 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -303,11 +303,11 @@ static void CommandLine_delay(Machine* host, unsigned long millisec) { } static void setCommFilter(State* state, char** commFilter) { - ProcessList* pl = state->host->pl; + Table* table = state->host->activeTable; IncSet* inc = state->mainPanel->inc; IncSet_setFilter(inc, *commFilter); - pl->incFilter = IncSet_filter(inc); + table->incFilter = IncSet_filter(inc); free(*commFilter); *commFilter = NULL; @@ -334,8 +334,6 @@ int CommandLine_run(int argc, char** argv) { if (!Platform_init()) return 1; - Process_setupColumnWidths(); - UsersTable* ut = UsersTable_new(); Hashtable* dm = DynamicMeters_new(); Hashtable* dc = DynamicColumns_new(); @@ -347,7 +345,7 @@ int CommandLine_run(int argc, char** argv) { Settings* settings = Settings_new(host->activeCPUs, dm, dc); host->settings = settings; - Machine_addList(host, pl); + Machine_addTable(host, &pl->super, true); Header* header = Header_new(host, 2); Header_populateFromSettings(header); @@ -379,7 +377,7 @@ int CommandLine_run(int argc, char** argv) { CRT_init(settings, flags.allowUnicode, flags.iterationsRemaining != -1); MainPanel* panel = MainPanel_new(); - ProcessList_setPanel(pl, (Panel*) panel); + Table_setPanel(&pl->super, (Panel*) panel); MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter); @@ -400,13 +398,13 @@ int CommandLine_run(int argc, char** argv) { ScreenManager_add(scr, (Panel*) panel, -1); Machine_scan(host); - ProcessList_scan(pl); + Machine_scanTables(host); CommandLine_delay(host, 75); Machine_scan(host); - ProcessList_scan(pl); + Machine_scanTables(host); if (settings->ss->allBranchesCollapsed) - ProcessList_collapseAllBranches(pl); + Table_collapseAllBranches(&pl->super); ScreenManager_run(scr, NULL, NULL, NULL); @@ -421,7 +419,6 @@ int CommandLine_run(int argc, char** argv) { } Header_delete(header); - ProcessList_delete(pl); Machine_delete(host); ScreenManager_delete(scr); diff --git a/CommandScreen.c b/CommandScreen.c index 6a87d1375..ecd823bdf 100644 --- a/CommandScreen.c +++ b/CommandScreen.c @@ -46,7 +46,7 @@ static void CommandScreen_scan(InfoScreen* this) { } static void CommandScreen_draw(InfoScreen* this) { - InfoScreen_drawTitled(this, "Command of process %d - %s", this->process->pid, Process_getCommand(this->process)); + InfoScreen_drawTitled(this, "Command of process %d - %s", Process_getPid(this->process), Process_getCommand(this->process)); } const InfoScreenClass CommandScreen_class = { diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index f9fa9b12b..326d33b14 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -109,7 +109,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* #undef TABMSG Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->ss->treeView))); - Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID))); + Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by ID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByID))); Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed))); Panel_add(super, (Object*) TextItem_new("Global options:")); Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs))); diff --git a/EnvScreen.c b/EnvScreen.c index 0fcee83a2..b27155e67 100644 --- a/EnvScreen.c +++ b/EnvScreen.c @@ -24,7 +24,7 @@ void EnvScreen_delete(Object* this) { } static void EnvScreen_draw(InfoScreen* this) { - InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process)); + InfoScreen_drawTitled(this, "Environment of process %d - %s", Process_getPid(this->process), Process_getCommand(this->process)); } static void EnvScreen_scan(InfoScreen* this) { @@ -33,7 +33,7 @@ static void EnvScreen_scan(InfoScreen* this) { Panel_prune(panel); - char* env = Platform_getProcessEnv(this->process->pid); + char* env = Platform_getProcessEnv(Process_getPid(this->process)); if (env) { for (const char* p = env; *p; p = strrchr(p, 0) + 1) InfoScreen_addLine(this, p); diff --git a/Machine.c b/Machine.c index 63a996ef5..8846aed76 100644 --- a/Machine.c +++ b/Machine.c @@ -15,6 +15,7 @@ in the source distribution for its full text. #include "Hashtable.h" #include "Macros.h" #include "Platform.h" +#include "Row.h" #include "XUtils.h" @@ -22,6 +23,11 @@ void Machine_init(Machine* this, UsersTable* usersTable, uid_t userId) { this->usersTable = usersTable; this->userId = userId; + this->htopUserId = getuid(); + + // discover fixed column width limits + Row_setPidColumnWidth(Platform_getMaxPid()); + // always maintain valid realtime timestamps Platform_gettime_realtime(&this->realtime, &this->realtimeMs); @@ -49,12 +55,48 @@ void Machine_done(Machine* this) { if (this->topologyOk) { hwloc_topology_destroy(this->topology); } -#else - (void)this; #endif + for (size_t i = 0; i < this->tableCount; i++) { + Object_delete(&this->tables[i]->super); + } } -void Machine_addList(Machine* this, struct ProcessList_ *pl) { - // currently only process lists are supported - this->pl = pl; +void Machine_addTable(Machine* this, Table* table, bool processes) { + if (processes) + this->processTable = table; + this->activeTable = table; + + size_t nmemb = this->tableCount + 1; + Table** tables = xReallocArray(this->tables, nmemb, sizeof(Table*)); + tables[nmemb - 1] = table; + this->tables = tables; + this->tableCount++; +} + +void Machine_scanTables(Machine* this) { + // set scan timestamp + static bool firstScanDone = false; + + if (firstScanDone) + Platform_gettime_monotonic(&this->monotonicMs); + else + firstScanDone = true; + + this->maxUserId = 0; + Row_resetFieldWidths(); + + for (size_t i = 0; i < this->tableCount; i++) { + Table* table = this->tables[i]; + + // pre-processing of each row + Table_scanPrepare(table); + + // scan values for this table + Table_scanIterate(table); + + // post-process after scanning + Table_scanCleanup(table); + } + + Row_setUidColumnWidth(this->maxUserId); } diff --git a/Machine.h b/Machine.h index 64d6f6c29..a1c49e246 100644 --- a/Machine.h +++ b/Machine.h @@ -18,6 +18,7 @@ in the source distribution for its full text. #include "Hashtable.h" #include "Settings.h" +#include "Table.h" #include "UsersTable.h" #include "Vector.h" @@ -37,10 +38,10 @@ in the source distribution for its full text. typedef unsigned long long int memory_t; #define MEMORY_MAX ULLONG_MAX -struct ProcessList_; +struct Settings_; typedef struct Machine_ { - Settings* settings; + struct Settings_* settings; struct timeval realtime; /* time of the current sample */ uint64_t realtimeMs; /* current time in milliseconds */ @@ -68,11 +69,14 @@ typedef struct Machine_ { unsigned int existingCPUs; UsersTable* usersTable; - uid_t userId; - - /* To become an array of lists - processes, cgroups, filesystems,... etc */ - /* for now though, just point back to the one list we have at the moment */ - struct ProcessList_ *pl; + uid_t htopUserId; + uid_t maxUserId; /* recently observed */ + uid_t userId; /* selected row user ID */ + + size_t tableCount; + Table **tables; + Table *activeTable; + Table *processTable; } Machine; @@ -86,8 +90,10 @@ void Machine_done(Machine* this); bool Machine_isCPUonline(const Machine* this, unsigned int id); -void Machine_addList(Machine* this, struct ProcessList_ *pl); +void Machine_addTable(Machine* this, Table *table, bool processes); void Machine_scan(Machine* this); +void Machine_scanTables(Machine* this); + #endif diff --git a/MainPanel.c b/MainPanel.c index 14bd3bbdf..54127fe9c 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -14,10 +14,10 @@ in the source distribution for its full text. #include "CRT.h" #include "FunctionBar.h" #include "Platform.h" -#include "Process.h" -#include "ProcessList.h" #include "ProvideCurses.h" +#include "Row.h" #include "Settings.h" +#include "Table.h" #include "XUtils.h" @@ -30,25 +30,25 @@ void MainPanel_updateLabels(MainPanel* this, bool list, bool filter) { FunctionBar_setLabel(bar, KEY_F(4), filter ? "FILTER" : "Filter"); } -static void MainPanel_pidSearch(MainPanel* this, int ch) { +static void MainPanel_idSearch(MainPanel* this, int ch) { Panel* super = (Panel*) this; - pid_t pid = ch - 48 + this->pidSearch; + pid_t id = ch - 48 + this->idSearch; for (int i = 0; i < Panel_size(super); i++) { - const Process* p = (const Process*) Panel_get(super, i); - if (p && p->pid == pid) { + const Row* row = (const Row*) Panel_get(super, i); + if (row && row->id == id) { Panel_setSelected(super, i); break; } } - this->pidSearch = pid * 10; - if (this->pidSearch > 10000000) { - this->pidSearch = 0; + this->idSearch = id * 10; + if (this->idSearch > 10000000) { + this->idSearch = 0; } } static const char* MainPanel_getValue(Panel* this, int i) { - const Process* p = (const Process*) Panel_get(this, i); - return Process_getCommand(p); + const Row* row = (const Row*) Panel_get(this, i); + return Row_sortKeyString(row); } static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { @@ -77,8 +77,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { if (EVENT_IS_HEADER_CLICK(ch)) { int x = EVENT_HEADER_CLICK_GET_X(ch); int hx = super->scrollH + x + 1; - ProcessField field = ProcessList_keyAt(host->pl, hx); - if (ss->treeView && ss->treeViewAlwaysByPID) { + RowField field = RowField_keyAt(settings, hx); + if (ss->treeView && ss->treeViewAlwaysByID) { ss->treeView = false; ss->direction = 1; reaction |= Action_setSortKey(settings, field); @@ -96,7 +96,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } else if (ch != ERR && this->inc->active) { bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL); if (filterChanged) { - host->pl->incFilter = IncSet_filter(this->inc); + host->activeTable->incFilter = IncSet_filter(this->inc); reaction = HTOP_REFRESH | HTOP_REDRAW_BAR; } if (this->inc->found) { @@ -111,17 +111,17 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { reaction |= (this->keys[ch])(this->state); result = HANDLED; } else if (0 < ch && ch < 255 && isdigit((unsigned char)ch)) { - MainPanel_pidSearch(this, ch); + MainPanel_idSearch(this, ch); } else { if (ch != ERR) { - this->pidSearch = 0; + this->idSearch = 0; } else { reaction |= HTOP_KEEP_FOLLOWING; } } if ((reaction & HTOP_REDRAW_BAR) == HTOP_REDRAW_BAR) { - MainPanel_updateLabels(this, settings->ss->treeView, host->pl->incFilter); + MainPanel_updateLabels(this, settings->ss->treeView, host->activeTable->incFilter); } if ((reaction & HTOP_RESIZE) == HTOP_RESIZE) { result |= RESIZE; @@ -142,35 +142,32 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { return BREAK_LOOP; } if ((reaction & HTOP_KEEP_FOLLOWING) != HTOP_KEEP_FOLLOWING) { - host->pl->following = -1; + host->activeTable->following = -1; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); } return result; } -int MainPanel_selectedPid(MainPanel* this) { - const Process* p = (const Process*) Panel_getSelected((Panel*)this); - if (p) { - return p->pid; - } - return -1; +int MainPanel_selectedRow(MainPanel* this) { + const Row* row = (const Row*) Panel_getSelected((Panel*)this); + return row ? row->id : -1; } -bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) { +bool MainPanel_foreachRow(MainPanel* this, MainPanel_foreachRowFn fn, Arg arg, bool* wasAnyTagged) { Panel* super = (Panel*) this; bool ok = true; bool anyTagged = false; for (int i = 0; i < Panel_size(super); i++) { - Process* p = (Process*) Panel_get(super, i); - if (p->tag) { - ok = fn(p, arg) && ok; + Row* row = (Row*) Panel_get(super, i); + if (row->tag) { + ok &= fn(row, arg); anyTagged = true; } } if (!anyTagged) { - Process* p = (Process*) Panel_getSelected(super); - if (p) { - ok &= fn(p, arg); + Row* row = (Row*) Panel_getSelected(super); + if (row) { + ok &= fn(row, arg); } } @@ -196,7 +193,7 @@ static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) { static void MainPanel_printHeader(Panel* super) { MainPanel* this = (MainPanel*) super; Machine* host = this->state->host; - ProcessList_printHeader(host->pl, &super->header); + Table_printHeader(host->settings, &super->header); } const PanelClass MainPanel_class = { @@ -211,7 +208,7 @@ const PanelClass MainPanel_class = { MainPanel* MainPanel_new(void) { MainPanel* this = AllocThis(MainPanel); - Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(Settings_isReadonly() ? MainFunctions_ro : MainFunctions, NULL, NULL)); + Panel_init((Panel*) this, 1, 1, 1, 1, Class(Row), false, FunctionBar_new(Settings_isReadonly() ? MainFunctions_ro : MainFunctions, NULL, NULL)); this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action)); this->inc = IncSet_new(MainPanel_getFunctionBar(this)); diff --git a/MainPanel.h b/MainPanel.h index bd22acd0c..d062616d2 100644 --- a/MainPanel.h +++ b/MainPanel.h @@ -17,7 +17,7 @@ in the source distribution for its full text. #include "IncSet.h" #include "Object.h" #include "Panel.h" -#include "Process.h" +#include "Row.h" typedef struct MainPanel_ { @@ -25,19 +25,19 @@ typedef struct MainPanel_ { State* state; IncSet* inc; Htop_Action* keys; - pid_t pidSearch; + unsigned int idSearch; } MainPanel; -typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg); +typedef bool(*MainPanel_foreachRowFn)(Row*, Arg); #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) // update the Label Keys in the MainPanel bar, list: list / tree mode, filter: filter (inc) active / inactive void MainPanel_updateLabels(MainPanel* this, bool list, bool filter); -int MainPanel_selectedPid(MainPanel* this); +int MainPanel_selectedRow(MainPanel* this); -bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged); +bool MainPanel_foreachRow(MainPanel* this, MainPanel_foreachRowFn fn, Arg arg, bool* wasAnyTagged); extern const PanelClass MainPanel_class; diff --git a/Makefile.am b/Makefile.am index e36994c13..a05c7ea9e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -74,6 +74,7 @@ myhtopsources = \ Process.c \ ProcessList.c \ ProcessLocksScreen.c \ + Row.c \ RichString.c \ Scheduling.c \ ScreenManager.c \ @@ -82,6 +83,7 @@ myhtopsources = \ SignalsPanel.c \ SwapMeter.c \ SysArchMeter.c \ + Table.c \ TasksMeter.c \ TraceScreen.c \ UptimeMeter.c \ @@ -141,6 +143,8 @@ myhtopheaders = \ ProvideCurses.h \ ProvideTerm.h \ RichString.h \ + Row.h \ + RowField.h \ Scheduling.h \ ScreenManager.h \ ScreensPanel.h \ @@ -148,6 +152,7 @@ myhtopheaders = \ SignalsPanel.h \ SwapMeter.h \ SysArchMeter.h \ + Table.h \ TasksMeter.h \ TraceScreen.h \ UptimeMeter.h \ diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c index 2e782b986..3077490d2 100644 --- a/OpenFilesScreen.c +++ b/OpenFilesScreen.c @@ -72,12 +72,12 @@ static const char* getDataForType(const OpenFiles_Data* data, char type) { } OpenFilesScreen* OpenFilesScreen_new(const Process* process) { - OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen)); + OpenFilesScreen* this = xCalloc(1, sizeof(OpenFilesScreen)); Object_setClass(this, Class(OpenFilesScreen)); if (Process_isThread(process)) { - this->pid = process->tgid; + this->pid = Process_getThreadGroup(process); } else { - this->pid = process->pid; + this->pid = Process_getPid(process); } return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE OFFSET NODE NAME"); } diff --git a/Process.c b/Process.c index 7244ad9f6..6f74e014e 100644 --- a/Process.c +++ b/Process.c @@ -40,239 +40,9 @@ in the source distribution for its full text. /* Used to identify kernel threads in Comm and Exe columns */ static const char* const kthreadID = "KTHREAD"; -static uid_t Process_getuid = (uid_t)-1; - -int Process_pidDigits = PROCESS_MIN_PID_DIGITS; -int Process_uidDigits = PROCESS_MIN_UID_DIGITS; - -void Process_setupColumnWidths(void) { - int maxPid = Platform_getMaxPid(); - if (maxPid == -1) - return; - - if (maxPid < (int)pow(10, PROCESS_MIN_PID_DIGITS)) { - Process_pidDigits = PROCESS_MIN_PID_DIGITS; - return; - } - - Process_pidDigits = (int)log10(maxPid) + 1; - assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS); -} - -void Process_setUidColumnWidth(uid_t maxUid) { - if (maxUid < (uid_t)pow(10, PROCESS_MIN_UID_DIGITS)) { - Process_uidDigits = PROCESS_MIN_UID_DIGITS; - return; - } - - Process_uidDigits = (int)log10(maxUid) + 1; - assert(Process_uidDigits <= PROCESS_MAX_UID_DIGITS); -} - -void Process_printBytes(RichString* str, unsigned long long number, bool coloring) { - char buffer[16]; - int len; - - int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; - int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; - int processGigabytesColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS]; - int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS]; - int processColor = CRT_colors[PROCESS]; - - if (number == ULLONG_MAX) { - //Invalid number - RichString_appendAscii(str, shadowColor, " N/A "); - return; - } - - number /= ONE_K; - - if (number < 1000) { - //Plain number, no markings - len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (number < 100000) { - //2 digit MB, 3 digit KB - len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 1000); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - number %= 1000; - len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (number < 1000 * ONE_K) { - //3 digit MB - number /= ONE_K; - len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - } else if (number < 10000 * ONE_K) { - //1 digit GB, 3 digit MB - number /= ONE_K; - len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - number %= 1000; - len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - } else if (number < 100 * ONE_M) { - //2 digit GB, 1 digit MB - len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / ONE_M); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - number = (number % ONE_M) * 10 / ONE_M; - len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - RichString_appendAscii(str, processGigabytesColor, "G "); - } else if (number < 1000 * ONE_M) { - //3 digit GB - number /= ONE_M; - len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - } else if (number < 10000ULL * ONE_M) { - //1 digit TB, 3 digit GB - number /= ONE_M; - len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - number %= 1000; - len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - } else if (number < 100ULL * ONE_G) { - //2 digit TB, 1 digit GB - len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / ONE_G); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - number = (number % ONE_G) * 10 / ONE_G; - len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); - RichString_appendnAscii(str, processGigabytesColor, buffer, len); - RichString_appendAscii(str, largeNumberColor, "T "); - } else if (number < 10000ULL * ONE_G) { - //3 digit TB or 1 digit PB, 3 digit TB - number /= ONE_G; - len = xSnprintf(buffer, sizeof(buffer), "%4lluT ", number); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } else { - //2 digit PB and above - len = xSnprintf(buffer, sizeof(buffer), "%4.1lfP ", (double)number / ONE_T); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } -} - -void Process_printKBytes(RichString* str, unsigned long long number, bool coloring) { - if (number == ULLONG_MAX) - Process_printBytes(str, ULLONG_MAX, coloring); - else - Process_printBytes(str, number * ONE_K, coloring); -} - -void Process_printCount(RichString* str, unsigned long long number, bool coloring) { - char buffer[13]; - - int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; - int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; - int processColor = CRT_colors[PROCESS]; - int processShadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS]; - - if (number == ULLONG_MAX) { - RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A "); - } else if (number >= 100000LL * ONE_DECIMAL_T) { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G); - RichString_appendnAscii(str, largeNumberColor, buffer, 12); - } else if (number >= 100LL * ONE_DECIMAL_T) { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M); - RichString_appendnAscii(str, largeNumberColor, buffer, 8); - RichString_appendnAscii(str, processMegabytesColor, buffer + 8, 4); - } else if (number >= 10LL * ONE_DECIMAL_G) { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K); - RichString_appendnAscii(str, largeNumberColor, buffer, 5); - RichString_appendnAscii(str, processMegabytesColor, buffer + 5, 3); - RichString_appendnAscii(str, processColor, buffer + 8, 4); - } else { - xSnprintf(buffer, sizeof(buffer), "%11llu ", number); - RichString_appendnAscii(str, largeNumberColor, buffer, 2); - RichString_appendnAscii(str, processMegabytesColor, buffer + 2, 3); - RichString_appendnAscii(str, processColor, buffer + 5, 3); - RichString_appendnAscii(str, processShadowColor, buffer + 8, 4); - } -} - -void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring) { - char buffer[10]; - int len; - - unsigned long long totalSeconds = totalHundredths / 100; - unsigned long long hours = totalSeconds / 3600; - unsigned long long days = totalSeconds / 86400; - int minutes = (totalSeconds / 60) % 60; - int seconds = totalSeconds % 60; - int hundredths = totalHundredths - (totalSeconds * 100); - - int yearColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; - int dayColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS]; - int hourColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; - int defColor = CRT_colors[PROCESS]; - - if (days >= /* Ignore leap years */365) { - int years = days / 365; - int daysLeft = days - 365 * years; - - if (years >= 10000000) { - RichString_appendnAscii(str, yearColor, "eternity ", 9); - } else if (years >= 1000) { - len = xSnprintf(buffer, sizeof(buffer), "%7dy ", years); - RichString_appendnAscii(str, yearColor, buffer, len); - } else if (daysLeft >= 100) { - len = xSnprintf(buffer, sizeof(buffer), "%3dy", years); - RichString_appendnAscii(str, yearColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%3dd ", daysLeft); - RichString_appendnAscii(str, dayColor, buffer, len); - } else if (daysLeft >= 10) { - len = xSnprintf(buffer, sizeof(buffer), "%4dy", years); - RichString_appendnAscii(str, yearColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%2dd ", daysLeft); - RichString_appendnAscii(str, dayColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%5dy", years); - RichString_appendnAscii(str, yearColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%1dd ", daysLeft); - RichString_appendnAscii(str, dayColor, buffer, len); - } - } else if (days >= 100) { - int hoursLeft = hours - days * 24; - - if (hoursLeft >= 10) { - len = xSnprintf(buffer, sizeof(buffer), "%4llud", days); - RichString_appendnAscii(str, dayColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%2dh ", hoursLeft); - RichString_appendnAscii(str, hourColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%5llud", days); - RichString_appendnAscii(str, dayColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%1dh ", hoursLeft); - RichString_appendnAscii(str, hourColor, buffer, len); - } - } else if (hours >= 100) { - int minutesLeft = totalSeconds / 60 - hours * 60; - - if (minutesLeft >= 10) { - len = xSnprintf(buffer, sizeof(buffer), "%4lluh", hours); - RichString_appendnAscii(str, hourColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%2dm ", minutesLeft); - RichString_appendnAscii(str, defColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%5lluh", hours); - RichString_appendnAscii(str, hourColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%1dm ", minutesLeft); - RichString_appendnAscii(str, defColor, buffer, len); - } - } else if (hours > 0) { - len = xSnprintf(buffer, sizeof(buffer), "%2lluh", hours); - RichString_appendnAscii(str, hourColor, buffer, len); - len = xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds); - RichString_appendnAscii(str, defColor, buffer, len); - } else { - len = xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths); - RichString_appendnAscii(str, defColor, buffer, len); - } -} - void Process_fillStarttimeBuffer(Process* this) { struct tm date; - time_t now = this->host->realtime.tv_sec; + time_t now = this->super.host->realtime.tv_sec; (void) localtime_r(&this->starttime_ctime, &date); strftime(this->starttime_show, @@ -408,9 +178,8 @@ static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr * Process_writeCommand() for coloring. The merged Command string is also * returned by Process_getCommand() for searching, sorting and filtering. */ -void Process_makeCommandStr(Process* this) { +void Process_makeCommandStr(Process* this, const Settings* settings) { ProcessMergedCommand* mc = &this->mergedCommand; - const Settings* settings = this->host->settings; bool showMergedCommand = settings->showMergedCommand; bool showProgramPath = settings->showProgramPath; @@ -678,7 +447,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin int strStart = RichString_size(str); - const Settings* settings = this->host->settings; + const Settings* settings = this->super.host->settings; const bool highlightBaseName = settings->highlightBaseName; const bool highlightSeparator = true; const bool highlightDeleted = settings->highlightDeletedExe; @@ -744,73 +513,6 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin } } -void Process_printRate(RichString* str, double rate, bool coloring) { - char buffer[16]; - - int largeNumberColor = CRT_colors[LARGE_NUMBER]; - int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; - int processColor = CRT_colors[PROCESS]; - int shadowColor = CRT_colors[PROCESS_SHADOW]; - - if (!coloring) { - largeNumberColor = CRT_colors[PROCESS]; - processMegabytesColor = CRT_colors[PROCESS]; - } - - if (!isNonnegative(rate)) { - RichString_appendAscii(str, shadowColor, " N/A "); - } else if (rate < 0.005) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate); - RichString_appendnAscii(str, shadowColor, buffer, len); - } else if (rate < ONE_K) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (rate < ONE_M) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f K/s ", rate / ONE_K); - RichString_appendnAscii(str, processColor, buffer, len); - } else if (rate < ONE_G) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f M/s ", rate / ONE_M); - RichString_appendnAscii(str, processMegabytesColor, buffer, len); - } else if (rate < ONE_T) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f G/s ", rate / ONE_G); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } else if (rate < ONE_P) { - int len = snprintf(buffer, sizeof(buffer), "%7.2f T/s ", rate / ONE_T); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } else { - int len = snprintf(buffer, sizeof(buffer), "%7.2f P/s ", rate / ONE_P); - RichString_appendnAscii(str, largeNumberColor, buffer, len); - } -} - -void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) { - int columns = width; - RichString_appendnWideColumns(str, attr, content, strlen(content), &columns); - RichString_appendChr(str, attr, ' ', width + 1 - columns); -} - -void Process_printPercentage(float val, char* buffer, size_t n, uint8_t width, int* attr) { - if (isNonnegative(val)) { - if (val < 0.05F) - *attr = CRT_colors[PROCESS_SHADOW]; - else if (val >= 99.9F) - *attr = CRT_colors[PROCESS_MEGABYTES]; - - int precision = 1; - - // Display "val" as "100" for columns like "MEM%". - if (width == 4 && val > 99.9F) { - precision = 0; - val = 100.0F; - } - - xSnprintf(buffer, n, "%*.*f ", width, precision, val); - } else { - *attr = CRT_colors[PROCESS_SHADOW]; - xSnprintf(buffer, n, "%*.*s ", width, width, "N/A"); - } -} - static inline char processStateChar(ProcessState state) { switch (state) { case UNKNOWN: return '?'; @@ -833,11 +535,19 @@ static inline char processStateChar(ProcessState state) { } } -void Process_writeField(const Process* this, RichString* str, ProcessField field) { +static void Process_rowWriteField(const Row* super, RichString* str, RowField field) { + const Process* this = (const Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + Process_writeField(this, str, field); +} + +void Process_writeField(const Process* this, RichString* str, RowField field) { char buffer[256]; size_t n = sizeof(buffer); int attr = CRT_colors[DEFAULT_COLOR]; - const Settings* settings = this->host->settings; + const Row* super = (const Row*) &this->super; + const Machine* host = super->host; + const Settings* settings = host->settings; bool coloring = settings->highlightMegabytes; switch (field) { @@ -848,15 +558,15 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } const ScreenSettings* ss = settings->ss; - if (!ss->treeView || this->indent == 0) { + if (!ss->treeView || super->indent == 0) { Process_writeCommand(this, attr, baseattr, str); return; } char* buf = buffer; - const bool lastItem = (this->indent < 0); + const bool lastItem = (super->indent < 0); - for (uint32_t indent = (this->indent < 0 ? -this->indent : this->indent); indent > 1; indent >>= 1) { + for (uint32_t indent = (super->indent < 0 ? -super->indent : super->indent); indent > 1; indent >>= 1) { int written, ret; if (indent & 1U) { ret = xSnprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); @@ -873,7 +583,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field } const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE]; - xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); + xSnprintf(buf, n, "%s%s ", draw, super->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer); Process_writeCommand(this, attr, baseattr, str); return; @@ -888,7 +598,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field procComm = Process_isKernelThread(this) ? kthreadID : "N/A"; } - Process_printLeftAlignedField(str, attr, procComm, TASK_COMM_LEN - 1); + Row_printLeftAlignedField(str, attr, procComm, TASK_COMM_LEN - 1); return; } case PROC_EXE: { @@ -907,7 +617,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field procExe = Process_isKernelThread(this) ? kthreadID : "N/A"; } - Process_printLeftAlignedField(str, attr, procExe, TASK_COMM_LEN - 1); + Row_printLeftAlignedField(str, attr, procExe, TASK_COMM_LEN - 1); return; } case CWD: { @@ -921,22 +631,22 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field } else { cwd = this->procCwd; } - Process_printLeftAlignedField(str, attr, cwd, 25); + Row_printLeftAlignedField(str, attr, cwd, 25); return; } case ELAPSED: { - const uint64_t rt = this->host->realtimeMs; + const uint64_t rt = host->realtimeMs; const uint64_t st = this->starttime_ctime * 1000; const uint64_t dt = rt < st ? 0 : rt - st; - Process_printTime(str, /* convert to hundreds of a second */ dt / 10, coloring); + Row_printTime(str, /* convert to hundreds of a second */ dt / 10, coloring); return; } - case MAJFLT: Process_printCount(str, this->majflt, coloring); return; - case MINFLT: Process_printCount(str, this->minflt, coloring); return; - case M_RESIDENT: Process_printKBytes(str, this->m_resident, coloring); return; - case M_VIRT: Process_printKBytes(str, this->m_virt, coloring); return; + case MAJFLT: Row_printCount(str, this->majflt, coloring); return; + case MINFLT: Row_printCount(str, this->minflt, coloring); return; + case M_RESIDENT: Row_printKBytes(str, this->m_resident, coloring); return; + case M_VIRT: Row_printKBytes(str, this->m_virt, coloring); return; case NICE: xSnprintf(buffer, n, "%3ld ", this->nice); attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY] @@ -949,16 +659,16 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field xSnprintf(buffer, n, "%4ld ", this->nlwp); break; - case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, Process_fieldWidths[PERCENT_CPU], &attr); break; + case PERCENT_CPU: Row_printPercentage(this->percent_cpu, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break; case PERCENT_NORM_CPU: { - float cpuPercentage = this->percent_cpu / this->host->activeCPUs; - Process_printPercentage(cpuPercentage, buffer, n, Process_fieldWidths[PERCENT_CPU], &attr); + float cpuPercentage = this->percent_cpu / host->activeCPUs; + Row_printPercentage(cpuPercentage, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break; } - case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, 4, &attr); break; + case PERCENT_MEM: Row_printPercentage(this->percent_mem, buffer, n, 4, &attr); break; case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break; - case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break; - case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break; + case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getPid(this)); break; + case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getParent(this)); break; case PRIORITY: if (this->priority <= -100) xSnprintf(buffer, n, " RT "); @@ -1007,12 +717,12 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field } break; case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break; - case TIME: Process_printTime(str, this->time, coloring); return; + case TIME: Row_printTime(str, this->time, coloring); return; case TGID: - if (this->tgid == this->pid) + if (Process_getThreadGroup(this) == Process_getPid(this)) attr = CRT_colors[PROCESS_SHADOW]; - xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid); + xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getThreadGroup(this)); break; case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break; case TTY: @@ -1027,11 +737,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field case USER: if (this->elevated_priv) attr = CRT_colors[PROCESS_PRIV]; - else if (Process_getuid != this->st_uid) + else if (host->htopUserId != this->st_uid) attr = CRT_colors[PROCESS_SHADOW]; if (this->user) { - Process_printLeftAlignedField(str, attr, this->user, 10); + Row_printLeftAlignedField(str, attr, this->user, 10); return; } @@ -1047,32 +757,6 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field RichString_appendAscii(str, attr, buffer); } -void Process_display(const Object* cast, RichString* out) { - const Process* this = (const Process*) cast; - const Settings* settings = this->host->settings; - const ProcessField* fields = settings->ss->fields; - for (int i = 0; fields[i]; i++) - As_Process(this)->writeField(this, out, fields[i]); - - if (settings->shadowOtherUsers && this->st_uid != Process_getuid) { - RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); - } - - if (this->tag == true) { - RichString_setAttr(out, CRT_colors[PROCESS_TAG]); - } - - if (settings->highlightChanges) { - if (Process_isTomb(this)) { - out->highlightAttr = CRT_colors[PROCESS_TOMB]; - } else if (Process_isNew(this)) { - out->highlightAttr = CRT_colors[PROCESS_NEW]; - } - } - - assert(RichString_size(out) > 0); -} - void Process_done(Process* this) { assert (this != NULL); free(this->cmdline); @@ -1087,7 +771,8 @@ void Process_done(Process* this) { * happens on what is displayed - whether comm, full path, basename, etc.. So * this follows Process_writeField(COMM) and Process_writeCommand */ const char* Process_getCommand(const Process* this) { - const Settings* settings = this->host->settings; + const Settings* settings = this->super.host->settings; + if ((Process_isUserlandThread(this) && settings->showThreadNames) || !this->mergedCommand.str) { return this->cmdline; } @@ -1095,75 +780,112 @@ const char* Process_getCommand(const Process* this) { return this->mergedCommand.str; } -const ProcessClass Process_class = { - .super = { - .extends = Class(Object), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare - }, - .writeField = Process_writeField, -}; +static const char* Process_getSortKey(const Process* this) { + return Process_getCommand(this); +} -void Process_init(Process* this, const Machine* host) { - this->host = host; - this->tag = false; - this->showChildren = true; - this->show = true; - this->updated = false; - this->cmdlineBasenameEnd = -1; - this->st_uid = (uid_t)-1; +const char* Process_rowGetSortKey(const Row* super) { + const Process* this = (const Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + return Process_getSortKey(this); +} - if (Process_getuid == (uid_t)-1) { - Process_getuid = getuid(); - } +/* Test whether display must highlight this row (if the htop UID matches) */ +static bool Process_isHighlighted(const Process* this) { + const Machine* host = this->super.host; + const Settings* settings = host->settings; + return settings->shadowOtherUsers && this->st_uid != host->htopUserId; } -void Process_toggleTag(Process* this) { - this->tag = !this->tag; +bool Process_rowIsHighlighted(const Row* super) { + const Process* this = (const Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + return Process_isHighlighted(this); } -bool Process_isNew(const Process* this) { - assert(this->host); - const Machine* host = this->host; - if (host->monotonicMs >= this->seenStampMs) { - const Settings* settings = host->settings; - return host->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)settings->highlightDelaySecs; - } +/* Test whether display must follow parent process (if this thread is hidden) */ +static bool Process_isVisible(const Process* p, const Settings* settings) { + if (settings->hideUserlandThreads) + return !Process_isThread(p); + return true; +} + +bool Process_rowIsVisible(const Row* super, const Table* table) { + const Process* this = (const Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + return Process_isVisible(this, table->host->settings); +} + +/* Test whether display must filter out this process (various mechanisms) */ +static bool Process_matchesFilter(const Process* this, const Table* table) { + const Machine* host = table->host; + if (host->userId != (uid_t) -1 && this->st_uid != host->userId) + return true; + + const char* incFilter = table->incFilter; + if (incFilter && !String_contains_i(Process_getCommand(this), incFilter, true)) + return true; + + const ProcessList* pl = (const ProcessList*) host->activeTable; + assert(Object_isA((const Object*) pl, (const ObjectClass*) &ProcessList_class)); + if (pl->pidMatchList && !Hashtable_get(pl->pidMatchList, Process_getThreadGroup(this))) + return true; + return false; } -bool Process_isTomb(const Process* this) { - return this->tombStampMs > 0; +bool Process_rowMatchesFilter(const Row* super, const Table* table) { + const Process* this = (const Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + return Process_matchesFilter(this, table); +} + +void Process_init(Process* this, const Machine* host) { + Row_init(&this->super, host); + + this->cmdlineBasenameEnd = -1; } -bool Process_setPriority(Process* this, int priority) { +static bool Process_setPriority(Process* this, int priority) { if (Settings_isReadonly()) return false; - int old_prio = getpriority(PRIO_PROCESS, this->pid); - int err = setpriority(PRIO_PROCESS, this->pid, priority); + int old_prio = getpriority(PRIO_PROCESS, Process_getPid(this)); + int err = setpriority(PRIO_PROCESS, Process_getPid(this), priority); - if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) { + if (err == 0 && old_prio != getpriority(PRIO_PROCESS, Process_getPid(this))) { this->nice = priority; } return (err == 0); } -bool Process_changePriorityBy(Process* this, Arg delta) { +bool Process_rowSetPriority(Row* super, int priority) { + Process* this = (Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + return Process_setPriority(this, priority); +} + +bool Process_rowChangePriorityBy(Row* super, Arg delta) { + Process* this = (Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); return Process_setPriority(this, this->nice + delta.i); } -bool Process_sendSignal(Process* this, Arg sgn) { - return kill(this->pid, sgn.i) == 0; +static bool Process_sendSignal(Process* this, Arg sgn) { + return kill(Process_getPid(this), sgn.i) == 0; +} + +bool Process_rowSendSignal(Row* super, Arg sgn) { + Process* this = (Process*) super; + assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class)); + return Process_sendSignal(this, sgn); } int Process_compare(const void* v1, const void* v2) { const Process* p1 = (const Process*)v1; const Process* p2 = (const Process*)v2; - const Settings* settings = p1->host->settings; - const ScreenSettings* ss = settings->ss; + const ScreenSettings* ss = p1->super.host->settings->ss; ProcessField key = ScreenSettings_getActiveSortKey(ss); @@ -1171,11 +893,20 @@ int Process_compare(const void* v1, const void* v2) { // Implement tie-breaker (needed to make tree mode more stable) if (!result) - return SPACESHIP_NUMBER(p1->pid, p2->pid); + return SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2)); return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result; } +int Process_compareByParent(const Row* r1, const Row* r2) { + int result = Row_compareByParent_Base(r1, r2); + + if (result != 0) + return result; + + return Process_compare(r1, r2); +} + int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) { int r; @@ -1201,7 +932,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField return SPACESHIP_NULLSTR(p1->procCwd, p2->procCwd); case ELAPSED: r = -SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime); - return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid); + return r != 0 ? r : SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2)); case MAJFLT: return SPACESHIP_NUMBER(p1->majflt, p2->majflt); case MINFLT: @@ -1217,9 +948,9 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField case PGRP: return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp); case PID: - return SPACESHIP_NUMBER(p1->pid, p2->pid); + return SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2)); case PPID: - return SPACESHIP_NUMBER(p1->ppid, p2->ppid); + return SPACESHIP_NUMBER(Process_getParent(p1), Process_getParent(p2)); case PRIORITY: return SPACESHIP_NUMBER(p1->priority, p2->priority); case PROCESSOR: @@ -1230,7 +961,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField return SPACESHIP_NUMBER(p1->session, p2->session); case STARTTIME: r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime); - return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid); + return r != 0 ? r : SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2)); case STATE: return SPACESHIP_NUMBER(p1->state, p2->state); case ST_UID: @@ -1238,7 +969,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField case TIME: return SPACESHIP_NUMBER(p1->time, p2->time); case TGID: - return SPACESHIP_NUMBER(p1->tgid, p2->tgid); + return SPACESHIP_NUMBER(Process_getThreadGroup(p1), Process_getThreadGroup(p2)); case TPGID: return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid); case TTY: @@ -1249,7 +980,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField default: CRT_debug("Process_compareByKey_Base() called with key %d", key); assert(0 && "Process_compareByKey_Base: default key reached"); /* should never be reached */ - return SPACESHIP_NUMBER(p1->pid, p2->pid); + return SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2)); } } @@ -1327,36 +1058,33 @@ void Process_updateExe(Process* this, const char* exe) { this->mergedCommand.lastUpdate = 0; } -uint8_t Process_fieldWidths[LAST_PROCESSFIELD] = { 0 }; - -void Process_resetFieldWidths(void) { - for (size_t i = 0; i < LAST_PROCESSFIELD; i++) { - if (!Process_fields[i].autoWidth) - continue; - - size_t len = strlen(Process_fields[i].title); - assert(len <= UINT8_MAX); - Process_fieldWidths[i] = (uint8_t)len; - } -} - -void Process_updateFieldWidth(ProcessField key, size_t width) { - if (width > UINT8_MAX) - Process_fieldWidths[key] = UINT8_MAX; - else if (width > Process_fieldWidths[key]) - Process_fieldWidths[key] = (uint8_t)width; -} - void Process_updateCPUFieldWidths(float percentage) { if (percentage < 99.9F) { - Process_updateFieldWidth(PERCENT_CPU, 4); - Process_updateFieldWidth(PERCENT_NORM_CPU, 4); + Row_updateFieldWidth(PERCENT_CPU, 4); + Row_updateFieldWidth(PERCENT_NORM_CPU, 4); return; } // Add additional two characters, one for "." and another for precision. uint8_t width = ceil(log10(percentage + 0.1)) + 2; - Process_updateFieldWidth(PERCENT_CPU, width); - Process_updateFieldWidth(PERCENT_NORM_CPU, width); + Row_updateFieldWidth(PERCENT_CPU, width); + Row_updateFieldWidth(PERCENT_NORM_CPU, width); } + +const ProcessClass Process_class = { + .super = { + .super = { + .extends = Class(Row), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .sortKeyString = Process_rowGetSortKey, + .compareByParent = Process_compareByParent, + .writeField = Process_rowWriteField + }, +}; diff --git a/Process.h b/Process.h index 884164789..c6cdb995e 100644 --- a/Process.h +++ b/Process.h @@ -13,8 +13,8 @@ in the source distribution for its full text. #include #include "Object.h" -#include "ProcessField.h" #include "RichString.h" +#include "Row.h" #define PROCESS_FLAG_IO 0x00000001 @@ -23,45 +23,6 @@ in the source distribution for its full text. #define DEFAULT_HIGHLIGHT_SECS 5 -typedef enum ProcessField_ { - NULL_PROCESSFIELD = 0, - PID = 1, - COMM = 2, - STATE = 3, - PPID = 4, - PGRP = 5, - SESSION = 6, - TTY = 7, - TPGID = 8, - MINFLT = 10, - MAJFLT = 12, - PRIORITY = 18, - NICE = 19, - STARTTIME = 21, - PROCESSOR = 38, - M_VIRT = 39, - M_RESIDENT = 40, - ST_UID = 46, - PERCENT_CPU = 47, - PERCENT_MEM = 48, - USER = 49, - TIME = 50, - NLWP = 51, - TGID = 52, - PERCENT_NORM_CPU = 53, - ELAPSED = 54, - SCHEDULERPOLICY = 55, - PROC_COMM = 124, - PROC_EXE = 125, - CWD = 126, - - /* Platform specific fields, defined in ${platform}/ProcessField.h */ - PLATFORM_PROCESS_FIELDS - - /* Do not add new fields after this entry (dynamic entries follow) */ - LAST_PROCESSFIELD -} ProcessField; - /* Core process states (shared by platforms) * NOTE: The enum has an ordering that is important! * See processStateChar in process.c for ProcessSate -> letter mapping */ @@ -83,6 +44,7 @@ typedef enum ProcessState_ { } ProcessState; struct Machine_; +struct Settings_; /* Holds information about regions of the cmdline that should be * highlighted (e.g. program basename, delimiter, comm). */ @@ -106,19 +68,7 @@ typedef struct ProcessMergedCommand_ { typedef struct Process_ { /* Super object for emulated OOP */ - Object super; - - /* Pointer to quasi-global data */ - const struct Machine_* host; - - /* Process identifier */ - pid_t pid; - - /* Parent process identifier */ - pid_t ppid; - - /* Thread group identifier */ - pid_t tgid; + Row super; /* Process group identifier */ int pgrp; @@ -232,36 +182,6 @@ typedef struct Process_ { /* Current scheduling policy */ int scheduling_policy; - /* Whether the process was updated during the current scan */ - bool updated; - - /* Whether the process was tagged by the user */ - bool tag; - - /* Whether to display this process */ - bool show; - - /* Whether this process was shown last cycle */ - bool wasShown; - - /* Whether to show children of this process in tree-mode */ - bool showChildren; - - /* - * Internal time counts for showing new and exited processes. - */ - uint64_t seenStampMs; - uint64_t tombStampMs; - - /* - * Internal state for tree-mode. - */ - int32_t indent; - unsigned int tree_depth; - - /* Has no known parent process */ - bool isRoot; - /* * Internal state for merged Command display */ @@ -291,39 +211,57 @@ typedef struct ProcessFieldData_ { bool autoWidth; } ProcessFieldData; +#define LAST_PROCESSFIELD LAST_RESERVED_FIELD +typedef int32_t ProcessField; /* see ReservedField list in RowField.h */ + // Implemented in platform-specific code: -void Process_writeField(const Process* this, RichString* str, ProcessField field); +void Process_writeField(const Process* row, RichString* str, ProcessField field); int Process_compare(const void* v1, const void* v2); +int Process_compareByParent(const Row* r1, const Row* v2); void Process_delete(Object* cast); extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -extern uint8_t Process_fieldWidths[LAST_PROCESSFIELD]; -#define PROCESS_MIN_PID_DIGITS 5 -#define PROCESS_MAX_PID_DIGITS 19 -#define PROCESS_MIN_UID_DIGITS 5 -#define PROCESS_MAX_UID_DIGITS 20 -extern int Process_pidDigits; -extern int Process_uidDigits; +#define Process_pidDigits Row_pidDigits +#define Process_uidDigits Row_uidDigits typedef Process* (*Process_New)(const struct Machine_*); -typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField); typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField); typedef struct ProcessClass_ { - const ObjectClass super; - const Process_WriteField writeField; + const RowClass super; const Process_CompareByKey compareByKey; } ProcessClass; -#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass)) +#define As_Process(this_) ((const ProcessClass*)((this_)->super.super.klass)) + +#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_)) + + +static inline void Process_setPid(Process* this, pid_t pid) { + this->super.id = pid; +} + +static inline pid_t Process_getPid(const Process* this) { + return (pid_t)this->super.id; +} -#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_)) +static inline void Process_setThreadGroup(Process* this, pid_t pid) { + this->super.group = pid; +} + +static inline pid_t Process_getThreadGroup(const Process* this) { + return (pid_t)this->super.group; +} -static inline pid_t Process_getParentPid(const Process* this) { - return this->tgid == this->pid ? this->ppid : this->tgid; +static inline void Process_setParent(Process* this, pid_t pid) { + this->super.parent = pid; } -static inline bool Process_isChildOf(const Process* this, pid_t pid) { - return pid == Process_getParentPid(this); +static inline pid_t Process_getParent(const Process* this) { + return (pid_t)this->super.parent; +} + +static inline pid_t Process_getGroupOrParent(const Process* this) { + return Row_getGroupOrParent(&this->super); } static inline bool Process_isKernelThread(const Process* this) { @@ -344,68 +282,30 @@ static inline bool Process_isThread(const Process* this) { #define CMDLINE_HIGHLIGHT_FLAG_DELETED 0x00000008 #define CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR 0x00000010 -#define ONE_K 1024UL -#define ONE_M (ONE_K * ONE_K) -#define ONE_G (ONE_M * ONE_K) -#define ONE_T (1ULL * ONE_G * ONE_K) -#define ONE_P (1ULL * ONE_T * ONE_K) - -#define ONE_DECIMAL_K 1000UL -#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) -#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) -#define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K) -#define ONE_DECIMAL_P (1ULL * ONE_DECIMAL_T * ONE_DECIMAL_K) - -void Process_setupColumnWidths(void); - -/* Sets the size of the UID column based on the passed UID */ -void Process_setUidColumnWidth(uid_t maxUid); - -/* Takes number in bytes (base 1024). Prints 6 columns. */ -void Process_printBytes(RichString* str, unsigned long long number, bool coloring); - -/* Takes number in kilo bytes (base 1024). Prints 6 columns. */ -void Process_printKBytes(RichString* str, unsigned long long number, bool coloring); - -/* Takes number as count (base 1000). Prints 12 columns. */ -void Process_printCount(RichString* str, unsigned long long number, bool coloring); - -/* Takes time in hundredths of a seconds. Prints 9 columns. */ -void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring); - -/* Takes rate in bare unit (base 1024) per second. Prints 12 columns. */ -void Process_printRate(RichString* str, double rate, bool coloring); - void Process_fillStarttimeBuffer(Process* this); -void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width); - -void Process_printPercentage(float val, char* buffer, size_t n, uint8_t width, int* attr); - -void Process_display(const Object* cast, RichString* out); - void Process_done(Process* this); extern const ProcessClass Process_class; void Process_init(Process* this, const struct Machine_* host); -void Process_toggleTag(Process* this); +const char* Process_rowGetSortKey(const Row* super); + +bool Process_rowSetPriority(Row* super, int priority); -bool Process_isNew(const Process* this); +bool Process_rowChangePriorityBy(Row* super, Arg delta); -bool Process_isTomb(const Process* this); +bool Process_rowSendSignal(Row* super, Arg sgn); -bool Process_setPriority(Process* this, int priority); +bool Process_rowIsHighlighted(const Row* super); -bool Process_changePriorityBy(Process* this, Arg delta); +bool Process_rowIsVisible(const Row* super, const struct Table_* table); -bool Process_sendSignal(Process* this, Arg sgn); +bool Process_rowMatchesFilter(const Row* super, const struct Table_* table); static inline int Process_pidEqualCompare(const void* v1, const void* v2) { - const pid_t p1 = ((const Process*)v1)->pid; - const pid_t p2 = ((const Process*)v2)->pid; - return p1 != p2; /* return zero when equal */ + return Row_idEqualCompare(v1, v2); } int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key); @@ -418,12 +318,10 @@ void Process_updateExe(Process* this, const char* exe); /* This function constructs the string that is displayed by * Process_writeCommand and also returned by Process_getCommand */ -void Process_makeCommandStr(Process* this); +void Process_makeCommandStr(Process* this, const struct Settings_ *settings); void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str); -void Process_resetFieldWidths(void); -void Process_updateFieldWidth(ProcessField key, size_t width); void Process_updateCPUFieldWidths(float percentage); #endif diff --git a/ProcessList.c b/ProcessList.c index 58e63d030..516dcd7c5 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -21,453 +21,74 @@ in the source distribution for its full text. void ProcessList_init(ProcessList* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList) { - this->processes = Vector_new(klass, true, DEFAULT_SIZE); - this->displayList = Vector_new(klass, false, DEFAULT_SIZE); - this->processTable = Hashtable_new(200, false); + Table_init(&this->super, klass, host); + this->pidMatchList = pidMatchList; - this->needsSort = true; - this->following = -1; - this->host = host; } void ProcessList_done(ProcessList* this) { - Hashtable_delete(this->processTable); - Vector_delete(this->displayList); - Vector_delete(this->processes); -} - -void ProcessList_setPanel(ProcessList* this, Panel* panel) { - this->panel = panel; -} - -// helper function to fill an aligned title string for a dynamic column -static const char* alignedTitleDynamicColumn(const Settings* settings, int key, char* titleBuffer, size_t titleBufferSize) { - const DynamicColumn* column = Hashtable_get(settings->dynamicColumns, key); - if (column == NULL) - 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); - return titleBuffer; -} - -// helper function to fill an aligned title string for a process field -static const char* alignedTitleProcessField(ProcessField field, char* titleBuffer, size_t titleBufferSize) { - const char* title = Process_fields[field].title; - if (!title) - return "- "; - - if (Process_fields[field].pidColumn) { - xSnprintf(titleBuffer, titleBufferSize, "%*s ", Process_pidDigits, title); - return titleBuffer; - } - - if (field == ST_UID) { - xSnprintf(titleBuffer, titleBufferSize, "%*s ", Process_uidDigits, title); - return titleBuffer; - } - - if (Process_fields[field].autoWidth) { - if (field == PERCENT_CPU) - xSnprintf(titleBuffer, titleBufferSize, "%*s ", Process_fieldWidths[field], title); - else - xSnprintf(titleBuffer, titleBufferSize, "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title); - return titleBuffer; - } - - return title; -} - -// helper function to create an aligned title string for a given field -static const char* ProcessField_alignedTitle(const Settings* settings, ProcessField field) { - static char titleBuffer[UINT8_MAX + sizeof(" ")]; - assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" ")); - assert(sizeof(titleBuffer) >= PROCESS_MAX_PID_DIGITS + sizeof(" ")); - assert(sizeof(titleBuffer) >= PROCESS_MAX_UID_DIGITS + sizeof(" ")); - - if (field < LAST_PROCESSFIELD) - return alignedTitleProcessField(field, titleBuffer, sizeof(titleBuffer)); - return alignedTitleDynamicColumn(settings, field, titleBuffer, sizeof(titleBuffer)); -} - -void ProcessList_printHeader(const ProcessList* this, RichString* header) { - RichString_rewind(header, RichString_size(header)); - - const Settings* settings = this->host->settings; - const ScreenSettings* ss = settings->ss; - const ProcessField* fields = ss->fields; - - ProcessField key = ScreenSettings_getActiveSortKey(ss); - - for (int i = 0; fields[i]; i++) { - int color; - if (ss->treeView && ss->treeViewAlwaysByPID) { - color = CRT_colors[PANEL_HEADER_FOCUS]; - } else if (key == fields[i]) { - color = CRT_colors[PANEL_SELECTION_FOCUS]; - } else { - color = CRT_colors[PANEL_HEADER_FOCUS]; - } - - RichString_appendWide(header, color, ProcessField_alignedTitle(settings, fields[i])); - if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') { - bool ascending = ScreenSettings_getActiveDirection(ss) == 1; - RichString_rewind(header, 1); // rewind to override space - RichString_appendnWide(header, - CRT_colors[PANEL_SELECTION_FOCUS], - CRT_treeStr[ascending ? TREE_STR_ASC : TREE_STR_DESC], - 1); - } - if (COMM == fields[i] && settings->showMergedCommand) { - RichString_appendAscii(header, color, "(merged)"); - } - } -} - -void ProcessList_add(ProcessList* this, Process* p) { - assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) == -1); - assert(Hashtable_get(this->processTable, p->pid) == NULL); - - // highlighting processes found in first scan by first scan marked "far in the past" - p->seenStampMs = this->host->monotonicMs; - - Vector_add(this->processes, p); - Hashtable_put(this->processTable, p->pid, p); - - assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) != -1); - assert(Hashtable_get(this->processTable, p->pid) != NULL); - assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable))); -} - -// ProcessList_removeIndex removes Process p from the list's map and soft deletes -// it from its vector. Vector_compact *must* be called once the caller is done -// removing items. -// Should only be called from ProcessList_scan to avoid breaking dying process highlighting. -static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx) { - pid_t pid = p->pid; - - assert(p == (Process*)Vector_get(this->processes, idx)); - assert(Hashtable_get(this->processTable, pid) != NULL); - - Hashtable_remove(this->processTable, pid); - Vector_softRemove(this->processes, idx); - - if (this->following != -1 && this->following == pid) { - this->following = -1; - Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS); - } - - assert(Hashtable_get(this->processTable, pid) == NULL); - assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable))); -} - -static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, unsigned int level, int32_t indent, bool show) { - // On OpenBSD the kernel thread 'swapper' has pid 0. - // Do not treat it as root of any tree. - if (pid == 0) - return; - - // The vector is sorted by parent PID, find the start of the range by bisection - int vsize = Vector_size(this->processes); - int l = 0; - int r = vsize; - while (l < r) { - int c = (l + r) / 2; - Process* process = (Process*)Vector_get(this->processes, c); - pid_t ppid = process->isRoot ? 0 : Process_getParentPid(process); - if (ppid < pid) { - l = c + 1; - } else { - r = c; - } - } - // Find the end to know the last line for indent handling purposes - int lastShown = r; - while (r < vsize) { - Process* process = (Process*)Vector_get(this->processes, r); - if (!Process_isChildOf(process, pid)) - break; - if (process->show) - lastShown = r; - r++; - } - - for (int i = l; i < r; i++) { - Process* process = (Process*)Vector_get(this->processes, i); - - if (!show) { - process->show = false; - } - - Vector_add(this->displayList, process); - - int32_t nextIndent = indent | ((int32_t)1 << MINIMUM(level, sizeof(process->indent) * 8 - 2)); - ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < lastShown) ? nextIndent : indent, process->show && process->showChildren); - if (i == lastShown) { - process->indent = -nextIndent; - } else { - process->indent = nextIndent; - } - - process->tree_depth = level + 1; - } -} - -static int compareProcessByKnownParentThenNatural(const void* v1, const void* v2) { - const Process* p1 = (const Process*)v1; - const Process* p2 = (const Process*)v2; - - int result = SPACESHIP_NUMBER( - p1->isRoot ? 0 : Process_getParentPid(p1), - p2->isRoot ? 0 : Process_getParentPid(p2) - ); - - if (result != 0) - return result; - - return Process_compare(v1, v2); -} - -// Builds a sorted tree from scratch, without relying on previously gathered information -static void ProcessList_buildTree(ProcessList* this) { - Vector_prune(this->displayList); - - // Mark root processes - int vsize = Vector_size(this->processes); - for (int i = 0; i < vsize; i++) { - Process* process = (Process*)Vector_get(this->processes, i); - pid_t ppid = Process_getParentPid(process); - process->isRoot = false; - - // If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0) - // on Mac OS X 10.11.6) regard this process as root. - if (process->pid == ppid) { - process->isRoot = true; - continue; - } - - // On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2) - // use a ppid of 0. As that PID can't exist, we can skip searching for it. - if (!ppid) { - process->isRoot = true; - continue; - } - - // We don't know about its parent for whatever reason - if (ProcessList_findProcess(this, ppid) == NULL) - process->isRoot = true; - } - - // Sort by known parent PID (roots first), then PID - Vector_quickSortCustomCompare(this->processes, compareProcessByKnownParentThenNatural); - - // Find all processes whose parent is not visible - for (int i = 0; i < vsize; i++) { - Process* process = (Process*)Vector_get(this->processes, i); - - // If parent not found, then construct the tree with this node as root - if (process->isRoot) { - process = (Process*)Vector_get(this->processes, i); - process->indent = 0; - process->tree_depth = 0; - Vector_add(this->displayList, process); - ProcessList_buildTreeBranch(this, process->pid, 0, 0, process->showChildren); - continue; - } - } - - this->needsSort = false; - - // Check consistency of the built structures - assert(Vector_size(this->displayList) == vsize); (void)vsize; -} - -void ProcessList_updateDisplayList(ProcessList* this) { - if (this->host->settings->ss->treeView) { - if (this->needsSort) - ProcessList_buildTree(this); - } else { - if (this->needsSort) - Vector_insertionSort(this->processes); - Vector_prune(this->displayList); - int size = Vector_size(this->processes); - for (int i = 0; i < size; i++) - Vector_add(this->displayList, Vector_get(this->processes, i)); - } - this->needsSort = false; -} - -ProcessField ProcessList_keyAt(const ProcessList* this, int at) { - int x = 0; - const Settings* settings = this->host->settings; - const ProcessField* fields = settings->ss->fields; - ProcessField field; - for (int i = 0; (field = fields[i]); i++) { - int len = strlen(ProcessField_alignedTitle(settings, field)); - if (at >= x && at <= x + len) { - return field; - } - x += len; - } - return COMM; -} - -void ProcessList_expandTree(ProcessList* this) { - int size = Vector_size(this->processes); - for (int i = 0; i < size; i++) { - Process* process = (Process*) Vector_get(this->processes, i); - process->showChildren = true; - } -} - -// Called on collapse-all toggle and on startup, possibly in non-tree mode -void ProcessList_collapseAllBranches(ProcessList* this) { - ProcessList_buildTree(this); // Update `tree_depth` fields of the processes - this->needsSort = true; // ProcessList is sorted by parent now, force new sort - int size = Vector_size(this->processes); - for (int i = 0; i < size; i++) { - Process* process = (Process*) Vector_get(this->processes, i); - // FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1 - if (process->tree_depth > 0 && process->pid > 1) - process->showChildren = false; - } -} - -void ProcessList_rebuildPanel(ProcessList* this) { - ProcessList_updateDisplayList(this); - - const char* incFilter = this->incFilter; - - const int currPos = Panel_getSelectedIndex(this->panel); - const int currScrollV = this->panel->scrollV; - const int currSize = Panel_size(this->panel); - - Panel_prune(this->panel); - - /* Follow main process if followed a userland thread and threads are now hidden */ - const Machine* host= this->host; - const Settings* settings = host->settings; - if (this->following != -1 && settings->hideUserlandThreads) { - const Process* followedProcess = (const Process*) Hashtable_get(this->processTable, this->following); - if (followedProcess && Process_isThread(followedProcess) && Hashtable_get(this->processTable, followedProcess->tgid) != NULL) { - this->following = followedProcess->tgid; - } - } - - const int processCount = Vector_size(this->displayList); - int idx = 0; - bool foundFollowed = false; - - for (int i = 0; i < processCount; i++) { - Process* p = (Process*) Vector_get(this->displayList, i); - - if ( (!p->show) - || (host->userId != (uid_t) -1 && (p->st_uid != host->userId)) - || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter, true))) - || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) ) - continue; - - Panel_set(this->panel, idx, (Object*)p); - - if (this->following != -1 && p->pid == this->following) { - foundFollowed = true; - Panel_setSelected(this->panel, idx); - /* Keep scroll position relative to followed process */ - this->panel->scrollV = idx - (currPos-currScrollV); - } - idx++; - } - - if (this->following != -1 && !foundFollowed) { - /* Reset if current followed pid not found */ - this->following = -1; - Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS); - } - - if (this->following == -1) { - /* If the last item was selected, keep the new last item selected */ - if (currPos > 0 && currPos == currSize - 1) - Panel_setSelected(this->panel, Panel_size(this->panel) - 1); - else - Panel_setSelected(this->panel, currPos); - - this->panel->scrollV = currScrollV; - } + Table_done(&this->super); } Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) { - Process* proc = (Process*) Hashtable_get(this->processTable, pid); + const Table* table = &this->super; + Process* proc = (Process*) Hashtable_get(table->table, pid); *preExisting = proc != NULL; if (proc) { - assert(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -1); - assert(proc->pid == pid); + assert(Vector_indexOf(table->rows, proc, Row_idEqualCompare) != -1); + assert(Process_getPid(proc) == pid); } else { - proc = constructor(this->host); + proc = constructor(table->host); assert(proc->cmdline == NULL); - proc->pid = pid; + Process_setPid(proc, pid); } return proc; } -void ProcessList_scan(ProcessList* this) { - // mark all process as "dirty" - for (int i = 0; i < Vector_size(this->processes); i++) { - Process* p = (Process*) Vector_get(this->processes, i); - p->updated = false; - p->wasShown = p->show; - p->show = true; - } - +static void ProcessList_prepareEntries(Table* super) { + ProcessList* this = (ProcessList*) super; this->totalTasks = 0; this->userlandThreads = 0; this->kernelThreads = 0; this->runningTasks = 0; - Process_resetFieldWidths(); - - // set scan timestamp - static bool firstScanDone = false; - Machine* host = this->host; - if (firstScanDone) { - Platform_gettime_monotonic(&host->monotonicMs); - } else { - host->monotonicMs = 0; - firstScanDone = true; - } + Table_prepareEntries(super); +} +static void ProcessList_iterateEntries(Table* super) { + ProcessList* this = (ProcessList*) super; + // calling into platform-specific code ProcessList_goThroughEntries(this); +} - uid_t maxUid = 0; +static void ProcessList_cleanupEntries(Table* super) { + Machine* host = super->host; const Settings* settings = host->settings; - for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { - Process* p = (Process*) Vector_get(this->processes, i); - Process_makeCommandStr(p); + + // Finish process table update, culling any exit'd processes + for (int i = Vector_size(super->rows) - 1; i >= 0; i--) { + Process* p = (Process*) Vector_get(super->rows, i); + + // tidy up Process state after refreshing the ProcessList table + Process_makeCommandStr(p, settings); // keep track of the highest UID for column scaling - if (p->st_uid > maxUid) - maxUid = p->st_uid; + if (p->st_uid > host->maxUserId) + host->maxUserId = p->st_uid; - if (p->tombStampMs > 0) { - // remove tombed process - if (host->monotonicMs >= p->tombStampMs) { - ProcessList_removeIndex(this, p, i); - } - } else if (p->updated == false) { - // process no longer exists - if (settings->highlightChanges && p->wasShown) { - // mark tombed - p->tombStampMs = host->monotonicMs + 1000 * settings->highlightDelaySecs; - } else { - // immediately remove - ProcessList_removeIndex(this, p, i); - } - } + Table_cleanupRow(super, (Row*) p, i); } - // Compact the processes vector in case of any deletions - Vector_compact(this->processes); - - // Set UID column width based on max UID. - Process_setUidColumnWidth(maxUid); + // compact the table in case of deletions + Table_compact(super); } + +const TableClass ProcessList_class = { + .super = { + .extends = Class(Table), + .delete = ProcessList_delete, + }, + .prepare = ProcessList_prepareEntries, + .iterate = ProcessList_iterateEntries, + .cleanup = ProcessList_cleanupEntries, +}; diff --git a/ProcessList.h b/ProcessList.h index 0f0f7d517..9710a0a54 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -21,24 +21,12 @@ in the source distribution for its full text. #include "Panel.h" #include "Process.h" #include "RichString.h" -#include "Settings.h" -#include "UsersTable.h" -#include "Vector.h" +#include "Table.h" typedef struct ProcessList_ { - struct Machine_* host; + Table super; - Vector* processes; /* all known processes; sort order can vary and differ from display order */ - Vector* displayList; /* process tree flattened in display order (borrowed); - updated in ProcessList_updateDisplayList when rebuilding panel */ - Hashtable* processTable; /* fast known process lookup by PID */ - - bool needsSort; - - Panel* panel; - int following; - const char* incFilter; Hashtable* pidMatchList; unsigned int totalTasks; @@ -49,35 +37,23 @@ typedef struct ProcessList_ { /* Implemented by platforms */ ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); -void ProcessList_delete(ProcessList* this); +void ProcessList_delete(Object* cast); void ProcessList_goThroughEntries(ProcessList* this); void ProcessList_init(ProcessList* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList); void ProcessList_done(ProcessList* this); -void ProcessList_setPanel(ProcessList* this, Panel* panel); - -void ProcessList_printHeader(const ProcessList* this, RichString* header); - -void ProcessList_add(ProcessList* this, Process* p); - -void ProcessList_updateDisplayList(ProcessList* this); +extern const TableClass ProcessList_class; -ProcessField ProcessList_keyAt(const ProcessList* this, int at); - -void ProcessList_expandTree(ProcessList* this); - -void ProcessList_collapseAllBranches(ProcessList* this); - -void ProcessList_rebuildPanel(ProcessList* this); +static inline void ProcessList_add(ProcessList* this, Process* process) { + Table_add(&this->super, &process->super); +} Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); -void ProcessList_scan(ProcessList* this); - static inline Process* ProcessList_findProcess(ProcessList* this, pid_t pid) { - return (Process*) Hashtable_get(this->processTable, pid); + return (Process*) Table_findRow(&this->super, pid); } #endif diff --git a/ProcessLocksScreen.c b/ProcessLocksScreen.c index 57c9ce75f..36a37f927 100644 --- a/ProcessLocksScreen.c +++ b/ProcessLocksScreen.c @@ -24,9 +24,9 @@ ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) { ProcessLocksScreen* this = xMalloc(sizeof(ProcessLocksScreen)); Object_setClass(this, Class(ProcessLocksScreen)); if (Process_isThread(process)) - this->pid = process->tgid; + this->pid = Process_getThreadGroup(process); else - this->pid = process->pid; + this->pid = Process_getPid(process); return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE EXCLUSION READ/WRITE DEVICE NODE START END FILENAME"); } diff --git a/Row.c b/Row.c new file mode 100644 index 000000000..09a320690 --- /dev/null +++ b/Row.c @@ -0,0 +1,486 @@ +/* +htop - Row.c +(C) 2004-2015 Hisham H. Muhammad +(C) 2020-2023 Red Hat, Inc. All Rights Reserved. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "Row.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "DynamicColumn.h" +#include "Machine.h" +#include "Macros.h" +#include "RichString.h" +#include "Settings.h" +#include "Table.h" +#include "XUtils.h" + + +int Row_pidDigits = ROW_MIN_PID_DIGITS; +int Row_uidDigits = ROW_MIN_UID_DIGITS; + +void Row_init(Row* this, const Machine* host) { + this->host = host; + this->tag = false; + this->showChildren = true; + this->show = true; + this->wasShown = false; + this->updated = false; +} + +static inline bool Row_isNew(const Row* this) { + const Machine* host = this->host; + if (host->monotonicMs < this->seenStampMs) + return false; + + const Settings* settings = host->settings; + return host->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)settings->highlightDelaySecs; +} + +static inline bool Row_isTomb(const Row* this) { + return this->tombStampMs > 0; +} + +void Row_display(const Object* cast, RichString* out) { + const Row* this = (const Row*) cast; + const Settings* settings = this->host->settings; + const ProcessField* fields = settings->ss->fields; + + for (int i = 0; fields[i]; i++) + As_Row(this)->writeField(this, out, fields[i]); + + if (Row_isHighlighted(this)) + RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); + + if (this->tag == true) + RichString_setAttr(out, CRT_colors[PROCESS_TAG]); + + if (settings->highlightChanges) { + if (Row_isTomb(this)) + out->highlightAttr = CRT_colors[PROCESS_TOMB]; + else if (Row_isNew(this)) + out->highlightAttr = CRT_colors[PROCESS_NEW]; + } + + assert(RichString_size(out) > 0); +} + +void Row_setPidColumnWidth(pid_t maxPid) { + if (maxPid < (int)pow(10, ROW_MIN_PID_DIGITS)) { + Row_pidDigits = ROW_MIN_PID_DIGITS; + return; + } + + Row_pidDigits = (int)log10(maxPid) + 1; + assert(Row_pidDigits <= ROW_MAX_PID_DIGITS); +} + +void Row_setUidColumnWidth(uid_t maxUid) { + if (maxUid < (uid_t)pow(10, ROW_MIN_UID_DIGITS)) { + Row_uidDigits = ROW_MIN_UID_DIGITS; + return; + } + + Row_uidDigits = (int)log10(maxUid) + 1; + assert(Row_uidDigits <= ROW_MAX_UID_DIGITS); +} + +uint8_t Row_fieldWidths[LAST_PROCESSFIELD] = { 0 }; + +void Row_resetFieldWidths(void) { + for (size_t i = 0; i < LAST_PROCESSFIELD; i++) { + if (!Process_fields[i].autoWidth) + continue; + + size_t len = strlen(Process_fields[i].title); + assert(len <= UINT8_MAX); + Row_fieldWidths[i] = (uint8_t)len; + } +} + +void Row_updateFieldWidth(RowField key, size_t width) { + if (width > UINT8_MAX) + Row_fieldWidths[key] = UINT8_MAX; + else if (width > Row_fieldWidths[key]) + Row_fieldWidths[key] = (uint8_t)width; +} + +// helper function to fill an aligned title string for a dynamic column +static const char* alignedTitleDynamicColumn(const Settings* settings, int key, char* titleBuffer, size_t titleBufferSize) { + const DynamicColumn* column = Hashtable_get(settings->dynamicColumns, key); + if (column == NULL) + 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); + return titleBuffer; +} + +// helper function to fill an aligned title string for a process field +static const char* alignedTitleProcessField(ProcessField field, char* titleBuffer, size_t titleBufferSize) { + const char* title = Process_fields[field].title; + if (!title) + return "- "; + + if (Process_fields[field].pidColumn) { + xSnprintf(titleBuffer, titleBufferSize, "%*s ", Row_pidDigits, title); + return titleBuffer; + } + + if (field == ST_UID) { + xSnprintf(titleBuffer, titleBufferSize, "%*s ", Row_uidDigits, title); + return titleBuffer; + } + + if (Process_fields[field].autoWidth) { + if (field == PERCENT_CPU) + xSnprintf(titleBuffer, titleBufferSize, "%*s ", Row_fieldWidths[field], title); + else + xSnprintf(titleBuffer, titleBufferSize, "%-*.*s ", Row_fieldWidths[field], Row_fieldWidths[field], title); + return titleBuffer; + } + + return title; +} + +// helper function to create an aligned title string for a given field +const char* RowField_alignedTitle(const Settings* settings, RowField field) { + static char titleBuffer[UINT8_MAX + sizeof(" ")]; + assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" ")); + assert(sizeof(titleBuffer) >= ROW_MAX_PID_DIGITS + sizeof(" ")); + assert(sizeof(titleBuffer) >= ROW_MAX_UID_DIGITS + sizeof(" ")); + + if (field < LAST_PROCESSFIELD) + return alignedTitleProcessField((ProcessField)field, titleBuffer, sizeof(titleBuffer)); + return alignedTitleDynamicColumn(settings, field, titleBuffer, sizeof(titleBuffer)); +} + +RowField RowField_keyAt(const Settings* settings, int at) { + const RowField* fields = (const RowField*) settings->ss->fields; + RowField field; + int x = 0; + for (int i = 0; (field = fields[i]); i++) { + int len = strlen(RowField_alignedTitle(settings, field)); + if (at >= x && at <= x + len) { + return field; + } + x += len; + } + return COMM; +} + +void Row_printBytes(RichString* str, unsigned long long number, bool coloring) { + char buffer[16]; + int len; + + int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; + int megabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; + int gigabytesColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS]; + int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS]; + int baseColor = CRT_colors[PROCESS]; + + if (number == ULLONG_MAX) { + //Invalid number + RichString_appendAscii(str, shadowColor, " N/A "); + return; + } + + number /= ONE_K; + + if (number < 1000) { + //Plain number, no markings + len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number); + RichString_appendnAscii(str, baseColor, buffer, len); + } else if (number < 100000) { + //2 digit MB, 3 digit KB + len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 1000); + RichString_appendnAscii(str, megabytesColor, buffer, len); + number %= 1000; + len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number); + RichString_appendnAscii(str, baseColor, buffer, len); + } else if (number < 1000 * ONE_K) { + //3 digit MB + number /= ONE_K; + len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number); + RichString_appendnAscii(str, megabytesColor, buffer, len); + } else if (number < 10000 * ONE_K) { + //1 digit GB, 3 digit MB + number /= ONE_K; + len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000); + RichString_appendnAscii(str, gigabytesColor, buffer, len); + number %= 1000; + len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number); + RichString_appendnAscii(str, megabytesColor, buffer, len); + } else if (number < 100 * ONE_M) { + //2 digit GB, 1 digit MB + len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / ONE_M); + RichString_appendnAscii(str, gigabytesColor, buffer, len); + number = (number % ONE_M) * 10 / ONE_M; + len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); + RichString_appendnAscii(str, megabytesColor, buffer, len); + RichString_appendAscii(str, gigabytesColor, "G "); + } else if (number < 1000 * ONE_M) { + //3 digit GB + number /= ONE_M; + len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number); + RichString_appendnAscii(str, gigabytesColor, buffer, len); + } else if (number < 10000ULL * ONE_M) { + //1 digit TB, 3 digit GB + number /= ONE_M; + len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000); + RichString_appendnAscii(str, largeNumberColor, buffer, len); + number %= 1000; + len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number); + RichString_appendnAscii(str, gigabytesColor, buffer, len); + } else if (number < 100ULL * ONE_G) { + //2 digit TB, 1 digit GB + len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / ONE_G); + RichString_appendnAscii(str, largeNumberColor, buffer, len); + number = (number % ONE_G) * 10 / ONE_G; + len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); + RichString_appendnAscii(str, gigabytesColor, buffer, len); + RichString_appendAscii(str, largeNumberColor, "T "); + } else if (number < 10000ULL * ONE_G) { + //3 digit TB or 1 digit PB, 3 digit TB + number /= ONE_G; + len = xSnprintf(buffer, sizeof(buffer), "%4lluT ", number); + RichString_appendnAscii(str, largeNumberColor, buffer, len); + } else { + //2 digit PB and above + len = xSnprintf(buffer, sizeof(buffer), "%4.1lfP ", (double)number / ONE_T); + RichString_appendnAscii(str, largeNumberColor, buffer, len); + } +} + +void Row_printKBytes(RichString* str, unsigned long long number, bool coloring) { + if (number == ULLONG_MAX) + Row_printBytes(str, ULLONG_MAX, coloring); + else + Row_printBytes(str, number * ONE_K, coloring); +} + +void Row_printCount(RichString* str, unsigned long long number, bool coloring) { + char buffer[13]; + + int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; + int megabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; + int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS]; + int baseColor = CRT_colors[PROCESS]; + + if (number == ULLONG_MAX) { + RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A "); + } else if (number >= 100000LL * ONE_DECIMAL_T) { + xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G); + RichString_appendnAscii(str, largeNumberColor, buffer, 12); + } else if (number >= 100LL * ONE_DECIMAL_T) { + xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M); + RichString_appendnAscii(str, largeNumberColor, buffer, 8); + RichString_appendnAscii(str, megabytesColor, buffer + 8, 4); + } else if (number >= 10LL * ONE_DECIMAL_G) { + xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K); + RichString_appendnAscii(str, largeNumberColor, buffer, 5); + RichString_appendnAscii(str, megabytesColor, buffer + 5, 3); + RichString_appendnAscii(str, baseColor, buffer + 8, 4); + } else { + xSnprintf(buffer, sizeof(buffer), "%11llu ", number); + RichString_appendnAscii(str, largeNumberColor, buffer, 2); + RichString_appendnAscii(str, megabytesColor, buffer + 2, 3); + RichString_appendnAscii(str, baseColor, buffer + 5, 3); + RichString_appendnAscii(str, shadowColor, buffer + 8, 4); + } +} + +void Row_printTime(RichString* str, unsigned long long totalHundredths, bool coloring) { + char buffer[10]; + int len; + + unsigned long long totalSeconds = totalHundredths / 100; + unsigned long long hours = totalSeconds / 3600; + unsigned long long days = totalSeconds / 86400; + int minutes = (totalSeconds / 60) % 60; + int seconds = totalSeconds % 60; + int hundredths = totalHundredths - (totalSeconds * 100); + + int yearColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS]; + int dayColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS]; + int hourColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS]; + int baseColor = CRT_colors[PROCESS]; + + if (days >= /* Ignore leap years */365) { + int years = days / 365; + int daysLeft = days - 365 * years; + + if (years >= 10000000) { + RichString_appendnAscii(str, yearColor, "eternity ", 9); + } else if (years >= 1000) { + len = xSnprintf(buffer, sizeof(buffer), "%7dy ", years); + RichString_appendnAscii(str, yearColor, buffer, len); + } else if (daysLeft >= 100) { + len = xSnprintf(buffer, sizeof(buffer), "%3dy", years); + RichString_appendnAscii(str, yearColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%3dd ", daysLeft); + RichString_appendnAscii(str, dayColor, buffer, len); + } else if (daysLeft >= 10) { + len = xSnprintf(buffer, sizeof(buffer), "%4dy", years); + RichString_appendnAscii(str, yearColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%2dd ", daysLeft); + RichString_appendnAscii(str, dayColor, buffer, len); + } else { + len = xSnprintf(buffer, sizeof(buffer), "%5dy", years); + RichString_appendnAscii(str, yearColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%1dd ", daysLeft); + RichString_appendnAscii(str, dayColor, buffer, len); + } + } else if (days >= 100) { + int hoursLeft = hours - days * 24; + + if (hoursLeft >= 10) { + len = xSnprintf(buffer, sizeof(buffer), "%4llud", days); + RichString_appendnAscii(str, dayColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%2dh ", hoursLeft); + RichString_appendnAscii(str, hourColor, buffer, len); + } else { + len = xSnprintf(buffer, sizeof(buffer), "%5llud", days); + RichString_appendnAscii(str, dayColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%1dh ", hoursLeft); + RichString_appendnAscii(str, hourColor, buffer, len); + } + } else if (hours >= 100) { + int minutesLeft = totalSeconds / 60 - hours * 60; + + if (minutesLeft >= 10) { + len = xSnprintf(buffer, sizeof(buffer), "%4lluh", hours); + RichString_appendnAscii(str, hourColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%2dm ", minutesLeft); + RichString_appendnAscii(str, baseColor, buffer, len); + } else { + len = xSnprintf(buffer, sizeof(buffer), "%5lluh", hours); + RichString_appendnAscii(str, hourColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%1dm ", minutesLeft); + RichString_appendnAscii(str, baseColor, buffer, len); + } + } else if (hours > 0) { + len = xSnprintf(buffer, sizeof(buffer), "%2lluh", hours); + RichString_appendnAscii(str, hourColor, buffer, len); + len = xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds); + RichString_appendnAscii(str, baseColor, buffer, len); + } else { + len = xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths); + RichString_appendnAscii(str, baseColor, buffer, len); + } +} + +void Row_printRate(RichString* str, double rate, bool coloring) { + char buffer[16]; + + int largeNumberColor = CRT_colors[LARGE_NUMBER]; + int megabytesColor = CRT_colors[PROCESS_MEGABYTES]; + int shadowColor = CRT_colors[PROCESS_SHADOW]; + int baseColor = CRT_colors[PROCESS]; + + if (!coloring) { + largeNumberColor = CRT_colors[PROCESS]; + megabytesColor = CRT_colors[PROCESS]; + } + + if (!isNonnegative(rate)) { + RichString_appendAscii(str, shadowColor, " N/A "); + } else if (rate < 0.005) { + int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate); + RichString_appendnAscii(str, shadowColor, buffer, len); + } else if (rate < ONE_K) { + int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate); + RichString_appendnAscii(str, baseColor, buffer, len); + } else if (rate < ONE_M) { + int len = snprintf(buffer, sizeof(buffer), "%7.2f K/s ", rate / ONE_K); + RichString_appendnAscii(str, baseColor, buffer, len); + } else if (rate < ONE_G) { + int len = snprintf(buffer, sizeof(buffer), "%7.2f M/s ", rate / ONE_M); + RichString_appendnAscii(str, megabytesColor, buffer, len); + } else if (rate < ONE_T) { + int len = snprintf(buffer, sizeof(buffer), "%7.2f G/s ", rate / ONE_G); + RichString_appendnAscii(str, largeNumberColor, buffer, len); + } else if (rate < ONE_P) { + int len = snprintf(buffer, sizeof(buffer), "%7.2f T/s ", rate / ONE_T); + RichString_appendnAscii(str, largeNumberColor, buffer, len); + } else { + int len = snprintf(buffer, sizeof(buffer), "%7.2f P/s ", rate / ONE_P); + RichString_appendnAscii(str, largeNumberColor, buffer, len); + } +} + +void Row_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) { + int columns = width; + RichString_appendnWideColumns(str, attr, content, strlen(content), &columns); + RichString_appendChr(str, attr, ' ', width + 1 - columns); +} + +void Row_printPercentage(float val, char* buffer, size_t n, uint8_t width, int* attr) { + if (isNonnegative(val)) { + if (val < 0.05F) + *attr = CRT_colors[PROCESS_SHADOW]; + else if (val >= 99.9F) + *attr = CRT_colors[PROCESS_MEGABYTES]; + + int precision = 1; + + // Display "val" as "100" for columns like "MEM%". + if (width == 4 && val > 99.9F) { + precision = 0; + val = 100.0F; + } + + xSnprintf(buffer, n, "%*.*f ", width, precision, val); + } else { + *attr = CRT_colors[PROCESS_SHADOW]; + xSnprintf(buffer, n, "%*.*s ", width, width, "N/A"); + } +} + +void Row_toggleTag(Row* this) { + this->tag = !this->tag; +} + +int Row_compare(const void* v1, const void* v2) { + const Row* r1 = (const Row*)v1; + const Row* r2 = (const Row*)v2; + + return SPACESHIP_NUMBER(r1->id, r2->id); +} + +int Row_compareByParent_Base(const void* v1, const void* v2) { + const Row* r1 = (const Row*)v1; + const Row* r2 = (const Row*)v2; + + int result = SPACESHIP_NUMBER( + r1->isRoot ? 0 : Row_getGroupOrParent(r1), + r2->isRoot ? 0 : Row_getGroupOrParent(r2) + ); + + if (result != 0) + return result; + + return Row_compare(v1, v2); +} + +const RowClass Row_class = { + .super = { + .extends = Class(Object), + .compare = Row_compare + }, +}; diff --git a/Row.h b/Row.h new file mode 100644 index 000000000..5cdb3fe75 --- /dev/null +++ b/Row.h @@ -0,0 +1,179 @@ +#ifndef HEADER_Row +#define HEADER_Row +/* +htop - Row.h +(C) 2004-2015 Hisham H. Muhammad +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include +#include + +#include "Object.h" +#include "RichString.h" +#include "RowField.h" + + +extern uint8_t Row_fieldWidths[LAST_RESERVED_FIELD]; +#define ROW_MIN_PID_DIGITS 5 +#define ROW_MAX_PID_DIGITS 19 +#define ROW_MIN_UID_DIGITS 5 +#define ROW_MAX_UID_DIGITS 20 +extern int Row_pidDigits; +extern int Row_uidDigits; + +struct Machine_; +struct Settings_; +struct Table_; + +/* Class representing entities (such as processes) that can be + * represented in a tabular form in the lower half of the htop + * display. */ + +typedef struct Row_ { + /* Super object for emulated OOP */ + Object super; + + /* Pointer to quasi-global data */ + const struct Machine_* host; + + int id; + int group; + int parent; + + /* Has no known parent */ + bool isRoot; + + /* Whether the row was tagged by the user */ + bool tag; + + /* Whether to display this row */ + bool show; + + /* Whether this row was shown last cycle */ + bool wasShown; + + /* Whether to show children of this row in tree-mode */ + bool showChildren; + + /* Whether the row was updated during the last scan */ + bool updated; + + /* + * Internal state for tree-mode. + */ + int32_t indent; + unsigned int tree_depth; + + /* + * Internal time counts for showing new and exited processes. + */ + uint64_t seenStampMs; + uint64_t tombStampMs; +} Row; + +typedef Row* (*Row_New)(const struct Machine_*); +typedef void (*Row_WriteField)(const Row*, RichString*, RowField); +typedef bool (*Row_IsHighlighted)(const Row*); +typedef bool (*Row_IsVisible)(const Row*, const struct Table_*); +typedef bool (*Row_MatchesFilter)(const Row*, const struct Table_*); +typedef const char* (*Row_SortKeyString)(const Row*); +typedef int (*Row_CompareByParent)(const Row*, const Row*); + +int Row_compare(const void* v1, const void* v2); + +typedef struct RowClass_ { + const ObjectClass super; + const Row_IsHighlighted isHighlighted; + const Row_IsVisible isVisible; + const Row_WriteField writeField; + const Row_MatchesFilter matchesFilter; + const Row_SortKeyString sortKeyString; + const Row_CompareByParent compareByParent; +} RowClass; + +#define As_Row(this_) ((const RowClass*)((this_)->super.klass)) + +#define Row_isHighlighted(r_) (As_Row(r_)->isHighlighted ? (As_Row(r_)->isHighlighted(r_)) : false) +#define Row_isVisible(r_, t_) (As_Row(r_)->isVisible ? (As_Row(r_)->isVisible(r_, t_)) : true) +#define Row_matchesFilter(r_, t_) (As_Row(r_)->matchesFilter ? (As_Row(r_)->matchesFilter(r_, t_)) : false) +#define Row_sortKeyString(r_) (As_Row(r_)->sortKeyString ? (As_Row(r_)->sortKeyString(r_)) : "") +#define Row_compareByParent(r1_, r2_) (As_Row(r1_)->compareByParent ? (As_Row(r1_)->compareByParent(r1_, r2_)) : Row_compareByParent_Base(r1_, r2_)) + +#define ONE_K 1024UL +#define ONE_M (ONE_K * ONE_K) +#define ONE_G (ONE_M * ONE_K) +#define ONE_T (1ULL * ONE_G * ONE_K) +#define ONE_P (1ULL * ONE_T * ONE_K) + +#define ONE_DECIMAL_K 1000UL +#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) +#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) +#define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K) +#define ONE_DECIMAL_P (1ULL * ONE_DECIMAL_T * ONE_DECIMAL_K) + +extern const RowClass Row_class; + +void Row_init(Row* this, const struct Machine_* host); + +void Row_display(const Object* cast, RichString* out); + +void Row_toggleTag(Row* this); + +void Row_resetFieldWidths(void); + +void Row_updateFieldWidth(RowField key, size_t width); + +void Row_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width); + +const char* RowField_alignedTitle(const struct Settings_* settings, RowField field); + +RowField RowField_keyAt(const struct Settings_* settings, int at); + +/* Sets the size of the PID column based on the passed PID */ +void Row_setPidColumnWidth(pid_t maxPid); + +/* Sets the size of the UID column based on the passed UID */ +void Row_setUidColumnWidth(uid_t maxUid); + +/* Takes number in bytes (base 1024). Prints 6 columns. */ +void Row_printBytes(RichString* str, unsigned long long number, bool coloring); + +/* Takes number in kilo bytes (base 1024). Prints 6 columns. */ +void Row_printKBytes(RichString* str, unsigned long long number, bool coloring); + +/* Takes number as count (base 1000). Prints 12 columns. */ +void Row_printCount(RichString* str, unsigned long long number, bool coloring); + +/* Takes time in hundredths of a seconds. Prints 9 columns. */ +void Row_printTime(RichString* str, unsigned long long totalHundredths, bool coloring); + +/* Takes rate in bare unit (base 1024) per second. Prints 12 columns. */ +void Row_printRate(RichString* str, double rate, bool coloring); + +void Row_printPercentage(float val, char* buffer, size_t n, uint8_t width, int* attr); + +void Row_display(const Object* cast, RichString* out); + +static inline int Row_idEqualCompare(const void* v1, const void* v2) { + const int p1 = ((const Row*)v1)->id; + const int p2 = ((const Row*)v2)->id; + return p1 != p2; /* return zero when equal */ +} + +/* Routines used primarily with the tree view */ +static inline int Row_getGroupOrParent(const Row* this) { + return this->group == this->id ? this->parent : this->group; +} + +static inline bool Row_isChildOf(const Row* this, int id) { + return id == Row_getGroupOrParent(this); +} + +int Row_compareByParent_Base(const void* v1, const void* v2); + +#endif diff --git a/RowField.h b/RowField.h new file mode 100644 index 000000000..1e01ea3e9 --- /dev/null +++ b/RowField.h @@ -0,0 +1,56 @@ +#ifndef HEADER_RowField +#define HEADER_RowField +/* +htop - RowField.h +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "ProcessField.h" // platform-specific fields reserved for processes + + +typedef enum ReservedFields_ { + NULL_FIELD = 0, + PID = 1, + COMM = 2, + STATE = 3, + PPID = 4, + PGRP = 5, + SESSION = 6, + TTY = 7, + TPGID = 8, + MINFLT = 10, + MAJFLT = 12, + PRIORITY = 18, + NICE = 19, + STARTTIME = 21, + PROCESSOR = 38, + M_VIRT = 39, + M_RESIDENT = 40, + ST_UID = 46, + PERCENT_CPU = 47, + PERCENT_MEM = 48, + USER = 49, + TIME = 50, + NLWP = 51, + TGID = 52, + PERCENT_NORM_CPU = 53, + ELAPSED = 54, + SCHEDULERPOLICY = 55, + PROC_COMM = 124, + PROC_EXE = 125, + CWD = 126, + + /* Platform specific fields, defined in ${platform}/ProcessField.h */ + PLATFORM_PROCESS_FIELDS + + /* Do not add new fields after this entry (dynamic entries follow) */ + LAST_RESERVED_FIELD +} ReservedFields; + +/* Follow ReservedField entries with dynamic fields defined at runtime */ +#define ROW_DYNAMIC_FIELDS LAST_RESERVED_FIELD +typedef int32_t RowField; + +#endif diff --git a/Scheduling.c b/Scheduling.c index 5ca49ae2d..10846c0b1 100644 --- a/Scheduling.c +++ b/Scheduling.c @@ -97,7 +97,7 @@ Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority) { return this; } -bool Scheduling_setPolicy(Process* proc, Arg arg) { +static bool Scheduling_setPolicy(Process* p, Arg arg) { const SchedulingArg* sarg = arg.v; int policy = sarg->policy; @@ -112,13 +112,19 @@ bool Scheduling_setPolicy(Process* proc, Arg arg) { policy &= SCHED_RESET_ON_FORK; #endif - int r = sched_setscheduler(proc->pid, policy, ¶m); + int r = sched_setscheduler(Process_getPid(p), policy, ¶m); /* POSIX says on success the previous scheduling policy should be returned, * but Linux always returns 0. */ return r != -1; } +bool Scheduling_rowSetPolicy(Row* row, Arg arg) { + Process* p = (Process*) row; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + return Scheduling_setPolicy(p, arg); +} + const char* Scheduling_formatPolicy(int policy) { #ifdef SCHED_RESET_ON_FORK policy = policy & ~SCHED_RESET_ON_FORK; @@ -149,6 +155,6 @@ const char* Scheduling_formatPolicy(int policy) { } void Scheduling_readProcessPolicy(Process* proc) { - proc->scheduling_policy = sched_getscheduler(proc->pid); + proc->scheduling_policy = sched_getscheduler(Process_getPid(proc)); } #endif /* SCHEDULER_SUPPORT */ diff --git a/Scheduling.h b/Scheduling.h index d91855ae4..e5952b007 100644 --- a/Scheduling.h +++ b/Scheduling.h @@ -13,6 +13,7 @@ in the source distribution for its full text. #include #include "Panel.h" +#include "Process.h" #if defined(HAVE_SCHED_SETSCHEDULER) && defined(HAVE_SCHED_GETSCHEDULER) @@ -38,7 +39,7 @@ typedef struct { int priority; } SchedulingArg; -bool Scheduling_setPolicy(Process* proc, Arg arg); +bool Scheduling_rowSetPolicy(Row* proc, Arg arg); const char* Scheduling_formatPolicy(int policy); diff --git a/ScreenManager.c b/ScreenManager.c index a089eda10..e39c4f069 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -133,13 +133,14 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi *oldTime = newTime; int oldUidDigits = Process_uidDigits; if (!this->state->pauseUpdate && (*sortTimeout == 0 || host->settings->ss->treeView)) { - host->pl->needsSort = true; + host->activeTable->needsSort = true; *sortTimeout = 1; } // sample current values for system metrics and processes if not paused Machine_scan(host); if (!this->state->pauseUpdate) - ProcessList_scan(host->pl); + Machine_scanTables(host); + // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); // force redraw if the number of UID digits was changed @@ -149,7 +150,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi *redraw = true; } if (*redraw) { - ProcessList_rebuildPanel(host->pl); + Table_rebuildPanel(host->activeTable); if (!this->state->hideMeters) Header_draw(this->header); } diff --git a/Settings.c b/Settings.c index c712966e3..b772f0916 100644 --- a/Settings.c +++ b/Settings.c @@ -206,7 +206,7 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) static const char* toFieldName(Hashtable* columns, int id) { if (id < 0) return NULL; - if (id >= LAST_PROCESSFIELD) { + if (id >= ROW_DYNAMIC_FIELDS) { const DynamicColumn* column = DynamicColumn_lookup(columns, id); return column->name; } @@ -283,7 +283,7 @@ ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* default .sortKey = sortKey, .treeSortKey = PID, .treeView = false, - .treeViewAlwaysByPID = false, + .treeViewAlwaysByID = false, .allBranchesCollapsed = false, }; @@ -372,7 +372,7 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } 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); - screen->treeViewAlwaysByPID = atoi(option[1]); + screen->treeViewAlwaysByID = atoi(option[1]); } else if (String_eq(option[0], "all_branches_collapsed") && this->config_version <= 2) { // old (no screen) naming also supported for backwards compatibility screen = Settings_defaultScreens(this); @@ -501,7 +501,7 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini screen->treeView = atoi(option[1]); } else if (String_eq(option[0], ".tree_view_always_by_pid")) { if (screen) - screen->treeViewAlwaysByPID = atoi(option[1]); + screen->treeViewAlwaysByID = atoi(option[1]); } else if (String_eq(option[0], ".all_branches_collapsed")) { if (screen) screen->allBranchesCollapsed = atoi(option[1]); @@ -634,7 +634,7 @@ int Settings_write(const Settings* this, bool onCrash) { printSettingInteger("tree_sort_key", this->screens[0]->treeSortKey - 1); printSettingInteger("sort_direction", this->screens[0]->direction); printSettingInteger("tree_sort_direction", this->screens[0]->treeDirection); - printSettingInteger("tree_view_always_by_pid", this->screens[0]->treeViewAlwaysByPID); + printSettingInteger("tree_view_always_by_pid", this->screens[0]->treeViewAlwaysByID); printSettingInteger("all_branches_collapsed", this->screens[0]->allBranchesCollapsed); for (unsigned int i = 0; i < this->nScreens; i++) { @@ -644,7 +644,7 @@ int Settings_write(const Settings* this, bool onCrash) { 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(".tree_view_always_by_pid", ss->treeViewAlwaysByID); printSettingInteger(".sort_direction", ss->direction); printSettingInteger(".tree_sort_direction", ss->treeDirection); printSettingInteger(".all_branches_collapsed", ss->allBranchesCollapsed); @@ -787,7 +787,7 @@ void ScreenSettings_invertSortOrder(ScreenSettings* this) { } void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey) { - if (this->treeViewAlwaysByPID || !this->treeView) { + if (this->treeViewAlwaysByID || !this->treeView) { this->sortKey = sortKey; this->direction = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1; this->treeView = false; diff --git a/Settings.h b/Settings.h index 48c62590a..3caaab282 100644 --- a/Settings.h +++ b/Settings.h @@ -14,7 +14,7 @@ in the source distribution for its full text. #include "Hashtable.h" #include "HeaderLayout.h" -#include "Process.h" +#include "Row.h" #define DEFAULT_DELAY 15 @@ -33,16 +33,16 @@ typedef struct { int* modes; } MeterColumnSetting; -typedef struct { +typedef struct ScreenSettings_ { char* name; - ProcessField* fields; + RowField* fields; uint32_t flags; int direction; int treeDirection; - ProcessField sortKey; - ProcessField treeSortKey; + RowField sortKey; + RowField treeSortKey; bool treeView; - bool treeViewAlwaysByPID; + bool treeViewAlwaysByID; bool allBranchesCollapsed; } ScreenSettings; @@ -104,9 +104,9 @@ typedef struct Settings_ { #define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu)) -static inline ProcessField ScreenSettings_getActiveSortKey(const ScreenSettings* this) { +static inline RowField ScreenSettings_getActiveSortKey(const ScreenSettings* this) { return (this->treeView) - ? (this->treeViewAlwaysByPID ? PID : this->treeSortKey) + ? (this->treeViewAlwaysByID ? 1 : this->treeSortKey) : this->sortKey; } @@ -126,7 +126,7 @@ void ScreenSettings_delete(ScreenSettings* this); void ScreenSettings_invertSortOrder(ScreenSettings* this); -void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey); +void ScreenSettings_setSortKey(ScreenSettings* this, RowField sortKey); void Settings_enableReadonly(void); diff --git a/Table.c b/Table.c new file mode 100644 index 000000000..dcb486777 --- /dev/null +++ b/Table.c @@ -0,0 +1,370 @@ +/* +htop - Table.c +(C) 2004,2005 Hisham H. Muhammad +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Table.h" + +#include +#include +#include + +#include "CRT.h" +#include "DynamicColumn.h" +#include "Hashtable.h" +#include "Machine.h" +#include "Macros.h" +#include "Platform.h" +#include "Vector.h" +#include "XUtils.h" + + +Table* Table_init(Table* this, const ObjectClass* klass, Machine* host) { + this->rows = Vector_new(klass, true, DEFAULT_SIZE); + this->displayList = Vector_new(klass, false, DEFAULT_SIZE); + this->table = Hashtable_new(200, false); + this->needsSort = true; + this->following = -1; + this->host = host; + return this; +} + +void Table_done(Table* this) { + Hashtable_delete(this->table); + Vector_delete(this->displayList); + Vector_delete(this->rows); +} + +static void Table_delete(Object* cast) { + Table* this = (Table*) cast; + Table_done(this); + free(this); +} + +void Table_setPanel(Table* this, Panel* panel) { + this->panel = panel; +} + +void Table_add(Table* this, Row* row) { + assert(Vector_indexOf(this->rows, row, Row_idEqualCompare) == -1); + assert(Hashtable_get(this->table, row->id) == NULL); + + // highlighting row found in first scan by first scan marked "far in the past" + row->seenStampMs = this->host->monotonicMs; + + Vector_add(this->rows, row); + Hashtable_put(this->table, row->id, row); + + assert(Vector_indexOf(this->rows, row, Row_idEqualCompare) != -1); + assert(Hashtable_get(this->table, row->id) != NULL); + assert(Vector_countEquals(this->rows, Hashtable_count(this->table))); +} + +// Table_removeIndex removes a given row from the lists map and soft deletes +// it from its vector. Vector_compact *must* be called once the caller is done +// removing items. +// Note: for processes should only be called from ProcessList_iterate to avoid +// breaking dying process highlighting. +void Table_removeIndex(Table* this, const Row* row, int idx) { + int rowid = row->id; + + assert(row == (Row*)Vector_get(this->rows, idx)); + assert(Hashtable_get(this->table, rowid) != NULL); + + Hashtable_remove(this->table, rowid); + Vector_softRemove(this->rows, idx); + + if (this->following != -1 && this->following == rowid) { + this->following = -1; + Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS); + } + + assert(Hashtable_get(this->table, rowid) == NULL); + assert(Vector_countEquals(this->rows, Hashtable_count(this->table))); +} + +static void Table_buildTreeBranch(Table* this, int rowid, unsigned int level, int32_t indent, bool show) { + // Do not treat zero as root of any tree. + // (e.g. on OpenBSD the kernel thread 'swapper' has pid 0.) + if (rowid == 0) + return; + + // The vector is sorted by parent, find the start of the range by bisection + int vsize = Vector_size(this->rows); + int l = 0; + int r = vsize; + while (l < r) { + int c = (l + r) / 2; + Row* row = (Row*)Vector_get(this->rows, c); + int parent = row->isRoot ? 0 : Row_getGroupOrParent(row); + if (parent < rowid) { + l = c + 1; + } else { + r = c; + } + } + // Find the end to know the last line for indent handling purposes + int lastShown = r; + while (r < vsize) { + Row* row = (Row*)Vector_get(this->rows, r); + if (!Row_isChildOf(row, rowid)) + break; + if (row->show) + lastShown = r; + r++; + } + + for (int i = l; i < r; i++) { + Row* row = (Row*)Vector_get(this->rows, i); + + if (!show) + row->show = false; + + Vector_add(this->displayList, row); + + int32_t nextIndent = indent | ((int32_t)1 << MINIMUM(level, sizeof(row->indent) * 8 - 2)); + Table_buildTreeBranch(this, row->id, level + 1, (i < lastShown) ? nextIndent : indent, row->show && row->showChildren); + if (i == lastShown) + row->indent = -nextIndent; + else + row->indent = nextIndent; + + row->tree_depth = level + 1; + } +} + +static int compareRowByKnownParentThenNatural(const void* v1, const void* v2) { + return Row_compareByParent((const Row*) v1, (const Row*) v2); +} + +// Builds a sorted tree from scratch, without relying on previously gathered information +static void Table_buildTree(Table* this) { + Vector_prune(this->displayList); + + // Mark root processes + int vsize = Vector_size(this->rows); + for (int i = 0; i < vsize; i++) { + Row* row = (Row*) Vector_get(this->rows, i); + int parent = Row_getGroupOrParent(row); + row->isRoot = false; + + if (row->id == parent) { + row->isRoot = true; + continue; + } + + if (!parent) { + row->isRoot = true; + continue; + } + + // We don't know about its parent for whatever reason + if (Table_findRow(this, parent) == NULL) + row->isRoot = true; + } + + // Sort by known parent (roots first), then row ID + Vector_quickSortCustomCompare(this->rows, compareRowByKnownParentThenNatural); + + // Find all processes whose parent is not visible + for (int i = 0; i < vsize; i++) { + Row* row = (Row*)Vector_get(this->rows, i); + + // If parent not found, then construct the tree with this node as root + if (row->isRoot) { + row = (Row*)Vector_get(this->rows, i); + row->indent = 0; + row->tree_depth = 0; + Vector_add(this->displayList, row); + Table_buildTreeBranch(this, row->id, 0, 0, row->showChildren); + continue; + } + } + + this->needsSort = false; + + // Check consistency of the built structures + assert(Vector_size(this->displayList) == vsize); (void)vsize; +} + +void Table_updateDisplayList(Table* this) { + const Settings* settings = this->host->settings; + if (settings->ss->treeView) { + if (this->needsSort) + Table_buildTree(this); + } else { + if (this->needsSort) + Vector_insertionSort(this->rows); + Vector_prune(this->displayList); + int size = Vector_size(this->rows); + for (int i = 0; i < size; i++) + Vector_add(this->displayList, Vector_get(this->rows, i)); + } + this->needsSort = false; +} + +void Table_expandTree(Table* this) { + int size = Vector_size(this->rows); + for (int i = 0; i < size; i++) { + Row* row = (Row*) Vector_get(this->rows, i); + row->showChildren = true; + } +} + +// Called on collapse-all toggle and on startup, possibly in non-tree mode +void Table_collapseAllBranches(Table* this) { + Table_buildTree(this); // Update `tree_depth` fields of the rows + this->needsSort = true; // Table is sorted by parent now, force new sort + int size = Vector_size(this->rows); + for (int i = 0; i < size; i++) { + Row* row = (Row*) Vector_get(this->rows, i); + // FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1 + if (row->tree_depth > 0 && row->id > 1) + row->showChildren = false; + } +} + +void Table_rebuildPanel(Table* this) { + Table_updateDisplayList(this); + + const int currPos = Panel_getSelectedIndex(this->panel); + const int currScrollV = this->panel->scrollV; + const int currSize = Panel_size(this->panel); + + Panel_prune(this->panel); + + /* Follow main group row instead if following a row that is occluded (hidden) */ + if (this->following != -1) { + const Row* followed = (const Row*) Hashtable_get(this->table, this->following); + if (followed != NULL + && Hashtable_get(this->table, followed->group) + && Row_isVisible(followed, this) == false ) { + this->following = followed->group; + } + } + + const int rowCount = Vector_size(this->displayList); + bool foundFollowed = false; + int idx = 0; + + for (int i = 0; i < rowCount; i++) { + Row* row = (Row*) Vector_get(this->displayList, i); + + if ( !row->show || (Row_matchesFilter(row, this) == true) ) + continue; + + Panel_set(this->panel, idx, (Object*)row); + + if (this->following != -1 && row->id == this->following) { + foundFollowed = true; + Panel_setSelected(this->panel, idx); + /* Keep scroll position relative to followed row */ + this->panel->scrollV = idx - (currPos-currScrollV); + } + idx++; + } + + if (this->following != -1 && !foundFollowed) { + /* Reset if current followed row not found */ + this->following = -1; + Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS); + } + + if (this->following == -1) { + /* If the last item was selected, keep the new last item selected */ + if (currPos > 0 && currPos == currSize - 1) + Panel_setSelected(this->panel, Panel_size(this->panel) - 1); + else + Panel_setSelected(this->panel, currPos); + + this->panel->scrollV = currScrollV; + } +} + +void Table_printHeader(const Settings* settings, RichString* header) { + RichString_rewind(header, RichString_size(header)); + + const ScreenSettings* ss = settings->ss; + const RowField* fields = ss->fields; + + RowField key = ScreenSettings_getActiveSortKey(ss); + + for (int i = 0; fields[i]; i++) { + int color; + if (ss->treeView && ss->treeViewAlwaysByID) { + color = CRT_colors[PANEL_HEADER_FOCUS]; + } else if (key == fields[i]) { + color = CRT_colors[PANEL_SELECTION_FOCUS]; + } else { + color = CRT_colors[PANEL_HEADER_FOCUS]; + } + + RichString_appendWide(header, color, RowField_alignedTitle(settings, fields[i])); + if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') { + bool ascending = ScreenSettings_getActiveDirection(ss) == 1; + RichString_rewind(header, 1); // rewind to override space + RichString_appendnWide(header, + CRT_colors[PANEL_SELECTION_FOCUS], + CRT_treeStr[ascending ? TREE_STR_ASC : TREE_STR_DESC], + 1); + } + if (COMM == fields[i] && settings->showMergedCommand) { + RichString_appendAscii(header, color, "(merged)"); + } + } +} + +// set flags on an existing rows before refreshing table +void Table_prepareEntries(Table* this) { + for (int i = 0; i < Vector_size(this->rows); i++) { + Row* row = (struct Row_*) Vector_get(this->rows, i); + row->updated = false; + row->wasShown = row->show; + row->show = true; + } +} + +// tidy up Row state after refreshing the table +void Table_cleanupRow(Table* table, Row* row, int idx) { + Machine* host = table->host; + const Settings* settings = host->settings; + + if (row->tombStampMs > 0) { + // remove tombed process + if (host->monotonicMs >= row->tombStampMs) { + Table_removeIndex(table, row, idx); + } + } else if (row->updated == false) { + // process no longer exists + if (settings->highlightChanges && row->wasShown) { + // mark tombed + row->tombStampMs = host->monotonicMs + 1000 * settings->highlightDelaySecs; + } else { + // immediately remove + Table_removeIndex(table, row, idx); + } + } +} + +void Table_cleanupEntries(Table* this) { + // Finish process table update, culling any removed rows + for (int i = Vector_size(this->rows) - 1; i >= 0; i--) { + Row* row = (Row*) Vector_get(this->rows, i); + Table_cleanupRow(this, row, i); + } + + // compact the table in case of any earlier row removals + Table_compact(this); +} + +const TableClass Table_class = { + .super = { + .extends = Class(Object), + .delete = Table_delete, + }, + .prepare = Table_prepareEntries, + .cleanup = Table_cleanupEntries, +}; diff --git a/Table.h b/Table.h new file mode 100644 index 000000000..d16e47daa --- /dev/null +++ b/Table.h @@ -0,0 +1,101 @@ +#ifndef HEADER_Table +#define HEADER_Table +/* +htop - Table.h +(C) 2004,2005 Hisham H. Muhammad +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include +#include +#include +#include +#include + +#include "Hashtable.h" +#include "Object.h" +#include "RichString.h" +#include "Settings.h" +#include "Vector.h" + + +struct Machine_; +struct Panel_; +struct Row_; + +typedef struct Table_ { + /* Super object for emulated OOP */ + Object super; + + Vector* rows; /* all known; sort order can vary and differ from display order */ + Vector* displayList; /* row tree flattened in display order (borrowed); + updated in Table_updateDisplayList when rebuilding panel */ + Hashtable* table; /* fast known row lookup by identifier */ + + struct Machine_* host; + const char* incFilter; + bool needsSort; + int following; /* -1 or row being visually tracked in the user interface */ + + struct Panel_* panel; +} Table; + +typedef Table* (*Table_New)(const struct Machine_*); +typedef void (*Table_ScanPrepare)(Table* this); +typedef void (*Table_ScanIterate)(Table* this); +typedef void (*Table_ScanCleanup)(Table* this); + +typedef struct TableClass_ { + const ObjectClass super; + const Table_ScanPrepare prepare; + const Table_ScanIterate iterate; + const Table_ScanCleanup cleanup; +} TableClass; + +#define As_Table(this_) ((const TableClass*)((this_)->super.klass)) + +#define Table_scanPrepare(t_) (As_Table(t_)->prepare ? (As_Table(t_)->prepare(t_)) : Table_prepareEntries(t_)) +#define Table_scanIterate(t_) (As_Table(t_)->iterate(t_)) /* mandatory; must have a custom iterate method */ +#define Table_scanCleanup(t_) (As_Table(t_)->cleanup ? (As_Table(t_)->cleanup(t_)) : Table_cleanupEntries(t_)) + +Table* Table_init(Table* this, const ObjectClass* klass, struct Machine_* host); + +void Table_done(Table* this); + +extern const TableClass Table_class; + +void Table_setPanel(Table* this, struct Panel_* panel); + +void Table_printHeader(const Settings* settings, RichString* header); + +void Table_add(Table* this, struct Row_* row); + +void Table_removeIndex(Table* this, const struct Row_* row, int idx); + +void Table_updateDisplayList(Table* this); + +void Table_expandTree(Table* this); + +void Table_collapseAllBranches(Table* this); + +void Table_rebuildPanel(Table* this); + +static inline struct Row_* Table_findRow(Table* this, int id) { + return (struct Row_*) Hashtable_get(this->table, id); +} + +void Table_prepareEntries(Table* this); + +void Table_cleanupEntries(Table* this); + +void Table_cleanupRow(Table* this, Row* row, int idx); + +static inline void Table_compact(Table* this) { + Vector_compact(this->rows); +} + +#endif diff --git a/TasksMeter.c b/TasksMeter.c index b5563fcab..7dd6fdb41 100644 --- a/TasksMeter.c +++ b/TasksMeter.c @@ -25,7 +25,8 @@ static const int TasksMeter_attributes[] = { static void TasksMeter_updateValues(Meter* this) { const Machine* host = this->host; - const ProcessList* pl = host->pl; + const ProcessList* pl = (const ProcessList*) host->processTable; + this->values[0] = pl->kernelThreads; this->values[1] = pl->userlandThreads; this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads; diff --git a/TraceScreen.c b/TraceScreen.c index e8f55ff56..03315482b 100644 --- a/TraceScreen.c +++ b/TraceScreen.c @@ -62,7 +62,7 @@ void TraceScreen_delete(Object* cast) { } static void TraceScreen_draw(InfoScreen* this) { - InfoScreen_drawTitled(this, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process)); + InfoScreen_drawTitled(this, "Trace of process %d - %s", Process_getPid(this->process), Process_getCommand(this->process)); } bool TraceScreen_forkTracer(TraceScreen* this) { @@ -89,7 +89,7 @@ bool TraceScreen_forkTracer(TraceScreen* this) { close(fdpair[1]); char buffer[32] = {0}; - xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); + xSnprintf(buffer, sizeof(buffer), "%d", Process_getPid(this->super.process)); // Use of NULL in variadic functions must have a pointer cast. // The NULL constant is not required by standard to have a pointer type. execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, (char*)NULL); diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index e6366d70f..ec98341e3 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -72,8 +72,8 @@ void Process_delete(Object* cast) { free(this); } -static void DarwinProcess_writeField(const Process* this, RichString* str, ProcessField field) { - const DarwinProcess* dp = (const DarwinProcess*) this; +static void DarwinProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const DarwinProcess* dp = (const DarwinProcess*) super; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; @@ -81,7 +81,7 @@ static void DarwinProcess_writeField(const Process* this, RichString* str, Proce // add Platform-specific fields here case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break; default: - Process_writeField(this, str, field); + Process_writeField(&dp->super, str, field); return; } RichString_appendWide(str, attr, buffer); @@ -292,7 +292,7 @@ static char* DarwinProcess_getDevname(dev_t dev) { void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) { DarwinProcess* dp = (DarwinProcess*)proc; - const Settings* settings = proc->host->settings; + const Settings* settings = proc->super.host->settings; const struct extern_proc* ep = &ps->kp_proc; @@ -312,12 +312,12 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, /* First, the "immutable" parts */ if (!exists) { /* Set the PID/PGID/etc. */ - proc->pid = ep->p_pid; - proc->ppid = ps->kp_eproc.e_ppid; + Process_setPid(proc, ep->p_pid); + Process_setThreadGroup(proc, ep->p_pid); + Process_setParent(proc, ps->kp_eproc.e_ppid); proc->pgrp = ps->kp_eproc.e_pgid; proc->session = 0; /* TODO Get the session id */ proc->tpgid = ps->kp_eproc.e_tpgid; - proc->tgid = proc->pid; proc->isKernelThread = false; proc->isUserlandThread = false; dp->translated = ps->kp_proc.p_flag & P_TRANSLATED; @@ -359,14 +359,14 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, proc->state = (ep->p_stat == SZOMB) ? ZOMBIE : UNKNOWN; /* Make sure the updated flag is set */ - proc->updated = true; + proc->super.updated = true; } void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double timeIntervalNS) { struct proc_taskinfo pti; - if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { - const DarwinMachine* dhost = (const DarwinMachine*) proc->super.host; + if (sizeof(pti) == proc_pidinfo(Process_getPid(&proc->super), PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { + const DarwinMachine* dhost = (const DarwinMachine*) proc->super.super.host; uint64_t total_existing_time_ns = proc->stime + proc->utime; @@ -419,7 +419,7 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) { } task_t port; - ret = task_for_pid(mach_task_self(), proc->pid, &port); + ret = task_for_pid(mach_task_self(), Process_getPid(proc), &port); if (ret != KERN_SUCCESS) { dp->taskAccess = false; return; @@ -472,11 +472,18 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) { const ProcessClass DarwinProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = DarwinProcess_rowWriteField }, - .writeField = DarwinProcess_writeField, - .compareByKey = DarwinProcess_compareByKey, + .compareByKey = DarwinProcess_compareByKey }; diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index c10e772c9..1545600eb 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -55,20 +55,22 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); - ProcessList* super = &this->super; + Object_setClass(this, Class(ProcessList)); + ProcessList* super = &this->super; ProcessList_init(super, Class(DarwinProcess), host, pidMatchList); return super; } -void ProcessList_delete(ProcessList* this) { - ProcessList_done(this); +void ProcessList_delete(Object* cast) { + DarwinProcessList* this = (DarwinProcessList*) cast; + ProcessList_done(&this->super); free(this); } void ProcessList_goThroughEntries(ProcessList* super) { - const Machine* host = super->host; + const Machine* host = super->super.host; const DarwinMachine* dhost = (const DarwinMachine*) host; DarwinProcessList* dpl = (DarwinProcessList*) super; bool preExisting = true; diff --git a/dragonflybsd/DragonFlyBSDProcess.c b/dragonflybsd/DragonFlyBSDProcess.c index 7cfc71be5..a85b6efb0 100644 --- a/dragonflybsd/DragonFlyBSDProcess.c +++ b/dragonflybsd/DragonFlyBSDProcess.c @@ -66,16 +66,17 @@ void Process_delete(Object* cast) { free(this); } -static void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { +static void DragonFlyBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const Process* this = (const Process*) super; const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; switch (field) { // add Platform-specific fields here - case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_isKernelThread(this) ? -1 : this->pid); break; + case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_isKernelThread(this) ? -1 : Process_getPid(this)); break; case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break; - case JAIL: Process_printLeftAlignedField(str, attr, fp->jname, 11); return; + case JAIL: Row_printLeftAlignedField(str, attr, fp->jname, 11); return; default: Process_writeField(this, str, field); return; @@ -100,11 +101,18 @@ static int DragonFlyBSDProcess_compareByKey(const Process* v1, const Process* v2 const ProcessClass DragonFlyBSDProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = DragonFlyBSDProcess_rowWriteField }, - .writeField = DragonFlyBSDProcess_writeField, .compareByKey = DragonFlyBSDProcess_compareByKey }; diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index 4ff17932c..6330e911d 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -28,16 +28,17 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { DragonFlyBSDProcessList* this = xCalloc(1, sizeof(DragonFlyBSDProcessList)); - ProcessList* super = (ProcessList*) this; + Object_setClass(this, Class(ProcessList)); + ProcessList* super = (ProcessList*) this; ProcessList_init(super, Class(DragonFlyBSDProcess), host, pidMatchList); return super; } -void ProcessList_delete(ProcessList* super) { - const DragonFlyBSDProcessList* this = (DragonFlyBSDProcessList*) super; - ProcessList_done(super); +void ProcessList_delete(Object* cast) { + const DragonFlyBSDProcessList* this = (DragonFlyBSDProcessList*) cast; + ProcessList_done(&this->super); free(this); } @@ -153,17 +154,17 @@ void ProcessList_goThroughEntries(ProcessList* super) { dfp->jid = kproc->kp_jailid; if (kproc->kp_ktaddr && kproc->kp_flags & P_SYSTEM) { // dfb kernel threads all have the same pid, so we misuse the kernel thread address to give them a unique identifier - proc->pid = (pid_t)kproc->kp_ktaddr; + Process_setPid(proc, (pid_t)kproc->kp_ktaddr); proc->isKernelThread = true; } else { - proc->pid = kproc->kp_pid; // process ID + Process_setPid(proc, kproc->kp_pid); // process ID proc->isKernelThread = false; } proc->isUserlandThread = kproc->kp_nthreads > 1; - proc->ppid = kproc->kp_ppid; // parent process id + Process_setParent(proc, kproc->kp_ppid); // parent process id proc->tpgid = kproc->kp_tpgid; // tty process group id - //proc->tgid = kproc->kp_lwp.kl_tid; // thread group id - proc->tgid = kproc->kp_pid; // thread group id + //Process_setThreadGroup(proc, kproc->kp_lwp.kl_tid); // thread group id + Process_setThreadGroup(proc, kproc->kp_pid); proc->pgrp = kproc->kp_pgid; // process group id proc->session = kproc->kp_sid; proc->st_uid = kproc->kp_uid; // user ID @@ -199,7 +200,7 @@ void ProcessList_goThroughEntries(ProcessList* super) { dfp->jname = DragonFlyBSDMachine_readJailName(dhost, kproc->kp_jailid); } // if there are reapers in the system, process can get reparented anytime - proc->ppid = kproc->kp_ppid; + Process_setParent(proc, kproc->kp_ppid); if (proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs) proc->st_uid = kproc->kp_uid; proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); @@ -303,7 +304,7 @@ void ProcessList_goThroughEntries(ProcessList* super) { if (proc->state == RUNNING) super->runningTasks++; - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); - proc->updated = true; + proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); + proc->super.updated = true; } } diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c index 4970ff2cc..f6d3451da 100644 --- a/freebsd/FreeBSDProcess.c +++ b/freebsd/FreeBSDProcess.c @@ -71,8 +71,8 @@ void Process_delete(Object* cast) { free(this); } -static void FreeBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { - const FreeBSDProcess* fp = (const FreeBSDProcess*) this; +static void FreeBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const FreeBSDProcess* fp = (const FreeBSDProcess*) super; char buffer[256]; size_t n = sizeof(buffer); int attr = CRT_colors[DEFAULT_COLOR]; @@ -81,13 +81,13 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc // add FreeBSD-specific fields here case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break; case JAIL: - Process_printLeftAlignedField(str, attr, fp->jname ? fp->jname : "N/A", 11); + Row_printLeftAlignedField(str, attr, fp->jname ? fp->jname : "N/A", 11); return; case EMULATION: - Process_printLeftAlignedField(str, attr, fp->emul ? fp->emul : "N/A", 16); + Row_printLeftAlignedField(str, attr, fp->emul ? fp->emul : "N/A", 16); return; default: - Process_writeField(this, str, field); + Process_writeField(&fp->super, str, field); return; } RichString_appendWide(str, attr, buffer); @@ -112,11 +112,18 @@ static int FreeBSDProcess_compareByKey(const Process* v1, const Process* v2, Pro const ProcessClass FreeBSDProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = FreeBSDProcess_rowWriteField }, - .writeField = FreeBSDProcess_writeField, .compareByKey = FreeBSDProcess_compareByKey }; diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index d8d4bbe04..aabb61ccd 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -43,18 +43,17 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { FreeBSDProcessList* this = xCalloc(1, sizeof(FreeBSDProcessList)); - ProcessList* super = &this->super; + Object_setClass(this, Class(ProcessList)); + ProcessList* super = &this->super; ProcessList_init(super, Class(FreeBSDProcess), host, pidMatchList); return super; } -void ProcessList_delete(ProcessList* super) { - FreeBSDProcessList* this = (FreeBSDProcessList*) super; - - ProcessList_done(super); - +void ProcessList_delete(Object* cast) { + FreeBSDProcessList* this = (FreeBSDProcessList*) cast; + ProcessList_done(&this->super); free(this); } @@ -174,12 +173,12 @@ void ProcessList_goThroughEntries(ProcessList* super) { if (!preExisting) { fp->jid = kproc->ki_jid; - proc->pid = kproc->ki_pid; + Process_setPid(proc, kproc->ki_pid); + Process_setThreadGroup(proc, kproc->ki_pid); + Process_setParent(proc, kproc->ki_ppid); proc->isKernelThread = kproc->ki_pid != 1 && (kproc->ki_flag & P_SYSTEM); proc->isUserlandThread = false; - proc->ppid = kproc->ki_ppid; proc->tpgid = kproc->ki_tpgid; - proc->tgid = kproc->ki_pid; proc->session = kproc->ki_sid; proc->pgrp = kproc->ki_pgid; proc->st_uid = kproc->ki_uid; @@ -279,11 +278,11 @@ void ProcessList_goThroughEntries(ProcessList* super) { Scheduling_readProcessPolicy(proc); #endif - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); + proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); super->totalTasks++; if (proc->state == RUNNING) super->runningTasks++; - proc->updated = true; + proc->super.updated = true; } } diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c index 5d2e67e92..ff2087c75 100644 --- a/linux/LinuxMachine.c +++ b/linux/LinuxMachine.c @@ -505,7 +505,8 @@ static void LinuxMachine_scanCPUTime(LinuxMachine* this) { char buffer[PROC_LINE_LENGTH + 1]; while (fgets(buffer, sizeof(buffer), file)) { if (String_startsWith(buffer, "procs_running")) { - this->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); + ProcessList* pl = (ProcessList*) super->processTable; + pl->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); break; } } diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 875618187..e348f9ab1 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -143,22 +143,29 @@ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) { #define SYS_ioprio_set __NR_ioprio_set #endif -IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) { +IOPriority LinuxProcess_updateIOPriority(Process* p) { IOPriority ioprio = 0; // Other OSes masquerading as Linux (NetBSD?) don't have this syscall #ifdef SYS_ioprio_get - ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, this->super.pid); + ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, Process_getPid(p)); #endif + LinuxProcess* this = (LinuxProcess*) p; this->ioPriority = ioprio; return ioprio; } -bool LinuxProcess_setIOPriority(Process* this, Arg ioprio) { +static bool LinuxProcess_setIOPriority(Process* p, Arg ioprio) { // Other OSes masquerading as Linux (NetBSD?) don't have this syscall #ifdef SYS_ioprio_set - syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->pid, ioprio.i); + syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, Process_getPid(p), ioprio.i); #endif - return (LinuxProcess_updateIOPriority((LinuxProcess*)this) == ioprio.i); + return LinuxProcess_updateIOPriority(p) == ioprio.i; +} + +bool LinuxProcess_rowSetIOPriority(Row* super, Arg ioprio) { + Process* p = (Process*) super; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + return LinuxProcess_setIOPriority(p, ioprio); } bool LinuxProcess_isAutogroupEnabled(void) { @@ -168,9 +175,10 @@ bool LinuxProcess_isAutogroupEnabled(void) { return buf[0] == '1'; } -bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) { +static bool LinuxProcess_changeAutogroupPriorityBy(Process* p, Arg delta) { char buffer[256]; - xSnprintf(buffer, sizeof(buffer), PROCDIR "/%d/autogroup", this->pid); + pid_t pid = Process_getPid(p); + xSnprintf(buffer, sizeof(buffer), PROCDIR "/%d/autogroup", pid); FILE* file = fopen(buffer, "r+"); if (!file) @@ -192,6 +200,12 @@ bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) { return success; } +bool LinuxProcess_rowChangeAutogroupPriorityBy(Row* super, Arg delta) { + Process* p = (Process*) super; + assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); + return LinuxProcess_changeAutogroupPriorityBy(p, delta); +} + static double LinuxProcess_totalIORate(const LinuxProcess* lp) { double totalRate = NAN; if (isNonnegative(lp->io_rate_read_bps)) { @@ -205,45 +219,47 @@ static double LinuxProcess_totalIORate(const LinuxProcess* lp) { return totalRate; } -static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) { +static void LinuxProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const Process* this = (const Process*) super; + const Machine* host = (const Machine*) super->host; + const LinuxMachine* lhost = (const LinuxMachine*) host; const LinuxProcess* lp = (const LinuxProcess*) this; - const LinuxMachine* lhost = (const LinuxMachine*) this->host; - bool coloring = this->host->settings->highlightMegabytes; + bool coloring = host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; switch (field) { - case CMINFLT: Process_printCount(str, lp->cminflt, coloring); return; - case CMAJFLT: Process_printCount(str, lp->cmajflt, coloring); return; - case M_DRS: Process_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return; + case CMINFLT: Row_printCount(str, lp->cminflt, coloring); return; + case CMAJFLT: Row_printCount(str, lp->cmajflt, coloring); return; + case M_DRS: Row_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return; case M_LRS: if (lp->m_lrs) { - Process_printBytes(str, lp->m_lrs * lhost->pageSize, coloring); + Row_printBytes(str, lp->m_lrs * lhost->pageSize, coloring); return; } attr = CRT_colors[PROCESS_SHADOW]; xSnprintf(buffer, n, " N/A "); break; - case M_TRS: Process_printBytes(str, lp->m_trs * lhost->pageSize, coloring); return; - case M_SHARE: Process_printBytes(str, lp->m_share * lhost->pageSize, coloring); return; - case M_PSS: Process_printKBytes(str, lp->m_pss, coloring); return; - case M_SWAP: Process_printKBytes(str, lp->m_swap, coloring); return; - case M_PSSWP: Process_printKBytes(str, lp->m_psswp, coloring); return; - case UTIME: Process_printTime(str, lp->utime, coloring); return; - case STIME: Process_printTime(str, lp->stime, coloring); return; - case CUTIME: Process_printTime(str, lp->cutime, coloring); return; - case CSTIME: Process_printTime(str, lp->cstime, coloring); return; - case RCHAR: Process_printBytes(str, lp->io_rchar, coloring); return; - case WCHAR: Process_printBytes(str, lp->io_wchar, coloring); return; - case SYSCR: Process_printCount(str, lp->io_syscr, coloring); return; - case SYSCW: Process_printCount(str, lp->io_syscw, coloring); return; - case RBYTES: Process_printBytes(str, lp->io_read_bytes, coloring); return; - case WBYTES: Process_printBytes(str, lp->io_write_bytes, coloring); return; - case CNCLWB: Process_printBytes(str, lp->io_cancelled_write_bytes, coloring); return; - case IO_READ_RATE: Process_printRate(str, lp->io_rate_read_bps, coloring); return; - case IO_WRITE_RATE: Process_printRate(str, lp->io_rate_write_bps, coloring); return; - case IO_RATE: Process_printRate(str, LinuxProcess_totalIORate(lp), coloring); return; + case M_TRS: Row_printBytes(str, lp->m_trs * lhost->pageSize, coloring); return; + case M_SHARE: Row_printBytes(str, lp->m_share * lhost->pageSize, coloring); return; + case M_PSS: Row_printKBytes(str, lp->m_pss, coloring); return; + case M_SWAP: Row_printKBytes(str, lp->m_swap, coloring); return; + case M_PSSWP: Row_printKBytes(str, lp->m_psswp, coloring); return; + case UTIME: Row_printTime(str, lp->utime, coloring); return; + case STIME: Row_printTime(str, lp->stime, coloring); return; + case CUTIME: Row_printTime(str, lp->cutime, coloring); return; + case CSTIME: Row_printTime(str, lp->cstime, coloring); return; + case RCHAR: Row_printBytes(str, lp->io_rchar, coloring); return; + case WCHAR: Row_printBytes(str, lp->io_wchar, coloring); return; + case SYSCR: Row_printCount(str, lp->io_syscr, coloring); return; + case SYSCW: Row_printCount(str, lp->io_syscw, coloring); return; + case RBYTES: Row_printBytes(str, lp->io_read_bytes, coloring); return; + case WBYTES: Row_printBytes(str, lp->io_write_bytes, coloring); return; + case CNCLWB: Row_printBytes(str, lp->io_cancelled_write_bytes, coloring); return; + case IO_READ_RATE: Row_printRate(str, lp->io_rate_read_bps, coloring); return; + case IO_WRITE_RATE: Row_printRate(str, lp->io_rate_write_bps, coloring); return; + case IO_RATE: Row_printRate(str, LinuxProcess_totalIORate(lp), coloring); return; #ifdef HAVE_OPENVZ case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break; case VPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, lp->vpid); break; @@ -251,8 +267,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces #ifdef HAVE_VSERVER case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; #endif - case CGROUP: xSnprintf(buffer, n, "%-*.*s ", Process_fieldWidths[CGROUP], Process_fieldWidths[CGROUP], lp->cgroup ? lp->cgroup : "N/A"); break; - case CCGROUP: xSnprintf(buffer, n, "%-*.*s ", Process_fieldWidths[CCGROUP], Process_fieldWidths[CCGROUP], lp->cgroup_short ? lp->cgroup_short : (lp->cgroup ? lp->cgroup : "N/A")); break; + case CGROUP: xSnprintf(buffer, n, "%-*.*s ", Row_fieldWidths[CGROUP], Row_fieldWidths[CGROUP], lp->cgroup ? lp->cgroup : "N/A"); break; + case CCGROUP: xSnprintf(buffer, n, "%-*.*s ", Row_fieldWidths[CCGROUP], Row_fieldWidths[CCGROUP], lp->cgroup_short ? lp->cgroup_short : (lp->cgroup ? lp->cgroup : "N/A")); break; case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break; case IO_PRIORITY: { int klass = IOPriority_class(lp->ioPriority); @@ -273,9 +289,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces break; } #ifdef HAVE_DELAYACCT - case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 5, &attr); break; - case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 5, &attr); break; - case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break; + case PERCENT_CPU_DELAY: Row_printPercentage(lp->cpu_delay_percent, buffer, n, 5, &attr); break; + case PERCENT_IO_DELAY: Row_printPercentage(lp->blkio_delay_percent, buffer, n, 5, &attr); break; + case PERCENT_SWAP_DELAY: Row_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break; #endif case CTXT: if (lp->ctxt_diff > 1000) { @@ -283,7 +299,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces } xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff); break; - case SECATTR: snprintf(buffer, n, "%-*.*s ", Process_fieldWidths[SECATTR], Process_fieldWidths[SECATTR], lp->secattr ? lp->secattr : "N/A"); break; + case SECATTR: snprintf(buffer, n, "%-*.*s ", Row_fieldWidths[SECATTR], Row_fieldWidths[SECATTR], lp->secattr ? lp->secattr : "N/A"); break; case AUTOGROUP_ID: if (lp->autogroup_id != -1) { xSnprintf(buffer, n, "%4ld ", lp->autogroup_id); @@ -398,11 +414,18 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce const ProcessClass LinuxProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = LinuxProcess_rowWriteField }, - .writeField = LinuxProcess_writeField, .compareByKey = LinuxProcess_compareByKey }; diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 6c309893c..1cd0eaaff 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -122,13 +122,13 @@ Process* LinuxProcess_new(const Machine* host); void Process_delete(Object* cast); -IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); +IOPriority LinuxProcess_updateIOPriority(Process* proc); -bool LinuxProcess_setIOPriority(Process* this, Arg ioprio); +bool LinuxProcess_rowSetIOPriority(Row* super, Arg ioprio); bool LinuxProcess_isAutogroupEnabled(void); -bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta); +bool LinuxProcess_rowChangeAutogroupPriorityBy(Row* super, Arg delta); bool Process_isThread(const Process* this); diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index a17b5e137..9be2433e9 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -202,9 +202,11 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); - ProcessList* super = &this->super; + Object_setClass(this, Class(ProcessList)); + ProcessList* super = &this->super; ProcessList_init(super, Class(LinuxProcess), host, pidMatchList); + LinuxProcessList_initTtyDrivers(this); // Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+) @@ -213,9 +215,9 @@ ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { return super; } -void ProcessList_delete(ProcessList* pl) { - LinuxProcessList* this = (LinuxProcessList*) pl; - ProcessList_done(pl); +void ProcessList_delete(Object* cast) { + LinuxProcessList* this = (LinuxProcessList*) cast; + ProcessList_done(&this->super); if (this->ttyDrivers) { for (int i = 0; this->ttyDrivers[i].path; i++) { free(this->ttyDrivers[i].path); @@ -257,14 +259,14 @@ static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, char buf[MAX_READ + 1]; char path[22] = "stat"; if (scanMainThread) { - xSnprintf(path, sizeof(path), "task/%"PRIi32"/stat", (int32_t)process->pid); + xSnprintf(path, sizeof(path), "task/%"PRIi32"/stat", (int32_t)Process_getPid(process)); } ssize_t r = xReadfileat(procFd, path, buf, sizeof(buf)); if (r < 0) return false; /* (1) pid - %d */ - assert(process->pid == atoi(buf)); + assert(Process_getPid(process) == atoi(buf)); char* location = strchr(buf, ' '); if (!location) return false; @@ -284,7 +286,7 @@ static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, location += 2; /* (4) ppid - %d */ - process->ppid = strtol(location, &location, 10); + Process_setParent(process, strtol(location, &location, 10)); location += 1; /* (5) pgrp - %d */ @@ -482,11 +484,11 @@ static bool LinuxProcessList_updateUser(const Machine* host, Process* process, o static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread) { Process* process = &lp->super; - const Machine* host = process->host; + const Machine* host = process->super.host; char path[20] = "io"; char buffer[1024]; if (scanMainThread) { - xSnprintf(path, sizeof(path), "task/%"PRIi32"/io", (int32_t)process->pid); + xSnprintf(path, sizeof(path), "task/%"PRIi32"/io", (int32_t)Process_getPid(process)); } ssize_t r = xReadfileat(procFd, path, buffer, sizeof(buffer)); if (r < 0) { @@ -743,7 +745,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t if (access(PROCDIR "/vz", R_OK) != 0) { free(process->ctid); process->ctid = NULL; - process->vpid = process->super.pid; + process->vpid = Process_getPid(&process->super); return; } @@ -751,7 +753,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t if (!file) { free(process->ctid); process->ctid = NULL; - process->vpid = process->super.pid; + process->vpid = Process_getPid(&process->super); return; } @@ -823,7 +825,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t } if (!foundVPid) { - process->vpid = process->super.pid; + process->vpid = Process_getPid(&process->super); } } @@ -875,27 +877,27 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t bool changed = !process->cgroup || !String_eq(process->cgroup, output); - Process_updateFieldWidth(CGROUP, strlen(output)); + Row_updateFieldWidth(CGROUP, strlen(output)); free_and_xStrdup(&process->cgroup, output); if (!changed) { if (process->cgroup_short) { - Process_updateFieldWidth(CCGROUP, strlen(process->cgroup_short)); + Row_updateFieldWidth(CCGROUP, strlen(process->cgroup_short)); } else { //CCGROUP is alias to normal CGROUP if shortening fails - Process_updateFieldWidth(CCGROUP, strlen(process->cgroup)); + Row_updateFieldWidth(CCGROUP, strlen(process->cgroup)); } return; } char* cgroup_short = CGroup_filterName(process->cgroup); if (cgroup_short) { - Process_updateFieldWidth(CCGROUP, strlen(cgroup_short)); + Row_updateFieldWidth(CCGROUP, strlen(cgroup_short)); free_and_xStrdup(&process->cgroup_short, cgroup_short); free(cgroup_short); } else { //CCGROUP is alias to normal CGROUP if shortening fails - Process_updateFieldWidth(CCGROUP, strlen(process->cgroup)); + Row_updateFieldWidth(CCGROUP, strlen(process->cgroup)); free(process->cgroup_short); process->cgroup_short = NULL; } @@ -955,7 +957,7 @@ static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t *newline = '\0'; } - Process_updateFieldWidth(SECATTR, strlen(buffer)); + Row_updateFieldWidth(SECATTR, strlen(buffer)); if (process->secattr && String_eq(process->secattr, buffer)) { return; @@ -1004,7 +1006,7 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats)); - assert(lp->super.pid == (pid_t)stats.ac_pid); + assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid); // The xxx_delay_total values wrap around on overflow. // (Linux Kernel "Documentation/accounting/taskstats-struct.rst") @@ -1045,7 +1047,7 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc nlmsg_free(msg); } - if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) { + if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) { nlmsg_free(msg); } @@ -1294,13 +1296,15 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo } static bool isOlderThan(const Process* proc, unsigned int seconds) { - assert(proc->host->realtimeMs > 0); + const Machine* host = proc->super.host; + + assert(host->realtimeMs > 0); /* Starttime might not yet be parsed */ if (proc->starttime_ctime <= 0) return false; - uint64_t realtime = proc->host->realtimeMs / 1000; + uint64_t realtime = host->realtimeMs / 1000; if (realtime < (uint64_t)proc->starttime_ctime) return false; @@ -1366,7 +1370,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } // Skip task directory of main thread - if (parent && pid == parent->pid) + if (parent && pid == Process_getPid(parent)) continue; #ifdef HAVE_OPENAT @@ -1382,8 +1386,8 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new); LinuxProcess* lp = (LinuxProcess*) proc; - proc->tgid = parent ? parent->pid : pid; - proc->isUserlandThread = proc->pid != proc->tgid; + Process_setThreadGroup(proc, parent ? Process_getPid(parent) : pid); + proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc); LinuxProcessList_recurseProcTree(this, procFd, lhost, "task", proc); @@ -1394,24 +1398,24 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ * But it will short-circuit subsequent scans. */ if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) { - proc->updated = true; - proc->show = false; + proc->super.updated = true; + proc->super.show = false; pl->kernelThreads++; pl->totalTasks++; Compat_openatArgClose(procFd); continue; } if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) { - proc->updated = true; - proc->show = false; + proc->super.updated = true; + proc->super.show = false; pl->userlandThreads++; pl->totalTasks++; Compat_openatArgClose(procFd); continue; } if (preExisting && hideRunningInContainer && proc->isRunningInContainer) { - proc->updated = true; - proc->show = false; + proc->super.updated = true; + proc->super.show = false; Compat_openatArgClose(procFd); continue; } @@ -1479,7 +1483,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) { - LinuxProcess_updateIOPriority(lp); + LinuxProcess_updateIOPriority(proc); } proc->percent_cpu = NAN; @@ -1564,11 +1568,11 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ * Final section after all data has been gathered */ - proc->updated = true; + proc->super.updated = true; Compat_openatArgClose(procFd); if (hideRunningInContainer && proc->isRunningInContainer) { - proc->show = false; + proc->super.show = false; continue; } @@ -1579,7 +1583,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } /* Set at the end when we know if a new entry is a thread */ - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); + proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); pl->totalTasks++; /* runningTasks is set in Machine_scanCPUTime() from /proc/stat */ @@ -1612,7 +1616,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ void ProcessList_goThroughEntries(ProcessList* super) { LinuxProcessList* this = (LinuxProcessList*) super; - const Machine* host = super->host; + const Machine* host = super->super.host; const Settings* settings = host->settings; const LinuxMachine* lhost = (const LinuxMachine*) host; diff --git a/linux/Platform.c b/linux/Platform.c index 1a2ef6179..33bd8ced4 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -164,7 +164,7 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) { const void* set = Action_pickFromVector(st, ioprioPanel, 20, true); if (set) { IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel); - bool ok = MainPanel_foreachProcess(st->mainPanel, LinuxProcess_setIOPriority, (Arg) { .i = ioprio2 }, NULL); + bool ok = MainPanel_foreachRow(st->mainPanel, LinuxProcess_rowSetIOPriority, (Arg) { .i = ioprio2 }, NULL); if (!ok) { beep(); } @@ -179,7 +179,7 @@ static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) { return false; } bool anyTagged; - bool ok = MainPanel_foreachProcess(panel, LinuxProcess_changeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged); + bool ok = MainPanel_foreachRow(panel, LinuxProcess_rowChangeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged); if (!ok) beep(); return anyTagged; diff --git a/netbsd/NetBSDProcess.c b/netbsd/NetBSDProcess.c index bdb0f50c0..605b56c80 100644 --- a/netbsd/NetBSDProcess.c +++ b/netbsd/NetBSDProcess.c @@ -225,7 +225,8 @@ void Process_delete(Object* cast) { free(this); } -static void NetBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { +static void NetBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const Process* this = (const Process*) super; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; @@ -254,11 +255,18 @@ static int NetBSDProcess_compareByKey(const Process* v1, const Process* v2, Proc const ProcessClass NetBSDProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = NetBSDProcess_rowWriteField }, - .writeField = NetBSDProcess_writeField, .compareByKey = NetBSDProcess_compareByKey }; diff --git a/netbsd/NetBSDProcessList.c b/netbsd/NetBSDProcessList.c index 9e5a9be4d..1327de706 100644 --- a/netbsd/NetBSDProcessList.c +++ b/netbsd/NetBSDProcessList.c @@ -37,17 +37,18 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { NetBSDProcessList* this = xCalloc(1, sizeof(NetBSDProcessList)); - ProcessList* super = (ProcessList*) this; + Object_setClass(this, Class(ProcessList)); + ProcessList* super = (ProcessList*) this; ProcessList_init(super, Class(NetBSDProcess), host, pidMatchList); return super; } -void ProcessList_delete(ProcessList* this) { - NetBSDProcessList* npl = (NetBSDProcessList*) this; - ProcessList_done(this); - free(npl); +void ProcessList_delete(Object* cast) { + NetBSDProcessList* this = (NetBSDProcessList*) cast; + ProcessList_done(&this->super); + free(this); } static void NetBSDProcessList_updateExe(const struct kinfo_proc2* kproc, Process* proc) { @@ -163,17 +164,17 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { bool preExisting = false; Process* proc = ProcessList_getProcess(&this->super, kproc->p_pid, &preExisting, NetBSDProcess_new); - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); + proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); if (!preExisting) { - proc->pid = kproc->p_pid; - proc->ppid = kproc->p_ppid; + Process_setPid(proc, kproc->p_pid); + Process_setParent(proc, kproc->p_ppid); + Process_setThreadGroup(proc, kproc->p_pid); proc->tpgid = kproc->p_tpgid; - proc->tgid = kproc->p_pid; proc->session = kproc->p_sid; proc->pgrp = kproc->p__pgid; proc->isKernelThread = !!(kproc->p_flag & P_SYSTEM); - proc->isUserlandThread = proc->pid != proc->tgid; + proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc); // eh? proc->starttime_ctime = kproc->p_ustart_sec; Process_fillStarttimeBuffer(proc); ProcessList_add(&this->super, proc); @@ -261,7 +262,7 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { if (proc->state == RUNNING) { this->super.runningTasks++; } - proc->updated = true; + proc->super.updated = true; } } diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c index 0875dc00f..c54a1763b 100644 --- a/openbsd/OpenBSDProcess.c +++ b/openbsd/OpenBSDProcess.c @@ -217,8 +217,9 @@ void Process_delete(Object* cast) { free(this); } -static void OpenBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { - //const OpenBSDProcess* op = (const OpenBSDProcess*) this; +static void OpenBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + //const OpenBSDProcess* op = (const OpenBSDProcess*) super; + const Process* this = (const Process*) super; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; //int n = sizeof(buffer) - 1; @@ -247,11 +248,18 @@ static int OpenBSDProcess_compareByKey(const Process* v1, const Process* v2, Pro const ProcessClass OpenBSDProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = OpenBSDProcess_rowWriteField }, - .writeField = OpenBSDProcess_writeField, .compareByKey = OpenBSDProcess_compareByKey }; diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index 84c833c3c..170663972 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -34,17 +34,17 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { OpenBSDProcessList* this = xCalloc(1, sizeof(OpenBSDProcessList)); - ProcessList* super = (ProcessList*) this; + Object_setClass(this, Class(ProcessList)); + ProcessList* super = &this->super; ProcessList_init(super, Class(OpenBSDProcess), host, pidMatchList); return this; } -void ProcessList_delete(ProcessList* super) { +void ProcessList_delete(Object* cast) { OpenBSDProcessList* this = (OpenBSDProcessList*) super; - - ProcessList_done(super); + ProcessList_done(&this->super); free(this); } @@ -156,9 +156,9 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { OpenBSDProcess* op = (OpenBSDProcess*) proc; if (!preExisting) { - proc->ppid = kproc->p_ppid; + Process_setParent(proc, kproc->p_ppid); + Process_setThreadGroup(proc, kproc->p_pid); proc->tpgid = kproc->p_tpgid; - proc->tgid = kproc->p_pid; proc->session = kproc->p_sid; proc->pgrp = kproc->p__pgid; proc->isKernelThread = proc->pgrp == 0; @@ -231,8 +231,8 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { this->super.runningTasks++; } - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); - proc->updated = true; + proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); + proc->super.updated = true; } } diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index 2481d81d1..88b4dbd55 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -250,7 +250,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri unsigned int type = PCPMetric_type(this->id); pmAtomValue atom; - if (!PCPMetric_instance(this->id, proc->pid, pp->offset, &atom, type)) { + if (!PCPMetric_instance(this->id, Process_getPid(proc), pp->offset, &atom, type)) { RichString_appendAscii(str, CRT_colors[METER_VALUE_ERROR], "no data"); return; } @@ -269,7 +269,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri switch (type) { case PM_TYPE_STRING: attr = CRT_colors[PROCESS_SHADOW]; - Process_printLeftAlignedField(str, attr, atom.cp, abswidth); + Row_printLeftAlignedField(str, attr, atom.cp, abswidth); free(atom.cp); break; case PM_TYPE_32: @@ -304,7 +304,9 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri } int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key) { - const PCPDynamicColumn* column = Hashtable_get(p1->super.host->settings->dynamicColumns, key); + const Process* proc = &p1->super; + const Settings* settings = proc->super.host->settings; + const PCPDynamicColumn* column = Hashtable_get(settings->dynamicColumns, key); if (!column) return -1; @@ -313,8 +315,8 @@ int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, Pr unsigned int type = PCPMetric_type(metric); pmAtomValue atom1 = {0}, atom2 = {0}; - if (!PCPMetric_instance(metric, p1->super.pid, p1->offset, &atom1, type) || - !PCPMetric_instance(metric, p2->super.pid, p2->offset, &atom2, type)) { + if (!PCPMetric_instance(metric, Process_getPid(&p1->super), p1->offset, &atom1, type) || + !PCPMetric_instance(metric, Process_getPid(&p2->super), p2->offset, &atom2, type)) { if (type == PM_TYPE_STRING) { free(atom1.cp); free(atom2.cp); diff --git a/pcp/PCPProcess.c b/pcp/PCPProcess.c index eadc9eb4a..b54834765 100644 --- a/pcp/PCPProcess.c +++ b/pcp/PCPProcess.c @@ -124,37 +124,37 @@ static double PCPProcess_totalIORate(const PCPProcess* pp) { return totalRate; } -static void PCPProcess_writeField(const Process* this, RichString* str, ProcessField field) { - const PCPProcess* pp = (const PCPProcess*) this; - bool coloring = this->host->settings->highlightMegabytes; +static void PCPProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const PCPProcess* pp = (const PCPProcess*) super; + bool coloring = super->host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; switch ((int)field) { - case CMINFLT: Process_printCount(str, pp->cminflt, coloring); return; - case CMAJFLT: Process_printCount(str, pp->cmajflt, coloring); return; - case M_DRS: Process_printBytes(str, pp->m_drs, coloring); return; - case M_DT: Process_printBytes(str, pp->m_dt, coloring); return; - case M_LRS: Process_printBytes(str, pp->m_lrs, coloring); return; - case M_TRS: Process_printBytes(str, pp->m_trs, coloring); return; - case M_SHARE: Process_printBytes(str, pp->m_share, coloring); return; - case M_PSS: Process_printKBytes(str, pp->m_pss, coloring); return; - case M_SWAP: Process_printKBytes(str, pp->m_swap, coloring); return; - case M_PSSWP: Process_printKBytes(str, pp->m_psswp, coloring); return; - case UTIME: Process_printTime(str, pp->utime, coloring); return; - case STIME: Process_printTime(str, pp->stime, coloring); return; - case CUTIME: Process_printTime(str, pp->cutime, coloring); return; - case CSTIME: Process_printTime(str, pp->cstime, coloring); return; - case RCHAR: Process_printBytes(str, pp->io_rchar, coloring); return; - case WCHAR: Process_printBytes(str, pp->io_wchar, coloring); return; - case SYSCR: Process_printCount(str, pp->io_syscr, coloring); return; - case SYSCW: Process_printCount(str, pp->io_syscw, coloring); return; - case RBYTES: Process_printBytes(str, pp->io_read_bytes, coloring); return; - case WBYTES: Process_printBytes(str, pp->io_write_bytes, coloring); return; - case CNCLWB: Process_printBytes(str, pp->io_cancelled_write_bytes, coloring); return; - case IO_READ_RATE: Process_printRate(str, pp->io_rate_read_bps, coloring); return; - case IO_WRITE_RATE: Process_printRate(str, pp->io_rate_write_bps, coloring); return; - case IO_RATE: Process_printRate(str, PCPProcess_totalIORate(pp), coloring); return; + case CMINFLT: Row_printCount(str, pp->cminflt, coloring); return; + case CMAJFLT: Row_printCount(str, pp->cmajflt, coloring); return; + case M_DRS: Row_printBytes(str, pp->m_drs, coloring); return; + case M_DT: Row_printBytes(str, pp->m_dt, coloring); return; + case M_LRS: Row_printBytes(str, pp->m_lrs, coloring); return; + case M_TRS: Row_printBytes(str, pp->m_trs, coloring); return; + case M_SHARE: Row_printBytes(str, pp->m_share, coloring); return; + case M_PSS: Row_printKBytes(str, pp->m_pss, coloring); return; + case M_SWAP: Row_printKBytes(str, pp->m_swap, coloring); return; + case M_PSSWP: Row_printKBytes(str, pp->m_psswp, coloring); return; + case UTIME: Row_printTime(str, pp->utime, coloring); return; + case STIME: Row_printTime(str, pp->stime, coloring); return; + case CUTIME: Row_printTime(str, pp->cutime, coloring); return; + case CSTIME: Row_printTime(str, pp->cstime, coloring); return; + case RCHAR: Row_printBytes(str, pp->io_rchar, coloring); return; + case WCHAR: Row_printBytes(str, pp->io_wchar, coloring); return; + case SYSCR: Row_printCount(str, pp->io_syscr, coloring); return; + case SYSCW: Row_printCount(str, pp->io_syscw, coloring); return; + case RBYTES: Row_printBytes(str, pp->io_read_bytes, coloring); return; + case WBYTES: Row_printBytes(str, pp->io_write_bytes, coloring); return; + case CNCLWB: Row_printBytes(str, pp->io_cancelled_write_bytes, coloring); return; + case IO_READ_RATE: Row_printRate(str, pp->io_rate_read_bps, coloring); return; + case IO_WRITE_RATE: Row_printRate(str, pp->io_rate_write_bps, coloring); return; + case IO_RATE: Row_printRate(str, PCPProcess_totalIORate(pp), coloring); return; case CGROUP: xSnprintf(buffer, n, "%-10s ", pp->cgroup ? pp->cgroup : ""); break; case OOM: xSnprintf(buffer, n, "%4u ", pp->oom); break; case PERCENT_CPU_DELAY: @@ -193,7 +193,7 @@ static void PCPProcess_writeField(const Process* this, RichString* str, ProcessF } break; default: - Process_writeField(this, str, field); + Process_writeField(&pp->super, str, field); return; } RichString_appendWide(str, attr, buffer); @@ -275,11 +275,18 @@ static int PCPProcess_compareByKey(const Process* v1, const Process* v2, Process const ProcessClass PCPProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = PCPProcess_rowWriteField, }, - .writeField = PCPProcess_writeField, - .compareByKey = PCPProcess_compareByKey + .compareByKey = PCPProcess_compareByKey, }; diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index 1a2d89edf..f320ba61d 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -33,16 +33,17 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList)); - ProcessList* super = &(this->super); + Object_setClass(this, Class(ProcessList)); + ProcessList* super = &this->super; ProcessList_init(super, Class(PCPProcess), host, pidMatchList); return super; } -void ProcessList_delete(ProcessList* super) { - PCPProcessList* this = (PCPProcessList*) super; - ProcessList_done(super); +void ProcessList_delete(Object* cast) { + PCPProcessList* this = (PCPProcessList*) cast; + ProcessList_done(&this->super); free(this); } @@ -129,8 +130,8 @@ static inline ProcessState PCPProcessList_getProcessState(char state) { } static void PCPProcessList_updateID(Process* process, int pid, int offset) { - process->tgid = Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1); - process->ppid = Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1); + Process_setThreadGroup(process, Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1)); + Process_setParent(process, Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1)); process->state = PCPProcessList_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?')); } @@ -310,7 +311,7 @@ static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, static bool PCPProcessList_updateProcesses(PCPProcessList* this) { ProcessList* pl = (ProcessList*) this; - Machine* host = pl->host; + Machine* host = pl->super.host; PCPMachine* phost = (PCPMachine*) host; const Settings* settings = host->settings; @@ -328,7 +329,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this) { Process* proc = ProcessList_getProcess(pl, pid, &preExisting, PCPProcess_new); PCPProcess* pp = (PCPProcess*) proc; PCPProcessList_updateID(proc, pid, offset); - proc->isUserlandThread = proc->pid != proc->tgid; + proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc); pp->offset = offset >= 0 ? offset : 0; /* @@ -338,8 +339,8 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this) { * But it will short-circuit subsequent scans. */ if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) { - proc->updated = true; - proc->show = false; + proc->super.updated = true; + proc->super.show = false; if (proc->state == RUNNING) pl->runningTasks++; pl->kernelThreads++; @@ -347,8 +348,8 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this) { continue; } if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) { - proc->updated = true; - proc->show = false; + proc->super.updated = true; + proc->super.show = false; if (proc->state == RUNNING) pl->runningTasks++; pl->userlandThreads++; @@ -427,13 +428,13 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this) { } /* Set at the end when we know if a new entry is a thread */ - proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || + proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); pl->totalTasks++; if (proc->state == RUNNING) pl->runningTasks++; - proc->updated = true; + proc->super.updated = true; } return true; } diff --git a/solaris/SolarisProcess.c b/solaris/SolarisProcess.c index 3b5ea1ae5..2ec3c1d52 100644 --- a/solaris/SolarisProcess.c +++ b/solaris/SolarisProcess.c @@ -73,8 +73,8 @@ void Process_delete(Object* cast) { free(sp); } -static void SolarisProcess_writeField(const Process* this, RichString* str, ProcessField field) { - const SolarisProcess* sp = (const SolarisProcess*) this; +static void SolarisProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const SolarisProcess* sp = (const SolarisProcess*) super; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; @@ -85,13 +85,13 @@ static void SolarisProcess_writeField(const Process* this, RichString* str, Proc case TASKID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->taskid); break; case POOLID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->poolid); break; case CONTID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->contid); break; - case ZONE: Process_printLeftAlignedField(str, attr, sp->zname ? sp->zname : "global", ZONENAME_MAX/4); return; + case ZONE: Row_printLeftAlignedField(str, attr, sp->zname ? sp->zname : "global", ZONENAME_MAX/4); return; case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realpid); break; case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realppid); break; case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realtgid); break; case LWPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->lwpid); break; default: - Process_writeField(this, str, field); + Process_writeField(&sp->super, str, field); return; } RichString_appendWide(str, attr, buffer); @@ -127,11 +127,18 @@ static int SolarisProcess_compareByKey(const Process* v1, const Process* v2, Pro const ProcessClass SolarisProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = SolarisProcess_rowWriteField }, - .writeField = SolarisProcess_writeField, .compareByKey = SolarisProcess_compareByKey }; diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index e759b1877..46067a7ba 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -45,18 +45,19 @@ static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sp } ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); - ProcessList* pl = (ProcessList*) spl; + SolarisProcessList* this = xCalloc(1, sizeof(SolarisProcessList)); + Object_setClass(this, Class(ProcessList)); - ProcessList_init(pl, Class(SolarisProcess), host, pidMatchList); + ProcessList* super = &this->super; + ProcessList_init(super, Class(SolarisProcess), host, pidMatchList); - return pl; + return super; } -void ProcessList_delete(ProcessList* pl) { - SolarisProcessList* spl = (SolarisProcessList*) pl; - ProcessList_done(pl); - free(spl); +void ProcessList_delete(Object* cast) { + SolarisProcessList* this = (SolarisProcessList*) cast; + ProcessList_done(&this->super); + free(this); } static void SolarisProcessList_updateExe(pid_t pid, Process* proc) { @@ -183,8 +184,8 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, // End common code pass 1 if (onMasterLWP) { // Are we on the representative LWP? - proc->ppid = (_psinfo->pr_ppid * 1024); - proc->tgid = (_psinfo->pr_ppid * 1024); + Process_setParent(proc, (_psinfo->pr_ppid * 1024)); + Process_setThreadGroup(proc, (_psinfo->pr_ppid * 1024)); sproc->realppid = _psinfo->pr_ppid; sproc->realtgid = _psinfo->pr_ppid; @@ -224,8 +225,8 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, proc->time = _lwpsinfo->pr_time.tv_sec * 100 + _lwpsinfo->pr_time.tv_nsec / 10000000; if (!preExisting) { // Tasks done only for NEW LWPs proc->isUserlandThread = true; - proc->ppid = _psinfo->pr_pid * 1024; - proc->tgid = _psinfo->pr_pid * 1024; + Process_setParent(proc, _psinfo->pr_pid * 1024); + Process_setThreadGroup(proc, _psinfo->pr_pid * 1024); sproc->realppid = _psinfo->pr_pid; sproc->realtgid = _psinfo->pr_pid; proc->starttime_ctime = _lwpsinfo->pr_start.tv_sec; @@ -233,10 +234,10 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, // Top-level process only gets this for the representative LWP if (proc->isKernelThread && !settings->hideKernelThreads) { - proc->show = true; + proc->super.show = true; } if (!proc->isKernelThread && !settings->hideUserlandThreads) { - proc->show = true; + proc->super.show = true; } } // Top-level LWP or subordinate LWP @@ -253,7 +254,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, ProcessList_add(pl, proc); } - proc->updated = true; + proc->super.updated = true; // End common code pass 2 diff --git a/unsupported/UnsupportedProcess.c b/unsupported/UnsupportedProcess.c index 4d8cb0803..6df2e36a4 100644 --- a/unsupported/UnsupportedProcess.c +++ b/unsupported/UnsupportedProcess.c @@ -58,21 +58,20 @@ void Process_delete(Object* cast) { free(cast); } -static void UnsupportedProcess_writeField(const Process* this, RichString* str, ProcessField field) { - const UnsupportedProcess* up = (const UnsupportedProcess*) this; - bool coloring = this->host->settings->highlightMegabytes; +static void UnsupportedProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const UnsupportedProcess* up = (const UnsupportedProcess*) super; + bool coloring = super->host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; - (void) up; (void) coloring; (void) n; switch (field) { /* Add platform specific fields */ default: - Process_writeField(this, str, field); + Process_writeField(&up->super, str, field); return; } RichString_appendWide(str, attr, buffer); @@ -94,11 +93,18 @@ static int UnsupportedProcess_compareByKey(const Process* v1, const Process* v2, const ProcessClass UnsupportedProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = UnsupportedProcess_rowWriteField }, - .writeField = UnsupportedProcess_writeField, .compareByKey = UnsupportedProcess_compareByKey }; diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index e56f49782..1e29c17a1 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -15,15 +15,18 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - ProcessList* this = xCalloc(1, sizeof(ProcessList)); + UnsupportedProcessList* this = xCalloc(1, sizeof(UnsupportedProcessList)); + Object_setClass(this, Class(ProcessList)); - ProcessList_init(this, Class(Process), host, pidMatchList); + ProcessList* super = &this->super; + ProcessList_init(super, Class(Process), host, pidMatchList); return this; } -void ProcessList_delete(ProcessList* this) { - ProcessList_done(this); +void ProcessList_delete(Object* cast) { + UnsupportedProcessList* this = (UnsupportedProcessList*) cast; + ProcessList_done(&this->super); free(this); } @@ -35,9 +38,9 @@ void ProcessList_goThroughEntries(ProcessList* super) { /* Empty values */ proc->time = proc->time + 10; - proc->pid = 1; - proc->ppid = 1; - proc->tgid = 0; + Process_setPid(proc, 1); + Process_setParent(proc, 1); + Process_setThreadGroup(proc, 0); Process_updateComm(proc, "commof16char"); Process_updateCmdline(proc, "", 0, 0); @@ -48,12 +51,12 @@ void ProcessList_goThroughEntries(ProcessList* super) { free_and_xStrdup(&proc->procCwd, "/current/working/directory"); } - proc->updated = true; + proc->super.updated = true; proc->state = RUNNING; proc->isKernelThread = false; proc->isUserlandThread = false; - proc->show = true; /* Reflected in settings-> "hideXXX" really */ + proc->super.show = true; /* Reflected in settings-> "hideXXX" really */ proc->pgrp = 0; proc->session = 0; proc->tty_nr = 0;