From a7c2aedcecd3763464e83f80bd6c4f1216f58428 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Thu, 8 Nov 2007 23:23:01 +0000 Subject: [PATCH] Improve construction of tree view, properly nesting threads. Add CPU affinity screen ('a' key). BUGFIX: Correct display of TPGID field. Add TGID field. --- ChangeLog | 8 ++++++- CheckItem.c | 22 ++++++++++++++++--- CheckItem.h | 9 ++++++-- ColorsPanel.c | 13 +++++------ ColorsPanel.h | 1 - DisplayOptionsPanel.c | 22 +++++++++---------- DisplayOptionsPanel.h | 2 +- Makefile.am | 4 ++-- Process.c | 24 +++++++++++++++----- Process.h | 10 +++++++-- ProcessList.c | 51 ++++++++++++++++++++++--------------------- SignalsPanel.c | 4 ++-- SignalsPanel.h | 2 +- htop.1 | 3 +++ htop.c | 40 +++++++++++++++++++++++++++++++-- htop.h | 1 + 16 files changed, 150 insertions(+), 66 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3a35f2bf..ae4d9f7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ -What's new in version 0.6.7 +What's new in version 0.7 + +* CPU affinity configuration ('a' key) +* Improve display of tree view, properly nesting + threads of the same app based on TGID. +* BUGFIX: Correct display of TPGID field +* Add TGID field * BUGFIX: Don't crash with invalid command-line flags (thanks to Nico Golde for the report) * Fix GCC 4.3 compilation issues diff --git a/CheckItem.c b/CheckItem.c index 83261237..45c0b22b 100644 --- a/CheckItem.c +++ b/CheckItem.c @@ -16,7 +16,8 @@ in the source distribution for its full text. typedef struct CheckItem_ { Object super; char* text; - bool* value; + bool value; + bool* ref; } CheckItem; }*/ @@ -27,13 +28,14 @@ char* CHECKITEM_CLASS = "CheckItem"; #define CHECKITEM_CLASS NULL #endif -CheckItem* CheckItem_new(char* text, bool* value) { +CheckItem* CheckItem_new(char* text, bool* ref, bool value) { CheckItem* this = malloc(sizeof(CheckItem)); Object_setClass(this, CHECKITEM_CLASS); ((Object*)this)->display = CheckItem_display; ((Object*)this)->delete = CheckItem_delete; this->text = text; this->value = value; + this->ref = ref; return this; } @@ -45,11 +47,25 @@ void CheckItem_delete(Object* cast) { free(this); } +void CheckItem_set(CheckItem* this, bool value) { + if (this->ref) + *(this->ref) = value; + else + this->value = value; +} + +bool CheckItem_get(CheckItem* this) { + if (this->ref) + return *(this->ref); + else + return this->value; +} + void CheckItem_display(Object* cast, RichString* out) { CheckItem* this = (CheckItem*)cast; assert (this != NULL); RichString_write(out, CRT_colors[CHECK_BOX], "["); - if (*(this->value)) + if (CheckItem_get(this)) RichString_append(out, CRT_colors[CHECK_MARK], "x"); else RichString_append(out, CRT_colors[CHECK_MARK], " "); diff --git a/CheckItem.h b/CheckItem.h index e1ad4c9b..359a0ef7 100644 --- a/CheckItem.h +++ b/CheckItem.h @@ -18,7 +18,8 @@ in the source distribution for its full text. typedef struct CheckItem_ { Object super; char* text; - bool* value; + bool value; + bool* ref; } CheckItem; @@ -28,10 +29,14 @@ extern char* CHECKITEM_CLASS; #define CHECKITEM_CLASS NULL #endif -CheckItem* CheckItem_new(char* text, bool* value); +CheckItem* CheckItem_new(char* text, bool* ref, bool value); void CheckItem_delete(Object* cast); +void CheckItem_set(CheckItem* this, bool value); + +bool CheckItem_get(CheckItem* this); + void CheckItem_display(Object* cast, RichString* out); #endif diff --git a/ColorsPanel.c b/ColorsPanel.c index e62583cd..00868457 100644 --- a/ColorsPanel.c +++ b/ColorsPanel.c @@ -23,7 +23,6 @@ typedef struct ColorsPanel_ { Settings* settings; ScreenManager* scr; - bool check[5]; } ColorsPanel; }*/ @@ -50,10 +49,9 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) { Panel_setHeader(super, "Colors"); for (int i = 0; ColorSchemes[i] != NULL; i++) { - Panel_add(super, (Object*) CheckItem_new(String_copy(ColorSchemes[i]), &(this->check[i]))); - this->check[i] = false; + Panel_add(super, (Object*) CheckItem_new(String_copy(ColorSchemes[i]), NULL, false)); } - this->check[settings->colorScheme] = true; + CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true); return this; } @@ -75,10 +73,9 @@ HandlerResult ColorsPanel_EventHandler(Panel* super, int ch) { case 0x0d: case KEY_ENTER: case ' ': - for (int i = 0; ColorSchemes[i] != NULL; i++) { - this->check[i] = false; - } - this->check[mark] = true; + for (int i = 0; ColorSchemes[i] != NULL; i++) + CheckItem_set((CheckItem*)Panel_get(super, i), false); + CheckItem_set((CheckItem*)Panel_get(super, mark), true); this->settings->colorScheme = mark; result = HANDLED; } diff --git a/ColorsPanel.h b/ColorsPanel.h index 2cf29477..ed60a8c6 100644 --- a/ColorsPanel.h +++ b/ColorsPanel.h @@ -25,7 +25,6 @@ typedef struct ColorsPanel_ { Settings* settings; ScreenManager* scr; - bool check[5]; } ColorsPanel; diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 119b4e64..fa40283f 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -28,17 +28,17 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* this->settings = settings; this->scr = scr; - super->eventHandler = DisplayOptionsPanel_EventHandler; + super->eventHandler = DisplayOptionsPanel_eventHandler; Panel_setHeader(super, "Display options"); - Panel_add(super, (Object*) CheckItem_new(String_copy("Tree view"), &(settings->pl->treeView))); - Panel_add(super, (Object*) CheckItem_new(String_copy("Shadow other users' processes"), &(settings->pl->shadowOtherUsers))); - Panel_add(super, (Object*) CheckItem_new(String_copy("Hide kernel threads"), &(settings->pl->hideKernelThreads))); - Panel_add(super, (Object*) CheckItem_new(String_copy("Hide userland threads"), &(settings->pl->hideUserlandThreads))); - Panel_add(super, (Object*) CheckItem_new(String_copy("Highlight program \"basename\""), &(settings->pl->highlightBaseName))); - Panel_add(super, (Object*) CheckItem_new(String_copy("Highlight megabytes in memory counters"), &(settings->pl->highlightMegabytes))); - Panel_add(super, (Object*) CheckItem_new(String_copy("Leave a margin around header"), &(settings->header->margin))); - Panel_add(super, (Object*) CheckItem_new(String_copy("Split System Time into System/IO-Wait/Hard-IRQ/Soft-IRQ"), &(settings->pl->expandSystemTime))); + Panel_add(super, (Object*) CheckItem_new(String_copy("Tree view"), &(settings->pl->treeView), false)); + Panel_add(super, (Object*) CheckItem_new(String_copy("Shadow other users' processes"), &(settings->pl->shadowOtherUsers), false)); + Panel_add(super, (Object*) CheckItem_new(String_copy("Hide kernel threads"), &(settings->pl->hideKernelThreads), false)); + Panel_add(super, (Object*) CheckItem_new(String_copy("Hide userland threads"), &(settings->pl->hideUserlandThreads), false)); + Panel_add(super, (Object*) CheckItem_new(String_copy("Highlight program \"basename\""), &(settings->pl->highlightBaseName), false)); + Panel_add(super, (Object*) CheckItem_new(String_copy("Highlight megabytes in memory counters"), &(settings->pl->highlightMegabytes), false)); + Panel_add(super, (Object*) CheckItem_new(String_copy("Leave a margin around header"), &(settings->header->margin), false)); + Panel_add(super, (Object*) CheckItem_new(String_copy("Split System Time into System/IO-Wait/Hard-IRQ/Soft-IRQ"), &(settings->pl->expandSystemTime), false)); return this; } @@ -49,7 +49,7 @@ void DisplayOptionsPanel_delete(Object* object) { free(this); } -HandlerResult DisplayOptionsPanel_EventHandler(Panel* super, int ch) { +HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { DisplayOptionsPanel* this = (DisplayOptionsPanel*) super; HandlerResult result = IGNORED; @@ -60,7 +60,7 @@ HandlerResult DisplayOptionsPanel_EventHandler(Panel* super, int ch) { case 0x0d: case KEY_ENTER: case ' ': - *(selected->value) = ! *(selected->value); + CheckItem_set(selected, ! (CheckItem_get(selected)) ); result = HANDLED; } diff --git a/DisplayOptionsPanel.h b/DisplayOptionsPanel.h index 87faaad8..7c32b0cd 100644 --- a/DisplayOptionsPanel.h +++ b/DisplayOptionsPanel.h @@ -25,7 +25,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* void DisplayOptionsPanel_delete(Object* object); -HandlerResult DisplayOptionsPanel_EventHandler(Panel* super, int ch); +HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch); #endif diff --git a/Makefile.am b/Makefile.am index b7301cec..d1ef6e38 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,7 +17,7 @@ DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ SignalItem.c SignalsPanel.c String.c SwapMeter.c TasksMeter.c TraceScreen.c \ -UptimeMeter.c UsersTable.c Vector.c AvailableColumnsPanel.c +UptimeMeter.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \ @@ -26,7 +26,7 @@ Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \ Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \ ScreenManager.h Settings.h SignalItem.h SignalsPanel.h String.h \ SwapMeter.h TasksMeter.h TraceScreen.h UptimeMeter.h UsersTable.h Vector.h \ -Process.h +Process.h AffinityPanel.h SUFFIXES = .h diff --git a/Process.c b/Process.c index 71840f4f..ed49bace 100644 --- a/Process.c +++ b/Process.c @@ -25,6 +25,7 @@ in the source distribution for its full text. #include #include #include +#include // This works only with glibc 2.1+. On earlier versions // the behavior is similar to have a hardcoded page size. @@ -41,7 +42,7 @@ typedef enum ProcessField_ { STIME, CUTIME, CSTIME, PRIORITY, NICE, ITREALVALUE, STARTTIME, VSIZE, RSS, RLIM, STARTCODE, ENDCODE, STARTSTACK, KSTKESP, KSTKEIP, SIGNAL, BLOCKED, SSIGIGNORE, SIGCATCH, WCHAN, NSWAP, CNSWAP, EXIT_SIGNAL, PROCESSOR, M_SIZE, M_RESIDENT, M_SHARE, M_TRS, M_DRS, M_LRS, M_DT, ST_UID, PERCENT_CPU, PERCENT_MEM, - USER, TIME, NLWP, + USER, TIME, NLWP, TGID #ifdef HAVE_OPENVZ VEID, VPID, #endif @@ -65,7 +66,8 @@ typedef struct Process_ { unsigned int pgrp; unsigned int session; unsigned int tty_nr; - unsigned int tpgid; + unsigned int tgid; + int tpgid; unsigned long int flags; #ifdef DEBUG unsigned long int minflt; @@ -127,7 +129,7 @@ char* PROCESS_CLASS = "Process"; #endif char *Process_fieldNames[] = { - "", "PID", "Command", "STATE", "PPID", "PGRP", "SESSION", "TTY_NR", "TPGID", "FLAGS", "MINFLT", "CMINFLT", "MAJFLT", "CMAJFLT", "UTIME", "STIME", "CUTIME", "CSTIME", "PRIORITY", "NICE", "ITREALVALUE", "STARTTIME", "VSIZE", "RSS", "RLIM", "STARTCODE", "ENDCODE", "STARTSTACK", "KSTKESP", "KSTKEIP", "SIGNAL", "BLOCKED", "SIGIGNORE", "SIGCATCH", "WCHAN", "NSWAP", "CNSWAP", "EXIT_SIGNAL", "PROCESSOR", "M_SIZE", "M_RESIDENT", "M_SHARE", "M_TRS", "M_DRS", "M_LRS", "M_DT", "ST_UID", "PERCENT_CPU", "PERCENT_MEM", "USER", "TIME", "NLWP", + "", "PID", "Command", "STATE", "PPID", "PGRP", "SESSION", "TTY_NR", "TPGID", "FLAGS", "MINFLT", "CMINFLT", "MAJFLT", "CMAJFLT", "UTIME", "STIME", "CUTIME", "CSTIME", "PRIORITY", "NICE", "ITREALVALUE", "STARTTIME", "VSIZE", "RSS", "RLIM", "STARTCODE", "ENDCODE", "STARTSTACK", "KSTKESP", "KSTKEIP", "SIGNAL", "BLOCKED", "SIGIGNORE", "SIGCATCH", "WCHAN", "NSWAP", "CNSWAP", "EXIT_SIGNAL", "PROCESSOR", "M_SIZE", "M_RESIDENT", "M_SHARE", "M_TRS", "M_DRS", "M_LRS", "M_DT", "ST_UID", "PERCENT_CPU", "PERCENT_MEM", "USER", "TIME", "NLWP", "TGID", #ifdef HAVE_OPENVZ "VEID", "VPID", #endif @@ -193,6 +195,16 @@ void Process_setPriority(Process* this, int priority) { } } +unsigned long Process_getAffinity(Process* this) { + unsigned long mask = 0; + sched_getaffinity(this->pid, sizeof(unsigned long), (cpu_set_t*) &mask); + return mask; +} + +void Process_setAffinity(Process* this, unsigned long mask) { + sched_setaffinity(this->pid, sizeof(unsigned long), (cpu_set_t*) &mask); +} + void Process_sendSignal(Process* this, int signal) { kill(this->pid, signal); } @@ -281,7 +293,8 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) { case PGRP: snprintf(buffer, n, "%5u ", this->pgrp); break; case SESSION: snprintf(buffer, n, "%5u ", this->session); break; case TTY_NR: snprintf(buffer, n, "%5u ", this->tty_nr); break; - case TPGID: snprintf(buffer, n, "%5u ", this->tpgid); break; + case TGID: snprintf(buffer, n, "%5u ", this->tgid); break; + case TPGID: snprintf(buffer, n, "%5d ", this->tpgid); break; case PROCESSOR: snprintf(buffer, n, "%3d ", this->processor+1); break; case NLWP: snprintf(buffer, n, "%4ld ", this->nlwp); break; case COMM: { @@ -462,7 +475,8 @@ char* Process_printField(ProcessField field) { case PGRP: return " PGRP "; case SESSION: return " SESN "; case TTY_NR: return " TTY "; - case TPGID: return " TGID "; + case TGID: return " TGID "; + case TPGID: return "TPGID "; case COMM: return "Command "; case STATE: return "S "; case PRIORITY: return "PRI "; diff --git a/Process.h b/Process.h index 37a20cb9..778d4644 100644 --- a/Process.h +++ b/Process.h @@ -28,6 +28,7 @@ in the source distribution for its full text. #include #include #include +#include // This works only with glibc 2.1+. On earlier versions // the behavior is similar to have a hardcoded page size. @@ -43,7 +44,7 @@ typedef enum ProcessField_ { STIME, CUTIME, CSTIME, PRIORITY, NICE, ITREALVALUE, STARTTIME, VSIZE, RSS, RLIM, STARTCODE, ENDCODE, STARTSTACK, KSTKESP, KSTKEIP, SIGNAL, BLOCKED, SSIGIGNORE, SIGCATCH, WCHAN, NSWAP, CNSWAP, EXIT_SIGNAL, PROCESSOR, M_SIZE, M_RESIDENT, M_SHARE, M_TRS, M_DRS, M_LRS, M_DT, ST_UID, PERCENT_CPU, PERCENT_MEM, - USER, TIME, NLWP, + USER, TIME, NLWP, TGID, #ifdef HAVE_OPENVZ VEID, VPID, #endif @@ -67,7 +68,8 @@ typedef struct Process_ { unsigned int pgrp; unsigned int session; unsigned int tty_nr; - unsigned int tpgid; + unsigned int tgid; + int tpgid; unsigned long int flags; #ifdef DEBUG unsigned long int minflt; @@ -141,6 +143,10 @@ void Process_toggleTag(Process* this); void Process_setPriority(Process* this, int priority); +unsigned long Process_getAffinity(Process* this); + +void Process_setAffinity(Process* this, unsigned long mask); + void Process_sendSignal(Process* this, int signal); #define ONE_K 1024 diff --git a/ProcessList.c b/ProcessList.c index fa550cb5..4b22bc08 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -333,7 +333,7 @@ static void ProcessList_buildTree(ProcessList* this, int pid, int level, int ind for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { Process* process = (Process*) (Vector_get(this->processes, i)); - if (process->ppid == pid) { + if (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid)) { Process* process = (Process*) (Vector_take(this->processes, i)); Vector_add(children, process); } @@ -360,26 +360,34 @@ void ProcessList_sort(ProcessList* this) { if (!this->treeView) { Vector_sort(this->processes); } else { + // Save settings int direction = this->direction; int sortKey = this->sortKey; + // Sort by PID this->sortKey = PID; this->direction = 1; Vector_sort(this->processes); + // Restore settings this->sortKey = sortKey; this->direction = direction; + // Take PID 1 as root and add to the new listing int vsize = Vector_size(this->processes); Process* init = (Process*) (Vector_take(this->processes, 0)); assert(init->pid == 1); init->indent = 0; Vector_add(this->processes2, init); + // Recursively empty list ProcessList_buildTree(this, init->pid, 0, 0, direction); + // Add leftovers while (Vector_size(this->processes)) { Process* p = (Process*) (Vector_take(this->processes, 0)); p->indent = 0; Vector_add(this->processes2, p); + ProcessList_buildTree(this, p->pid, 0, 0, direction); } assert(Vector_size(this->processes2) == vsize); (void)vsize; assert(Vector_size(this->processes) == 0); + // Swap listings around Vector* t = this->processes; this->processes = this->processes2; this->processes2 = t; @@ -408,7 +416,7 @@ static int ProcessList_readStatFile(ProcessList* this, Process *proc, FILE *f, c #ifdef DEBUG_PROC int num = ProcessList_read(this, location, - "%c %u %u %u %u %u %lu %lu %lu %lu " + "%c %u %u %u %u %d %lu %lu %lu %lu " "%lu %lu %lu %ld %ld %ld %ld %ld %ld " "%lu %lu %ld %lu %lu %lu %lu %lu " "%lu %lu %lu %lu %lu %lu %lu %lu " @@ -426,7 +434,7 @@ static int ProcessList_readStatFile(ProcessList* this, Process *proc, FILE *f, c #else long int uzero; int num = ProcessList_read(this, location, - "%c %u %u %u %u %u %lu %lu %lu %lu " + "%c %u %u %u %u %d %lu %lu %lu %lu " "%lu %lu %lu %ld %ld %ld %ld %ld %ld " "%lu %lu %ld %lu %lu %lu %lu %lu " "%lu %lu %lu %lu %lu %lu %lu %lu " @@ -454,10 +462,12 @@ static int ProcessList_readStatFile(ProcessList* this, Process *proc, FILE *f, c bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, char* name) { char statusfilename[MAX_NAME+1]; statusfilename[MAX_NAME] = '\0'; - /* + bool success = false; char buffer[256]; buffer[255] = '\0'; + + // We need to parse the status file just for tgid, which is missing in stat. snprintf(statusfilename, MAX_NAME, "%s/%s/status", dirname, name); FILE* status = ProcessList_fopen(this, statusfilename, "r"); if (status) { @@ -465,12 +475,11 @@ bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, char* ok = fgets(buffer, 255, status); if (!ok) break; - if (String_startsWith(buffer, "Uid:")) { - int uid1, uid2, uid3, uid4; - // TODO: handle other uid's. - int ok = ProcessList_read(this, buffer, "Uid:\t%d\t%d\t%d\t%d", &uid1, &uid2, &uid3, &uid4); + if (String_startsWith(buffer, "Tgid:")) { + int tgid; + int ok = ProcessList_read(this, buffer, "Tgid:\t%d", &tgid); if (ok >= 1) { - proc->st_uid = uid1; + proc->tgid = tgid; success = true; } break; @@ -478,19 +487,13 @@ bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, } fclose(status); } - if (!success) { - */ - snprintf(statusfilename, MAX_NAME, "%s/%s", dirname, name); - struct stat sstat; - int statok = stat(statusfilename, &sstat); - if (statok == -1) - return false; - proc->st_uid = sstat.st_uid; - return true; - /* - } else - return true; - */ + snprintf(statusfilename, MAX_NAME, "%s/%s", dirname, name); + struct stat sstat; + int statok = stat(statusfilename, &sstat); + if (statok == -1) + return false; + proc->st_uid = sstat.st_uid; + return true; } void ProcessList_processEntries(ProcessList* this, char* dirname, int parent, float period) { @@ -521,9 +524,7 @@ void ProcessList_processEntries(ProcessList* this, char* dirname, int parent, fl char subdirname[MAX_NAME+1]; snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name); - if (access(subdirname, X_OK) == 0) { - ProcessList_processEntries(this, subdirname, pid, period); - } + ProcessList_processEntries(this, subdirname, pid, period); } FILE* status; diff --git a/SignalsPanel.c b/SignalsPanel.c index cb9382ea..517fee67 100644 --- a/SignalsPanel.c +++ b/SignalsPanel.c @@ -27,7 +27,7 @@ SignalsPanel* SignalsPanel_new(int x, int y, int w, int h) { ((Object*)this)->delete = SignalsPanel_delete; this->signals = Signal_getSignalTable(); - super->eventHandler = SignalsPanel_EventHandler; + super->eventHandler = SignalsPanel_eventHandler; int sigCount = Signal_getSignalCount(); for(int i = 0; i < sigCount; i++) Panel_set(super, i, (Object*) this->signals[i]); @@ -51,7 +51,7 @@ void SignalsPanel_reset(SignalsPanel* this) { this->state = 0; } -HandlerResult SignalsPanel_EventHandler(Panel* super, int ch) { +HandlerResult SignalsPanel_eventHandler(Panel* super, int ch) { SignalsPanel* this = (SignalsPanel*) super; int size = Panel_getSize(super); diff --git a/SignalsPanel.h b/SignalsPanel.h index d34a9941..4401a520 100644 --- a/SignalsPanel.h +++ b/SignalsPanel.h @@ -27,6 +27,6 @@ void SignalsPanel_delete(Object* object); void SignalsPanel_reset(SignalsPanel* this); -HandlerResult SignalsPanel_EventHandler(Panel* super, int ch); +HandlerResult SignalsPanel_eventHandler(Panel* super, int ch); #endif diff --git a/htop.1 b/htop.1 index 45bb4eb3..9dd36e0b 100644 --- a/htop.1 +++ b/htop.1 @@ -77,6 +77,9 @@ If none is tagged, sends to the currently selected process. .B F10, q Quit .TP +.B a (on multiprocessor machines) +Set CPU affinity: mark which CPUs a process is allowed to use. +.TP .B u Show only processes owned by a specified user. .TP diff --git a/htop.c b/htop.c index 5b2a996f..d5bebd47 100644 --- a/htop.c +++ b/htop.c @@ -25,6 +25,7 @@ in the source distribution for its full text. #include "CategoriesPanel.h" #include "SignalsPanel.h" #include "TraceScreen.h" +#include "AffinityPanel.h" #include "config.h" #include "debug.h" @@ -104,7 +105,10 @@ void showHelp(ProcessList* pl) { mvaddstr(15, 0, " F9 k: kill process/tagged processes P: sort by CPU%"); mvaddstr(16, 0, " + [ F7: lower priority (+ nice) M: sort by MEM%"); mvaddstr(17, 0, " - ] F8: higher priority (root only) T: sort by TIME"); - mvaddstr(18, 0, " F4 I: invert sort order"); + if (pl->processorCount > 1) + mvaddstr(18, 0, " a: set CPU affinity F4 I: invert sort order"); + else + mvaddstr(18, 0, " F4 I: invert sort order"); mvaddstr(19, 0, " F2 S: setup F6 >: select sort column"); mvaddstr(20, 0, " F1 h: show this help screen"); mvaddstr(21, 0, " F10 q: quit s: trace syscalls with strace"); @@ -120,6 +124,8 @@ void showHelp(ProcessList* pl) { mvaddstr(16, 0, " + [ F7"); mvaddstr(16,40, " M"); mvaddstr(17, 0, " - ] F8"); mvaddstr(17,40, " T"); mvaddstr(18,40, " F4 I"); + if (pl->processorCount > 1) + mvaddstr(18, 0, " a:"); mvaddstr(19, 0, " F2 S"); mvaddstr(19,40, " F6 >"); mvaddstr(20, 0, " F1 h"); mvaddstr(21, 0, " F10 q"); mvaddstr(21,40, " s"); @@ -169,7 +175,7 @@ static HandlerResult pickWithEnter(Panel* panel, int ch) { static Object* pickFromList(Panel* panel, Panel* list, int x, int y, char** keyLabels, FunctionBar* prevBar) { char* fuKeys[2] = {"Enter", "Esc"}; int fuEvents[2] = {13, 27}; - if (!panel->eventHandler) + if (!list->eventHandler) Panel_setEventHandler(list, pickWithEnter); ScreenManager* scr = ScreenManager_new(0, y, 0, -1, HORIZONTAL, false); ScreenManager_add(scr, list, FunctionBar_new(2, keyLabels, fuKeys, fuEvents), x - 1); @@ -592,6 +598,36 @@ int main(int argc, char** argv) { refreshTimeout = 0; break; } + case 'a': + { + if (pl->processorCount == 1) + break; + + Process* p = (Process*) Panel_getSelected(panel); + unsigned long curr = Process_getAffinity(p); + + Panel* affinityPanel = AffinityPanel_new(pl->processorCount, curr); + + char* fuFunctions[2] = {"Toggle ", "Done "}; + pickFromList(panel, affinityPanel, 15, headerHeight, fuFunctions, defaultBar); + unsigned long new = AffinityPanel_getAffinity(affinityPanel); + bool anyTagged = false; + for (int i = 0; i < Panel_getSize(panel); i++) { + Process* p = (Process*) Panel_get(panel, i); + if (p->tag) { + Process_setAffinity(p, new); + anyTagged = true; + } + } + if (!anyTagged) { + Process* p = (Process*) Panel_getSelected(panel); + Process_setAffinity(p, new); + } + ((Object*)affinityPanel)->delete((Object*)affinityPanel); + Panel_setRichHeader(panel, ProcessList_printHeader(pl)); + refreshTimeout = 0; + break; + } case KEY_F(10): case 'q': quit = 1; diff --git a/htop.h b/htop.h index 5e7c385e..ca3ae912 100644 --- a/htop.h +++ b/htop.h @@ -29,6 +29,7 @@ in the source distribution for its full text. #include "CategoriesPanel.h" #include "SignalsPanel.h" #include "TraceScreen.h" +#include "AffinityPanel.h" #include "config.h" #include "debug.h"