Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CRT.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_SUM] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black),
[PROCESS_MEGABYTES] = ColorPair(Cyan, Black),
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
Expand Down Expand Up @@ -273,6 +274,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[TASKS_RUNNING] = A_BOLD,
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_DIM,
[PROCESS_SUM] = A_DIM,
[PROCESS_TAG] = A_BOLD,
[PROCESS_MEGABYTES] = A_BOLD,
[PROCESS_GIGABYTES] = A_BOLD,
Expand Down Expand Up @@ -391,6 +393,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[TASKS_RUNNING] = ColorPair(Green, White),
[PROCESS] = ColorPair(Black, White),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black, White),
[PROCESS_SUM] = A_BOLD | ColorPair(Black, White),
[PROCESS_TAG] = ColorPair(White, Blue),
[PROCESS_MEGABYTES] = ColorPair(Blue, White),
[PROCESS_GIGABYTES] = ColorPair(Green, White),
Expand Down Expand Up @@ -509,6 +512,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[TASKS_RUNNING] = ColorPair(Green, Black),
[PROCESS] = ColorPair(Black, Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_SUM] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = ColorPair(White, Blue),
[PROCESS_MEGABYTES] = ColorPair(Blue, Black),
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
Expand Down Expand Up @@ -627,6 +631,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Blue),
[PROCESS] = ColorPair(White, Blue),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black, Blue),
[PROCESS_SUM] = A_BOLD | ColorPair(Black, Blue),
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Blue),
[PROCESS_MEGABYTES] = ColorPair(Cyan, Blue),
[PROCESS_GIGABYTES] = ColorPair(Green, Blue),
Expand Down Expand Up @@ -745,6 +750,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
[PROCESS] = ColorPair(Cyan, Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_SUM] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black),
[PROCESS_MEGABYTES] = A_BOLD | ColorPair(Green, Black),
[PROCESS_GIGABYTES] = A_BOLD | ColorPair(Yellow, Black),
Expand Down Expand Up @@ -862,6 +868,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[TASKS_RUNNING] = A_BOLD,
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_SUM] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Cyan, Black),
[PROCESS_MEGABYTES] = A_BOLD | ColorPair(White, Black),
[PROCESS_GIGABYTES] = A_BOLD | ColorPair(Cyan, Black),
Expand Down
1 change: 1 addition & 0 deletions CRT.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ typedef enum ColorElements_ {
SWAP_FRONTSWAP,
PROCESS,
PROCESS_SHADOW,
PROCESS_SUM,
PROCESS_TAG,
PROCESS_MEGABYTES,
PROCESS_GIGABYTES,
Expand Down
1 change: 1 addition & 0 deletions DisplayOptionsPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
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 collapsed by default", &(settings->ss->allBranchesCollapsed)));
Panel_add(super, (Object*) NumberItem_newByRef("- Tree view is kept visually stable (0 - off, 1 - soft, 2 - hard)", &(settings->ss->stableTreeView), 0, 0, 2));
Panel_add(super, (Object*) CheckItem_newByRef("- Sum CPU/memory usage of collapsed subtrees", &(settings->collapsedSubtreeSum)));
Panel_add(super, (Object*) TextItem_new("Global options:"));
Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Expand Down
96 changes: 87 additions & 9 deletions Process.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,10 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
int attr = CRT_colors[DEFAULT_COLOR];
size_t n = sizeof(buffer) - 1;

/* When the row is a collapsed tree node whose subtree is being summed,
* additive fields are rendered from the aggregate and tinted accordingly. */
const bool useAgg = super->aggregated;

switch (field) {
case COMM: {
int baseattr = CRT_colors[PROCESS_BASENAME];
Expand Down Expand Up @@ -695,10 +699,30 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
Row_printTime(str, /* convert to hundreds of a second */ dt / 10, 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 MAJFLT: {
size_t start = RichString_size(str);
Row_printCount(str, useAgg ? this->aggregate.majflt : this->majflt, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
case MINFLT: {
size_t start = RichString_size(str);
Row_printCount(str, useAgg ? this->aggregate.minflt : this->minflt, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
case M_RESIDENT: {
size_t start = RichString_size(str);
Row_printKBytes(str, useAgg ? (unsigned long long)this->aggregate.m_resident : (unsigned long long)this->m_resident, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
case M_VIRT: {
size_t start = RichString_size(str);
Row_printKBytes(str, useAgg ? (unsigned long long)this->aggregate.m_virt : (unsigned long long)this->m_virt, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
Comment on lines +702 to +725

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Move single-statement if bodies to the next line.

Several newly added branches keep the body on the same line as the condition (e.g., if (useAgg) ...). This violates the C style rule used in this repo.

Suggested style-only patch
-      if (useAgg) Process_aggregateRecolor(str, start);
+      if (useAgg)
+         Process_aggregateRecolor(str, start);

-      if (useAgg) attr = CRT_colors[PROCESS_SUM];
+      if (useAgg)
+         attr = CRT_colors[PROCESS_SUM];
As per coding guidelines, `**/*.c`: “never put control flow body on same line as condition”.

Also applies to: 743-756, 808-813

Source: Coding guidelines

case NICE:
if (this->nice == PROCESS_NICE_UNKNOWN) {
xSnprintf(buffer, n, "N/A ");
Expand All @@ -716,13 +740,20 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {

xSnprintf(buffer, n, "%4ld ", this->nlwp);
break;
case PERCENT_CPU: Row_printPercentage(this->percent_cpu, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break;
case PERCENT_CPU:
Row_printPercentage(useAgg ? this->aggregate.percent_cpu : this->percent_cpu, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr);
if (useAgg) attr = CRT_colors[PROCESS_SUM];
break;
case PERCENT_NORM_CPU: {
float cpuPercentage = this->percent_cpu / host->activeCPUs;
float cpuPercentage = (useAgg ? this->aggregate.percent_cpu : this->percent_cpu) / host->activeCPUs;
Row_printPercentage(cpuPercentage, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr);
if (useAgg) attr = CRT_colors[PROCESS_SUM];
break;
}
case PERCENT_MEM: Row_printPercentage(this->percent_mem, buffer, n, 4, &attr); break;
case PERCENT_MEM:
Row_printPercentage(useAgg ? this->aggregate.percent_mem : this->percent_mem, buffer, n, 4, &attr);
if (useAgg) attr = CRT_colors[PROCESS_SUM];
break;
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); 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;
Expand Down Expand Up @@ -774,7 +805,12 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
}
break;
case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break;
case TIME: Row_printTime(str, this->time, coloring); return;
case TIME: {
size_t start = RichString_size(str);
Row_printTime(str, useAgg ? this->aggregate.time : this->time, coloring);
if (useAgg) Process_aggregateRecolor(str, start);
return;
}
case TGID:
if (Process_getThreadGroup(this) == Process_getPid(this))
attr = CRT_colors[PROCESS_SHADOW];
Expand Down Expand Up @@ -815,6 +851,46 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
RichString_appendAscii(str, attr, buffer);
}

void Process_aggregateRecolor(RichString* str, size_t start) {
size_t end = RichString_size(str);
if (end > start)
RichString_setAttrn(str, CRT_colors[PROCESS_SUM], start, end - start);
}

void Process_rowAggregateClear(Row* super) {
Process* this = (Process*) super;
this->aggregate.percent_cpu = this->percent_cpu;
this->aggregate.percent_mem = this->percent_mem;
this->aggregate.m_virt = this->m_virt;
this->aggregate.m_resident = this->m_resident;
this->aggregate.minflt = this->minflt;
this->aggregate.majflt = this->majflt;
this->aggregate.time = this->time;
}

void Process_rowAggregateAdd(Row* super, const Row* child) {
Process* this = (Process*) super;
const Process* cp = (const Process*) child;

// Threads share their process' resources, which are already counted in the
// process row; summing them would multiply memory and CPU. Skip them.
if (Process_isThread(cp))
return;

this->aggregate.percent_cpu += cp->aggregate.percent_cpu;
this->aggregate.percent_mem += cp->aggregate.percent_mem;
this->aggregate.m_virt += cp->aggregate.m_virt;
this->aggregate.m_resident += cp->aggregate.m_resident;
this->aggregate.minflt += cp->aggregate.minflt;
this->aggregate.majflt += cp->aggregate.majflt;
this->aggregate.time += cp->aggregate.time;

// A collapsed node will display this running total, which can be wider than
// any single process; grow the CPU% columns so the sum does not overflow.
if (!super->showChildren)
Process_updateCPUFieldWidths(this->aggregate.percent_cpu);
}

void Process_done(Process* this) {
assert(this != NULL);
free(this->cmdline);
Expand Down Expand Up @@ -1146,6 +1222,8 @@ const ProcessClass Process_class = {
.matchesFilter = Process_rowMatchesFilter,
.sortKeyString = Process_rowGetSortKey,
.compareByParent = Process_compareByParent,
.writeField = Process_rowWriteField
.writeField = Process_rowWriteField,
.aggregateClear = Process_rowAggregateClear,
.aggregateAdd = Process_rowAggregateAdd
},
};
25 changes: 25 additions & 0 deletions Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,26 @@ typedef struct ProcessMergedCommand_ {
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
} ProcessMergedCommand;

/* Subtree totals of the additive fields common to all platforms. Populated
* during tree building when the "sum collapsed subtrees" setting is enabled,
* and displayed in place of the node's own values while it is collapsed. */
typedef struct ProcessAggregate_ {
float percent_cpu;
float percent_mem;
long m_virt;
long m_resident;
unsigned long int minflt;
unsigned long int majflt;
unsigned long long int time;
} ProcessAggregate;

typedef struct Process_ {
/* Super object for emulated OOP */
Row super;

/* Subtree totals for collapsed tree nodes (see ProcessAggregate) */
ProcessAggregate aggregate;

/* Process group identifier */
int pgrp;

Expand Down Expand Up @@ -234,6 +250,15 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
int Process_compare(const void* v1, const void* v2);
int Process_compareByParent(const Row* r1, const Row* r2);
void Process_delete(Object* cast);

/* Row vtable hooks for subtree summation; platform classes may wrap these to
* additionally aggregate their own additive fields. */
void Process_rowAggregateClear(Row* super);
void Process_rowAggregateAdd(Row* super, const Row* child);

/* Recolors the most recently appended span of `str` (from `start` to the
* current end) using the aggregate (summed value) color. */
void Process_aggregateRecolor(RichString* str, size_t start);
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
#define Process_pidDigits Row_pidDigits
#define Process_uidDigits Row_uidDigits
Expand Down
1 change: 1 addition & 0 deletions Row.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void Row_init(Row* this, const Machine* host) {
this->host = host;
this->tag = false;
this->showChildren = true;
this->aggregated = false;
this->show = true;
this->wasShown = false;
this->updated = false;
Expand Down
13 changes: 13 additions & 0 deletions Row.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ typedef struct Row_ {
/* Whether to show children of this row in tree-mode */
bool showChildren;

/* Whether this row displays aggregated (summed) values for its collapsed
subtree (tree-mode only, when the corresponding setting is enabled) */
bool aggregated;

/* Whether the row was updated during the last scan */
bool updated;

Expand All @@ -83,6 +87,8 @@ typedef bool (*Row_IsVisible)(const Row*, const struct Table_*);
typedef bool (*Row_MatchesFilter)(const Row*, const struct Table_*);
typedef const char* (*Row_SortKeyString)(Row*);
typedef int (*Row_CompareByParent)(const Row*, const Row*);
typedef void (*Row_AggregateClear)(Row*);
typedef void (*Row_AggregateAdd)(Row*, const Row*);

int Row_compare(const void* v1, const void* v2);

Expand All @@ -94,6 +100,8 @@ typedef struct RowClass_ {
const Row_MatchesFilter matchesFilter;
const Row_SortKeyString sortKeyString;
const Row_CompareByParent compareByParent;
const Row_AggregateClear aggregateClear;
const Row_AggregateAdd aggregateAdd;
} RowClass;

#define As_Row(this_) ((const RowClass*)((this_)->super.klass))
Expand All @@ -104,6 +112,11 @@ typedef struct RowClass_ {
#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_))

/* Reset a row's aggregate to its own values; no-op if unsupported by the class */
#define Row_aggregateClear(r_) do { if (As_Row(r_)->aggregateClear) As_Row(r_)->aggregateClear(r_); } while (0)
/* Add a child's subtree total into a row's aggregate; no-op if unsupported */
#define Row_aggregateAdd(r_, c_) do { if (As_Row(r_)->aggregateAdd) As_Row(r_)->aggregateAdd((r_), (c_)); } while (0)

#define ONE_K 1024UL
#define ONE_M (ONE_K * ONE_K)
#define ONE_G (ONE_M * ONE_K)
Expand Down
4 changes: 4 additions & 0 deletions Settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,8 @@ static bool Settings_read(Settings* this, const char* fileName, const Machine* h
this->showCPUFrequency = atoi(option[1]);
} else if (String_eq(option[0], "show_cached_memory")) {
this->showCachedMemory = atoi(option[1]);
} else if (String_eq(option[0], "tree_sum_collapsed_subtree")) {
this->collapsedSubtreeSum = atoi(option[1]);
#ifdef BUILD_WITH_CPU_TEMP
} else if (String_eq(option[0], "show_cpu_temperature")) {
this->showCPUTemperature = atoi(option[1]);
Expand Down Expand Up @@ -722,6 +724,7 @@ int Settings_write(const Settings* this, bool onCrash) {
printSettingInteger("degree_fahrenheit", this->degreeFahrenheit);
#endif
printSettingInteger("show_cached_memory", this->showCachedMemory);
printSettingInteger("tree_sum_collapsed_subtree", this->collapsedSubtreeSum);
printSettingInteger("update_process_names", this->updateProcessNames);
printSettingInteger("account_guest_in_cpu_meter", this->accountGuestInCPUMeter);
printSettingInteger("color_scheme", this->colorScheme);
Expand Down Expand Up @@ -830,6 +833,7 @@ Settings* Settings_new(const Machine* host, Hashtable* dynamicMeters, Hashtable*
this->degreeFahrenheit = false;
#endif
this->showCachedMemory = true;
this->collapsedSubtreeSum = false;
this->updateProcessNames = false;
this->showProgramPath = true;
this->highlightThreads = true;
Expand Down
1 change: 1 addition & 0 deletions Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ typedef struct Settings_ {
bool headerMargin;
bool screenTabs;
bool showCachedMemory;
bool collapsedSubtreeSum;
#ifdef HAVE_GETMOUSE
bool enableMouse;
#endif
Expand Down
15 changes: 15 additions & 0 deletions Table.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ static void Table_buildTreeBranch(Table* this, int rowid, unsigned int level, in
if (rowid == 0)
return;

// When enabled, sum the additive fields of the whole subtree into this node,
// so a collapsed node can display its subtree totals.
const Settings* settings = this->host->settings;
const bool aggregating = settings->collapsedSubtreeSum && settings->ss->treeView;
Row* node = aggregating ? Table_findRow(this, rowid) : NULL;
if (node)
Row_aggregateClear(node);

// The vector is sorted by parent, find the start of the range by bisection
int vsize = Vector_size(this->rows);
int l = 0;
Expand Down Expand Up @@ -142,13 +150,19 @@ static void Table_buildTreeBranch(Table* this, int rowid, unsigned int level, in

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 (node)
Row_aggregateAdd(node, row);
if (i == lastShown)
row->indent = -nextIndent;
else
row->indent = nextIndent;

row->tree_depth = level + 1;
}

// This node displays subtree totals only while it is collapsed and non-empty.
if (node)
node->aggregated = (l < r) && !node->showChildren;
}

static int compareRowByKnownParentThenNatural(const void* v1, const void* v2) {
Expand All @@ -165,6 +179,7 @@ static void Table_buildTree(Table* this) {
Row* row = (Row*) Vector_get(this->rows, i);
int parent = Row_getGroupOrParent(row);
row->isRoot = false;
row->aggregated = false;

if (row->id == parent) {
row->isRoot = true;
Expand Down
4 changes: 3 additions & 1 deletion darwin/DarwinProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,9 @@ const ProcessClass DarwinProcess_class = {
.matchesFilter = Process_rowMatchesFilter,
.compareByParent = Process_compareByParent,
.sortKeyString = Process_rowGetSortKey,
.writeField = DarwinProcess_rowWriteField
.writeField = DarwinProcess_rowWriteField,
.aggregateClear = Process_rowAggregateClear,
.aggregateAdd = Process_rowAggregateAdd
},
.compareByKey = DarwinProcess_compareByKey
};
Loading
Loading