Skip to content

Commit 48f741c

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

File tree

11 files changed

+313
-56
lines changed

11 files changed

+313
-56
lines changed

Action.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ static Htop_Reaction actionToggleTreeView(State* st) {
220220
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
221221
}
222222

223+
static Htop_Reaction actionToggleMergeApplication(State* st) {
224+
st->settings->mergeApplications = !st->settings->mergeApplications;
225+
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
226+
}
227+
223228
static Htop_Reaction actionIncFilter(State* st) {
224229
IncSet* inc = ((MainPanel*)st->panel)->inc;
225230
IncSet_activate(inc, INC_FILTER, st->panel);
@@ -437,6 +442,7 @@ static const struct { const char* key; const char* info; } helpRight[] = {
437442
{ .key = " l: ", .info = "list open files with lsof" },
438443
{ .key = " s: ", .info = "trace syscalls with strace" },
439444
{ .key = " w: ", .info = "wrap process command in multiple lines" },
445+
{ .key = " A: ", .info = "Merge processes of same application" },
440446
{ .key = " F2 C S: ", .info = "setup" },
441447
{ .key = " F1 h: ", .info = "show this help screen" },
442448
{ .key = " F10 q: ", .info = "quit" },
@@ -632,4 +638,5 @@ void Action_setBindings(Htop_Action* keys) {
632638
keys['e'] = actionShowEnvScreen;
633639
keys['w'] = actionShowCommandScreen;
634640
keys['Z'] = actionTogglePauseProcessUpdate;
641+
keys['A'] = actionToggleMergeApplication;
635642
}

DisplayOptionsPanel.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
7777

7878
Panel_setHeader(super, "Display options");
7979
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
80+
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Merge processes of same applications"), &(settings->mergeApplications)));
8081
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
8182
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
8283
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));

Process.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
277277
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
278278
}
279279
if (!this->settings->treeView || this->indent == 0) {
280+
if (this->merged > 1) {
281+
char merged[16];
282+
xSnprintf(merged, sizeof(merged), "[%u] ", this->merged);
283+
RichString_append(str, CRT_colors[PROCESS_SHADOW], merged);
284+
}
280285
Process_writeCommand(this, attr, baseattr, str);
281286
return;
282287
} else {
@@ -305,6 +310,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
305310
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
306311
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
307312
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
313+
if (this->merged > 1) {
314+
char merged[16];
315+
xSnprintf(merged, sizeof(merged), "[%u] ", this->merged);
316+
RichString_append(str, CRT_colors[PROCESS_SHADOW], merged);
317+
}
308318
Process_writeCommand(this, attr, baseattr, str);
309319
return;
310320
}
@@ -441,6 +451,36 @@ long Process_pidCompare(const void* v1, const void* v2) {
441451
return (p1->pid - p2->pid);
442452
}
443453

454+
static bool isTransitiveChildOf(const Process* child, const Process* parent) {
455+
assert(child->pl == parent->pl);
456+
457+
for (const Process* tChild = child; tChild; tChild = ProcessList_findProcess(parent->pl, Process_getParentPid(tChild)))
458+
if (Process_isChildOf(tChild, parent->pid))
459+
return true;
460+
461+
return false;
462+
}
463+
464+
int Process_sameApplication(const Process* v1, const Process* v2) {
465+
if (v1->session != v2->session)
466+
return 0;
467+
468+
// we can compare pointers since the field user points to a hashtable entry
469+
if (v1->user != v2->user)
470+
return 0;
471+
472+
// TODO exe check
473+
474+
475+
if (isTransitiveChildOf(v1, v2))
476+
return 2;
477+
478+
if (isTransitiveChildOf(v2, v1))
479+
return 1;
480+
481+
return 0;
482+
}
483+
444484
long Process_compare(const void* v1, const void* v2) {
445485
const Process *p1, *p2;
446486
const Settings *settings = ((const Process*)v1)->settings;
@@ -506,3 +546,36 @@ long Process_compare(const void* v1, const void* v2) {
506546
return SPACESHIP_NUMBER(p1->pid, p2->pid);
507547
}
508548
}
549+
550+
void Process_mergeData(Process* p1, const Process* p2) {
551+
assert(p1->pl == p2->pl);
552+
553+
//TODO: handle thread (Process_isThread())
554+
555+
p1->percent_cpu += p2->percent_cpu;
556+
p1->percent_mem += p2->percent_mem;
557+
// keep COMM
558+
p1->majflt += p2->majflt;
559+
p1->minflt += p2->minflt;
560+
p1->m_resident += p2->m_resident;
561+
p1->m_size += p2->m_size;
562+
// store min NICE
563+
p1->nice = MINIMUM(p1->nice, p2->nice);
564+
p1->nlwp += p2->nlwp;
565+
// keep PGRP
566+
// keep PID
567+
// keep PPID
568+
p1->priority = MAXIMUM(p1->priority, p2->priority);
569+
// keep PROCESSOR
570+
// keep SESSION
571+
p1->starttime_ctime = MINIMUM(p1->starttime_ctime, p2->starttime_ctime);
572+
// keep STATE
573+
// keep ST_UID
574+
p1->time += p2->time;
575+
// keep TGID
576+
// keep TPGID
577+
// keep TTY_NR
578+
// keep USER
579+
580+
p1->merged += p2->merged;
581+
}

Process.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ typedef struct Process_ {
103103

104104
unsigned long int minflt;
105105
unsigned long int majflt;
106+
107+
unsigned int merged;
106108
} Process;
107109

108110
typedef struct ProcessFieldData_ {
@@ -115,6 +117,11 @@ typedef struct ProcessFieldData_ {
115117
// Implemented in platform-specific code:
116118
void Process_writeField(const Process* this, RichString* str, ProcessField field);
117119
long Process_compare(const void* v1, const void* v2);
120+
/* returns 1 on match and if v1 is the master process,
121+
* 2 on match and if v2 is the master process,
122+
* NULL else */
123+
int Process_sameApplication(const Process* p1, const Process* p2);
124+
void Process_mergeData(Process* p1, const Process* p2);
118125
void Process_delete(Object* cast);
119126
bool Process_isThread(const Process* this);
120127
extern ProcessFieldData Process_fields[];
@@ -123,10 +130,14 @@ extern char Process_pidFormat[20];
123130

124131
typedef Process*(*Process_New)(const struct Settings_*);
125132
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
133+
typedef int (*Process_SameApplication)(const Process*, const Process*);
134+
typedef void (*Process_MergeData)(Process*, const Process*);
126135

127136
typedef struct ProcessClass_ {
128137
const ObjectClass super;
129138
const Process_WriteField writeField;
139+
const Process_SameApplication sameApplication;
140+
const Process_MergeData mergeData;
130141
} ProcessClass;
131142

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

0 commit comments

Comments
 (0)