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 Jan 21, 2021
1 parent a56a35a commit 4056905
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 62 deletions.
7 changes: 7 additions & 0 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,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 @@ -470,6 +475,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 @@ -636,6 +642,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['='] = actionExpandOrCollapse;
keys['>'] = actionSetSortColumn;
keys['?'] = actionHelp;
keys['A'] = actionToggleMergeApplication;
keys['C'] = actionSetup;
keys['F'] = Action_follow;
keys['H'] = actionToggleUserlandThreads;
Expand Down
1 change: 1 addition & 0 deletions DisplayOptionsPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("Merge processes of same applications", &(settings->mergeApplications)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
Expand Down
67 changes: 67 additions & 0 deletions Process.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,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_appendAscii(str, CRT_colors[PROCESS_SHADOW], merged);
}
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
Expand Down Expand Up @@ -320,6 +325,11 @@ 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] );
RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
if (this->merged > 1) {
char merged[16];
xSnprintf(merged, sizeof(merged), "[%u] ", this->merged);
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], merged);
}
Process_writeCommand(this, attr, baseattr, str);
return;
}
Expand Down Expand Up @@ -488,6 +498,33 @@ int Process_pidCompare(const void* v1, const void* v2) {
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}

static bool isTransitiveChildOf(const Process* child, const Process* parent) {
assert(child->processList == parent->processList);

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;

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

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

return 0;
}

int Process_compare(const void* v1, const void* v2) {
const Process *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
Expand Down Expand Up @@ -567,3 +604,33 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField
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_virt += p2->m_virt;
// 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 @@ -109,6 +109,8 @@ typedef struct Process_ {
unsigned int tree_right;
unsigned int tree_depth;
unsigned int tree_index;

unsigned int merged;
} Process;

typedef struct ProcessFieldData_ {
Expand All @@ -122,6 +124,11 @@ typedef struct ProcessFieldData_ {
// Implemented in platform-specific code:
void Process_writeField(const Process* this, RichString* str, ProcessField field);
int 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 const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Expand All @@ -132,12 +139,16 @@ typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*);
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_CompareByKey compareByKey;
const Process_GetCommandStr getCommandStr;
const Process_SameApplication sameApplication;
const Process_MergeData mergeData;
} ProcessClass;

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

0 comments on commit 4056905

Please sign in to comment.