diff --git a/Action.c b/Action.c index 11e870093..522c50f98 100644 --- a/Action.c +++ b/Action.c @@ -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); @@ -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" }, @@ -684,4 +690,5 @@ void Action_setBindings(Htop_Action* keys) { keys['e'] = actionShowEnvScreen; keys['w'] = actionShowCommandScreen; keys['Z'] = actionTogglePauseProcessUpdate; + keys['A'] = actionToggleMergeApplication; } diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index e539906c0..b2804b3e8 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -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))); diff --git a/Process.c b/Process.c index 28cc03070..85b9f8d35 100644 --- a/Process.c +++ b/Process.c @@ -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 { @@ -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; } @@ -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; @@ -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; +} diff --git a/Process.h b/Process.h index c75ee501d..6b3365227 100644 --- a/Process.h +++ b/Process.h @@ -107,6 +107,8 @@ typedef struct Process_ { unsigned long int minflt; unsigned long int majflt; + + unsigned int merged; } Process; typedef struct ProcessFieldData_ { @@ -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[]; @@ -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)) diff --git a/ProcessList.c b/ProcessList.c index bbc728f0a..b9441b407 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -16,15 +16,13 @@ in the source distribution for its full text. ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { - this->processes = Vector_new(klass, true, DEFAULT_SIZE); + this->rawProcesses = Vector_new(klass, true, 140); + this->displayProcesses = Vector_new(klass, false, 140); this->processTable = Hashtable_new(140, false); this->usersTable = usersTable; this->pidMatchList = pidMatchList; this->userId = userId; - // tree-view auxiliary buffer - this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); - // set later by platform-specific code this->cpuCount = 0; @@ -60,8 +58,8 @@ void ProcessList_done(ProcessList* this) { } #endif Hashtable_delete(this->processTable); - Vector_delete(this->processes); - Vector_delete(this->processes2); + Vector_delete(this->displayProcesses); + Vector_delete(this->rawProcesses); } void ProcessList_setPanel(ProcessList* this, Panel* panel) { @@ -86,68 +84,68 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) { } void ProcessList_add(ProcessList* this, Process* p) { - assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1); + assert(Vector_indexOf(this->rawProcesses, p, Process_pidCompare) == -1); assert(Hashtable_get(this->processTable, p->pid) == NULL); p->processList = this; // highlighting processes found in first scan by first scan marked "far in the past" p->seenTs = this->scanTs; - Vector_add(this->processes, p); + Vector_add(this->rawProcesses, p); Hashtable_put(this->processTable, p->pid, p); - assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); + assert(Vector_indexOf(this->rawProcesses, p, Process_pidCompare) != -1); assert(Hashtable_get(this->processTable, p->pid) != NULL); - assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); + assert(Hashtable_count(this->processTable) == Vector_count(this->rawProcesses)); } void ProcessList_remove(ProcessList* this, Process* p) { - assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); + assert(Vector_indexOf(this->rawProcesses, p, Process_pidCompare) != -1); assert(Hashtable_get(this->processTable, p->pid) != NULL); Process* pp = Hashtable_remove(this->processTable, p->pid); assert(pp == p); (void)pp; unsigned int pid = p->pid; - int idx = Vector_indexOf(this->processes, p, Process_pidCompare); + int idx = Vector_indexOf(this->rawProcesses, p, Process_pidCompare); assert(idx != -1); if (idx >= 0) { - Vector_remove(this->processes, idx); + Vector_remove(this->rawProcesses, idx); } assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid; - assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); + assert(Hashtable_count(this->processTable) == Vector_count(this->rawProcesses)); } -static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) { +static void ProcessList_buildTree(Vector* sourceProcesses, Vector* targetProcesses, pid_t pid, int level, int indent, int direction, bool show) { Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE); - for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { - Process* process = (Process*) (Vector_get(this->processes, i)); + for (int i = Vector_size(sourceProcesses) - 1; i >= 0; i--) { + Process* process = (Process*) Vector_get(sourceProcesses, i); if (process->show && Process_isChildOf(process, pid)) { - process = (Process*) (Vector_take(this->processes, i)); + process = (Process*) Vector_take(sourceProcesses, i); Vector_add(children, process); } } int size = Vector_size(children); for (int i = 0; i < size; i++) { - Process* process = (Process*) (Vector_get(children, i)); + Process* process = (Process*) Vector_get(children, i); if (!show) { process->show = false; } - int s = Vector_size(this->processes2); + int s = Vector_size(targetProcesses); if (direction == 1) { - Vector_add(this->processes2, process); + Vector_add(targetProcesses, process); } else { - Vector_insert(this->processes2, 0, process); + Vector_insert(targetProcesses, 0, process); } - assert(Vector_size(this->processes2) == s+1); (void)s; + assert(Vector_size(targetProcesses) == s+1); (void)s; int nextIndent = indent | (1 << level); - ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false); + ProcessList_buildTree(sourceProcesses, targetProcesses, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false); if (i == size - 1) { process->indent = -nextIndent; @@ -158,34 +156,89 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i Vector_delete(children); } -static long ProcessList_treeProcessCompare(const void* v1, const void* v2) { +static long ProcessList_ProcessPIDCompare(const void* v1, const void* v2) { + const Process *p1 = (const Process*)v1; + const Process *p2 = (const Process*)v2; + + return SPACESHIP_NUMBER(p1->pid, p2->pid); +} + +static long ProcessList_ProcessSessionCompare(const void* v1, const void* v2) { const Process *p1 = (const Process*)v1; const Process *p2 = (const Process*)v2; - return p1->pid - p2->pid; + return SPACESHIP_NUMBER(p1->session, p2->session); } void ProcessList_sort(ProcessList* this) { + if (this->settings->mergeApplications) { + // Sort by session + Vector_insertionSortCustomCompare(this->rawProcesses, ProcessList_ProcessSessionCompare); + + Vector_prune(this->displayProcesses); + + for (int i = 0; i < Vector_size(this->rawProcesses); i++) { + Process* curProc = (Process*) Vector_get(this->rawProcesses, i); + curProc->merged = 1; + + // ignore threads : TODO + if (Process_isThread(curProc)) + continue; + + const int size = Vector_size(this->displayProcesses); + if (size == 0 || ((const Process*)Vector_get(this->displayProcesses, size - 1))->session < curProc->session) { + // first process with this session id -> just insert + Vector_add(this->displayProcesses, curProc); + continue; + } + + bool inserted = false; + // check in reverse if merge with already inserted process + for (int idx = size - 1; idx >= 0 && ((const Process*)Vector_get(this->displayProcesses, idx))->session == curProc->session; idx--) { + Process* testProc = (Process*) Vector_get(this->displayProcesses, idx); + int r = As_Process(testProc)->sameApplication(testProc, curProc); + if (r == 1) { + As_Process(testProc)->mergeData(testProc, curProc); + inserted = true; + break; + } + if (r == 2) { + As_Process(testProc)->mergeData(curProc, testProc); + Vector_add(this->displayProcesses, curProc); + inserted = true; + break; + } + } + if (!inserted) + Vector_add(this->displayProcesses, curProc); + } + + Vector_insertionSort(this->displayProcesses); + } + + Vector** processes = Vector_size(this->displayProcesses) > 0 ? &this->displayProcesses : &this->rawProcesses; + if (!this->settings->treeView) { - Vector_insertionSort(this->processes); + Vector_insertionSort(*processes); } else { // Save settings int direction = this->settings->direction; // Sort by PID - Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare); - int vsize = Vector_size(this->processes); + Vector_quickSortCustomCompare(*processes, ProcessList_ProcessPIDCompare); + int vsize = Vector_size(*processes); + Vector* processes2 = Vector_new((*processes)->type, (*processes)->owner, vsize); // Find all processes whose parent is not visible int size; - while ((size = Vector_size(this->processes))) { + while ((size = Vector_size(*processes))) { int i; for (i = 0; i < size; i++) { - Process* process = (Process*)(Vector_get(this->processes, i)); + Process* process = (Process*) Vector_get(*processes, i); // Immediately consume not shown processes if (!process->show) { - process = (Process*)(Vector_take(this->processes, i)); + process = (Process*) Vector_take(*processes, i); process->indent = 0; - Vector_add(this->processes2, process); - ProcessList_buildTree(this, process->pid, 0, 0, direction, false); + Vector_add(processes2, process); + ProcessList_buildTree(*processes, processes2, process->pid, 0, 0, direction, false); break; } pid_t ppid = Process_getParentPid(process); @@ -199,7 +252,7 @@ void ProcessList_sort(ProcessList* this) { while (l < r) { int c = (l + r) / 2; - pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid; + pid_t pid = ((Process*) Vector_get(*processes, c))->pid; if (ppid == pid) { break; } else if (ppid < pid) { @@ -210,22 +263,21 @@ void ProcessList_sort(ProcessList* this) { } // If parent not found, then construct the tree with this root if (l >= r) { - process = (Process*)(Vector_take(this->processes, i)); + process = (Process*) Vector_take(*processes, i); process->indent = 0; - Vector_add(this->processes2, process); - ProcessList_buildTree(this, process->pid, 0, 0, direction, process->showChildren); + Vector_add(processes2, process); + ProcessList_buildTree(*processes, processes2, process->pid, 0, 0, direction, process->showChildren); break; } } // There should be no loop in the process tree assert(i < size); } - assert(Vector_size(this->processes2) == vsize); (void)vsize; - assert(Vector_size(this->processes) == 0); + assert(Vector_size(processes2) == vsize); (void)vsize; + assert(Vector_size(*processes) == 0); // Swap listings around - Vector* t = this->processes; - this->processes = this->processes2; - this->processes2 = t; + Vector_delete(*processes); + *processes = processes2; } } @@ -250,26 +302,27 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) { } void ProcessList_expandTree(ProcessList* this) { - int size = Vector_size(this->processes); + int size = Vector_size(this->rawProcesses); for (int i = 0; i < size; i++) { - Process* process = (Process*) Vector_get(this->processes, i); + Process* process = (Process*) Vector_get(this->rawProcesses, i); process->showChildren = true; } } void ProcessList_rebuildPanel(ProcessList* this) { const char* incFilter = this->incFilter; + Vector* processes = Vector_size(this->displayProcesses) > 0 ? this->displayProcesses : this->rawProcesses; int currPos = Panel_getSelectedIndex(this->panel); pid_t currPid = this->following != -1 ? this->following : 0; int currScrollV = this->panel->scrollV; Panel_prune(this->panel); - int size = Vector_size(this->processes); + int size = Vector_size(processes); int idx = 0; for (int i = 0; i < size; i++) { bool hidden = false; - Process* p = (Process*) Vector_get(this->processes, i); + Process* p = (Process*) Vector_get(processes, i); if ( (!p->show) || (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) @@ -292,7 +345,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process* proc = (Process*) Hashtable_get(this->processTable, pid); *preExisting = proc; if (proc) { - assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1); + assert(Vector_indexOf(this->rawProcesses, proc, Process_pidCompare) != -1); assert(proc->pid == pid); } else { proc = constructor(this->settings); @@ -312,13 +365,17 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { } // mark all process as "dirty" - for (int i = 0; i < Vector_size(this->processes); i++) { - Process* p = (Process*) Vector_get(this->processes, i); + for (int i = 0; i < Vector_size(this->rawProcesses); i++) { + Process* p = (Process*) Vector_get(this->rawProcesses, i); p->updated = false; p->wasShown = p->show; p->show = true; + p->merged = 1; } + // we might remove processes, so pointers might dangle + Vector_prune(this->displayProcesses); + this->totalTasks = 0; this->userlandThreads = 0; this->kernelThreads = 0; @@ -336,12 +393,12 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { ProcessList_goThroughEntries(this, false); - for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { - Process* p = (Process*) Vector_get(this->processes, i); + for (int i = Vector_size(this->rawProcesses) - 1; i >= 0; i--) { + Process* p = (Process*) Vector_get(this->rawProcesses, i); if (p->tombTs > 0) { // remove tombed process if (this->scanTs >= p->tombTs) { - ProcessList_remove(this, p); + ProcessList_remove(this, p); } } else if (p->updated == false) { // process no longer exists diff --git a/ProcessList.h b/ProcessList.h index c47b446ea..1b86558a2 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -37,8 +37,8 @@ in the source distribution for its full text. typedef struct ProcessList_ { const Settings* settings; - Vector* processes; - Vector* processes2; + Vector* rawProcesses; /**< actually existing processes */ + Vector* displayProcesses; /**< processes to display */ Hashtable* processTable; UsersTable* usersTable; @@ -98,6 +98,10 @@ void ProcessList_expandTree(ProcessList* this); void ProcessList_rebuildPanel(ProcessList* this); +static inline const Process* ProcessList_findProcess(const ProcessList* this, pid_t pid) { + return (Process*) Hashtable_get(this->processTable, pid); +} + Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate); diff --git a/Settings.c b/Settings.c index 158264710..947f07d82 100644 --- a/Settings.c +++ b/Settings.c @@ -141,6 +141,8 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo this->direction = atoi(option[1]); } else if (String_eq(option[0], "tree_view")) { this->treeView = atoi(option[1]); + } else if (String_eq(option[0], "merge_applications")) { + this->mergeApplications = atoi(option[1]); } else if (String_eq(option[0], "hide_kernel_threads")) { this->hideKernelThreads = atoi(option[1]); } else if (String_eq(option[0], "hide_userland_threads")) { @@ -272,6 +274,7 @@ bool Settings_write(Settings* this) { fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges); fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs); fprintf(fd, "tree_view=%d\n", (int) this->treeView); + fprintf(fd, "merge_applications=%d\n", (int) this->mergeApplications); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne); @@ -303,6 +306,7 @@ Settings* Settings_new(int initialCpuCount) { this->hideKernelThreads = false; this->hideUserlandThreads = false; this->treeView = false; + this->mergeApplications = false; this->highlightBaseName = false; this->highlightMegabytes = false; this->detailedCPUTime = false; diff --git a/Settings.h b/Settings.h index 93b98bd8a..71afe854a 100644 --- a/Settings.h +++ b/Settings.h @@ -39,6 +39,7 @@ typedef struct Settings_ { bool showCPUUsage; bool showCPUFrequency; bool treeView; + bool mergeApplications; bool showProgramPath; bool shadowOtherUsers; bool showThreadNames; diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 2fec52f1d..a464b4fb1 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -112,6 +112,7 @@ ProcessFieldData Process_fields[] = { [M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, }, [CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, }, [SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, }, + [EXE] = { .name = "EXE", .title = " Executable ", .description = "Path to executable", .flags = 0, }, [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, }; @@ -144,6 +145,7 @@ void Process_delete(Object* cast) { #ifdef HAVE_OPENVZ free(this->ctid); #endif + free(this->exe); free(this->secattr); free(this->ttyDevice); free(this); @@ -287,6 +289,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff); break; case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break; + case EXE: xSnprintf(buffer, n, "%-30s ", lp->exe ? lp->exe : "?"); break; default: Process_writeField(this, str, field); return; @@ -383,6 +386,8 @@ static long LinuxProcess_compare(const void* v1, const void* v2) { return SPACESHIP_NUMBER(p2->ctxt_diff, p1->ctxt_diff); case SECATTR: return SPACESHIP_NULLSTR(p1->secattr, p2->secattr); + case EXE: + return SPACESHIP_NULLSTR(p1->exe, p2->exe); default: return Process_compare(v1, v2); } @@ -392,6 +397,77 @@ bool Process_isThread(const Process* this) { return (Process_isUserlandThread(this) || Process_isKernelThread(this)); } +static int LinuxProcess_sameApplication(const Process* p1, const Process* p2) { + const LinuxProcess* lp1 = (const LinuxProcess*) p1; + const LinuxProcess* lp2 = (const LinuxProcess*) p2; + const char* exe1 = lp1->exe; + const char* exe2 = lp2->exe; + + if (!exe1 || !exe2 || !String_eq(exe1, exe2)) + return 0; + + return Process_sameApplication(p1, p2); +} + +static void LinuxProcess_mergeData(Process* p1, const Process* p2) { + //LinuxProcess* lp1 = (LinuxProcess*) p1; + //const LinuxProcess* lp2 = (const LinuxProcess*) p2; + + // TODO + + /* + * TTY_NR + * CMINFLT + * CMAJFLT + * M_DRS + * M_DT + * M_LRS + * M_TRS + * M_SHARE + * M_PSS + * M_SWAP + * M_PSSWP + * UTIME + * STIME + * CUTIME + * CSTIME + * #ifdef HAVE_TASKSTATS + * RCHAR + * WCHAR + * SYSCR + * SYSCW + * RBYTES + * WBYTES + * CNCLWB + * IO_READ_RATE + * IO_WRITE_RATE + * IO_RATE + * #endif + * #ifdef HAVE_OPENVZ + * CTID + * VPID + * #endif + * #ifdef HAVE_VSERVER + * VXID + * #endif + * #ifdef HAVE_CGROUP + * CGROUP + * #endif + * OOM + * IO_PRIORITY + * #ifdef HAVE_DELAYACCT + * PERCENT_CPU_DELAY + * PERCENT_IO_DELAY + * PERCENT_SWAP_DELAY + * #endif + * CTXT + * SECATTR + * EXE + */ + + Process_mergeData(p1, p2); +} + const ProcessClass LinuxProcess_class = { .super = { .extends = Class(Process), @@ -399,5 +475,7 @@ const ProcessClass LinuxProcess_class = { .delete = Process_delete, .compare = LinuxProcess_compare }, - .writeField = LinuxProcess_writeField + .writeField = LinuxProcess_writeField, + .sameApplication = LinuxProcess_sameApplication, + .mergeData = LinuxProcess_mergeData }; diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 856b7bee4..0a6039ce1 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -95,7 +95,8 @@ typedef enum LinuxProcessFields { M_PSSWP = 121, CTXT = 122, SECATTR = 123, - LAST_PROCESSFIELD = 124, + EXE = 124, + LAST_PROCESSFIELD = 125, } LinuxProcessField; typedef struct LinuxProcess_ { @@ -153,6 +154,7 @@ typedef struct LinuxProcess_ { unsigned long ctxt_total; unsigned long ctxt_diff; char* secattr; + char* exe; } LinuxProcess; #define Process_isKernelThread(_process) (((const LinuxProcess*)(_process))->isKernelThread) diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 63b77326c..611b7f9e3 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -915,6 +915,23 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna return true; } +static void LinuxProcessList_readExe(LinuxProcess* process, const char* dirname, const char* name) { + char filename[MAX_NAME+1]; + xSnprintf(filename, MAX_NAME, "%s/%s/exe", dirname, name); + + char exePath[4096]; + ssize_t r = readlink(filename, exePath, sizeof(exePath) - 1); + if (r < 0) { + free(process->exe); + process->exe = NULL; + return; + } + + exePath[MINIMUM((size_t)r, sizeof(exePath) - 1)] = '\0'; + free(process->exe); + process->exe = strdup(exePath); +} + static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned int tty_nr) { unsigned int maj = major(tty_nr); unsigned int min = minor(tty_nr); @@ -1091,6 +1108,8 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* goto errorReadingProcess; } + LinuxProcessList_readExe(lp, dirname, name); + Process_fillStarttimeBuffer(proc); ProcessList_add(pl, proc);