15 Commits

141 changed files with 2545 additions and 4375 deletions

View File

@ -14,4 +14,3 @@ charset = utf-8
[*.{c,h}]
indent_style = space
indent_size = 3
trim_trailing_whitespace = true

View File

@ -1,29 +0,0 @@
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

1
.gitignore vendored
View File

@ -20,7 +20,6 @@ htop
.deps/
Makefile
Makefile.in
INSTALL
aclocal.m4
autom4te.cache/
compile

101
Action.c
View File

@ -62,7 +62,7 @@ typedef struct State_ {
}*/
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Object* Action_pickFromVector(State* st, Panel* list, int x) {
Panel* panel = st->panel;
Header* header = st->header;
Settings* settings = st->settings;
@ -75,8 +75,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
Panel* panelFocus;
int ch;
bool unfollow = false;
int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1;
if (followProcess && header->pl->following == -1) {
int pid = MainPanel_selectedPid((MainPanel*)panel);
if (header->pl->following == -1) {
header->pl->following = pid;
unfollow = true;
}
@ -88,16 +88,11 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1);
if (panelFocus == list && ch == 13) {
if (followProcess) {
Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid)
return Panel_getSelected(list);
else
beep();
} else {
return Panel_getSelected(list);
}
}
return NULL;
}
@ -120,7 +115,7 @@ static void Action_runSetup(Settings* settings, const Header* header, ProcessLis
static bool changePriority(MainPanel* panel, int delta) {
bool anyTagged;
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged);
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged);
if (!ok)
beep();
return anyTagged;
@ -160,25 +155,11 @@ static bool expandCollapse(Panel* panel) {
return true;
}
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
if (q->pid == ppid) {
q->showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
settings->treeView = false;
ScreenSettings* ss = settings->ss;
ss->sortKey = sortKey;
ss->direction = 1;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
@ -186,15 +167,16 @@ static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields;
ScreenSettings* ss = st->settings->ss;
ProcessField* fields = ss->fields;
for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey)
if (fields[i] == ss->sortKey)
Panel_setSelected(sortPanel, i);
free(name);
}
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15);
if (field) {
reaction |= Action_setSortKey(st->settings, field->key);
}
@ -205,7 +187,6 @@ static Htop_Reaction sortBy(State* st) {
// ----------------------------------------
static Htop_Reaction actionResize(State* st) {
clear();
Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1);
return HTOP_REDRAW_BAR;
}
@ -239,8 +220,9 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
}
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) st->settings->direction = 1;
ScreenSettings* ss = st->settings->ss;
ss->treeView = !ss->treeView;
if (ss->treeView) ss->direction = 1;
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@ -253,21 +235,10 @@ 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;
@ -279,7 +250,7 @@ static Htop_Reaction actionLowerPriority(State* st) {
}
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
ScreenSettings_invertSortOrder(st->settings->ss);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
@ -292,22 +263,24 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->treeView) {
return HTOP_OK;
}
bool changed = collapseIntoParent(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
@ -321,10 +294,10 @@ static Htop_Reaction actionSetAffinity(State* st) {
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity);
Affinity_delete(affinity);
void* set = Action_pickFromVector(st, affinityPanel, 15, true);
void* set = Action_pickFromVector(st, affinityPanel, 15);
if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity }, NULL);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (size_t) affinity, NULL);
if (!ok) beep();
Affinity_delete(affinity);
}
@ -335,13 +308,13 @@ 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, true);
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15);
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true);
refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL);
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
napms(500);
}
}
@ -356,7 +329,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, false);
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20);
if (picked) {
if (picked == allUsers) {
st->pl->userId = -1;
@ -479,7 +452,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_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
@ -489,8 +462,8 @@ 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_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "virtualiz");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
}
addattrstr(CRT_colors[BAR_BORDER], "]");
@ -575,8 +548,6 @@ 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;
@ -598,7 +569,6 @@ void Action_setBindings(Htop_Action* keys) {
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
@ -614,5 +584,6 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
}

View File

@ -39,20 +39,20 @@ typedef struct State_ {
} State;
extern Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
Object* Action_pickFromVector(State* st, Panel* list, int x);
// ----------------------------------------
extern bool Action_setUserOnly(const char* userName, uid_t* userId);
bool Action_setUserOnly(const char* userName, uid_t* userId);
extern Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ----------------------------------------
extern Htop_Reaction Action_follow(State* st);
Htop_Reaction Action_follow(State* st);
extern void Action_setBindings(Htop_Action* keys);
void Action_setBindings(Htop_Action* keys);
#endif

View File

@ -1,7 +1,6 @@
/*
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.
*/
@ -80,8 +79,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
return affinity;
}
bool Affinity_set(Process* proc, Arg arg) {
Affinity *this = arg.v;
bool Affinity_set(Process* proc, Affinity* this) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]);
@ -105,8 +103,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
return affinity;
}
bool Affinity_set(Process* proc, Arg arg) {
Affinity *this = arg.v;
bool Affinity_set(Process* proc, Affinity* this) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for (int i = 0; i < this->used; i++) {

View File

@ -5,7 +5,6 @@
/*
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.
*/
@ -30,23 +29,23 @@ typedef struct Affinity_ {
} Affinity;
extern Affinity* Affinity_new(ProcessList* pl);
Affinity* Affinity_new(ProcessList* pl);
extern void Affinity_delete(Affinity* this);
void Affinity_delete(Affinity* this);
extern void Affinity_add(Affinity* this, int id);
void Affinity_add(Affinity* this, int id);
#ifdef HAVE_LIBHWLOC
extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
Affinity* Affinity_get(Process* proc, ProcessList* pl);
extern bool Affinity_set(Process* proc, Arg arg);
bool Affinity_set(Process* proc, Affinity* this);
#elif HAVE_LINUX_AFFINITY
extern Affinity* Affinity_get(Process* proc, ProcessList* pl);
Affinity* Affinity_get(Process* proc, ProcessList* pl);
extern bool Affinity_set(Process* proc, Arg arg);
bool Affinity_set(Process* proc, Affinity* this);
#endif

View File

@ -16,8 +16,8 @@ in the source distribution for its full text.
extern PanelClass AffinityPanel_class;
extern Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity);
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity);
extern Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl);
Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl);
#endif

View File

@ -19,6 +19,6 @@ typedef struct AvailableColumnsPanel_ {
extern PanelClass AvailableColumnsPanel_class;
extern AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
#endif

View File

@ -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, Settings_cpuId(this->settings, i - 1));
xSnprintf(buffer, 50, "%s %d", type->uiName, i);
Panel_add(super, (Object*) ListItem_new(buffer, i));
}
} else {

View File

@ -27,6 +27,6 @@ typedef struct AvailableMetersPanel_ {
extern PanelClass AvailableMetersPanel_class;
extern AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);
#endif

View File

@ -1,56 +0,0 @@
Contributing Guide
==================
Hello, and thank you so much for taking your time to contribute in any way to
htop! There are many ways to contribute, and I'll try to list them below. The
support from the free software community has been amazing over the years and
it is the number one thing that keeps me going, maintaining and improving
something that started as a tiny pet project back in 2004 and that nowadays is
a piece of software used all over the world, in both reality [and
fiction!](http://hisham.hm/htop/index.php?page=sightings). Cheers!
-- Hisham Muhammad
Bug Reports
-----------
Bug reports should be posted in the [Github issue
tracker](http://github.com/hishamhm/htop/issues). (I reply to them all, but I
usually do it in batches! :) ) Bug reports are extremely important since it's
impossible for me to test htop in every possible system, distribution and
scenario. Your feedback is what keeps the tool stable and always improving!
Thank you!
Pull Requests
-------------
Code contributions are most welcome! Just [fork the
repo](http://github.com/hishamhm/htop) and send a [pull
request](https://github.com/hishamhm/htop/pulls). Help is especially
appreciated for support of platforms other than Linux. If proposing new
features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately I
can't accept every new feature proposed, as I need to keep the tool slim and
maintainable. Great ideas backed by a PR are always carefully considered for
inclusion, though! Also, PRs containing bug fixes and portability tweaks are a
no-brainer, please send those in!
Feature Requests
----------------
Back when htop was hosted in SourceForge, there used to be separate Bug
Tracker and Feature Request pages. These go all lumped together under "Issues"
in Github, which is a bit confusing. For this reason, I close Feature Requests
and file them with the [`feature
request`](https://github.com/hishamhm/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label, where they remain accessible, but not mixed with actual bug reports.
This doesn't mean I'm dismissing or ignoring feature requests right away! It's
just an organizational issue (with Github, really!).
Donations
---------
If you like htop, feel free to [buy the author a
beer](http://hisham.hm/htop/index.php?page=donate). :-)

View File

@ -28,14 +28,13 @@ typedef enum {
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
CPU_METER_ITEMCOUNT = 8, // number of entries in this enum
} CPUMeterValues;
}*/
int CPUMeter_attributes[] = {
CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT
CPU_NICE, CPU_NORMAL, CPU_KERNEL, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT
};
#ifndef MIN
@ -64,30 +63,7 @@ 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);
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) {
@ -104,7 +80,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_SYSTEM], buffer);
RichString_append(out, CRT_colors[CPU_KERNEL], 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);
@ -130,7 +106,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_SYSTEM], buffer);
RichString_append(out, CRT_colors[CPU_KERNEL], 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);
@ -161,15 +137,6 @@ 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)
@ -185,8 +152,10 @@ static void AllCPUsMeter_init(Meter* this) {
if (this->mode == 0)
this->mode = BAR_METERMODE;
int h = Meter_modes[this->mode]->h;
int ncol = MapClassnameToColumncount(this);
this->h = h * ((count + ncol - 1)/ ncol);
if (strchr(Meter_name(this), '2'))
this->h = h * ((count+1) / 2);
else
this->h = h * count;
}
static void AllCPUsMeter_done(Meter* this) {
@ -206,8 +175,10 @@ static void AllCPUsMeter_updateMode(Meter* this, int mode) {
for (int i = 0; i < count; i++) {
Meter_setMode(meters[i], mode);
}
int ncol = MapClassnameToColumncount(this);
this->h = h * ((count + ncol - 1)/ ncol);
if (strchr(Meter_name(this), '2'))
this->h = h * ((count+1) / 2);
else
this->h = h * count;
}
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
@ -238,22 +209,6 @@ 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),
@ -385,59 +340,3 @@ 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
};

View File

@ -20,8 +20,7 @@ typedef enum {
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_FREQUENCY = 8,
CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
CPU_METER_ITEMCOUNT = 8, // number of entries in this enum
} CPUMeterValues;
@ -48,10 +47,5 @@ extern MeterClass LeftCPUs2Meter_class;
extern MeterClass RightCPUs2Meter_class;
extern MeterClass AllCPUs4Meter_class;
extern MeterClass LeftCPUs4Meter_class;
extern MeterClass RightCPUs4Meter_class;
#endif

127
CRT.c
View File

@ -37,7 +37,6 @@ in the source distribution for its full text.
#define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
@ -122,28 +121,19 @@ typedef enum ColorElements_ {
CPU_NICE,
CPU_NICE_TEXT,
CPU_NORMAL,
CPU_SYSTEM,
CPU_KERNEL,
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,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
PANEL_EDIT,
LAST_COLORELEMENT
} ColorElements;
extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_fatalError(const char* note) __attribute__ ((noreturn));
extern void CRT_handleSIGSEGV(int sgn);
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
@ -236,22 +226,13 @@ 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_SYSTEM] = ColorPair(Red,Black),
[CPU_IOWAIT] = A_BOLD | ColorPairGrayBlack,
[CPU_KERNEL] = ColorPair(Red,Black),
[CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black),
[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),
[ZFS_HEADER] = ColorPair(Cyan,Black),
[ZFS_OTHER] = ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Blue,Black),
[ZFS_RATIO] = ColorPair(Magenta,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL,
@ -305,22 +286,13 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = A_NORMAL,
[CPU_NICE_TEXT] = A_NORMAL,
[CPU_NORMAL] = A_BOLD,
[CPU_SYSTEM] = A_BOLD,
[CPU_KERNEL] = 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,
[ZFS_HEADER] = A_BOLD,
[ZFS_OTHER] = A_DIM,
[ZFS_COMPRESSED] = A_BOLD,
[ZFS_RATIO] = A_BOLD,
[PANEL_EDIT] = A_BOLD,
},
[COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White),
@ -374,22 +346,13 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = ColorPair(Cyan,White),
[CPU_NICE_TEXT] = ColorPair(Cyan,White),
[CPU_NORMAL] = ColorPair(Green,White),
[CPU_SYSTEM] = ColorPair(Red,White),
[CPU_KERNEL] = 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),
[ZFS_HEADER] = ColorPair(Yellow,White),
[ZFS_OTHER] = ColorPair(Magenta,White),
[ZFS_COMPRESSED] = ColorPair(Cyan,White),
[ZFS_RATIO] = ColorPair(Magenta,White),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black),
@ -443,22 +406,13 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_NICE] = ColorPair(Cyan,Black),
[CPU_NICE_TEXT] = ColorPair(Cyan,Black),
[CPU_NORMAL] = ColorPair(Green,Black),
[CPU_SYSTEM] = ColorPair(Red,Black),
[CPU_KERNEL] = 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),
[ZFS_HEADER] = ColorPair(Black,Black),
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Cyan,Black),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue),
@ -512,22 +466,13 @@ 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_SYSTEM] = A_BOLD | ColorPair(Red,Blue),
[CPU_KERNEL] = 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),
[ZFS_HEADER] = A_BOLD | ColorPair(Yellow,Blue),
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Blue),
[ZFS_COMPRESSED] = A_BOLD | ColorPair(White,Blue),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Blue),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black),
@ -581,28 +526,17 @@ 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_SYSTEM] = ColorPair(Red,Black),
[CPU_KERNEL] = 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),
[ZFS_HEADER] = ColorPair(Yellow,Black),
[ZFS_OTHER] = ColorPair(Magenta,Black),
[ZFS_COMPRESSED] = ColorPair(Blue,Black),
[ZFS_RATIO] = ColorPair(Magenta,Black),
[PANEL_EDIT] = ColorPair(White,Cyan),
},
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
};
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;
@ -784,23 +718,22 @@ void CRT_enableDelay() {
void CRT_setColors(int colorScheme) {
CRT_colorScheme = colorScheme;
for (int i = 0; i < 8; i++) {
if (colorScheme == COLORSCHEME_BLACKNIGHT) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
if (ColorIndex(i,j) != ColorPairGrayBlack) {
int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? (j==0 ? -1 : j)
: j;
init_pair(ColorIndex(i,j), i, bg);
if (ColorIndex(i,j) != ColorIndex(Magenta,Magenta)) {
init_pair(ColorIndex(i,j), i, j);
}
}
init_pair(ColorIndex(Magenta,Magenta), 8, 0);
} else {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
if (ColorIndex(i,j) != ColorIndex(Magenta,Magenta)) {
init_pair(ColorIndex(i,j), i, (j==0?-1:j));
}
}
init_pair(ColorIndex(Magenta,Magenta), 8, -1);
}
int grayBlackFg = COLORS > 8 ? 8 : 0;
int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? -1
: 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
CRT_colors = CRT_colorSchemes[colorScheme];
}

44
CRT.h
View File

@ -26,7 +26,6 @@ in the source distribution for its full text.
#define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
@ -110,28 +109,19 @@ typedef enum ColorElements_ {
CPU_NICE,
CPU_NICE_TEXT,
CPU_NORMAL,
CPU_SYSTEM,
CPU_KERNEL,
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,
ZFS_HEADER,
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
PANEL_EDIT,
LAST_COLORELEMENT
} ColorElements;
extern void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_fatalError(const char* note) __attribute__ ((noreturn));
extern void CRT_handleSIGSEGV(int sgn);
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
@ -150,31 +140,29 @@ extern const char **CRT_treeStr;
extern int CRT_delay;
extern int* CRT_colors;
int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
extern char* CRT_termType;
char* CRT_termType;
// TODO move color scheme to Settings, perhaps?
extern int CRT_colorScheme;
extern void *backtraceArray[128];
void *backtraceArray[128];
#if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
extern void CRT_dropPrivileges();
void CRT_dropPrivileges();
extern void CRT_restorePrivileges();
void CRT_restorePrivileges();
#else
@ -189,18 +177,18 @@ extern void CRT_restorePrivileges();
// TODO: pass an instance of Settings instead.
extern void CRT_init(int delay, int colorScheme);
void CRT_init(int delay, int colorScheme);
extern void CRT_done();
void CRT_done();
extern void CRT_fatalError(const char* note);
void CRT_fatalError(const char* note);
extern int CRT_readKey();
int CRT_readKey();
extern void CRT_disableDelay();
void CRT_disableDelay();
extern void CRT_enableDelay();
void CRT_enableDelay();
extern void CRT_setColors(int colorScheme);
void CRT_setColors(int colorScheme);
#endif

View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "DisplayOptionsPanel.h"
#include "ColumnsPanel.h"
#include "ScreensPanel.h"
#include "ColorsPanel.h"
#include "AvailableColumnsPanel.h"
@ -64,9 +64,11 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1);
}
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Panel* screens = (Panel*) ScreensPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}
@ -118,7 +120,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel_makeColorsPage(this);
break;
case 3:
CategoriesPanel_makeColumnsPage(this);
CategoriesPanel_makeScreensPage(this);
break;
}
}
@ -147,6 +149,6 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0));
Panel_add(super, (Object*) ListItem_new("Screens", 0));
return this;
}

View File

@ -24,10 +24,10 @@ typedef struct CategoriesPanel_ {
} CategoriesPanel;
extern void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
extern PanelClass CategoriesPanel_class;
extern CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);
#endif

View File

@ -1,68 +1,3 @@
What's new in version 2.2.0
* Solaris/Illumos/OpenIndiana support
(thanks to Guy M. Broome)
* -t/--tree flag for starting in tree-view mode
(thanks to Daniel Flanagan)
* macOS: detects High Sierra version to avoid OS bug
(thanks to Pierre Malhaire)
* OpenBSD: read battery data
(thanks to @nerd972)
* Various automake and build improvements
(thanks to Kang-Che Sung)
* Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report)
* Avoid some bashisms in configure script
(thanks to Jesin)
* Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein)
* Header generator supports non-UTF-8 environments
(thanks to @volkov-am)
* Linux: changed detection of kernel threads
* Collapse current subtree pressing Backspace
* BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde)
* BUGFIX: obtain exit code of lsof correctly
(thanks to @wangqr)
* BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fixed overflow for systems with >= 100 signals
What's new in version 2.1.0
* Linux: Delay accounting metrics
(thanks to André Carvalho)
* DragonFlyBSD support
(thanks to Diederik de Groot)
* Support for real-time signals
(thanks to Kang-Che Sung)
* 'c' key now works with threads as well
* Session column renamed from SESN to SID
(thanks to Kamyar Rasta)
* Improved UI for meter style selection
(thanks to Kang-Che Sung)
* Improved code for constructing process tree
(thanks to wangqr)
* Compile-time option to disable setuid
* Error checking of various standard library operations
* Replacement of sprintf with snprintf
(thanks to Tomasz Kramkowski)
* Linux: performance improvements in battery meter
* Linux: update process TTY device
* Linux: add support for sorting TASK_IDLE
(thanks to Vladimir Panteleev)
* Linux: add upper-bound to running process counter
(thanks to Lucas Correia Villa Real)
* BUGFIX: avoid crash when battery is removed
(thanks to Jan Chren)
* BUGFIX: macOS: fix infinite loop in tree view
(thanks to Wataru Ashihara)
What's new in version 2.0.2

View File

@ -21,12 +21,12 @@ typedef struct CheckItem_ {
extern ObjectClass CheckItem_class;
extern CheckItem* CheckItem_newByRef(char* text, bool* ref);
CheckItem* CheckItem_newByRef(char* text, bool* ref);
extern CheckItem* CheckItem_newByVal(char* text, bool value);
CheckItem* CheckItem_newByVal(char* text, bool value);
extern void CheckItem_set(CheckItem* this, bool value);
void CheckItem_set(CheckItem* this, bool value);
extern bool CheckItem_get(CheckItem* this);
bool CheckItem_get(CheckItem* this);
#endif

View File

@ -78,7 +78,6 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
this->settings->changed = true;
const Header* header = this->scr->header;
CRT_setColors(mark);
clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);

View File

@ -29,6 +29,6 @@ typedef struct ColorsPanel_ {
extern PanelClass ColorsPanel_class;
extern ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
#endif

View File

@ -22,8 +22,9 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
@ -123,22 +124,31 @@ PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler
};
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss) {
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->fields;
Panel_prune(super);
ProcessField* fields = ss->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
this->ss = ss;
}
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->ss = ss;
this->changed = changed;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ColumnsPanel_fill(this, ss);
return this;
}
@ -154,14 +164,14 @@ int ColumnsPanel_fieldNameToIndex(const char* name) {
void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
this->settings->flags = 0;
*(this->changed) = true;
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size+1));
this->ss->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
this->settings->fields[i] = key;
this->settings->flags |= Process_fields[key].flags;
this->ss->fields[i] = key;
this->ss->flags |= key < 1000 ? Process_fields[key].flags : 0;
}
this->settings->fields[size] = 0;
this->ss->fields[size] = 0;
}

View File

@ -14,19 +14,22 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
extern PanelClass ColumnsPanel_class;
extern ColumnsPanel* ColumnsPanel_new(Settings* settings);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss);
extern int ColumnsPanel_fieldNameToIndex(const char* name);
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed);
extern void ColumnsPanel_update(Panel* super);
int ColumnsPanel_fieldNameToIndex(const char* name);
void ColumnsPanel_update(Panel* super);
#endif

View File

@ -83,7 +83,6 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
@ -97,8 +96,5 @@ 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;
}

View File

@ -23,6 +23,6 @@ typedef struct DisplayOptionsPanel_ {
extern PanelClass DisplayOptionsPanel_class;
extern DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
#endif

View File

@ -11,12 +11,12 @@ typedef struct EnvScreen_ {
extern InfoScreenClass EnvScreen_class;
extern EnvScreen* EnvScreen_new(Process* process);
EnvScreen* EnvScreen_new(Process* process);
extern void EnvScreen_delete(Object* this);
void EnvScreen_delete(Object* this);
extern void EnvScreen_draw(InfoScreen* this);
void EnvScreen_draw(InfoScreen* this);
extern void EnvScreen_scan(InfoScreen* this);
void EnvScreen_scan(InfoScreen* this);
#endif

View File

@ -96,11 +96,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
void FunctionBar_draw(const FunctionBar* this, char* buffer) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
int FunctionBar_draw(const FunctionBar* this, char* buffer) {
return FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
}
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int cursorX = 0;
attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS);
int x = 0;
@ -115,12 +116,10 @@ void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
if (buffer) {
attrset(attr);
mvaddstr(LINES-1, x, buffer);
CRT_cursorX = x + strlen(buffer);
curs_set(1);
} else {
curs_set(0);
cursorX = x + strlen(buffer);
}
attrset(CRT_colors[RESET_COLOR]);
return cursorX;
}
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -22,18 +22,18 @@ typedef struct FunctionBar_ {
extern FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
extern FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
extern void FunctionBar_delete(FunctionBar* this);
void FunctionBar_delete(FunctionBar* this);
extern void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
extern void FunctionBar_draw(const FunctionBar* this, char* buffer);
int FunctionBar_draw(const FunctionBar* this, char* buffer);
extern void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
extern int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);
#endif

View File

@ -30,20 +30,20 @@ struct Hashtable_ {
#ifdef DEBUG
extern int Hashtable_count(Hashtable* this);
int Hashtable_count(Hashtable* this);
#endif
extern Hashtable* Hashtable_new(int size, bool owner);
Hashtable* Hashtable_new(int size, bool owner);
extern void Hashtable_delete(Hashtable* this);
void Hashtable_delete(Hashtable* this);
extern void Hashtable_put(Hashtable* this, unsigned int key, void* value);
void Hashtable_put(Hashtable* this, unsigned int key, void* value);
extern void* Hashtable_remove(Hashtable* this, unsigned int key);
void* Hashtable_remove(Hashtable* this, unsigned int key);
extern void* Hashtable_get(Hashtable* this, unsigned int key);
extern void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
#endif

View File

@ -62,7 +62,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) {
@ -75,7 +75,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
String_freeArray(colSettings->names);
free(colSettings->modes);
@ -120,8 +120,6 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
break;
}
}
if (paren)
*paren = '(';
return mode;
}

View File

@ -31,30 +31,30 @@ typedef struct Header_ {
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
extern Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);
extern void Header_delete(Header* this);
void Header_delete(Header* this);
extern void Header_populateFromSettings(Header* this);
void Header_populateFromSettings(Header* this);
extern void Header_writeBackToSettings(const Header* this);
void Header_writeBackToSettings(const Header* this);
extern MeterModeId Header_addMeterByName(Header* this, char* name, int column);
MeterModeId Header_addMeterByName(Header* this, char* name, int column);
extern void Header_setMode(Header* this, int i, MeterModeId mode, int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column);
extern Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column);
Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column);
extern int Header_size(Header* this, int column);
int Header_size(Header* this, int column);
extern char* Header_readMeterName(Header* this, int i, int column);
char* Header_readMeterName(Header* this, int i, int column);
extern MeterModeId Header_readMeterMode(Header* this, int i, int column);
MeterModeId Header_readMeterMode(Header* this, int i, int column);
extern void Header_reinit(Header* this);
void Header_reinit(Header* this);
extern void Header_draw(const Header* this);
void Header_draw(const Header* this);
extern int Header_calculateHeight(Header* this);
int Header_calculateHeight(Header* this);
#endif

370
INSTALL Normal file
View File

@ -0,0 +1,370 @@
Installation Instructions
*************************
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell command `./configure && make && make install'
should configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
HP-UX `make' updates targets which have the same time stamps as
their prerequisites, which makes it generally unusable when shipped
generated files such as `configure' are involved. Use GNU `make'
instead.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf limitation. Until the limitation is lifted, you can use
this workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -38,6 +38,7 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
@ -52,10 +53,6 @@ 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};
@ -119,45 +116,33 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
}
}
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
static bool search(IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel);
bool found = false;
for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) {
Panel_setSelected(panel, i);
found = true;
break;
}
}
if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]);
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;
}
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
panel->cursorOn = true;
this->panel = panel;
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
}
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);
static void IncSet_deactivate(IncSet* this, Panel* panel) {
this->active = NULL;
Panel_setDefaultBar(panel);
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar, NULL);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
@ -169,7 +154,17 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
bool doSearch = true;
if (ch == KEY_F(3)) {
if (size == 0) return true;
IncMode_find(mode, panel, getPanelValue, 1);
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;
}
}
doSearch = false;
} else if (ch < 255 && isprint((char)ch)) {
if (mode->index < INCMODE_MAX) {
@ -205,17 +200,13 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode);
}
} else {
if (ch == 27) {
IncMode_reset(mode);
}
}
this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
IncSet_deactivate(this, panel);
doSearch = false;
}
if (doSearch) {
this->found = search(mode, panel, getPanelValue);
this->found = search(this, panel, getPanelValue);
}
if (filterChanged && lines) {
updateWeakPanel(this, panel, lines);
@ -230,15 +221,11 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
void IncSet_drawBar(IncSet* this, int attr) {
if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer);
int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else {
FunctionBar_draw(this->defaultBar, NULL);
}

View File

@ -33,6 +33,7 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
@ -41,24 +42,18 @@ typedef struct IncSet_ {
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
extern void IncSet_reset(IncSet* this, IncType type);
IncSet* IncSet_new(FunctionBar* bar);
extern IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this);
extern void IncSet_delete(IncSet* this);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
extern bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
extern bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
const char* IncSet_getListItemValue(Panel* panel, int i);
extern bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
void IncSet_drawBar(IncSet* this, int attr);
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);
int IncSet_synthesizeEvent(IncSet* this, int x);
#endif

View File

@ -85,7 +85,7 @@ void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
va_end(ap);
}
@ -115,11 +115,7 @@ void InfoScreen_run(InfoScreen* this) {
Panel_draw(panel, true);
if (this->inc->active) {
(void) move(LINES-1, CRT_cursorX);
}
set_escdelay(25);
int ch = getch();
int ch = Panel_getCh(panel);
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {
@ -131,15 +127,13 @@ 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;
} else if (mevent.y == LINES - 1) {
} 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);

View File

@ -38,16 +38,16 @@ struct InfoScreen_ {
Vector* lines;
};
extern InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader);
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader);
extern InfoScreen* InfoScreen_done(InfoScreen* this);
InfoScreen* InfoScreen_done(InfoScreen* this);
extern void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...);
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...);
extern void InfoScreen_addLine(InfoScreen* this, const char* line);
void InfoScreen_addLine(InfoScreen* this, const char* line);
extern void InfoScreen_appendLine(InfoScreen* this, const char* line);
void InfoScreen_appendLine(InfoScreen* this, const char* line);
extern void InfoScreen_run(InfoScreen* this);
void InfoScreen_run(InfoScreen* this);
#endif

View File

@ -27,13 +27,13 @@ typedef struct ListItem_ {
}*/
static void ListItem_delete(Object* cast) {
void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast;
free(this->value);
free(this);
}
static void ListItem_display(Object* cast, RichString* out) {
void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast;
assert (this != NULL);
/*
@ -59,11 +59,15 @@ ObjectClass ListItem_class = {
.compare = ListItem_compare
};
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
void ListItem_init(ListItem* this, const char* value, int key) {
this->value = xStrdup(value);
this->key = key;
this->moving = false;
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
return this;
}

View File

@ -19,15 +19,21 @@ typedef struct ListItem_ {
} ListItem;
void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
extern ObjectClass ListItem_class;
extern ListItem* ListItem_new(const char* value, int key);
void ListItem_init(ListItem* this, const char* value, int key);
extern void ListItem_append(ListItem* this, const char* text);
ListItem* ListItem_new(const char* value, int key);
extern const char* ListItem_getRef(ListItem* this);
void ListItem_append(ListItem* this, const char* text);
extern long ListItem_compare(const void* cast1, const void* cast2);
const char* ListItem_getRef(ListItem* this);
long ListItem_compare(const void* cast1, const void* cast2);
#endif

View File

@ -1,7 +1,6 @@
/*
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.
*/
@ -26,7 +25,7 @@ typedef struct MainPanel_ {
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
@ -68,15 +67,17 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
if (field == ss->sortKey) {
ScreenSettings_invertSortOrder(ss);
ss->treeView = false;
} else {
reaction |= Action_setSortKey(settings, field);
}
@ -109,8 +110,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
MainPanel_updateTreeFunctions(this, settings->ss->treeView);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
@ -149,7 +150,7 @@ const char* MainPanel_getValue(MainPanel* this, int i) {
return "";
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, size_t arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this;
bool ok = true;
bool anyTagged = false;

View File

@ -5,7 +5,6 @@
/*
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.
*/
@ -22,27 +21,27 @@ typedef struct MainPanel_ {
pid_t pidSearch;
} MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
extern void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
extern void MainPanel_pidSearch(MainPanel* this, int ch);
void MainPanel_pidSearch(MainPanel* this, int ch);
extern int MainPanel_selectedPid(MainPanel* this);
int MainPanel_selectedPid(MainPanel* this);
extern const char* MainPanel_getValue(MainPanel* this, int i);
const char* MainPanel_getValue(MainPanel* this, int i);
extern bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, size_t arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class;
extern MainPanel* MainPanel_new();
MainPanel* MainPanel_new();
extern void MainPanel_setState(MainPanel* this, State* state);
void MainPanel_setState(MainPanel* this, State* state);
extern void MainPanel_delete(Object* object);
void MainPanel_delete(Object* object);
#endif

View File

@ -12,8 +12,8 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png
AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS =
htop_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
htop_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
@ -21,7 +21,7 @@ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c ScreensPanel.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c
@ -31,187 +31,71 @@ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h ScreensPanel.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h
all_platform_headers =
# Linux
# -----
linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h \
linux/PressureStallMeter.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX
AM_LDFLAGS += -rdynamic
htop_CFLAGS += -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
linux/PerfCounter.c
myhtopplatheaders = $(linux_platform_headers)
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h \
linux/PerfCounter.h
endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c
myhtopplatheaders = $(freebsd_platform_headers)
myhtopplatheaders = freebsd/Platform.h freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h
endif
# DragonFlyBSD
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
all_platform_headers += $(dragonflybsd_platform_headers)
if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
htop_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = $(dragonflybsd_platform_headers)
myhtopplatheaders = dragonflybsd/Platform.h dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDCRT.h dragonflybsd/Battery.h
endif
# OpenBSD
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
all_platform_headers += $(openbsd_platform_headers)
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
myhtopplatheaders = $(openbsd_platform_headers)
myhtopplatheaders = openbsd/Platform.h openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h openbsd/OpenBSDCRT.h openbsd/Battery.h
endif
# Darwin
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
zfs/openzfs_sysctl.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
htop_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c
myhtopplatheaders = $(darwin_platform_headers)
myhtopplatheaders = darwin/Platform.h darwin/DarwinProcess.h \
darwin/DarwinProcessList.h darwin/DarwinCRT.h darwin/Battery.h
endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers)
endif
# Unsupported
# -----------
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
all_platform_headers += $(unsupported_platform_headers)
if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = $(unsupported_platform_headers)
myhtopplatheaders = unsupported/Platform.h \
unsupported/UnsupportedProcess.h unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h unsupported/Battery.h
endif
# ----
SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h
.PHONY: htop-headers clean-htop-headers
htop-headers: $(myhtopheaders) $(all_platform_headers)
clean-htop-headers:
-rm -f $(myhtopheaders) $(all_platform_headers)
target:
echo $(htop_SOURCES)
@ -221,6 +105,9 @@ profile:
debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
@ -230,13 +117,6 @@ coverage:
cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \
echo 'configure is generated without pkg.m4. Please supply pkg.m4 and run ./autogen.sh to rebuild the configure script.'>&2; \
(exit 1); \
else :; \
fi
.PHONY: lcov
lcov:

View File

@ -152,7 +152,7 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
if (value / 1024 < powi)
break;
if (prefix[1] == '\0')
if (prefix[1] == 0)
break;
powi *= 1024;
@ -287,7 +287,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
int blockSizes[10];
xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
xSnprintf(bar, w + 1, "%*s", w, buffer);
// First draw in the bar[] buffer...
int offset = 0;
@ -404,7 +404,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
k = -i/2;
i = 0;
}
for (; i < nValues - 1; i+=2, k++) {
for (; i < nValues; i+=2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);
@ -523,9 +523,6 @@ 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) {

12
Meter.h
View File

@ -109,17 +109,17 @@ typedef struct GraphData_ {
extern MeterClass Meter_class;
extern Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
extern int Meter_humanUnit(char* buffer, unsigned long int value, int size);
int Meter_humanUnit(char* buffer, unsigned long int value, int size);
extern void Meter_delete(Object* cast);
void Meter_delete(Object* cast);
extern void Meter_setCaption(Meter* this, const char* caption);
void Meter_setCaption(Meter* this, const char* caption);
extern void Meter_setMode(Meter* this, int modeIndex);
void Meter_setMode(Meter* this, int modeIndex);
extern ListItem* Meter_toListItem(Meter* this, bool moving);
ListItem* Meter_toListItem(Meter* this, bool moving);
/* ---------- TextMeterMode ---------- */

View File

@ -34,10 +34,10 @@ struct MetersPanel_ {
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
extern void MetersPanel_setMoving(MetersPanel* this, bool moving);
void MetersPanel_setMoving(MetersPanel* this, bool moving);
extern PanelClass MetersPanel_class;
extern MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);
#endif

View File

@ -1,7 +1,6 @@
/*
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.
*/
@ -41,11 +40,6 @@ struct Object_ {
ObjectClass* klass;
};
typedef union {
int i;
void* v;
} Arg;
}*/
ObjectClass Object_class = {

View File

@ -5,7 +5,6 @@
/*
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.
*/
@ -42,17 +41,12 @@ struct Object_ {
ObjectClass* klass;
};
typedef union {
int i;
void* v;
} Arg;
extern ObjectClass Object_class;
#ifdef DEBUG
extern bool Object_isA(Object* o, const ObjectClass* klass);
bool Object_isA(Object* o, const ObjectClass* klass);
#endif

View File

@ -76,35 +76,16 @@ void OpenFilesScreen_draw(InfoScreen* this) {
}
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char buffer[1025];
xSnprintf(buffer, 1024, "%d", pid);
char command[1025];
xSnprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid);
FILE* fd = popen(command, "r");
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data);
int fdpair[2];
if (pipe(fdpair) == -1) {
pdata->error = 1;
if (!fd) {
pdata->error = 127;
return pdata;
}
pid_t child = fork();
if (child == -1) {
pdata->error = 1;
return pdata;
}
if (child == 0) {
close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0)
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
FILE* fd = fdopen(fdpair[0], "r");
for (;;) {
char* line = String_readLine(fd);
if (!line) {
@ -124,16 +105,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;
return pdata;
}
if (!WIFEXITED(wstatus))
pdata->error = 1;
else
pdata->error = WEXITSTATUS(wstatus);
pdata->error = pclose(fd);
return pdata;
}
@ -158,7 +130,7 @@ void OpenFilesScreen_scan(InfoScreen* this) {
char** data = fdata->data.data;
int lenN = data['n'] ? strlen(data['n']) : 0;
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
char* entry = xMalloc(sizeEntry);
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "",
data['t'] ? data['t'] : "",

View File

@ -34,12 +34,12 @@ typedef struct OpenFilesScreen_ {
extern InfoScreenClass OpenFilesScreen_class;
extern OpenFilesScreen* OpenFilesScreen_new(Process* process);
OpenFilesScreen* OpenFilesScreen_new(Process* process);
extern void OpenFilesScreen_delete(Object* this);
void OpenFilesScreen_delete(Object* this);
extern void OpenFilesScreen_draw(InfoScreen* this);
void OpenFilesScreen_draw(InfoScreen* this);
extern void OpenFilesScreen_scan(InfoScreen* this);
void OpenFilesScreen_scan(InfoScreen* this);
#endif

24
Panel.c
View File

@ -57,6 +57,7 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
@ -66,6 +67,7 @@ struct Panel_ {
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -85,6 +87,11 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
PanelClass Panel_class = {
.super = {
.extends = Class(Object),
@ -112,6 +119,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->y = y;
this->w = w;
this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0;
@ -367,7 +376,6 @@ void Panel_draw(Panel* this, bool focus) {
RichString_end(old);
}
this->oldSelected = this->selected;
move(0, 0);
}
bool Panel_onKey(Panel* this, int key) {
@ -469,7 +477,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
if (ch > 0 && ch < 255 && isalnum(ch)) {
if (ch < 255 && isalnum(ch)) {
int len = strlen(buffer);
if (len < 99) {
buffer[len] = ch;
@ -499,3 +507,15 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
}
return IGNORED;
}
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
set_escdelay(25);
return getch();
}

53
Panel.h
View File

@ -46,6 +46,7 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
@ -55,6 +56,7 @@ struct Panel_ {
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -73,54 +75,59 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern PanelClass Panel_class;
extern Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
extern void Panel_delete(Object* cast);
void Panel_delete(Object* cast);
extern void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar);
extern void Panel_done(Panel* this);
void Panel_done(Panel* this);
extern void Panel_setSelectionColor(Panel* this, int color);
void Panel_setSelectionColor(Panel* this, int color);
extern RichString* Panel_getHeader(Panel* this);
RichString* Panel_getHeader(Panel* this);
extern void Panel_setHeader(Panel* this, const char* header);
extern void Panel_move(Panel* this, int x, int y);
void Panel_move(Panel* this, int x, int y);
extern void Panel_resize(Panel* this, int w, int h);
void Panel_resize(Panel* this, int w, int h);
extern void Panel_prune(Panel* this);
void Panel_prune(Panel* this);
extern void Panel_add(Panel* this, Object* o);
void Panel_add(Panel* this, Object* o);
extern void Panel_insert(Panel* this, int i, Object* o);
void Panel_insert(Panel* this, int i, Object* o);
extern void Panel_set(Panel* this, int i, Object* o);
void Panel_set(Panel* this, int i, Object* o);
extern Object* Panel_get(Panel* this, int i);
Object* Panel_get(Panel* this, int i);
extern Object* Panel_remove(Panel* this, int i);
Object* Panel_remove(Panel* this, int i);
extern Object* Panel_getSelected(Panel* this);
Object* Panel_getSelected(Panel* this);
extern void Panel_moveSelectedUp(Panel* this);
void Panel_moveSelectedUp(Panel* this);
extern void Panel_moveSelectedDown(Panel* this);
void Panel_moveSelectedDown(Panel* this);
extern int Panel_getSelectedIndex(Panel* this);
int Panel_getSelectedIndex(Panel* this);
extern int Panel_size(Panel* this);
int Panel_size(Panel* this);
extern void Panel_setSelected(Panel* this, int selected);
void Panel_setSelected(Panel* this, int selected);
extern void Panel_draw(Panel* this, bool focus);
void Panel_draw(Panel* this, bool focus);
extern bool Panel_onKey(Panel* this, int key);
bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
extern HandlerResult Panel_selectByTyping(Panel* this, int ch);
#endif

118
Process.c
View File

@ -1,7 +1,6 @@
/*
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.
*/
@ -9,8 +8,6 @@ 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"
@ -31,11 +28,7 @@ in the source distribution for its full text.
#include <time.h>
#include <assert.h>
#include <math.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
#include <inttypes.h>
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
@ -158,7 +151,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -180,12 +173,8 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
}*/
static int Process_getuid = -1;
@ -193,12 +182,10 @@ 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 ";
@ -232,7 +219,7 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
if(number >= (10 * ONE_DECIMAL_M)) {
#ifdef __LP64__
if(number >= (100 * ONE_DECIMAL_G)) {
len = snprintf(buffer, 10, "%4luT ", number / ONE_G);
len = snprintf(buffer, 10, "%4ldT ", number / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
} else if (number >= (1000 * ONE_DECIMAL_M)) {
@ -242,7 +229,7 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
}
#endif
if(number >= (100 * ONE_DECIMAL_M)) {
len = snprintf(buffer, 10, "%4luG ", number / ONE_M);
len = snprintf(buffer, 10, "%4ldG ", number / ONE_M);
RichString_appendn(str, largeNumberColor, buffer, len);
return;
}
@ -250,11 +237,11 @@ void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
RichString_appendn(str, largeNumberColor, buffer, len);
return;
} else if (number >= 100000) {
len = snprintf(buffer, 10, "%4luM ", number / ONE_K);
len = snprintf(buffer, 10, "%4ldM ", number / ONE_K);
RichString_appendn(str, processMegabytesColor, buffer, len);
return;
} else if (number >= 1000) {
len = snprintf(buffer, 10, "%2lu", number/1000);
len = snprintf(buffer, 10, "%2ld", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len);
number %= 1000;
len = snprintf(buffer, 10, "%03lu ", number);
@ -281,15 +268,8 @@ 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 >= 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);
} else if (number > 10000000000) {
xSnprintf(buffer, 13, "%11lld ", number / 1000);
RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4);
@ -369,21 +349,33 @@ 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_M) {
} else if (rate < ONE_K * ONE_K) {
int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K);
RichString_appendn(str, processColor, buffer, len);
} else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
} else if (rate < ONE_K * ONE_K * ONE_K) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_K / ONE_K);
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 T/s ", rate / ONE_T);
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_K / ONE_K / ONE_K);
RichString_appendn(str, largeNumberColor, buffer, len);
}
}
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
if (val >= 0) {
if (val < 100) {
xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 1000) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)val);
} else {
xSnprintf(buffer, n, "%4d ", (unsigned int)val);
}
} else {
*attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
@ -392,30 +384,15 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes;
switch (field) {
case PERCENT_CPU: {
if (this->percent_cpu > 999.9) {
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);
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
xSnprintf(buffer, n, "100. ");
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
if (!this->settings->treeView || this->indent == 0) {
ScreenSettings* ss = this->settings->ss;
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
@ -428,20 +405,15 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
if (indent & (1U << i))
maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) {
int written, ret;
int written;
if (indent & (1 << i))
ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
written = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
else
ret = snprintf(buf, n, " ");
if (ret < 0 || ret >= n) {
written = n;
} else {
written = ret;
}
written = snprintf(buf, n, " ");
buf += written;
n -= written;
}
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str);
@ -485,7 +457,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
}
break;
}
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case ST_UID: xSnprintf(buffer, n, "%4d ", 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;
@ -512,7 +484,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
RichString_prune(out);
for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]);
@ -563,15 +535,14 @@ bool Process_setPriority(Process* this, int priority) {
return (err == 0);
}
bool Process_changePriorityBy(Process* this, Arg delta) {
return Process_setPriority(this, this->nice + delta.i);
bool Process_changePriorityBy(Process* this, size_t delta) {
return Process_setPriority(this, this->nice + delta);
}
bool Process_sendSignal(Process* this, Arg sgn) {
void Process_sendSignal(Process* this, size_t sgn) {
CRT_dropPrivileges();
bool ok = (kill(this->pid, sgn.i) == 0);
kill(this->pid, (int) sgn);
CRT_restorePrivileges();
return ok;
}
long Process_pidCompare(const void* v1, const void* v2) {
@ -583,14 +554,15 @@ long Process_pidCompare(const void* v1, const void* v2) {
long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
ScreenSettings* ss = settings->ss;
if (ss->direction == 1) {
p1 = (Process*)v1;
p2 = (Process*)v2;
} else {
p2 = (Process*)v1;
p1 = (Process*)v2;
}
switch (settings->sortKey) {
switch (ss->sortKey) {
case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM:
@ -628,7 +600,7 @@ long Process_compare(const void* v1, const void* v2) {
return (p1->starttime_ctime - p2->starttime_ctime);
}
case STATE:
return (Process_sortState(p1->state) - Process_sortState(p2->state));
return (p1->state - p2->state);
case ST_UID:
return (p1->st_uid - p2->st_uid);
case TIME:

View File

@ -5,15 +5,10 @@
/*
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)
#endif
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
@ -134,7 +129,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -156,55 +151,51 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
#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];
extern void Process_setupColumnWidths();
void Process_setupColumnWidths();
extern void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
extern void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
extern void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_printTime(RichString* str, unsigned long long totalHundredths);
extern void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
extern void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
extern void Process_display(Object* cast, RichString* out);
void Process_writeField(Process* this, RichString* str, ProcessField field);
extern void Process_done(Process* this);
void Process_display(Object* cast, RichString* out);
void Process_done(Process* this);
extern ProcessClass Process_class;
extern void Process_init(Process* this, struct Settings_* settings);
void Process_init(Process* this, struct Settings_* settings);
extern void Process_toggleTag(Process* this);
void Process_toggleTag(Process* this);
extern bool Process_setPriority(Process* this, int priority);
bool Process_setPriority(Process* this, int priority);
extern bool Process_changePriorityBy(Process* this, Arg delta);
bool Process_changePriorityBy(Process* this, size_t delta);
extern bool Process_sendSignal(Process* this, Arg sgn);
void Process_sendSignal(Process* this, size_t sgn);
extern long Process_pidCompare(const void* v1, const void* v2);
long Process_pidCompare(const void* v1, const void* v2);
extern long Process_compare(const void* v1, const void* v2);
long Process_compare(const void* v1, const void* v2);
#endif

View File

@ -124,14 +124,15 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
unsigned int key = fields[i];
const char* field = Process_fields[key].title;
if (!field) field = "- ";
if (!this->settings->treeView && this->settings->sortKey == fields[i])
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
else
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
int color = (!this->settings->ss->treeView && this->settings->ss->sortKey == key)
? CRT_colors[PANEL_SELECTION_FOCUS]
: CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, color, field);
}
}
@ -200,19 +201,19 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
}
void ProcessList_sort(ProcessList* this) {
if (!this->settings->treeView) {
if (!this->settings->ss->treeView) {
Vector_insertionSort(this->processes);
} else {
// Save settings
int direction = this->settings->direction;
int sortKey = this->settings->sortKey;
int direction = this->settings->ss->direction;
int sortKey = this->settings->ss->sortKey;
// Sort by PID
this->settings->sortKey = PID;
this->settings->direction = 1;
this->settings->ss->sortKey = PID;
this->settings->ss->direction = 1;
Vector_quickSort(this->processes);
// Restore settings
this->settings->sortKey = sortKey;
this->settings->direction = direction;
this->settings->ss->sortKey = sortKey;
this->settings->ss->direction = direction;
int vsize = Vector_size(this->processes);
// Find all processes whose parent is not visible
int size;
@ -228,14 +229,9 @@ void ProcessList_sort(ProcessList* this) {
ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
break;
}
pid_t ppid = Process_getParentPid(process);
pid_t ppid = process->tgid == process->pid ? process->ppid : process->tgid;
// Bisect the process vector to find parent
int l = 0, r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
// root.
if (process->pid == ppid)
r = 0;
while (l < r) {
int c = (l + r) / 2;
pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid;
@ -271,7 +267,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessField ProcessList_keyAt(ProcessList* this, int at) {
int x = 0;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title;

View File

@ -71,32 +71,32 @@ void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl);
extern ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
extern void ProcessList_done(ProcessList* this);
void ProcessList_done(ProcessList* this);
extern void ProcessList_setPanel(ProcessList* this, Panel* panel);
void ProcessList_setPanel(ProcessList* this, Panel* panel);
extern void ProcessList_printHeader(ProcessList* this, RichString* header);
void ProcessList_printHeader(ProcessList* this, RichString* header);
extern void ProcessList_add(ProcessList* this, Process* p);
void ProcessList_add(ProcessList* this, Process* p);
extern void ProcessList_remove(ProcessList* this, Process* p);
void ProcessList_remove(ProcessList* this, Process* p);
extern Process* ProcessList_get(ProcessList* this, int idx);
Process* ProcessList_get(ProcessList* this, int idx);
extern int ProcessList_size(ProcessList* this);
int ProcessList_size(ProcessList* this);
extern void ProcessList_sort(ProcessList* this);
void ProcessList_sort(ProcessList* this);
extern ProcessField ProcessList_keyAt(ProcessList* this, int at);
ProcessField ProcessList_keyAt(ProcessList* this, int at);
extern void ProcessList_expandTree(ProcessList* this);
void ProcessList_expandTree(ProcessList* this);
extern void ProcessList_rebuildPanel(ProcessList* this);
void ProcessList_rebuildPanel(ProcessList* this);
extern Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
extern void ProcessList_scan(ProcessList* this);
void ProcessList_scan(ProcessList* this);
#endif

50
README
View File

@ -1,28 +1,46 @@
![htop](https://htop.dev)
[![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)
[![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)
[htop](http://hisham.hm/htop/)
====
by Hisham Muhammad <hisham@gobolinux.org> (2004 - 2016)
Introduction
------------
`htop` is a cross-platform interactive process viewer.
It requires `ncurses`.
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!)
For more information and details on how to contribute to `htop`
visit [htop.dev](https://htop.dev).
This software has evolved considerably over the years,
and is reasonably complete, but there is always room for improvement.
Build instructions
------------------
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
------------------------
This program is distributed as a standard autotools-based package.
For detailed instructions see the [INSTALL](/INSTALL) file, which
is created after `./autogen.sh` is run.
See the [INSTALL](/INSTALL) file for detailed instructions.
When compiling from a [release tarball](https://github.com/htop-dev/htop/releases/), run:
When compiling from a [release tarball](https://hisham.hm/htop/releases/), run:
./configure && make
@ -36,6 +54,8 @@ 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)

View File

@ -63,10 +63,6 @@ typedef struct RichString_ {
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#define charBytes(n) (sizeof(CharType) * (n))
static void RichString_extendLen(RichString* this, int len) {
@ -107,7 +103,6 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
cchar_t* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) {
ch->attr = attrs;
ch++;
@ -137,7 +132,6 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
chtype* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) {
*ch = (*ch & 0xff) | attrs;
ch++;

View File

@ -59,10 +59,6 @@ typedef struct RichString_ {
} RichString;
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#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 +67,24 @@ typedef struct RichString_ {
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
extern int RichString_findChar(RichString* this, char c, int start);
int RichString_findChar(RichString* this, char c, int start);
#else
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
extern int RichString_findChar(RichString* this, char c, int start);
int RichString_findChar(RichString* this, char c, int start);
#endif
extern void RichString_prune(RichString* this);
void RichString_prune(RichString* this);
extern void RichString_setAttr(RichString* this, int attrs);
void RichString_setAttr(RichString* this, int attrs);
extern void RichString_append(RichString* this, int attrs, const char* data);
void RichString_append(RichString* this, int attrs, const char* data);
extern void RichString_appendn(RichString* this, int attrs, const char* data, int len);
void RichString_appendn(RichString* this, int attrs, const char* data, int len);
extern void RichString_write(RichString* this, int attrs, const char* data);
void RichString_write(RichString* this, int attrs, const char* data);
#endif

View File

@ -71,30 +71,46 @@ inline int ScreenManager_size(ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
ScreenManager_insert(this, item, size, Vector_size(this->panels));
}
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
if (this->orientation == HORIZONTAL) {
int lastX = 0;
if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
if (idx > 0) {
Panel* last = (Panel*) Vector_get(this->panels, idx - 1);
lastX = last->x + last->w + 1;
}
int height = LINES - this->y1 + this->y2;
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
if (size <= 0) {
size = COLS-this->x1+this->x2-lastX;
}
Panel_resize(item, size, height);
Panel_move(item, lastX, this->y1);
if (idx < this->panelCount) {
for (int i = idx + 1; i <= this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x + size, p->y);
}
}
}
// TODO: VERTICAL
Vector_add(this->panels, item);
Vector_insert(this->panels, idx, item);
item->needsRedraw = true;
this->panelCount++;
}
Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--;
if (idx < this->panelCount) {
for (int i = idx; i < this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x - w, p->y);
}
}
return panel;
}
@ -131,7 +147,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->treeView) {
if (*sortTimeout == 0 || this->settings->ss->treeView) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
@ -145,17 +161,20 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
const int nPanels = this->panelCount;
int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus);
if (i < nPanels) {
if (this->orientation == HORIZONTAL) {
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
}
}
}
}
static Panel* setCurrentPanel(Panel* panel) {
static Panel* setCurrentPanel(ScreenManager* this, int focus) {
Panel* panel = (Panel*) Vector_get(this->panels, focus);
FunctionBar_draw(panel->currentBar, NULL);
return panel;
}
@ -164,7 +183,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false;
int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
Panel* panelFocus = setCurrentPanel(this, focus);
double oldTime = 0.0;
@ -187,27 +206,10 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
}
int prevCh = ch;
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;
}
}
ch = Panel_getCh(panelFocus);
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE && this->settings->enableMouse) {
if (ch == KEY_MOUSE) {
ch = ERR;
MEVENT mevent;
int ok = getmouse(&mevent);
@ -226,7 +228,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(panel);
panelFocus = setCurrentPanel(this, i);
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@ -302,7 +304,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryLeft:
if (focus > 0)
focus--;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus > 0)
goto tryLeft;
break;
@ -317,7 +319,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryRight:
if (focus < this->panelCount - 1)
focus++;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
goto tryRight;
break;

View File

@ -35,18 +35,20 @@ typedef struct ScreenManager_ {
} ScreenManager;
extern ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner);
ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner);
extern void ScreenManager_delete(ScreenManager* this);
void ScreenManager_delete(ScreenManager* this);
extern int ScreenManager_size(ScreenManager* this);
extern void ScreenManager_add(ScreenManager* this, Panel* item, int size);
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
extern Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);
extern void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
Panel* ScreenManager_remove(ScreenManager* this, int idx);
extern void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
#endif

330
ScreensPanel.c Normal file
View File

@ -0,0 +1,330 @@
/*
htop - ScreensPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ScreensPanel.h"
#include "Platform.h"
#include "StringUtils.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
/*{
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
}*/
ObjectClass ScreenListItem_class = {
.extends = Class(ListItem),
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) {
ScreenListItem* this = AllocThis(ScreenListItem);
ListItem_init((ListItem*)this, value, 0);
this->ss = ss;
return this;
}
static const char* const ScreensFunctions[] = {" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ScreensPanel_delete(Object* object) {
Panel* super = (Panel*) object;
ScreensPanel* this = (ScreensPanel*) object;
Panel_done(super);
free(this);
}
static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
//ListItem* item = (ListItem*)Panel_getSelected(super);
if (ch >= 32 && ch < 127 && ch != 61 && ch != 22) {
if (this->cursor < SCREEN_NAME_LEN - 1) {
this->buffer[this->cursor] = ch;
this->cursor++;
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
} else {
switch(ch) {
case 127:
case KEY_BACKSPACE:
{
if (this->cursor > 0) {
this->cursor--;
this->buffer[this->cursor] = '\0';
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
break;
}
case 0x0a:
case 0x0d:
case KEY_ENTER:
{
ListItem* item = (ListItem*) Panel_getSelected(super);
free(this->saved);
item->value = xStrdup(this->buffer);
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
ScreensPanel_update(super);
break;
}
case 27: // Esc
{
ListItem* item = (ListItem*) Panel_getSelected(super);
item->value = this->saved;
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
break;
}
}
}
return HANDLED;
}
static void startRenaming(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
ListItem* item = (ListItem*) Panel_getSelected(super);
this->renaming = true;
super->cursorOn = true;
char* name = item->value;
this->saved = name;
strncpy(this->buffer, name, SCREEN_NAME_LEN);
this->buffer[SCREEN_NAME_LEN] = '\0';
this->cursor = strlen(this->buffer);
item->value = this->buffer;
Panel_setSelectionColor(super, CRT_colors[PANEL_EDIT]);
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
static void rebuildSettingsArray(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
int n = Panel_size(super);
free(this->settings->screens);
this->settings->screens = xMalloc(sizeof(ScreenSettings*) * (n + 1));
this->settings->screens[n] = NULL;
for (int i = 0; i < n; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
this->settings->screens[i] = item->ss;
}
}
static void addNewScreen(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
char* name = "New";
ScreenSettings* ss = Settings_newScreen(this->settings, name, "PID Command");
ScreenListItem* item = ScreenListItem_new(name, ss);
int idx = Panel_getSelectedIndex(super);
Panel_insert(super, idx + 1, (Object*) item);
Panel_setSelected(super, idx + 1);
}
static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
int selected = Panel_getSelectedIndex(super);
ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super);
bool shouldRebuildArray = false;
HandlerResult result = IGNORED;
switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
result = HANDLED;
break;
}
case EVENT_SET_SELECTED:
result = HANDLED;
break;
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_HOME:
case KEY_END: {
Panel_onKey(super, ch);
break;
}
case KEY_F(2):
case KEY_CTRL('R'):
{
startRenaming(super);
result = HANDLED;
break;
}
case KEY_F(5):
case KEY_CTRL('N'):
{
addNewScreen(super);
startRenaming(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_UP:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(7):
case '[':
case '-':
{
Panel_moveSelectedUp(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_DOWN:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(8):
case ']':
case '+':
{
Panel_moveSelectedDown(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_F(9):
//case KEY_DC:
{
if (Panel_size(super) > 1) {
Panel_remove(super, selected);
}
shouldRebuildArray = true;
result = HANDLED;
break;
}
default:
{
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
break;
}
}
ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super);
if (oldFocus != newFocus) {
ColumnsPanel_fill(this->columns, newFocus->ss);
result = HANDLED;
}
if (shouldRebuildArray) {
rebuildSettingsArray(super);
}
if (result == HANDLED)
ScreensPanel_update(super);
return result;
}
static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
if (this->renaming) {
return ScreensPanel_eventHandlerRenaming(super, ch);
} else {
return ScreensPanel_eventHandlerNormal(super, ch);
}
}
PanelClass ScreensPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ScreensPanel_delete
},
.eventHandler = ScreensPanel_eventHandler
};
ScreensPanel* ScreensPanel_new(Settings* settings) {
ScreensPanel* this = AllocThis(ScreensPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ScreensFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->columns = ColumnsPanel_new(settings->screens[0], &(settings->changed));
this->moving = false;
this->renaming = false;
super->cursorOn = false;
this->cursor = 0;
Panel_setHeader(super, "Screens");
for (unsigned int i = 0; i < settings->nScreens; i++) {
ScreenSettings* ss = settings->screens[i];
char* name = ss->name;
Panel_add(super, (Object*) ScreenListItem_new(name, ss));
}
return this;
}
void ScreensPanel_update(Panel* super) {
ScreensPanel* this = (ScreensPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->screens = xRealloc(this->settings->screens, sizeof(char*) * (size+1));
for (int i = 0; i < size; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
ScreenSettings* ss = item->ss;
free(ss->name);
this->settings->screens[i] = ss;
ss->name = xStrdup(((ListItem*) item)->value);
}
this->settings->screens[size] = NULL;
}

51
ScreensPanel.h Normal file
View File

@ -0,0 +1,51 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ScreensPanel
#define HEADER_ScreensPanel
/*
htop - ScreensPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
extern ObjectClass ScreenListItem_class;
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss);
extern PanelClass ScreensPanel_class;
ScreensPanel* ScreensPanel_new(Settings* settings);
void ScreensPanel_update(Panel* super);
#endif

View File

@ -29,25 +29,32 @@ typedef struct {
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -60,8 +67,6 @@ typedef struct Settings_ {
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool enableMouse;
bool vimMode;
bool changed;
} Settings;
@ -72,25 +77,82 @@ typedef struct Settings_ {
}*/
static void writeList(FILE* fd, char** list, int len) {
const char* sep = "";
for (int i = 0; i < len; i++) {
fprintf(fd, "%s%s", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
/*
static char** readQuotedList(char* line) {
int n = 0;
char** list = xCalloc(sizeof(char*), 1);
int start = 0;
for (;;) {
while (line[start] && line[start] == ' ') {
start++;
}
if (line[start] != '"') {
break;
}
start++;
int close = start;
while (line[close] && line[close] != '"') {
close++;
}
int len = close - start;
char* item = xMalloc(len + 1);
strncpy(item, line + start, len);
item[len] = '\0';
list[n] = item;
n++;
list = xRealloc(list, sizeof(char*) * (n + 1));
start = close + 1;
}
list[n] = NULL;
return list;
}
static void writeQuotedList(FILE* fd, char** list) {
const char* sep = "";
for (int i = 0; list[i]; i++) {
fprintf(fd, "%s\"%s\"", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
*/
void Settings_delete(Settings* this) {
free(this->filename);
free(this->fields);
for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->columns[i].names);
free(this->columns[i].modes);
for (unsigned int i = 0; i < (sizeof(this->meterColumns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->meterColumns[i].names);
free(this->meterColumns[i].modes);
}
if (this->screens) {
for (unsigned int i = 0; this->screens[i]; i++) {
free(this->screens[i]->name);
free(this->screens[i]->fields);
}
free(this->screens);
}
free(this);
}
static void Settings_readMeters(Settings* this, char* line, int column) {
static void Settings_readMeters(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
this->columns[column].names = ids;
this->meterColumns[side].names = ids;
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
static void Settings_readMeterModes(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
@ -99,13 +161,13 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
for (int i = 0; ids[i]; i++) {
len++;
}
this->columns[column].len = len;
this->meterColumns[side].len = len;
int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]);
}
String_freeArray(ids);
this->columns[column].modes = modes;
this->meterColumns[side].modes = modes;
}
static void Settings_defaultMeters(Settings* this) {
@ -114,37 +176,59 @@ static void Settings_defaultMeters(Settings* this) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
this->meterColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->meterColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->meterColumns[i].len = sizes[i];
}
int r = 0;
if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs2");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs2");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else {
this->columns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("AllCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
}
this->columns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->meterColumns[0].names[1] = xStrdup("Memory");
this->meterColumns[0].modes[1] = BAR_METERMODE;
this->meterColumns[0].names[2] = xStrdup("Swap");
this->meterColumns[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");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Tasks");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("LoadAverage");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Uptime");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
}
static const char* toFieldName(int i) {
return Process_fields[i].name;
}
static int toFieldIndex(const char* str) {
if (isdigit(str[0])) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(str) + 1;
if (id < Platform_numberOfFields && toFieldName(id)) {
return id;
}
} else {
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
const char* pName = toFieldName(p);
if (pName && strcmp(pName, str) == 0) {
return p;
}
}
}
return -1;
}
static void readFields(ProcessField* fields, int* flags, const char* line) {
@ -155,11 +239,10 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
fields[j] = id;
*flags |= Process_fields[id].flags;
int idx = toFieldIndex(ids[i]);
if (idx != -1) {
fields[j] = idx;
*flags |= Process_fields[idx].flags;
j++;
}
}
@ -167,6 +250,32 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
String_freeArray(ids);
}
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line) {
ScreenSettings* ss = xCalloc(sizeof(ScreenSettings), 1);
ss->name = xStrdup(name);
ss->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
ss->flags = 0;
ss->direction = 1;
ss->treeView = 0;
readFields(ss->fields, &(ss->flags), line);
this->screens[this->nScreens] = ss;
this->nScreens++;
this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
this->screens[this->nScreens] = NULL;
return ss;
}
static void Settings_defaultScreens(Settings* this) {
Settings_newScreen(this, "Default", "PID USER PRIORITY NICE M_SIZE M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command");
this->screens[0]->sortKey = toFieldIndex("PERCENT_CPU");
Settings_newScreen(this, "I/O", "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command");
this->screens[1]->sortKey = toFieldIndex("IO_RATE");
Settings_newScreen(this, "Perf Counters", "PID USER PERCENT_CPU PROCESSOR MCYCLE MINSTR IPC PERCENT_MISS PERCENT_BMISS Command");
this->screens[2]->sortKey = toFieldIndex("MCYCLE");
Settings_newScreen(this, "L1 Data Cache", "PID USER PERCENT_CPU L1DREADS L1DRMISSES L1DWRITES L1DWMISSES Command");
this->screens[3]->sortKey = toFieldIndex("L1DREADS");
}
static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd;
@ -176,8 +285,10 @@ static bool Settings_read(Settings* this, const char* fileName) {
if (!fd)
return false;
bool didReadMeters = false;
bool didReadFields = false;
bool readMeters = false;
ProcessField* legacyFields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
int legacyFlags;
bool legacyFieldsRead = false;
for (;;) {
char* line = String_readLine(fd);
if (!line) {
@ -191,15 +302,8 @@ static bool Settings_read(Settings* this, const char* fileName) {
continue;
}
if (String_eq(option[0], "fields")) {
readFields(this->fields, &(this->flags), option[1]);
didReadFields = true;
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
readFields(legacyFields, &legacyFlags, option[1]);
legacyFieldsRead = true;
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
@ -227,10 +331,6 @@ 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")) {
@ -240,56 +340,72 @@ 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;
readMeters = true;
} else if (String_eq(option[0], "right_meters")) {
Settings_readMeters(this, option[1], 1);
didReadMeters = true;
readMeters = true;
} else if (String_eq(option[0], "left_meter_modes")) {
Settings_readMeterModes(this, option[1], 0);
didReadMeters = true;
readMeters = true;
} 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]);
readMeters = true;
} else if (strncmp(option[0], "screen:", 7) == 0) {
Settings_newScreen(this, option[0] + 7, option[1]);
} else if (String_eq(option[0], ".tree_view")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->treeView = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_direction")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->direction = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_key")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->sortKey = toFieldIndex(option[1]);
}
}
String_freeArray(option);
}
fclose(fd);
if (!didReadMeters) {
if (this->nScreens == 0) {
Settings_defaultScreens(this);
if (legacyFieldsRead) {
free(this->screens[0]->fields);
this->screens[0]->fields = legacyFields;
this->screens[0]->flags = legacyFlags;
}
}
if (!readMeters) {
Settings_defaultMeters(this);
}
return didReadFields;
return true;
}
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
static void writeFields(FILE* fd, ProcessField* fields, bool byName) {
const char* sep = "";
for (int i = 0; fields[i]; i++) {
if (byName) {
fprintf(fd, "%s%s", sep, toFieldName(fields[i]));
} else {
// This " - 1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
}
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeters(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]);
sep = " ";
}
fprintf(fd, "\n");
static void writeMeters(Settings* this, FILE* fd, int side) {
writeList(fd, this->meterColumns[side].names, this->meterColumns[side].len);
}
static void writeMeterModes(Settings* this, FILE* fd, int column) {
static void writeMeterModes(Settings* this, FILE* fd, int side) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]);
for (int i = 0; i < this->meterColumns[side].len; i++) {
fprintf(fd, "%s%d", sep, this->meterColumns[side].modes[i]);
sep = " ";
}
fprintf(fd, "\n");
@ -307,10 +423,7 @@ bool Settings_write(Settings* this) {
}
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields");
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, false);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
@ -320,22 +433,34 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
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);
// Legacy compatibility with older versions of htop
fprintf(fd, "tree_view=%d\n", (int) this->screens[0]->treeView);
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->screens[0]->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->screens[0]->direction);
if (this->screens && this->screens[0]) {
for (unsigned int i = 0; i < this->nScreens; i++) {
ScreenSettings* ss = this->screens[i];
fprintf(fd, "screen:%s=", ss->name);
writeFields(fd, ss->fields, true);
fprintf(fd, ".tree_view=%d\n", (int) ss->treeView);
fprintf(fd, ".sort_key=%s\n", toFieldName(ss->sortKey));
fprintf(fd, ".sort_direction=%d\n", (int) ss->direction);
}
}
fclose(fd);
return true;
}
@ -344,34 +469,22 @@ Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
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).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
this->screens = xCalloc(sizeof(ScreenSettings*), 1);
this->nScreens = 0;
char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC");
@ -400,48 +513,49 @@ Settings* Settings_new(int cpuCount) {
free(htopDir);
free(configDir);
struct stat st;
int err = lstat(legacyDotfile, &st);
if (err || S_ISLNK(st.st_mode)) {
if (lstat(legacyDotfile, &st) != 0) {
st.st_mode = 0;
}
if (access(legacyDotfile, R_OK) != 0 || S_ISLNK(st.st_mode)) {
free(legacyDotfile);
legacyDotfile = NULL;
}
CRT_restorePrivileges();
}
this->colorScheme = 0;
this->enableMouse = true;
this->changed = false;
this->delay = DEFAULT_DELAY;
bool ok = false;
if (legacyDotfile) {
ok = Settings_read(this, legacyDotfile);
bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->filename);
if (ok) {
if (legacyDotfile) {
// Transition to new location and delete old configuration file
if (Settings_write(this))
unlink(legacyDotfile);
}
free(legacyDotfile);
}
if (!ok) {
ok = Settings_read(this, this->filename);
}
if (!ok) {
} else {
this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings);
free(systemSettings);
}
if (!ok) {
Settings_defaultMeters(this);
Settings_defaultScreens(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
}
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
free(legacyDotfile);
return this;
}
void Settings_invertSortOrder(Settings* this) {
void ScreenSettings_invertSortOrder(ScreenSettings* this) {
if (this->direction == 1)
this->direction = -1;
else

View File

@ -9,7 +9,6 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define DEFAULT_DELAY 15
#include "Process.h"
#include <stdbool.h>
@ -20,25 +19,32 @@ typedef struct {
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -51,8 +57,6 @@ typedef struct Settings_ {
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool enableMouse;
bool vimMode;
bool changed;
} Settings;
@ -62,12 +66,18 @@ typedef struct Settings_ {
#endif
extern void Settings_delete(Settings* this);
/*
extern bool Settings_write(Settings* this);
*/
extern Settings* Settings_new(int cpuCount);
void Settings_delete(Settings* this);
extern void Settings_invertSortOrder(Settings* this);
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line);
bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount);
void ScreenSettings_invertSortOrder(ScreenSettings* this);
#endif

View File

@ -41,10 +41,10 @@ Panel* SignalsPanel_new() {
}
#if (defined(SIGRTMIN) && defined(SIGRTMAX))
if (SIGRTMAX - SIGRTMIN <= 100) {
static char buf[16];
static char buf[15];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
xSnprintf(buf, 15, "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}

View File

@ -16,6 +16,6 @@ typedef struct SignalItem_ {
} SignalItem;
extern Panel* SignalsPanel_new();
Panel* SignalsPanel_new();
#endif

View File

@ -30,9 +30,8 @@ char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1);
memcpy(out, s1, l1);
memcpy(out+l1, s2, l2+1);
out[l1 + l2] = '\0';
strncpy(out, s1, l1);
strncpy(out+l1, s2, l2+1);
return out;
}

View File

@ -19,18 +19,18 @@ in the source distribution for its full text.
* at compile time (e.g. when they are immutable string literals). :)
*/
extern char* String_cat(const char* s1, const char* s2);
char* String_cat(const char* s1, const char* s2);
extern char* String_trim(const char* in);
char* String_trim(const char* in);
extern int String_eq(const char* s1, const char* s2);
extern char** String_split(const char* s, char sep, int* n);
char** String_split(const char* s, char sep, int* n);
extern void String_freeArray(char** s);
void String_freeArray(char** s);
extern char* String_getToken(const char* line, const unsigned short int numMatch);
char* String_getToken(const char* line, const unsigned short int numMatch);
extern char* String_readLine(FILE* fd);
char* String_readLine(FILE* fd);
#endif

View File

@ -15,7 +15,7 @@ in the source distribution for its full text.
}*/
int TasksMeter_attributes[] = {
CPU_SYSTEM, PROCESS_THREAD, PROCESS, TASKS_RUNNING
CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING
};
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {

View File

@ -86,7 +86,7 @@ void TraceScreen_draw(InfoScreen* this) {
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
bool TraceScreen_forkTracer(TraceScreen* this) {
@ -101,15 +101,14 @@ 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", "-T", "-tt", "-s", "512", "-p", buffer, NULL);
execlp("strace", "strace", "-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));
(void) written;
exit(1);
}
int ok = fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK);
if (ok == -1) return false;
fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK);
this->strace = fdopen(this->fdpair[0], "r");
this->fd_strace = fileno(this->strace);
return true;

View File

@ -25,16 +25,16 @@ typedef struct TraceScreen_ {
extern InfoScreenClass TraceScreen_class;
extern TraceScreen* TraceScreen_new(Process* process);
TraceScreen* TraceScreen_new(Process* process);
extern void TraceScreen_delete(Object* cast);
void TraceScreen_delete(Object* cast);
extern void TraceScreen_draw(InfoScreen* this);
void TraceScreen_draw(InfoScreen* this);
extern bool TraceScreen_forkTracer(TraceScreen* this);
bool TraceScreen_forkTracer(TraceScreen* this);
extern void TraceScreen_updateTrace(InfoScreen* super);
void TraceScreen_updateTrace(InfoScreen* super);
extern bool TraceScreen_onKey(InfoScreen* super, int ch);
bool TraceScreen_onKey(InfoScreen* super, int ch);
#endif

View File

@ -15,11 +15,11 @@ typedef struct UsersTable_ {
Hashtable* users;
} UsersTable;
extern UsersTable* UsersTable_new();
UsersTable* UsersTable_new();
extern void UsersTable_delete(UsersTable* this);
void UsersTable_delete(UsersTable* this);
extern char* UsersTable_getRef(UsersTable* this, unsigned int uid);
char* UsersTable_getRef(UsersTable* this, unsigned int uid);
extern void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData);

View File

@ -284,19 +284,11 @@ inline Object* Vector_get(Vector* this, int idx) {
#endif
#ifdef DEBUG
inline int Vector_size(Vector* this) {
assert(Vector_isConsistent(this));
return this->items;
}
#else
#define Vector_size(v_) ((v_)->items)
#endif
/*
static void Vector_merge(Vector* this, Vector* v2) {

View File

@ -27,38 +27,38 @@ typedef struct Vector_ {
} Vector;
extern Vector* Vector_new(ObjectClass* type, bool owner, int size);
Vector* Vector_new(ObjectClass* type, bool owner, int size);
extern void Vector_delete(Vector* this);
void Vector_delete(Vector* this);
#ifdef DEBUG
extern int Vector_count(Vector* this);
int Vector_count(Vector* this);
#endif
extern void Vector_prune(Vector* this);
void Vector_prune(Vector* this);
// If I were to use only one sorting algorithm for both cases, it would probably be this one:
/*
*/
extern void Vector_quickSort(Vector* this);
void Vector_quickSort(Vector* this);
extern void Vector_insertionSort(Vector* this);
void Vector_insertionSort(Vector* this);
extern void Vector_insert(Vector* this, int idx, void* data_);
void Vector_insert(Vector* this, int idx, void* data_);
extern Object* Vector_take(Vector* this, int idx);
Object* Vector_take(Vector* this, int idx);
extern Object* Vector_remove(Vector* this, int idx);
Object* Vector_remove(Vector* this, int idx);
extern void Vector_moveUp(Vector* this, int idx);
void Vector_moveUp(Vector* this, int idx);
extern void Vector_moveDown(Vector* this, int idx);
void Vector_moveDown(Vector* this, int idx);
extern void Vector_set(Vector* this, int idx, void* data_);
void Vector_set(Vector* this, int idx, void* data_);
#ifdef DEBUG
@ -70,21 +70,13 @@ extern Object* Vector_get(Vector* this, int idx);
#endif
#ifdef DEBUG
extern int Vector_size(Vector* this);
#else
#define Vector_size(v_) ((v_)->items)
#endif
/*
*/
extern void Vector_add(Vector* this, void* data_);
void Vector_add(Vector* this, void* data_);
extern int Vector_indexOf(Vector* this, void* search_, Object_Compare compare);

View File

@ -11,11 +11,11 @@
#include <assert.h>
#include <stdlib.h>
extern void* xMalloc(size_t size);
void* xMalloc(size_t size);
extern void* xCalloc(size_t nmemb, size_t size);
void* xCalloc(size_t nmemb, size_t size);
extern void* xRealloc(void* ptr, size_t size);
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 @@ extern void* xRealloc(void* ptr, size_t size);
#endif
#if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
extern char* xStrdup_(const char* str) __attribute__((nonnull));
char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later
extern char* xStrdup_(const char* str);
char* xStrdup_(const char* str);
#endif

View File

@ -2,7 +2,10 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
AC_INIT([htop],[3.0.0],[htop@groups.io])
AC_INIT([htop],[2.0.2],[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_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.])
@ -40,9 +43,6 @@ dragonfly*)
darwin*)
my_htop_platform=darwin
;;
solaris*)
my_htop_platform=solaris
;;
*)
my_htop_platform=unsupported
;;
@ -61,16 +61,6 @@ AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/param.h sys/time.h unistd.h],[
])
AC_CHECK_HEADERS([execinfo.h],[:],[:])
AC_HEADER_MAJOR
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" != 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])
# Checks for typedefs, structures, and compiler characteristics.
# ----------------------------------------------------------------------
AC_HEADER_STDBOOL
@ -164,26 +154,20 @@ m4_define([HTOP_CHECK_SCRIPT],
[
if test ! -z "m4_toupper($HTOP_[$1]_CONFIG_SCRIPT)"; then
# to be used to set the path to ncurses*-config when cross-compiling
htop_config_script_libs=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null)
htop_config_script_cflags=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --cflags 2> /dev/null)
htop_config_script=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null)
else
htop_config_script_libs=$([$4] --libs 2> /dev/null)
htop_config_script_cflags=$([$4] --cflags 2> /dev/null)
htop_config_script=$([$4] --libs 2> /dev/null)
fi
htop_script_success=no
htop_save_LDFLAGS="$LDFLAGS"
htop_save_CFLAGS="$CFLAGS"
if test ! "x$htop_config_script_libs" = x; then
LDFLAGS="$htop_config_script_libs $LDFLAGS"
CFLAGS="$htop_config_script_cflags $CFLAGS"
if test ! "x$htop_config_script" = x; then
LDFLAGS="$htop_config_script $LDFLAGS"
AC_CHECK_LIB([$1], [$2], [
AC_DEFINE([$3], 1, [The library is present.])
LIBS="$htop_config_script_libs $LIBS "
LIBS="$htop_config_script $LIBS "
htop_script_success=yes
], [
CFLAGS="$htop_save_CFLAGS"
])
LDFLAGS="$htop_save_LDFLAGS"
], [])
LDFLAGS="$save_LDFLAGS"
fi
if test "x$htop_script_success" = xno; then
[$5]
@ -238,12 +222,6 @@ if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi
if test "$my_htop_platform" = "solaris"; then
AC_CHECK_LIB([kstat], [kstat_open], [], [missing_libraries="$missing_libraries libkstat"])
AC_CHECK_LIB([proc], [Pgrab_error], [], [missing_libraries="$missing_libraries libproc"])
AC_CHECK_LIB([malloc], [free], [], [missing_libraries="$missing_libraries libmalloc"])
fi
AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
AC_MSG_CHECKING([for usable sched_setaffinity])
@ -278,21 +256,60 @@ then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes")
if test "x$enable_perfcounters" = "xyes" -a "$my_htop_platform" = "linux"
then
AC_DEFINE(HAVE_PERFCOUNTERS, 1, [Define if hardware performance counter support should be enabled.])
AC_CHECK_HEADERS([linux/perf_counter.h], [have_perf_counter=yes],
[have_perf_counter=no])
AC_CHECK_HEADERS([linux/perf_event.h], [have_perf_event=yes],
[have_perf_event=no])
if test "x${have_perf_counter}" = xno -a "x${have_perf_event}" = xno; then
os=`uname -s -r`
AC_MSG_FAILURE([
------------------------------------------------------------
Could not locate linux/perf_count.h or linux/perf_event.h.
Are performance counters supported on this machine?
Linux 2.6.31+ is required.
uname reports: ${os}
------------------------------------------------------------])
fi
# Check for hardware architecture
no_target=yes
AC_MSG_CHECKING([hardware])
hw=`uname -m`
case $hw in
x86_64 | i386 | i686 ) :
AC_MSG_RESULT([x86])
AC_DEFINE([TARGET_X86], [1], [Define to 1 if the target is x86.])
no_target=no
;;
unknown ) :
AC_MSG_RESULT([unknown])
AC_MSG_WARN([Could not detect architecture])
;;
* ) :
AC_MSG_RESULT([$hw])
;;
esac
if test x$no_target = xyes; then
AC_DEFINE([NOTARGET], [1], [Define to 1 when no specific target is supported.])
fi
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes
then
m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"])
CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS"
CFLAGS+=" $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
LIBS+=" $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
], [
pkg_m4_absent=1
m4_warning([configure is generated without pkg.m4. 'make dist' target will be disabled.])
AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.])
])
fi
@ -305,7 +322,7 @@ if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers])
fi
AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2018 Hisham Muhammad", [Copyright message.])
AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright message.])
# We're done, let's go!
# ----------------------------------------------------------------------
@ -314,7 +331,6 @@ AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin])
AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1])

View File

@ -9,8 +9,6 @@ in the source distribution for its full text.
#include "DarwinProcess.h"
#include "DarwinProcessList.h"
#include "CRT.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/openzfs_sysctl.h"
#include <stdlib.h>
#include <string.h>
@ -20,43 +18,9 @@ in the source distribution for its full text.
#include <sys/mman.h>
#include <utmpx.h>
#include <err.h>
#include <sys/sysctl.h>
#include <stdbool.h>
struct kern {
short int version[3];
};
void GetKernelVersion(struct kern *k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
version_[0] = version_[1] = version_[2] = -1;
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
}
memcpy(k->version, version_, sizeof(version_));
}
/* compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
int CompareKernelVersion(short int major, short int minor, short int component) {
struct kern k;
GetKernelVersion(&k);
if ( k.version[0] != major) return k.version[0] - major;
if ( k.version[1] != minor) return k.version[1] - minor;
if ( k.version[2] != component) return k.version[2] - component;
return 0;
}
/*{
#include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
#include <mach/mach_host.h>
#include <sys/sysctl.h>
@ -70,8 +34,6 @@ typedef struct DarwinProcessList_ {
uint64_t kernel_threads;
uint64_t user_threads;
uint64_t global_diff;
ZfsArcStats zfs;
} DarwinProcessList;
}*/
@ -89,8 +51,9 @@ void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
if(0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n");
}
*p = NULL;
}
*p = NULL;
}
unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
@ -136,8 +99,8 @@ struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
return processes;
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
size_t len;
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId);
@ -150,10 +113,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
/* Initialize the VM statistics */
ProcessList_getVMStats(&this->vm_stats);
/* Initialize the ZFS kstats, if zfs.kext loaded */
openzfs_sysctl_init(&this->zfs);
openzfs_sysctl_updateArcStats(&this->zfs);
this->super.kernelThreads = 0;
this->super.userlandThreads = 0;
this->super.totalTasks = 0;
@ -182,7 +141,6 @@ void ProcessList_goThroughEntries(ProcessList* super) {
dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats);
openzfs_sysctl_updateArcStats(&dpl->zfs);
/* Get the time difference */
dpl->global_diff = 0;
@ -213,12 +171,7 @@ void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
// Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);
if (isScanThreadSupported){
DarwinProcess_scanThreads(proc);
}
super->totalTasks += 1;

View File

@ -9,19 +9,7 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
struct kern;
void GetKernelVersion(struct kern *k);
/* compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
int CompareKernelVersion(short int major, short int minor, short int component);
#include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
#include <mach/mach_host.h>
#include <sys/sysctl.h>
@ -35,8 +23,6 @@ typedef struct DarwinProcessList_ {
uint64_t kernel_threads;
uint64_t user_threads;
uint64_t global_diff;
ZfsArcStats zfs;
} DarwinProcessList;

View File

@ -15,8 +15,6 @@ in the source distribution for its full text.
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "DarwinProcessList.h"
#include <stdlib.h>
@ -119,8 +117,6 @@ MeterClass* Platform_meterTypes[] = {
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
&BlankMeter_class,
NULL
};
@ -221,8 +217,6 @@ 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);
}
@ -247,18 +241,6 @@ void Platform_setSwapValues(Meter* mtr) {
mtr->values[0] = swapused.xsu_used / 1024;
}
void Platform_setZfsArcValues(Meter* this) {
DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(dpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(dpl->zfs));
}
char* Platform_getProcessEnv(pid_t pid) {
char* env = NULL;

View File

@ -48,10 +48,6 @@ void Platform_setMemoryValues(Meter* mtr);
void Platform_setSwapValues(Meter* mtr);
void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
#endif

View File

@ -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);
xSnprintf(buffer, n, "%-11s ", fp->jname); break;
if (buffer[11] != '\0') {
buffer[11] = ' ';
buffer[12] = '\0';

View File

@ -179,9 +179,6 @@ 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;
}

View File

@ -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);
xSnprintf(buffer, n, "%-11s ", fp->jname); break;
if (buffer[11] != '\0') {
buffer[11] = ' ';
buffer[12] = '\0';

View File

@ -8,8 +8,6 @@ in the source distribution for its full text.
#include "ProcessList.h"
#include "FreeBSDProcessList.h"
#include "FreeBSDProcess.h"
#include "zfs/ZfsArcStats.h"
#include "zfs/openzfs_sysctl.h"
#include <unistd.h>
#include <stdlib.h>
@ -20,12 +18,9 @@ in the source distribution for its full text.
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <time.h>
/*{
#include "zfs/ZfsArcStats.h"
#include <kvm.h>
#include <sys/param.h>
#include <sys/jail.h>
@ -50,12 +45,14 @@ typedef struct FreeBSDProcessList_ {
ProcessList super;
kvm_t* kd;
int zfsArcEnabled;
unsigned long long int memWire;
unsigned long long int memActive;
unsigned long long int memInactive;
unsigned long long int memFree;
unsigned long long int memZfsArc;
ZfsArcStats zfs;
CPUData* cpus;
@ -83,6 +80,8 @@ static int MIB_vm_stats_vm_v_free_count[4];
static int MIB_vfs_bufspace[2];
static int MIB_kstat_zfs_misc_arcstats_size[5];
static int MIB_kern_cp_time[2];
static int MIB_kern_cp_times[2];
static int kernelFScale;
@ -119,8 +118,15 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len);
openzfs_sysctl_init(&fpl->zfs);
openzfs_sysctl_updateArcStats(&fpl->zfs);
len = sizeof(fpl->memZfsArc);
if (sysctlbyname("kstat.zfs.misc.arcstats.size", &fpl->memZfsArc, &len,
NULL, 0) == 0 && fpl->memZfsArc != 0) {
sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len);
fpl->zfsArcEnabled = 1;
} else {
fpl->zfsArcEnabled = 0;
}
int smp = 0;
len = sizeof(smp);
@ -290,57 +296,37 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) {
//
// htop_used = active + (wired - arc)
// htop_cache = buffers + cache + arc
u_long totalMem;
u_int memActive, memWire, cachedMem;
long buffersMem;
uint64_t memZfsArc;
size_t len;
size_t len = sizeof(pl->totalMem);
//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;
len = sizeof(totalMem);
sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0);
totalMem /= 1024;
pl->totalMem = totalMem;
sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0);
pl->totalMem /= 1024;
len = sizeof(memActive);
sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0);
memActive *= pageSizeKb;
fpl->memActive = memActive;
sysctl(MIB_vm_stats_vm_v_active_count, 4, &(fpl->memActive), &len, NULL, 0);
fpl->memActive *= pageSizeKb;
len = sizeof(memWire);
sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0);
memWire *= pageSizeKb;
fpl->memWire = memWire;
sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(fpl->memWire), &len, NULL, 0);
fpl->memWire *= pageSizeKb;
len = sizeof(buffersMem);
sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0);
buffersMem /= 1024;
pl->buffersMem = buffersMem;
sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0);
pl->buffersMem /= 1024;
len = sizeof(cachedMem);
sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0);
cachedMem *= pageSizeKb;
pl->cachedMem = cachedMem;
sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0);
pl->cachedMem *= pageSizeKb;
if (fpl->zfsArcEnabled) {
len = sizeof(memZfsArc);
sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(memZfsArc), &len , NULL, 0);
memZfsArc /= 1024;
fpl->memZfsArc = memZfsArc;
len = sizeof(fpl->memZfsArc);
sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(fpl->memZfsArc), &len , NULL, 0);
fpl->memZfsArc /= 1024;
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;
pl->cachedMem += fpl->zfs.size;
}
pl->usedMem = fpl->memActive + fpl->memWire;
//currently unused, same as with arc, custom meter perhaps
@ -436,7 +422,6 @@ void ProcessList_goThroughEntries(ProcessList* this) {
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
openzfs_sysctl_updateArcStats(&fpl->zfs);
FreeBSDProcessList_scanMemoryInfo(this);
FreeBSDProcessList_scanCPUTime(this);
@ -444,14 +429,10 @@ 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;
@ -542,9 +523,6 @@ 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++;

View File

@ -10,8 +10,6 @@ in the source distribution for its full text.
*/
#include "zfs/ZfsArcStats.h"
#include <kvm.h>
#include <sys/param.h>
#include <sys/jail.h>
@ -36,12 +34,14 @@ typedef struct FreeBSDProcessList_ {
ProcessList super;
kvm_t* kd;
int zfsArcEnabled;
unsigned long long int memWire;
unsigned long long int memActive;
unsigned long long int memInactive;
unsigned long long int memFree;
unsigned long long int memZfsArc;
ZfsArcStats zfs;
CPUData* cpus;

View File

@ -15,8 +15,6 @@ in the source distribution for its full text.
#include "UptimeMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "FreeBSDProcess.h"
#include "FreeBSDProcessList.h"
@ -106,8 +104,6 @@ MeterClass* Platform_meterTypes[] = {
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
NULL
};
@ -182,9 +178,6 @@ 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;
}
@ -204,18 +197,6 @@ void Platform_setSwapValues(Meter* this) {
this->values[0] = pl->usedSwap;
}
void Platform_setZfsArcValues(Meter* this) {
FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(fpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(fpl->zfs));
}
void Platform_setTasksValues(Meter* this) {
// TODO
}

View File

@ -44,10 +44,6 @@ void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
void Platform_setTasksValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);

105
htop.1.in
View File

@ -1,22 +1,17 @@
.TH "HTOP" "1" "2020" "@PACKAGE_STRING@" "User Commands"
.TH "HTOP" "1" "2015" "@PACKAGE_STRING@" "Utils"
.SH "NAME"
htop \- interactive process viewer
.SH "SYNOPSIS"
.LP
.B htop
.RB [ \-dChpustv ]
.B htop [\fI\-dChusv\fR]
.SH "DESCRIPTION"
.LP
.B htop
is a cross-platform ncurses-based process viewer.
Htop is a free (GPL) ncurses-based process viewer for Linux.
.LP
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.
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.
.LP
Tasks related to processes (killing, renicing) can be done without
entering their PIDs.
@ -27,14 +22,10 @@ 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. 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.
Delay between updates, in tenths of seconds
.TP
\fB\-C \-\-no-color \-\-no-colour\fR
Start
.B htop
in monochrome mode
Start htop in monochrome mode
.TP
\fB\-h \-\-help
Display a help message and exit
@ -50,21 +41,19 @@ Show only the processes of a given user
.TP
\fB\-v \-\-version
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
.BR htop :
The following commands are supported while in htop:
.LP
.TP 5
.B Up, Alt-k
Select (highlight) the previous process in the process list. Scroll the list
Select (hightlight) the previous process in the process list. Scroll the list
if necessary.
.TP
.B Down, Alt-j
Select (highlight) the next process in the process list. Scroll the list if
Select (hightlight) the next process in the process list. Scroll the list if
necessary.
.TP
.B Left, Alt-h
@ -93,13 +82,8 @@ Tag or untag a process. Commands that can operate on multiple processes,
like "kill", will then apply over the list of tagged processes, instead
of the currently highlighted one.
.TP
.B c
Tag the current process and its children. Commands that can operate on multiple
processes, like "kill", will then apply over the list of tagged processes,
instead of the currently highlighted one.
.TP
.B U
Untag all processes (remove all tags added with the Space or c keys).
Untag all processes (remove all tags added with the Space key).
.TP
.B s
Trace process system calls: if strace(1) is installed, pressing this key
@ -202,17 +186,14 @@ 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
.BR htop .
The names below are the ones used in the
currently unimplemented in htop. The names below are the ones used in the
"Available Columns" section of the setup screen. If a different name is
shown in
.BR htop 's
main screen, it is shown below in parenthesis.
shown in htop's main screen, it is shown below in parenthesis.
.LP
.TP 5
.B Command
@ -400,44 +381,36 @@ 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
.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.
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.
.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
.B htop
are displayed in a human-readable form.
Sizes are printed in powers of 1024. (e.g., 1023M = 1072693248 Bytes)
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)
.LP
The decision to use this convention was made in order to conserve screen
space and make memory size representations consistent throughout
.BR htop .
The decision to use this convention was made in order to conserve screen space
and make memory size representations consistent throughout htop.
.SH "SEE ALSO"
.BR proc (5),
.BR top (1),
.BR free (1),
.BR ps (1),
.BR uptime (1)
and
.BR limits.conf (5).
proc(5), top(1), free(1), ps(1), uptime(1), limits.conf(5)
.SH "AUTHORS"
.LP
.B htop
was originally developed by Hisham Muhammad.
Nowadays it is maintained by the community at <htop@groups.io>.
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.

46
htop.c
View File

@ -39,12 +39,10 @@ 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 for a given user (or $USER)\n"
"-u --user=USERNAME Show only processes of a given user\n"
"-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
"-v --version Print version info\n"
"\n"
@ -63,8 +61,6 @@ typedef struct CommandLineSettings_ {
int sortKey;
int delay;
bool useColors;
bool enableMouse;
bool treeView;
} CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) {
@ -75,8 +71,6 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.sortKey = 0,
.delay = -1,
.useColors = true,
.enableMouse = true,
.treeView = false,
};
static struct option long_opts[] =
@ -85,18 +79,17 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"version", no_argument, 0, 'v'},
{"delay", required_argument, 0, 'd'},
{"sort-key", required_argument, 0, 's'},
{"user", optional_argument, 0, 'u'},
{"user", required_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'},
{"io", no_argument, 0, 'i'},
{0,0,0,0}
};
int opt, opti=0;
/* Parse arguments */
while ((opt = getopt_long(argc, argv, "hvmCs:td:u::p:", long_opts, &opti))) {
while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:i", long_opts, &opti))) {
if (opt == EOF) break;
switch (opt) {
case 'h':
@ -116,7 +109,6 @@ 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':
@ -125,34 +117,16 @@ 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;
case 'p': {
char* argCopy = xStrdup(optarg);
char* saveptr;
@ -223,22 +197,18 @@ int main(int argc, char** argv) {
settings->delay = flags.delay;
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);
MainPanel_updateTreeFunctions(panel, settings->screens[0]->treeView);
if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey;
settings->treeView = false;
settings->direction = 1;
settings->screens[0]->sortKey = flags.sortKey;
settings->screens[0]->treeView = false;
settings->screens[0]->direction = 1;
}
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));

View File

@ -1,39 +1,9 @@
[Desktop Entry]
Type=Application
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
GenericName[fi]=Prosessikatselin
GenericName[fr]=Visualiseur de processus
GenericName[gl]=Visor de procesos
GenericName[it]=Visore dei processi
GenericName[ko]=프로세스 뷰어
GenericName[nb]=Prosessviser
GenericName[nl]=Viewer van processen
GenericName[nn]=Prosessvisar
GenericName[pl]=Przeglądarka procesów
GenericName[pt]=Visualizador de Processos
GenericName[pt_BR]=Visualizador de processos
GenericName[ru]=Монитор процессов
GenericName[sk]=Prehliadač procesov
GenericName[sl]=Pregledovalnik opravil
GenericName[sr@ijekavian]=Приказивач процеса
GenericName[sr@ijekavianlatin]=Prikazivač procesa
GenericName[sr@latin]=Prikazivač procesa
GenericName[sr]=Приказивач процеса
GenericName[sv]=Processvisning
GenericName[tr]=Süreç Görüntüleyici
GenericName[uk]=Перегляд процесів
GenericName[zh_CN]=进程查看器
GenericName[zh_TW]=行程檢視器
Type=Application
Comment=Show System Processes
Comment[ca]=Visualitzeu els processos del sistema
Comment[da]=Vis systemprocesser
Comment[ca]=Mostra els processos del sistema
Comment[de]=Systemprozesse anzeigen
Comment[en_GB]=Show System Processes
Comment[es]=Mostrar procesos del sistema
@ -51,17 +21,45 @@ Comment[pt_BR]=Mostra os processos do sistema
Comment[ru]=Просмотр списка процессов в системе
Comment[sk]=Zobraziť systémové procesy
Comment[sl]=Prikaz sistemskih opravil
Comment[sr]=Приказ системских процеса
Comment[sr@ijekavian]=Приказ системских процеса
Comment[sr@ijekavianlatin]=Prikaz sistemskih procesa
Comment[sr@latin]=Prikaz sistemskih procesa
Comment[sr]=Приказ системских процеса
Comment[sv]=Visa systemprocesser
Comment[tr]=Sistem Süreçlerini Göster
Comment[uk]=Перегляд системних процесів
Comment[zh_CN]=显示系统进程
Comment[zh_TW]=顯示系統行程
Icon=htop
Exec=htop
Terminal=true
Categories=System;Monitor;ConsoleOnly;
Exec=htop
Icon=htop
Categories=ConsoleOnly;System;
GenericName=Process Viewer
GenericName[ca]=Visor de processos
GenericName[de]=Prozessanzeige
GenericName[en_GB]=Process Viewer
GenericName[es]=Visor de procesos
GenericName[fi]=Prosessikatselin
GenericName[fr]=Visualiseur de processus
GenericName[gl]=Visor de procesos
GenericName[it]=Visore dei processi
GenericName[ko]=프로세스 뷰어
GenericName[nb]=Prosessviser
GenericName[nl]=Viewer van processen
GenericName[nn]=Prosessvisar
GenericName[pl]=Przeglądarka procesów
GenericName[pt]=Visualizador de Processos
GenericName[pt_BR]=Visualizador de processos
GenericName[ru]=Монитор процессов
GenericName[sk]=Prehliadač procesov
GenericName[sl]=Pregledovalnik opravil
GenericName[sr]=Приказивач процеса
GenericName[sr@ijekavian]=Приказивач процеса
GenericName[sr@ijekavianlatin]=Prikazivač procesa
GenericName[sr@latin]=Prikazivač procesa
GenericName[sv]=Processvisning
GenericName[tr]=Süreç Görüntüleyici
GenericName[uk]=Перегляд процесів
GenericName[zh_CN]=进程查看器
GenericName[zh_TW]=行程檢視器
Keywords=system;process;task

2
htop.h
View File

@ -14,6 +14,6 @@ in the source distribution for its full text.
// ----------------------------------------
extern int main(int argc, char** argv);
int main(int argc, char** argv);
#endif

View File

@ -72,8 +72,6 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
fclose(file);
if (!line) break;
char *foundNumStr = String_getToken(line, wordNum);
const unsigned long int foundNum = atoi(foundNumStr);
free(foundNumStr);
@ -110,14 +108,17 @@ static ACPresence procAcpiCheck() {
char statePath[50];
xSnprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
FILE* file = fopen(statePath, "r");
if (!file) {
isOn = AC_ERROR;
continue;
}
char* line = String_readLine(file);
fclose(file);
if (!line) continue;
fclose(file);
const char *isOnline = String_getToken(line, 2);
free(line);
@ -193,18 +194,8 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
char* entryName = (char *) dirEntry->d_name;
const char filePath[50];
xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
int fd = open(filePath, O_RDONLY);
if (fd == -1)
continue;
if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') {
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) {

View File

@ -29,6 +29,6 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat
// READ FROM /sys
// ----------------------------------------
extern void Battery_getData(double* level, ACPresence* isOnAC);
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

View File

@ -13,9 +13,9 @@ in the source distribution for its full text.
#include "IOPriority.h"
#include "ListItem.h"
extern Panel* IOPriorityPanel_new(IOPriority currPrio);
Panel* IOPriorityPanel_new(IOPriority currPrio);
extern IOPriority IOPriorityPanel_getIOPriority(Panel* this);
IOPriority IOPriorityPanel_getIOPriority(Panel* this);
#endif

View File

@ -12,6 +12,6 @@ in the source distribution for its full text.
#ifdef HAVE_EXECINFO_H
#endif
extern void CRT_handleSIGSEGV(int sgn);
void CRT_handleSIGSEGV(int sgn);
#endif

View File

@ -1,7 +1,6 @@
/*
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.
*/
@ -16,16 +15,27 @@ in the source distribution for its full text.
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>
#include <time.h>
/*{
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#define PROCESS_FLAG_LINUX_SMAPS 0x2000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -89,17 +99,24 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
M_PSS = 119,
M_SWAP = 120,
M_PSSWP = 121,
LAST_PROCESSFIELD = 122,
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
typedef struct LinuxProcess_ {
Process super;
bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
unsigned long int cmajflt;
@ -108,14 +125,10 @@ 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;
long m_dt;
unsigned long long starttime;
#ifdef HAVE_TASKSTATS
unsigned long long io_rchar;
unsigned long long io_wchar;
@ -150,10 +163,29 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#endif
#ifndef Process_isUserlandThread
@ -162,14 +194,11 @@ typedef struct LinuxProcess_ {
}*/
/* semi-global */
long long btime;
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
@ -244,13 +273,21 @@ ProcessFieldData Process_fields[] = {
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, },
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPD% ", .description = "CPU delay %", .flags = 0, },
[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, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWD% ", .description = "Swapin delay %", .flags = 0, },
#endif
#ifdef HAVE_PERFCOUNTERS
[IPC] = { .name = "IPC", .title = " IPC ", .description = "Executed instructions per cycle", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE | PROCESS_FLAG_LINUX_HPC_INSN, },
[MCYCLE] = { .name = "MCYCLE", .title = " Mcycle ", .description = "Cycles (millions)", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE, },
[MINSTR] = { .name = "MINSTR", .title = " Minstr ", .description = "Instructions (millions)", .flags = PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_MISS] = { .name = "PERCENT_MISS", .title = "MIS% ", .description = "Cache misses per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_MISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_BMISS] = { .name = "PERCENT_BMISS", .title = "BrM% ", .description = "Branch misprediction per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_BMISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[L1DREADS] = { .name = "L1DREADS", .title = " L1Dread ", .description = "L1 data cache: reads (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DR, },
[L1DRMISSES] = { .name = "L1DRMISSES", .title = " R miss ", .description = "L1 data cache: reads misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DRM, },
[L1DWRITES] = { .name = "L1DWRITES", .title = " L1Dwrite ", .description = "L1D data cache: writes (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DW, },
[L1DWMISSES] = { .name = "L1DWMISSES", .title = " W miss ", .description = "L1D data cache: write misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DWM, },
#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, },
};
@ -290,6 +327,10 @@ void Process_delete(Object* cast) {
Process_done((Process*)cast);
#ifdef HAVE_CGROUP
free(this->cgroup);
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter_delete(this->cycleCounter);
PerfCounter_delete(this->insnCounter);
#endif
free(this->ttyDevice);
free(this);
@ -315,22 +356,51 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
return ioprio;
}
bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio) {
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority 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.i);
syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio);
#endif
return (LinuxProcess_updateIOPriority(this) == ioprio.i);
return (LinuxProcess_updateIOPriority(this) == ioprio);
}
#ifdef HAVE_DELAYACCT
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
if (delay_percent == -1LL) {
xSnprintf(buffer, n, " N/A ");
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
static char* perfFmt[] = {
"%6.2f ",
NULL,
NULL,
NULL,
NULL,
NULL,
"%6.1f ",
"%7.1f ",
"%8.2f ",
"%9.1f ",
};
static char* perfNA[] = {
" N/A ",
NULL,
NULL,
NULL,
NULL,
NULL,
" N/A ",
" N/A ",
" N/A ",
" N/A ",
};
static inline void LinuxProcess_printPerfCounter(float val, int len, char* buffer, int n, int* attr) {
if (val != -1) {
xSnprintf(buffer, n, perfFmt[len], val);
} else {
xSnprintf(buffer, n, "%4.1f ", delay_percent);
xSnprintf(buffer, n, perfNA[len]);
*attr = CRT_colors[PROCESS_SHADOW];
}
}
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
@ -356,20 +426,10 @@ 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;
case CSTIME: Process_printTime(str, lp->cstime); return;
case STARTTIME: {
struct tm date;
time_t starttimewall = btime + (lp->starttime / sysconf(_SC_CLK_TCK));
(void) localtime_r(&starttimewall, &date);
strftime(buffer, n, ((starttimewall > time(NULL) - 86400) ? "%R " : "%b%d "), &date);
break;
}
#ifdef HAVE_TASKSTATS
case RCHAR: Process_colorNumber(str, lp->io_rchar, coloring); return;
case WCHAR: Process_colorNumber(str, lp->io_wchar, coloring); return;
@ -417,9 +477,20 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
break;
}
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break;
case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break;
case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break;
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break;
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break;
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break;
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: Process_printPercentage(lp->pMiss, buffer, n, &attr); break;
case PERCENT_BMISS: Process_printPercentage(lp->pBMiss, buffer, n, &attr); break;
case IPC: LinuxProcess_printPerfCounter(lp->ipc, 0, buffer, n, &attr); break;
case MCYCLE: LinuxProcess_printPerfCounter(lp->mcycle, 8, buffer, n, &attr); break;
case MINSTR: LinuxProcess_printPerfCounter(lp->minstr, 8, buffer, n, &attr); break;
case L1DREADS: LinuxProcess_printPerfCounter(lp->l1dr, 9, buffer, n, &attr); break;
case L1DRMISSES: LinuxProcess_printPerfCounter(lp->l1drm, 9, buffer, n, &attr); break;
case L1DWRITES: LinuxProcess_printPerfCounter(lp->l1dw, 9, buffer, n, &attr); break;
case L1DWMISSES: LinuxProcess_printPerfCounter(lp->l1dwm, 9, buffer, n, &attr); break;
#endif
default:
Process_writeField((Process*)this, str, field);
@ -428,10 +499,12 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
RichString_append(str, attr, buffer);
}
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2) {
LinuxProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
if (settings->ss->direction == 1) {
p1 = (LinuxProcess*)v1;
p2 = (LinuxProcess*)v2;
} else {
@ -439,7 +512,7 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
p1 = (LinuxProcess*)v2;
}
long long diff;
switch ((int)settings->sortKey) {
switch ((int)settings->ss->sortKey) {
case M_DRS:
return (p2->m_drs - p1->m_drs);
case M_DT:
@ -450,22 +523,10 @@ 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;
case CSTIME: diff = p2->cstime - p1->cstime; goto test_diff;
case STARTTIME: {
if (p1->starttime == p2->starttime)
return (p1->super.pid - p2->super.pid);
else
return (p1->starttime - p2->starttime);
}
#ifdef HAVE_TASKSTATS
case RCHAR: diff = p2->io_rchar - p1->io_rchar; goto test_diff;
case WCHAR: diff = p2->io_wchar - p1->io_wchar; goto test_diff;
@ -495,12 +556,20 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
case OOM:
return (p2->oom - p1->oom);
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY:
return (p2->cpu_delay_percent > p1->cpu_delay_percent ? 1 : -1);
case PERCENT_IO_DELAY:
return (p2->blkio_delay_percent > p1->blkio_delay_percent ? 1 : -1);
case PERCENT_SWAP_DELAY:
return (p2->swapin_delay_percent > p1->swapin_delay_percent ? 1 : -1);
case PERCENT_CPU_DELAY: COMPARE_FIELD(cpu_delay_percent);
case PERCENT_IO_DELAY: COMPARE_FIELD(blkio_delay_percent);
case PERCENT_SWAP_DELAY: COMPARE_FIELD(swapin_delay_percent);
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: COMPARE_FIELD(pMiss);
case PERCENT_BMISS: COMPARE_FIELD(pBMiss);
case IPC: COMPARE_FIELD(ipc);
case MCYCLE: COMPARE_FIELD(mcycle);
case MINSTR: COMPARE_FIELD(minstr);
case L1DREADS: COMPARE_FIELD(l1dr);
case L1DRMISSES: COMPARE_FIELD(l1drm);
case L1DWRITES: COMPARE_FIELD(l1dw);
case L1DWMISSES: COMPARE_FIELD(l1dwm);
#endif
case IO_PRIORITY:
return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2);
@ -514,4 +583,3 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
bool Process_isThread(Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this));
}

View File

@ -5,18 +5,29 @@
/*
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.
*/
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#define PROCESS_FLAG_LINUX_SMAPS 0x2000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -80,17 +91,24 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
M_PSS = 119,
M_SWAP = 120,
M_PSSWP = 121,
LAST_PROCESSFIELD = 122,
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
typedef struct LinuxProcess_ {
Process super;
bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
unsigned long int cmajflt;
@ -99,14 +117,10 @@ 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;
long m_dt;
unsigned long long starttime;
#ifdef HAVE_TASKSTATS
unsigned long long io_rchar;
unsigned long long io_wchar;
@ -141,10 +155,29 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#endif
#ifndef Process_isUserlandThread
@ -152,18 +185,15 @@ typedef struct LinuxProcess_ {
#endif
/* semi-global */
extern long long btime;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern ProcessClass LinuxProcess_class;
extern LinuxProcess* LinuxProcess_new(Settings* settings);
LinuxProcess* LinuxProcess_new(Settings* settings);
extern void Process_delete(Object* cast);
void Process_delete(Object* cast);
/*
[1] Note that before kernel 2.6.26 a process that has not asked for
@ -175,19 +205,20 @@ 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)
extern IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
extern bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio);
#ifdef HAVE_DELAYACCT
extern void LinuxProcess_printDelay(float delay_percent, char* buffer, int n);
#endif
extern void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
extern long LinuxProcess_compare(const void* v1, const void* v2);
extern bool Process_isThread(Process* this);
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio);
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -26,11 +26,6 @@ in the source distribution for its full text.
#include <assert.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
#ifdef HAVE_DELAYACCT
#include <netlink/attr.h>
@ -45,9 +40,6 @@ in the source distribution for its full text.
/*{
#include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
extern long long btime;
typedef struct CPUData_ {
unsigned long long int totalTime;
@ -75,8 +67,6 @@ typedef struct CPUData_ {
unsigned long long int softIrqPeriod;
unsigned long long int stealPeriod;
unsigned long long int guestPeriod;
double frequency;
} CPUData;
typedef struct TtyDriver_ {
@ -91,24 +81,17 @@ typedef struct LinuxProcessList_ {
CPUData* cpus;
TtyDriver* ttyDrivers;
bool haveSmapsRollup;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
int netlink_family;
#endif
ZfsArcStats zfs;
} LinuxProcessList;
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif
#ifndef PROCCPUINFOFILE
#define PROCCPUINFOFILE PROCDIR "/cpuinfo"
#endif
#ifndef PROCSTATFILE
#define PROCSTATFILE PROCDIR "/stat"
#endif
@ -117,16 +100,12 @@ typedef struct LinuxProcessList_ {
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif
#ifndef PROCARCSTATSFILE
#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats"
#endif
#ifndef PROCTTYDRIVERSFILE
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
#endif
#ifndef PROC_LINE_LENGTH
#define PROC_LINE_LENGTH 4096
#define PROC_LINE_LENGTH 512
#endif
}*/
@ -245,41 +224,26 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
ProcessList_init(pl, Class(LinuxProcess), usersTable, pidWhiteList, userId);
LinuxProcessList_initTtyDrivers(this);
#ifdef HAVE_DELAYACCT
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 = fopen(PROCSTATFILE, "r");
FILE* file = fopen(PROCSTATFILE, "r");
if (file == NULL) {
CRT_fatalError("Cannot open " PROCSTATFILE);
}
int cpus = 0;
do {
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, PROC_LINE_LENGTH + 1, file) == NULL) {
CRT_fatalError("No btime in " PROCSTATFILE);
} else if (String_startsWith(buffer, "cpu")) {
int cpus = -1;
do {
cpus++;
} else if (String_startsWith(buffer, "btime ")) {
sscanf(buffer, "btime %lld\n", &btime);
break;
}
} while(true);
char * s = fgets(buffer, PROC_LINE_LENGTH, file);
(void) s;
} while (String_startsWith(buffer, "cpu"));
fclose(file);
pl->cpuCount = MAX(cpus - 1, 1);
@ -289,6 +253,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
this->cpus[i].totalTime = 1;
this->cpus[i].totalPeriod = 1;
}
return pl;
}
@ -384,10 +349,7 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
location += 1;
process->nlwp = strtol(location, &location, 10);
location += 1;
location = strchr(location, ' ')+1;
lp->starttime = strtoll(location, &location, 10);
location += 1;
for (int i=0; i<15; i++) location = strchr(location, ' ')+1;
for (int i=0; i<17; i++) location = strchr(location, ' ')+1;
process->exit_signal = strtol(location, &location, 10);
location += 1;
assert(location != NULL);
@ -399,7 +361,7 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
}
static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name, time_t curTime) {
char filename[MAX_NAME+1];
filename[MAX_NAME] = '\0';
@ -409,6 +371,13 @@ static bool LinuxProcessList_statProcessDir(Process* process, const char* dirnam
if (statok == -1)
return false;
process->st_uid = sstat.st_uid;
struct tm date;
time_t ctime = sstat.st_ctime;
process->starttime_ctime = ctime;
(void) localtime_r((time_t*) &ctime, &date);
strftime(process->starttime_show, 7, ((ctime > curTime - 86400) ? "%R " : "%b%d "), &date);
return true;
}
@ -467,7 +436,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
}
break;
case 's':
if (line[4] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
if (line[5] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
process->io_syscr = strtoull(line+7, NULL, 10);
} else if (strncmp(line+1, "yscw: ", 6) == 0) {
process->io_syscw = strtoull(line+7, NULL, 10);
@ -508,62 +477,6 @@ 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) {
@ -734,9 +647,9 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
}
if (nl_send_sync(this->netlink_socket, msg) < 0) {
process->swapin_delay_percent = -1LL;
process->blkio_delay_percent = -1LL;
process->cpu_delay_percent = -1LL;
process->swapin_delay_percent = -1;
process->blkio_delay_percent = -1;
process->cpu_delay_percent = -1;
return;
}
@ -747,6 +660,77 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
#endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
static void LinuxProcessList_readPerfCounters(LinuxProcess* lp, uint64_t flags) {
READ_COUNTER(c, lp->cycleCounter, PROCESS_FLAG_LINUX_HPC_CYCLE, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
READ_COUNTER(i, lp->insnCounter, PROCESS_FLAG_LINUX_HPC_INSN, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
READ_COUNTER(m, lp->missCounter, PROCESS_FLAG_LINUX_HPC_MISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES);
READ_COUNTER(b, lp->brCounter, PROCESS_FLAG_LINUX_HPC_BMISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES);
READ_COUNTER(r, lp->l1drCounter, PROCESS_FLAG_LINUX_HPC_L1DR, PERF_TYPE_HW_CACHE, L1DR);
READ_COUNTER(R, lp->l1drmCounter, PROCESS_FLAG_LINUX_HPC_L1DRM, PERF_TYPE_HW_CACHE, L1DRM);
READ_COUNTER(w, lp->l1dwCounter, PROCESS_FLAG_LINUX_HPC_L1DW, PERF_TYPE_HW_CACHE, L1DW);
READ_COUNTER(W, lp->l1dwmCounter, PROCESS_FLAG_LINUX_HPC_L1DWM, PERF_TYPE_HW_CACHE, L1DWM);
SET_IF(cOk, lp->mcycle, (double)cDelta / 1000000);
SET_IF(iOk, lp->minstr, (double)iDelta / 1000000);
SET_IFNZ(cOk && iOk, cDelta, lp->ipc, (double)iDelta / cDelta);
SET_IFNZ(mOk && iOk, iDelta, lp->pMiss, 100 * ((double)mDelta / iDelta));
SET_IFNZ(bOk && iOk, iDelta, lp->pBMiss, 100 * ((double)bDelta / iDelta));
SET_IF(rOk, lp->l1dr, (double)rDelta / 1000);
SET_IF(ROk, lp->l1drm, (double)RDelta / 1000);
SET_IF(wOk, lp->l1dw, (double)wDelta / 1000);
SET_IF(WOk, lp->l1dwm, (double)WDelta / 1000);
}
#endif
static void setCommand(Process* process, const char* command, int len) {
if (process->comm && process->commLen >= len) {
strncpy(process->comm, command, len + 1);
@ -758,6 +742,9 @@ static void setCommand(Process* process, const char* command, int len) {
}
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
if (Process_isKernelThread(process))
return true;
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
int fd = open(filename, O_RDONLY);
@ -769,14 +756,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
close(fd);
int tokenEnd = 0;
int lastChar = 0;
if (amtRead == 0) {
if (process->state == 'Z') {
process->basenameOffset = 0;
} else {
((LinuxProcess*)process)->isKernelThread = true;
}
return true;
} else if (amtRead < 0) {
if (amtRead <= 0) {
return false;
}
for (int i = 0; i < amtRead; i++) {
@ -794,7 +774,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
}
command[lastChar + 1] = '\0';
process->basenameOffset = tokenEnd;
setCommand(process, command, lastChar + 1);
setCommand(process, command, lastChar);
return true;
}
@ -846,7 +826,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
DIR* dir;
struct dirent* entry;
Settings* settings = pl->settings;
ScreenSettings* ss = settings->ss;
time_t curTime = tv.tv_sec;
#ifdef HAVE_TASKSTATS
unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL;
#endif
@ -890,28 +872,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
#ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO)
if (ss->flags & PROCESS_FLAG_IO)
LinuxProcessList_readIoFile(lp, dirname, name, now);
#endif
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];
@ -924,7 +891,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO)
LinuxProcess_updateIOPriority(lp);
float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
@ -933,19 +900,19 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
if(!preExisting) {
if (! LinuxProcessList_statProcessDir(proc, dirname, name))
if (! LinuxProcessList_statProcessDir(proc, dirname, name, curTime))
goto errorReadingProcess;
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) {
LinuxProcessList_readOpenVZData(lp, dirname, name);
}
#endif
#ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) {
LinuxProcessList_readVServerData(lp, dirname, name);
}
#endif
@ -968,13 +935,18 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
#endif
#ifdef HAVE_CGROUP
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
if (ss->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcessList_readCGroupFile(lp, dirname, name);
#endif
if (settings->flags & PROCESS_FLAG_LINUX_OOM)
if (ss->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcessList_readOomData(lp, dirname, name);
#ifdef HAVE_PERFCOUNTERS
if (ss->flags & PROCESS_FLAG_LINUX_HPC)
LinuxProcessList_readPerfCounters(lp, ss->flags);
#endif
if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
setCommand(proc, command, commLen);
@ -1024,30 +996,30 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
char buffer[128];
while (fgets(buffer, 128, file)) {
#define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %32llu kB", variable)) { break; } } while(0)
#define tryRead(label, variable) (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %32llu kB", variable))
switch (buffer[0]) {
case 'M':
tryRead("MemTotal:", &this->totalMem);
tryRead("MemFree:", &this->freeMem);
tryRead("MemShared:", &this->sharedMem);
if (tryRead("MemTotal:", &this->totalMem)) {}
else if (tryRead("MemFree:", &this->freeMem)) {}
else if (tryRead("MemShared:", &this->sharedMem)) {}
break;
case 'B':
tryRead("Buffers:", &this->buffersMem);
if (tryRead("Buffers:", &this->buffersMem)) {}
break;
case 'C':
tryRead("Cached:", &this->cachedMem);
if (tryRead("Cached:", &this->cachedMem)) {}
break;
case 'S':
switch (buffer[1]) {
case 'w':
tryRead("SwapTotal:", &this->totalSwap);
tryRead("SwapFree:", &swapFree);
if (tryRead("SwapTotal:", &this->totalSwap)) {}
else if (tryRead("SwapFree:", &swapFree)) {}
break;
case 'h':
tryRead("Shmem:", &shmem);
if (tryRead("Shmem:", &shmem)) {}
break;
case 'R':
tryRead("SReclaimable:", &sreclaimable);
if (tryRead("SReclaimable:", &sreclaimable)) {}
break;
}
break;
@ -1061,68 +1033,6 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
fclose(file);
}
static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
unsigned long long int dbufSize;
unsigned long long int dnodeSize;
unsigned long long int bonusSize;
FILE* file = fopen(PROCARCSTATSFILE, "r");
if (file == NULL) {
lpl->zfs.enabled = 0;
return;
}
char buffer[128];
while (fgets(buffer, 128, file)) {
#define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { break; } } while(0)
#define tryReadFlag(label, variable, flag) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { flag = 1; break; } else { flag = 0; } } while(0)
switch (buffer[0]) {
case 'c':
tryRead("c_max", &lpl->zfs.max);
tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed);
break;
case 'u':
tryRead("uncompressed_size", &lpl->zfs.uncompressed);
break;
case 's':
tryRead("size", &lpl->zfs.size);
break;
case 'h':
tryRead("hdr_size", &lpl->zfs.header);
break;
case 'd':
tryRead("dbuf_size", &dbufSize);
tryRead("dnode_size", &dnodeSize);
break;
case 'b':
tryRead("bonus_size", &bonusSize);
break;
case 'a':
tryRead("anon_size", &lpl->zfs.anon);
break;
case 'm':
tryRead("mfu_size", &lpl->zfs.MFU);
tryRead("mru_size", &lpl->zfs.MRU);
break;
}
#undef tryRead
#undef tryReadFlag
}
fclose(file);
lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0);
lpl->zfs.size /= 1024;
lpl->zfs.max /= 1024;
lpl->zfs.MFU /= 1024;
lpl->zfs.MRU /= 1024;
lpl->zfs.anon /= 1024;
lpl->zfs.header /= 1024;
lpl->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024;
if ( lpl->zfs.isCompressed ) {
lpl->zfs.compressed /= 1024;
lpl->zfs.uncompressed /= 1024;
}
}
static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
FILE* file = fopen(PROCSTATFILE, "r");
@ -1187,87 +1097,18 @@ 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;
LinuxProcessList_scanMemoryInfo(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);

View File

@ -9,18 +9,11 @@ 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)
#endif
#ifdef HAVE_DELAYACCT
#endif
#include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
extern long long btime;
typedef struct CPUData_ {
unsigned long long int totalTime;
@ -48,8 +41,6 @@ typedef struct CPUData_ {
unsigned long long int softIrqPeriod;
unsigned long long int stealPeriod;
unsigned long long int guestPeriod;
double frequency;
} CPUData;
typedef struct TtyDriver_ {
@ -64,24 +55,17 @@ typedef struct LinuxProcessList_ {
CPUData* cpus;
TtyDriver* ttyDrivers;
bool haveSmapsRollup;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
int netlink_family;
#endif
ZfsArcStats zfs;
} LinuxProcessList;
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif
#ifndef PROCCPUINFOFILE
#define PROCCPUINFOFILE PROCDIR "/cpuinfo"
#endif
#ifndef PROCSTATFILE
#define PROCSTATFILE PROCDIR "/stat"
#endif
@ -90,16 +74,12 @@ typedef struct LinuxProcessList_ {
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif
#ifndef PROCARCSTATSFILE
#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats"
#endif
#ifndef PROCTTYDRIVERSFILE
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
#endif
#ifndef PROC_LINE_LENGTH
#define PROC_LINE_LENGTH 4096
#define PROC_LINE_LENGTH 512
#endif
@ -111,9 +91,9 @@ typedef struct LinuxProcessList_ {
#endif
extern ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
extern void ProcessList_delete(ProcessList* pl);
void ProcessList_delete(ProcessList* pl);
#ifdef HAVE_TASKSTATS
@ -136,6 +116,54 @@ extern void ProcessList_delete(ProcessList* pl);
#endif
extern void ProcessList_goThroughEntries(ProcessList* super);
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#endif
void ProcessList_goThroughEntries(ProcessList* super);
#endif

View File

@ -19,12 +19,10 @@ 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"
#include "zfs/ZfsCompressedArcMeter.h"
#include "LinuxProcess.h"
#include "CRT.h"
#include <math.h>
#include <assert.h>
@ -96,10 +94,10 @@ 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, true);
void* set = Action_pickFromVector(st, ioprioPanel, 21);
if (set) {
IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (Arg){ .i = ioprio }, NULL);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (size_t) ioprio, NULL);
if (!ok)
beep();
}
@ -124,21 +122,11 @@ 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
};
@ -205,9 +193,6 @@ 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;
}
@ -229,17 +214,6 @@ void Platform_setSwapValues(Meter* this) {
this->values[0] = pl->usedSwap;
}
void Platform_setZfsArcValues(Meter* this) {
LinuxProcessList* lpl = (LinuxProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(lpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
LinuxProcessList* lpl = (LinuxProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(lpl->zfs));
}
char* Platform_getProcessEnv(pid_t pid) {
char procname[32+1];
xSnprintf(procname, 32, "/proc/%d/environ", pid);
@ -264,21 +238,3 @@ 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);
}

Some files were not shown because too many files have changed in this diff Show More