diff --git a/.gitignore b/.gitignore index c21162d6..85f580bf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,12 @@ htop # all object files *.o +*.gcda +*/*.gcda +*.gcno +*/*.gcno +*.h.gch + .deps/ Makefile Makefile.in diff --git a/Panel.c b/Panel.c index ccf6ba68..7428dda6 100644 --- a/Panel.c +++ b/Panel.c @@ -56,7 +56,6 @@ typedef struct PanelClass_ { struct Panel_ { Object super; - PanelClass* class; int x, y, w, h; WINDOW* window; Vector* items; diff --git a/Panel.h b/Panel.h index 71740bf6..2e9c5ad3 100644 --- a/Panel.h +++ b/Panel.h @@ -45,7 +45,6 @@ typedef struct PanelClass_ { struct Panel_ { Object super; - PanelClass* class; int x, y, w, h; WINDOW* window; Vector* items; diff --git a/Process.c b/Process.c index 3d95ce00..267d1733 100644 --- a/Process.c +++ b/Process.c @@ -149,7 +149,15 @@ extern ProcessFieldData Process_fields[]; extern char* Process_pidFormat; extern char* Process_tpgidFormat; -typedef Process*(*Process_new_fn)(struct Settings_*); +typedef Process*(*Process_New)(struct Settings_*); +typedef void (*Process_WriteField)(Process*, RichString*, ProcessField); + +typedef struct ProcessClass_ { + const ObjectClass super; + const Process_WriteField writeField; +} ProcessClass; + +#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) }*/ @@ -306,7 +314,7 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c } } -void Process_writeDefaultField(Process* this, RichString* str, ProcessField field) { +void Process_writeField(Process* this, RichString* str, ProcessField field) { char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int baseattr = CRT_colors[PROCESS_BASENAME]; @@ -427,12 +435,12 @@ void Process_writeDefaultField(Process* this, RichString* str, ProcessField fiel RichString_append(str, attr, buffer); } -static void Process_display(Object* cast, RichString* out) { +void Process_display(Object* cast, RichString* out) { Process* this = (Process*) cast; ProcessField* fields = this->settings->fields; RichString_prune(out); for (int i = 0; fields[i]; i++) - Process_writeField(this, out, fields[i]); + As_Process(this)->writeField(this, out, fields[i]); if (this->settings->shadowOtherUsers && (int)this->st_uid != Process_getuid) RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); if (this->tag == true) @@ -445,11 +453,14 @@ void Process_done(Process* this) { free(this->comm); } -ObjectClass Process_class = { - .extends = Class(Object), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare +ProcessClass Process_class = { + .super = { + .extends = Class(Object), + .display = Process_display, + .delete = Process_delete, + .compare = Process_compare + }, + .writeField = Process_writeField, }; void Process_init(Process* this, struct Settings_* settings) { @@ -489,7 +500,7 @@ long Process_pidCompare(const void* v1, const void* v2) { return (p1->pid - p2->pid); } -long Process_defaultCompare(const void* v1, const void* v2) { +long Process_compare(const void* v1, const void* v2) { Process *p1, *p2; Settings *settings = ((Process*)v1)->settings; if (settings->direction == 1) { diff --git a/Process.h b/Process.h index 341c0e82..e1b7a8fa 100644 --- a/Process.h +++ b/Process.h @@ -130,7 +130,15 @@ extern ProcessFieldData Process_fields[]; extern char* Process_pidFormat; extern char* Process_tpgidFormat; -typedef Process*(*Process_new_fn)(struct Settings_*); +typedef Process*(*Process_New)(struct Settings_*); +typedef void (*Process_WriteField)(Process*, RichString*, ProcessField); + +typedef struct ProcessClass_ { + const ObjectClass super; + const Process_WriteField writeField; +} ProcessClass; + +#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) #define ONE_K 1024L @@ -149,11 +157,13 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths); void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring); -void Process_writeDefaultField(Process* this, RichString* str, ProcessField field); +void Process_writeField(Process* this, RichString* str, ProcessField field); + +void Process_display(Object* cast, RichString* out); void Process_done(Process* this); -extern ObjectClass Process_class; +extern ProcessClass Process_class; void Process_init(Process* this, struct Settings_* settings); @@ -167,6 +177,6 @@ void Process_sendSignal(Process* this, size_t sgn); long Process_pidCompare(const void* v1, const void* v2); -long Process_defaultCompare(const void* v1, const void* v2); +long Process_compare(const void* v1, const void* v2); #endif diff --git a/ProcessList.c b/ProcessList.c index 229e370d..7fbe6c3a 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -287,7 +287,7 @@ void ProcessList_rebuildPanel(ProcessList* this) { } } -Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_new_fn constructor) { +Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) { Process* proc = (Process*) Hashtable_get(this->processTable, pid); *preExisting = proc; if (proc) { diff --git a/ProcessList.h b/ProcessList.h index 9465e82e..e00585fd 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -91,7 +91,7 @@ void ProcessList_expandTree(ProcessList* this); void ProcessList_rebuildPanel(ProcessList* this); -Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_new_fn constructor); +Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); void ProcessList_scan(ProcessList* this); diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 409a4ae5..0e73b20a 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -261,9 +261,19 @@ void Process_setupColumnWidths() { } } +ProcessClass LinuxProcess_class = { + .super = { + .extends = Class(Process), + .display = Process_display, + .delete = Process_delete, + .compare = LinuxProcess_compare + }, + .writeField = (Process_WriteField) LinuxProcess_writeField, +}; + LinuxProcess* LinuxProcess_new(Settings* settings) { LinuxProcess* this = calloc(sizeof(LinuxProcess), 1); - Object_setClass(this, Class(Process)); + Object_setClass(this, Class(LinuxProcess)); Process_init(&this->super, settings); return this; } @@ -298,7 +308,7 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) { return (LinuxProcess_updateIOPriority(this) == ioprio); } -void Process_writeField(Process* this, RichString* str, ProcessField field) { +void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) { LinuxProcess* lp = (LinuxProcess*) this; bool coloring = this->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; @@ -360,13 +370,13 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) { break; } default: - Process_writeDefaultField(this, str, field); + Process_writeField((Process*)this, str, field); return; } RichString_append(str, attr, buffer); } -long Process_compare(const void* v1, const void* v2) { +long LinuxProcess_compare(const void* v1, const void* v2) { LinuxProcess *p1, *p2; Settings *settings = ((Process*)v1)->settings; if (settings->direction == 1) { @@ -425,7 +435,7 @@ long Process_compare(const void* v1, const void* v2) { case IO_PRIORITY: return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2); default: - return Process_defaultCompare(v1, v2); + return Process_compare(v1, v2); } test_diff: return (diff > 0) ? 1 : (diff < 0 ? -1 : 0); diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 55fa1b9e..bfc70234 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -137,6 +137,8 @@ extern char* Process_tpgidFormat; void Process_setupColumnWidths(); +extern ProcessClass LinuxProcess_class; + LinuxProcess* LinuxProcess_new(Settings* settings); void Process_delete(Object* cast); @@ -155,9 +157,9 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio); -void Process_writeField(Process* this, RichString* str, ProcessField field); +void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field); -long Process_compare(const void* v1, const void* v2); +long LinuxProcess_compare(const void* v1, const void* v2); bool Process_isThread(Process* this); diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 1c5f2059..b9c4073c 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -517,7 +517,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* continue; bool preExisting = false; - Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_new_fn) LinuxProcess_new); + Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_New) LinuxProcess_new); proc->tgid = parent ? parent->pid : pid; LinuxProcess* lp = (LinuxProcess*) proc; diff --git a/test_spec.lua b/test_spec.lua index 38f27b53..e57437d6 100755 --- a/test_spec.lua +++ b/test_spec.lua @@ -20,7 +20,7 @@ os.execute("killall htop") os.execute("ps aux | grep '[s]leep 12345' | awk '{print $2}' | xargs kill 2> /dev/null") os.execute("cp ./default.htoprc ./test.htoprc") -rt:forkPty("HTOPRC=./test.htoprc ./htop") +rt:forkPty("LC_ALL=C HTOPRC=./test.htoprc ./htop") local stdscr, term_win -- Curses initalization needed even when not in visual mode @@ -68,7 +68,7 @@ end local function send(key, times) if times == 0 then return end - for i = 1, times or 1 do + for _ = 1, times or 1 do delay(0.003) -- 30ms delay to avoid clobbering Esc sequences if type(key) == "string" then for c in key:gmatch('.') do @@ -138,8 +138,9 @@ local attrs = { red_on_cyan = 22, } -local function find_selected_y() - for y = y_panelhdr + 1, rt:rows() - 1 do +local function find_selected_y(from) + rt:update() + for y = from or (y_panelhdr + 1), rt:rows() - 1 do local attr = rt:cellAttr(y-1, 1) if attr == attrs.black_on_cyan then return y @@ -161,7 +162,7 @@ describe("htop test suite", function() running_it("performs incremental filter", function() send("\\") - send("busted") + send("x\127bux\127sted") -- test backspace send("\n") delay(0.2) rt:update() @@ -188,7 +189,6 @@ describe("htop test suite", function() local attr = rt:cellAttr(rt:rows() - 1, 30) send("\n") delay(0.4) - rt:update() local line = find_selected_y() local pid = (" "..tostring(unistd.getpid())):sub(-5) assert.equal(attr, attrs.black_on_cyan) @@ -224,7 +224,6 @@ describe("htop test suite", function() send("\n") send("s") delay(1) - delay(1) send(ESC) end) @@ -235,15 +234,48 @@ describe("htop test suite", function() send("\n") send("l") delay(1) - delay(1) send(ESC) end) - running_it("cycles through meter modes", function() + running_it("performs filtering in lsof", function() + send(curses.KEY_HOME) + send("/") + send("htop") + send("\n") + send("l") + send(curses.KEY_F4) + send("pipe") + delay(1) + local pipefd = check_string_at(1, 3, " 3") + send(ESC) + assert.equal(check(pipefd)) + end) + + running_it("performs search in lsof", function() + send(curses.KEY_HOME) + send("/") + send("htop") + send("\n") + send("l") + send(curses.KEY_F3) + send("pipe") + delay(1) + local line = find_selected_y(3) + local pipefd = check_string_at(1, line, " 3") + send(ESC) + assert.equal(check(pipefd)) + end) + + + running_it("cycles through meter modes in the default meters", function() send("S") - send(curses.KEY_RIGHT) - send(curses.KEY_DOWN) - send("\n\n\n\n\n") + for _ = 1, 2 do + send(curses.KEY_RIGHT) + for _ = 1, 3 do + send("\n", 4) + send(curses.KEY_DOWN) + end + end send(ESC) end) @@ -366,6 +398,7 @@ describe("htop test suite", function() send(curses.KEY_DOWN, 4) send(curses.KEY_F7, 4) end + send(curses.KEY_F4, 4) -- cycle through meter modes delay(0.15) rt:update() local with = check_string_at(x_metercol2, 2, item.string) @@ -382,7 +415,7 @@ describe("htop test suite", function() send(curses.KEY_F2) send(curses.KEY_DOWN, 2) send(curses.KEY_RIGHT) - for i = 1, 6 do + for _ = 1, 6 do send("\n") send(curses.KEY_DOWN) end @@ -420,8 +453,22 @@ describe("htop test suite", function() end end) end + + running_it("shows detailed CPU with guest time", function() + for _ = 1, 2 do + send("S") + send(curses.KEY_DOWN) + send(curses.KEY_RIGHT) + send(curses.KEY_DOWN, 9) + send("\n") + send(curses.KEY_DOWN, 3) + send("\n") + send(curses.KEY_F10) + delay(0.1) + end + end) - for i = 1, 53 do + for i = 1, 62 do running_it("show column "..i, function() send("S") send(curses.KEY_END)