summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Scott <nathans@redhat.com>2023-08-22 16:11:05 +1000
committerNathan Scott <nathans@redhat.com>2023-08-30 13:11:57 +1000
commit0f751e991d399769fb8d7800f7c4bccec2ca7f60 (patch)
tree34cd7838f7ebf51049816f9acb6a63cea175af06
parent68f4f10f012d11bd57bb725fe4113b2af937fc1d (diff)
Introduce Row and Table classes for screens beyond top-processes
This commit refactors the Process and ProcessList structures such they each have a new parent - Row and Table, respectively. These new classes handle screen updates relating to anything that could be represented in tabular format, e.g. cgroups, filesystems, etc, without us having to reimplement the display logic repeatedly for each new entity.
-rw-r--r--Action.c104
-rw-r--r--Affinity.c34
-rw-r--r--Affinity.h6
-rw-r--r--AvailableColumnsPanel.c2
-rw-r--r--ColumnsPanel.c2
-rw-r--r--CommandLine.c17
-rw-r--r--CommandScreen.c2
-rw-r--r--DisplayOptionsPanel.c2
-rw-r--r--EnvScreen.c4
-rw-r--r--Machine.c52
-rw-r--r--Machine.h22
-rw-r--r--MainPanel.c63
-rw-r--r--MainPanel.h10
-rw-r--r--Makefile.am5
-rw-r--r--OpenFilesScreen.c6
-rw-r--r--Process.c578
-rw-r--r--Process.h198
-rw-r--r--ProcessList.c461
-rw-r--r--ProcessList.h40
-rw-r--r--ProcessLocksScreen.c4
-rw-r--r--Row.c486
-rw-r--r--Row.h179
-rw-r--r--RowField.h56
-rw-r--r--Scheduling.c12
-rw-r--r--Scheduling.h3
-rw-r--r--ScreenManager.c7
-rw-r--r--Settings.c14
-rw-r--r--Settings.h18
-rw-r--r--Table.c370
-rw-r--r--Table.h101
-rw-r--r--TasksMeter.c3
-rw-r--r--TraceScreen.c4
-rw-r--r--darwin/DarwinProcess.c41
-rw-r--r--darwin/DarwinProcessList.c10
-rw-r--r--dragonflybsd/DragonFlyBSDProcess.c24
-rw-r--r--dragonflybsd/DragonFlyBSDProcessList.c25
-rw-r--r--freebsd/FreeBSDProcess.c27
-rw-r--r--freebsd/FreeBSDProcessList.c21
-rw-r--r--linux/LinuxMachine.c3
-rw-r--r--linux/LinuxProcess.c111
-rw-r--r--linux/LinuxProcess.h6
-rw-r--r--linux/LinuxProcessList.c76
-rw-r--r--linux/Platform.c4
-rw-r--r--netbsd/NetBSDProcess.c20
-rw-r--r--netbsd/NetBSDProcessList.c23
-rw-r--r--openbsd/OpenBSDProcess.c22
-rw-r--r--openbsd/OpenBSDProcessList.c16
-rw-r--r--pcp/PCPDynamicColumn.c12
-rw-r--r--pcp/PCPProcess.c75
-rw-r--r--pcp/PCPProcessList.c29
-rw-r--r--solaris/SolarisProcess.c25
-rw-r--r--solaris/SolarisProcessList.c31
-rw-r--r--unsupported/UnsupportedProcess.c26
-rw-r--r--unsupported/UnsupportedProcessList.c21
54 files changed, 2053 insertions, 1460 deletions
diff --git a/Action.c b/Action.c
index 62308da7..f7fc4a43 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 f7c597bf..546975d5 100644
--- a/Affinity.c
+++ b/Affinity.c
@@ -12,6 +12,7 @@ in the source distribution for its full text.
#include <stdlib.h>
+#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 58d9bd73..341b0c04 100644
--- a/Affinity.h
+++ b/Affinity.h
@@ -16,7 +16,7 @@ in the source distribution for its full text.
#include <stdbool.h>
#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 b8c09c74..f3c40843 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 d53fff25..ecae36f7 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 3d1f953f..848c7ae7 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 6a87d137..ecd823bd 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 f9fa9b12..326d33b1 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 0fcee83a..b27155e6 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 63a996ef..8846aed7 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 64d6f6c2..a1c49e24 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 14bd3bbd..54127fe9 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 bd22acd0..d062616d 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 e36994c1..a05c7ea9 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 2e782b98..3077490d 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 7244ad9f..6f74e014 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 88416478..c6cdb995 100644
--- a/Process.h
+++ b/Process.h
@@ -13,8 +13,8 @@ in the source distribution for its full text.
#include <sys/types.h>
#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 58e63d03..516dcd7c 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 0f0f7d51..9710a0a5 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 57c9ce75..36a37f92 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 00000000..09a32069
--- /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 <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#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 00000000..5cdb3fe7
--- /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 <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#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 00000000..1e01ea3e
--- /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 5ca49ae2..10846c0b 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, &param);
+ int r = sched_setscheduler(Process_getPid(p), policy, &param);
/* 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 d91855ae..e5952b00 100644
--- a/Scheduling.h
+++ b/Scheduling.h
@@ -13,6 +13,7 @@ in the source distribution for its full text.
#include <stdbool.h>
#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 a089eda1..e39c4f06 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 c712966e..b772f091 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 48c62590..3caaab28 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 00000000..dcb48677
--- /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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 00000000..d16e47da
--- /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 <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#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 b5563fca..7dd6fdb4 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 e8f55ff5..03315482 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 e6366d70..ec98341e 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 c10e772c..1545600e 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 7cfc71be..a85b6efb 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 4ff17932..6330e911 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 4970ff2c..f6d3451d 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 d8d4bbe0..aabb61cc 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 5d2e67e9..ff2087c7 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 87561818..e348f9ab 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 6c309893..1cd0eaaf 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 a17b5e13..9be2433e 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 @@ errorReadingProcess:
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 1a2ef617..33bd8ced 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 bdb0f50c..605b56c8 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 9e5a9be4..1327de70 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 0875dc00..c54a1763 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 84c833c3..17066397 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 2481d81d..88b4dbd5 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 eadc9eb4..b5483476 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 1a2d89ed..f320ba61 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 3b5ea1ae..2ec3c1d5 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 e759b187..46067a7b 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 4d8cb080..6df2e36a 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 e56f4978..1e29c17a 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, "<unsupported architecture>", 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;

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