diff options
113 files changed, 1522 insertions, 729 deletions
diff --git a/.editorconfig b/.editorconfig index 18807892..dd881dd0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,4 @@ charset = utf-8 [*.{c,h}] indent_style = space indent_size = 3 +trim_trailing_whitespace = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..74d0ece8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-ubuntu-latest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build + run: | + sudo apt-get install libncursesw5-dev + ./autogen.sh + ./configure + make + +# build-macos-latest: +# runs-on: macos-latest +# steps: +# - uses: actions/checkout@v2 +# - name: make +# run: | +# ./autogen.sh +# ./configure +# make @@ -62,11 +62,11 @@ typedef struct State_ { }*/ -Object* Action_pickFromVector(State* st, Panel* list, int x) { +Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) { Panel* panel = st->panel; Header* header = st->header; Settings* settings = st->settings; - + int y = panel->y; ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false); scr->allowFocusChange = false; @@ -75,8 +75,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) { Panel* panelFocus; int ch; bool unfollow = false; - int pid = MainPanel_selectedPid((MainPanel*)panel); - if (header->pl->following == -1) { + int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1; + if (followProcess && header->pl->following == -1) { header->pl->following = pid; unfollow = true; } @@ -88,11 +88,16 @@ Object* Action_pickFromVector(State* st, Panel* list, int x) { Panel_move(panel, 0, y); Panel_resize(panel, COLS, LINES-y-1); if (panelFocus == list && ch == 13) { - Process* selected = (Process*)Panel_getSelected(panel); - if (selected && selected->pid == pid) + if (followProcess) { + Process* selected = (Process*)Panel_getSelected(panel); + if (selected && selected->pid == pid) + return Panel_getSelected(list); + else + beep(); + } else { return Panel_getSelected(list); - else - beep(); + } + } return NULL; } @@ -189,7 +194,7 @@ static Htop_Reaction sortBy(State* st) { Panel_setSelected(sortPanel, i); free(name); } - ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15); + ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false); if (field) { reaction |= Action_setSortKey(st->settings, field->key); } @@ -248,10 +253,21 @@ static Htop_Reaction actionIncFilter(State* st) { } static Htop_Reaction actionIncSearch(State* st) { + IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH); IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel); return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; } +static Htop_Reaction actionIncNext(State* st) { + IncSet_next(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue); + return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; +} + +static Htop_Reaction actionIncPrev(State* st) { + IncSet_prev(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue); + return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; +} + static Htop_Reaction actionHigherPriority(State* st) { bool changed = changePriority((MainPanel*)st->panel, -1); return changed ? HTOP_REFRESH : HTOP_OK; @@ -297,7 +313,7 @@ static Htop_Reaction actionSetAffinity(State* st) { return HTOP_OK; #if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY) Panel* panel = st->panel; - + Process* p = (Process*) Panel_getSelected(panel); if (!p) return HTOP_OK; Affinity* affinity = Affinity_get(p, st->pl); @@ -305,7 +321,7 @@ static Htop_Reaction actionSetAffinity(State* st) { Panel* affinityPanel = AffinityPanel_new(st->pl, affinity); Affinity_delete(affinity); - void* set = Action_pickFromVector(st, affinityPanel, 15); + void* set = Action_pickFromVector(st, affinityPanel, 15, true); if (set) { Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl); bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity }, NULL); @@ -319,7 +335,7 @@ static Htop_Reaction actionSetAffinity(State* st) { static Htop_Reaction actionKill(State* st) { Panel* signalsPanel = (Panel*) SignalsPanel_new(); - ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15); + ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true); if (sgn) { if (sgn->key != 0) { Panel_setHeader(st->panel, "Sending..."); @@ -340,7 +356,7 @@ static Htop_Reaction actionFilterByUser(State* st) { Vector_insertionSort(usersPanel->items); ListItem* allUsers = ListItem_new("All users", -1); Panel_insert(usersPanel, 0, (Object*) allUsers); - ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20); + ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false); if (picked) { if (picked == allUsers) { st->pl->userId = -1; @@ -463,7 +479,7 @@ static Htop_Reaction actionHelp(State* st) { if (settings->detailedCPUTime) { addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); - addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); + addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/"); addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/"); addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/"); @@ -473,7 +489,7 @@ static Htop_Reaction actionHelp(State* st) { } else { addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); - addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/"); + addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_GUEST], "virtualiz"); addattrstr(CRT_colors[BAR_SHADOW], " used%"); } @@ -559,6 +575,8 @@ void Action_setBindings(Htop_Action* keys) { keys['\\'] = actionIncFilter; keys[KEY_F(3)] = actionIncSearch; keys['/'] = actionIncSearch; + keys['n'] = actionIncNext; + keys['N'] = actionIncPrev; keys[']'] = actionHigherPriority; keys[KEY_F(7)] = actionHigherPriority; @@ -39,20 +39,20 @@ typedef struct State_ { } State; -Object* Action_pickFromVector(State* st, Panel* list, int x); +extern Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess); // ---------------------------------------- -bool Action_setUserOnly(const char* userName, uid_t* userId); +extern bool Action_setUserOnly(const char* userName, uid_t* userId); -Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey); +extern Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey); // ---------------------------------------- -Htop_Reaction Action_follow(State* st); +extern Htop_Reaction Action_follow(State* st); -void Action_setBindings(Htop_Action* keys); +extern void Action_setBindings(Htop_Action* keys); #endif @@ -1,6 +1,7 @@ /* htop - Affinity.c (C) 2004-2011 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -79,7 +80,8 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) { return affinity; } -bool Affinity_set(Process* proc, Affinity* this) { +bool Affinity_set(Process* proc, Arg arg) { + Affinity *this = arg.v; hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); for (int i = 0; i < this->used; i++) { hwloc_bitmap_set(cpuset, this->cpus[i]); @@ -103,7 +105,8 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) { return affinity; } -bool Affinity_set(Process* proc, Affinity* this) { +bool Affinity_set(Process* proc, Arg arg) { + Affinity *this = arg.v; cpu_set_t cpuset; CPU_ZERO(&cpuset); for (int i = 0; i < this->used; i++) { @@ -5,6 +5,7 @@ /* htop - Affinity.h (C) 2004-2011 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -29,23 +30,23 @@ typedef struct Affinity_ { } Affinity; -Affinity* Affinity_new(ProcessList* pl); +extern Affinity* Affinity_new(ProcessList* pl); -void Affinity_delete(Affinity* this); +extern void Affinity_delete(Affinity* this); -void Affinity_add(Affinity* this, int id); +extern void Affinity_add(Affinity* this, int id); #ifdef HAVE_LIBHWLOC -Affinity* Affinity_get(Process* proc, ProcessList* pl); +extern Affinity* Affinity_get(Process* proc, ProcessList* pl); -bool Affinity_set(Process* proc, Affinity* this); +extern bool Affinity_set(Process* proc, Arg arg); #elif HAVE_LINUX_AFFINITY -Affinity* Affinity_get(Process* proc, ProcessList* pl); +extern Affinity* Affinity_get(Process* proc, ProcessList* pl); -bool Affinity_set(Process* proc, Affinity* this); +extern bool Affinity_set(Process* proc, Arg arg); #endif diff --git a/AffinityPanel.h b/AffinityPanel.h index 2b6059b0..3a9c7807 100644 --- a/AffinityPanel.h +++ b/AffinityPanel.h @@ -16,8 +16,8 @@ in the source distribution for its full text. extern PanelClass AffinityPanel_class; -Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity); +extern Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity); -Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl); +extern Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl); #endif diff --git a/AvailableColumnsPanel.h b/AvailableColumnsPanel.h index 5a8371dd..2d6daf4b 100644 --- a/AvailableColumnsPanel.h +++ b/AvailableColumnsPanel.h @@ -19,6 +19,6 @@ typedef struct AvailableColumnsPanel_ { extern PanelClass AvailableColumnsPanel_class; -AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); +extern AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); #endif diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index ddb55367..5dac10b9 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -52,7 +52,7 @@ static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, M static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { AvailableMetersPanel* this = (AvailableMetersPanel*) super; Header* header = this->header; - + ListItem* selected = (ListItem*) Panel_getSelected(super); int param = selected->key & 0xff; int type = selected->key >> 16; @@ -104,7 +104,7 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); - + this->settings = settings; this->header = header; this->leftPanel = leftMeters; @@ -127,7 +127,7 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade Panel_add(super, (Object*) ListItem_new("CPU average", 0)); for (int i = 1; i <= cpus; i++) { char buffer[50]; - xSnprintf(buffer, 50, "%s %d", type->uiName, i); + xSnprintf(buffer, 50, "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1)); Panel_add(super, (Object*) ListItem_new(buffer, i)); } } else { diff --git a/AvailableMetersPanel.h b/AvailableMetersPanel.h index e9b949b0..00e0f596 100644 --- a/AvailableMetersPanel.h +++ b/AvailableMetersPanel.h @@ -27,6 +27,6 @@ typedef struct AvailableMetersPanel_ { extern PanelClass AvailableMetersPanel_class; -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl); +extern AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl); #endif diff --git a/BatteryMeter.c b/BatteryMeter.c index 214248e2..716b7750 100644 --- a/BatteryMeter.c +++ b/BatteryMeter.c @@ -35,7 +35,7 @@ int BatteryMeter_attributes[] = { static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) { ACPresence isOnAC; double percent; - + Battery_getData(&percent, &isOnAC); if (percent == -1) { @@ -28,13 +28,14 @@ typedef enum { CPU_METER_STEAL = 5, CPU_METER_GUEST = 6, CPU_METER_IOWAIT = 7, - CPU_METER_ITEMCOUNT = 8, // number of entries in this enum + CPU_METER_FREQUENCY = 8, + CPU_METER_ITEMCOUNT = 9, // number of entries in this enum } CPUMeterValues; }*/ int CPUMeter_attributes[] = { - CPU_NICE, CPU_NORMAL, CPU_KERNEL, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT + CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT }; #ifndef MIN @@ -63,7 +64,30 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { } memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); double percent = Platform_setCPUValues(this, cpu); - xSnprintf(buffer, size, "%5.1f%%", percent); + if (this->pl->settings->showCPUFrequency) { + /* Initial frequency is in MHz. Emit it as GHz if it's larger than 1000MHz */ + double cpuFrequency = this->values[CPU_METER_FREQUENCY]; + char unit = 'M'; + char cpuFrequencyBuffer[16]; + if (cpuFrequency < 0) { + xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A"); + } else { + if (cpuFrequency > 1000) { + cpuFrequency /= 1000; + unit = 'G'; + } + xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.3f%cHz", cpuFrequency, unit); + } + if (this->pl->settings->showCPUUsage) { + xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer); + } else { + xSnprintf(buffer, size, "%s", cpuFrequencyBuffer); + } + } else if (this->pl->settings->showCPUUsage) { + xSnprintf(buffer, size, "%5.1f%%", percent); + } else if (size > 0) { + buffer[0] = '\0'; + } } static void CPUMeter_display(Object* cast, RichString* out) { @@ -80,7 +104,7 @@ static void CPUMeter_display(Object* cast, RichString* out) { if (this->pl->settings->detailedCPUTime) { xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); RichString_append(out, CRT_colors[METER_TEXT], "sy:"); - RichString_append(out, CRT_colors[CPU_KERNEL], buffer); + RichString_append(out, CRT_colors[CPU_SYSTEM], buffer); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); RichString_append(out, CRT_colors[METER_TEXT], "ni:"); RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); @@ -106,7 +130,7 @@ static void CPUMeter_display(Object* cast, RichString* out) { } else { xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); RichString_append(out, CRT_colors[METER_TEXT], "sys:"); - RichString_append(out, CRT_colors[CPU_KERNEL], buffer); + RichString_append(out, CRT_colors[CPU_SYSTEM], buffer); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); RichString_append(out, CRT_colors[METER_TEXT], "low:"); RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); @@ -137,6 +161,15 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) { } } +static int MapClassnameToColumncount(Meter* this){ + if (strchr(Meter_name(this), '4')) + return 4; + else if (strchr(Meter_name(this), '2')) + return 2; + else + return 1; +} + static void AllCPUsMeter_init(Meter* this) { int cpus = this->pl->cpuCount; if (!this->drawData) @@ -152,10 +185,8 @@ static void AllCPUsMeter_init(Meter* this) { if (this->mode == 0) this->mode = BAR_METERMODE; int h = Meter_modes[this->mode]->h; - if (strchr(Meter_name(this), '2')) - this->h = h * ((count+1) / 2); - else - this->h = h * count; + int ncol = MapClassnameToColumncount(this); + this->h = h * ((count + ncol - 1)/ ncol); } static void AllCPUsMeter_done(Meter* this) { @@ -175,10 +206,8 @@ static void AllCPUsMeter_updateMode(Meter* this, int mode) { for (int i = 0; i < count; i++) { Meter_setMode(meters[i], mode); } - if (strchr(Meter_name(this), '2')) - this->h = h * ((count+1) / 2); - else - this->h = h * count; + int ncol = MapClassnameToColumncount(this); + this->h = h * ((count + ncol - 1)/ ncol); } static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) { @@ -209,6 +238,22 @@ static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) { } } +static void MultiColCPUsMeter_draw(Meter* this, int x, int y, int w){ + Meter** meters = (Meter**) this->drawData; + int start, count; + AllCPUsMeter_getRange(this, &start, &count); + int ncol = MapClassnameToColumncount(this); + int colwidth = (w-ncol)/ncol + 1; + int diff = (w - (colwidth * ncol)); + int nrows = (count + ncol - 1) / ncol; + for (int i = 0; i < count; i++){ + int d = (i/nrows) > diff ? diff : (i / nrows) ; // dynamic spacer + int xpos = x + ((i / nrows) * colwidth) + d; + int ypos = y + ((i % nrows) * meters[0]->h); + meters[i]->draw(meters[i], xpos, ypos, colwidth); + } +} + MeterClass CPUMeter_class = { .super = { .extends = Class(Meter), @@ -219,7 +264,7 @@ MeterClass CPUMeter_class = { .defaultMode = BAR_METERMODE, .maxItems = CPU_METER_ITEMCOUNT, .total = 100.0, - .attributes = CPUMeter_attributes, + .attributes = CPUMeter_attributes, .name = "CPU", .uiName = "CPU", .caption = "CPU", @@ -234,7 +279,7 @@ MeterClass AllCPUsMeter_class = { }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, - .attributes = CPUMeter_attributes, + .attributes = CPUMeter_attributes, .name = "AllCPUs", .uiName = "CPUs (1/1)", .description = "CPUs (1/1): all CPUs", @@ -253,7 +298,7 @@ MeterClass AllCPUs2Meter_class = { }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, - .attributes = CPUMeter_attributes, + .attributes = CPUMeter_attributes, .name = "AllCPUs2", .uiName = "CPUs (1&2/2)", .description = "CPUs (1&2/2): all CPUs in 2 shorter columns", @@ -272,7 +317,7 @@ MeterClass LeftCPUsMeter_class = { }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, - .attributes = CPUMeter_attributes, + .attributes = CPUMeter_attributes, .name = "LeftCPUs", .uiName = "CPUs (1/2)", .description = "CPUs (1/2): first half of list", @@ -291,7 +336,7 @@ MeterClass RightCPUsMeter_class = { }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, - .attributes = CPUMeter_attributes, + .attributes = CPUMeter_attributes, .name = "RightCPUs", .uiName = "CPUs (2/2)", .description = "CPUs (2/2): second half of list", @@ -310,7 +355,7 @@ MeterClass LeftCPUs2Meter_class = { }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, - .attributes = CPUMeter_attributes, + .attributes = CPUMeter_attributes, .name = "LeftCPUs2", .uiName = "CPUs (1&2/4)", .description = "CPUs (1&2/4): first half in 2 shorter columns", @@ -329,7 +374,7 @@ MeterClass RightCPUs2Meter_class = { }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, - .attributes = CPUMeter_attributes, + .attributes = CPUMeter_attributes, .name = "RightCPUs2", .uiName = "CPUs (3&4/4)", .description = "CPUs (3&4/4): second half in 2 shorter columns", @@ -340,3 +385,59 @@ MeterClass RightCPUs2Meter_class = { .done = AllCPUsMeter_done }; +MeterClass AllCPUs4Meter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = CPUMeter_display + }, + .defaultMode = CUSTOM_METERMODE, + .total = 100.0, + .attributes = CPUMeter_attributes, + .name = "AllCPUs4", + .uiName = "CPUs (1&2&3&4/4)", + .description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns", + .caption = "CPU", + .draw = MultiColCPUsMeter_draw, + .init = AllCPUsMeter_init, + .updateMode = AllCPUsMeter_updateMode, + .done = AllCPUsMeter_done +}; + +MeterClass LeftCPUs4Meter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = CPUMeter_display + }, + .defaultMode = CUSTOM_METERMODE, + .total = 100.0, + .attributes = CPUMeter_attributes, + .name = "LeftCPUs4", + .uiName = "CPUs (1-4/8)", + .description = "CPUs (1-4/8): first half in 4 shorter columns", + .caption = "CPU", + .draw = MultiColCPUsMeter_draw, + .init = AllCPUsMeter_init, + .updateMode = AllCPUsMeter_updateMode, + .done = AllCPUsMeter_done +}; + +MeterClass RightCPUs4Meter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = CPUMeter_display + }, + .defaultMode = CUSTOM_METERMODE, + .total = 100.0, + .attributes = CPUMeter_attributes, + .name = "RightCPUs4", + .uiName = "CPUs (5-8/8)", + .description = "CPUs (5-8/8): second half in 4 shorter columns", + .caption = "CPU", + .draw = MultiColCPUsMeter_draw, + .init = AllCPUsMeter_init, + .updateMode = AllCPUsMeter_updateMode, + .done = AllCPUsMeter_done +}; @@ -20,7 +20,8 @@ typedef enum { CPU_METER_STEAL = 5, CPU_METER_GUEST = 6, CPU_METER_IOWAIT = 7, - CPU_METER_ITEMCOUNT = 8, // number of entries in this enum + CPU_METER_FREQUENCY = 8, + CPU_METER_ITEMCOUNT = 9, // number of entries in this enum } CPUMeterValues; @@ -47,5 +48,10 @@ extern MeterClass LeftCPUs2Meter_class; extern MeterClass RightCPUs2Meter_class; +extern MeterClass AllCPUs4Meter_class; + +extern MeterClass LeftCPUs4Meter_class; + +extern MeterClass RightCPUs4Meter_class; #endif @@ -122,12 +122,15 @@ typedef enum ColorElements_ { CPU_NICE, CPU_NICE_TEXT, CPU_NORMAL, - CPU_KERNEL, + CPU_SYSTEM, CPU_IOWAIT, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, + PRESSURE_STALL_TEN, + PRESSURE_STALL_SIXTY, + PRESSURE_STALL_THREEHUNDRED, ZFS_MFU, ZFS_MRU, ZFS_ANON, @@ -138,9 +141,9 @@ typedef enum ColorElements_ { LAST_COLORELEMENT } ColorElements; -void CRT_fatalError(const char* note) __attribute__ ((noreturn)); +extern void CRT_fatalError(const char* note) __attribute__ ((noreturn)); -void CRT_handleSIGSEGV(int sgn); +extern void CRT_handleSIGSEGV(int sgn); #define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A')) @@ -233,12 +236,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_NICE] = ColorPair(Blue,Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black), [CPU_NORMAL] = ColorPair(Green,Black), - [CPU_KERNEL] = ColorPair(Red,Black), - [CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black), + [CPU_SYSTEM] = ColorPair(Red,Black), + [CPU_IOWAIT] = A_BOLD | ColorPairGrayBlack, [CPU_IRQ] = ColorPair(Yellow,Black), [CPU_SOFTIRQ] = ColorPair(Magenta,Black), [CPU_STEAL] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black), + [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan,Black), + [PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan,Black), + [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Black), [ZFS_MFU] = ColorPair(Blue,Black), [ZFS_MRU] = ColorPair(Yellow,Black), [ZFS_ANON] = ColorPair(Magenta,Black), @@ -299,12 +305,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_NICE] = A_NORMAL, [CPU_NICE_TEXT] = A_NORMAL, [CPU_NORMAL] = A_BOLD, - [CPU_KERNEL] = A_BOLD, + [CPU_SYSTEM] = A_BOLD, [CPU_IOWAIT] = A_NORMAL, [CPU_IRQ] = A_BOLD, [CPU_SOFTIRQ] = A_BOLD, [CPU_STEAL] = A_REVERSE, [CPU_GUEST] = A_REVERSE, + [PRESSURE_STALL_THREEHUNDRED] = A_DIM, + [PRESSURE_STALL_SIXTY] = A_NORMAL, + [PRESSURE_STALL_TEN] = A_BOLD, [ZFS_MFU] = A_NORMAL, [ZFS_MRU] = A_NORMAL, [ZFS_ANON] = A_DIM, @@ -365,12 +374,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_NICE] = ColorPair(Cyan,White), [CPU_NICE_TEXT] = ColorPair(Cyan,White), [CPU_NORMAL] = ColorPair(Green,White), - [CPU_KERNEL] = ColorPair(Red,White), - [CPU_IOWAIT] = A_BOLD | ColorPair(Black, White), + [CPU_SYSTEM] = ColorPair(Red,White), + [CPU_IOWAIT] = A_BOLD | ColorPair(Black,White), [CPU_IRQ] = ColorPair(Blue,White), [CPU_SOFTIRQ] = ColorPair(Blue,White), [CPU_STEAL] = ColorPair(Cyan,White), [CPU_GUEST] = ColorPair(Cyan,White), + [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,White), + [PRESSURE_STALL_SIXTY] = ColorPair(Black,White), + [PRESSURE_STALL_TEN] = ColorPair(Black,White), [ZFS_MFU] = ColorPair(Cyan,White), [ZFS_MRU] = ColorPair(Yellow,White), [ZFS_ANON] = ColorPair(Magenta,White), @@ -431,12 +443,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_NICE] = ColorPair(Cyan,Black), [CPU_NICE_TEXT] = ColorPair(Cyan,Black), [CPU_NORMAL] = ColorPair(Green,Black), - [CPU_KERNEL] = ColorPair(Red,Black), - [CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black), + [CPU_SYSTEM] = ColorPair(Red,Black), + [CPU_IOWAIT] = A_BOLD | ColorPair(Black,Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_STEAL] = ColorPair(Black,Black), [CPU_GUEST] = ColorPair(Black,Black), + [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,Black), + [PRESSURE_STALL_SIXTY] = ColorPair(Black,Black), + [PRESSURE_STALL_TEN] = ColorPair(Black,Black), [ZFS_MFU] = ColorPair(Cyan,Black), [ZFS_MRU] = ColorPair(Yellow,Black), [ZFS_ANON] = A_BOLD | ColorPair(Magenta,Black), @@ -497,12 +512,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_NICE] = A_BOLD | ColorPair(Cyan,Blue), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan,Blue), [CPU_NORMAL] = A_BOLD | ColorPair(Green,Blue), - [CPU_KERNEL] = A_BOLD | ColorPair(Red,Blue), + [CPU_SYSTEM] = A_BOLD | ColorPair(Red,Blue), [CPU_IOWAIT] = A_BOLD | ColorPair(Blue,Blue), [CPU_IRQ] = A_BOLD | ColorPair(Black,Blue), [CPU_SOFTIRQ] = ColorPair(Black,Blue), [CPU_STEAL] = ColorPair(White,Blue), [CPU_GUEST] = ColorPair(White,Blue), + [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black,Blue), + [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White,Blue), + [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Blue), [ZFS_MFU] = A_BOLD | ColorPair(White,Blue), [ZFS_MRU] = A_BOLD | ColorPair(Yellow,Blue), [ZFS_ANON] = A_BOLD | ColorPair(Magenta,Blue), @@ -563,12 +581,15 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_NICE] = ColorPair(Blue,Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black), [CPU_NORMAL] = ColorPair(Green,Black), - [CPU_KERNEL] = ColorPair(Red,Black), + [CPU_SYSTEM] = ColorPair(Red,Black), [CPU_IOWAIT] = ColorPair(Yellow,Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_STEAL] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black), + [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green,Black), + [PRESSURE_STALL_SIXTY] = ColorPair(Green,Black), + [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green,Black), [ZFS_MFU] = ColorPair(Blue,Black), [ZFS_MRU] = ColorPair(Yellow,Black), [ZFS_ANON] = ColorPair(Magenta,Black), @@ -653,12 +674,12 @@ void CRT_init(int delay, int colorScheme) { } CRT_colors = CRT_colorSchemes[colorScheme]; CRT_colorScheme = colorScheme; - + for (int i = 0; i < LAST_COLORELEMENT; i++) { unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i]; CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White,Black) : color; } - + halfdelay(CRT_delay); nonl(); intrflush(stdscr, false); @@ -110,12 +110,15 @@ typedef enum ColorElements_ { CPU_NICE, CPU_NICE_TEXT, CPU_NORMAL, - CPU_KERNEL, + CPU_SYSTEM, CPU_IOWAIT, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, + PRESSURE_STALL_TEN, + PRESSURE_STALL_SIXTY, + PRESSURE_STALL_THREEHUNDRED, ZFS_MFU, ZFS_MRU, ZFS_ANON, @@ -126,9 +129,9 @@ typedef enum ColorElements_ { LAST_COLORELEMENT } ColorElements; -void CRT_fatalError(const char* note) __attribute__ ((noreturn)); +extern void CRT_fatalError(const char* note) __attribute__ ((noreturn)); -void CRT_handleSIGSEGV(int sgn); +extern void CRT_handleSIGSEGV(int sgn); #define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A')) @@ -147,7 +150,7 @@ extern const char **CRT_treeStr; extern int CRT_delay; -int* CRT_colors; +extern int* CRT_colors; extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT]; @@ -157,21 +160,21 @@ extern int CRT_scrollHAmount; extern int CRT_scrollWheelVAmount; -char* CRT_termType; +extern char* CRT_termType; // TODO move color scheme to Settings, perhaps? extern int CRT_colorScheme; -void *backtraceArray[128]; +extern void *backtraceArray[128]; #if HAVE_SETUID_ENABLED #define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0) -void CRT_dropPrivileges(); +extern void CRT_dropPrivileges(); -void CRT_restorePrivileges(); +extern void CRT_restorePrivileges(); #else @@ -186,18 +189,18 @@ void CRT_restorePrivileges(); // TODO: pass an instance of Settings instead. -void CRT_init(int delay, int colorScheme); +extern void CRT_init(int delay, int colorScheme); -void CRT_done(); +extern void CRT_done(); -void CRT_fatalError(const char* note); +extern void CRT_fatalError(const char* note); -int CRT_readKey(); +extern int CRT_readKey(); -void CRT_disableDelay(); +extern void CRT_disableDelay(); -void CRT_enableDelay(); +extern void CRT_enableDelay(); -void CRT_setColors(int colorScheme); +extern void CRT_setColors(int colorScheme); #endif diff --git a/CategoriesPanel.h b/CategoriesPanel.h index ccef0fae..44c6842a 100644 --- a/CategoriesPanel.h +++ b/CategoriesPanel.h @@ -24,10 +24,10 @@ typedef struct CategoriesPanel_ { } CategoriesPanel; -void CategoriesPanel_makeMetersPage(CategoriesPanel* this); +extern void CategoriesPanel_makeMetersPage(CategoriesPanel* this); extern PanelClass CategoriesPanel_class; -CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); +extern CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); #endif diff --git a/CheckItem.c b/CheckItem.c index b7ba6fec..910981e8 100644 --- a/CheckItem.c +++ b/CheckItem.c @@ -66,14 +66,14 @@ CheckItem* CheckItem_newByVal(char* text, bool value) { } void CheckItem_set(CheckItem* this, bool value) { - if (this->ref) + if (this->ref) *(this->ref) = value; else this->value = value; } bool CheckItem_get(CheckItem* this) { - if (this->ref) + if (this->ref) return *(this->ref); else return this->value; diff --git a/CheckItem.h b/CheckItem.h index 5847d4b2..dbd1a039 100644 --- a/CheckItem.h +++ b/CheckItem.h @@ -21,12 +21,12 @@ typedef struct CheckItem_ { extern ObjectClass CheckItem_class; -CheckItem* CheckItem_newByRef(char* text, bool* ref); +extern CheckItem* CheckItem_newByRef(char* text, bool* ref); -CheckItem* CheckItem_newByVal(char* text, bool value); +extern CheckItem* CheckItem_newByVal(char* text, bool value); -void CheckItem_set(CheckItem* this, bool value); +extern void CheckItem_set(CheckItem* this, bool value); -bool CheckItem_get(CheckItem* this); +extern bool CheckItem_get(CheckItem* this); #endif diff --git a/ColorsPanel.c b/ColorsPanel.c index 2028335f..b2d3500c 100644 --- a/ColorsPanel.c +++ b/ColorsPanel.c @@ -56,7 +56,7 @@ static void ColorsPanel_delete(Object* object) { static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) { ColorsPanel* this = (ColorsPanel*) super; - + HandlerResult result = IGNORED; int mark = Panel_getSelectedIndex(super); diff --git a/ColorsPanel.h b/ColorsPanel.h index ee3111e0..82b782b8 100644 --- a/ColorsPanel.h +++ b/ColorsPanel.h @@ -29,6 +29,6 @@ typedef struct ColorsPanel_ { extern PanelClass ColorsPanel_class; -ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr); +extern ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr); #endif diff --git a/ColumnsPanel.c b/ColumnsPanel.c index 8974ffdc..f982292e 100644 --- a/ColumnsPanel.c +++ b/ColumnsPanel.c @@ -40,7 +40,7 @@ static void ColumnsPanel_delete(Object* object) { static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { ColumnsPanel* const this = (ColumnsPanel*) super; - + int selected = Panel_getSelectedIndex(super); HandlerResult result = IGNORED; int size = Panel_size(super); @@ -87,7 +87,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { case ']': case '+': { - if (selected < size - 2) + if (selected < size - 2) Panel_moveSelectedDown(super); result = HANDLED; break; diff --git a/ColumnsPanel.h b/ColumnsPanel.h index 0da674a8..46d20bbb 100644 --- a/ColumnsPanel.h +++ b/ColumnsPanel.h @@ -22,11 +22,11 @@ typedef struct ColumnsPanel_ { extern PanelClass ColumnsPanel_class; -ColumnsPanel* ColumnsPanel_new(Settings* settings); +extern ColumnsPanel* ColumnsPanel_new(Settings* settings); -int ColumnsPanel_fieldNameToIndex(const char* name); +extern int ColumnsPanel_fieldNameToIndex(const char* name); -void ColumnsPanel_update(Panel* super); +extern void ColumnsPanel_update(Panel* super); #endif diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 0ff54e33..0f44acc2 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -39,7 +39,7 @@ static void DisplayOptionsPanel_delete(Object* object) { static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { DisplayOptionsPanel* this = (DisplayOptionsPanel*) super; - + HandlerResult result = IGNORED; CheckItem* selected = (CheckItem*) Panel_getSelected(super); @@ -97,5 +97,8 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter))); + Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage))); + Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency))); + Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse))); return this; } diff --git a/DisplayOptionsPanel.h b/DisplayOptionsPanel.h index 2a7509ae..d9de4005 100644 --- a/DisplayOptionsPanel.h +++ b/DisplayOptionsPanel.h @@ -23,6 +23,6 @@ typedef struct DisplayOptionsPanel_ { extern PanelClass DisplayOptionsPanel_class; -DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr); +extern DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr); #endif diff --git a/EnvScreen.h b/EnvScreen.h index 7cdbb865..025cb8d3 100644 --- a/EnvScreen.h +++ b/EnvScreen.h @@ -11,12 +11,12 @@ typedef struct EnvScreen_ { extern InfoScreenClass EnvScreen_class; -EnvScreen* EnvScreen_new(Process* process); +extern EnvScreen* EnvScreen_new(Process* process); -void EnvScreen_delete(Object* this); +extern void EnvScreen_delete(Object* this); -void EnvScreen_draw(InfoScreen* this); +extern void EnvScreen_draw(InfoScreen* this); -void EnvScreen_scan(InfoScreen* this); +extern void EnvScreen_scan(InfoScreen* this); #endif diff --git a/FunctionBar.c b/FunctionBar.c index 4e4baaf3..8661bf0c 100644 --- a/FunctionBar.c +++ b/FunctionBar.c @@ -52,7 +52,7 @@ FunctionBar* FunctionBar_new(const char* const* functions, const char* const* ke this->functions[i] = xStrdup(functions[i]); } if (keys && events) { - this->staticData = false; + this->staticData = false; this->keys = xCalloc(15, sizeof(char*)); this->events = xCalloc(15, sizeof(int)); int i = 0; diff --git a/FunctionBar.h b/FunctionBar.h index b60f6582..ee5b9347 100644 --- a/FunctionBar.h +++ b/FunctionBar.h @@ -22,18 +22,18 @@ typedef struct FunctionBar_ { -FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc); +extern FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc); -FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events); +extern FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events); -void FunctionBar_delete(FunctionBar* this); +extern void FunctionBar_delete(FunctionBar* this); -void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); +extern void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); -void FunctionBar_draw(const FunctionBar* this, char* buffer); +extern void FunctionBar_draw(const FunctionBar* this, char* buffer); -void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr); +extern void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr); -int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos); +extern int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos); #endif diff --git a/Hashtable.c b/Hashtable.c index b3eac8da..3436dad6 100644 --- a/Hashtable.c +++ b/Hashtable.c @@ -63,7 +63,7 @@ int Hashtable_count(Hashtable* this) { static HashtableItem* HashtableItem_new(unsigned int key, void* value) { HashtableItem* this; - + this = xMalloc(sizeof(HashtableItem)); this->key = key; this->value = value; @@ -73,7 +73,7 @@ static HashtableItem* HashtableItem_new(unsigned int key, void* value) { Hashtable* Hashtable_new(int size, bool owner) { Hashtable* this; - + this = xMalloc(sizeof(Hashtable)); this->items = 0; this->size = size; @@ -119,10 +119,10 @@ void Hashtable_put(Hashtable* this, unsigned int key, void* value) { void* Hashtable_remove(Hashtable* this, unsigned int key) { unsigned int index = key % this->size; - + assert(Hashtable_isConsistent(this)); - HashtableItem** bucket; + HashtableItem** bucket; for (bucket = &(this->buckets[index]); *bucket; bucket = &((*bucket)->next) ) { if ((*bucket)->key == key) { void* value = (*bucket)->value; diff --git a/Hashtable.h b/Hashtable.h index 25608961..32b02188 100644 --- a/Hashtable.h +++ b/Hashtable.h @@ -30,20 +30,20 @@ struct Hashtable_ { #ifdef DEBUG -int Hashtable_count(Hashtable* this); +extern int Hashtable_count(Hashtable* this); #endif -Hashtable* Hashtable_new(int size, bool owner); +extern Hashtable* Hashtable_new(int size, bool owner); -void Hashtable_delete(Hashtable* this); +extern void Hashtable_delete(Hashtable* this); -void Hashtable_put(Hashtable* this, unsigned int key, void* value); +extern void Hashtable_put(Hashtable* this, unsigned int key, void* value); -void* Hashtable_remove(Hashtable* this, unsigned int key); +extern void* Hashtable_remove(Hashtable* this, unsigned int key); extern void* Hashtable_get(Hashtable* this, unsigned int key); -void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData); +extern void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData); #endif @@ -76,17 +76,17 @@ void Header_populateFromSettings(Header* this) { void Header_writeBackToSettings(const Header* this) { Header_forEachColumn(this, col) { MeterColumnSettings* colSettings = &this->settings->columns[col]; - + String_freeArray(colSettings->names); free(colSettings->modes); - + Vector* vec = this->columns[col]; int len = Vector_size(vec); - + colSettings->names = xCalloc(len+1, sizeof(char*)); colSettings->modes = xCalloc(len, sizeof(int)); colSettings->len = len; - + for (int i = 0; i < len; i++) { Meter* meter = (Meter*) Vector_get(vec, i); char* name = xCalloc(64, sizeof(char)); @@ -120,6 +120,8 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) { break; } } + if (paren) + *paren = '('; return mode; } @@ -186,7 +188,7 @@ void Header_draw(const Header* this) { } int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1; int x = pad; - + Header_forEachColumn(this, col) { Vector* meters = this->columns[col]; for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { @@ -31,30 +31,30 @@ typedef struct Header_ { #define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) #endif -Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns); +extern Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns); -void Header_delete(Header* this); +extern void Header_delete(Header* this); -void Header_populateFromSettings(Header* this); +extern void Header_populateFromSettings(Header* this); -void Header_writeBackToSettings(const Header* this); +extern void Header_writeBackToSettings(const Header* this); -MeterModeId Header_addMeterByName(Header* this, char* name, int column); +extern MeterModeId Header_addMeterByName(Header* this, char* name, int column); -void Header_setMode(Header* this, int i, MeterModeId mode, int column); +extern void Header_setMode(Header* this, int i, MeterModeId mode, int column); -Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column); +extern Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column); -int Header_size(Header* this, int column); +extern int Header_size(Header* this, int column); -char* Header_readMeterName(Header* this, int i, int column); +extern char* Header_readMeterName(Header* this, int i, int column); -MeterModeId Header_readMeterMode(Header* this, int i, int column); +extern MeterModeId Header_readMeterMode(Header* this, int i, int column); -void Header_reinit(Header* this); +extern void Header_reinit(Header* this); -void Header_draw(const Header* this); +extern void Header_draw(const Header* this); -int Header_calculateHeight(Header* this); +extern int Header_calculateHeight(Header* this); #endif @@ -52,6 +52,10 @@ static void IncMode_reset(IncMode* mode) { mode->buffer[0] = 0; } +void IncSet_reset(IncSet* this, IncType type) { + IncMode_reset(&this->modes[type]); +} + static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL}; static const char* const searchKeys[] = {"F3", "Esc", " "}; static int searchEvents[] = {KEY_F(3), 27, ERR}; @@ -132,6 +136,30 @@ static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa return found; } +static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) { + int size = Panel_size(panel); + int here = Panel_getSelectedIndex(panel); + int i = here; + for(;;) { + i+=step; + if (i == size) i = 0; + if (i == -1) i = size - 1; + if (i == here) return false; + if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { + Panel_setSelected(panel, i); + return true; + } + } +} + +bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) { + return IncMode_find(&this->modes[type], panel, getPanelValue, 1); +} + +bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) { + return IncMode_find(&this->modes[type], panel, getPanelValue, -1); +} + bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { if (ch == ERR) return true; @@ -141,17 +169,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue bool doSearch = true; if (ch == KEY_F(3)) { if (size == 0) return true; - int here = Panel_getSelectedIndex(panel); - int i = here; - for(;;) { - i++; - if (i == size) i = 0; - if (i == here) break; - if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { - Panel_setSelected(panel, i); - break; - } - } + IncMode_find(mode, panel, getPanelValue, 1); doSearch = false; } else if (ch < 255 && isprint((char)ch)) { if (mode->index < INCMODE_MAX) { @@ -187,7 +205,9 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue IncMode_reset(mode); } } else { - IncMode_reset(mode); + if (ch == 27) { + IncMode_reset(mode); + } } this->active = NULL; Panel_setDefaultBar(panel); @@ -41,18 +41,24 @@ typedef struct IncSet_ { typedef const char* (*IncMode_GetPanelValue)(Panel*, int); -IncSet* IncSet_new(FunctionBar* bar); +extern void IncSet_reset(IncSet* this, IncType type); -void IncSet_delete(IncSet* this); +extern IncSet* IncSet_new(FunctionBar* bar); -bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); +extern void IncSet_delete(IncSet* this); -const char* IncSet_getListItemValue(Panel* panel, int i); +extern bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue); -void IncSet_activate(IncSet* this, IncType type, Panel* panel); +extern bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue); -void IncSet_drawBar(IncSet* this); +extern bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); -int IncSet_synthesizeEvent(IncSet* this, int x); +extern const char* IncSet_getListItemValue(Panel* panel, int i); + +extern void IncSet_activate(IncSet* this, IncType type, Panel* panel); + +extern void IncSet_drawBar(IncSet* this); + +extern int IncSet_synthesizeEvent(IncSet* this, int x); #endif diff --git a/InfoScreen.c b/InfoScreen.c index fab8daea..0b919932 100644 --- a/InfoScreen.c +++ b/InfoScreen.c @@ -120,7 +120,7 @@ void InfoScreen_run(InfoScreen* this) { } set_escdelay(25); int ch = getch(); - + if (ch == ERR) { if (As_InfoScreen(this)->onErr) { InfoScreen_onErr(this); @@ -131,19 +131,21 @@ void InfoScreen_run(InfoScreen* this) { if (ch == KEY_MOUSE) { MEVENT mevent; int ok = getmouse(&mevent); - if (ok == OK) + if (ok == OK) { if (mevent.y >= panel->y && mevent.y < LINES - 1) { Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV); ch = 0; - } if (mevent.y == LINES - 1) + } else if (mevent.y == LINES - 1) { ch = IncSet_synthesizeEvent(this->inc, mevent.x); + } + } } if (this->inc->active) { IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines); continue; } - + switch(ch) { case ERR: continue; diff --git a/InfoScreen.h b/InfoScreen.h index 2e1599f9..b9387cda 100644 --- a/InfoScreen.h +++ b/InfoScreen.h @@ -38,16 +38,16 @@ struct InfoScreen_ { Vector* lines; }; -InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader); +extern InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader); -InfoScreen* InfoScreen_done(InfoScreen* this); +extern InfoScreen* InfoScreen_done(InfoScreen* this); -void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...); +extern void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...); -void InfoScreen_addLine(InfoScreen* this, const char* line); +extern void InfoScreen_addLine(InfoScreen* this, const char* line); -void InfoScreen_appendLine(InfoScreen* this, const char* line); +extern void InfoScreen_appendLine(InfoScreen* this, const char* line); -void InfoScreen_run(InfoScreen* this); +extern void InfoScreen_run(InfoScreen* this); #endif @@ -21,13 +21,13 @@ typedef struct ListItem_ { extern ObjectClass ListItem_class; -ListItem* ListItem_new(const char* value, int key); +extern ListItem* ListItem_new(const char* value, int key); -void ListItem_append(ListItem* this, const char* text); +extern void ListItem_append(ListItem* this, const char* text); -const char* ListItem_getRef(ListItem* this); +extern const char* ListItem_getRef(ListItem* this); -long ListItem_compare(const void* cast1, const void* cast2); +extern long ListItem_compare(const void* cast1, const void* cast2); #endif diff --git a/MainPanel.c b/MainPanel.c index 25023367..55be17bf 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -1,6 +1,7 @@ /* htop - ColumnsPanel.c (C) 2004-2015 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -25,18 +26,13 @@ typedef struct MainPanel_ { pid_t pidSearch; } MainPanel; -typedef union { - int i; - void* v; -} Arg; - typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg); #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) }*/ -static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL}; +static const char* const MainFunctions[] = {"Help ", "Setup ", "Search ", "Filter ", "Tree ", "SortBy ", "Nice - ", "Nice + ", "Kill ", "Quit ", NULL}; void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { FunctionBar* bar = MainPanel_getFunctionBar(this); @@ -69,7 +65,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { MainPanel* this = (MainPanel*) super; HandlerResult result = IGNORED; - + Htop_Reaction reaction = HTOP_OK; if (EVENT_IS_HEADER_CLICK(ch)) { @@ -84,7 +80,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } else { reaction |= Action_setSortKey(settings, field); } - reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS; + reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS; result = HANDLED; } else if (ch != ERR && this->inc->active) { bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL); @@ -121,7 +117,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } if (reaction & HTOP_REFRESH) { result |= REDRAW; - } + } if (reaction & HTOP_RECALCULATE) { result |= RESCAN; } diff --git a/MainPanel.h b/MainPanel.h index 88496597..6162d8e9 100644 --- a/MainPanel.h +++ b/MainPanel.h @@ -5,6 +5,7 @@ /* htop - ColumnsPanel.h (C) 2004-2015 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -21,32 +22,27 @@ typedef struct MainPanel_ { pid_t pidSearch; } MainPanel; -typedef union { - int i; - void* v; -} Arg; - typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg); #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) -void MainPanel_updateTreeFunctions(MainPanel* this, bool mode); +extern void MainPanel_updateTreeFunctions(MainPanel* this, bool mode); -void MainPanel_pidSearch(MainPanel* this, int ch); +extern void MainPanel_pidSearch(MainPanel* this, int ch); -int MainPanel_selectedPid(MainPanel* this); +extern int MainPanel_selectedPid(MainPanel* this); -const char* MainPanel_getValue(MainPanel* this, int i); +extern const char* MainPanel_getValue(MainPanel* this, int i); -bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged); +extern bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged); extern PanelClass MainPanel_class; -MainPanel* MainPanel_new(); +extern MainPanel* MainPanel_new(); -void MainPanel_setState(MainPanel* this, State* state); +extern void MainPanel_setState(MainPanel* this, State* state); -void MainPanel_delete(Object* object); +extern void MainPanel_delete(Object* object); #endif diff --git a/Makefile.am b/Makefile.am index 2d159f02..159e22a7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,6 +49,7 @@ linux_platform_headers = \ linux/LinuxProcessList.h \ linux/LinuxCRT.h \ linux/Battery.h \ + linux/PressureStallMeter.h \ zfs/ZfsArcMeter.h \ zfs/ZfsCompressedArcMeter.h \ zfs/ZfsArcStats.h @@ -56,9 +57,10 @@ linux_platform_headers = \ all_platform_headers += $(linux_platform_headers) if HTOP_LINUX -AM_CFLAGS += -rdynamic +AM_LDFLAGS += -rdynamic myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \ linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \ +linux/PressureStallMeter.c \ zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c myhtopplatheaders = $(linux_platform_headers) diff --git a/MemoryMeter.c b/MemoryMeter.c index fbf5330c..8d5d809e 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -60,7 +60,7 @@ MeterClass MemoryMeter_class = { .delete = Meter_delete, .display = MemoryMeter_display, }, - .updateValues = MemoryMeter_updateValues, + .updateValues = MemoryMeter_updateValues, .defaultMode = BAR_METERMODE, .maxItems = 3, .total = 100.0, @@ -78,7 +78,7 @@ typedef struct MeterClass_ { struct Meter_ { Object super; Meter_Draw draw; - + char* caption; int mode; int param; @@ -275,7 +275,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) { attrset(CRT_colors[BAR_BORDER]); mvaddch(y, x, '['); mvaddch(y, x + w, ']'); - + w--; x++; @@ -284,7 +284,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) { return; } char bar[w + 1]; - + int blockSizes[10]; xSnprintf(bar, w + 1, "%*.*s", w, w, buffer); @@ -378,7 +378,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { mvaddnstr(y, x, this->caption, captionLen); x += captionLen; w -= captionLen; - + struct timeval now; gettimeofday(&now, NULL); if (!timercmp(&now, &(data->time), <)) { @@ -387,10 +387,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { for (int i = 0; i < nValues - 1; i++) data->values[i] = data->values[i+1]; - + char buffer[nValues]; Meter_updateValues(this, buffer, nValues - 1); - + double value = 0.0; int items = Meter_getItems(this); for (int i = 0; i < items; i++) @@ -398,7 +398,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { value /= this->total; data->values[nValues - 1] = value; } - + int i = nValues - (w*2) + 2, k = 0; if (i < 0) { k = -i/2; @@ -459,7 +459,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) { char buffer[METER_BUFFER_LEN]; Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1); - + RichString_begin(out); Meter_displayBuffer(this, buffer, &out); @@ -523,6 +523,9 @@ MeterMode* Meter_modes[] = { static void BlankMeter_updateValues(Meter* this, char* buffer, int size) { (void) this; (void) buffer; (void) size; + if (size > 0) { + *buffer = 0; + } } static void BlankMeter_display(Object* cast, RichString* out) { @@ -65,7 +65,7 @@ typedef struct MeterClass_ { struct Meter_ { Object super; Meter_Draw draw; - + char* caption; int mode; int param; @@ -109,17 +109,17 @@ typedef struct GraphData_ { extern MeterClass Meter_class; -Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type); +extern Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type); -int Meter_humanUnit(char* buffer, unsigned long int value, int size); +extern int Meter_humanUnit(char* buffer, unsigned long int value, int size); -void Meter_delete(Object* cast); +extern void Meter_delete(Object* cast); -void Meter_setCaption(Meter* this, const char* caption); +extern void Meter_setCaption(Meter* this, const char* caption); -void Meter_setMode(Meter* this, int modeIndex); +extern void Meter_setMode(Meter* this, int modeIndex); -ListItem* Meter_toListItem(Meter* this, bool moving); +extern ListItem* Meter_toListItem(Meter* this, bool moving); /* ---------- TextMeterMode ---------- */ diff --git a/MetersPanel.c b/MetersPanel.c index 3cf3e075..397a6c7d 100644 --- a/MetersPanel.c +++ b/MetersPanel.c @@ -93,7 +93,7 @@ static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { MetersPanel* this = (MetersPanel*) super; - + int selected = Panel_getSelectedIndex(super); HandlerResult result = IGNORED; bool sideMove = false; diff --git a/MetersPanel.h b/MetersPanel.h index e00169c8..12eabbe6 100644 --- a/MetersPanel.h +++ b/MetersPanel.h @@ -34,10 +34,10 @@ struct MetersPanel_ { // In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are // considered "Ambiguous characters". -void MetersPanel_setMoving(MetersPanel* this, bool moving); +extern void MetersPanel_setMoving(MetersPanel* this, bool moving); extern PanelClass MetersPanel_class; -MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr); +extern MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr); #endif @@ -1,6 +1,7 @@ /* htop - Object.c (C) 2004-2012 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -28,7 +29,7 @@ typedef void(*Object_Delete)(Object*); #define Class(class_) ((ObjectClass*)(&(class_ ## _class))) #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_)); - + typedef struct ObjectClass_ { const void* extends; const Object_Display display; @@ -40,6 +41,11 @@ struct Object_ { ObjectClass* klass; }; +typedef union { + int i; + void* v; +} Arg; + }*/ ObjectClass Object_class = { @@ -5,6 +5,7 @@ /* htop - Object.h (C) 2004-2012 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -29,7 +30,7 @@ typedef void(*Object_Delete)(Object*); #define Class(class_) ((ObjectClass*)(&(class_ ## _class))) #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_)); - + typedef struct ObjectClass_ { const void* extends; const Object_Display display; @@ -41,12 +42,17 @@ struct Object_ { ObjectClass* klass; }; +typedef union { + int i; + void* v; +} Arg; + extern ObjectClass Object_class; #ifdef DEBUG -bool Object_isA(Object* o, const ObjectClass* klass); +extern bool Object_isA(Object* o, const ObjectClass* klass); #endif diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c index 9ea333bf..658b1866 100644 --- a/OpenFilesScreen.c +++ b/OpenFilesScreen.c @@ -124,6 +124,7 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { item->data[cmd] = xStrdup(line + 1); free(line); } + fclose(fd); int wstatus; if (waitpid(child, &wstatus, 0) == -1) { pdata->error = 1; diff --git a/OpenFilesScreen.h b/OpenFilesScreen.h index 8160cfe2..39c71d5b 100644 --- a/OpenFilesScreen.h +++ b/OpenFilesScreen.h @@ -34,12 +34,12 @@ typedef struct OpenFilesScreen_ { extern InfoScreenClass OpenFilesScreen_class; -OpenFilesScreen* OpenFilesScreen_new(Process* process); +extern OpenFilesScreen* OpenFilesScreen_new(Process* process); -void OpenFilesScreen_delete(Object* this); +extern void OpenFilesScreen_delete(Object* this); -void OpenFilesScreen_draw(InfoScreen* this); +extern void OpenFilesScreen_draw(InfoScreen* this); -void OpenFilesScreen_scan(InfoScreen* this); +extern void OpenFilesScreen_scan(InfoScreen* this); #endif @@ -372,7 +372,7 @@ void Panel_draw(Panel* this, bool focus) { bool Panel_onKey(Panel* this, int key) { assert (this != NULL); - + int size = Vector_size(this->items); switch (key) { case KEY_DOWN: @@ -455,7 +455,7 @@ bool Panel_onKey(Panel* this, int key) { if (this->selected < 0 || size == 0) { this->selected = 0; this->needsRedraw = true; - } else if (this->selected >= size) { + } else if (this->selected >= size) { this->selected = size - 1; this->needsRedraw = true; } @@ -75,52 +75,52 @@ struct Panel_ { extern PanelClass Panel_class; -Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar); +extern Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar); -void Panel_delete(Object* cast); +extern void Panel_delete(Object* cast); -void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar); +extern void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar); -void Panel_done(Panel* this); +extern void Panel_done(Panel* this); -void Panel_setSelectionColor(Panel* this, int color); +extern void Panel_setSelectionColor(Panel* this, int color); -RichString* Panel_getHeader(Panel* this); +extern RichString* Panel_getHeader(Panel* this); extern void Panel_setHeader(Panel* this, const char* header); -void Panel_move(Panel* this, int x, int y); +extern void Panel_move(Panel* this, int x, int y); -void Panel_resize(Panel* this, int w, int h); +extern void Panel_resize(Panel* this, int w, int h); -void Panel_prune(Panel* this); +extern void Panel_prune(Panel* this); -void Panel_add(Panel* this, Object* o); +extern void Panel_add(Panel* this, Object* o); -void Panel_insert(Panel* this, int i, Object* o); +extern void Panel_insert(Panel* this, int i, Object* o); -void Panel_set(Panel* this, int i, Object* o); +extern void Panel_set(Panel* this, int i, Object* o); -Object* Panel_get(Panel* this, int i); +extern Object* Panel_get(Panel* this, int i); -Object* Panel_remove(Panel* this, int i); +extern Object* Panel_remove(Panel* this, int i); -Object* Panel_getSelected(Panel* this); +extern Object* Panel_getSelected(Panel* this); -void Panel_moveSelectedUp(Panel* this); +extern void Panel_moveSelectedUp(Panel* this); -void Panel_moveSelectedDown(Panel* this); +extern void Panel_moveSelectedDown(Panel* this); -int Panel_getSelectedIndex(Panel* this); +extern int Panel_getSelectedIndex(Panel* this); -int Panel_size(Panel* this); +extern int Panel_size(Panel* this); -void Panel_setSelected(Panel* this, int selected); +extern void Panel_setSelected(Panel* this, int selected); -void Panel_draw(Panel* this, bool focus); +extern void Panel_draw(Panel* this, bool focus); -bool Panel_onKey(Panel* this, int key); +extern bool Panel_onKey(Panel* this, int key); -HandlerResult Panel_selectByTyping(Panel* this, int ch); +extern HandlerResult Panel_selectByTyping(Panel* this, int ch); #endif @@ -1,6 +1,7 @@ /* htop - Process.c (C) 2004-2015 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -8,6 +9,8 @@ in the source distribution for its full text. #include "Process.h" #include "Settings.h" +#include "config.h" + #include "CRT.h" #include "StringUtils.h" #include "RichString.h" @@ -30,8 +33,7 @@ in the source distribution for its full text. #include <math.h> #ifdef MAJOR_IN_MKDEV #include <sys/mkdev.h> -#elif defined(MAJOR_IN_SYSMACROS) || \ - (defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H) +#elif defined(MAJOR_IN_SYSMACROS) #include <sys/sysmacros.h> #endif @@ -191,10 +193,12 @@ static int Process_getuid = -1; #define ONE_K 1024L #define ONE_M (ONE_K * ONE_K) #define ONE_G (ONE_M * ONE_K) +#define ONE_T ((long long)ONE_G * ONE_K) #define ONE_DECIMAL_K 1000L #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) +#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K) char Process_pidFormat[20] = "%7d "; @@ -216,7 +220,7 @@ void Process_setupColumnWidths() { void Process_humanNumber(RichString* str, unsigned long number, bool coloring) { char buffer[11]; int len; - + int largeNumberColor = CRT_colors[LARGE_NUMBER]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processColor = CRT_colors[PROCESS]; @@ -224,7 +228,7 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) { largeNumberColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS]; } - + if(number >= (10 * ONE_DECIMAL_M)) { #ifdef __LP64__ if(number >= (100 * ONE_DECIMAL_G)) { @@ -277,8 +281,15 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori if ((long long) number == -1LL) { int len = snprintf(buffer, 13, " no perm "); RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len); - } else if (number > 10000000000) { - xSnprintf(buffer, 13, "%11llu ", number / 1000); + } else if (number >= 100000LL * ONE_DECIMAL_T) { + xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_G); + RichString_appendn(str, largeNumberColor, buffer, 12); + } else if (number >= 100LL * ONE_DECIMAL_T) { + xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M); + RichString_appendn(str, largeNumberColor, buffer, 8); + RichString_appendn(str, processMegabytesColor, buffer+8, 4); + } else if (number >= 10LL * ONE_DECIMAL_G) { + xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K); RichString_appendn(str, largeNumberColor, buffer, 5); RichString_appendn(str, processMegabytesColor, buffer+5, 3); RichString_appendn(str, processColor, buffer+8, 4); @@ -358,14 +369,17 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c } else if (rate < ONE_K) { int len = snprintf(buffer, n, "%7.2f B/s ", rate); RichString_appendn(str, processColor, buffer, len); - } else if (rate < ONE_K * ONE_K) { + } else if (rate < ONE_M) { int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K); RichString_appendn(str, processColor, buffer, len); - } else if (rate < ONE_K * ONE_K * ONE_K) { - int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_K / ONE_K); + } else if (rate < ONE_G) { + int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M); RichString_appendn(str, processMegabytesColor, buffer, len); + } else if (rate < ONE_T) { + int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G); + RichString_appendn(str, largeNumberColor, buffer, len); } else { - int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_K / ONE_K / ONE_K); + int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T); RichString_appendn(str, largeNumberColor, buffer, len); } } @@ -380,9 +394,9 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) { switch (field) { case PERCENT_CPU: { if (this->percent_cpu > 999.9) { - xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu); + xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu); } else if (this->percent_cpu > 99.9) { - xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu); + xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu); } else { xSnprintf(buffer, n, "%4.1f ", this->percent_cpu); } @@ -390,7 +404,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) { } case PERCENT_MEM: { if (this->percent_mem > 99.9) { - xSnprintf(buffer, n, "100. "); + xSnprintf(buffer, n, "100. "); } else { xSnprintf(buffer, n, "%4.1f ", this->percent_mem); } @@ -471,7 +485,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) { } break; } - case ST_UID: xSnprintf(buffer, n, "%4d ", this->st_uid); break; + case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break; case TIME: Process_printTime(str, this->time); return; case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break; case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break; @@ -549,14 +563,15 @@ bool Process_setPriority(Process* this, int priority) { return (err == 0); } -bool Process_changePriorityBy(Process* this, int delta) { - return Process_setPriority(this, this->nice + delta); +bool Process_changePriorityBy(Process* this, Arg delta) { + return Process_setPriority(this, this->nice + delta.i); } -void Process_sendSignal(Process* this, int sgn) { +bool Process_sendSignal(Process* this, Arg sgn) { CRT_dropPrivileges(); - kill(this->pid, (int) sgn); + bool ok = (kill(this->pid, sgn.i) == 0); CRT_restorePrivileges(); + return ok; } long Process_pidCompare(const void* v1, const void* v2) { @@ -5,13 +5,13 @@ /* htop - Process.h (C) 2004-2015 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ #ifdef MAJOR_IN_MKDEV -#elif defined(MAJOR_IN_SYSMACROS) || \ - (defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H) +#elif defined(MAJOR_IN_SYSMACROS) #endif #ifdef __ANDROID__ @@ -166,43 +166,45 @@ typedef struct ProcessClass_ { #define ONE_K 1024L #define ONE_M (ONE_K * ONE_K) #define ONE_G (ONE_M * ONE_K) +#define ONE_T ((long long)ONE_G * ONE_K) #define ONE_DECIMAL_K 1000L #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) +#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K) extern char Process_pidFormat[20]; -void Process_setupColumnWidths(); +extern void Process_setupColumnWidths(); -void Process_humanNumber(RichString* str, unsigned long number, bool coloring); +extern void Process_humanNumber(RichString* str, unsigned long number, bool coloring); -void Process_colorNumber(RichString* str, unsigned long long number, bool coloring); +extern void Process_colorNumber(RichString* str, unsigned long long number, bool coloring); -void Process_printTime(RichString* str, unsigned long long totalHundredths); +extern void Process_printTime(RichString* str, unsigned long long totalHundredths); -void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring); +extern void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring); -void Process_writeField(Process* this, RichString* str, ProcessField field); +extern void Process_writeField(Process* this, RichString* str, ProcessField field); -void Process_display(Object* cast, RichString* out); +extern void Process_display(Object* cast, RichString* out); -void Process_done(Process* this); +extern void Process_done(Process* this); extern ProcessClass Process_class; -void Process_init(Process* this, struct Settings_* settings); +extern void Process_init(Process* this, struct Settings_* settings); -void Process_toggleTag(Process* this); +extern void Process_toggleTag(Process* this); -bool Process_setPriority(Process* this, int priority); +extern bool Process_setPriority(Process* this, int priority); -bool Process_changePriorityBy(Process* this, int delta); +extern bool Process_changePriorityBy(Process* this, Arg delta); -void Process_sendSignal(Process* this, int sgn); +extern bool Process_sendSignal(Process* this, Arg sgn); -long Process_pidCompare(const void* v1, const void* v2); +extern long Process_pidCompare(const void* v1, const void* v2); -long Process_compare(const void* v1, const void* v2); +extern long Process_compare(const void* v1, const void* v2); #endif diff --git a/ProcessList.c b/ProcessList.c index 7482b037..edbc114c 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -84,10 +84,10 @@ ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* this->usersTable = usersTable; this->pidWhiteList = pidWhiteList; this->userId = userId; - + // tree-view auxiliary buffer this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); - + // set later by platform-specific code this->cpuCount = 0; @@ -138,10 +138,10 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) { void ProcessList_add(ProcessList* this, Process* p) { assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1); assert(Hashtable_get(this->processTable, p->pid) == NULL); - + Vector_add(this->processes, p); Hashtable_put(this->processTable, p->pid, p); - + assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); assert(Hashtable_get(this->processTable, p->pid) != NULL); assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); @@ -353,7 +353,7 @@ void ProcessList_scan(ProcessList* this) { this->runningTasks = 0; ProcessList_goThroughEntries(this); - + for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { Process* p = (Process*) Vector_get(this->processes, i); if (p->updated == false) diff --git a/ProcessList.h b/ProcessList.h index 572d4843..ddc6f3fa 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -71,32 +71,32 @@ void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* pl); -ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); +extern ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); -void ProcessList_done(ProcessList* this); +extern void ProcessList_done(ProcessList* this); -void ProcessList_setPanel(ProcessList* this, Panel* panel); +extern void ProcessList_setPanel(ProcessList* this, Panel* panel); -void ProcessList_printHeader(ProcessList* this, RichString* header); +extern void ProcessList_printHeader(ProcessList* this, RichString* header); -void ProcessList_add(ProcessList* this, Process* p); +extern void ProcessList_add(ProcessList* this, Process* p); -void ProcessList_remove(ProcessList* this, Process* p); +extern void ProcessList_remove(ProcessList* this, Process* p); -Process* ProcessList_get(ProcessList* this, int idx); +extern Process* ProcessList_get(ProcessList* this, int idx); -int ProcessList_size(ProcessList* this); +extern int ProcessList_size(ProcessList* this); -void ProcessList_sort(ProcessList* this); +extern void ProcessList_sort(ProcessList* this); -ProcessField ProcessList_keyAt(ProcessList* this, int at); +extern ProcessField ProcessList_keyAt(ProcessList* this, int at); -void ProcessList_expandTree(ProcessList* this); +extern void ProcessList_expandTree(ProcessList* this); -void ProcessList_rebuildPanel(ProcessList* this); +extern void ProcessList_rebuildPanel(ProcessList* this); -Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); +extern Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); -void ProcessList_scan(ProcessList* this); +extern void ProcessList_scan(ProcessList* this); #endif @@ -1,46 +1,28 @@ -[![Build Status](https://travis-ci.org/hishamhm/htop.svg?branch=master)](https://travis-ci.org/hishamhm/htop) -[![PayPal donate](https://img.shields.io/badge/paypal-donate-green.svg)](http://hisham.hm/htop/index.php?page=donate) +![htop](https://htop.dev) -[htop](http://hisham.hm/htop/) -==== - -by Hisham Muhammad <hisham@gobolinux.org> (2004 - 2016) +[![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/21617/badge.svg)](https://scan.coverity.com/projects/21617) +[![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop) +[![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://webchat.freenode.net/#htop) +[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest) Introduction ------------ -This is `htop`, an interactive process viewer. -It requires `ncurses`. It is developed primarily on Linux, -but we also have code for running under FreeBSD and Mac OS X -(help and testing are wanted for these platforms!) - -This software has evolved considerably over the years, -and is reasonably complete, but there is always room for improvement. - -Comparison between `htop` and classic `top` -------------------------------------------- - -* In `htop` you can scroll the list vertically and horizontally - to see all processes and full command lines. -* In `top` you are subject to a delay for each unassigned - key you press (especially annoying when multi-key escape - sequences are triggered by accident). -* `htop` starts faster (`top` seems to collect data for a while - before displaying anything). -* In `htop` you don't need to type the process number to - kill a process, in `top` you do. -* In `htop` you don't need to type the process number or - the priority value to renice a process, in `top` you do. -* In `htop` you can kill multiple processes at once. -* `top` is older, hence, more tested. - -Compilation instructions ------------------------- +`htop` is a cross-platform interactive process viewer. +It requires `ncurses`. + +For more information and details on how to contribute to `htop` +visit [htop.dev](https://htop.dev). + +Build instructions +------------------ This program is distributed as a standard autotools-based package. -See the [INSTALL](/INSTALL) file for detailed instructions. +For detailed instructions see the [INSTALL](/INSTALL) file, which +is created after `./autogen.sh` is run. -When compiling from a [release tarball](https://hisham.hm/htop/releases/), run: +When compiling from a [release tarball](https://github.com/htop-dev/htop/releases/), run: ./configure && make @@ -54,8 +36,6 @@ the path use `./configure --prefix=/some/path`. See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands. -If not all keys work check your curses configuration. - ## License GNU General Public License, version 2 (GPL-2.0) diff --git a/RichString.c b/RichString.c index 370566a8..d246f1e1 100644 --- a/RichString.c +++ b/RichString.c @@ -67,7 +67,7 @@ typedef struct RichString_ { #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #endif -#define charBytes(n) (sizeof(CharType) * (n)) +#define charBytes(n) (sizeof(CharType) * (n)) static void RichString_extendLen(RichString* this, int len) { if (this->chlen <= RICHSTRING_MAXLEN) { diff --git a/RichString.h b/RichString.h index f5b5cba1..d6fa6354 100644 --- a/RichString.h +++ b/RichString.h @@ -63,7 +63,7 @@ typedef struct RichString_ { #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #endif -#define charBytes(n) (sizeof(CharType) * (n)) +#define charBytes(n) (sizeof(CharType) * (n)) #define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0) @@ -71,24 +71,24 @@ typedef struct RichString_ { extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish); -int RichString_findChar(RichString* this, char c, int start); +extern int RichString_findChar(RichString* this, char c, int start); #else -void RichString_setAttrn(RichString* this, int attrs, int start, int finish); +extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish); -int RichString_findChar(RichString* this, char c, int start); +extern int RichString_findChar(RichString* this, char c, int start); #endif -void RichString_prune(RichString* this); +extern void RichString_prune(RichString* this); -void RichString_setAttr(RichString* this, int attrs); +extern void RichString_setAttr(RichString* this, int attrs); -void RichString_append(RichString* this, int attrs, const char* data); +extern void RichString_append(RichString* this, int attrs, const char* data); -void RichString_appendn(RichString* this, int attrs, const char* data, int len); +extern void RichString_appendn(RichString* this, int attrs, const char* data, int len); -void RichString_write(RichString* this, int attrs, const char* data); +extern void RichString_write(RichString* this, int attrs, const char* data); #endif diff --git a/ScreenManager.c b/ScreenManager.c index 06e90193..8e5f697f 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -163,7 +163,7 @@ static Panel* setCurrentPanel(Panel* panel) { void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { bool quit = false; int focus = 0; - + Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus)); double oldTime = 0.0; @@ -181,7 +181,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { if (this->header) { checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut); } - + if (redraw) { ScreenManager_drawPanels(this, focus); } @@ -190,8 +190,24 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { set_escdelay(25); ch = getch(); + if (this->settings->vimMode) { + switch (ch) { + case 'h': ch = KEY_LEFT; break; + case 'j': ch = KEY_DOWN; break; + case 'k': ch = KEY_UP; break; + case 'l': ch = KEY_RIGHT; break; + case KEY_LEFT: ch = 'h'; break; + case KEY_DOWN: ch = 'j'; break; + case KEY_UP: ch = 'k'; break; + case KEY_RIGHT: ch = 'l'; break; + case 'K': ch = 'k'; break; + case 'J': ch = 'K'; break; + case 'L': ch = 'l'; break; + } + } + HandlerResult result = IGNORED; - if (ch == KEY_MOUSE) { + if (ch == KEY_MOUSE && this->settings->enableMouse) { ch = ERR; MEVENT mevent; int ok = getmouse(&mevent); @@ -269,7 +285,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { quit = true; continue; } - + switch (ch) { case KEY_RESIZE: { diff --git a/ScreenManager.h b/ScreenManager.h index 3d02a883..b7b5617b 100644 --- a/ScreenManager.h +++ b/ScreenManager.h @@ -35,18 +35,18 @@ typedef struct ScreenManager_ { } ScreenManager; -ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner); +extern ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner); -void ScreenManager_delete(ScreenManager* this); +extern void ScreenManager_delete(ScreenManager* this); extern int ScreenManager_size(ScreenManager* this); -void ScreenManager_add(ScreenManager* this, Panel* item, int size); +extern void ScreenManager_add(ScreenManager* this, Panel* item, int size); -Panel* ScreenManager_remove(ScreenManager* this, int idx); +extern Panel* ScreenManager_remove(ScreenManager* this, int idx); -void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2); +extern void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2); -void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey); +extern void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey); #endif @@ -31,7 +31,7 @@ typedef struct { typedef struct Settings_ { char* filename; - + MeterColumnSettings columns[2]; ProcessField* fields; @@ -45,6 +45,8 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUUsage; + bool showCPUFrequency; bool treeView; bool showProgramPath; bool hideThreads; @@ -58,6 +60,8 @@ typedef struct Settings_ { bool updateProcessNames; bool accountGuestInCPUMeter; bool headerMargin; + bool enableMouse; + bool vimMode; bool changed; } Settings; @@ -114,7 +118,7 @@ static void Settings_defaultMeters(Settings* this) { this->columns[i].modes = xCalloc(sizes[i], sizeof(int)); this->columns[i].len = sizes[i]; } - + int r = 0; if (this->cpuCount > 8) { this->columns[0].names[0] = xStrdup("LeftCPUs2"); @@ -134,7 +138,7 @@ static void Settings_defaultMeters(Settings* this) { this->columns[0].modes[1] = BAR_METERMODE; this->columns[0].names[2] = xStrdup("Swap"); this->columns[0].modes[2] = BAR_METERMODE; - + this->columns[1].names[r] = xStrdup("Tasks"); this->columns[1].modes[r++] = TEXT_METERMODE; this->columns[1].names[r] = xStrdup("LoadAverage"); @@ -165,13 +169,13 @@ static void readFields(ProcessField* fields, int* flags, const char* line) { static bool Settings_read(Settings* this, const char* fileName) { FILE* fd; - + CRT_dropPrivileges(); fd = fopen(fileName, "r"); CRT_restorePrivileges(); if (!fd) return false; - + bool didReadMeters = false; bool didReadFields = false; for (;;) { @@ -223,6 +227,10 @@ static bool Settings_read(Settings* this, const char* fileName) { this->detailedCPUTime = atoi(option[1]); } else if (String_eq(option[0], "cpu_count_from_zero")) { this->countCPUsFromZero = atoi(option[1]); + } else if (String_eq(option[0], "show_cpu_usage")) { + this->showCPUUsage = atoi(option[1]); + } else if (String_eq(option[0], "show_cpu_frequency")) { + this->showCPUFrequency = atoi(option[1]); } else if (String_eq(option[0], "update_process_names")) { this->updateProcessNames = atoi(option[1]); } else if (String_eq(option[0], "account_guest_in_cpu_meter")) { @@ -232,6 +240,8 @@ static bool Settings_read(Settings* this, const char* fileName) { } else if (String_eq(option[0], "color_scheme")) { this->colorScheme = atoi(option[1]); if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0; + } else if (String_eq(option[0], "enable_mouse")) { + this->enableMouse = atoi(option[1]); } else if (String_eq(option[0], "left_meters")) { Settings_readMeters(this, option[1], 0); didReadMeters = true; @@ -244,6 +254,8 @@ static bool Settings_read(Settings* this, const char* fileName) { } else if (String_eq(option[0], "right_meter_modes")) { Settings_readMeterModes(this, option[1], 1); didReadMeters = true; + } else if (String_eq(option[0], "vim_mode")) { + this->vimMode = atoi(option[1]); } String_freeArray(option); } @@ -312,20 +324,24 @@ bool Settings_write(Settings* this) { fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); + fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage); + fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency); fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); + fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse); fprintf(fd, "delay=%d\n", (int) this->delay); fprintf(fd, "left_meters="); writeMeters(this, fd, 0); fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); + fprintf(fd, "vim_mode=%d\n", (int) this->vimMode); fclose(fd); return true; } Settings* Settings_new(int cpuCount) { - + Settings* this = xCalloc(1, sizeof(Settings)); this->sortKey = PERCENT_CPU; @@ -340,11 +356,13 @@ Settings* Settings_new(int cpuCount) { this->highlightMegabytes = false; this->detailedCPUTime = false; this->countCPUsFromZero = false; + this->showCPUUsage = true; + this->showCPUFrequency = false; this->updateProcessNames = false; this->cpuCount = cpuCount; this->showProgramPath = true; this->highlightThreads = true; - + this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField)); // TODO: turn 'fields' into a Vector, // (and ProcessFields into proper objects). @@ -375,7 +393,7 @@ Settings* Settings_new(int cpuCount) { htopDir = String_cat(home, "/.config/htop"); } legacyDotfile = String_cat(home, "/.htoprc"); - + CRT_dropPrivileges(); (void) mkdir(configDir, 0700); (void) mkdir(htopDir, 0700); @@ -390,6 +408,7 @@ Settings* Settings_new(int cpuCount) { CRT_restorePrivileges(); } this->colorScheme = 0; + this->enableMouse = true; this->changed = false; this->delay = DEFAULT_DELAY; bool ok = false; @@ -22,7 +22,7 @@ typedef struct { typedef struct Settings_ { char* filename; - + MeterColumnSettings columns[2]; ProcessField* fields; @@ -36,6 +36,8 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUUsage; + bool showCPUFrequency; bool treeView; bool showProgramPath; bool hideThreads; @@ -49,6 +51,8 @@ typedef struct Settings_ { bool updateProcessNames; bool accountGuestInCPUMeter; bool headerMargin; + bool enableMouse; + bool vimMode; bool changed; } Settings; @@ -58,12 +62,12 @@ typedef struct Settings_ { #endif -void Settings_delete(Settings* this); +extern void Settings_delete(Settings* this); -bool Settings_write(Settings* this); +extern bool Settings_write(Settings* this); -Settings* Settings_new(int cpuCount); +extern Settings* Settings_new(int cpuCount); -void Settings_invertSortOrder(Settings* this); +extern void Settings_invertSortOrder(Settings* this); #endif diff --git a/SignalsPanel.h b/SignalsPanel.h index da753546..8720376a 100644 --- a/SignalsPanel.h +++ b/SignalsPanel.h @@ -16,6 +16,6 @@ typedef struct SignalItem_ { } SignalItem; -Panel* SignalsPanel_new(); +extern Panel* SignalsPanel_new(); #endif diff --git a/StringUtils.c b/StringUtils.c index 0578cdea..0ac54b12 100644 --- a/StringUtils.c +++ b/StringUtils.c @@ -30,8 +30,9 @@ char* String_cat(const char* s1, const char* s2) { int l1 = strlen(s1); int l2 = strlen(s2); char* out = xMalloc(l1 + l2 + 1); - strncpy(out, s1, l1); - strncpy(out+l1, s2, l2+1); + memcpy(out, s1, l1); + memcpy(out+l1, s2, l2+1); + out[l1 + l2] = '\0'; return out; } @@ -116,7 +117,7 @@ char* String_getToken(const char* line, const unsigned short int numMatch) { if (lastState == 0 && inWord == 1) count++; - + if(inWord == 1){ if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) { match[foundCount] = line[i]; diff --git a/StringUtils.h b/StringUtils.h index d3378b38..c1070d4f 100644 --- a/StringUtils.h +++ b/StringUtils.h @@ -19,18 +19,18 @@ in the source distribution for its full text. * at compile time (e.g. when they are immutable string literals). :) */ -char* String_cat(const char* s1, const char* s2); +extern char* String_cat(const char* s1, const char* s2); -char* String_trim(const char* in); +extern char* String_trim(const char* in); extern int String_eq(const char* s1, const char* s2); -char** String_split(const char* s, char sep, int* n); +extern char** String_split(const char* s, char sep, int* n); -void String_freeArray(char** s); +extern void String_freeArray(char** s); -char* String_getToken(const char* line, const unsigned short int numMatch); +extern char* String_getToken(const char* line, const unsigned short int numMatch); -char* String_readLine(FILE* fd); +extern char* String_readLine(FILE* fd); #endif diff --git a/TasksMeter.c b/TasksMeter.c index f56c8613..8a913ec0 100644 --- a/TasksMeter.c +++ b/TasksMeter.c @@ -15,7 +15,7 @@ in the source distribution for its full text. }*/ int TasksMeter_attributes[] = { - CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING + CPU_SYSTEM, PROCESS_THREAD, PROCESS, TASKS_RUNNING }; static void TasksMeter_updateValues(Meter* this, char* buffer, int len) { @@ -37,9 +37,9 @@ static void TasksMeter_display(Object* cast, RichString* out) { Meter* this = (Meter*)cast; Settings* settings = this->pl->settings; char buffer[20]; - + int processes = (int) this->values[2]; - + xSnprintf(buffer, sizeof(buffer), "%d", processes); RichString_write(out, CRT_colors[METER_VALUE], buffer); int threadValueColor = CRT_colors[METER_VALUE]; @@ -76,7 +76,7 @@ MeterClass TasksMeter_class = { .defaultMode = TEXT_METERMODE, .maxItems = 4, .total = 100.0, - .attributes = TasksMeter_attributes, + .attributes = TasksMeter_attributes, .name = "Tasks", .uiName = "Task counter", .caption = "Tasks: " diff --git a/TraceScreen.c b/TraceScreen.c index 91f71ae8..b4b8436b 100644 --- a/TraceScreen.c +++ b/TraceScreen.c @@ -101,7 +101,7 @@ bool TraceScreen_forkTracer(TraceScreen* this) { int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK); if (ok != -1) { xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); - execlp("strace", "strace", "-s", "512", "-p", buffer, NULL); + execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL); } const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; ssize_t written = write(this->fdpair[1], message, strlen(message)); diff --git a/TraceScreen.h b/TraceScreen.h index 8845c51a..d43281d9 100644 --- a/TraceScreen.h +++ b/TraceScreen.h @@ -25,16 +25,16 @@ typedef struct TraceScreen_ { extern InfoScreenClass TraceScreen_class; -TraceScreen* TraceScreen_new(Process* process); +extern TraceScreen* TraceScreen_new(Process* process); -void TraceScreen_delete(Object* cast); +extern void TraceScreen_delete(Object* cast); -void TraceScreen_draw(InfoScreen* this); +extern void TraceScreen_draw(InfoScreen* this); -bool TraceScreen_forkTracer(TraceScreen* this); +extern bool TraceScreen_forkTracer(TraceScreen* this); -void TraceScreen_updateTrace(InfoScreen* super); +extern void TraceScreen_updateTrace(InfoScreen* super); -bool TraceScreen_onKey(InfoScreen* super, int ch); +extern bool TraceScreen_onKey(InfoScreen* super, int ch); #endif diff --git a/UsersTable.h b/UsersTable.h index 93ece0dc..9eba59ad 100644 --- a/UsersTable.h +++ b/UsersTable.h @@ -15,11 +15,11 @@ typedef struct UsersTable_ { Hashtable* users; } UsersTable; -UsersTable* UsersTable_new(); +extern UsersTable* UsersTable_new(); -void UsersTable_delete(UsersTable* this); +extern void UsersTable_delete(UsersTable* this); -char* UsersTable_getRef(UsersTable* this, unsigned int uid); +extern char* UsersTable_getRef(UsersTable* this, unsigned int uid); extern void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData); @@ -195,7 +195,7 @@ void Vector_insert(Vector* this, int idx, void* data_) { if (idx > this->items) { idx = this->items; } - + Vector_checkArraySize(this); //assert(this->array[this->items] == NULL); for (int i = this->items; i > idx; i--) { @@ -302,7 +302,7 @@ inline int Vector_size(Vector* this) { static void Vector_merge(Vector* this, Vector* v2) { int i; assert(Vector_isConsistent(this)); - + for (i = 0; i < v2->items; i++) Vector_add(this, v2->array[i]); v2->items = 0; @@ -27,38 +27,38 @@ typedef struct Vector_ { } Vector; -Vector* Vector_new(ObjectClass* type, bool owner, int size); +extern Vector* Vector_new(ObjectClass* type, bool owner, int size); -void Vector_delete(Vector* this); +extern void Vector_delete(Vector* this); #ifdef DEBUG -int Vector_count(Vector* this); +extern int Vector_count(Vector* this); #endif -void Vector_prune(Vector* this); +extern void Vector_prune(Vector* this); // If I were to use only one sorting algorithm for both cases, it would probably be this one: /* */ -void Vector_quickSort(Vector* this); +extern void Vector_quickSort(Vector* this); -void Vector_insertionSort(Vector* this); +extern void Vector_insertionSort(Vector* this); -void Vector_insert(Vector* this, int idx, void* data_); +extern void Vector_insert(Vector* this, int idx, void* data_); -Object* Vector_take(Vector* this, int idx); +extern Object* Vector_take(Vector* this, int idx); -Object* Vector_remove(Vector* this, int idx); +extern Object* Vector_remove(Vector* this, int idx); -void Vector_moveUp(Vector* this, int idx); +extern void Vector_moveUp(Vector* this, int idx); -void Vector_moveDown(Vector* this, int idx); +extern void Vector_moveDown(Vector* this, int idx); -void Vector_set(Vector* this, int idx, void* data_); +extern void Vector_set(Vector* this, int idx, void* data_); #ifdef DEBUG @@ -84,7 +84,7 @@ extern int Vector_size(Vector* this); */ -void Vector_add(Vector* this, void* data_); +extern void Vector_add(Vector* this, void* data_); extern int Vector_indexOf(Vector* this, void* search_, Object_Compare compare); @@ -11,11 +11,11 @@ #include <assert.h> #include <stdlib.h> -void* xMalloc(size_t size); +extern void* xMalloc(size_t size); -void* xCalloc(size_t nmemb, size_t size); +extern void* xCalloc(size_t nmemb, size_t size); -void* xRealloc(void* ptr, size_t size); +extern void* xRealloc(void* ptr, size_t size); #define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0) @@ -32,9 +32,9 @@ void* xRealloc(void* ptr, size_t size); #endif #if (__has_attribute(nonnull) || \ ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))) -char* xStrdup_(const char* str) __attribute__((nonnull)); +extern char* xStrdup_(const char* str) __attribute__((nonnull)); #endif // __has_attribute(nonnull) || GNU C 3.3 or later -char* xStrdup_(const char* str); +extern char* xStrdup_(const char* str); #endif diff --git a/configure.ac b/configure.ac index ffd8fede..71a4f799 100644 --- a/configure.ac +++ b/configure.ac @@ -2,10 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.65) -AC_INIT([htop],[2.2.0],[hisham@gobolinux.org]) - -SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" -year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y") +AC_INIT([htop],[3.0.0],[htop@groups.io]) AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_AUX_DIR([.]) @@ -69,7 +66,7 @@ dnl glibc 2.25 deprecates 'major' and 'minor' in <sys/types.h> and requires to dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet dnl been updated in Autoconf 2.69, so use a workaround: m4_version_prereq([2.70], [], -[if test "x$ac_cv_header_sys_mkdev_h" = xno; then +[if test "x$ac_cv_header_sys_mkdev_h" != xyes; then AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1, [Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])]) fi]) @@ -308,7 +305,7 @@ if test ! -z "$missing_headers"; then AC_MSG_ERROR([missing headers: $missing_headers]) fi -AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright message.]) +AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2018 Hisham Muhammad", [Copyright message.]) # We're done, let's go! # ---------------------------------------------------------------------- diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index e1a16f9c..bf5fd577 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -212,7 +212,7 @@ ERROR_B: ERROR_A: retval = xStrdup(k->kp_proc.p_comm); *basenameOffset = strlen(retval); - + return retval; } @@ -302,11 +302,11 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList void DarwinProcess_scanThreads(DarwinProcess *dp) { Process* proc = (Process*) dp; kern_return_t ret; - + if (!dp->taskAccess) { return; } - + if (proc->state == 'Z') { return; } @@ -317,7 +317,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) { dp->taskAccess = false; return; } - + task_info_data_t tinfo; mach_msg_type_number_t task_info_count = TASK_INFO_MAX; ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count); @@ -325,7 +325,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) { dp->taskAccess = false; return; } - + thread_array_t thread_list; mach_msg_type_number_t thread_count; ret = task_threads(port, &thread_list, &thread_count); @@ -334,7 +334,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) { mach_port_deallocate(mach_task_self(), port); return; } - + integer_t run_state = 999; for (unsigned int i = 0; i < thread_count; i++) { thread_info_data_t thinfo; diff --git a/darwin/Platform.c b/darwin/Platform.c index 286ff16b..26e84112 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -92,7 +92,7 @@ ProcessFieldData Process_fields[] = { [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, - [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, @@ -221,6 +221,8 @@ double Platform_setCPUValues(Meter* mtr, int cpu) { /* Convert to percent and return */ total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL]; + mtr->values[CPU_METER_FREQUENCY] = -1; + return CLAMP(total, 0.0, 100.0); } diff --git a/dragonflybsd/DragonFlyBSDProcess.c b/dragonflybsd/DragonFlyBSDProcess.c index dade106d..ffaf5eb6 100644 --- a/dragonflybsd/DragonFlyBSDProcess.c +++ b/dragonflybsd/DragonFlyBSDProcess.c @@ -74,7 +74,7 @@ ProcessFieldData Process_fields[] = { [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, - [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, @@ -121,7 +121,7 @@ void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break; case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JAIL:{ - xSnprintf(buffer, n, "%-11s ", fp->jname); break; + xSnprintf(buffer, n, "%-11s ", fp->jname); if (buffer[11] != '\0') { buffer[11] = ' '; buffer[12] = '\0'; diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index a41af854..5bc4b384 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -565,7 +565,7 @@ void ProcessList_goThroughEntries(ProcessList* this) { if (kproc->kp_flags & P_JAILED) { proc->state = 'J'; } - + if (Process_isKernelThread(dfp)) { this->kernelThreads++; } diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 370943d7..250e50e7 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -179,6 +179,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = -1; + return percent; } diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c index f81fadf5..c8d30f69 100644 --- a/freebsd/FreeBSDProcess.c +++ b/freebsd/FreeBSDProcess.c @@ -73,7 +73,7 @@ ProcessFieldData Process_fields[] = { [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, - [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, @@ -119,7 +119,7 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel // add FreeBSD-specific fields here case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JAIL:{ - xSnprintf(buffer, n, "%-11s ", fp->jname); break; + xSnprintf(buffer, n, "%-11s ", fp->jname); if (buffer[11] != '\0') { buffer[11] = ' '; buffer[12] = '\0'; diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index fd694198..7bc42db6 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -20,6 +20,7 @@ in the source distribution for its full text. #include <fcntl.h> #include <limits.h> #include <string.h> +#include <time.h> /*{ @@ -289,26 +290,51 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { // // htop_used = active + (wired - arc) // htop_cache = buffers + cache + arc - size_t len = sizeof(pl->totalMem); + u_long totalMem; + u_int memActive, memWire, cachedMem; + long buffersMem; + uint64_t memZfsArc; + size_t len; //disabled for now, as it is always smaller than phycal amount of memory... //...to avoid "where is my memory?" questions //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0); //pl->totalMem *= pageSizeKb; - sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0); - pl->totalMem /= 1024; - - sysctl(MIB_vm_stats_vm_v_active_count, 4, &(fpl->memActive), &len, NULL, 0); - fpl->memActive *= pageSizeKb; - - sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(fpl->memWire), &len, NULL, 0); - fpl->memWire *= pageSizeKb; - - sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0); - pl->buffersMem /= 1024; - - sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0); - pl->cachedMem *= pageSizeKb; + len = sizeof(totalMem); + sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); + totalMem /= 1024; + pl->totalMem = totalMem; + + len = sizeof(memActive); + sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); + memActive *= pageSizeKb; + fpl->memActive = memActive; + + len = sizeof(memWire); + sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0); + memWire *= pageSizeKb; + fpl->memWire = memWire; + + len = sizeof(buffersMem); + sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); + buffersMem /= 1024; + pl->buffersMem = buffersMem; + + len = sizeof(cachedMem); + sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); + cachedMem *= pageSizeKb; + pl->cachedMem = cachedMem; + + if (fpl->zfsArcEnabled) { + len = sizeof(memZfsArc); + sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(memZfsArc), &len , NULL, 0); + memZfsArc /= 1024; + fpl->memZfsArc = memZfsArc; + fpl->memWire -= fpl->memZfsArc; + pl->cachedMem += fpl->memZfsArc; + // maybe when we learn how to make custom memory meter + // we could do custom arc breakdown? + } if (fpl->zfs.enabled) { fpl->memWire -= fpl->zfs.size; @@ -418,10 +444,14 @@ void ProcessList_goThroughEntries(ProcessList* this) { int count = 0; struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count); + struct timeval tv; + gettimeofday(&tv, NULL); + for (int i = 0; i < count; i++) { struct kinfo_proc* kproc = &kprocs[i]; bool preExisting = false; bool isIdleProcess = false; + struct tm date; Process* proc = ProcessList_getProcess(this, kproc->ki_pid, &preExisting, (Process_New) FreeBSDProcess_new); FreeBSDProcess* fp = (FreeBSDProcess*) proc; @@ -512,6 +542,9 @@ void ProcessList_goThroughEntries(ProcessList* this) { this->kernelThreads++; } + (void) localtime_r((time_t*) &proc->starttime_ctime, &date); + strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date); + this->totalTasks++; if (proc->state == 'R') this->runningTasks++; diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 0986a3dd..e6acebdb 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -182,6 +182,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = -1; + return percent; } @@ -1,17 +1,22 @@ -.TH "HTOP" "1" "2015" "@PACKAGE_STRING@" "Utils" +.TH "HTOP" "1" "2020" "@PACKAGE_STRING@" "User Commands" .SH "NAME" htop \- interactive process viewer .SH "SYNOPSIS" .LP -.B htop [\fI\-dChustv\fR] +.B htop +.RB [ \-dChpustv ] .SH "DESCRIPTION" .LP -Htop is a free (GPL) ncurses-based process viewer for Linux. +.B htop +is a cross-platform ncurses-based process viewer. .LP -It is similar to top, but allows you to scroll vertically and horizontally, -so you can see all the processes running on the system, along with their full -command lines, as well as viewing them as a process tree, selecting multiple -processes and acting on them all at once. +It is similar to +.BR top , +but allows you to scroll vertically and horizontally, and interact using +a pointing device (mouse). +You can observe all processes running on the system, along with their +command line arguments, as well as view them in a tree format, select +multiple processes and acting on them all at once. .LP Tasks related to processes (killing, renicing) can be done without entering their PIDs. @@ -22,10 +27,14 @@ Mandatory arguments to long options are mandatory for short options too. .LP .TP \fB\-d \-\-delay=DELAY\fR -Delay between updates, in tenths of seconds +Delay between updates, in tenths of seconds. If the delay value is +less than 1 it is increased to 1, i.e. 1/10 second. If the delay value +is greater than 100, it is decreased to 100, i.e. 10 seconds. .TP \fB\-C \-\-no-color \-\-no-colour\fR -Start htop in monochrome mode +Start +.B htop +in monochrome mode .TP \fB\-h \-\-help Display a help message and exit @@ -44,11 +53,10 @@ Output version information and exit .TP \fB\-t \-\-tree Show processes in tree view -.PP -.br .SH "INTERACTIVE COMMANDS" .LP -The following commands are supported while in htop: +The following commands are supported while in +.BR htop : .LP .TP 5 .B Up, Alt-k @@ -194,14 +202,17 @@ Refresh: redraw screen and recalculate values. .B Numbers PID search: type in process ID and the selection highlight will be moved to it. .PD - .SH "COLUMNS" .LP The following columns can display data about each process. A value of '\-' in all the rows indicates that a column is unsupported on your system, or -currently unimplemented in htop. The names below are the ones used in the +currently unimplemented in +.BR htop . +The names below are the ones used in the "Available Columns" section of the setup screen. If a different name is -shown in htop's main screen, it is shown below in parenthesis. +shown in +.BR htop 's +main screen, it is shown below in parenthesis. .LP .TP 5 .B Command @@ -389,36 +400,44 @@ The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN. .TP .B All other flags Currently unsupported (always displays '-'). - .SH "CONFIG FILE" .LP -By default htop reads its configuration from the XDG-compliant path -~/.config/htop/htoprc -- the configuration file is overwritten by htop's -in-program Setup configuration, so it should not be hand-edited. If no -user configuration exists htop tries to read the system-wide configuration -from @sysconfdir@/htoprc and as a last resort, falls back to its -hard coded defaults. +By default +.B htop +reads its configuration from the XDG-compliant path +.IR ~/.config/htop/htoprc . +The configuration file is overwritten by +.BR htop 's +in-program Setup configuration, so it should not be hand-edited. +If no user configuration exists +.B htop +tries to read the system-wide configuration from +.I @sysconfdir@/htoprc +and as a last resort, falls back to its hard coded defaults. .LP You may override the location of the configuration file using the $HTOPRC environment variable (so you can have multiple configurations for different machines that share the same home directory, for example). - .SH "MEMORY SIZES" .LP -Memory sizes in htop are displayed as they are in tools from the GNU Coreutils -(when ran with the --human-readable option). This means that sizes are printed -in powers of 1024. (e.g., 1023M = 1072693248 Bytes) +Memory sizes in +.B htop +are displayed in a human-readable form. +Sizes are printed in powers of 1024. (e.g., 1023M = 1072693248 Bytes) .LP -The decision to use this convention was made in order to conserve screen space -and make memory size representations consistent throughout htop. - +The decision to use this convention was made in order to conserve screen +space and make memory size representations consistent throughout +.BR htop . .SH "SEE ALSO" -proc(5), top(1), free(1), ps(1), uptime(1), limits.conf(5) - +.BR proc (5), +.BR top (1), +.BR free (1), +.BR ps (1), +.BR uptime (1) +and +.BR limits.conf (5). .SH "AUTHORS" .LP -htop is developed by Hisham Muhammad <hisham@gobolinux.org>. -.LP -This man page was written by Bartosz Fenski <fenio@o2.pl> for the Debian -GNU/Linux distribution (but it may be used by others). It was updated by Hisham -Muhammad, and later by Vincent Launchbury, who wrote the 'Columns' section. +.B htop +was originally developed by Hisham Muhammad. +Nowadays it is maintained by the community at <htop@groups.io>. @@ -34,16 +34,17 @@ static void printVersionFlag() { stdout); exit(0); } - + static void printHelpFlag() { fputs("htop " VERSION " - " COPYRIGHT "\n" "Released under the GNU GPL.\n\n" "-C --no-color Use a monochrome color scheme\n" + "-m --no-mouse Disable the mouse\n" "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" "-h --help Print this help screen\n" "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n" "-t --tree Show the tree view by default\n" - "-u --user=USERNAME Show only processes of a given user\n" + "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n" "-p --pid=PID,[,PID,PID...] Show only the given PIDs\n" "-v --version Print version info\n" "\n" @@ -62,6 +63,7 @@ typedef struct CommandLineSettings_ { int sortKey; int delay; bool useColors; + bool enableMouse; bool treeView; } CommandLineSettings; @@ -73,6 +75,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) { .sortKey = 0, .delay = -1, .useColors = true, + .enableMouse = true, .treeView = false, }; @@ -82,9 +85,10 @@ static CommandLineSettings parseArguments(int argc, char** argv) { {"version", no_argument, 0, 'v'}, {"delay", required_argument, 0, 'd'}, {"sort-key", required_argument, 0, 's'}, - {"user", required_argument, 0, 'u'}, + {"user", optional_argument, 0, 'u'}, {"no-color", no_argument, 0, 'C'}, {"no-colour",no_argument, 0, 'C'}, + {"no-mouse", no_argument, 0, 'm'}, {"tree", no_argument, 0, 't'}, {"pid", required_argument, 0, 'p'}, {0,0,0,0} @@ -92,7 +96,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) { int opt, opti=0; /* Parse arguments */ - while ((opt = getopt_long(argc, argv, "hvCs:td:u:p:", long_opts, &opti))) { + while ((opt = getopt_long(argc, argv, "hvmCs:td:u::p:", long_opts, &opti))) { if (opt == EOF) break; switch (opt) { case 'h': @@ -112,6 +116,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) { flags.sortKey = ColumnsPanel_fieldNameToIndex(optarg); if (flags.sortKey == -1) { fprintf(stderr, "Error: invalid column \"%s\".\n", optarg); + exit(1); } break; case 'd': @@ -120,16 +125,31 @@ static CommandLineSettings parseArguments(int argc, char** argv) { if (flags.delay > 100) flags.delay = 100; } else { fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); + exit(1); } break; case 'u': + if (!optarg && optind < argc && argv[optind] != NULL && + (argv[optind][0] != '\0' && argv[optind][0] != '-')) { + optarg = argv[optind++]; + } + + if (!optarg) { + optarg = getenv("USER"); + flags.userId = geteuid(); + } + if (!Action_setUserOnly(optarg, &(flags.userId))) { fprintf(stderr, "Error: invalid user \"%s\".\n", optarg); + exit(1); } break; case 'C': flags.useColors = false; break; + case 'm': + flags.enableMouse = false; + break; case 't': flags.treeView = true; break; @@ -186,12 +206,12 @@ int main(int argc, char** argv) { exit(1); } #endif - + Process_setupColumnWidths(); - + UsersTable* ut = UsersTable_new(); ProcessList* pl = ProcessList_new(ut, flags.pidWhiteList, flags.userId); - + Settings* settings = Settings_new(pl->cpuCount); pl->settings = settings; @@ -201,18 +221,20 @@ int main(int argc, char** argv) { if (flags.delay != -1) settings->delay = flags.delay; - if (!flags.useColors) + if (!flags.useColors) settings->colorScheme = COLORSCHEME_MONOCHROME; + if (!flags.enableMouse) + settings->enableMouse = false; if (flags.treeView) settings->treeView = true; CRT_init(settings->delay, settings->colorScheme); - + MainPanel* panel = MainPanel_new(); ProcessList_setPanel(pl, (Panel*) panel); MainPanel_updateTreeFunctions(panel, settings->treeView); - + if (flags.sortKey > 0) { settings->sortKey = flags.sortKey; settings->treeView = false; @@ -228,7 +250,7 @@ int main(int argc, char** argv) { .header = header, }; MainPanel_setState(panel, &state); - + ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true); ScreenManager_add(scr, (Panel*) panel, -1); @@ -236,13 +258,13 @@ int main(int argc, char** argv) { millisleep(75); ProcessList_scan(pl); - ScreenManager_run(scr, NULL, NULL); - + ScreenManager_run(scr, NULL, NULL); + attron(CRT_colors[RESET_COLOR]); mvhline(LINES-1, 0, ' ', COLS); attroff(CRT_colors[RESET_COLOR]); refresh(); - + CRT_done(); if (settings->changed) Settings_write(settings); @@ -250,10 +272,10 @@ int main(int argc, char** argv) { ProcessList_delete(pl); ScreenManager_delete(scr); - + UsersTable_delete(ut); Settings_delete(settings); - + if(flags.pidWhiteList) { Hashtable_delete(flags.pidWhiteList); } diff --git a/htop.desktop b/htop.desktop index d59a2650..20bed490 100644 --- a/htop.desktop +++ b/htop.desktop @@ -4,6 +4,7 @@ Version=1.0 Name=Htop GenericName=Process Viewer GenericName[ca]=Visualitzador de processos +GenericName[da]=Procesfremviser GenericName[de]=Prozessanzeige GenericName[en_GB]=Process Viewer GenericName[es]=Visor de procesos @@ -32,6 +33,7 @@ GenericName[zh_CN]=进程查看器 GenericName[zh_TW]=行程檢視器 Comment=Show System Processes Comment[ca]=Visualitzeu els processos del sistema +Comment[da]=Vis systemprocesser Comment[de]=Systemprozesse anzeigen Comment[en_GB]=Show System Processes Comment[es]=Mostrar procesos del sistema @@ -14,6 +14,6 @@ in the source distribution for its full text. // ---------------------------------------- -int main(int argc, char** argv); +extern int main(int argc, char** argv); #endif diff --git a/linux/Battery.c b/linux/Battery.c index aedacabc..7a1c8163 100644 --- a/linux/Battery.c +++ b/linux/Battery.c @@ -78,7 +78,7 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short const unsigned long int foundNum = atoi(foundNumStr); free(foundNumStr); free(line); - + total += foundNum; } @@ -175,7 +175,7 @@ static inline ssize_t xread(int fd, void *buf, size_t count) { } static void Battery_getSysData(double* level, ACPresence* isOnAC) { - + *level = 0; *isOnAC = AC_ERROR; @@ -193,8 +193,18 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) { char* entryName = (char *) dirEntry->d_name; const char filePath[50]; - if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') { - + xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName); + int fd = open(filePath, O_RDONLY); + if (fd == -1) + continue; + + char type[8]; + ssize_t typelen = xread(fd, type, 7); + close(fd); + if (typelen < 1) + continue; + + if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') { xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); int fd = open(filePath, O_RDONLY); if (fd == -1) { @@ -247,7 +257,7 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) { if (*isOnAC != AC_ERROR) { continue; } - + xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName); int fd = open(filePath, O_RDONLY); if (fd == -1) { diff --git a/linux/Battery.h b/linux/Battery.h index cfb6c324..261cff05 100644 --- a/linux/Battery.h +++ b/linux/Battery.h @@ -29,6 +29,6 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat // READ FROM /sys // ---------------------------------------- -void Battery_getData(double* level, ACPresence* isOnAC); +extern void Battery_getData(double* level, ACPresence* isOnAC); #endif diff --git a/linux/IOPriority.c b/linux/IOPriority.c index dd7c84a1..80007df0 100644 --- a/linux/IOPriority.c +++ b/linux/IOPriority.c @@ -4,7 +4,7 @@ htop - IOPriority.c Released under the GNU GPL, see the COPYING file in the source distribution for its full text. -Based on ionice, +Based on ionice, Copyright (C) 2005 Jens Axboe <jens@axboe.dk> Released under the terms of the GNU General Public License version 2 */ diff --git a/linux/IOPriority.h b/linux/IOPriority.h index 148e344c..1c9ca95b 100644 --- a/linux/IOPriority.h +++ b/linux/IOPriority.h @@ -8,7 +8,7 @@ htop - IOPriority.h Released under the GNU GPL, see the COPYING file in the source distribution for its full text. -Based on ionice, +Based on ionice, Copyright (C) 2005 Jens Axboe <jens@axboe.dk> Released under the terms of the GNU General Public License version 2 */ diff --git a/linux/IOPriorityPanel.h b/linux/IOPriorityPanel.h index 9f77a4d9..0bf74f50 100644 --- a/linux/IOPriorityPanel.h +++ b/linux/IOPriorityPanel.h @@ -13,9 +13,9 @@ in the source distribution for its full text. #include "IOPriority.h" #include "ListItem.h" -Panel* IOPriorityPanel_new(IOPriority currPrio); +extern Panel* IOPriorityPanel_new(IOPriority currPrio); -IOPriority IOPriorityPanel_getIOPriority(Panel* this); +extern IOPriority IOPriorityPanel_getIOPriority(Panel* this); #endif diff --git a/linux/LinuxCRT.h b/linux/LinuxCRT.h index f8c43e4d..966dfecf 100644 --- a/linux/LinuxCRT.h +++ b/linux/LinuxCRT.h @@ -12,6 +12,6 @@ in the source distribution for its full text. #ifdef HAVE_EXECINFO_H #endif -void CRT_handleSIGSEGV(int sgn); +extern void CRT_handleSIGSEGV(int sgn); #endif diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 5f697078..50b4d220 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -1,6 +1,7 @@ /* htop - LinuxProcess.c (C) 2014 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -24,6 +25,7 @@ in the source distribution for its full text. #define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OOM 0x1000 +#define PROCESS_FLAG_LINUX_SMAPS 0x2000 typedef enum UnsupportedProcessFields { FLAGS = 9, @@ -87,7 +89,10 @@ typedef enum LinuxProcessFields { PERCENT_IO_DELAY = 117, PERCENT_SWAP_DELAY = 118, #endif - LAST_PROCESSFIELD = 119, + M_PSS = 119, + M_SWAP = 120, + M_PSSWP = 121, + LAST_PROCESSFIELD = 122, } LinuxProcessField; #include "IOPriority.h" @@ -103,6 +108,9 @@ typedef struct LinuxProcess_ { unsigned long long int cutime; unsigned long long int cstime; long m_share; + long m_pss; + long m_swap; + long m_psswp; long m_trs; long m_drs; long m_lrs; @@ -117,7 +125,7 @@ typedef struct LinuxProcess_ { unsigned long long io_write_bytes; unsigned long long io_cancelled_write_bytes; unsigned long long io_rate_read_time; - unsigned long long io_rate_write_time; + unsigned long long io_rate_write_time; double io_rate_read_bps; double io_rate_write_bps; #endif @@ -154,7 +162,8 @@ typedef struct LinuxProcess_ { }*/ -long long btime; /* semi-global */ +/* semi-global */ +long long btime; ProcessFieldData Process_fields[] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, @@ -203,7 +212,7 @@ ProcessFieldData Process_fields[] = { [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, }, [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process", .flags = 0, }, [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process", .flags = 0, }, - [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, @@ -239,6 +248,9 @@ ProcessFieldData Process_fields[] = { [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, }, [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, }, #endif + [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, }, + [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, }, + [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, }, [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, }; @@ -303,12 +315,12 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) { return ioprio; } -bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) { +bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio) { // Other OSes masquerading as Linux (NetBSD?) don't have this syscall #ifdef SYS_ioprio_set - syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio); + syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio.i); #endif - return (LinuxProcess_updateIOPriority(this) == ioprio); + return (LinuxProcess_updateIOPriority(this) == ioprio.i); } #ifdef HAVE_DELAYACCT @@ -344,6 +356,9 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) case M_LRS: Process_humanNumber(str, lp->m_lrs * PAGE_SIZE_KB, coloring); return; case M_TRS: Process_humanNumber(str, lp->m_trs * PAGE_SIZE_KB, coloring); return; case M_SHARE: Process_humanNumber(str, lp->m_share * PAGE_SIZE_KB, coloring); return; + case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return; + case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return; + case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return; case UTIME: Process_printTime(str, lp->utime); return; case STIME: Process_printTime(str, lp->stime); return; case CUTIME: Process_printTime(str, lp->cutime); return; @@ -394,7 +409,7 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) attr = CRT_colors[PROCESS_HIGH_PRIORITY]; xSnprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority)); } else if (klass == IOPRIO_CLASS_IDLE) { - attr = CRT_colors[PROCESS_LOW_PRIORITY]; + attr = CRT_colors[PROCESS_LOW_PRIORITY]; xSnprintf(buffer, n, "id "); } else { xSnprintf(buffer, n, "?? "); @@ -435,6 +450,12 @@ long LinuxProcess_compare(const void* v1, const void* v2) { return (p2->m_trs - p1->m_trs); case M_SHARE: return (p2->m_share - p1->m_share); + case M_PSS: + return (p2->m_pss - p1->m_pss); + case M_SWAP: + return (p2->m_swap - p1->m_swap); + case M_PSSWP: + return (p2->m_psswp - p1->m_psswp); case UTIME: diff = p2->utime - p1->utime; goto test_diff; case CUTIME: diff = p2->cutime - p1->cutime; goto test_diff; case STIME: diff = p2->stime - p1->stime; goto test_diff; diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 6ce3037d..1e47ba28 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -5,6 +5,7 @@ /* htop - LinuxProcess.h (C) 2014 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -15,6 +16,7 @@ in the source distribution for its full text. #define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OOM 0x1000 +#define PROCESS_FLAG_LINUX_SMAPS 0x2000 typedef enum UnsupportedProcessFields { FLAGS = 9, @@ -78,7 +80,10 @@ typedef enum LinuxProcessFields { PERCENT_IO_DELAY = 117, PERCENT_SWAP_DELAY = 118, #endif - LAST_PROCESSFIELD = 119, + M_PSS = 119, + M_SWAP = 120, + M_PSSWP = 121, + LAST_PROCESSFIELD = 122, } LinuxProcessField; #include "IOPriority.h" @@ -94,6 +99,9 @@ typedef struct LinuxProcess_ { unsigned long long int cutime; unsigned long long int cstime; long m_share; + long m_pss; + long m_swap; + long m_psswp; long m_trs; long m_drs; long m_lrs; @@ -108,7 +116,7 @@ typedef struct LinuxProcess_ { unsigned long long io_write_bytes; unsigned long long io_cancelled_write_bytes; unsigned long long io_rate_read_time; - unsigned long long io_rate_write_time; + unsigned long long io_rate_write_time; double io_rate_read_bps; double io_rate_write_bps; #endif @@ -144,7 +152,8 @@ typedef struct LinuxProcess_ { #endif -long long btime; /* semi-global */ +/* semi-global */ +extern long long btime; extern ProcessFieldData Process_fields[]; @@ -152,9 +161,9 @@ extern ProcessPidColumn Process_pidColumns[]; extern ProcessClass LinuxProcess_class; -LinuxProcess* LinuxProcess_new(Settings* settings); +extern LinuxProcess* LinuxProcess_new(Settings* settings); -void Process_delete(Object* cast); +extern void Process_delete(Object* cast); /* [1] Note that before kernel 2.6.26 a process that has not asked for @@ -166,19 +175,19 @@ extern io_priority; */ #define LinuxProcess_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->super.nice + 20) / 5) : p_->ioPriority) -IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); +extern IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); -bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio); +extern bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio); #ifdef HAVE_DELAYACCT -void LinuxProcess_printDelay(float delay_percent, char* buffer, int n); +extern void LinuxProcess_printDelay(float delay_percent, char* buffer, int n); #endif -void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field); +extern void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field); -long LinuxProcess_compare(const void* v1, const void* v2); +extern long LinuxProcess_compare(const void* v1, const void* v2); -bool Process_isThread(Process* this); +extern bool Process_isThread(Process* this); #endif diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 1d5700e5..bcdd5515 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -28,8 +28,7 @@ in the source distribution for its full text. #include <fcntl.h> #ifdef MAJOR_IN_MKDEV #include <sys/mkdev.h> -#elif defined(MAJOR_IN_SYSMACROS) || \ - (defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H) +#elif defined(MAJOR_IN_SYSMACROS) #include <sys/sysmacros.h> #endif @@ -63,7 +62,7 @@ typedef struct CPUData_ { unsigned long long int softIrqTime; unsigned long long int stealTime; unsigned long long int guestTime; - + unsigned long long int totalPeriod; unsigned long long int userPeriod; unsigned long long int systemPeriod; @@ -76,6 +75,8 @@ typedef struct CPUData_ { unsigned long long int softIrqPeriod; unsigned long long int stealPeriod; unsigned long long int guestPeriod; + + double frequency; } CPUData; typedef struct TtyDriver_ { @@ -87,10 +88,11 @@ typedef struct TtyDriver_ { typedef struct LinuxProcessList_ { ProcessList super; - + CPUData* cpus; TtyDriver* ttyDrivers; - + bool haveSmapsRollup; + #ifdef HAVE_DELAYACCT struct nl_sock *netlink_socket; int netlink_family; @@ -103,6 +105,10 @@ typedef struct LinuxProcessList_ { #define PROCDIR "/proc" #endif +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + #ifndef PROCSTATFILE #define PROCSTATFILE PROCDIR "/stat" #endif @@ -247,8 +253,17 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui LinuxProcessList_initNetlinkSocket(this); #endif + // Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+) + FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r"); + if(file != NULL) { + this->haveSmapsRollup = true; + fclose(file); + } else { + this->haveSmapsRollup = false; + } + // Update CPU count: - FILE* file = fopen(PROCSTATFILE, "r"); + file = fopen(PROCSTATFILE, "r"); if (file == NULL) { CRT_fatalError("Cannot open " PROCSTATFILE); } @@ -326,7 +341,7 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, location += 2; char *end = strrchr(location, ')'); if (!end) return false; - + int commsize = end - location; memcpy(command, location, commsize); command[commsize] = '\0'; @@ -377,9 +392,9 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, location += 1; assert(location != NULL); process->processor = strtol(location, &location, 10); - + process->time = lp->utime + lp->stime; - + return true; } @@ -419,7 +434,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna process->io_rate_write_time = -1LL; return; } - + char buffer[1024]; ssize_t buflen = xread(fd, buffer, 1023); close(fd); @@ -436,7 +451,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna process->io_rchar = strtoull(line+7, NULL, 10); else if (strncmp(line+1, "ead_bytes: ", 11) == 0) { process->io_read_bytes = strtoull(line+12, NULL, 10); - process->io_rate_read_bps = + process->io_rate_read_bps = ((double)(process->io_read_bytes - last_read))/(((double)(now - process->io_rate_read_time))/1000); process->io_rate_read_time = now; } @@ -446,7 +461,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna process->io_wchar = strtoull(line+7, NULL, 10); else if (strncmp(line+1, "rite_bytes: ", 12) == 0) { process->io_write_bytes = strtoull(line+13, NULL, 10); - process->io_rate_write_bps = + process->io_rate_write_bps = ((double)(process->io_write_bytes - last_write))/(((double)(now - process->io_rate_write_time))/1000); process->io_rate_write_time = now; } @@ -493,6 +508,62 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* di return (errno == 0); } +static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* dirname, const char* name, bool haveSmapsRollup) { + //http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719 + //kernel will return data in chunks of size PAGE_SIZE or less. + + char buffer[PAGE_SIZE];// 4k + char *start,*end; + ssize_t nread=0; + int tmp=0; + if(haveSmapsRollup) {// only available in Linux 4.14+ + snprintf(buffer, PAGE_SIZE-1, "%s/%s/smaps_rollup", dirname, name); + } else { + snprintf(buffer, PAGE_SIZE-1, "%s/%s/smaps", dirname, name); + } + int fd = open(buffer, O_RDONLY); + if (fd == -1) + return false; + + process->m_pss = 0; + process->m_swap = 0; + process->m_psswp = 0; + + while ( ( nread = read(fd,buffer, sizeof(buffer)) ) > 0 ){ + start = (char *)&buffer; + end = start + nread; + do{//parse 4k block + + if( (tmp = (end - start)) > 0 && + (start = memmem(start,tmp,"\nPss:",5)) != NULL ) + { + process->m_pss += strtol(start+5, &start, 10); + start += 3;//now we must be at the end of line "Pss: 0 kB" + }else + break; //read next 4k block + + if( (tmp = (end - start)) > 0 && + (start = memmem(start,tmp,"\nSwap:",6)) != NULL ) + { + process->m_swap += strtol(start+6, &start, 10); + start += 3; + }else + break; + + if( (tmp = (end - start)) > 0 && + (start = memmem(start,tmp,"\nSwapPss:",9)) != NULL ) + { + process->m_psswp += strtol(start+9, &start, 10); + start += 3; + }else + break; + + }while(1); + }//while read + close(fd); + return true; +} + #ifdef HAVE_OPENVZ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* dirname, const char* name) { @@ -668,7 +739,7 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc process->cpu_delay_percent = -1LL; return; } - + if (nl_recvmsgs_default(this->netlink_socket) < 0) { return; } @@ -692,14 +763,18 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna int fd = open(filename, O_RDONLY); if (fd == -1) return false; - + char command[4096+1]; // max cmdline length on Linux int amtRead = xread(fd, command, sizeof(command) - 1); close(fd); - int tokenEnd = 0; + int tokenEnd = 0; int lastChar = 0; if (amtRead == 0) { - ((LinuxProcess*)process)->isKernelThread = true; + if (process->state == 'Z') { + process->basenameOffset = 0; + } else { + ((LinuxProcess*)process)->isKernelThread = true; + } return true; } else if (amtRead < 0) { return false; @@ -733,13 +808,13 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned in i++; if ((!ttyDrivers[i].path) || maj < ttyDrivers[i].major) { break; - } + } if (maj > ttyDrivers[i].major) { continue; } if (min < ttyDrivers[i].minorFrom) { break; - } + } if (min > ttyDrivers[i].minorTo) { continue; } @@ -797,17 +872,17 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* // filename is a number: process directory int pid = atoi(name); - + if (parent && pid == parent->pid) continue; - if (pid <= 0) + if (pid <= 0) continue; bool preExisting = false; Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_New) LinuxProcess_new); proc->tgid = parent ? parent->pid : pid; - + LinuxProcess* lp = (LinuxProcess*) proc; char subdirname[MAX_NAME+1]; @@ -822,6 +897,21 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* if (! LinuxProcessList_readStatmFile(lp, dirname, name)) goto errorReadingProcess; + if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)){ + if (!parent){ + // Read smaps file of each process only every second pass to improve performance + static int smaps_flag = 0; + if ((pid & 1) == smaps_flag){ + LinuxProcessList_readSmapsFile(lp, dirname, name, this->haveSmapsRollup); + } + if (pid == 1) { + smaps_flag = !smaps_flag; + } + } else { + lp->m_pss = ((LinuxProcess*)parent)->m_pss; + } + } + proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); char command[MAX_NAME+1]; @@ -853,7 +943,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* LinuxProcessList_readOpenVZData(lp, dirname, name); } #endif - + #ifdef HAVE_VSERVER if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) { LinuxProcessList_readVServerData(lp, dirname, name); @@ -881,7 +971,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) LinuxProcessList_readCGroupFile(lp, dirname, name); #endif - + if (settings->flags & PROCESS_FLAG_LINUX_OOM) LinuxProcessList_readOomData(lp, dirname, name); @@ -1097,12 +1187,78 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) { cpuData->stealTime = steal; cpuData->guestTime = virtalltime; cpuData->totalTime = totaltime; + } double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file); return period; } +static inline double LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { + ProcessList* pl = (ProcessList*) this; + Settings* settings = pl->settings; + + int cpus = this->super.cpuCount; + assert(cpus > 0); + + for (int i = 0; i <= cpus; i++) { + CPUData* cpuData = &(this->cpus[i]); + cpuData->frequency = -1; + } + + int numCPUsWithFrequency = 0; + double totalFrequency = 0; + + if (settings->showCPUFrequency) { + FILE* file = fopen(PROCCPUINFOFILE, "r"); + if (file == NULL) { + CRT_fatalError("Cannot open " PROCCPUINFOFILE); + } + + int cpuid = -1; + double frequency; + while (!feof(file)) { + char buffer[PROC_LINE_LENGTH]; + char *ok = fgets(buffer, PROC_LINE_LENGTH, file); + if (!ok) break; + + if ( + (sscanf(buffer, "processor : %d", &cpuid) == 1) || + (sscanf(buffer, "processor: %d", &cpuid) == 1) + ) { + if (cpuid < 0 || cpuid > (cpus - 1)) { + char buffer[64]; + xSnprintf(buffer, sizeof(buffer), PROCCPUINFOFILE " contains out-of-range CPU number %d", cpuid); + CRT_fatalError(buffer); + } + } else if ( + (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || + (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) + ) { + if (cpuid < 0) { + CRT_fatalError(PROCCPUINFOFILE " is malformed: cpu MHz line without corresponding processor line"); + } + + int cpu = cpuid + 1; + CPUData* cpuData = &(this->cpus[cpu]); + cpuData->frequency = frequency; + numCPUsWithFrequency++; + totalFrequency += frequency; + } else if (buffer[0] == '\n') { + cpuid = -1; + } + } + fclose(file); + + if (numCPUsWithFrequency > 0) { + this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency; + } + } + + double period = (double)this->cpus[0].totalPeriod / cpus; + return period; +} + void ProcessList_goThroughEntries(ProcessList* super) { LinuxProcessList* this = (LinuxProcessList*) super; @@ -1110,6 +1266,8 @@ void ProcessList_goThroughEntries(ProcessList* super) { LinuxProcessList_scanZfsArcstats(this); double period = LinuxProcessList_scanCPUTime(this); + LinuxProcessList_scanCPUFrequency(this); + struct timeval tv; gettimeofday(&tv, NULL); LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv); diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 353fe603..86e9d3fd 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -10,8 +10,7 @@ in the source distribution for its full text. */ #ifdef MAJOR_IN_MKDEV -#elif defined(MAJOR_IN_SYSMACROS) || \ - (defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H) +#elif defined(MAJOR_IN_SYSMACROS) #endif #ifdef HAVE_DELAYACCT @@ -36,7 +35,7 @@ typedef struct CPUData_ { unsigned long long int softIrqTime; unsigned long long int stealTime; unsigned long long int guestTime; - + unsigned long long int totalPeriod; unsigned long long int userPeriod; unsigned long long int systemPeriod; @@ -49,6 +48,8 @@ typedef struct CPUData_ { unsigned long long int softIrqPeriod; unsigned long long int stealPeriod; unsigned long long int guestPeriod; + + double frequency; } CPUData; typedef struct TtyDriver_ { @@ -60,10 +61,11 @@ typedef struct TtyDriver_ { typedef struct LinuxProcessList_ { ProcessList super; - + CPUData* cpus; TtyDriver* ttyDrivers; - + bool haveSmapsRollup; + #ifdef HAVE_DELAYACCT struct nl_sock *netlink_socket; int netlink_family; @@ -76,6 +78,10 @@ typedef struct LinuxProcessList_ { #define PROCDIR "/proc" #endif +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + #ifndef PROCSTATFILE #define PROCSTATFILE PROCDIR "/stat" #endif @@ -105,9 +111,9 @@ typedef struct LinuxProcessList_ { #endif -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); +extern ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); -void ProcessList_delete(ProcessList* pl); +extern void ProcessList_delete(ProcessList* pl); #ifdef HAVE_TASKSTATS @@ -130,6 +136,6 @@ void ProcessList_delete(ProcessList* pl); #endif -void ProcessList_goThroughEntries(ProcessList* super); +extern void ProcessList_goThroughEntries(ProcessList* super); #endif diff --git a/linux/Platform.c b/linux/Platform.c index f7088cf4..1725eea9 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -19,6 +19,7 @@ in the source distribution for its full text. #include "TasksMeter.h" #include "LoadAverageMeter.h" #include "UptimeMeter.h" +#include "PressureStallMeter.h" #include "ClockMeter.h" #include "HostnameMeter.h" #include "zfs/ZfsArcMeter.h" @@ -95,7 +96,7 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) { if (!p) return HTOP_OK; IOPriority ioprio = p->ioPriority; Panel* ioprioPanel = IOPriorityPanel_new(ioprio); - void* set = Action_pickFromVector(st, ioprioPanel, 21); + void* set = Action_pickFromVector(st, ioprioPanel, 21, true); if (set) { IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel); bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (Arg){ .i = ioprio }, NULL); @@ -123,11 +124,19 @@ MeterClass* Platform_meterTypes[] = { &HostnameMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, + &AllCPUs4Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, + &LeftCPUs4Meter_class, + &RightCPUs4Meter_class, &BlankMeter_class, + &PressureStallCPUSomeMeter_class, + &PressureStallIOSomeMeter_class, + &PressureStallIOFullMeter_class, + &PressureStallMemorySomeMeter_class, + &PressureStallMemoryFullMeter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, NULL @@ -196,6 +205,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { } percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = cpuData->frequency; + return percent; } @@ -252,3 +264,21 @@ char* Platform_getProcessEnv(pid_t pid) { } return env; } + +void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred) { + *ten = *sixty = *threehundred = 0; + char procname[128+1]; + xSnprintf(procname, 128, PROCDIR "/pressure/%s", file); + FILE *fd = fopen(procname, "r"); + if (!fd) { + *ten = *sixty = *threehundred = NAN; + return; + } + int total = fscanf(fd, "some avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred); + if (!some) { + total = fscanf(fd, "full avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred); + } + (void) total; + assert(total == 3); + fclose(fd); +} diff --git a/linux/Platform.h b/linux/Platform.h index 5d85eb36..bf163a54 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -27,26 +27,27 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; -void Platform_setBindings(Htop_Action* keys); +extern void Platform_setBindings(Htop_Action* keys); extern MeterClass* Platform_meterTypes[]; -int Platform_getUptime(); +extern int Platform_getUptime(); -void Platform_getLoadAverage(double* one, double* five, double* fifteen); +extern void Platform_getLoadAverage(double* one, double* five, double* fifteen); -int Platform_getMaxPid(); +extern int Platform_getMaxPid(); -double Platform_setCPUValues(Meter* this, int cpu); +extern double Platform_setCPUValues(Meter* this, int cpu); -void Platform_setMemoryValues(Meter* this); +extern void Platform_setMemoryValues(Meter* this); -void Platform_setSwapValues(Meter* this); +extern void Platform_setSwapValues(Meter* this); -void Platform_setZfsArcValues(Meter* this); +extern void Platform_setZfsArcValues(Meter* this); -void Platform_setZfsCompressedArcValues(Meter* this); +extern void Platform_setZfsCompressedArcValues(Meter* this); +extern char* Platform_getProcessEnv(pid_t pid); -char* Platform_getProcessEnv(pid_t pid); +extern void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred); #endif diff --git a/linux/PressureStallMeter.c b/linux/PressureStallMeter.c new file mode 100644 index 00000000..56055bff --- /dev/null +++ b/linux/PressureStallMeter.c @@ -0,0 +1,133 @@ +/* +htop - PressureStallMeter.c +(C) 2004-2011 Hisham H. Muhammad +(C) 2019 Ran Benita +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "PressureStallMeter.h" +#include "Platform.h" +#include "CRT.h" + +#include <string.h> + +/*{ +#include "Meter.h" +}*/ + +static int PressureStallMeter_attributes[] = { + PRESSURE_STALL_TEN, PRESSURE_STALL_SIXTY, PRESSURE_STALL_THREEHUNDRED +}; + +static void PressureStallMeter_updateValues(Meter* this, char* buffer, int len) { + const char *file; + if (strstr(Meter_name(this), "CPU")) { + file = "cpu"; + } else if (strstr(Meter_name(this), "IO")) { + file = "io"; + } else { + file = "memory"; + } + + bool some; + if (strstr(Meter_name(this), "Some")) { + some = true; + } else { + some = false; + } + + Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]); + xSnprintf(buffer, len, "xxxx %.2lf%% %.2lf%% %.2lf%%", this->values[0], this->values[1], this->values[2]); +} + +static void PressureStallMeter_display(Object* cast, RichString* out) { + Meter* this = (Meter*)cast; + char buffer[20]; + xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[0]); + RichString_write(out, CRT_colors[PRESSURE_STALL_TEN], buffer); + xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[1]); + RichString_append(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer); + xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[2]); + RichString_append(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer); +} + +MeterClass PressureStallCPUSomeMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallCPUSome", + .uiName = "Pressure Stall Information, some CPU", + .caption = "Some CPU pressure: " +}; + +MeterClass PressureStallIOSomeMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallIOSome", + .uiName = "Pressure Stall Information, some IO", + .caption = "Some IO pressure: " +}; + +MeterClass PressureStallIOFullMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallIOFull", + .uiName = "Pressure Stall Information, full IO", + .caption = "Full IO pressure: " +}; + +MeterClass PressureStallMemorySomeMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallMemorySome", + .uiName = "Pressure Stall Information, some memory", + .caption = "Some Mem pressure: " +}; + +MeterClass PressureStallMemoryFullMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallMemoryFull", + .uiName = "Pressure Stall Information, full memory", + .caption = "Full Mem pressure: " +}; diff --git a/linux/PressureStallMeter.h b/linux/PressureStallMeter.h new file mode 100644 index 00000000..22b8b972 --- /dev/null +++ b/linux/PressureStallMeter.h @@ -0,0 +1,25 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_PressureStallMeter +#define HEADER_PressureStallMeter +/* +htop - PressureStallMeter.h +(C) 2004-2011 Hisham H. Muhammad +(C) 2019 Ran Benita +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Meter.h" + +extern MeterClass PressureStallCPUSomeMeter_class; + +extern MeterClass PressureStallIOSomeMeter_class; + +extern MeterClass PressureStallIOFullMeter_class; + +extern MeterClass PressureStallMemorySomeMeter_class; + +extern MeterClass PressureStallMemoryFullMeter_class; + +#endif diff --git a/openbsd/Battery.c b/openbsd/Battery.c index 3a0bae14..c215e418 100644 --- a/openbsd/Battery.c +++ b/openbsd/Battery.c @@ -10,6 +10,7 @@ in the source distribution for its full text. #include <sys/sysctl.h> #include <sys/sensors.h> #include <errno.h> +#include <string.h> static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) { for (int devn = 0;; devn++) { @@ -34,7 +35,7 @@ void Battery_getData(double* level, ACPresence* isOnAC) { size_t sdlen = sizeof(struct sensordev); bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen); - + *level = -1; if (found) { /* last full capacity */ @@ -57,9 +58,9 @@ void Battery_getData(double* level, ACPresence* isOnAC) { } } } - + found = findDevice("acpiac0", mib, &snsrdev, &sdlen); - + *isOnAC = AC_ERROR; if (found) { mib[3] = 9; diff --git a/openbsd/Battery.h b/openbsd/Battery.h index b1a4982e..0f05af3a 100644 --- a/openbsd/Battery.h +++ b/openbsd/Battery.h @@ -12,5 +12,4 @@ in the source distribution for its full text. void Battery_getData(double* level, ACPresence* isOnAC); - #endif diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c index 70f9653b..f54c9111 100644 --- a/openbsd/OpenBSDProcess.c +++ b/openbsd/OpenBSDProcess.c @@ -135,7 +135,7 @@ ProcessFieldData Process_fields[] = { .flags = 0, }, [ST_UID] = { .name = "ST_UID", - .title = " UID ", + .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index e49cbd71..2d8951c3 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -6,6 +6,7 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ +#include "CRT.h" #include "ProcessList.h" #include "OpenBSDProcessList.h" #include "OpenBSDProcess.h" @@ -17,6 +18,7 @@ in the source distribution for its full text. #include <sys/param.h> #include <sys/proc.h> #include <sys/resource.h> +#include <sys/sched.h> #include <sys/sysctl.h> #include <sys/types.h> #include <sys/user.h> @@ -31,7 +33,22 @@ in the source distribution for its full text. typedef struct CPUData_ { unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int niceTime; + unsigned long long int sysTime; + unsigned long long int sysAllTime; + unsigned long long int spinTime; + unsigned long long int intrTime; + unsigned long long int idleTime; + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int nicePeriod; + unsigned long long int sysPeriod; + unsigned long long int sysAllPeriod; + unsigned long long int spinPeriod; + unsigned long long int intrPeriod; + unsigned long long int idlePeriod; } CPUData; typedef struct OpenBSDProcessList_ { @@ -79,16 +96,17 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui if (e == -1 || pl->cpuCount < 1) { pl->cpuCount = 1; } - opl->cpus = xRealloc(opl->cpus, pl->cpuCount * sizeof(CPUData)); + opl->cpus = xCalloc(pl->cpuCount + 1, sizeof(CPUData)); size = sizeof(fscale); if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { err(1, "fscale sysctl call failed"); } - for (i = 0; i < pl->cpuCount; i++) { - opl->cpus[i].totalTime = 1; - opl->cpus[i].totalPeriod = 1; + for (i = 0; i <= pl->cpuCount; i++) { + CPUData *d = opl->cpus + i; + d->totalTime = 1; + d->totalPeriod = 1; } opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); @@ -205,7 +223,7 @@ char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, in /* * Taken from OpenBSD's ps(1). */ -double getpcpu(const struct kinfo_proc *kp) { +static double getpcpu(const struct kinfo_proc *kp) { if (fscale == 0) return (0.0); @@ -214,9 +232,8 @@ double getpcpu(const struct kinfo_proc *kp) { return (100.0 * fxtofl(kp->p_pctcpu)); } -void ProcessList_goThroughEntries(ProcessList* this) { - OpenBSDProcessList* opl = (OpenBSDProcessList*) this; - Settings* settings = this->settings; +static inline void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { + Settings* settings = this->super.settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; struct kinfo_proc* kproc; @@ -228,10 +245,8 @@ void ProcessList_goThroughEntries(ProcessList* this) { int count = 0; int i; - OpenBSDProcessList_scanMemoryInfo(this); - // use KERN_PROC_KTHREAD to also include kernel threads - struct kinfo_proc* kprocs = kvm_getprocs(opl->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count); + struct kinfo_proc* kprocs = kvm_getprocs(this->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count); //struct kinfo_proc* kprocs = getprocs(KERN_PROC_ALL, 0, &count); gettimeofday(&tv, NULL); @@ -240,7 +255,7 @@ void ProcessList_goThroughEntries(ProcessList* this) { kproc = &kprocs[i]; preExisting = false; - proc = ProcessList_getProcess(this, kproc->p_pid, &preExisting, (Process_New) OpenBSDProcess_new); + proc = ProcessList_getProcess(&this->super, kproc->p_pid, &preExisting, (Process_New) OpenBSDProcess_new); fp = (OpenBSDProcess*) proc; proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) @@ -255,22 +270,22 @@ void ProcessList_goThroughEntries(ProcessList* this) { proc->pgrp = kproc->p__pgid; proc->st_uid = kproc->p_uid; proc->starttime_ctime = kproc->p_ustart_sec; - proc->user = UsersTable_getRef(this->usersTable, proc->st_uid); - ProcessList_add((ProcessList*)this, proc); - proc->comm = OpenBSDProcessList_readProcessName(opl->kd, kproc, &proc->basenameOffset); + proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid); + ProcessList_add(&this->super, proc); + proc->comm = OpenBSDProcessList_readProcessName(this->kd, kproc, &proc->basenameOffset); (void) localtime_r((time_t*) &kproc->p_ustart_sec, &date); strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date); } else { if (settings->updateProcessNames) { free(proc->comm); - proc->comm = OpenBSDProcessList_readProcessName(opl->kd, kproc, &proc->basenameOffset); + proc->comm = OpenBSDProcessList_readProcessName(this->kd, kproc, &proc->basenameOffset); } } proc->m_size = kproc->p_vm_dsize; proc->m_resident = kproc->p_vm_rssize; - proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->cpuCount*100.0); + proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->super.totalMem) * 100.0; + proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount*100.0); //proc->nlwp = kproc->p_numthreads; //proc->time = kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 10); proc->nice = kproc->p_nice - 20; @@ -290,14 +305,99 @@ void ProcessList_goThroughEntries(ProcessList* this) { } if (Process_isKernelThread(proc)) { - this->kernelThreads++; + this->super.kernelThreads++; } - this->totalTasks++; + this->super.totalTasks++; // SRUN ('R') means runnable, not running if (proc->state == 'P') { - this->runningTasks++; + this->super.runningTasks++; } proc->updated = true; } } + +static unsigned long long saturatingSub(unsigned long long a, unsigned long long b) { + return a > b ? a - b : 0; +} + +static void getKernelCPUTimes(int cpuId, u_int64_t* times) { + int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId }; + size_t length = sizeof(u_int64_t) * CPUSTATES; + if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || + length != sizeof(u_int64_t) * CPUSTATES) { + CRT_fatalError("sysctl kern.cp_time2 failed"); + } +} + +static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { + unsigned long long totalTime = 0; + for (int i = 0; i < CPUSTATES; i++) { + totalTime += times[i]; + } + + unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; + + // XXX Not sure if CP_SPIN should be added to sysAllTime. + // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c + #ifdef CP_SPIN + sysAllTime += times[CP_SPIN]; + #endif + + cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); + cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); + cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); + cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); + cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); + #ifdef CP_SPIN + cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime); + #endif + cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); + cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); + + cpu->totalTime = totalTime; + cpu->userTime = times[CP_USER]; + cpu->niceTime = times[CP_NICE]; + cpu->sysTime = times[CP_SYS]; + cpu->sysAllTime = sysAllTime; + #ifdef CP_SPIN + cpu->spinTime = times[CP_SPIN]; + #endif + cpu->intrTime = times[CP_INTR]; + cpu->idleTime = times[CP_IDLE]; +} + +static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { + u_int64_t kernelTimes[CPUSTATES] = {0}; + u_int64_t avg[CPUSTATES] = {0}; + + for (int i = 0; i < this->super.cpuCount; i++) { + getKernelCPUTimes(i, kernelTimes); + CPUData* cpu = this->cpus + i + 1; + kernelCPUTimesToHtop(kernelTimes, cpu); + + avg[CP_USER] += cpu->userTime; + avg[CP_NICE] += cpu->niceTime; + avg[CP_SYS] += cpu->sysTime; + #ifdef CP_SPIN + avg[CP_SPIN] += cpu->spinTime; + #endif + avg[CP_INTR] += cpu->intrTime; + avg[CP_IDLE] += cpu->idleTime; + } + + for (int i = 0; i < CPUSTATES; i++) { + avg[i] /= this->super.cpuCount; + } + + kernelCPUTimesToHtop(avg, this->cpus); +} + +void ProcessList_goThroughEntries(ProcessList* this) { + OpenBSDProcessList* opl = (OpenBSDProcessList*) this; + + OpenBSDProcessList_scanMemoryInfo(this); + OpenBSDProcessList_scanProcs(opl); + OpenBSDProcessList_scanCPUTime(opl); +} + diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h index ba9e6d14..ec9fab27 100644 --- a/openbsd/OpenBSDProcessList.h +++ b/openbsd/OpenBSDProcessList.h @@ -15,7 +15,22 @@ in the source distribution for its full text. typedef struct CPUData_ { unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int niceTime; + unsigned long long int sysTime; + unsigned long long int sysAllTime; + unsigned long long int spinTime; + unsigned long long int intrTime; + unsigned long long int idleTime; + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int nicePeriod; + unsigned long long int sysPeriod; + unsigned long long int sysAllPeriod; + unsigned long long int spinPeriod; + unsigned long long int intrPeriod; + unsigned long long int idlePeriod; } CPUData; typedef struct OpenBSDProcessList_ { @@ -51,8 +66,7 @@ char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, in /* * Taken from OpenBSD's ps(1). */ -double getpcpu(const struct kinfo_proc *kp); - void ProcessList_goThroughEntries(ProcessList* this); + #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 4bb2e35e..0f5279e8 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -20,8 +20,6 @@ in the source distribution for its full text. #include "OpenBSDProcess.h" #include "OpenBSDProcessList.h" -#include <sys/sched.h> -#include <uvm/uvmexp.h> #include <sys/param.h> #include <sys/sysctl.h> #include <sys/swap.h> @@ -31,13 +29,13 @@ in the source distribution for its full text. #include <stdint.h> #include <string.h> #include <sys/types.h> -#include <sys/sysctl.h> #include <sys/time.h> #include <sys/resource.h> #include <time.h> #include <fcntl.h> #include <kvm.h> #include <limits.h> +#include <math.h> /*{ #include "Action.h" @@ -48,54 +46,6 @@ extern ProcessFieldData Process_fields[]; }*/ -#define MAXCPU 256 -// XXX: probably should be a struct member -static int64_t old_v[MAXCPU][5]; - -/* - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * Taken directly from OpenBSD's top(1). - * - * percentages(cnt, out, new, old, diffs) - calculate percentage change - * between array "old" and "new", putting the percentages in "out". - * "cnt" is size of each array and "diffs" is used for scratch space. - * The array "old" is updated on each call. - * The routine assumes modulo arithmetic. This function is especially - * useful on BSD machines for calculating cpu state percentages. - */ -static int percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_t *diffs) { - int64_t change, total_change, *dp, half_total; - int i; - - /* initialization */ - total_change = 0; - dp = diffs; - - /* calculate changes for each state and the overall change */ - for (i = 0; i < cnt; i++) { - if ((change = *new - *old) < 0) { - /* this only happens when the counter wraps */ - change = INT64_MAX - *old + *new; - } - total_change += (*dp++ = change); - *old++ = *new++; - } - - /* avoid divide by zero potential */ - if (total_change == 0) - total_change = 1; - - /* calculate percentages based on overall change, rounding up */ - half_total = total_change / 2l; - for (i = 0; i < cnt; i++) - *out++ = ((*diffs++ * 1000 + half_total) / total_change); - - /* return the total in case the caller wants to use it */ - return (total_change); -} - ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; int Platform_numberOfFields = LAST_PROCESSFIELD; @@ -201,43 +151,38 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { int Platform_getMaxPid() { // this is hard-coded in sys/sys/proc.h - no sysctl exists - return 32766; + return 99999; } double Platform_setCPUValues(Meter* this, int cpu) { - int i; - double perc; - - OpenBSDProcessList* pl = (OpenBSDProcessList*) this->pl; - CPUData* cpuData = &(pl->cpus[cpu]); - int64_t new_v[CPUSTATES], diff_v[CPUSTATES], scratch_v[CPUSTATES]; + const OpenBSDProcessList* pl = (OpenBSDProcessList*) this->pl; + const CPUData* cpuData = &(pl->cpus[cpu]); + double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; + double totalPercent; double *v = this->values; - size_t size = sizeof(double) * CPUSTATES; - int mib[] = { CTL_KERN, KERN_CPTIME2, cpu-1 }; - if (sysctl(mib, 3, new_v, &size, NULL, 0) == -1) { - return 0.; - } - - // XXX: why? - cpuData->totalPeriod = 1; - - percentages(CPUSTATES, diff_v, new_v, - (int64_t *)old_v[cpu-1], scratch_v); - for (i = 0; i < CPUSTATES; i++) { - old_v[cpu-1][i] = new_v[i]; - v[i] = diff_v[i] / 10.; - } - - Meter_setItems(this, 4); - - perc = v[0] + v[1] + v[2] + v[3]; - - if (perc <= 100. && perc >= 0.) { - return perc; + v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; + v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; + if (this->pl->settings->detailedCPUTime) { + v[CPU_METER_KERNEL] = cpuData->sysPeriod / total * 100.0; + v[CPU_METER_IRQ] = cpuData->intrPeriod / total * 100.0; + v[CPU_METER_SOFTIRQ] = 0.0; + v[CPU_METER_STEAL] = 0.0; + v[CPU_METER_GUEST] = 0.0; + v[CPU_METER_IOWAIT] = 0.0; + v[CPU_METER_FREQUENCY] = -1; + Meter_setItems(this, 8); + totalPercent = v[0]+v[1]+v[2]+v[3]; } else { - return 0.; + v[2] = cpuData->sysAllPeriod / total * 100.0; + v[3] = 0.0; // No steal nor guest on OpenBSD + totalPercent = v[0]+v[1]+v[2]; + Meter_setItems(this, 4); } + + totalPercent = CLAMP(totalPercent, 0.0, 100.0); + if (isnan(totalPercent)) totalPercent = 0.0; + return totalPercent; } void Platform_setMemoryValues(Meter* this) { diff --git a/openbsd/Platform.h b/openbsd/Platform.h index e0da7b9f..56e4c400 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -17,21 +17,6 @@ in the source distribution for its full text. extern ProcessFieldData Process_fields[]; -#define MAXCPU 256 -// XXX: probably should be a struct member -/* - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * Taken directly from OpenBSD's top(1). - * - * percentages(cnt, out, new, old, diffs) - calculate percentage change - * between array "old" and "new", putting the percentages in "out". - * "cnt" is size of each array and "diffs" is used for scratch space. - * The array "old" is updated on each call. - * The routine assumes modulo arithmetic. This function is especially - * useful on BSD machines for calculating cpu state percentages. - */ extern ProcessField Platform_defaultFields[]; extern int Platform_numberOfFields; diff --git a/scripts/MakeHeader.py b/scripts/MakeHeader.py index 349531b8..3ef34b88 100755 --- a/scripts/MakeHeader.py +++ b/scripts/MakeHeader.py @@ -54,8 +54,10 @@ for line in file.readlines(): elif line.startswith("typedef struct"): state = SKIP elif line[-1] == "{": - out.write( line[:-2].replace("inline", "extern") + ";\n" ) + out.write("extern " + line[:-2].replace("inline ", "") + ";\n") state = SKIP + elif line[-1] == ";": + out.write("extern " + line + "\n") else: out.write( line + "\n") is_blank = False diff --git a/solaris/Platform.c b/solaris/Platform.c index 7dcfe323..092f4507 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -50,7 +50,7 @@ typedef struct var kvar_t; typedef struct envAccum_ { size_t capacity; size_t size; - size_t bytes; + size_t bytes; char *env; } envAccum; @@ -140,7 +140,7 @@ extern char Process_pidFormat[20]; int Platform_getUptime() { int boot_time = 0; - int curr_time = time(NULL); + int curr_time = time(NULL); struct utmpx * ent; while (( ent = getutxent() )) { @@ -174,7 +174,7 @@ int Platform_getMaxPid() { vproc = ksvar->v_proc; } if (kc != NULL) { kstat_close(kc); } - return vproc; + return vproc; } double Platform_setCPUValues(Meter* this, int cpu) { @@ -207,6 +207,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = -1; + return percent; } @@ -239,7 +242,7 @@ void Platform_setZfsCompressedArcValues(Meter* this) { static int Platform_buildenv(void *accum, struct ps_prochandle *Phandle, uintptr_t addr, const char *str) { envAccum *accump = accum; (void) Phandle; - (void) addr; + (void) addr; size_t thissz = strlen(str); if ((thissz + 2) > (accump->capacity - accump->size)) accump->env = xRealloc(accump->env, accump->capacity *= 2); @@ -248,7 +251,7 @@ static int Platform_buildenv(void *accum, struct ps_prochandle *Phandle, uintptr strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size)); strncpy( accump->env + accump->size + thissz + 1, "\n", 1); accump->size = accump->size + thissz + 1; - return 0; + return 0; } char* Platform_getProcessEnv(pid_t pid) { @@ -256,7 +259,7 @@ char* Platform_getProcessEnv(pid_t pid) { pid_t realpid = pid / 1024; int graberr; struct ps_prochandle *Phandle; - + if ((Phandle = Pgrab(realpid,PGRAB_RDONLY,&graberr)) == NULL) return "Unable to read process environment."; @@ -264,7 +267,7 @@ char* Platform_getProcessEnv(pid_t pid) { envBuilder.size = 0; envBuilder.env = xMalloc(envBuilder.capacity); - (void) Penv_iter(Phandle,Platform_buildenv,&envBuilder); + (void) Penv_iter(Phandle,Platform_buildenv,&envBuilder); Prelease(Phandle, 0); diff --git a/solaris/Platform.h b/solaris/Platform.h index 3b5aef86..dd1962dd 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -27,7 +27,7 @@ typedef struct var kvar_t; typedef struct envAccum_ { size_t capacity; size_t size; - size_t bytes; + size_t bytes; char *env; } envAccum; diff --git a/solaris/SolarisProcess.c b/solaris/SolarisProcess.c index 31f488ef..514453fd 100644 --- a/solaris/SolarisProcess.c +++ b/solaris/SolarisProcess.c @@ -90,7 +90,7 @@ ProcessFieldData Process_fields[] = { [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, - [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index d62ea1d4..8faa49f3 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -25,6 +25,8 @@ in the source distribution for its full text. #define MAXCMDLINE 255 +#define GZONE "global " +#define UZONE "unknown " /*{ #include "zfs/ZfsArcStats.h" @@ -65,12 +67,12 @@ typedef struct SolarisProcessList_ { char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) { char* zname; if ( sproc->zoneid == 0 ) { - zname = xStrdup("global "); + zname = xStrdup(GZONE); } else if ( kd == NULL ) { - zname = xStrdup("unknown "); + zname = xStrdup(UZONE); } else { kstat_t* ks = kstat_lookup( kd, "zones", sproc->zoneid, NULL ); - zname = xStrdup(ks->ks_name); + zname = xStrdup(ks == NULL ? UZONE : ks->ks_name); } return zname; } @@ -156,7 +158,7 @@ static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { idlebuf += cpuData->idlePercent; } } - + if (cpus > 1) { CPUData* cpuData = &(spl->cpus[0]); cpuData->userPercent = userbuf / cpus; @@ -180,7 +182,7 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { uint64_t totalswap = 0; uint64_t totalfree = 0; int nswap = 0; - char *spath = NULL; + char *spath = NULL; char *spathbase = NULL; // Part 1 - physical memory @@ -194,7 +196,7 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { pl->totalMem = totalmem_pgs->value.ui64 * PAGE_SIZE_KB; pl->usedMem = lockedmem_pgs->value.ui64 * PAGE_SIZE_KB; // Not sure how to implement this on Solaris - suggestions welcome! - pl->cachedMem = 0; + pl->cachedMem = 0; // Not really "buffers" but the best Solaris analogue that I can find to // "memory in use but not by programs or the kernel itself" pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * PAGE_SIZE_KB; @@ -205,12 +207,12 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { pl->cachedMem = 0; pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * PAGE_SIZE); } - + // Part 2 - swap nswap = swapctl(SC_GETNSWP, NULL); if (nswap > 0) { sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); } if (sl != NULL) { spathbase = xMalloc( nswap * MAXPATHLEN ); } - if (spathbase != NULL) { + if (spathbase != NULL) { spath = spathbase; swapdev = sl->swt_ent; for (int i = 0; i < nswap; i++, swapdev++) { @@ -220,7 +222,7 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { sl->swt_n = nswap; } nswap = swapctl(SC_LIST, sl); - if (nswap > 0) { + if (nswap > 0) { swapdev = sl->swt_ent; for (int i = 0; i < nswap; i++, swapdev++) { totalswap += swapdev->ste_pages; @@ -230,7 +232,7 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { free(spathbase); free(sl); pl->totalSwap = totalswap * PAGE_SIZE_KB; - pl->usedSwap = pl->totalSwap - (totalfree * PAGE_SIZE_KB); + pl->usedSwap = pl->totalSwap - (totalfree * PAGE_SIZE_KB); } static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) { @@ -288,7 +290,7 @@ void ProcessList_delete(ProcessList* pl) { * and MUST conform to the appropriate definition in order * to work. See libproc(3LIB) on a Solaris or Illumos * system for more info. - */ + */ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr) { struct timeval tv; @@ -308,7 +310,7 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void * getpid = _psinfo->pr_pid * 1024; } else { getpid = lwpid; - } + } Process *proc = ProcessList_getProcess(pl, getpid, &preExisting, (Process_New) SolarisProcess_new); SolarisProcess *sproc = (SolarisProcess*) proc; @@ -339,7 +341,7 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void * sproc->realpid = _psinfo->pr_pid; sproc->lwpid = lwpid_real; sproc->zoneid = _psinfo->pr_zoneid; - sproc->zname = SolarisProcessList_readZoneName(spl->kd,sproc); + sproc->zname = SolarisProcessList_readZoneName(spl->kd,sproc); proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid); proc->comm = xStrdup(_psinfo->pr_fname); proc->commLen = strnlen(_psinfo->pr_fname,PRFNSZ); @@ -378,7 +380,7 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void * proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu/(double)32768)*(double)100.0; proc->time = _lwpsinfo->pr_time.tv_sec; if (!preExisting) { // Tasks done only for NEW LWPs - sproc->is_lwp = true; + sproc->is_lwp = true; proc->basenameOffset = -1; proc->ppid = _psinfo->pr_pid * 1024; proc->tgid = _psinfo->pr_pid * 1024; diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h index 26bf4491..06c1330e 100644 --- a/solaris/SolarisProcessList.h +++ b/solaris/SolarisProcessList.h @@ -12,6 +12,8 @@ in the source distribution for its full text. #define MAXCMDLINE 255 +#define GZONE "global " +#define UZONE "unknown " #include "zfs/ZfsArcStats.h" @@ -57,7 +59,7 @@ void ProcessList_delete(ProcessList* pl); * and MUST conform to the appropriate definition in order * to work. See libproc(3LIB) on a Solaris or Illumos * system for more info. - */ + */ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr); diff --git a/unsupported/Platform.c b/unsupported/Platform.c index ba844191..0e46a348 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -50,7 +50,7 @@ ProcessFieldData Process_fields[] = { [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, - [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, @@ -108,8 +108,11 @@ int Platform_getMaxPid() { } double Platform_setCPUValues(Meter* this, int cpu) { - (void) this; (void) cpu; + + double* v = this->values; + v[CPU_METER_FREQUENCY] = -1; + return 0.0; } diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index 9be4eee4..65749b18 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -18,7 +18,7 @@ in the source distribution for its full text. ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { ProcessList* this = xCalloc(1, sizeof(ProcessList)); ProcessList_init(this, Class(Process), usersTable, pidWhiteList, userId); - + return this; } |