Skip to content

Commit

Permalink
Add option to merge processes of same application
Browse files Browse the repository at this point in the history
Experimental - Linux only

Closes: htop-dev#301
  • Loading branch information
cgzones committed Nov 16, 2020
1 parent 77c80f1 commit 66e72b4
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 57 deletions.
7 changes: 7 additions & 0 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ static Htop_Reaction actionToggleTreeView(State* st) {
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}

static Htop_Reaction actionToggleMergeApplication(State* st) {
st->settings->mergeApplications = !st->settings->mergeApplications;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}

static Htop_Reaction actionIncFilter(State* st) {
IncSet* inc = ((MainPanel*)st->panel)->inc;
IncSet_activate(inc, INC_FILTER, st->panel);
Expand Down Expand Up @@ -477,6 +482,7 @@ static const struct {
{ .key = " x: ", .info = "list file locks of process" },
{ .key = " s: ", .info = "trace syscalls with strace" },
{ .key = " w: ", .info = "wrap process command in multiple lines" },
{ .key = " A: ", .info = "Merge processes of same application" },
{ .key = " F2 C S: ", .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" },
Expand Down Expand Up @@ -684,4 +690,5 @@ void Action_setBindings(Htop_Action* keys) {
keys['e'] = actionShowEnvScreen;
keys['w'] = actionShowCommandScreen;
keys['Z'] = actionTogglePauseProcessUpdate;
keys['A'] = actionToggleMergeApplication;
}
1 change: 1 addition & 0 deletions DisplayOptionsPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*

Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Merge processes of same applications"), &(settings->mergeApplications)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
Expand Down
68 changes: 68 additions & 0 deletions Process.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
if (!this->settings->treeView || this->indent == 0) {
if (this->merged > 1) {
char merged[16];
xSnprintf(merged, sizeof(merged), "[%u] ", this->merged);
RichString_append(str, CRT_colors[PROCESS_SHADOW], merged);
}
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
Expand Down Expand Up @@ -313,6 +318,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
if (this->merged > 1) {
char merged[16];
xSnprintf(merged, sizeof(merged), "[%u] ", this->merged);
RichString_append(str, CRT_colors[PROCESS_SHADOW], merged);
}
Process_writeCommand(this, attr, baseattr, str);
return;
}
Expand Down Expand Up @@ -477,6 +487,34 @@ long Process_pidCompare(const void* v1, const void* v2) {
return (p1->pid - p2->pid);
}

static bool isTransitiveChildOf(const Process* child, const Process* parent) {
for (const Process* tChild = child; tChild; tChild = ProcessList_findProcess(parent->processList, Process_getParentPid(tChild)))
if (Process_isChildOf(tChild, parent->pid))
return true;

return false;
}

int Process_sameApplication(const Process* v1, const Process* v2) {
if (v1->session != v2->session)
return 0;

// we can compare pointers since the field user points to a hashtable entry
if (v1->user != v2->user)
return 0;

// TODO exe check


if (isTransitiveChildOf(v1, v2))
return 2;

if (isTransitiveChildOf(v2, v1))
return 1;

return 0;
}

long Process_compare(const void* v1, const void* v2) {
const Process *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
Expand Down Expand Up @@ -542,3 +580,33 @@ long Process_compare(const void* v1, const void* v2) {
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
}

void Process_mergeData(Process* p1, const Process* p2) {

p1->percent_cpu += p2->percent_cpu;
p1->percent_mem += p2->percent_mem;
// keep COMM
p1->majflt += p2->majflt;
p1->minflt += p2->minflt;
p1->m_resident += p2->m_resident;
p1->m_size += p2->m_size;
// store min NICE
p1->nice = MINIMUM(p1->nice, p2->nice);
p1->nlwp += p2->nlwp;
// keep PGRP
// keep PID
// keep PPID
p1->priority = MAXIMUM(p1->priority, p2->priority);
// keep PROCESSOR
// keep SESSION
p1->starttime_ctime = MINIMUM(p1->starttime_ctime, p2->starttime_ctime);
// keep STATE
// keep ST_UID
p1->time += p2->time;
// keep TGID
// keep TPGID
// keep TTY_NR
// keep USER

p1->merged += p2->merged;
}
11 changes: 11 additions & 0 deletions Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ typedef struct Process_ {

unsigned long int minflt;
unsigned long int majflt;

unsigned int merged;
} Process;

typedef struct ProcessFieldData_ {
Expand All @@ -119,6 +121,11 @@ typedef struct ProcessFieldData_ {
// Implemented in platform-specific code:
void Process_writeField(const Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2);
/* returns 1 on match and if v1 is the master process,
* 2 on match and if v2 is the master process,
* 0 else */
int Process_sameApplication(const Process* p1, const Process* p2);
void Process_mergeData(Process* p1, const Process* p2);
void Process_delete(Object* cast);
bool Process_isThread(const Process* this);
extern ProcessFieldData Process_fields[];
Expand All @@ -127,10 +134,14 @@ extern char Process_pidFormat[20];

typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef int (*Process_SameApplication)(const Process*, const Process*);
typedef void (*Process_MergeData)(Process*, const Process*);

typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
const Process_SameApplication sameApplication;
const Process_MergeData mergeData;
} ProcessClass;

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

0 comments on commit 66e72b4

Please sign in to comment.