39 Commits

Author SHA1 Message Date
04cc193e3c Bump version to 2.2.0 2018-04-10 10:42:59 -03:00
5b38466eab Update ChangeLog 2018-04-10 10:42:46 -03:00
34dff098c5 Add tree view flag to man page (#777) 2018-04-10 10:36:34 -03:00
e85b072ad0 Require pkg-config as an optional build dependency on Linux (#775)
With this commit:

* if pkgconfig is installed, it will expand the code inside the shell if, adding the pkgconfig-based tests for the dependencies of Linux delay accounting.
* if pkgconfig is not installed, it will add an error message inside the test of Linux delay accounting, telling the user to install pkgconfig and rebuild the configure script if they want to use Linux delay accounting.

The end result is:

* people running Linux
  * will not need pkgconfig when not using delay accounting
  * will need pkgconfig when using delay accounting
    * if they don't have it
      * they are told by configure they need to install it and re-run autogen.sh when running from Git
      * they are told by configure they need to install it and re-run configure when running from the tarball
* people not running Linux
  * will never need pkgconfig

...and in none of the above scenarios the generated configure script produces unexpanded macros for users checking out the sources from Git.
2018-04-06 11:03:00 -03:00
7cfaa9dede MakeHeader.py: Fix for non-utf8 environments (#770)
Header creation fails with non-utf8 locale and python3.
Simply set LC_ALL="C" and use python3 to reproduce the issue.

env LC_ALL="C" ./scripts/MakeHeader.py MetersPanel.c
Traceback (most recent call last):
  File "./scripts/MakeHeader.py", line 32, in <module>
    for line in file.readlines():
  File "/usr/lib64/python3.5/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 956: ordinal not in range(128)

This changes is python2 and python3 compatible

cStringIO.StringIO module is removed because it is not able to accept unicode strings
https://docs.python.org/2/library/stringio.html#cStringIO.StringIO
2018-04-05 19:55:51 -03:00
db05ba6106 Add -t command-line flag for tree view 2018-04-05 19:52:19 -03:00
0505a7cfe1 macOS: fix the switched version test (#772) 2018-04-05 19:41:22 -03:00
cf04300d49 Solaris: update proc state letters to reflect Solaris usage 2018-04-05 19:38:13 -03:00
c53e384213 Solaris: bump copyright in Platform.{c,h} 2018-04-05 19:38:13 -03:00
da4877f48c Solaris: fix a memory leak caused by calling ProcessList_getProcess twice for each LWP 2018-04-05 19:38:13 -03:00
0969f83b21 Solaris: Implement process environment listing 2018-04-05 19:38:13 -03:00
155d7cbeee Solaris: add placeholder message about environment listing 2018-04-05 19:38:13 -03:00
1ae7625c42 Solaris: showing a dash for the top-level process is no longer necessary 2018-04-05 19:38:13 -03:00
3c96467f7b Solaris: add warning about proc_walk_f callback function 2018-04-05 19:38:13 -03:00
7f40a3a5af Solaris: condense separate process vs lwp handling down to a single workflow 2018-04-05 19:38:13 -03:00
45fad05b4a Solaris: get completely out of the file handling business using libproc 2018-04-05 19:38:13 -03:00
4ba06c51e5 Solaris: Condense conditional blocks for new vs old LWPs and procs 2018-04-05 19:38:13 -03:00
fa18ac964c Solaris: remove unneeded accumulators for process and thread counting 2018-04-05 19:38:13 -03:00
192e43c1ed Solaris: Assorted post-LWP code cleanup 2018-04-05 19:38:13 -03:00
75598c1389 Solaris: Implement kernel thread counting 2018-04-05 19:38:13 -03:00
c235b45cd6 Solaris: If a process has a running LWP, then the process is by definition running 2018-04-05 19:38:13 -03:00
0dbedf95a8 Collapse current subtree pressing Backspace 2018-04-05 19:38:13 -03:00
42c3a1fcb3 Solaris: Implement LWP enumeration (#768)
Squashed the following commits:

* Solaris: Get LWP enumeration working
* Solaris: Make showing and hiding of kernel threads behave
* Solaris: remove usage of lwpstatus that is no longer needed
* Solaris: no discrete access to parent proc structure needed
* Solaris: Restore runtime MaxPid detection after LWP changes
* Solaris: Workaround virtual PID signal issue by shadowing kill() with a macro
* Solaris: Fix unintention double-shifting of virtual PID for LWP enumeration
* Solaris: Add LWPID to default display since LWP enumeration is also default
* Solaris: use PAGE_SIZE_KB from Process.h instead of custom definition
* Solaris: stop LWP enumeration at 1023 LWPs per proc since that is all we can handle in the virtual PID
2018-03-27 13:27:12 -03:00
1cf8f429a5 OpenBSD: read Battery data
Signed-off-by: Hisham Muhammad <hisham@gobolinux.org>
2018-03-26 15:14:12 -03:00
23f96048c7 macOS: keep scanning thread for versions before High Sierra (#728)
Keep scanning threads for versions before High Sierra 13.0.0 and after 13.3.0.
2018-03-26 15:14:12 -03:00
70bc51a387 Solaris: Fix virt and resident memory sizes. Was using KiB, needed pages. 2018-03-26 15:14:12 -03:00
442b0d2576 New makefile targets to rebuild and clean htop headers.
`make htop-headers` will regenerate all '.h' headers in htop source for
all platforms.
`make clean-htop-headers` will delete all generated htop headers.

Because of the introduction of these two targets, I slightly changed
the style of platform-specific portions of makefile rules.
Please comment if you accept such a style, or need me to revert to old
style.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-03-26 15:14:12 -03:00
47cf1532b0 Linux: change how kernel threads are detected
Use the same method that ps and top use to determine if a
process is a kernel thread on Linux: check if cmdline is empty.

Thanks to @wangqr's investigation reported here:
https://github.com/hishamhm/htop/issues/761#issuecomment-375306069

Fixes #761.
2018-03-25 15:26:05 -03:00
dc050e8088 Fix overflow for signals >= 100.
Thanks to @gzip4 for tracking this down.

Closes #764.
2018-03-25 15:15:37 -03:00
aa3dc634dc strace: increase string length 2018-03-25 15:14:04 -03:00
5a75797f4d Solaris: code indentation fix 2018-03-16 11:43:48 -03:00
cda84fcddc Solaris: enough changes made to justify a copyright bump to 2018 2018-03-16 11:43:48 -03:00
69355234d0 Solaris: fix memory allocation for usernames (some empty usernames in 32-bit builds) 2018-03-16 11:43:48 -03:00
cefbe499db Solaris: fix malloc() / free() issues with zone name handling 2018-03-16 11:43:48 -03:00
76ef3682db Solaris: Link against libmalloc to fix various crashes 2018-03-16 11:43:48 -03:00
4bd68809e4 Solaris: Import backtrace-on-abort from Linux, with minor modification for Solaris 2018-03-16 11:43:48 -03:00
a042cfece2 Use fork/exec instead of popen to run lsof (#757)
Fixes #675
2018-03-16 11:39:03 -03:00
2e3c35d66d Use AM_CFLAGS and AM_LDFLAGS in Makefile.am (#760)
This reduces generated Makefile.in size by 74%.
(217319 bytes -> 56326 bytes)

Automake considers that <prog>_CFLAGS and <prog>_LDFLAGS are
program-specific build rules, and when such are specified, Automake
will generate additional code just to avoid the "generic" and
package-wide AM_CFLAGS or AM_LDFLAGS. (Especially for <prog>_CFLAGS,
Automake will rename generated object files to become "prog-foo.o" and
such, and it's _a lot_ of code to achieve this in Makefile.)

There's no reason for htop to rename intermediate object files. It's
better to make things simpler.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-03-16 11:31:48 -03:00
697f5bb9c1 Import Solaris support (#741)
This commit adds support for Solaris, squashed from PR #741:

Summary of additions:

* Initial setup of Solaris platform directory
* Add Solaris platform into autoconf template
* Uptime and load averages
* Add dependency on libkstat
* Basic process listing
* Zone name display
* CPU detection
* Per-process memory and CPU usage parsed correctly
* Uses sysconf to discover number of CPUs, instead of more complex libkstat code
* Simple memory display working
* Reduce repetitive calls to the PAGE_SIZE macro when reading memory info
* Add Project, Contract, Task, and Pool into process properties
* Use system major()/minor() implementations and remove extraneous definition of mkdev()
* Get the STARTTIME column working properly, using the Linux implementation as a guide
2018-03-02 18:20:46 -03:00
57 changed files with 1754 additions and 1522 deletions

View File

@ -155,11 +155,25 @@ static bool expandCollapse(Panel* panel) {
return true; 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) { Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
ScreenSettings* ss = settings->ss; settings->sortKey = sortKey;
ss->sortKey = sortKey; settings->direction = 1;
ss->direction = 1; settings->treeView = false;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
} }
@ -167,12 +181,11 @@ static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel ")); Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by"); Panel_setHeader(sortPanel, "Sort by");
ScreenSettings* ss = st->settings->ss; ProcessField* fields = st->settings->fields;
ProcessField* fields = ss->fields;
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name); char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == ss->sortKey) if (fields[i] == st->settings->sortKey)
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
} }
@ -221,9 +234,8 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
} }
static Htop_Reaction actionToggleTreeView(State* st) { static Htop_Reaction actionToggleTreeView(State* st) {
ScreenSettings* ss = st->settings->ss; st->settings->treeView = !st->settings->treeView;
ss->treeView = !ss->treeView; if (st->settings->treeView) st->settings->direction = 1;
if (ss->treeView) ss->direction = 1;
ProcessList_expandTree(st->pl); ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
@ -251,7 +263,7 @@ static Htop_Reaction actionLowerPriority(State* st) {
} }
static Htop_Reaction actionInvertSortOrder(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) {
ScreenSettings_invertSortOrder(st->settings->ss); Settings_invertSortOrder(st->settings);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS; return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
} }
@ -264,24 +276,22 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
return changed ? HTOP_RECALCULATE : HTOP_OK; 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) { static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
} }
static Htop_Reaction actionQuit() { static Htop_Reaction actionQuit() {
return HTOP_QUIT; 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) { static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1) if (st->pl->cpuCount == 1)
return HTOP_OK; return HTOP_OK;
@ -570,6 +580,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['+'] = actionExpandOrCollapse; keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse; keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse; keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser; keys['u'] = actionFilterByUser;
keys['F'] = Action_follow; keys['F'] = Action_follow;
keys['S'] = actionSetup; keys['S'] = actionSetup;
@ -585,6 +596,5 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll; keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren; keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen; keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
} }

9
CRT.c
View File

@ -128,7 +128,6 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
@ -233,7 +232,6 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Magenta,Black), [CPU_SOFTIRQ] = ColorPair(Magenta,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_MONOCHROME] = { [COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL, [RESET_COLOR] = A_NORMAL,
@ -293,7 +291,6 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = A_BOLD, [CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE, [CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE, [CPU_GUEST] = A_REVERSE,
[PANEL_EDIT] = A_BOLD,
}, },
[COLORSCHEME_BLACKONWHITE] = { [COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White), [RESET_COLOR] = ColorPair(Black,White),
@ -353,7 +350,6 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,White), [CPU_SOFTIRQ] = ColorPair(Blue,White),
[CPU_STEAL] = ColorPair(Cyan,White), [CPU_STEAL] = ColorPair(Cyan,White),
[CPU_GUEST] = ColorPair(Cyan,White), [CPU_GUEST] = ColorPair(Cyan,White),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_LIGHTTERMINAL] = { [COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black), [RESET_COLOR] = ColorPair(Black,Black),
@ -413,7 +409,6 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Black,Black), [CPU_STEAL] = ColorPair(Black,Black),
[CPU_GUEST] = ColorPair(Black,Black), [CPU_GUEST] = ColorPair(Black,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_MIDNIGHT] = { [COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue), [RESET_COLOR] = ColorPair(White,Blue),
@ -473,7 +468,6 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Black,Blue), [CPU_SOFTIRQ] = ColorPair(Black,Blue),
[CPU_STEAL] = ColorPair(White,Blue), [CPU_STEAL] = ColorPair(White,Blue),
[CPU_GUEST] = ColorPair(White,Blue), [CPU_GUEST] = ColorPair(White,Blue),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_BLACKNIGHT] = { [COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black), [RESET_COLOR] = ColorPair(Cyan,Black),
@ -533,11 +527,12 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Cyan),
}, },
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
}; };
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5; int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10; int CRT_scrollWheelVAmount = 10;

3
CRT.h
View File

@ -116,7 +116,6 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
@ -145,6 +144,8 @@ int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT]; extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount; extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount; extern int CRT_scrollWheelVAmount;

View File

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

View File

@ -1,21 +1,38 @@
What's new in version 2.1.1 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 * Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report) (thanks to @florian2833z for the report)
* Avoid some bashisms in configure script
(thanks to Jesin)
* Use CFLAGS from ncurses*-config if present * Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein) (thanks to Michael Klein)
* Fix build failure in Glibc 2.28 * Header generator supports non-UTF-8 environments
(thanks to Kang-Che Sung) (thanks to @volkov-am)
* Linux: changed detection of kernel threads
* Collapse current subtree pressing Backspace
* BUGFIX: fix behavior of SYSCR column * BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde) (thanks to Marc Kleine-Budde)
* BUGFIX: preserve LDFLAGS when building * BUGFIX: obtain exit code of lsof correctly
(thanks to Lance Frederickson for the report) (thanks to @wangqr)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix crash with particular keycodes * BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report) (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 * BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report) (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 What's new in version 2.1.0

View File

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

View File

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

View File

@ -83,6 +83,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Display options"); 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("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 kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));

View File

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

View File

@ -30,9 +30,9 @@ void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
int FunctionBar_draw(const FunctionBar* this, char* buffer); void FunctionBar_draw(const FunctionBar* this, char* buffer);
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr); void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos); int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

View File

@ -62,7 +62,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) { void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->meterColumns[col]; MeterColumnSettings* colSettings = &this->settings->columns[col];
for (int i = 0; i < colSettings->len; i++) { for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col); Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) { if (colSettings->modes[i] != 0) {
@ -75,7 +75,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) { void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->meterColumns[col]; MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names); String_freeArray(colSettings->names);
free(colSettings->modes); free(colSettings->modes);

View File

@ -38,7 +38,6 @@ typedef struct IncMode_ {
typedef struct IncSet_ { typedef struct IncSet_ {
IncMode modes[2]; IncMode modes[2];
IncMode* active; IncMode* active;
Panel* panel;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found; bool found;
@ -116,35 +115,23 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
} }
} }
static bool search(IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) { static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel); int size = Panel_size(panel);
bool found = false; bool found = false;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) { if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i); Panel_setSelected(panel, i);
found = true; found = true;
break; break;
} }
} }
IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]); if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
return found; return found;
} }
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]);
}
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) { bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR) if (ch == ERR)
return true; return true;
@ -202,11 +189,13 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} else { } else {
IncMode_reset(mode); IncMode_reset(mode);
} }
IncSet_deactivate(this, panel); this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
this->found = search(this, panel, getPanelValue); this->found = search(mode, panel, getPanelValue);
} }
if (filterChanged && lines) { if (filterChanged && lines) {
updateWeakPanel(this, panel, lines); updateWeakPanel(this, panel, lines);
@ -221,11 +210,15 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return ""; return "";
} }
void IncSet_drawBar(IncSet* this, int attr) { 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) {
if (this->active) { if (this->active) {
int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr); FunctionBar_draw(this->active->bar, this->active->buffer);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else { } else {
FunctionBar_draw(this->defaultBar, NULL); FunctionBar_draw(this->defaultBar, NULL);
} }

View File

@ -33,7 +33,6 @@ typedef struct IncMode_ {
typedef struct IncSet_ { typedef struct IncSet_ {
IncMode modes[2]; IncMode modes[2];
IncMode* active; IncMode* active;
Panel* panel;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found; bool found;
@ -46,13 +45,13 @@ IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this); void IncSet_delete(IncSet* this);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
const char* IncSet_getListItemValue(Panel* panel, int i); const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_drawBar(IncSet* this, int attr); void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(IncSet* this);
int IncSet_synthesizeEvent(IncSet* this, int x); int IncSet_synthesizeEvent(IncSet* this, int x);

View File

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

View File

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

View File

@ -19,14 +19,8 @@ typedef struct ListItem_ {
} ListItem; } ListItem;
void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
extern ObjectClass ListItem_class; extern ObjectClass ListItem_class;
void ListItem_init(ListItem* this, const char* value, int key);
ListItem* ListItem_new(const char* value, int key); ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text); void ListItem_append(ListItem* this, const char* text);

View File

@ -72,17 +72,15 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) { if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch); int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl; ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1; int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx); ProcessField field = ProcessList_keyAt(pl, hx);
if (field == ss->sortKey) { if (field == settings->sortKey) {
ScreenSettings_invertSortOrder(ss); Settings_invertSortOrder(settings);
ss->treeView = false; settings->treeView = false;
} else { } else {
reaction |= Action_setSortKey(settings, field); reaction |= Action_setSortKey(settings, field);
} }
@ -115,8 +113,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
if (reaction & HTOP_REDRAW_BAR) { if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, settings->ss->treeView); MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); IncSet_drawBar(this->inc);
} }
if (reaction & HTOP_UPDATE_PANELHDR) { if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super)); ProcessList_printHeader(this->state->pl, Panel_getHeader(super));

View File

@ -12,8 +12,8 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
htop_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
htop_LDFLAGS = AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \ 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 \ DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.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 \ BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c ScreensPanel.c \ SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \ TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \ HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c InfoScreen.c XAlloc.c
@ -31,71 +31,167 @@ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \ CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.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 \ BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h ScreensPanel.h \ ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.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 \ AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h EnvScreen.h InfoScreen.h XAlloc.h
if HTOP_LINUX all_platform_headers =
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/PerfCounter.c
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \ # Linux
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h \ # -----
linux/PerfCounter.h
linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h
all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX
AM_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c
myhtopplatheaders = $(linux_platform_headers)
endif endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \ myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c
myhtopplatheaders = freebsd/Platform.h freebsd/FreeBSDProcessList.h \ myhtopplatheaders = $(freebsd_platform_headers)
freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h
endif 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 if HTOP_DRAGONFLYBSD
htop_LDFLAGS += -lkvm -lkinfo -lexecinfo AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \ myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = dragonflybsd/Platform.h dragonflybsd/DragonFlyBSDProcessList.h \ myhtopplatheaders = $(dragonflybsd_platform_headers)
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDCRT.h dragonflybsd/Battery.h
endif 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 if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
myhtopplatheaders = openbsd/Platform.h openbsd/OpenBSDProcessList.h \ myhtopplatheaders = $(openbsd_platform_headers)
openbsd/OpenBSDProcess.h openbsd/OpenBSDCRT.h openbsd/Battery.h
endif endif
# Darwin
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN if HTOP_DARWIN
htop_LDFLAGS += -framework IOKit -framework CoreFoundation AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \ myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c
myhtopplatheaders = darwin/Platform.h darwin/DarwinProcess.h \ myhtopplatheaders = $(darwin_platform_headers)
darwin/DarwinProcessList.h darwin/DarwinCRT.h darwin/Battery.h
endif endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.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
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 if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \ myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \ unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = unsupported/Platform.h \ myhtopplatheaders = $(unsupported_platform_headers)
unsupported/UnsupportedProcess.h unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h unsupported/Battery.h
endif endif
# ----
SUFFIXES = .h SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders) BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h 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: target:
echo $(htop_SOURCES) echo $(htop_SOURCES)
@ -105,9 +201,6 @@ profile:
debug: debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
coverage: coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"

View File

@ -76,16 +76,35 @@ void OpenFilesScreen_draw(InfoScreen* this) {
} }
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char command[1025]; char buffer[1025];
xSnprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid); xSnprintf(buffer, 1024, "%d", pid);
FILE* fd = popen(command, "r");
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData)); OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL; OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data); OpenFiles_Data* item = &(pdata->data);
if (!fd) { int fdpair[2];
pdata->error = 127; if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata; 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 (;;) { for (;;) {
char* line = String_readLine(fd); char* line = String_readLine(fd);
if (!line) { if (!line) {
@ -105,7 +124,15 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
item->data[cmd] = xStrdup(line + 1); item->data[cmd] = xStrdup(line + 1);
free(line); free(line);
} }
pdata->error = pclose(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);
return pdata; return pdata;
} }

22
Panel.c
View File

@ -57,7 +57,6 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
int cursorX, cursorY;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
@ -67,7 +66,6 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
@ -87,11 +85,6 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1) #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 = { PanelClass Panel_class = {
.super = { .super = {
.extends = Class(Object), .extends = Class(Object),
@ -119,8 +112,6 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->y = y; this->y = y;
this->w = w; this->w = w;
this->h = h; this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL; this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE); this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0; this->scrollV = 0;
@ -376,6 +367,7 @@ void Panel_draw(Panel* this, bool focus) {
RichString_end(old); RichString_end(old);
} }
this->oldSelected = this->selected; this->oldSelected = this->selected;
move(0, 0);
} }
bool Panel_onKey(Panel* this, int key) { bool Panel_onKey(Panel* this, int key) {
@ -507,15 +499,3 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
} }
return IGNORED; 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();
}

View File

@ -46,7 +46,6 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
int cursorX, cursorY;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
@ -56,7 +55,6 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
@ -75,8 +73,6 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1) #define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern PanelClass Panel_class; extern PanelClass Panel_class;
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);
@ -127,7 +123,4 @@ bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch); HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
#endif #endif

View File

@ -51,7 +51,6 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include <sys/types.h> #include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
@ -157,7 +156,7 @@ typedef struct ProcessFieldData_ {
const char* name; const char* name;
const char* title; const char* title;
const char* description; const char* description;
uint64_t flags; int flags;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
@ -179,6 +178,8 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) #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_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state)) #define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
@ -369,21 +370,6 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
} }
} }
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) { void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
@ -392,15 +378,30 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes; bool coloring = this->settings->highlightMegabytes;
switch (field) { switch (field) {
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break; case PERCENT_CPU: {
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break; if (this->percent_cpu > 999.9) {
xSnprintf(buffer, n, "%4d ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
xSnprintf(buffer, n, "%3d. ", (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 COMM: { case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) { if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD]; attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
} }
ScreenSettings* ss = this->settings->ss; if (!this->settings->treeView || this->indent == 0) {
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str); Process_writeCommand(this, attr, baseattr, str);
return; return;
} else { } else {
@ -421,7 +422,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
buf += written; buf += written;
n -= written; n -= written;
} }
const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE]; const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); 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); RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str); Process_writeCommand(this, attr, baseattr, str);
@ -492,7 +493,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
void Process_display(Object* cast, RichString* out) { void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast; Process* this = (Process*) cast;
ProcessField* fields = this->settings->ss->fields; ProcessField* fields = this->settings->fields;
RichString_prune(out); RichString_prune(out);
for (int i = 0; fields[i]; i++) for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]); As_Process(this)->writeField(this, out, fields[i]);
@ -562,15 +563,14 @@ long Process_pidCompare(const void* v1, const void* v2) {
long Process_compare(const void* v1, const void* v2) { long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2; Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings; Settings *settings = ((Process*)v1)->settings;
ScreenSettings* ss = settings->ss; if (settings->direction == 1) {
if (ss->direction == 1) {
p1 = (Process*)v1; p1 = (Process*)v1;
p2 = (Process*)v2; p2 = (Process*)v2;
} else { } else {
p2 = (Process*)v1; p2 = (Process*)v1;
p1 = (Process*)v2; p1 = (Process*)v2;
} }
switch (ss->sortKey) { switch (settings->sortKey) {
case PERCENT_CPU: case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1); return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM: case PERCENT_MEM:

View File

@ -29,7 +29,6 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include <sys/types.h> #include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
@ -135,7 +134,7 @@ typedef struct ProcessFieldData_ {
const char* name; const char* name;
const char* title; const char* title;
const char* description; const char* description;
uint64_t flags; int flags;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
@ -157,6 +156,8 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) #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_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state)) #define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
@ -182,8 +183,6 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
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);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
void Process_writeField(Process* this, RichString* str, ProcessField field); void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out); void Process_display(Object* cast, RichString* out);

View File

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

View File

@ -71,46 +71,30 @@ inline int ScreenManager_size(ScreenManager* this) {
} }
void ScreenManager_add(ScreenManager* this, Panel* item, int size) { 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) { if (this->orientation == HORIZONTAL) {
int lastX = 0; int lastX = 0;
if (idx > 0) { if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, idx - 1); Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
lastX = last->x + last->w + 1; lastX = last->x + last->w + 1;
} }
int height = LINES - this->y1 + this->y2; int height = LINES - this->y1 + this->y2;
if (size <= 0) { if (size > 0) {
size = COLS-this->x1+this->x2-lastX; Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
} }
Panel_resize(item, size, height);
Panel_move(item, lastX, this->y1); 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 // TODO: VERTICAL
Vector_insert(this->panels, idx, item); Vector_add(this->panels, item);
item->needsRedraw = true; item->needsRedraw = true;
this->panelCount++; this->panelCount++;
} }
Panel* ScreenManager_remove(ScreenManager* this, int idx) { Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx); assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx); Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--; 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; return panel;
} }
@ -147,7 +131,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) { if (*rescan) {
*oldTime = newTime; *oldTime = newTime;
ProcessList_scan(pl); ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->ss->treeView) { if (*sortTimeout == 0 || this->settings->treeView) {
ProcessList_sort(pl); ProcessList_sort(pl);
*sortTimeout = 1; *sortTimeout = 1;
} }
@ -173,8 +157,7 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
} }
} }
static Panel* setCurrentPanel(ScreenManager* this, int focus) { static Panel* setCurrentPanel(Panel* panel) {
Panel* panel = (Panel*) Vector_get(this->panels, focus);
FunctionBar_draw(panel->currentBar, NULL); FunctionBar_draw(panel->currentBar, NULL);
return panel; return panel;
} }
@ -183,7 +166,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false; bool quit = false;
int focus = 0; int focus = 0;
Panel* panelFocus = setCurrentPanel(this, focus); Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
double oldTime = 0.0; double oldTime = 0.0;
@ -206,7 +189,8 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} }
int prevCh = ch; int prevCh = ch;
ch = Panel_getCh(panelFocus); set_escdelay(25);
ch = getch();
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) { if (ch == KEY_MOUSE) {
@ -228,7 +212,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE; ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) { if (panel == panelFocus || this->allowFocusChange) {
focus = i; focus = i;
panelFocus = setCurrentPanel(this, i); panelFocus = setCurrentPanel(panel);
Object* oldSelection = Panel_getSelected(panel); Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) { if (Panel_getSelected(panel) == oldSelection) {
@ -304,7 +288,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryLeft: tryLeft:
if (focus > 0) if (focus > 0)
focus--; focus--;
panelFocus = setCurrentPanel(this, focus); panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus > 0) if (Panel_size(panelFocus) == 0 && focus > 0)
goto tryLeft; goto tryLeft;
break; break;
@ -319,7 +303,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryRight: tryRight:
if (focus < this->panelCount - 1) if (focus < this->panelCount - 1)
focus++; focus++;
panelFocus = setCurrentPanel(this, focus); panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
goto tryRight; goto tryRight;
break; break;

View File

@ -43,8 +43,6 @@ extern int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size); void ScreenManager_add(ScreenManager* this, Panel* item, int size);
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);
Panel* ScreenManager_remove(ScreenManager* this, int idx); Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2); void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);

View File

@ -1,330 +0,0 @@
/*
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;
}

View File

@ -1,51 +0,0 @@
/* 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,32 +29,23 @@ typedef struct {
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings meterColumns[2]; MeterColumnSettings columns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount; int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
bool shadowOtherUsers; bool shadowOtherUsers;
@ -77,82 +68,25 @@ 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) { void Settings_delete(Settings* this) {
free(this->filename); free(this->filename);
for (unsigned int i = 0; i < (sizeof(this->meterColumns)/sizeof(MeterColumnSettings)); i++) { free(this->fields);
String_freeArray(this->meterColumns[i].names); for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
free(this->meterColumns[i].modes); String_freeArray(this->columns[i].names);
} free(this->columns[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); free(this);
} }
static void Settings_readMeters(Settings* this, char* line, int side) { static void Settings_readMeters(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
this->meterColumns[side].names = ids; this->columns[column].names = ids;
} }
static void Settings_readMeterModes(Settings* this, char* line, int side) { static void Settings_readMeterModes(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
@ -161,13 +95,13 @@ static void Settings_readMeterModes(Settings* this, char* line, int side) {
for (int i = 0; ids[i]; i++) { for (int i = 0; ids[i]; i++) {
len++; len++;
} }
this->meterColumns[side].len = len; this->columns[column].len = len;
int* modes = xCalloc(len, sizeof(int)); int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]); modes[i] = atoi(ids[i]);
} }
String_freeArray(ids); String_freeArray(ids);
this->meterColumns[side].modes = modes; this->columns[column].modes = modes;
} }
static void Settings_defaultMeters(Settings* this) { static void Settings_defaultMeters(Settings* this) {
@ -176,59 +110,37 @@ static void Settings_defaultMeters(Settings* this) {
sizes[1]++; sizes[1]++;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
this->meterColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->meterColumns[i].modes = xCalloc(sizes[i], sizeof(int)); this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->meterColumns[i].len = sizes[i]; this->columns[i].len = sizes[i];
} }
int r = 0; int r = 0;
if (this->cpuCount > 8) { if (this->cpuCount > 8) {
this->meterColumns[0].names[0] = xStrdup("LeftCPUs2"); this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->meterColumns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs2"); this->columns[1].names[r] = xStrdup("RightCPUs2");
this->meterColumns[1].modes[r++] = BAR_METERMODE; this->columns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) { } else if (this->cpuCount > 4) {
this->meterColumns[0].names[0] = xStrdup("LeftCPUs"); this->columns[0].names[0] = xStrdup("LeftCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs"); this->columns[1].names[r] = xStrdup("RightCPUs");
this->meterColumns[1].modes[r++] = BAR_METERMODE; this->columns[1].modes[r++] = BAR_METERMODE;
} else { } else {
this->meterColumns[0].names[0] = xStrdup("AllCPUs"); this->columns[0].names[0] = xStrdup("AllCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE; this->columns[0].modes[0] = BAR_METERMODE;
} }
this->meterColumns[0].names[1] = xStrdup("Memory"); this->columns[0].names[1] = xStrdup("Memory");
this->meterColumns[0].modes[1] = BAR_METERMODE; this->columns[0].modes[1] = BAR_METERMODE;
this->meterColumns[0].names[2] = xStrdup("Swap"); this->columns[0].names[2] = xStrdup("Swap");
this->meterColumns[0].modes[2] = BAR_METERMODE; this->columns[0].modes[2] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Tasks"); this->columns[1].names[r] = xStrdup("Tasks");
this->meterColumns[1].modes[r++] = TEXT_METERMODE; this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("LoadAverage"); this->columns[1].names[r] = xStrdup("LoadAverage");
this->meterColumns[1].modes[r++] = TEXT_METERMODE; this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Uptime"); this->columns[1].names[r] = xStrdup("Uptime");
this->meterColumns[1].modes[r++] = TEXT_METERMODE; this->columns[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) { static void readFields(ProcessField* fields, int* flags, const char* line) {
@ -239,10 +151,11 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
int i, j; int i, j;
*flags = 0; *flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) { for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
int idx = toFieldIndex(ids[i]); // This "+1" is for compatibility with the older enum format.
if (idx != -1) { int id = atoi(ids[i]) + 1;
fields[j] = idx; if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
*flags |= Process_fields[idx].flags; fields[j] = id;
*flags |= Process_fields[id].flags;
j++; j++;
} }
} }
@ -250,32 +163,6 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
String_freeArray(ids); 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) { static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd; FILE* fd;
@ -287,9 +174,6 @@ static bool Settings_read(Settings* this, const char* fileName) {
bool didReadMeters = false; bool didReadMeters = false;
bool didReadFields = false; bool didReadFields = false;
ProcessField* legacyFields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
int legacyFlags;
bool legacyFieldsRead = false;
for (;;) { for (;;) {
char* line = String_readLine(fd); char* line = String_readLine(fd);
if (!line) { if (!line) {
@ -303,8 +187,15 @@ static bool Settings_read(Settings* this, const char* fileName) {
continue; continue;
} }
if (String_eq(option[0], "fields")) { if (String_eq(option[0], "fields")) {
readFields(legacyFields, &legacyFlags, option[1]); readFields(this->fields, &(this->flags), option[1]);
legacyFieldsRead = true; 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]);
} else if (String_eq(option[0], "hide_threads")) { } else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]); this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) { } else if (String_eq(option[0], "hide_kernel_threads")) {
@ -353,60 +244,40 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1); Settings_readMeterModes(this, option[1], 1);
didReadMeters = true; didReadMeters = 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); String_freeArray(option);
} }
fclose(fd); fclose(fd);
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 (!didReadMeters) { if (!didReadMeters) {
Settings_defaultMeters(this); Settings_defaultMeters(this);
} }
return didReadFields; return didReadFields;
} }
static void writeFields(FILE* fd, ProcessField* fields, bool byName) { static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
const char* sep = ""; const char* sep = "";
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
if (byName) { // This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%s", sep, toFieldName(fields[i])); fprintf(fd, "%s%d", sep, (int) fields[i]-1);
} else {
// This " - 1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
}
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
} }
static void writeMeters(Settings* this, FILE* fd, int side) { static void writeMeters(Settings* this, FILE* fd, int column) {
writeList(fd, this->meterColumns[side].names, this->meterColumns[side].len); 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 writeMeterModes(Settings* this, FILE* fd, int side) { static void writeMeterModes(Settings* this, FILE* fd, int column) {
const char* sep = ""; const char* sep = "";
for (int i = 0; i < this->meterColumns[side].len; i++) { for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->meterColumns[side].modes[i]); fprintf(fd, "%s%d", sep, this->columns[column].modes[i]);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
@ -424,7 +295,10 @@ bool Settings_write(Settings* this) {
} }
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n"); 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"); fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, false); 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, "hide_threads=%d\n", (int) this->hideThreads); fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
@ -434,6 +308,7 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); 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, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
@ -445,23 +320,6 @@ bool Settings_write(Settings* this) {
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
// 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); fclose(fd);
return true; return true;
} }
@ -470,11 +328,14 @@ Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false; this->hideThreads = false;
this->shadowOtherUsers = false; this->shadowOtherUsers = false;
this->showThreadNames = false; this->showThreadNames = false;
this->hideKernelThreads = false; this->hideKernelThreads = false;
this->hideUserlandThreads = false; this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false; this->highlightBaseName = false;
this->highlightMegabytes = false; this->highlightMegabytes = false;
this->detailedCPUTime = false; this->detailedCPUTime = false;
@ -484,8 +345,15 @@ Settings* Settings_new(int cpuCount) {
this->showProgramPath = true; this->showProgramPath = true;
this->highlightThreads = true; this->highlightThreads = true;
this->screens = xCalloc(sizeof(ScreenSettings*), 1); this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
this->nScreens = 0; // 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;
}
char* legacyDotfile = NULL; char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC"); char* rcfile = getenv("HTOPRC");
@ -546,20 +414,15 @@ Settings* Settings_new(int cpuCount) {
} }
if (!ok) { if (!ok) {
Settings_defaultMeters(this); Settings_defaultMeters(this);
Settings_defaultScreens(this);
this->hideKernelThreads = true; this->hideKernelThreads = true;
this->highlightMegabytes = true; this->highlightMegabytes = true;
this->highlightThreads = true; this->highlightThreads = true;
this->headerMargin = true; this->headerMargin = true;
} }
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
return this; return this;
} }
void ScreenSettings_invertSortOrder(ScreenSettings* this) { void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1) if (this->direction == 1)
this->direction = -1; this->direction = -1;
else else

View File

@ -20,32 +20,23 @@ typedef struct {
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings meterColumns[2]; MeterColumnSettings columns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount; int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
bool shadowOtherUsers; bool shadowOtherUsers;
@ -67,18 +58,12 @@ typedef struct Settings_ {
#endif #endif
/*
*/
void Settings_delete(Settings* this); void Settings_delete(Settings* this);
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line);
bool Settings_write(Settings* this); bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount); Settings* Settings_new(int cpuCount);
void ScreenSettings_invertSortOrder(ScreenSettings* this); void Settings_invertSortOrder(Settings* this);
#endif #endif

View File

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

View File

@ -86,7 +86,7 @@ void TraceScreen_draw(InfoScreen* this) {
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm); mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); IncSet_drawBar(this->inc);
} }
bool TraceScreen_forkTracer(TraceScreen* this) { bool TraceScreen_forkTracer(TraceScreen* this) {
@ -101,7 +101,7 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK); int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) { if (ok != -1) {
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-p", buffer, NULL); execlp("strace", "strace", "-s", "512", "-p", buffer, NULL);
} }
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; 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)); ssize_t written = write(this->fdpair[1], message, strlen(message));

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65) AC_PREREQ(2.65)
AC_INIT([htop],[3.0.0beta3],[hisham@gobolinux.org]) AC_INIT([htop],[2.2.0],[hisham@gobolinux.org])
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" 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") year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
@ -43,6 +43,9 @@ dragonfly*)
darwin*) darwin*)
my_htop_platform=darwin my_htop_platform=darwin
;; ;;
solaris*)
my_htop_platform=solaris
;;
*) *)
my_htop_platform=unsupported my_htop_platform=unsupported
;; ;;
@ -238,6 +241,12 @@ if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"]) AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi 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") 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 if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
AC_MSG_CHECKING([for usable sched_setaffinity]) AC_MSG_CHECKING([for usable sched_setaffinity])
@ -272,61 +281,19 @@ then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi fi
AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes") AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
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 if test "x$enable_delayacct" = xyes
then then
PKG_PROG_PKG_CONFIG() m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"]) PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"]) PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"])
LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.]) LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
], [
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 fi
@ -348,6 +315,7 @@ AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd]) AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd]) AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin]) 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]) AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform) AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1]) AC_CONFIG_FILES([Makefile htop.1])

View File

@ -18,6 +18,39 @@ in the source distribution for its full text.
#include <sys/mman.h> #include <sys/mman.h>
#include <utmpx.h> #include <utmpx.h>
#include <err.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 "ProcessList.h"
@ -171,8 +204,12 @@ void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting); DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl); DarwinProcess_setFromLibprocPidinfo(proc, dpl);
// Disabled due to bug in macOS High Sierra // Disabled for High Sierra due to bug in macOS High Sierra
// DarwinProcess_scanThreads(proc); bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);
if (isScanThreadSupported){
DarwinProcess_scanThreads(proc);
}
super->totalTasks += 1; super->totalTasks += 1;

View File

@ -25,11 +25,6 @@ in the source distribution for its full text.
#include "CPUMeter.h" #include "CPUMeter.h"
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "DarwinProcess.h" #include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
}*/ }*/
#ifndef CLAMP #ifndef CLAMP
@ -102,7 +97,7 @@ ProcessFieldData Process_fields[] = {
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, [100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
}; };
MeterClass* Platform_meterTypes[] = { MeterClass* Platform_meterTypes[] = {

View File

@ -16,10 +16,6 @@ in the source distribution for its full text.
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "DarwinProcess.h" #include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
#ifndef CLAMP #ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif

View File

@ -3,7 +3,7 @@
htop \- interactive process viewer htop \- interactive process viewer
.SH "SYNOPSIS" .SH "SYNOPSIS"
.LP .LP
.B htop [\fI\-dChusv\fR] .B htop [\fI\-dChustv\fR]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.LP .LP
Htop is a free (GPL) ncurses-based process viewer for Linux. Htop is a free (GPL) ncurses-based process viewer for Linux.
@ -41,6 +41,9 @@ Show only the processes of a given user
.TP .TP
\fB\-v \-\-version \fB\-v \-\-version
Output version information and exit Output version information and exit
.TP
\fB\-t \-\-tree
Show processes in tree view
.PP .PP
.br .br
.SH "INTERACTIVE COMMANDS" .SH "INTERACTIVE COMMANDS"

19
htop.c
View File

@ -42,6 +42,7 @@ static void printHelpFlag() {
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-h --help Print this help screen\n" "-h --help Print this help screen\n"
"-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n" "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
"-t --tree Show the tree view by default\n"
"-u --user=USERNAME Show only processes of a given user\n" "-u --user=USERNAME Show only processes of a given user\n"
"-p --pid=PID,[,PID,PID...] Show only the given PIDs\n" "-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
"-v --version Print version info\n" "-v --version Print version info\n"
@ -61,6 +62,7 @@ typedef struct CommandLineSettings_ {
int sortKey; int sortKey;
int delay; int delay;
bool useColors; bool useColors;
bool treeView;
} CommandLineSettings; } CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) { static CommandLineSettings parseArguments(int argc, char** argv) {
@ -71,6 +73,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.sortKey = 0, .sortKey = 0,
.delay = -1, .delay = -1,
.useColors = true, .useColors = true,
.treeView = false,
}; };
static struct option long_opts[] = static struct option long_opts[] =
@ -82,6 +85,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"user", required_argument, 0, 'u'}, {"user", required_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'}, {"no-color", no_argument, 0, 'C'},
{"no-colour",no_argument, 0, 'C'}, {"no-colour",no_argument, 0, 'C'},
{"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'}, {"pid", required_argument, 0, 'p'},
{"io", no_argument, 0, 'i'}, {"io", no_argument, 0, 'i'},
{0,0,0,0} {0,0,0,0}
@ -89,7 +93,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
int opt, opti=0; int opt, opti=0;
/* Parse arguments */ /* Parse arguments */
while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:i", long_opts, &opti))) { while ((opt = getopt_long(argc, argv, "hvCst::d:u:p:i", long_opts, &opti))) {
if (opt == EOF) break; if (opt == EOF) break;
switch (opt) { switch (opt) {
case 'h': case 'h':
@ -127,6 +131,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
case 'C': case 'C':
flags.useColors = false; flags.useColors = false;
break; break;
case 't':
flags.treeView = true;
break;
case 'p': { case 'p': {
char* argCopy = xStrdup(optarg); char* argCopy = xStrdup(optarg);
char* saveptr; char* saveptr;
@ -197,18 +204,20 @@ int main(int argc, char** argv) {
settings->delay = flags.delay; settings->delay = flags.delay;
if (!flags.useColors) if (!flags.useColors)
settings->colorScheme = COLORSCHEME_MONOCHROME; settings->colorScheme = COLORSCHEME_MONOCHROME;
if (flags.treeView)
settings->treeView = true;
CRT_init(settings->delay, settings->colorScheme); CRT_init(settings->delay, settings->colorScheme);
MainPanel* panel = MainPanel_new(); MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel); ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->screens[0]->treeView); MainPanel_updateTreeFunctions(panel, settings->treeView);
if (flags.sortKey > 0) { if (flags.sortKey > 0) {
settings->screens[0]->sortKey = flags.sortKey; settings->sortKey = flags.sortKey;
settings->screens[0]->treeView = false; settings->treeView = false;
settings->screens[0]->direction = 1; settings->direction = 1;
} }
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel)); ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));

View File

@ -18,24 +18,11 @@ in the source distribution for its full text.
/*{ /*{
#include "PerfCounter.h" #define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L #define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L #define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L #define PROCESS_FLAG_LINUX_OOM 0x1000
#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 { typedef enum UnsupportedProcessFields {
FLAGS = 9, FLAGS = 9,
@ -99,24 +86,14 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117, PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118, PERCENT_SWAP_DELAY = 118,
#endif #endif
#ifdef HAVE_PERFCOUNTERS LAST_PROCESSFIELD = 119,
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; } LinuxProcessField;
#include "IOPriority.h" #include "IOPriority.h"
typedef struct LinuxProcess_ { typedef struct LinuxProcess_ {
Process super; Process super;
bool isKernelThread;
IOPriority ioPriority; IOPriority ioPriority;
unsigned long int cminflt; unsigned long int cminflt;
unsigned long int cmajflt; unsigned long int cmajflt;
@ -163,29 +140,10 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent; float blkio_delay_percent;
float swapin_delay_percent; float swapin_delay_percent;
#endif #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; } LinuxProcess;
#ifndef Process_isKernelThread #ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0) #define Process_isKernelThread(_process) ((LinuxProcess*)(_process)->isKernelThread)
#endif #endif
#ifndef Process_isUserlandThread #ifndef Process_isUserlandThread
@ -273,20 +231,9 @@ ProcessFieldData Process_fields[] = {
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, }, [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, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPD% ", .description = "CPU delay %", .flags = 0, }, [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O 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 = "SWD% ", .description = "Swapin delay %", .flags = 0, }, [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .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 #endif
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
}; };
@ -327,10 +274,6 @@ void Process_delete(Object* cast) {
Process_done((Process*)cast); Process_done((Process*)cast);
#ifdef HAVE_CGROUP #ifdef HAVE_CGROUP
free(this->cgroup); free(this->cgroup);
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter_delete(this->cycleCounter);
PerfCounter_delete(this->insnCounter);
#endif #endif
free(this->ttyDevice); free(this->ttyDevice);
free(this); free(this);
@ -364,43 +307,14 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) {
return (LinuxProcess_updateIOPriority(this) == ioprio); return (LinuxProcess_updateIOPriority(this) == ioprio);
} }
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS #ifdef HAVE_DELAYACCT
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
static char* perfFmt[] = { if (delay_percent == -1LL) {
"%6.2f ", xSnprintf(buffer, n, " N/A ");
NULL, } else {
NULL, xSnprintf(buffer, n, "%4.1f ", delay_percent);
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, perfNA[len]);
*attr = CRT_colors[PROCESS_SHADOW];
}
} }
#endif #endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) { void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
@ -477,20 +391,9 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
break; break;
} }
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break; case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break;
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break; case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break;
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break; case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); 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 #endif
default: default:
Process_writeField((Process*)this, str, field); Process_writeField((Process*)this, str, field);
@ -499,12 +402,10 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
RichString_append(str, attr, buffer); 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) { long LinuxProcess_compare(const void* v1, const void* v2) {
LinuxProcess *p1, *p2; LinuxProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings; Settings *settings = ((Process*)v1)->settings;
if (settings->ss->direction == 1) { if (settings->direction == 1) {
p1 = (LinuxProcess*)v1; p1 = (LinuxProcess*)v1;
p2 = (LinuxProcess*)v2; p2 = (LinuxProcess*)v2;
} else { } else {
@ -512,7 +413,7 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
p1 = (LinuxProcess*)v2; p1 = (LinuxProcess*)v2;
} }
long long diff; long long diff;
switch ((int)settings->ss->sortKey) { switch ((int)settings->sortKey) {
case M_DRS: case M_DRS:
return (p2->m_drs - p1->m_drs); return (p2->m_drs - p1->m_drs);
case M_DT: case M_DT:
@ -556,20 +457,12 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
case OOM: case OOM:
return (p2->oom - p1->oom); return (p2->oom - p1->oom);
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: COMPARE_FIELD(cpu_delay_percent); case PERCENT_CPU_DELAY:
case PERCENT_IO_DELAY: COMPARE_FIELD(blkio_delay_percent); return (p2->cpu_delay_percent > p1->cpu_delay_percent ? 1 : -1);
case PERCENT_SWAP_DELAY: COMPARE_FIELD(swapin_delay_percent); case PERCENT_IO_DELAY:
#endif return (p2->blkio_delay_percent > p1->blkio_delay_percent ? 1 : -1);
#ifdef HAVE_PERFCOUNTERS case PERCENT_SWAP_DELAY:
case PERCENT_MISS: COMPARE_FIELD(pMiss); return (p2->swapin_delay_percent > p1->swapin_delay_percent ? 1 : -1);
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 #endif
case IO_PRIORITY: case IO_PRIORITY:
return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2); return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2);
@ -583,3 +476,4 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
bool Process_isThread(Process* this) { bool Process_isThread(Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this)); return (Process_isUserlandThread(this) || Process_isKernelThread(this));
} }

View File

@ -10,24 +10,11 @@ in the source distribution for its full text.
*/ */
#include "PerfCounter.h" #define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L #define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L #define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L #define PROCESS_FLAG_LINUX_OOM 0x1000
#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 { typedef enum UnsupportedProcessFields {
FLAGS = 9, FLAGS = 9,
@ -91,24 +78,14 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117, PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118, PERCENT_SWAP_DELAY = 118,
#endif #endif
#ifdef HAVE_PERFCOUNTERS LAST_PROCESSFIELD = 119,
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; } LinuxProcessField;
#include "IOPriority.h" #include "IOPriority.h"
typedef struct LinuxProcess_ { typedef struct LinuxProcess_ {
Process super; Process super;
bool isKernelThread;
IOPriority ioPriority; IOPriority ioPriority;
unsigned long int cminflt; unsigned long int cminflt;
unsigned long int cmajflt; unsigned long int cmajflt;
@ -155,29 +132,10 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent; float blkio_delay_percent;
float swapin_delay_percent; float swapin_delay_percent;
#endif #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; } LinuxProcess;
#ifndef Process_isKernelThread #ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0) #define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#endif #endif
#ifndef Process_isUserlandThread #ifndef Process_isUserlandThread
@ -209,16 +167,15 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio); bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio);
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS #ifdef HAVE_DELAYACCT
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n);
#endif #endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field); 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); long LinuxProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this); bool Process_isThread(Process* this);
#endif #endif

View File

@ -653,9 +653,9 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
} }
if (nl_send_sync(this->netlink_socket, msg) < 0) { if (nl_send_sync(this->netlink_socket, msg) < 0) {
process->swapin_delay_percent = -1; process->swapin_delay_percent = -1LL;
process->blkio_delay_percent = -1; process->blkio_delay_percent = -1LL;
process->cpu_delay_percent = -1; process->cpu_delay_percent = -1LL;
return; return;
} }
@ -666,77 +666,6 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
#endif #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) { static void setCommand(Process* process, const char* command, int len) {
if (process->comm && process->commLen >= len) { if (process->comm && process->commLen >= len) {
strncpy(process->comm, command, len + 1); strncpy(process->comm, command, len + 1);
@ -748,9 +677,6 @@ static void setCommand(Process* process, const char* command, int len) {
} }
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) { static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
if (Process_isKernelThread(process))
return true;
char filename[MAX_NAME+1]; char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name); xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
int fd = open(filename, O_RDONLY); int fd = open(filename, O_RDONLY);
@ -762,7 +688,10 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
close(fd); close(fd);
int tokenEnd = 0; int tokenEnd = 0;
int lastChar = 0; int lastChar = 0;
if (amtRead <= 0) { if (amtRead == 0) {
((LinuxProcess*)process)->isKernelThread = true;
return true;
} else if (amtRead < 0) {
return false; return false;
} }
for (int i = 0; i < amtRead; i++) { for (int i = 0; i < amtRead; i++) {
@ -832,7 +761,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
DIR* dir; DIR* dir;
struct dirent* entry; struct dirent* entry;
Settings* settings = pl->settings; Settings* settings = pl->settings;
ScreenSettings* ss = settings->ss;
time_t curTime = tv.tv_sec; time_t curTime = tv.tv_sec;
#ifdef HAVE_TASKSTATS #ifdef HAVE_TASKSTATS
@ -878,7 +806,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv); LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
#ifdef HAVE_TASKSTATS #ifdef HAVE_TASKSTATS
if (ss->flags & PROCESS_FLAG_IO) if (settings->flags & PROCESS_FLAG_IO)
LinuxProcessList_readIoFile(lp, dirname, name, now); LinuxProcessList_readIoFile(lp, dirname, name, now);
#endif #endif
@ -897,7 +825,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
free(lp->ttyDevice); free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr); lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
} }
if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO) if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
LinuxProcess_updateIOPriority(lp); LinuxProcess_updateIOPriority(lp);
float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0; float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0); proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
@ -912,13 +840,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid); proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) { if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
LinuxProcessList_readOpenVZData(lp, dirname, name); LinuxProcessList_readOpenVZData(lp, dirname, name);
} }
#endif #endif
#ifdef HAVE_VSERVER #ifdef HAVE_VSERVER
if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) { if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
LinuxProcessList_readVServerData(lp, dirname, name); LinuxProcessList_readVServerData(lp, dirname, name);
} }
#endif #endif
@ -941,18 +869,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
#endif #endif
#ifdef HAVE_CGROUP #ifdef HAVE_CGROUP
if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcessList_readCGroupFile(lp, dirname, name); LinuxProcessList_readCGroupFile(lp, dirname, name);
#endif #endif
if (ss->flags & PROCESS_FLAG_LINUX_OOM) if (settings->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcessList_readOomData(lp, dirname, name); 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)) { if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1; proc->basenameOffset = -1;
setCommand(proc, command, commLen); setCommand(proc, command, commLen);

View File

@ -121,54 +121,6 @@ void ProcessList_delete(ProcessList* pl);
#endif #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))
#endif
void ProcessList_goThroughEntries(ProcessList* super); void ProcessList_goThroughEntries(ProcessList* super);
#endif #endif

View File

@ -1,135 +0,0 @@
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include "PerfCounter.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "XAlloc.h"
/*{
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
}*/
int PerfCounter_openFds = 0;
static int PerfCounter_fdLimit = -1;
static void PerfCounter_initFdLimit() {
char name[100] = { 0 }; /* needs to fit the name /proc/xxxx/limits */
snprintf(name, sizeof(name) - 1, "/proc/%d/limits", getpid());
FILE* f = fopen(name, "r");
if (f) {
char line[100];
while (fgets(line, 100, f)) {
int n = sscanf(line, "Max open files %d", &PerfCounter_fdLimit);
if (n) {
break;
}
}
fclose(f);
}
PerfCounter_fdLimit -= 20; /* keep some slack */
if (PerfCounter_fdLimit == 0) { /* something went wrong */
PerfCounter_fdLimit = 200; /* reasonable default? */
}
}
static long perf_event_open(struct STRUCT_NAME *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
int ret = syscall(SYSCALL_NUM, hw_event, pid, cpu, group_fd, flags);
#if defined(__x86_64__) || defined(__i386__)
if (ret < 0 && ret > -4096) {
errno = -ret;
ret = -1;
}
#endif
return ret;
}
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config) {
if (PerfCounter_fdLimit == -1) {
PerfCounter_initFdLimit();
}
PerfCounter* this = xCalloc(sizeof(PerfCounter), 1);
this->pid = pid;
this->events.disabled = 0;
this->events.pinned = 1;
this->events.exclude_hv = 1;
this->events.exclude_kernel = 1;
this->events.type = type;
this->events.config = config;
if (PerfCounter_openFds < PerfCounter_fdLimit) {
this->fd = perf_event_open(&this->events, pid, -1, -1, 0);
} else {
this->fd = -1;
}
if (this->fd != -1) {
PerfCounter_openFds++;
}
return this;
}
void PerfCounter_delete(PerfCounter* this) {
if (!this) {
return;
}
if (this->fd != -1) {
PerfCounter_openFds--;
}
close(this->fd);
free(this);
}
bool PerfCounter_read(PerfCounter* this) {
if (this->fd == -1) {
return false;
}
uint64_t value;
int r = read(this->fd, &value, sizeof(value));
if (r != sizeof(value)) {
close(this->fd);
this->fd = perf_event_open(&this->events, this->pid, -1, -1, 0);
return false;
}
this->prevValue = this->value;
this->value = value;
return true;
}

View File

@ -1,54 +0,0 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_PerfCounter
#define HEADER_PerfCounter
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
extern int PerfCounter_openFds;
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config);
void PerfCounter_delete(PerfCounter* this);
bool PerfCounter_read(PerfCounter* this);
#endif

View File

@ -22,7 +22,6 @@ in the source distribution for its full text.
#include "ClockMeter.h" #include "ClockMeter.h"
#include "HostnameMeter.h" #include "HostnameMeter.h"
#include "LinuxProcess.h" #include "LinuxProcess.h"
#include "CRT.h"
#include <math.h> #include <math.h>
#include <assert.h> #include <assert.h>

View File

@ -7,10 +7,65 @@ in the source distribution for its full text.
*/ */
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include <sys/sysctl.h>
#include <sys/sensors.h>
#include <errno.h>
void Battery_getData(double* level, ACPresence* isOnAC) { static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) {
// TODO for (int devn = 0;; devn++) {
*level = -1; mib[2] = devn;
*isOnAC = AC_ERROR; if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) {
if (errno == ENXIO)
continue;
if (errno == ENOENT)
return false;
}
if (strcmp(name, snsrdev->xname) == 0) {
return true;
}
}
} }
void Battery_getData(double* level, ACPresence* isOnAC) {
static int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
struct sensor s;
size_t slen = sizeof(struct sensor);
struct sensordev snsrdev;
size_t sdlen = sizeof(struct sensordev);
bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen);
*level = -1;
if (found) {
/* last full capacity */
mib[3] = 7;
mib[4] = 0;
double last_full_capacity = 0;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
last_full_capacity = s.value;
}
if (last_full_capacity > 0) {
/* remaining capacity */
mib[3] = 7;
mib[4] = 3;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
double charge = s.value;
*level = 100*(charge / last_full_capacity);
if (charge >= last_full_capacity) {
*level = 100;
}
}
}
}
found = findDevice("acpiac0", mib, &snsrdev, &sdlen);
*isOnAC = AC_ERROR;
if (found) {
mib[3] = 9;
mib[4] = 0;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
*isOnAC = s.value;
}
}
}

View File

@ -1,23 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
import os, sys, string import os, sys, string, io
try: try:
from cStringIO import StringIO from StringIO import StringIO
except ImportError: except ImportError:
try: from io import StringIO
from StringIO import StringIO
except ImportError:
from io import StringIO
ANY=1 ANY=1
COPY=2 COPY=2
SKIP=3 SKIP=3
SKIPONE=4 SKIPONE=4
COPYDEFINE=5
state = ANY state = ANY
static = 0 static = 0
file = open(sys.argv[1]) file = io.open(sys.argv[1], "r", encoding="utf-8")
name = sys.argv[1][:-2] name = sys.argv[1][:-2]
out = StringIO() out = StringIO()
@ -50,11 +46,7 @@ for line in file.readlines():
elif len(line) > 1: elif len(line) > 1:
static = 0 static = 0
equals = line.find(" = ") equals = line.find(" = ")
if line[:7] == "#define": if line[-3:] == "= {":
if line[-1:] == "\\":
state = COPYDEFINE
out.write( line + "\n")
elif line[-3:] == "= {":
out.write( "extern " + line[:-4] + ";\n" ) out.write( "extern " + line[:-4] + ";\n" )
state = SKIP state = SKIP
elif equals != -1: elif equals != -1:
@ -65,7 +57,7 @@ for line in file.readlines():
out.write( line[:-2].replace("inline", "extern") + ";\n" ) out.write( line[:-2].replace("inline", "extern") + ";\n" )
state = SKIP state = SKIP
else: else:
out.write( line + "\n" ) out.write( line + "\n")
is_blank = False is_blank = False
elif line == "": elif line == "":
if not is_blank: if not is_blank:
@ -74,11 +66,6 @@ for line in file.readlines():
else: else:
out.write( line + "\n") out.write( line + "\n")
is_blank = False is_blank = False
elif state == COPYDEFINE:
is_blank = False
out.write( line + "\n")
if line[-1:] != "\\":
state = ANY
elif state == COPY: elif state == COPY:
is_blank = False is_blank = False
if line == "}*/": if line == "}*/":
@ -104,12 +91,12 @@ out.write( "#endif\n" )
# This prevents a lot of recompilation during development # This prevents a lot of recompilation during development
out.seek(0) out.seek(0)
try: try:
with open(name + ".h", "r") as orig: with io.open(name + ".h", "r", encoding="utf-8") as orig:
origcontents = orig.readlines() origcontents = orig.readlines()
except: except:
origcontents = "" origcontents = ""
if origcontents != out.readlines(): if origcontents != out.readlines():
with open(name + ".h", "w") as new: with io.open(name + ".h", "w", encoding="utf-8") as new:
print("Writing "+name+".h") print("Writing "+name+".h")
new.write(out.getvalue()) new.write(out.getvalue())
out.close() out.close()

8
solaris/Battery.c Normal file
View File

@ -0,0 +1,8 @@
#include "BatteryMeter.h"
void Battery_getData(double* level, ACPresence* isOnAC) {
*level = -1;
*isOnAC = AC_ERROR;
}

9
solaris/Battery.h Normal file
View File

@ -0,0 +1,9 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Battery
#define HEADER_Battery
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

257
solaris/Platform.c Normal file
View File

@ -0,0 +1,257 @@
/*
htop - solaris/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
#include "SolarisProcess.h"
#include "SolarisProcessList.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <utmpx.h>
#include <sys/loadavg.h>
#include <string.h>
#include <kstat.h>
#include <time.h>
#include <math.h>
#include <sys/var.h>
/*{
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
#include <signal.h>
#include <sys/mkdev.h>
#include <sys/proc.h>
#include <libproc.h>
#define kill(pid, signal) kill(pid / 1024, signal)
extern ProcessFieldData Process_fields[];
typedef struct var kvar_t;
typedef struct envAccum_ {
size_t capacity;
size_t size;
size_t bytes;
char *env;
} envAccum;
}*/
double plat_loadavg[3] = {0};
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
{ .name = " 3 SIGQUIT", .number = 3 },
{ .name = " 4 SIGILL", .number = 4 },
{ .name = " 5 SIGTRAP", .number = 5 },
{ .name = " 6 SIGABRT/IOT", .number = 6 },
{ .name = " 7 SIGEMT", .number = 7 },
{ .name = " 8 SIGFPE", .number = 8 },
{ .name = " 9 SIGKILL", .number = 9 },
{ .name = "10 SIGBUS", .number = 10 },
{ .name = "11 SIGSEGV", .number = 11 },
{ .name = "12 SIGSYS", .number = 12 },
{ .name = "13 SIGPIPE", .number = 13 },
{ .name = "14 SIGALRM", .number = 14 },
{ .name = "15 SIGTERM", .number = 15 },
{ .name = "16 SIGUSR1", .number = 16 },
{ .name = "17 SIGUSR2", .number = 17 },
{ .name = "18 SIGCHLD/CLD", .number = 18 },
{ .name = "19 SIGPWR", .number = 19 },
{ .name = "20 SIGWINCH", .number = 20 },
{ .name = "21 SIGURG", .number = 21 },
{ .name = "22 SIGPOLL/IO", .number = 22 },
{ .name = "23 SIGSTOP", .number = 23 },
{ .name = "24 SIGTSTP", .number = 24 },
{ .name = "25 SIGCONT", .number = 25 },
{ .name = "26 SIGTTIN", .number = 26 },
{ .name = "27 SIGTTOU", .number = 27 },
{ .name = "28 SIGVTALRM", .number = 28 },
{ .name = "29 SIGPROF", .number = 29 },
{ .name = "30 SIGXCPU", .number = 30 },
{ .name = "31 SIGXFSZ", .number = 31 },
{ .name = "32 SIGWAITING", .number = 32 },
{ .name = "33 SIGLWP", .number = 33 },
{ .name = "34 SIGFREEZE", .number = 34 },
{ .name = "35 SIGTHAW", .number = 35 },
{ .name = "36 SIGCANCEL", .number = 36 },
{ .name = "37 SIGLOST", .number = 37 },
{ .name = "38 SIGXRES", .number = 38 },
{ .name = "39 SIGJVM1", .number = 39 },
{ .name = "40 SIGJVM2", .number = 40 },
{ .name = "41 SIGINFO", .number = 41 },
};
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ProcessField Platform_defaultFields[] = { PID, LWPID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
MeterClass* Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
&BatteryMeter_class,
&HostnameMeter_class,
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
}
int Platform_numberOfFields = LAST_PROCESSFIELD;
extern char Process_pidFormat[20];
int Platform_getUptime() {
int boot_time = 0;
int curr_time = time(NULL);
struct utmpx * ent;
while (( ent = getutxent() )) {
if ( !strcmp("system boot", ent->ut_line )) {
boot_time = ent->ut_tv.tv_sec;
}
}
endutxent();
return (curr_time-boot_time);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
getloadavg( plat_loadavg, 3 );
*one = plat_loadavg[LOADAVG_1MIN];
*five = plat_loadavg[LOADAVG_5MIN];
*fifteen = plat_loadavg[LOADAVG_15MIN];
}
int Platform_getMaxPid() {
kstat_ctl_t *kc = NULL;
kstat_t *kshandle = NULL;
kvar_t *ksvar = NULL;
int vproc = 32778; // Reasonable Solaris default
kc = kstat_open();
if (kc != NULL) { kshandle = kstat_lookup(kc,"unix",0,"var"); }
if (kshandle != NULL) { kstat_read(kc,kshandle,NULL); }
ksvar = kshandle->ks_data;
if (ksvar->v_proc > 0 ) {
vproc = ksvar->v_proc;
}
if (kc != NULL) { kstat_close(kc); }
return vproc;
}
double Platform_setCPUValues(Meter* this, int cpu) {
SolarisProcessList* spl = (SolarisProcessList*) this->pl;
int cpus = this->pl->cpuCount;
CPUData* cpuData = NULL;
if (cpus == 1) {
// single CPU box has everything in spl->cpus[0]
cpuData = &(spl->cpus[0]);
} else {
cpuData = &(spl->cpus[cpu]);
}
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}
percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
this->values[1] = pl->buffersMem;
this->values[2] = pl->cachedMem;
}
void Platform_setSwapValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
static int Platform_buildenv(void *accum, struct ps_prochandle *Phandle, uintptr_t addr, const char *str) {
envAccum *accump = accum;
(void) Phandle;
(void) addr;
size_t thissz = strlen(str);
if ((thissz + 2) > (accump->capacity - accump->size))
accump->env = xRealloc(accump->env, accump->capacity *= 2);
if ((thissz + 2) > (accump->capacity - accump->size))
return 1;
strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size));
strncpy( accump->env + accump->size + thissz + 1, "\n", 1);
accump->size = accump->size + thissz + 1;
return 0;
}
char* Platform_getProcessEnv(pid_t pid) {
envAccum envBuilder;
pid_t realpid = pid / 1024;
int graberr;
struct ps_prochandle *Phandle;
if ((Phandle = Pgrab(realpid,PGRAB_RDONLY,&graberr)) == NULL)
return "Unable to read process environment.";
envBuilder.capacity = 4096;
envBuilder.size = 0;
envBuilder.env = xMalloc(envBuilder.capacity);
(void) Penv_iter(Phandle,Platform_buildenv,&envBuilder);
Prelease(Phandle, 0);
strncpy( envBuilder.env + envBuilder.size, "\0", 1);
return envBuilder.env;
}

65
solaris/Platform.h Normal file
View File

@ -0,0 +1,65 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Platform
#define HEADER_Platform
/*
htop - solaris/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
#include <signal.h>
#include <sys/mkdev.h>
#include <sys/proc.h>
#include <libproc.h>
#define kill(pid, signal) kill(pid / 1024, signal)
extern ProcessFieldData Process_fields[];
typedef struct var kvar_t;
typedef struct envAccum_ {
size_t capacity;
size_t size;
size_t bytes;
char *env;
} envAccum;
extern double plat_loadavg[3];
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ProcessField Platform_defaultFields[];
extern MeterClass* Platform_meterTypes[];
void Platform_setBindings(Htop_Action* keys);
extern int Platform_numberOfFields;
extern char Process_pidFormat[20];
int Platform_getUptime();
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid();
double Platform_setCPUValues(Meter* this, int cpu);
void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
#endif

32
solaris/SolarisCRT.c Normal file
View File

@ -0,0 +1,32 @@
/*
htop - SolarisCRT.c
(C) 2014 Hisham H. Muhammad
(C) 2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "CRT.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
void CRT_handleSIGSEGV(int sgn) {
(void) sgn;
CRT_done();
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n");
#ifdef HAVE_EXECINFO_H
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
fprintf(stderr, "\n Please include in your report the following backtrace: \n");
backtrace_symbols_fd(backtraceArray, size, 2);
fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
#endif
abort();
}

18
solaris/SolarisCRT.h Normal file
View File

@ -0,0 +1,18 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisCRT
#define HEADER_SolarisCRT
/*
htop - SolarisCRT.h
(C) 2014 Hisham H. Muhammad
(C) 2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_EXECINFO_H
#endif
void CRT_handleSIGSEGV(int sgn);
#endif

214
solaris/SolarisProcess.c Normal file
View File

@ -0,0 +1,214 @@
/*
htop - SolarisProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Process.h"
#include "ProcessList.h"
#include "SolarisProcess.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
/*{
#include "Settings.h"
#include <zone.h>
#include <sys/proc.h>
#include <libproc.h>
typedef enum SolarisProcessFields {
// Add platform-specific fields here, with ids >= 100
ZONEID = 100,
ZONE = 101,
PROJID = 102,
TASKID = 103,
POOLID = 104,
CONTID = 105,
LWPID = 106,
LAST_PROCESSFIELD = 107,
} SolarisProcessField;
typedef struct SolarisProcess_ {
Process super;
int kernel;
zoneid_t zoneid;
char* zname;
taskid_t taskid;
projid_t projid;
poolid_t poolid;
ctid_t contid;
bool is_lwp;
pid_t realpid;
pid_t realppid;
pid_t lwpid;
} SolarisProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
}*/
ProcessClass SolarisProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = SolarisProcess_compare
},
.writeField = (Process_WriteField) SolarisProcess_writeField,
};
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, O onproc, Z zombie, T stopped, W waiting)", .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, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[ZONEID] = { .name = "ZONEID", .title = " ZONEID ", .description = "Zone ID", .flags = 0, },
[ZONE] = { .name = "ZONE", .title = "ZONE ", .description = "Zone name", .flags = 0, },
[PROJID] = { .name = "PROJID", .title = " PRJID ", .description = "Project ID", .flags = 0, },
[TASKID] = { .name = "TASKID", .title = " TSKID ", .description = "Task ID", .flags = 0, },
[POOLID] = { .name = "POOLID", .title = " POLID ", .description = "Pool ID", .flags = 0, },
[CONTID] = { .name = "CONTID", .title = " CNTID ", .description = "Contract ID", .flags = 0, },
[LWPID] = { .name = "LWPID", .title = " LWPID ", .description = "LWP ID", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = ZONEID, .label = "ZONEID" },
{ .id = TASKID, .label = "TSKID" },
{ .id = PROJID, .label = "PRJID" },
{ .id = POOLID, .label = "POLID" },
{ .id = CONTID, .label = "CNTID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = LWPID, .label = "LWPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
SolarisProcess* SolarisProcess_new(Settings* settings) {
SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess));
Object_setClass(this, Class(SolarisProcess));
Process_init(&this->super, settings);
return this;
}
void Process_delete(Object* cast) {
SolarisProcess* sp = (SolarisProcess*) cast;
Process_done((Process*)cast);
free(sp->zname);
free(sp);
}
void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field) {
SolarisProcess* sp = (SolarisProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
// add Solaris-specific fields here
case ZONEID: xSnprintf(buffer, n, Process_pidFormat, sp->zoneid); break;
case PROJID: xSnprintf(buffer, n, Process_pidFormat, sp->projid); break;
case TASKID: xSnprintf(buffer, n, Process_pidFormat, sp->taskid); break;
case POOLID: xSnprintf(buffer, n, Process_pidFormat, sp->poolid); break;
case CONTID: xSnprintf(buffer, n, Process_pidFormat, sp->contid); break;
case ZONE:{
xSnprintf(buffer, n, "%-*s ", ZONENAME_MAX/4, sp->zname); break;
if (buffer[ZONENAME_MAX/4] != '\0') {
buffer[ZONENAME_MAX/4] = ' ';
buffer[(ZONENAME_MAX/4)+1] = '\0';
}
break;
}
case PID: xSnprintf(buffer, n, Process_pidFormat, sp->realpid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, sp->realppid); break;
case LWPID: xSnprintf(buffer, n, Process_pidFormat, sp->lwpid); break;
default:
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
}
long SolarisProcess_compare(const void* v1, const void* v2) {
SolarisProcess *p1, *p2;
Settings* settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
p1 = (SolarisProcess*)v1;
p2 = (SolarisProcess*)v2;
} else {
p2 = (SolarisProcess*)v1;
p1 = (SolarisProcess*)v2;
}
switch ((int) settings->sortKey) {
case ZONEID:
return (p1->zoneid - p2->zoneid);
case PROJID:
return (p1->projid - p2->projid);
case TASKID:
return (p1->taskid - p2->taskid);
case POOLID:
return (p1->poolid - p2->poolid);
case CONTID:
return (p1->contid - p2->contid);
case ZONE:
return strcmp(p1->zname ? p1->zname : "global", p2->zname ? p2->zname : "global");
case PID:
return (p1->realpid - p2->realpid);
case PPID:
return (p1->realppid - p2->realppid);
case LWPID:
return (p1->lwpid - p2->lwpid);
default:
return Process_compare(v1, v2);
}
}
bool Process_isThread(Process* this) {
SolarisProcess* fp = (SolarisProcess*) this;
if (fp->kernel == 1 ) {
return 1;
} else if (fp->is_lwp) {
return 1;
} else {
return 0;
}
}

72
solaris/SolarisProcess.h Normal file
View File

@ -0,0 +1,72 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisProcess
#define HEADER_SolarisProcess
/*
htop - SolarisProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Settings.h"
#include <zone.h>
#include <sys/proc.h>
#include <libproc.h>
typedef enum SolarisProcessFields {
// Add platform-specific fields here, with ids >= 100
ZONEID = 100,
ZONE = 101,
PROJID = 102,
TASKID = 103,
POOLID = 104,
CONTID = 105,
LWPID = 106,
LAST_PROCESSFIELD = 107,
} SolarisProcessField;
typedef struct SolarisProcess_ {
Process super;
int kernel;
zoneid_t zoneid;
char* zname;
taskid_t taskid;
projid_t projid;
poolid_t poolid;
ctid_t contid;
bool is_lwp;
pid_t realpid;
pid_t realppid;
pid_t lwpid;
} SolarisProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
extern ProcessClass SolarisProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
SolarisProcess* SolarisProcess_new(Settings* settings);
void Process_delete(Object* cast);
void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field);
long SolarisProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -0,0 +1,373 @@
/*
htop - SolarisProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
#include "SolarisProcess.h"
#include "SolarisProcessList.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/user.h>
#include <err.h>
#include <limits.h>
#include <string.h>
#include <procfs.h>
#include <errno.h>
#include <pwd.h>
#include <math.h>
#include <time.h>
#define MAXCMDLINE 255
/*{
#include <kstat.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/sysconf.h>
#include <sys/sysinfo.h>
#include <sys/swap.h>
#define ZONE_ERRMSGLEN 1024
char zone_errmsg[ZONE_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
uint64_t luser;
uint64_t lkrnl;
uint64_t lintr;
uint64_t lidle;
} CPUData;
typedef struct SolarisProcessList_ {
ProcessList super;
kstat_ctl_t* kd;
CPUData* cpus;
} SolarisProcessList;
}*/
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) {
char* zname;
if ( sproc->zoneid == 0 ) {
zname = xStrdup("global ");
} else if ( kd == NULL ) {
zname = xStrdup("unknown ");
} else {
kstat_t* ks = kstat_lookup( kd, "zones", sproc->zoneid, NULL );
zname = xStrdup(ks->ks_name);
}
return zname;
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList));
ProcessList* pl = (ProcessList*) spl;
ProcessList_init(pl, Class(SolarisProcess), usersTable, pidWhiteList, userId);
spl->kd = kstat_open();
pl->cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
if (pl->cpuCount == 1 ) {
spl->cpus = xRealloc(spl->cpus, sizeof(CPUData));
} else {
spl->cpus = xRealloc(spl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
return pl;
}
static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) {
const SolarisProcessList* spl = (SolarisProcessList*) pl;
int cpus = pl->cpuCount;
kstat_t *cpuinfo = NULL;
int kchain = 0;
kstat_named_t *idletime = NULL;
kstat_named_t *intrtime = NULL;
kstat_named_t *krnltime = NULL;
kstat_named_t *usertime = NULL;
double idlebuf = 0;
double intrbuf = 0;
double krnlbuf = 0;
double userbuf = 0;
uint64_t totaltime = 0;
int arrskip = 0;
assert(cpus > 0);
if (cpus > 1) {
// Store values for the stats loop one extra element up in the array
// to leave room for the average to be calculated afterwards
arrskip++;
}
// Calculate per-CPU statistics first
for (int i = 0; i < cpus; i++) {
if (spl->kd != NULL) { cpuinfo = kstat_lookup(spl->kd,"cpu",i,"sys"); }
if (cpuinfo != NULL) { kchain = kstat_read(spl->kd,cpuinfo,NULL); }
if (kchain != -1 ) {
idletime = kstat_data_lookup(cpuinfo,"cpu_nsec_idle");
intrtime = kstat_data_lookup(cpuinfo,"cpu_nsec_intr");
krnltime = kstat_data_lookup(cpuinfo,"cpu_nsec_kernel");
usertime = kstat_data_lookup(cpuinfo,"cpu_nsec_user");
}
assert( (idletime != NULL) && (intrtime != NULL)
&& (krnltime != NULL) && (usertime != NULL) );
CPUData* cpuData = &(spl->cpus[i+arrskip]);
totaltime = (idletime->value.ui64 - cpuData->lidle)
+ (intrtime->value.ui64 - cpuData->lintr)
+ (krnltime->value.ui64 - cpuData->lkrnl)
+ (usertime->value.ui64 - cpuData->luser);
// Calculate percentages of deltas since last reading
cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0;
cpuData->nicePercent = (double)0.0; // Not implemented on Solaris
cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0;
cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0;
cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent;
cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0;
// Store current values to use for the next round of deltas
cpuData->luser = usertime->value.ui64;
cpuData->lkrnl = krnltime->value.ui64;
cpuData->lintr = intrtime->value.ui64;
cpuData->lidle = idletime->value.ui64;
// Accumulate the current percentages into buffers for later average calculation
if (cpus > 1) {
userbuf += cpuData->userPercent;
krnlbuf += cpuData->systemPercent;
intrbuf += cpuData->irqPercent;
idlebuf += cpuData->idlePercent;
}
}
if (cpus > 1) {
CPUData* cpuData = &(spl->cpus[0]);
cpuData->userPercent = userbuf / cpus;
cpuData->nicePercent = (double)0.0; // Not implemented on Solaris
cpuData->systemPercent = krnlbuf / cpus;
cpuData->irqPercent = intrbuf / cpus;
cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent;
cpuData->idlePercent = idlebuf / cpus;
}
}
static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
kstat_t *meminfo = NULL;
int ksrphyserr = -1;
kstat_named_t *totalmem_pgs = NULL;
kstat_named_t *lockedmem_pgs = NULL;
kstat_named_t *pages = NULL;
struct swaptable *sl = NULL;
struct swapent *swapdev = NULL;
uint64_t totalswap = 0;
uint64_t totalfree = 0;
int nswap = 0;
char *spath = NULL;
char *spathbase = NULL;
// Part 1 - physical memory
if (spl->kd != NULL) { meminfo = kstat_lookup(spl->kd,"unix",0,"system_pages"); }
if (meminfo != NULL) { ksrphyserr = kstat_read(spl->kd,meminfo,NULL); }
if (ksrphyserr != -1) {
totalmem_pgs = kstat_data_lookup( meminfo, "physmem" );
lockedmem_pgs = kstat_data_lookup( meminfo, "pageslocked" );
pages = kstat_data_lookup( meminfo, "pagestotal" );
pl->totalMem = totalmem_pgs->value.ui64 * PAGE_SIZE_KB;
pl->usedMem = lockedmem_pgs->value.ui64 * PAGE_SIZE_KB;
// Not sure how to implement this on Solaris - suggestions welcome!
pl->cachedMem = 0;
// Not really "buffers" but the best Solaris analogue that I can find to
// "memory in use but not by programs or the kernel itself"
pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * PAGE_SIZE_KB;
} else {
// Fall back to basic sysconf if kstat isn't working
pl->totalMem = sysconf(_SC_PHYS_PAGES) * PAGE_SIZE;
pl->buffersMem = 0;
pl->cachedMem = 0;
pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * PAGE_SIZE);
}
// Part 2 - swap
nswap = swapctl(SC_GETNSWP, NULL);
if (nswap > 0) { sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); }
if (sl != NULL) { spathbase = xMalloc( nswap * MAXPATHLEN ); }
if (spathbase != NULL) {
spath = spathbase;
swapdev = sl->swt_ent;
for (int i = 0; i < nswap; i++, swapdev++) {
swapdev->ste_path = spath;
spath += MAXPATHLEN;
}
sl->swt_n = nswap;
}
nswap = swapctl(SC_LIST, sl);
if (nswap > 0) {
swapdev = sl->swt_ent;
for (int i = 0; i < nswap; i++, swapdev++) {
totalswap += swapdev->ste_pages;
totalfree += swapdev->ste_free;
}
}
free(spathbase);
free(sl);
pl->totalSwap = totalswap * PAGE_SIZE_KB;
pl->usedSwap = pl->totalSwap - (totalfree * PAGE_SIZE_KB);
}
void ProcessList_delete(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
ProcessList_done(pl);
free(spl->cpus);
if (spl->kd) kstat_close(spl->kd);
free(spl);
}
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
* system for more info.
*/
int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr) {
struct timeval tv;
struct tm date;
bool preExisting;
pid_t getpid;
// Setup process list
ProcessList *pl = (ProcessList*) listptr;
SolarisProcessList *spl = (SolarisProcessList*) listptr;
id_t lwpid_real = _lwpsinfo->pr_lwpid;
if (lwpid_real > 1023) return 0;
pid_t lwpid = (_psinfo->pr_pid * 1024) + lwpid_real;
bool onMasterLWP = (_lwpsinfo->pr_lwpid == _psinfo->pr_lwp.pr_lwpid);
if (onMasterLWP) {
getpid = _psinfo->pr_pid * 1024;
} else {
getpid = lwpid;
}
Process *proc = ProcessList_getProcess(pl, getpid, &preExisting, (Process_New) SolarisProcess_new);
SolarisProcess *sproc = (SolarisProcess*) proc;
gettimeofday(&tv, NULL);
// Common code pass 1
proc->show = false;
sproc->taskid = _psinfo->pr_taskid;
sproc->projid = _psinfo->pr_projid;
sproc->poolid = _psinfo->pr_poolid;
sproc->contid = _psinfo->pr_contract;
proc->priority = _lwpsinfo->pr_pri;
proc->nice = _lwpsinfo->pr_nice;
proc->processor = _lwpsinfo->pr_onpro;
proc->state = _lwpsinfo->pr_sname;
// NOTE: This 'percentage' is a 16-bit BINARY FRACTIONS where 1.0 = 0x8000
// Source: https://docs.oracle.com/cd/E19253-01/816-5174/proc-4/index.html
// (accessed on 18 November 2017)
proc->percent_mem = ((uint16_t)_psinfo->pr_pctmem/(double)32768)*(double)100.0;
proc->st_uid = _psinfo->pr_euid;
proc->pgrp = _psinfo->pr_pgid;
proc->nlwp = _psinfo->pr_nlwp;
proc->tty_nr = _psinfo->pr_ttydev;
proc->m_resident = _psinfo->pr_rssize/PAGE_SIZE_KB;
proc->m_size = _psinfo->pr_size/PAGE_SIZE_KB;
if (!preExisting) {
sproc->realpid = _psinfo->pr_pid;
sproc->lwpid = lwpid_real;
sproc->zoneid = _psinfo->pr_zoneid;
sproc->zname = SolarisProcessList_readZoneName(spl->kd,sproc);
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
proc->comm = xStrdup(_psinfo->pr_fname);
proc->commLen = strnlen(_psinfo->pr_fname,PRFNSZ);
}
// End common code pass 1
if (onMasterLWP) { // Are we on the representative LWP?
proc->ppid = (_psinfo->pr_ppid * 1024);
proc->tgid = (_psinfo->pr_ppid * 1024);
sproc->realppid = _psinfo->pr_ppid;
// See note above (in common section) about this BINARY FRACTION
proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu/(double)32768)*(double)100.0;
proc->time = _psinfo->pr_time.tv_sec;
if(!preExisting) { // Tasks done only for NEW processes
sproc->is_lwp = false;
proc->starttime_ctime = _psinfo->pr_start.tv_sec;
}
// Update proc and thread counts based on settings
if (sproc->kernel && !pl->settings->hideKernelThreads) {
pl->kernelThreads += proc->nlwp;
pl->totalTasks += proc->nlwp+1;
if (proc->state == 'O') pl->runningTasks++;
} else if (!sproc->kernel) {
if (proc->state == 'O') pl->runningTasks++;
if (pl->settings->hideUserlandThreads) {
pl->totalTasks++;
} else {
pl->userlandThreads += proc->nlwp;
pl->totalTasks += proc->nlwp+1;
}
}
proc->show = !(pl->settings->hideKernelThreads && sproc->kernel);
} else { // We are not in the master LWP, so jump to the LWP handling code
proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu/(double)32768)*(double)100.0;
proc->time = _lwpsinfo->pr_time.tv_sec;
if (!preExisting) { // Tasks done only for NEW LWPs
sproc->is_lwp = true;
proc->basenameOffset = -1;
proc->ppid = _psinfo->pr_pid * 1024;
proc->tgid = _psinfo->pr_pid * 1024;
sproc->realppid = _psinfo->pr_pid;
proc->starttime_ctime = _lwpsinfo->pr_start.tv_sec;
}
// Top-level process only gets this for the representative LWP
if (sproc->kernel && !pl->settings->hideKernelThreads) proc->show = true;
if (!sproc->kernel && !pl->settings->hideUserlandThreads) proc->show = true;
} // Top-level LWP or subordinate LWP
// Common code pass 2
if (!preExisting) {
if ((sproc->realppid <= 0) && !(sproc->realpid <= 1)) {
sproc->kernel = true;
} else {
sproc->kernel = false;
}
(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);
ProcessList_add(pl, proc);
}
proc->updated = true;
// End common code pass 2
return 0;
}
void ProcessList_goThroughEntries(ProcessList* this) {
SolarisProcessList_scanCPUTime(this);
SolarisProcessList_scanMemoryInfo(this);
this->kernelThreads = 1;
proc_walk(&SolarisProcessList_walkproc, this, PR_WALK_LWP);
}

View File

@ -0,0 +1,64 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisProcessList
#define HEADER_SolarisProcessList
/*
htop - SolarisProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define MAXCMDLINE 255
#include <kstat.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/sysconf.h>
#include <sys/sysinfo.h>
#include <sys/swap.h>
#define ZONE_ERRMSGLEN 1024
char zone_errmsg[ZONE_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
uint64_t luser;
uint64_t lkrnl;
uint64_t lintr;
uint64_t lidle;
} CPUData;
typedef struct SolarisProcessList_ {
ProcessList super;
kstat_ctl_t* kd;
CPUData* cpus;
} SolarisProcessList;
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc);
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
* system for more info.
*/
int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr);
void ProcessList_goThroughEntries(ProcessList* this);
#endif