Skip to content

Commit db33a7e

Browse files
committed
Add option to merge processes of same application
Experimental - Linux only Closes: htop-dev#301
1 parent c84ac61 commit db33a7e

9 files changed

+289
-68
lines changed

Action.c

+7
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
250250
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
251251
}
252252

253+
static Htop_Reaction actionToggleMergeApplication(State* st) {
254+
st->settings->mergeApplications = !st->settings->mergeApplications;
255+
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
256+
}
257+
253258
static Htop_Reaction actionIncFilter(State* st) {
254259
IncSet* inc = (st->mainPanel)->inc;
255260
IncSet_activate(inc, INC_FILTER, (Panel*)st->mainPanel);
@@ -543,6 +548,7 @@ static const struct {
543548
{ .key = " x: ", .roInactive = false, .info = "list file locks of process" },
544549
{ .key = " s: ", .roInactive = true, .info = "trace syscalls with strace" },
545550
{ .key = " w: ", .roInactive = false, .info = "wrap process command in multiple lines" },
551+
{ .key = " A: ", .roInactive = false, .info = "Merge processes of same application" },
546552
{ .key = " F2 C S: ", .roInactive = false, .info = "setup" },
547553
{ .key = " F1 h ?: ", .roInactive = false, .info = "show this help screen" },
548554
{ .key = " F10 q: ", .roInactive = false, .info = "quit" },
@@ -713,6 +719,7 @@ void Action_setBindings(Htop_Action* keys) {
713719
keys['='] = actionExpandOrCollapse;
714720
keys['>'] = actionSetSortColumn;
715721
keys['?'] = actionHelp;
722+
keys['A'] = actionToggleMergeApplication;
716723
keys['C'] = actionSetup;
717724
keys['F'] = Action_follow;
718725
keys['H'] = actionToggleUserlandThreads;

DisplayOptionsPanel.c

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
101101
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID)));
102102
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed)));
103103
Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs)));
104+
Panel_add(super, (Object*) CheckItem_newByRef("Merge processes of same applications", &(settings->mergeApplications)));
104105
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
105106
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
106107
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));

Process.c

+74
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
786786
}
787787
const ScreenSettings* ss = this->settings->ss;
788788
if (!ss->treeView || this->indent == 0) {
789+
if (this->merged > 1) {
790+
char merged[16];
791+
xSnprintf(merged, sizeof(merged), "[%u] ", this->merged);
792+
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], merged);
793+
}
789794
Process_writeCommand(this, attr, baseattr, str);
790795
return;
791796
}
@@ -820,6 +825,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
820825
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
821826
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
822827
RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
828+
if (this->merged > 1) {
829+
char merged[16];
830+
xSnprintf(merged, sizeof(merged), "[%u] ", this->merged);
831+
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], merged);
832+
}
823833
Process_writeCommand(this, attr, baseattr, str);
824834
return;
825835
}
@@ -1088,6 +1098,40 @@ int Process_pidCompare(const void* v1, const void* v2) {
10881098
return SPACESHIP_NUMBER(p1->pid, p2->pid);
10891099
}
10901100

1101+
static bool isTransitiveChildOf(const Process* child, const Process* parent) {
1102+
assert(child->processList == parent->processList);
1103+
1104+
for (const Process* tChild = child; tChild; tChild = ProcessList_findProcess(parent->processList, Process_getParentPid(tChild)))
1105+
if (Process_isChildOf(tChild, parent->pid))
1106+
return true;
1107+
1108+
return false;
1109+
}
1110+
1111+
int Process_sameApplication(const Process* v1, const Process* v2) {
1112+
const char* exe1 = v1->procExe;
1113+
const char* exe2 = v2->procExe;
1114+
1115+
if (!exe1 || !exe2 || !String_eq(exe1, exe2))
1116+
return 0;
1117+
1118+
1119+
if (v1->session != v2->session)
1120+
return 0;
1121+
1122+
// we can compare pointers since the field user points to a hashtable entry
1123+
if (v1->user != v2->user)
1124+
return 0;
1125+
1126+
if (isTransitiveChildOf(v1, v2))
1127+
return 2;
1128+
1129+
if (isTransitiveChildOf(v2, v1))
1130+
return 1;
1131+
1132+
return 0;
1133+
}
1134+
10911135
int Process_compare(const void* v1, const void* v2) {
10921136
const Process* p1 = (const Process*)v1;
10931137
const Process* p2 = (const Process*)v2;
@@ -1251,3 +1295,33 @@ void Process_updateExe(Process* this, const char* exe) {
12511295
}
12521296
this->mergedCommand.exeChanged = true;
12531297
}
1298+
1299+
void Process_mergeData(Process* p1, const Process* p2) {
1300+
1301+
p1->percent_cpu += p2->percent_cpu;
1302+
p1->percent_mem += p2->percent_mem;
1303+
// keep COMM
1304+
p1->majflt += p2->majflt;
1305+
p1->minflt += p2->minflt;
1306+
p1->m_resident += p2->m_resident;
1307+
p1->m_virt += p2->m_virt;
1308+
// store min NICE
1309+
p1->nice = MINIMUM(p1->nice, p2->nice);
1310+
p1->nlwp += p2->nlwp;
1311+
// keep PGRP
1312+
// keep PID
1313+
// keep PPID
1314+
p1->priority = MAXIMUM(p1->priority, p2->priority);
1315+
// keep PROCESSOR
1316+
// keep SESSION
1317+
p1->starttime_ctime = MINIMUM(p1->starttime_ctime, p2->starttime_ctime);
1318+
// keep STATE
1319+
// keep ST_UID
1320+
p1->time += p2->time;
1321+
// keep TGID
1322+
// keep TPGID
1323+
// keep TTY_NR
1324+
// keep USER
1325+
1326+
p1->merged += p2->merged;
1327+
}

Process.h

+12
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ typedef struct Process_ {
240240
/* Whether to show children of this process in tree-mode */
241241
bool showChildren;
242242

243+
/* The number of processes merged into this one. */
244+
unsigned int merged;
245+
243246
/*
244247
* Internal time counts for showing new and exited processes.
245248
*/
@@ -284,6 +287,11 @@ typedef struct ProcessFieldData_ {
284287
// Implemented in platform-specific code:
285288
void Process_writeField(const Process* this, RichString* str, ProcessField field);
286289
int Process_compare(const void* v1, const void* v2);
290+
/* returns 1 on match and if v1 is the master process,
291+
* 2 on match and if v2 is the master process,
292+
* 0 else */
293+
int Process_sameApplication(const Process* p1, const Process* p2);
294+
void Process_mergeData(Process* p1, const Process* p2);
287295
void Process_delete(Object* cast);
288296
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
289297
#define PROCESS_MIN_PID_DIGITS 5
@@ -297,12 +305,16 @@ typedef Process* (*Process_New)(const struct Settings_*);
297305
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
298306
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
299307
typedef const char* (*Process_GetCommandStr)(const Process*);
308+
typedef int (*Process_SameApplication)(const Process*, const Process*);
309+
typedef void (*Process_MergeData)(Process*, const Process*);
300310

301311
typedef struct ProcessClass_ {
302312
const ObjectClass super;
303313
const Process_WriteField writeField;
304314
const Process_CompareByKey compareByKey;
305315
const Process_GetCommandStr getCommandStr;
316+
const Process_SameApplication sameApplication;
317+
const Process_MergeData mergeData;
306318
} ProcessClass;
307319

308320
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))

0 commit comments

Comments
 (0)