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 4, 2020
1 parent fd86564 commit 433b4f5
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 92 deletions.
7 changes: 7 additions & 0 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,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 @@ -437,6 +442,7 @@ static const struct { const char* key; const char* info; } helpRight[] = {
{ .key = " l: ", .info = "list open files with lsof" },
{ .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 @@ -632,4 +638,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
2 changes: 1 addition & 1 deletion MainPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {

if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl;
const ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
Expand Down
78 changes: 76 additions & 2 deletions Process.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,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_TREE], merged);
}
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
Expand Down Expand Up @@ -305,6 +310,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_TREE], merged);
}
Process_writeCommand(this, attr, baseattr, str);
return;
}
Expand Down Expand Up @@ -441,6 +451,36 @@ long Process_pidCompare(const void* v1, const void* v2) {
return (p1->pid - p2->pid);
}

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

for (const Process* tChild = child; tChild; tChild = ProcessList_findProcess(parent->pl, 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 All @@ -451,6 +491,7 @@ long Process_compare(const void* v1, const void* v2) {
p2 = (const Process*)v1;
p1 = (const Process*)v2;
}
assert(p1->pl == p2->pl);
switch (settings->sortKey) {
case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
Expand All @@ -471,7 +512,7 @@ long Process_compare(const void* v1, const void* v2) {
case NLWP:
return (p1->nlwp - p2->nlwp);
case PGRP:
return (p1->pgrp - p2->pgrp);
return (long)p1->pgrp - (long)p2->pgrp;
case PID:
return (p1->pid - p2->pid);
case PPID:
Expand All @@ -481,7 +522,7 @@ long Process_compare(const void* v1, const void* v2) {
case PROCESSOR:
return (p1->processor - p2->processor);
case SESSION:
return (p1->session - p2->session);
return (long)p1->session - (long)p2->session;
case STARTTIME: {
if (p1->starttime_ctime == p2->starttime_ctime)
return (p1->pid - p2->pid);
Expand All @@ -506,3 +547,36 @@ long Process_compare(const void* v1, const void* v2) {
return (p1->pid - p2->pid);
}
}

void Process_mergeData(Process* p1, const Process* p2) {
assert(p1->pl == p2->pl);

//TODO: handle thread (Process_isThread())

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;
}
13 changes: 13 additions & 0 deletions Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ typedef struct ProcessPidColumn_ {
} ProcessPidColumn;

struct Settings_;
struct ProcessList_;

typedef struct Process_ {
Object super;

const struct Settings_* settings;
const struct ProcessList_* pl;

unsigned long long int time;
pid_t pid;
Expand Down Expand Up @@ -101,6 +103,8 @@ typedef struct Process_ {

unsigned long int minflt;
unsigned long int majflt;

unsigned int merged;
} Process;

typedef struct ProcessFieldData_ {
Expand All @@ -113,6 +117,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,
* NULL 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 @@ -121,10 +130,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 433b4f5

Please sign in to comment.