51 Commits
3.2.0 ... main

Author SHA1 Message Date
eebd5e8517 add rpi model 3b+ to tested list 2022-06-10 20:19:39 +03:00
7044d546ea rename ui names for cpu freq/temp 2022-06-10 20:14:06 +03:00
765e3de1e7 fix temp meter duplication 2022-06-10 20:12:16 +03:00
637550ed51 disclaimer 2022-06-07 20:14:57 +03:00
fc016ae780 new screenshot 2022-06-07 22:36:05 +03:00
36260b5814 CPU Temperature and Frequency meter for Linux platform 2022-06-07 22:16:47 +03:00
611ea4606f Prevent null pointer dereference on early error
If a fatal error occurs before CRT_init has been called, CRT_done
dereferences NULL in CRT_colors.

Simply check if CRT_crashSettings is not NULL, which is eventually
set when CRT_init has been called.

If it is not set, then do not try to disable ncurses.

Proof of Concept (on Linux):

$ ./configure --with-proc=/var/empty
$ make
$ ./htop
2022-06-03 20:11:24 +02:00
4e6ec4a087 Update changelog in preparation for htop-3.2.1 release 2022-06-03 10:54:39 +10:00
abaec509e6 Merge branch 'cmdline-render-cache-rework' of BenBE/htop 2022-06-01 08:00:55 +02:00
f156dfecd5 Fix typo 2022-05-31 22:21:52 +02:00
2999fff88e Refactor code for rendering command line cache
Fixes #1008
2022-05-31 13:55:43 +02:00
2613db4b0d Merge branch 'fix-strip-exe-from-cmdline' of benbe/htop 2022-05-31 09:29:11 +02:00
9eed30949b Restore functionality of stripExeFromCmdline setting
This was accidentally lost in fbec3e4005
2022-05-30 22:38:59 +02:00
ce50095323 Merge branch 'fix-allBranchesCollapsed' of tanriol/htop 2022-05-30 10:37:19 +02:00
17e28d5264 actionExpandOrCollapseAllBranches: NOP in flat mode
This shortcut does not have any visible effect in flat mode, so disable
it completely to avoid possible confusion.
2022-05-30 10:52:12 +03:00
da97d2625a ProcessList_collapseAllBranches: actually build tree
As the loop checks `tree_depth`, a tree build is needed to ensure
they're filled in correctly. Note that this breaks the display list sort
order in case it's non-tree-based (either startup in flat mode, or `*`
hotkey in flat mode), so the display list will need to be sorted again.
2022-05-30 10:52:12 +03:00
7694dbc821 Implement PCP support for minimum ZFS ARC size 2022-05-30 07:50:57 +02:00
c0a9e92eea Implement FreeBSD support for minimum ZFS ARC size 2022-05-30 07:50:57 +02:00
491c6f1044 consider only shrinkable ZFS ARC as cache on Linux 2022-05-30 07:50:57 +02:00
98cbdc6dca Correct PROCESS_MAX_UID_DIGITS constant
The PROCESS_MAX_UID_DIGITS=19 introduced in
696f79fe50 doesn't make sense.
The `uid_t` type does not require to be signed in POSIX. If we are to
support 64 bits as the maximum size of `uid_t`, then
PROCESS_MAX_UID_DIGITS should be 20. (= floor(log10(UINT64_MAX)) + 1).

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-30 07:50:32 +02:00
9ed9d73ab5 Correct titleBuffer size and share it in alignedProcessFieldTitle()
* The size of titleBuffer should be 257 bytes, not 256.

* Remove redundant `static char titleBuffer[]` delarations within
  `alignedProcessFieldTitle()` and let the subroutine use one shared
  buffer for printing field title. This reduces code size.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-30 07:50:32 +02:00
999801464a Add some headers in the Setup -> Display options panel 2022-05-27 19:47:06 +02:00
0e29174211 Do not scan new processes for deleted libs 2022-05-26 15:38:24 +02:00
WHR
efe09a5e39 Fix process time scaling error on Solaris 2022-05-26 15:31:30 +02:00
038f2ae777 Linux: Increase field width of CPUD% and SWAPD% to 5
Title width of "CPUD%" and "SWAPD%" is 5 and there value cannot go
beyond "100.0%", so increase their field width to 5.

"IOD%" is similar to "MEM%" column, title width is 4 and maximum value
cannot go beyond "100.0%". So in case of "IOD%" column, there is no need
to increase title width to "5". "Process_printPercentage()" function
will handle the maximum value case, it will display value beyond "99.9%"
as "100" instead of "100.0".
2022-05-26 15:03:39 +02:00
0af08bcfc9 Process: Display single digit precision for CPU% greater than 99.9%
Since commit edf319e[1], we're dynamically adjusting column width of
"CPU%", showing single digit precision also for values greater than
"99.9%" makes "CPU%" column consistent with all other values.

[1]: edf319e53d

Change "Process_printPercentage()" function's logic to always display
value (i.e. "val") with single precision. Except when value is greater
than "99.9%" for columns like "MEM%", whose width is fixed to "4" and
value cannot go beyond "100%".

Credits: @Explorer09, thanks for the patch[2] to fix title alignment
         issue.

[2]: https://github.com/htop-dev/htop/pull/959#issuecomment-1092480951

Closes: #957
2022-05-26 15:03:39 +02:00
e053446cbd Fix typo, thx Explorer09 2022-05-21 10:24:42 +02:00
WHR
3d8fa0b926 Mark item separator in default color on help screen
Closes: #1014
2022-05-20 21:54:19 +02:00
d73cc70566 fix typo (dist -> disk)
This was changed in commit 37e01cbe33,
probably unintentional.
2022-05-20 12:58:12 +02:00
37e01cbe33 Colorize process state characters in help screen
Thanks to @Low-power for the idea
Closes #1010
2022-05-20 12:30:37 +02:00
WHR
d22667725a Call mousemask(3X) to truly enable or disable mouse control 2022-05-19 20:23:22 +02:00
ef4cbae5ea Minor code style update 2022-05-19 18:13:46 +02:00
44091705db Use of NULL in execlp() must have a pointer cast.
Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-19 18:42:44 +08:00
87793b8555 Merge branch 'lxc-cpu-count-fix' of fasterit/htop 2022-05-17 19:10:40 +02:00
fe7f238e2c Increasing niceness is also disabled by --readonly mode 2022-05-13 09:43:56 +02:00
c24681a078 Fix heap buffer overflow in Vector_compact
Fixes: #1006
2022-05-10 12:12:46 +02:00
2da8f71209 Merge branch 'fix_running_containerized_for_lxc' of ilyam8/htop 2022-05-09 12:21:15 +02:00
51228b6239 fix container detection for LXC 2022-05-08 21:46:53 +03:00
33973f7e40 Limit active CPU count under LXC as well
Fixes the initial htoprc not being configured to amount for the host CPU count
2022-05-07 16:05:11 +02:00
79db69c48d Fix Solaris / OmniOS build 2022-05-06 15:22:08 +02:00
9fc72c1e9c Ensure buffer for environment is large enough on Solaris 2022-05-06 14:35:50 +02:00
db93268968 Ensure buffer for environment is large enough on OpenBSD 2022-05-06 14:35:50 +02:00
4f1269cc9f Ensure buffer for environment is large enough on NetBSD 2022-05-06 14:35:50 +02:00
0388b30077 Hashtable: fix handling of NULL pointer in Hashtable_dump
This fixes an issus in Hashtable_dump where `"(nil"` is passed as an
argument to `%p` in fprintf. This prints the static address of `"(nil)"`
not "(nil)". This commit changes the code to just pass the NULL pointer
to fprintf, which will consistently print "0x0".
2022-05-06 06:34:17 +02:00
4b8b61fe18 ProcessList.h: remove ProcessList_remove
As the "highlight dying processes" option has to keep processes in the
list when they disappear, no code except the cleanup loop in
`ProcessList_scan` should remove processes from the list directly.
Remove the export to prevent random process removals from being
reintroduced accidentally.
2022-05-05 10:00:34 +02:00
fae7ff6f03 LinuxProcessList_recurseProcTree: keep on read error
If a process goes away while reading its fields, but we already have
that process in the list, we should keep it in case the "highlight dying
processes" mode is active. Not only is that expected in this mode, but
this should also ensure parents are in the list when their children are
(wanted for tree mode consistency).
2022-05-05 10:00:34 +02:00
e07fce7014 LinuxProcessList_recurseProcTree: open dirfd first
A process can die between reading the directory listing and opening the
directory FD (if HAVE_OPENAT) or /proc files (otherwise) for reading the
process data. This race would cause LinuxProcessList_recurseProcTree to
remove it from the list immediately, which is unexpected in the
"highlight dying processes" mode and can break the tree structure.
This patch closes this race in the HAVE_OPENAT case by only accessing
the process entry after the directory FD has been opened.
2022-05-05 10:00:34 +02:00
e08eec813c Remove redundant sscanf calls (in (s)scanf a blank validates _zero_ or more whitespace)
man sscanf(3):
A sequence of white-space characters (space, tab, newline, etc.; see isspace(3)).
This directive matches any amount of white space, including none, in the input.
2022-05-05 09:34:25 +02:00
549fcb6bb8 Always abort on overflow in String_cat
Not only in debug mode.
2022-05-05 09:19:14 +02:00
08166b27b1 ProcessList: fix quadratic process removal when scanning
This commit changes ProcessList_scan to lazily remove Processes by
index, which is known, instead of performing a brute-force search by
pid and immediately reclaiming the lost vector space via compaction.

Searching by pid is potentially quadratic in ProcessList_scan because
the process we are searching for is always at the back of the vector
(the scan starts from the back of the vector). Additionally, removal
via Vector_remove immediately reclaims space (by sliding elements
down).

With these changes process removal in ProcessList_scan is now linear.

Changes:
  * ProcessList: add new ProcessList_removeIndex function to remove
    by index
  * Vector: add Vector_softRemove and Vector_compact functions to
    support lazy removal/deletion of entries Vector_softRemove
    Vector_compact
  * Vector: replace Vector_count with Vector_countEquals since it only
    used for consistency assertions.
2022-05-05 09:17:51 +02:00
0d53245cf9 LXC: Limit CPU count to what is given in /proc/cpuinfo despite the container seeing the real host CPUs 2022-05-04 18:21:41 +02:00
58 changed files with 2493 additions and 1647 deletions

View File

@ -88,6 +88,7 @@ static void Action_runSetup(State* st) {
ScreenManager_run(scr, NULL, NULL, "Setup"); ScreenManager_run(scr, NULL, NULL, "Setup");
ScreenManager_delete(scr); ScreenManager_delete(scr);
if (st->settings->changed) { if (st->settings->changed) {
CRT_setMouse(st->settings->enableMouse);
Header_writeBackToSettings(st->header); Header_writeBackToSettings(st->header);
} }
} }
@ -241,6 +242,9 @@ static Htop_Reaction actionToggleTreeView(State* st) {
static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) { static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
ScreenSettings* ss = st->settings->ss; ScreenSettings* ss = st->settings->ss;
if (!ss->treeView) {
return HTOP_OK;
}
ss->allBranchesCollapsed = !ss->allBranchesCollapsed; ss->allBranchesCollapsed = !ss->allBranchesCollapsed;
if (ss->allBranchesCollapsed) if (ss->allBranchesCollapsed)
ProcessList_collapseAllBranches(st->pl); ProcessList_collapseAllBranches(st->pl);
@ -533,7 +537,7 @@ static const struct {
{ .key = " U: ", .roInactive = false, .info = "untag all processes" }, { .key = " U: ", .roInactive = false, .info = "untag all processes" },
{ .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" }, { .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" }, { .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" },
{ .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" }, { .key = " F8 [: ", .roInactive = true, .info = "lower priority (+ nice)" },
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)) #if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" }, { .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
#endif #endif
@ -570,46 +574,57 @@ static Htop_Reaction actionHelp(State* st) {
line++; line++;
mvaddstr(line++, 0, "CPU usage bar: "); mvaddstr(line++, 0, "CPU usage bar: ");
#define addbartext(attr, prefix, text) \
do { \
addattrstr(CRT_colors[DEFAULT_COLOR], prefix); \
addattrstr(attr, text); \
} while(0)
addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[BAR_BORDER], "[");
addbartext(CRT_colors[CPU_NICE_TEXT], "", "low");
addbartext(CRT_colors[CPU_NORMAL], "/", "normal");
addbartext(CRT_colors[CPU_SYSTEM], "/", "kernel");
if (st->settings->detailedCPUTime) { if (st->settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); addbartext(CRT_colors[CPU_IRQ], "/", "irq");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addbartext(CRT_colors[CPU_SOFTIRQ], "/", "soft-irq");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/"); addbartext(CRT_colors[CPU_STEAL], "/", "steal");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/"); addbartext(CRT_colors[CPU_GUEST], "/", "guest");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/"); addbartext(CRT_colors[CPU_IOWAIT], "/", "io-wait");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/"); addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
} else { } else {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/"); addbartext(CRT_colors[CPU_GUEST], "/", "guest");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "virtualized");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
} }
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Memory bar: "); mvaddstr(line++, 0, "Memory bar: ");
addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/"); addbartext(CRT_colors[MEMORY_USED], "", "used");
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/"); addbartext(CRT_colors[MEMORY_BUFFERS_TEXT], "/", "buffers");
addattrstr(CRT_colors[MEMORY_SHARED], "shared"); addstr("/"); addbartext(CRT_colors[MEMORY_SHARED], "/", "shared");
addattrstr(CRT_colors[MEMORY_CACHE], "cache"); addbartext(CRT_colors[MEMORY_CACHE], "/", "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total"); addbartext(CRT_colors[BAR_SHADOW], " ", "used");
addbartext(CRT_colors[BAR_SHADOW], "/", "total");
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Swap bar: "); mvaddstr(line++, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[SWAP], "used"); addbartext(CRT_colors[SWAP], "", "used");
#ifdef HTOP_LINUX #ifdef HTOP_LINUX
addstr("/"); addbartext(CRT_colors[SWAP_CACHE], "/", "cache");
addattrstr(CRT_colors[SWAP_CACHE], "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
#else #else
addattrstr(CRT_colors[BAR_SHADOW], " used/total"); addbartext(CRT_colors[SWAP_CACHE], " ", "");
#endif #endif
addbartext(CRT_colors[BAR_SHADOW], " ", "used");
addbartext(CRT_colors[BAR_SHADOW], "/", "total");
addattrstr(CRT_colors[BAR_BORDER], "]"); addattrstr(CRT_colors[BAR_BORDER], "]");
line++;
#undef addbartext
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen."); mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
@ -617,9 +632,23 @@ static Htop_Reaction actionHelp(State* st) {
} }
line++; line++;
mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep"); #define addattrstatestr(attr, state, desc) \
do { \
addattrstr(attr, state); \
addattrstr(CRT_colors[DEFAULT_COLOR], ": " desc); \
} while(0)
line++; mvaddstr(line, 0, "Process state: ");
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "R", "running; ");
addattrstatestr(CRT_colors[PROCESS_SHADOW], "S", "sleeping; ");
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "t", "traced/stopped; ");
addattrstatestr(CRT_colors[PROCESS_D_STATE], "Z", "zombie; ");
addattrstatestr(CRT_colors[PROCESS_D_STATE], "D", "disk sleep");
attrset(CRT_colors[DEFAULT_COLOR]);
#undef addattrstatestr
line += 2;
const bool readonly = Settings_isReadonly(); const bool readonly = Settings_isReadonly();

View File

@ -79,6 +79,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
} }
if (update) { if (update) {
this->settings->changed = true; this->settings->changed = true;
this->settings->lastUpdate++;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_updateData(header); Header_updateData(header);
Header_draw(header); Header_draw(header);

349
CRT.c
View File

@ -27,21 +27,20 @@ in the source distribution for its full text.
#endif #endif
#if defined(HAVE_LIBUNWIND_H) && defined(HAVE_LIBUNWIND) #if defined(HAVE_LIBUNWIND_H) && defined(HAVE_LIBUNWIND)
# define PRINT_BACKTRACE #define PRINT_BACKTRACE
# define UNW_LOCAL_ONLY #define UNW_LOCAL_ONLY
# include <libunwind.h> #include <libunwind.h>
# if defined(HAVE_DLADDR) #if defined(HAVE_DLADDR)
# include <dlfcn.h> #include <dlfcn.h>
# endif #endif
#elif defined(HAVE_EXECINFO_H) #elif defined(HAVE_EXECINFO_H)
# define PRINT_BACKTRACE #define PRINT_BACKTRACE
# include <execinfo.h> #include <execinfo.h>
#endif #endif
#define ColorIndex(i, j) ((7 - (i)) * 8 + (j))
#define ColorIndex(i,j) ((7-(i))*8+(j)) #define ColorPair(i, j) COLOR_PAIR(ColorIndex(i, j))
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
#define Black COLOR_BLACK #define Black COLOR_BLACK
#define Red COLOR_RED #define Red COLOR_RED
@ -52,13 +51,13 @@ in the source distribution for its full text.
#define Cyan COLOR_CYAN #define Cyan COLOR_CYAN
#define White COLOR_WHITE #define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta) #define ColorPairGrayBlack ColorPair(Magenta, Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta) #define ColorIndexGrayBlack ColorIndex(Magenta, Magenta)
#define ColorPairWhiteDefault ColorPair(Red, Red) #define ColorPairWhiteDefault ColorPair(Red, Red)
#define ColorIndexWhiteDefault ColorIndex(Red, Red) #define ColorIndexWhiteDefault ColorIndex(Red, Red)
static const char* const CRT_treeStrAscii[LAST_TREE_STR] = { static const char *const CRT_treeStrAscii[LAST_TREE_STR] = {
[TREE_STR_VERT] = "|", [TREE_STR_VERT] = "|",
[TREE_STR_RTEE] = "`", [TREE_STR_RTEE] = "`",
[TREE_STR_BEND] = "`", [TREE_STR_BEND] = "`",
@ -71,7 +70,7 @@ static const char* const CRT_treeStrAscii[LAST_TREE_STR] = {
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static const char* const CRT_treeStrUtf8[LAST_TREE_STR] = { static const char *const CRT_treeStrUtf8[LAST_TREE_STR] = {
[TREE_STR_VERT] = "\xe2\x94\x82", // │ [TREE_STR_VERT] = "\xe2\x94\x82", // │
[TREE_STR_RTEE] = "\xe2\x94\x9c", // ├ [TREE_STR_RTEE] = "\xe2\x94\x9c", // ├
[TREE_STR_BEND] = "\xe2\x94\x94", // └ [TREE_STR_BEND] = "\xe2\x94\x94", // └
@ -88,14 +87,15 @@ bool CRT_utf8 = false;
#endif #endif
const char* const* CRT_treeStr = CRT_treeStrAscii; const char *const *CRT_treeStr = CRT_treeStrAscii;
static const Settings* CRT_crashSettings; static const Settings *CRT_crashSettings;
static const int* CRT_delay; static const int *CRT_delay;
const char* CRT_degreeSign; const char *CRT_degreeSign;
static const char* initDegreeSign(void) { static const char *initDegreeSign(void)
{
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
if (CRT_utf8) if (CRT_utf8)
return "\xc2\xb0"; return "\xc2\xb0";
@ -110,7 +110,7 @@ static const char* initDegreeSign(void) {
return ""; return "";
} }
const int* CRT_colors; const int *CRT_colors;
static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[COLORSCHEME_DEFAULT] = { [COLORSCHEME_DEFAULT] = {
@ -127,6 +127,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD | ColorPair(Red, Black), [FAILED_READ] = A_BOLD | ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = A_BOLD | ColorPair(Cyan, Black), [UPTIME] = A_BOLD | ColorPair(Cyan, Black),
[TEMP] = A_BOLD | ColorPair(Cyan, Black),
[FREQ] = A_BOLD | ColorPair(Cyan, Black),
[BATTERY] = A_BOLD | ColorPair(Cyan, Black), [BATTERY] = A_BOLD | ColorPair(Cyan, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack, [METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
@ -234,6 +236,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD, [FAILED_READ] = A_BOLD,
[PAUSED] = A_BOLD | A_REVERSE, [PAUSED] = A_BOLD | A_REVERSE,
[UPTIME] = A_BOLD, [UPTIME] = A_BOLD,
[TEMP] = A_BOLD,
[FREQ] = A_BOLD,
[BATTERY] = A_BOLD, [BATTERY] = A_BOLD,
[LARGE_NUMBER] = A_BOLD, [LARGE_NUMBER] = A_BOLD,
[METER_SHADOW] = A_DIM, [METER_SHADOW] = A_DIM,
@ -341,6 +345,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = ColorPair(Red, White), [FAILED_READ] = ColorPair(Red, White),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = ColorPair(Yellow, White), [UPTIME] = ColorPair(Yellow, White),
[TEMP] = ColorPair(Yellow, White),
[FREQ] = ColorPair(Yellow, White),
[BATTERY] = ColorPair(Yellow, White), [BATTERY] = ColorPair(Yellow, White),
[LARGE_NUMBER] = ColorPair(Red, White), [LARGE_NUMBER] = ColorPair(Red, White),
[METER_SHADOW] = ColorPair(Blue, White), [METER_SHADOW] = ColorPair(Blue, White),
@ -408,11 +414,11 @@ static 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), [PANEL_EDIT] = ColorPair(White, Blue),
[SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black,White), [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black, White),
[SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black,White), [SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black, White),
[SCREENS_CUR_BORDER] = ColorPair(Green,Green), [SCREENS_CUR_BORDER] = ColorPair(Green, Green),
[SCREENS_CUR_TEXT] = ColorPair(Black,Green), [SCREENS_CUR_TEXT] = ColorPair(Black, Green),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White),
[PRESSURE_STALL_SIXTY] = ColorPair(Black, White), [PRESSURE_STALL_SIXTY] = ColorPair(Black, White),
[PRESSURE_STALL_TEN] = ColorPair(Black, White), [PRESSURE_STALL_TEN] = ColorPair(Black, White),
@ -448,6 +454,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = ColorPair(Red, Black), [FAILED_READ] = ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = ColorPair(Yellow, Black), [UPTIME] = ColorPair(Yellow, Black),
[TEMP] = ColorPair(Yellow, Black),
[FREQ] = ColorPair(Yellow, Black),
[BATTERY] = ColorPair(Yellow, Black), [BATTERY] = ColorPair(Yellow, Black),
[LARGE_NUMBER] = ColorPair(Red, Black), [LARGE_NUMBER] = ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack, [METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
@ -515,11 +523,11 @@ static 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), [PANEL_EDIT] = ColorPair(White, Blue),
[SCREENS_OTH_BORDER] = ColorPair(Blue,Black), [SCREENS_OTH_BORDER] = ColorPair(Blue, Black),
[SCREENS_OTH_TEXT] = ColorPair(Blue,Black), [SCREENS_OTH_TEXT] = ColorPair(Blue, Black),
[SCREENS_CUR_BORDER] = ColorPair(Green,Green), [SCREENS_CUR_BORDER] = ColorPair(Green, Green),
[SCREENS_CUR_TEXT] = ColorPair(Black,Green), [SCREENS_CUR_TEXT] = ColorPair(Black, Green),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Black, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Black, Black),
[PRESSURE_STALL_TEN] = ColorPair(Black, Black), [PRESSURE_STALL_TEN] = ColorPair(Black, Black),
@ -555,6 +563,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD | ColorPair(Red, Blue), [FAILED_READ] = A_BOLD | ColorPair(Red, Blue),
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue), [UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
[TEMP] = A_BOLD | ColorPair(Yellow, Blue),
[FREQ] = A_BOLD | ColorPair(Yellow, Blue),
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue), [BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
[METER_SHADOW] = ColorPair(Cyan, Blue), [METER_SHADOW] = ColorPair(Cyan, Blue),
@ -622,11 +632,11 @@ static 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), [PANEL_EDIT] = ColorPair(White, Blue),
[SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow,Blue), [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
[SCREENS_OTH_TEXT] = ColorPair(Cyan,Blue), [SCREENS_OTH_TEXT] = ColorPair(Cyan, Blue),
[SCREENS_CUR_BORDER] = ColorPair(Cyan,Cyan), [SCREENS_CUR_BORDER] = ColorPair(Cyan, Cyan),
[SCREENS_CUR_TEXT] = ColorPair(Black,Cyan), [SCREENS_CUR_TEXT] = ColorPair(Black, Cyan),
[PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue), [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue),
[PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue), [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue),
@ -662,6 +672,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[FAILED_READ] = A_BOLD | ColorPair(Red, Black), [FAILED_READ] = A_BOLD | ColorPair(Red, Black),
[PAUSED] = A_BOLD | ColorPair(Yellow, Green), [PAUSED] = A_BOLD | ColorPair(Yellow, Green),
[UPTIME] = ColorPair(Green, Black), [UPTIME] = ColorPair(Green, Black),
[TEMP] = ColorPair(Green, Black),
[FREQ] = ColorPair(Green, Black),
[BATTERY] = ColorPair(Green, Black), [BATTERY] = ColorPair(Green, Black),
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack, [METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
@ -727,11 +739,11 @@ static 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), [PANEL_EDIT] = ColorPair(White, Cyan),
[SCREENS_OTH_BORDER] = ColorPair(White,Black), [SCREENS_OTH_BORDER] = ColorPair(White, Black),
[SCREENS_OTH_TEXT] = ColorPair(Cyan,Black), [SCREENS_OTH_TEXT] = ColorPair(Cyan, Black),
[SCREENS_CUR_BORDER] = A_BOLD | ColorPair(White,Black), [SCREENS_CUR_BORDER] = A_BOLD | ColorPair(White, Black),
[SCREENS_CUR_TEXT] = A_BOLD | ColorPair(Green,Black), [SCREENS_CUR_TEXT] = A_BOLD | ColorPair(Green, Black),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Green, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Green, Black),
[PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black),
@ -753,7 +765,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black), [DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
[DYNAMIC_WHITE] = ColorPair(White, Black), [DYNAMIC_WHITE] = ColorPair(White, Black),
}, },
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. [COLORSCHEME_BROKENGRAY] = {0} // dynamically generated.
}; };
int CRT_scrollHAmount = 5; int CRT_scrollHAmount = 5;
@ -763,7 +775,8 @@ int CRT_scrollWheelVAmount = 10;
ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT; ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
ATTR_NORETURN ATTR_NORETURN
static void CRT_handleSIGTERM(ATTR_UNUSED int sgn) { static void CRT_handleSIGTERM(ATTR_UNUSED int sgn)
{
CRT_done(); CRT_done();
_exit(0); _exit(0);
} }
@ -773,7 +786,8 @@ static void CRT_handleSIGTERM(ATTR_UNUSED int sgn) {
static int stderrRedirectNewFd = -1; static int stderrRedirectNewFd = -1;
static int stderrRedirectBackupFd = -1; static int stderrRedirectBackupFd = -1;
static int createStderrCacheFile(void) { static int createStderrCacheFile(void)
{
#if defined(HAVE_MEMFD_CREATE) #if defined(HAVE_MEMFD_CREATE)
return memfd_create("htop.stderr-redirect", 0); return memfd_create("htop.stderr-redirect", 0);
#elif defined(O_TMPFILE) #elif defined(O_TMPFILE)
@ -786,15 +800,17 @@ static int createStderrCacheFile(void) {
if (r < 0) if (r < 0)
return r; return r;
(void) unlink(tmpName); (void)unlink(tmpName);
return r; return r;
#endif /* HAVE_MEMFD_CREATE */ #endif /* HAVE_MEMFD_CREATE */
} }
static void redirectStderr(void) { static void redirectStderr(void)
{
stderrRedirectNewFd = createStderrCacheFile(); stderrRedirectNewFd = createStderrCacheFile();
if (stderrRedirectNewFd < 0) { if (stderrRedirectNewFd < 0)
{
/* ignore failure */ /* ignore failure */
return; return;
} }
@ -803,7 +819,8 @@ static void redirectStderr(void) {
dup2(stderrRedirectNewFd, STDERR_FILENO); dup2(stderrRedirectNewFd, STDERR_FILENO);
} }
static void dumpStderr(void) { static void dumpStderr(void)
{
if (stderrRedirectNewFd < 0) if (stderrRedirectNewFd < 0)
return; return;
@ -815,26 +832,31 @@ static void dumpStderr(void) {
bool header = false; bool header = false;
char buffer[8192]; char buffer[8192];
for (;;) { for (;;)
{
errno = 0; errno = 0;
ssize_t res = read(stderrRedirectNewFd, buffer, sizeof(buffer)); ssize_t res = read(stderrRedirectNewFd, buffer, sizeof(buffer));
if (res < 0) { if (res < 0)
{
if (errno == EINTR) if (errno == EINTR)
continue; continue;
break; break;
} }
if (res == 0) { if (res == 0)
{
break; break;
} }
if (res > 0) { if (res > 0)
if (!header) { {
if (!header)
{
fprintf(stderr, ">>>>>>>>>> stderr output >>>>>>>>>>\n"); fprintf(stderr, ">>>>>>>>>> stderr output >>>>>>>>>>\n");
header = true; header = true;
} }
(void)! write(STDERR_FILENO, buffer, res); (void)!write(STDERR_FILENO, buffer, res);
} }
} }
@ -845,7 +867,8 @@ static void dumpStderr(void) {
stderrRedirectNewFd = -1; stderrRedirectNewFd = -1;
} }
void CRT_debug_impl(const char* file, size_t lineno, const char* func, const char* fmt, ...) { void CRT_debug_impl(const char *file, size_t lineno, const char *func, const char *fmt, ...)
{
va_list args; va_list args;
fprintf(stderr, "[%s:%zu (%s)]: ", file, lineno, func); fprintf(stderr, "[%s:%zu (%s)]: ", file, lineno, func);
@ -857,28 +880,31 @@ void CRT_debug_impl(const char* file, size_t lineno, const char* func, const cha
#else /* !NDEBUG */ #else /* !NDEBUG */
static void redirectStderr(void) { static void redirectStderr(void)
{
} }
static void dumpStderr(void) { static void dumpStderr(void)
{
} }
#endif /* !NDEBUG */ #endif /* !NDEBUG */
static struct sigaction old_sig_handler[32]; static struct sigaction old_sig_handler[32];
static void CRT_installSignalHandlers(void) { static void CRT_installSignalHandlers(void)
{
struct sigaction act; struct sigaction act;
sigemptyset (&act.sa_mask); sigemptyset(&act.sa_mask);
act.sa_flags = (int)SA_RESETHAND | SA_NODEFER; act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
act.sa_handler = CRT_handleSIGSEGV; act.sa_handler = CRT_handleSIGSEGV;
sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]); sigaction(SIGSEGV, &act, &old_sig_handler[SIGSEGV]);
sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]); sigaction(SIGFPE, &act, &old_sig_handler[SIGFPE]);
sigaction (SIGILL, &act, &old_sig_handler[SIGILL]); sigaction(SIGILL, &act, &old_sig_handler[SIGILL]);
sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]); sigaction(SIGBUS, &act, &old_sig_handler[SIGBUS]);
sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]); sigaction(SIGPIPE, &act, &old_sig_handler[SIGPIPE]);
sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]); sigaction(SIGSYS, &act, &old_sig_handler[SIGSYS]);
sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]); sigaction(SIGABRT, &act, &old_sig_handler[SIGABRT]);
signal(SIGCHLD, SIG_DFL); signal(SIGCHLD, SIG_DFL);
signal(SIGINT, CRT_handleSIGTERM); signal(SIGINT, CRT_handleSIGTERM);
@ -886,21 +912,41 @@ static void CRT_installSignalHandlers(void) {
signal(SIGQUIT, CRT_handleSIGTERM); signal(SIGQUIT, CRT_handleSIGTERM);
} }
void CRT_resetSignalHandlers(void) { void CRT_resetSignalHandlers(void)
sigaction (SIGSEGV, &old_sig_handler[SIGSEGV], NULL); {
sigaction (SIGFPE, &old_sig_handler[SIGFPE], NULL); sigaction(SIGSEGV, &old_sig_handler[SIGSEGV], NULL);
sigaction (SIGILL, &old_sig_handler[SIGILL], NULL); sigaction(SIGFPE, &old_sig_handler[SIGFPE], NULL);
sigaction (SIGBUS, &old_sig_handler[SIGBUS], NULL); sigaction(SIGILL, &old_sig_handler[SIGILL], NULL);
sigaction (SIGPIPE, &old_sig_handler[SIGPIPE], NULL); sigaction(SIGBUS, &old_sig_handler[SIGBUS], NULL);
sigaction (SIGSYS, &old_sig_handler[SIGSYS], NULL); sigaction(SIGPIPE, &old_sig_handler[SIGPIPE], NULL);
sigaction (SIGABRT, &old_sig_handler[SIGABRT], NULL); sigaction(SIGSYS, &old_sig_handler[SIGSYS], NULL);
sigaction(SIGABRT, &old_sig_handler[SIGABRT], NULL);
signal(SIGINT, SIG_DFL); signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL); signal(SIGTERM, SIG_DFL);
signal(SIGQUIT, SIG_DFL); signal(SIGQUIT, SIG_DFL);
} }
void CRT_init(const Settings* settings, bool allowUnicode) { void CRT_setMouse(bool enabled)
{
#ifdef HAVE_GETMOUSE
if (enabled)
{
#if NCURSES_MOUSE_VERSION > 1
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
#else
mousemask(BUTTON1_RELEASED, NULL);
#endif
}
else
{
mousemask(0, NULL);
}
#endif
}
void CRT_init(const Settings *settings, bool allowUnicode)
{
redirectStderr(); redirectStderr();
initscr(); initscr();
@ -910,7 +956,8 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
CRT_colors = CRT_colorSchemes[settings->colorScheme]; CRT_colors = CRT_colorSchemes[settings->colorScheme];
CRT_colorScheme = settings->colorScheme; CRT_colorScheme = settings->colorScheme;
for (int i = 0; i < LAST_COLORELEMENT; i++) { for (int i = 0; i < LAST_COLORELEMENT; i++)
{
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i]; unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White, Black) : color; CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White, Black) : color;
} }
@ -924,21 +971,26 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
#endif #endif
curs_set(0); curs_set(0);
if (has_colors()) { if (has_colors())
{
start_color(); start_color();
} }
const char* termType = getenv("TERM"); const char *termType = getenv("TERM");
if (termType && String_eq(termType, "linux")) { if (termType && String_eq(termType, "linux"))
{
CRT_scrollHAmount = 20; CRT_scrollHAmount = 20;
} else { }
else
{
CRT_scrollHAmount = 5; CRT_scrollHAmount = 5;
} }
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) { if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220")))
{
#ifdef HTOP_NETBSD #ifdef HTOP_NETBSD
#define define_key(s_, k_) define_key((char*)s_, k_) #define define_key(s_, k_) define_key((char *)s_, k_)
IGNORE_WCASTQUAL_BEGIN IGNORE_WCASTQUAL_BEGIN
#endif #endif
define_key("\033[H", KEY_HOME); define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END); define_key("\033[F", KEY_END);
@ -957,16 +1009,18 @@ IGNORE_WCASTQUAL_BEGIN
define_key("\033[17;2~", KEY_F(18)); define_key("\033[17;2~", KEY_F(18));
define_key("\033[Z", KEY_SHIFT_TAB); define_key("\033[Z", KEY_SHIFT_TAB);
char sequence[3] = "\033a"; char sequence[3] = "\033a";
for (char c = 'a'; c <= 'z'; c++) { for (char c = 'a'; c <= 'z'; c++)
{
sequence[1] = c; sequence[1] = c;
define_key(sequence, KEY_ALT('A' + (c - 'a'))); define_key(sequence, KEY_ALT('A' + (c - 'a')));
} }
#ifdef HTOP_NETBSD #ifdef HTOP_NETBSD
IGNORE_WCASTQUAL_END IGNORE_WCASTQUAL_END
#undef define_key #undef define_key
#endif #endif
} }
if (termType && (String_startsWith(termType, "rxvt"))) { if (termType && (String_startsWith(termType, "rxvt")))
{
define_key("\033[Z", KEY_SHIFT_TAB); define_key("\033[Z", KEY_SHIFT_TAB);
} }
@ -978,13 +1032,16 @@ IGNORE_WCASTQUAL_END
CRT_setColors(CRT_colorScheme); CRT_setColors(CRT_colorScheme);
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
if (allowUnicode && String_eq(nl_langinfo(CODESET), "UTF-8")) { if (allowUnicode && String_eq(nl_langinfo(CODESET), "UTF-8"))
{
CRT_utf8 = true; CRT_utf8 = true;
} else { }
else
{
CRT_utf8 = false; CRT_utf8 = false;
} }
#else #else
(void) allowUnicode; (void)allowUnicode;
#endif #endif
CRT_treeStr = CRT_treeStr =
@ -993,21 +1050,18 @@ IGNORE_WCASTQUAL_END
#endif #endif
CRT_treeStrAscii; CRT_treeStrAscii;
#ifdef HAVE_GETMOUSE CRT_setMouse(settings->enableMouse);
#if NCURSES_MOUSE_VERSION > 1
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
#else
mousemask(BUTTON1_RELEASED, NULL);
#endif
#endif
CRT_degreeSign = initDegreeSign(); CRT_degreeSign = initDegreeSign();
} }
void CRT_done() { void CRT_done()
attron(CRT_colors[RESET_COLOR]); {
int resetColor = CRT_colors ? CRT_colors[RESET_COLOR] : CRT_colorSchemes[COLORSCHEME_DEFAULT][RESET_COLOR];
attron(resetColor);
mvhline(LINES - 1, 0, ' ', COLS); mvhline(LINES - 1, 0, ' ', COLS);
attroff(CRT_colors[RESET_COLOR]); attroff(resetColor);
refresh(); refresh();
curs_set(1); curs_set(1);
@ -1016,14 +1070,16 @@ void CRT_done() {
dumpStderr(); dumpStderr();
} }
void CRT_fatalError(const char* note) { void CRT_fatalError(const char *note)
const char* sysMsg = strerror(errno); {
const char *sysMsg = strerror(errno);
CRT_done(); CRT_done();
fprintf(stderr, "%s: %s\n", note, sysMsg); fprintf(stderr, "%s: %s\n", note, sysMsg);
exit(2); exit(2);
} }
int CRT_readKey() { int CRT_readKey()
{
nocbreak(); nocbreak();
cbreak(); cbreak();
nodelay(stdscr, FALSE); nodelay(stdscr, FALSE);
@ -1032,22 +1088,28 @@ int CRT_readKey() {
return ret; return ret;
} }
void CRT_disableDelay() { void CRT_disableDelay()
{
nocbreak(); nocbreak();
cbreak(); cbreak();
nodelay(stdscr, TRUE); nodelay(stdscr, TRUE);
} }
void CRT_enableDelay() { void CRT_enableDelay()
{
halfdelay(*CRT_delay); halfdelay(*CRT_delay);
} }
void CRT_setColors(int colorScheme) { void CRT_setColors(int colorScheme)
{
CRT_colorScheme = colorScheme; CRT_colorScheme = colorScheme;
for (short int i = 0; i < 8; i++) { for (short int i = 0; i < 8; i++)
for (short int j = 0; j < 8; j++) { {
if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault) { for (short int j = 0; j < 8; j++)
{
if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault)
{
short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT) short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? (j == 0 ? -1 : j) ? (j == 0 ? -1 : j)
: j; : j;
@ -1066,7 +1128,8 @@ void CRT_setColors(int colorScheme) {
} }
#ifdef PRINT_BACKTRACE #ifdef PRINT_BACKTRACE
static void print_backtrace(void) { static void print_backtrace(void)
{
#if defined(HAVE_LIBUNWIND_H) && defined(HAVE_LIBUNWIND) #if defined(HAVE_LIBUNWIND_H) && defined(HAVE_LIBUNWIND)
unw_context_t context; unw_context_t context;
unw_getcontext(&context); unw_getcontext(&context);
@ -1076,7 +1139,8 @@ static void print_backtrace(void) {
unsigned int item = 0; unsigned int item = 0;
while (unw_step(&cursor) > 0) { while (unw_step(&cursor) > 0)
{
unw_word_t pc; unw_word_t pc;
unw_get_reg(&cursor, UNW_REG_IP, &pc); unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (pc == 0) if (pc == 0)
@ -1089,26 +1153,27 @@ static void print_backtrace(void) {
unw_proc_info_t pip; unw_proc_info_t pip;
pip.unwind_info = NULL; pip.unwind_info = NULL;
const char* fname = "?"; const char *fname = "?";
const void* ptr = 0; const void *ptr = 0;
if (unw_get_proc_info(&cursor, &pip) == 0) { if (unw_get_proc_info(&cursor, &pip) == 0)
ptr = (const void*)(pip.start_ip + offset); {
ptr = (const void *)(pip.start_ip + offset);
#ifdef HAVE_DLADDR #ifdef HAVE_DLADDR
Dl_info dlinfo; Dl_info dlinfo;
if (dladdr(ptr, &dlinfo) && dlinfo.dli_fname && *dlinfo.dli_fname) if (dladdr(ptr, &dlinfo) && dlinfo.dli_fname && *dlinfo.dli_fname)
fname = dlinfo.dli_fname; fname = dlinfo.dli_fname;
#endif #endif
} }
const char* frame = ""; const char *frame = "";
if (unw_is_signal_frame(&cursor) > 0) if (unw_is_signal_frame(&cursor) > 0)
frame = "{signal frame}"; frame = "{signal frame}";
fprintf(stderr, "%2u: %#14lx %s (%s+%#lx) [%p]%s%s\n", item++, pc, fname, symbolName, offset, ptr, frame ? " " : "", frame); fprintf(stderr, "%2u: %#14lx %s (%s+%#lx) [%p]%s%s\n", item++, pc, fname, symbolName, offset, ptr, frame ? " " : "", frame);
} }
#elif defined(HAVE_EXECINFO_H) #elif defined(HAVE_EXECINFO_H)
void* backtraceArray[256]; void *backtraceArray[256];
size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray)); size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray));
backtrace_symbols_fd(backtraceArray, size, STDERR_FILENO); backtrace_symbols_fd(backtraceArray, size, STDERR_FILENO);
@ -1118,7 +1183,8 @@ static void print_backtrace(void) {
} }
#endif #endif
void CRT_handleSIGSEGV(int signal) { void CRT_handleSIGSEGV(int signal)
{
CRT_done(); CRT_done();
fprintf(stderr, "\n\n" fprintf(stderr, "\n\n"
@ -1126,22 +1192,21 @@ void CRT_handleSIGSEGV(int signal) {
"============================\n" "============================\n"
"Please check at https://htop.dev/issues whether this issue has already been reported.\n" "Please check at https://htop.dev/issues whether this issue has already been reported.\n"
"If no similar issue has been reported before, please create a new issue with the following information:\n" "If no similar issue has been reported before, please create a new issue with the following information:\n"
" - Your "PACKAGE" version: '"VERSION"'\n" " - Your " PACKAGE " version: '" VERSION "'\n"
" - Your OS and kernel version (uname -a)\n" " - Your OS and kernel version (uname -a)\n"
" - Your distribution and release (lsb_release -a)\n" " - Your distribution and release (lsb_release -a)\n"
" - Likely steps to reproduce (How did it happen?)\n" " - Likely steps to reproduce (How did it happen?)\n");
);
#ifdef PRINT_BACKTRACE #ifdef PRINT_BACKTRACE
fprintf(stderr, " - Backtrace of the issue (see below)\n"); fprintf(stderr, " - Backtrace of the issue (see below)\n");
#endif #endif
fprintf(stderr, fprintf(stderr,
"\n" "\n");
);
const char* signal_str = strsignal(signal); const char *signal_str = strsignal(signal);
if (!signal_str) { if (!signal_str)
{
signal_str = "unknown reason"; signal_str = "unknown reason";
} }
fprintf(stderr, fprintf(stderr,
@ -1149,8 +1214,7 @@ void CRT_handleSIGSEGV(int signal) {
"------------------\n" "------------------\n"
"A signal %d (%s) was received.\n" "A signal %d (%s) was received.\n"
"\n", "\n",
signal, signal_str signal, signal_str);
);
fprintf(stderr, fprintf(stderr,
"Setting information:\n" "Setting information:\n"
@ -1161,44 +1225,40 @@ void CRT_handleSIGSEGV(int signal) {
#ifdef PRINT_BACKTRACE #ifdef PRINT_BACKTRACE
fprintf(stderr, fprintf(stderr,
"Backtrace information:\n" "Backtrace information:\n"
"----------------------\n" "----------------------\n");
);
print_backtrace(); print_backtrace();
fprintf(stderr, fprintf(stderr,
"\n" "\n"
"To make the above information more practical to work with, " "To make the above information more practical to work with, "
"please also provide a disassembly of your "PACKAGE" binary. " "please also provide a disassembly of your " PACKAGE " binary. "
"This can usually be done by running the following command:\n" "This can usually be done by running the following command:\n"
"\n" "\n");
);
#ifdef HTOP_DARWIN #ifdef HTOP_DARWIN
fprintf(stderr, " otool -tvV `which "PACKAGE"` > ~/htop.otool\n"); fprintf(stderr, " otool -tvV `which " PACKAGE "` > ~/htop.otool\n");
#else #else
fprintf(stderr, " objdump -d -S -w `which "PACKAGE"` > ~/htop.objdump\n"); fprintf(stderr, " objdump -d -S -w `which " PACKAGE "` > ~/htop.objdump\n");
#endif #endif
fprintf(stderr, fprintf(stderr,
"\n" "\n"
"Please include the generated file in your report.\n" "Please include the generated file in your report.\n");
);
#endif #endif
fprintf(stderr, fprintf(stderr,
"Running this program with debug symbols or inside a debugger may provide further insights.\n" "Running this program with debug symbols or inside a debugger may provide further insights.\n"
"\n" "\n"
"Thank you for helping to improve "PACKAGE"!\n" "Thank you for helping to improve " PACKAGE "!\n"
"\n" "\n");
);
/* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */ /* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */
if (sigaction (signal, &old_sig_handler[signal], NULL) < 0) { if (sigaction(signal, &old_sig_handler[signal], NULL) < 0)
{
/* This avoids an infinite loop in case the handler could not be reset. */ /* This avoids an infinite loop in case the handler could not be reset. */
fprintf(stderr, fprintf(stderr,
"!!! Chained handler could not be restored. Forcing exit.\n" "!!! Chained handler could not be restored. Forcing exit.\n");
);
_exit(1); _exit(1);
} }
@ -1207,7 +1267,6 @@ void CRT_handleSIGSEGV(int signal) {
// Always terminate, even if installed handler returns // Always terminate, even if installed handler returns
fprintf(stderr, fprintf(stderr,
"!!! Chained handler did not exit. Forcing exit.\n" "!!! Chained handler did not exit. Forcing exit.\n");
);
_exit(1); _exit(1);
} }

30
CRT.h
View File

@ -15,8 +15,8 @@ in the source distribution for its full text.
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "Settings.h" #include "Settings.h"
typedef enum TreeStr_
typedef enum TreeStr_ { {
TREE_STR_VERT, TREE_STR_VERT,
TREE_STR_RTEE, TREE_STR_RTEE,
TREE_STR_BEND, TREE_STR_BEND,
@ -28,7 +28,8 @@ typedef enum TreeStr_ {
LAST_TREE_STR LAST_TREE_STR
} TreeStr; } TreeStr;
typedef enum ColorScheme_ { typedef enum ColorScheme_
{
COLORSCHEME_DEFAULT, COLORSCHEME_DEFAULT,
COLORSCHEME_MONOCHROME, COLORSCHEME_MONOCHROME,
COLORSCHEME_BLACKONWHITE, COLORSCHEME_BLACKONWHITE,
@ -39,7 +40,8 @@ typedef enum ColorScheme_ {
LAST_COLORSCHEME LAST_COLORSCHEME
} ColorScheme; } ColorScheme;
typedef enum ColorElements_ { typedef enum ColorElements_
{
RESET_COLOR, RESET_COLOR,
DEFAULT_COLOR, DEFAULT_COLOR,
FUNCTION_BAR, FUNCTION_BAR,
@ -64,6 +66,8 @@ typedef enum ColorElements_ {
METER_VALUE_WARN, METER_VALUE_WARN,
LED_COLOR, LED_COLOR,
UPTIME, UPTIME,
TEMP,
FREQ,
BATTERY, BATTERY,
TASKS_RUNNING, TASKS_RUNNING,
SWAP, SWAP,
@ -148,13 +152,13 @@ typedef enum ColorElements_ {
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
void CRT_fatalError(const char* note) ATTR_NORETURN; void CRT_fatalError(const char *note) ATTR_NORETURN;
#ifdef NDEBUG #ifdef NDEBUG
# define CRT_debug(...) #define CRT_debug(...)
#else #else
void CRT_debug_impl(const char* file, size_t lineno, const char* func, const char* fmt, ...) ATTR_FORMAT(printf, 4, 5); void CRT_debug_impl(const char *file, size_t lineno, const char *func, const char *fmt, ...) ATTR_FORMAT(printf, 4, 5);
# define CRT_debug(...) CRT_debug_impl(__FILE__, __LINE__, __func__, __VA_ARGS__) #define CRT_debug(...) CRT_debug_impl(__FILE__, __LINE__, __func__, __VA_ARGS__)
#endif #endif
void CRT_handleSIGSEGV(int signal) ATTR_NORETURN; void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;
@ -165,7 +169,7 @@ void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;
#define KEY_SHIFT_TAB KEY_F(33) #define KEY_SHIFT_TAB KEY_F(33)
#define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A')) #define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A'))
extern const char* CRT_degreeSign; extern const char *CRT_degreeSign;
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
@ -173,9 +177,9 @@ extern bool CRT_utf8;
#endif #endif
extern const char* const* CRT_treeStr; extern const char *const *CRT_treeStr;
extern const int* CRT_colors; extern const int *CRT_colors;
extern int CRT_cursorX; extern int CRT_cursorX;
@ -185,7 +189,9 @@ extern int CRT_scrollWheelVAmount;
extern ColorScheme CRT_colorScheme; extern ColorScheme CRT_colorScheme;
void CRT_init(const Settings* settings, bool allowUnicode); void CRT_setMouse(bool enabled);
void CRT_init(const Settings *settings, bool allowUnicode);
void CRT_done(void); void CRT_done(void);

View File

@ -1,3 +1,23 @@
What's new in version 3.2.1
* Fix setting to show all branches collapsed by default
* Restore functionality of stripExeFromCmdline setting
* Fix some command line display settings not being honored without restart
* Display single digit precision for CPU% greater than 99.9%
* On Linux, FreeBSD and PCP consider only shrinkable ZFS ARC as cache
* On Linux, increase field width of CPUD% and SWAPD% columns
* Colorize process state characters in help screen
* Use mousemask(3X) to enable and disable mouse control
* Fix heap buffer overflow in Vector_compact
* On Solaris, fix a process time scaling error
* On Solaris, fix the build
* On NetBSD, OpenBSD and Solaris ensure env buffer size is sufficient
* On Linux, resolve processes exiting interfering with sampling
* Fix ProcessList quadratic removal when scanning processes
* Under LXC, limit CPU count to that given by /proc/cpuinfo
* Improve container detection for LXC
* Some minor documentation fixes
What's new in version 3.2.0 What's new in version 3.2.0
* Support for displaying multiple tabs in the user interface * Support for displaying multiple tabs in the user interface

View File

@ -68,6 +68,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
this->settings->colorScheme = mark; this->settings->colorScheme = mark;
this->settings->changed = true; this->settings->changed = true;
this->settings->lastUpdate++;
CRT_setColors(mark); CRT_setColors(mark);
clear(); clear();

View File

@ -18,6 +18,7 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include "OptionItem.h" #include "OptionItem.h"
#include "ProvideCurses.h" #include "ProvideCurses.h"
#include "ScreensPanel.h"
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@ -43,6 +44,8 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
case KEY_RECLICK: case KEY_RECLICK:
case ' ': case ' ':
switch (OptionItem_kind(selected)) { switch (OptionItem_kind(selected)) {
case OPTION_ITEM_TEXT:
break;
case OPTION_ITEM_CHECK: case OPTION_ITEM_CHECK:
CheckItem_toggle((CheckItem*)selected); CheckItem_toggle((CheckItem*)selected);
result = HANDLED; result = HANDLED;
@ -69,6 +72,7 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED) { if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
this->settings->lastUpdate++;
Header* header = this->scr->header; Header* header = this->scr->header;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_reinit(header); Header_reinit(header);
@ -97,9 +101,17 @@ 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("Tree view (for the current Screen tab)", &(settings->ss->treeView)));
#define TABMSG "For current screen tab: \0"
char tabheader[sizeof(TABMSG) + SCREEN_NAME_LEN + 1] = TABMSG;
strncat(tabheader, settings->ss->name, SCREEN_NAME_LEN);
Panel_add(super, (Object*) TextItem_new(tabheader));
#undef TABMSG
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->ss->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID))); Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed))); Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed)));
Panel_add(super, (Object*) TextItem_new("Global options:"));
Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs))); Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));

29
FreqMeter.c Normal file
View File

@ -0,0 +1,29 @@
#include "FreqMeter.h"
#include "CRT.h"
#include "Object.h"
#include "Platform.h"
#include "XUtils.h"
static const int FreqMeter_attributes[] = {
FREQ};
static void FreqMeter_updateValues(Meter *this)
{
float freq = Platform_getFreq();
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1lf GHz", freq);
}
const MeterClass FreqMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete},
.updateValues = FreqMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = FreqMeter_attributes,
.name = "Freq",
.uiName = "CPU Frequency",
.caption = "CPU/Frequency: "};

8
FreqMeter.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef HEADER_FreqMeter
#define HEADER_FreqMeter
#include "Meter.h"
extern const MeterClass FreqMeter_class;
#endif

View File

@ -52,7 +52,7 @@ static void Hashtable_dump(const Hashtable* this) {
i, i,
this->buckets[i].key, this->buckets[i].key,
this->buckets[i].probe, this->buckets[i].probe,
this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)"); this->buckets[i].value);
if (this->buckets[i].value) if (this->buckets[i].value)
items++; items++;

View File

@ -52,6 +52,7 @@ static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
Header_setLayout(this->scr->header, mark); Header_setLayout(this->scr->header, mark);
this->settings->changed = true; this->settings->changed = true;
this->settings->lastUpdate++;
ScreenManager_resize(this->scr); ScreenManager_resize(this->scr);

View File

@ -82,6 +82,8 @@ myhtopsources = \
TasksMeter.c \ TasksMeter.c \
TraceScreen.c \ TraceScreen.c \
UptimeMeter.c \ UptimeMeter.c \
FreqMeter.c \
TempMeter.c \
UsersTable.c \ UsersTable.c \
Vector.c \ Vector.c \
XUtils.c XUtils.c
@ -144,6 +146,8 @@ myhtopheaders = \
TasksMeter.h \ TasksMeter.h \
TraceScreen.h \ TraceScreen.h \
UptimeMeter.h \ UptimeMeter.h \
FreqMeter.h \
TempMeter.h \
UsersTable.h \ UsersTable.h \
Vector.h \ Vector.h \
XUtils.h XUtils.h

View File

@ -184,6 +184,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED || sideMove) { if (result == HANDLED || sideMove) {
Header* header = this->scr->header; Header* header = this->scr->header;
this->settings->changed = true; this->settings->changed = true;
this->settings->lastUpdate++;
Header_calculateHeight(header); Header_calculateHeight(header);
ScreenManager_resize(this->scr); ScreenManager_resize(this->scr);
} }

View File

@ -120,7 +120,9 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
close(fdnull); close(fdnull);
char buffer[32] = {0}; char buffer[32] = {0};
xSnprintf(buffer, sizeof(buffer), "%d", pid); xSnprintf(buffer, sizeof(buffer), "%d", pid);
execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", NULL); // Use of NULL in variadic functions must have a pointer cast.
// The NULL constant is not required by standard to have a pointer type.
execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", (char *)NULL);
exit(127); exit(127);
} }
close(fdpair[1]); close(fdpair[1]);

View File

@ -25,6 +25,13 @@ static void OptionItem_delete(Object* cast) {
free(this); free(this);
} }
static void TextItem_display(const Object* cast, RichString* out) {
const TextItem* this = (const TextItem*)cast;
assert (this != NULL);
RichString_appendWide(out, CRT_colors[HELP_BOLD], this->super.text);
}
static void CheckItem_display(const Object* cast, RichString* out) { static void CheckItem_display(const Object* cast, RichString* out) {
const CheckItem* this = (const CheckItem*)cast; const CheckItem* this = (const CheckItem*)cast;
assert (this != NULL); assert (this != NULL);
@ -68,6 +75,16 @@ const OptionItemClass OptionItem_class = {
} }
}; };
const OptionItemClass TextItem_class = {
.super = {
.extends = Class(OptionItem),
.delete = OptionItem_delete,
.display = TextItem_display
},
.kind = OPTION_ITEM_TEXT
};
const OptionItemClass CheckItem_class = { const OptionItemClass CheckItem_class = {
.super = { .super = {
.extends = Class(OptionItem), .extends = Class(OptionItem),
@ -77,6 +94,7 @@ const OptionItemClass CheckItem_class = {
.kind = OPTION_ITEM_CHECK .kind = OPTION_ITEM_CHECK
}; };
const OptionItemClass NumberItem_class = { const OptionItemClass NumberItem_class = {
.super = { .super = {
.extends = Class(OptionItem), .extends = Class(OptionItem),
@ -86,6 +104,12 @@ const OptionItemClass NumberItem_class = {
.kind = OPTION_ITEM_NUMBER .kind = OPTION_ITEM_NUMBER
}; };
TextItem* TextItem_new(const char* text) {
TextItem* this = AllocThis(TextItem);
this->super.text = xStrdup(text);
return this;
}
CheckItem* CheckItem_newByRef(const char* text, bool* ref) { CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
CheckItem* this = AllocThis(CheckItem); CheckItem* this = AllocThis(CheckItem);
this->super.text = xStrdup(text); this->super.text = xStrdup(text);

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
enum OptionItemType { enum OptionItemType {
OPTION_ITEM_TEXT,
OPTION_ITEM_CHECK, OPTION_ITEM_CHECK,
OPTION_ITEM_NUMBER, OPTION_ITEM_NUMBER,
}; };
@ -32,6 +33,12 @@ typedef struct OptionItem_ {
char* text; char* text;
} OptionItem; } OptionItem;
typedef struct TextItem_ {
OptionItem super;
char* text;
} TextItem;
typedef struct CheckItem_ { typedef struct CheckItem_ {
OptionItem super; OptionItem super;
@ -51,9 +58,12 @@ typedef struct NumberItem_ {
} NumberItem; } NumberItem;
extern const OptionItemClass OptionItem_class; extern const OptionItemClass OptionItem_class;
extern const OptionItemClass TextItem_class;
extern const OptionItemClass CheckItem_class; extern const OptionItemClass CheckItem_class;
extern const OptionItemClass NumberItem_class; extern const OptionItemClass NumberItem_class;
TextItem* TextItem_new(const char* text);
CheckItem* CheckItem_newByRef(const char* text, bool* ref); CheckItem* CheckItem_newByRef(const char* text, bool* ref);
CheckItem* CheckItem_newByVal(const char* text, bool value); CheckItem* CheckItem_newByVal(const char* text, bool value);
bool CheckItem_get(const CheckItem* this); bool CheckItem_get(const CheckItem* this);

View File

@ -414,6 +414,8 @@ void Process_makeCommandStr(Process* this) {
bool stripExeFromCmdline = settings->stripExeFromCmdline; bool stripExeFromCmdline = settings->stripExeFromCmdline;
bool showThreadNames = settings->showThreadNames; bool showThreadNames = settings->showThreadNames;
uint64_t settingsStamp = settings->lastUpdate;
/* Nothing to do to (Re)Generate the Command string, if the process is: /* Nothing to do to (Re)Generate the Command string, if the process is:
* - a kernel thread, or * - a kernel thread, or
* - a zombie from before being under htop's watch, or * - a zombie from before being under htop's watch, or
@ -422,52 +424,27 @@ void Process_makeCommandStr(Process* this) {
return; return;
if (this->state == ZOMBIE && !this->mergedCommand.str) if (this->state == ZOMBIE && !this->mergedCommand.str)
return; return;
if (Process_isUserlandThread(this) && settings->showThreadNames && (showThreadNames == mc->prevShowThreadNames) && (mc->prevMergeSet == showMergedCommand))
return;
/* this->mergedCommand.str needs updating only if its state or contents changed. /* this->mergedCommand.str needs updating only if its state or contents changed.
* Its content is based on the fields cmdline, comm, and exe. */ * Its content is based on the fields cmdline, comm, and exe. */
if ( if (mc->lastUpdate >= settingsStamp)
mc->prevMergeSet == showMergedCommand &&
mc->prevPathSet == showProgramPath &&
mc->prevCommSet == searchCommInCmdline &&
mc->prevCmdlineSet == stripExeFromCmdline &&
mc->prevShowThreadNames == showThreadNames &&
!mc->cmdlineChanged &&
!mc->commChanged &&
!mc->exeChanged
) {
return; return;
}
mc->lastUpdate = settingsStamp;
/* The field separtor "│" has been chosen such that it will not match any /* The field separtor "│" has been chosen such that it will not match any
* valid string used for searching or filtering */ * valid string used for searching or filtering */
const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT]; const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT];
const int SEPARATOR_LEN = strlen(SEPARATOR); const int SEPARATOR_LEN = strlen(SEPARATOR);
/* Check for any changed fields since we last built this string */
if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) {
free(mc->str);
/* Accommodate the column text, two field separators and terminating NUL */ /* Accommodate the column text, two field separators and terminating NUL */
size_t maxLen = 2 * SEPARATOR_LEN + 1; size_t maxLen = 2 * SEPARATOR_LEN + 1;
maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)"); maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
maxLen += this->procComm ? strlen(this->procComm) : 0; maxLen += this->procComm ? strlen(this->procComm) : 0;
maxLen += this->procExe ? strlen(this->procExe) : 0; maxLen += this->procExe ? strlen(this->procExe) : 0;
free(mc->str);
mc->str = xCalloc(1, maxLen); mc->str = xCalloc(1, maxLen);
}
/* Preserve the settings used in this run */
mc->prevMergeSet = showMergedCommand;
mc->prevPathSet = showProgramPath;
mc->prevCommSet = searchCommInCmdline;
mc->prevCmdlineSet = stripExeFromCmdline;
mc->prevShowThreadNames = showThreadNames;
/* Mark everything as unchanged */
mc->cmdlineChanged = false;
mc->commChanged = false;
mc->exeChanged = false;
/* Reset all locations that need extra handling when actually displaying */ /* Reset all locations that need extra handling when actually displaying */
mc->highlightCount = 0; mc->highlightCount = 0;
@ -601,11 +578,15 @@ void Process_makeCommandStr(Process* this) {
} }
if (matchLen) { if (matchLen) {
if (stripExeFromCmdline) {
/* strip the matched exe prefix */ /* strip the matched exe prefix */
cmdline += matchLen; cmdline += matchLen;
commStart -= matchLen; commStart -= matchLen;
commEnd -= matchLen; commEnd -= matchLen;
} else {
matchLen = 0;
}
} }
if (!matchLen || (haveCommField && *cmdline)) { if (!matchLen || (haveCommField && *cmdline)) {
@ -739,17 +720,20 @@ void Process_printLeftAlignedField(RichString* str, int attr, const char* conten
void Process_printPercentage(float val, char* buffer, int n, uint8_t width, int* attr) { void Process_printPercentage(float val, char* buffer, int n, uint8_t width, int* attr) {
if (val >= 0) { if (val >= 0) {
if (val < 99.9F) { if (val < 0.05F)
if (val < 0.05F) {
*attr = CRT_colors[PROCESS_SHADOW]; *attr = CRT_colors[PROCESS_SHADOW];
} else if (val >= 99.9F)
xSnprintf(buffer, n, "%*.1f ", width, val);
} else {
*attr = CRT_colors[PROCESS_MEGABYTES]; *attr = CRT_colors[PROCESS_MEGABYTES];
if (val < 100.0F)
val = 100.0F; // Don't round down and display "val" as "99". int precision = 1;
xSnprintf(buffer, n, "%*.0f ", width, val);
// Display "val" as "100" for columns like "MEM%".
if (width == 4 && val > 99.9F) {
precision = 0;
val = 100.0F;
} }
xSnprintf(buffer, n, "%*.*f ", width, precision, val);
} else { } else {
*attr = CRT_colors[PROCESS_SHADOW]; *attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "%*.*s ", width, width, "N/A"); xSnprintf(buffer, n, "%*.*s ", width, width, "N/A");
@ -1095,13 +1079,6 @@ bool Process_sendSignal(Process* this, Arg sgn) {
return kill(this->pid, sgn.i) == 0; return kill(this->pid, sgn.i) == 0;
} }
int Process_pidCompare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
int Process_compare(const void* v1, const void* v2) { int Process_compare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1; const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2; const Process* p2 = (const Process*)v2;
@ -1204,7 +1181,8 @@ void Process_updateComm(Process* this, const char* comm) {
free(this->procComm); free(this->procComm);
this->procComm = comm ? xStrdup(comm) : NULL; this->procComm = comm ? xStrdup(comm) : NULL;
this->mergedCommand.commChanged = true;
this->mergedCommand.lastUpdate = 0;
} }
static int skipPotentialPath(const char* cmdline, int end) { static int skipPotentialPath(const char* cmdline, int end) {
@ -1244,7 +1222,8 @@ void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart
this->cmdline = cmdline ? xStrdup(cmdline) : NULL; this->cmdline = cmdline ? xStrdup(cmdline) : NULL;
this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd); this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd);
this->cmdlineBasenameEnd = basenameEnd; this->cmdlineBasenameEnd = basenameEnd;
this->mergedCommand.cmdlineChanged = true;
this->mergedCommand.lastUpdate = 0;
} }
void Process_updateExe(Process* this, const char* exe) { void Process_updateExe(Process* this, const char* exe) {
@ -1263,7 +1242,8 @@ void Process_updateExe(Process* this, const char* exe) {
this->procExe = NULL; this->procExe = NULL;
this->procExeBasenameOffset = 0; this->procExeBasenameOffset = 0;
} }
this->mergedCommand.exeChanged = true;
this->mergedCommand.lastUpdate = 0;
} }
uint8_t Process_fieldWidths[LAST_PROCESSFIELD] = { 0 }; uint8_t Process_fieldWidths[LAST_PROCESSFIELD] = { 0 };
@ -1287,13 +1267,14 @@ void Process_updateFieldWidth(ProcessField key, size_t width) {
} }
void Process_updateCPUFieldWidths(float percentage) { void Process_updateCPUFieldWidths(float percentage) {
if (percentage < 99.9) { if (percentage < 99.9F) {
Process_updateFieldWidth(PERCENT_CPU, 4); Process_updateFieldWidth(PERCENT_CPU, 4);
Process_updateFieldWidth(PERCENT_NORM_CPU, 4); Process_updateFieldWidth(PERCENT_NORM_CPU, 4);
return; return;
} }
uint8_t width = ceil(log10(percentage + .2)); // Add additional two characters, one for "." and another for precision.
uint8_t width = ceil(log10(percentage + 0.1)) + 2;
Process_updateFieldWidth(PERCENT_CPU, width); Process_updateFieldWidth(PERCENT_CPU, width);
Process_updateFieldWidth(PERCENT_NORM_CPU, width); Process_updateFieldWidth(PERCENT_NORM_CPU, width);

View File

@ -96,17 +96,10 @@ typedef struct ProcessCmdlineHighlight_ {
* Process_writeCommand to color the string. str will be NULL for kernel * Process_writeCommand to color the string. str will be NULL for kernel
* threads and zombies */ * threads and zombies */
typedef struct ProcessMergedCommand_ { typedef struct ProcessMergedCommand_ {
uint64_t lastUpdate; /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */
char* str; /* merged Command string */ char* str; /* merged Command string */
size_t highlightCount; /* how many portions of cmdline to highlight */ size_t highlightCount; /* how many portions of cmdline to highlight */
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */ ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
bool cmdlineChanged : 1; /* whether cmdline changed */
bool exeChanged : 1; /* whether exe changed */
bool commChanged : 1; /* whether comm changed */
bool prevMergeSet : 1; /* whether showMergedCommand was set */
bool prevPathSet : 1; /* whether showProgramPath was set */
bool prevCommSet : 1; /* whether findCommInCmdline was set */
bool prevCmdlineSet : 1; /* whether stripExeFromCmdline was set */
bool prevShowThreadNames : 1; /* whether showThreadNames was set */
} ProcessMergedCommand; } ProcessMergedCommand;
typedef struct Process_ { typedef struct Process_ {
@ -293,7 +286,7 @@ extern uint8_t Process_fieldWidths[LAST_PROCESSFIELD];
#define PROCESS_MIN_PID_DIGITS 5 #define PROCESS_MIN_PID_DIGITS 5
#define PROCESS_MAX_PID_DIGITS 19 #define PROCESS_MAX_PID_DIGITS 19
#define PROCESS_MIN_UID_DIGITS 5 #define PROCESS_MIN_UID_DIGITS 5
#define PROCESS_MAX_UID_DIGITS 19 #define PROCESS_MAX_UID_DIGITS 20
extern int Process_pidDigits; extern int Process_pidDigits;
extern int Process_uidDigits; extern int Process_uidDigits;
@ -394,7 +387,11 @@ bool Process_changePriorityBy(Process* this, Arg delta);
bool Process_sendSignal(Process* this, Arg sgn); bool Process_sendSignal(Process* this, Arg sgn);
int Process_pidCompare(const void* v1, const void* v2); static inline int Process_pidEqualCompare(const void* v1, const void* v2) {
const pid_t p1 = ((const Process*)v1)->pid;
const pid_t p2 = ((const Process*)v2)->pid;
return p1 != p2; /* return zero when equal */
}
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key); int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);

View File

@ -82,40 +82,44 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
this->panel = panel; this->panel = panel;
} }
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) { static const char* alignedDynamicColumnTitle(const ProcessList* this, int key, char* titleBuffer, size_t titleBufferSize) {
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key); const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
if (column == NULL) if (column == NULL)
return "- "; return "- ";
static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
int width = column->width; int width = column->width;
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH) if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
width = DYNAMIC_DEFAULT_COLUMN_WIDTH; width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading); xSnprintf(titleBuffer, titleBufferSize, "%*s", width, column->heading);
return titleBuffer; return titleBuffer;
} }
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) { static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
static char titleBuffer[UINT8_MAX + sizeof(" ")];
assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" "));
assert(sizeof(titleBuffer) >= PROCESS_MAX_PID_DIGITS + sizeof(" "));
assert(sizeof(titleBuffer) >= PROCESS_MAX_UID_DIGITS + sizeof(" "));
if (field >= LAST_PROCESSFIELD) if (field >= LAST_PROCESSFIELD)
return alignedDynamicColumnTitle(this, field); return alignedDynamicColumnTitle(this, field, titleBuffer, sizeof(titleBuffer));
const char* title = Process_fields[field].title; const char* title = Process_fields[field].title;
if (!title) if (!title)
return "- "; return "- ";
if (Process_fields[field].pidColumn) { if (Process_fields[field].pidColumn) {
static char titleBuffer[PROCESS_MAX_PID_DIGITS + sizeof(" ")];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title); xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
return titleBuffer; return titleBuffer;
} }
if (field == ST_UID) { if (field == ST_UID) {
static char titleBuffer[PROCESS_MAX_UID_DIGITS + sizeof(" ")];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title); xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
return titleBuffer; return titleBuffer;
} }
if (Process_fields[field].autoWidth) { if (Process_fields[field].autoWidth) {
static char titleBuffer[UINT8_MAX + 1]; if (field == PERCENT_CPU)
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_fieldWidths[field], title);
else
xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title); xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title);
return titleBuffer; return titleBuffer;
} }
@ -158,7 +162,7 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) {
} }
void ProcessList_add(ProcessList* this, Process* p) { void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1); assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL); assert(Hashtable_get(this->processTable, p->pid) == NULL);
p->processList = this; p->processList = this;
@ -168,25 +172,23 @@ void ProcessList_add(ProcessList* this, Process* p) {
Vector_add(this->processes, p); Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p); Hashtable_put(this->processTable, p->pid, p);
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL); assert(Hashtable_get(this->processTable, p->pid) != NULL);
assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
} }
void ProcessList_remove(ProcessList* this, const Process* p) { // ProcessList_removeIndex removes Process p from the list's map and soft deletes
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); // it from its vector. Vector_compact *must* be called once the caller is done
assert(Hashtable_get(this->processTable, p->pid) != NULL); // removing items.
// Should only be called from ProcessList_scan to avoid breaking dying process highlighting.
const Process* pp = Hashtable_remove(this->processTable, p->pid); static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx) {
assert(pp == p); (void)pp;
pid_t pid = p->pid; pid_t pid = p->pid;
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
assert(idx != -1);
if (idx >= 0) { assert(p == (Process*)Vector_get(this->processes, idx));
Vector_remove(this->processes, idx); assert(Hashtable_get(this->processTable, pid) != NULL);
}
Hashtable_remove(this->processTable, pid);
Vector_softRemove(this->processes, idx);
if (this->following != -1 && this->following == pid) { if (this->following != -1 && this->following == pid) {
this->following = -1; this->following = -1;
@ -194,7 +196,7 @@ void ProcessList_remove(ProcessList* this, const Process* p) {
} }
assert(Hashtable_get(this->processTable, pid) == NULL); assert(Hashtable_get(this->processTable, pid) == NULL);
assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
} }
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, bool show) { static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, bool show) {
@ -355,7 +357,10 @@ void ProcessList_expandTree(ProcessList* this) {
} }
} }
// Called on collapse-all toggle and on startup, possibly in non-tree mode
void ProcessList_collapseAllBranches(ProcessList* this) { void ProcessList_collapseAllBranches(ProcessList* this) {
ProcessList_buildTree(this); // Update `tree_depth` fields of the processes
this->needsSort = true; // ProcessList is sorted by parent now, force new sort
int size = Vector_size(this->processes); int size = Vector_size(this->processes);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Process* process = (Process*) Vector_get(this->processes, i); Process* process = (Process*) Vector_get(this->processes, i);
@ -429,7 +434,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
Process* proc = (Process*) Hashtable_get(this->processTable, pid); Process* proc = (Process*) Hashtable_get(this->processTable, pid);
*preExisting = proc != NULL; *preExisting = proc != NULL;
if (proc) { if (proc) {
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1); assert(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -1);
assert(proc->pid == pid); assert(proc->pid == pid);
} else { } else {
proc = constructor(this->settings); proc = constructor(this->settings);
@ -484,7 +489,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
if (p->tombStampMs > 0) { if (p->tombStampMs > 0) {
// remove tombed process // remove tombed process
if (this->monotonicMs >= p->tombStampMs) { if (this->monotonicMs >= p->tombStampMs) {
ProcessList_remove(this, p); ProcessList_removeIndex(this, p, i);
} }
} else if (p->updated == false) { } else if (p->updated == false) {
// process no longer exists // process no longer exists
@ -493,11 +498,14 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs; p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
} else { } else {
// immediately remove // immediately remove
ProcessList_remove(this, p); ProcessList_removeIndex(this, p, i);
} }
} }
} }
// Compact the processes vector in case of any deletions
Vector_compact(this->processes);
// Set UID column width based on max UID. // Set UID column width based on max UID.
Process_setUidColumnWidth(maxUid); Process_setUidColumnWidth(maxUid);
} }

View File

@ -106,8 +106,6 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header);
void ProcessList_add(ProcessList* this, Process* p); void ProcessList_add(ProcessList* this, Process* p);
void ProcessList_remove(ProcessList* this, const Process* p);
void ProcessList_updateDisplayList(ProcessList* this); void ProcessList_updateDisplayList(ProcessList* this);
ProcessField ProcessList_keyAt(const ProcessList* this, int at); ProcessField ProcessList_keyAt(const ProcessList* this, int at);

91
README
View File

@ -8,7 +8,22 @@
[![Packaging status](https://repology.org/badge/tiny-repos/htop.svg)](https://repology.org/project/htop/versions) [![Packaging status](https://repology.org/badge/tiny-repos/htop.svg)](https://repology.org/project/htop/versions)
[![License: GPL v2+](https://img.shields.io/badge/License-GPL%20v2+-blue.svg)](COPYING?raw=true) [![License: GPL v2+](https://img.shields.io/badge/License-GPL%20v2+-blue.svg)](COPYING?raw=true)
![Screenshot of htop](docs/images/screenshot.png?raw=true) ![Screenshot of htop](https://i.imgur.com/dCSsvSv.png)
## **Warning!**
This fork was created for personal use; correct operation is not guaranteed.
<details>
<summary>Tested on</summary>
- Manjaro ARM Minimal (Raspberry Pi 4)
- Debian 11 (Raspberry Pi 4)
- Ubuntu Server 22.04 (Raspberry Pi 4)
- Raspbian 64bit (Raspberry Pi 3B+)
- Arch Linux (x86 PC), used for the screenshot above
</details>
## Introduction ## Introduction
@ -30,55 +45,66 @@ For more information and details visit [htop.dev](https://htop.dev).
## Build instructions ## Build instructions
### Prerequisite ### Prerequisite
List of build-time dependencies: List of build-time dependencies:
* standard GNU autotools-based C toolchain
- standard GNU autotools-based C toolchain
- C99 compliant compiler - C99 compliant compiler
- `autoconf` - `autoconf`
- `autotools` - `autotools`
* `ncurses` - `ncurses`
**Note about `ncurses`:** **Note about `ncurses`:**
> `htop` requires `ncurses` 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also `ncurses` usually comes in two flavours: > `htop` requires `ncurses` 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also `ncurses` usually comes in two flavours:
>* With Unicode support. >
>* Without Unicode support. >- With Unicode support.
>- Without Unicode support.
> >
> This is also something that is reflected in the package name on Debian/Ubuntu (via the additional 'w' - 'w'ide character support). > This is also something that is reflected in the package name on Debian/Ubuntu (via the additional 'w' - 'w'ide character support).
List of additional build-time dependencies (based on feature flags): List of additional build-time dependencies (based on feature flags):
* `sensors`
* `hwloc` - `sensors`
* `libcap` (v2.21 or later) - `hwloc`
* `libnl-3` - `libcap` (v2.21 or later)
- `libnl-3`
Install these and other required packages for C development from your package manager. Install these and other required packages for C development from your package manager.
**Debian/Ubuntu** **Debian/Ubuntu**
~~~ shell ~~~ shell
sudo apt install libncursesw5-dev autotools-dev autoconf build-essential sudo apt install libncursesw5-dev autotools-dev autoconf build-essential
~~~ ~~~
**Fedora/RHEL** **Fedora/RHEL**
~~~ shell ~~~ shell
sudo dnf install ncurses-devel automake autoconf gcc sudo dnf install ncurses-devel automake autoconf gcc
~~~ ~~~
**Archlinux/Manjaro** **Archlinux/Manjaro**
~~~ shell ~~~ shell
sudo pacman -S ncurses automake autoconf gcc sudo pacman -S ncurses automake autoconf gcc
~~~ ~~~
**macOS** **macOS**
~~~ shell ~~~ shell
brew install ncurses automake autoconf gcc brew install ncurses automake autoconf gcc
~~~ ~~~
### Compile from source: ### Compile from source
To compile from source, download from the Git repository (`git clone` or downloads from [GitHub releases](https://github.com/htop-dev/htop/releases/)), then run: To compile from source, download from the Git repository (`git clone` or downloads from [GitHub releases](https://github.com/htop-dev/htop/releases/)), then run:
~~~ shell ~~~ shell
./autogen.sh && ./configure && make ./autogen.sh && ./configure && make
~~~ ~~~
### Install ### Install
To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`. To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`.
### Build Options ### Build Options
@ -87,80 +113,85 @@ To install on the local system run `make install`. By default `make install` ins
#### Generic #### Generic
* `--enable-unicode`: - `--enable-unicode`:
enable Unicode support enable Unicode support
- dependency: *libncursesw* - dependency: *libncursesw*
- default: *yes* - default: *yes*
* `--enable-affinity`: - `--enable-affinity`:
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
- default: *check* - default: *check*
* `--enable-hwloc`: - `--enable-hwloc`:
enable hwloc support for CPU affinity; disables affinity support enable hwloc support for CPU affinity; disables affinity support
- dependency: *libhwloc* - dependency: *libhwloc*
- default: *no* - default: *no*
* `--enable-static`: - `--enable-static`:
build a static htop binary; hwloc and delay accounting are not supported build a static htop binary; hwloc and delay accounting are not supported
- default: *no* - default: *no*
* `--enable-debug`: - `--enable-debug`:
Enable asserts and internal sanity checks; implies a performance penalty Enable asserts and internal sanity checks; implies a performance penalty
- default: *no* - default: *no*
#### Performance Co-Pilot #### Performance Co-Pilot
* `--enable-pcp`: - `--enable-pcp`:
enable Performance Co-Pilot support via a new pcp-htop utility enable Performance Co-Pilot support via a new pcp-htop utility
- dependency: *libpcp* - dependency: *libpcp*
- default: *no* - default: *no*
#### Linux #### Linux
* `--enable-sensors`: - `--enable-sensors`:
enable libsensors(3) support for reading temperature data enable libsensors(3) support for reading temperature data
- dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available - dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
- default: *check* - default: *check*
* `--enable-capabilities`: - `--enable-capabilities`:
enable Linux capabilities support enable Linux capabilities support
- dependency: *libcap* - dependency: *libcap*
- default: *check* - default: *check*
* `--with-proc`: - `--with-proc`:
location of a Linux-compatible proc filesystem location of a Linux-compatible proc filesystem
- default: */proc* - default: */proc*
* `--enable-openvz`: - `--enable-openvz`:
enable OpenVZ support enable OpenVZ support
- default: *no* - default: *no*
* `--enable-vserver`: - `--enable-vserver`:
enable VServer support enable VServer support
- default: *no* - default: *no*
* `--enable-ancient-vserver`: - `--enable-ancient-vserver`:
enable ancient VServer support (implies `--enable-vserver`) enable ancient VServer support (implies `--enable-vserver`)
- default: *no* - default: *no*
* `--enable-delayacct`: - `--enable-delayacct`:
enable Linux delay accounting support enable Linux delay accounting support
- dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3* - dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
- default: *check* - default: *check*
## Runtime dependencies
## Runtime dependencies:
`htop` has a set of fixed minimum runtime dependencies, which is kept as minimal as possible: `htop` has a set of fixed minimum runtime dependencies, which is kept as minimal as possible:
* `ncurses` libraries for terminal handling (wide character support).
### Runtime optional dependencies: - `ncurses` libraries for terminal handling (wide character support).
### Runtime optional dependencies
`htop` has a set of fixed optional dependencies, depending on build/configure option used: `htop` has a set of fixed optional dependencies, depending on build/configure option used:
#### Linux #### Linux
* `libdl`, if not building a static binary, is always required when support for optional dependencies (i.e. `libsensors`, `libsystemd`) is present.
* `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`. - `libdl`, if not building a static binary, is always required when support for optional dependencies (i.e. `libsensors`, `libsystemd`) is present.
* `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`. - `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`.
* `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely. - `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
- `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely.
`htop` checks for the availability of the actual runtime libraries as `htop` runs. `htop` checks for the availability of the actual runtime libraries as `htop` runs.
#### BSD #### BSD
On most BSD systems `kvm` is a requirement to read kernel information. On most BSD systems `kvm` is a requirement to read kernel information.
More information on required and optional dependencies can be found in [configure.ac](configure.ac). More information on required and optional dependencies can be found in [configure.ac](configure.ac).
## Usage ## Usage
See the manual page (`man htop`) or the help menu (**F1** or **h** inside `htop`) for a list of supported key commands. See the manual page (`man htop`) or the help menu (**F1** or **h** inside `htop`) for a list of supported key commands.
## Support ## Support

View File

@ -315,6 +315,7 @@ void ScreensPanel_update(Panel* super) {
ScreensPanel* this = (ScreensPanel*) super; ScreensPanel* this = (ScreensPanel*) super;
int size = Panel_size(super); int size = Panel_size(super);
this->settings->changed = true; this->settings->changed = true;
this->settings->lastUpdate++;
this->settings->screens = xReallocArray(this->settings->screens, size + 1, sizeof(ScreenSettings*)); this->settings->screens = xReallocArray(this->settings->screens, size + 1, sizeof(ScreenSettings*));
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i); ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);

File diff suppressed because it is too large Load Diff

View File

@ -96,6 +96,7 @@ typedef struct Settings_ {
#endif #endif
bool changed; bool changed;
uint64_t lastUpdate;
} Settings; } Settings;
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu)) #define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))

View File

@ -6,8 +6,8 @@ in the source distribution for its full text.
*/ */
#include "SignalsPanel.h" #include "SignalsPanel.h"
// the above contains #include <signal.h> so do not add that here again (breaks Solaris build)
#include <signal.h>
#include <stdbool.h> #include <stdbool.h>
#include "FunctionBar.h" #include "FunctionBar.h"

View File

@ -7,7 +7,11 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "config.h" // IWYU pragma: keep
#ifndef HTOP_SOLARIS
#include <signal.h> #include <signal.h>
#endif
#include "Panel.h" #include "Panel.h"

29
TempMeter.c Normal file
View File

@ -0,0 +1,29 @@
#include "TempMeter.h"
#include "CRT.h"
#include "Object.h"
#include "Platform.h"
#include "XUtils.h"
static const int TempMeter_attributes[] = {
TEMP};
static void TempMeter_updateValues(Meter *this)
{
float temp_c = Platform_getTemp();
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1lf °C", temp_c);
}
const MeterClass TempMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete},
.updateValues = TempMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = TempMeter_attributes,
.name = "Temp",
.uiName = "CPU Temperature",
.caption = "CPU/Temperature: "};

8
TempMeter.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef HEADER_TempMeter
#define HEADER_TempMeter
#include "Meter.h"
extern const MeterClass TempMeter_class;
#endif

View File

@ -90,7 +90,9 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
char buffer[32] = {0}; char buffer[32] = {0};
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL); // Use of NULL in variadic functions must have a pointer cast.
// The NULL constant is not required by standard to have a pointer type.
execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, (char *)NULL);
// Should never reach here, unless execlp fails ... // Should never reach here, unless execlp fails ...
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.";

View File

@ -29,6 +29,8 @@ Vector* Vector_new(const ObjectClass* type, bool owner, int size) {
this->items = 0; this->items = 0;
this->type = type; this->type = type;
this->owner = owner; this->owner = owner;
this->dirty_index = -1;
this->dirty_count = 0;
return this; return this;
} }
@ -44,10 +46,21 @@ void Vector_delete(Vector* this) {
free(this); free(this);
} }
static inline bool Vector_isDirty(const Vector* this) {
if (this->dirty_count > 0) {
assert(0 <= this->dirty_index && this->dirty_index < this->items);
assert(this->dirty_count <= this->items);
return true;
}
assert(this->dirty_index == -1);
return false;
}
#ifndef NDEBUG #ifndef NDEBUG
static bool Vector_isConsistent(const Vector* this) { static bool Vector_isConsistent(const Vector* this) {
assert(this->items <= this->arraySize); assert(this->items <= this->arraySize);
assert(!Vector_isDirty(this));
if (this->owner) { if (this->owner) {
for (int i = 0; i < this->items; i++) { for (int i = 0; i < this->items; i++) {
@ -60,15 +73,14 @@ static bool Vector_isConsistent(const Vector* this) {
return true; return true;
} }
unsigned int Vector_count(const Vector* this) { bool Vector_countEquals(const Vector* this, unsigned int expectedCount) {
unsigned int items = 0; unsigned int n = 0;
for (int i = 0; i < this->items; i++) { for (int i = 0; i < this->items; i++) {
if (this->array[i]) { if (this->array[i]) {
items++; n++;
} }
} }
assert(items == (unsigned int)this->items); return n == expectedCount;
return items;
} }
Object* Vector_get(const Vector* this, int idx) { Object* Vector_get(const Vector* this, int idx) {
@ -88,13 +100,16 @@ int Vector_size(const Vector* this) {
void Vector_prune(Vector* this) { void Vector_prune(Vector* this) {
assert(Vector_isConsistent(this)); assert(Vector_isConsistent(this));
if (this->owner) { if (this->owner) {
for (int i = 0; i < this->items; i++) for (int i = 0; i < this->items; i++) {
if (this->array[i]) { if (this->array[i]) {
Object_delete(this->array[i]); Object_delete(this->array[i]);
//this->array[i] = NULL; this->array[i] = NULL;
}
} }
} }
this->items = 0; this->items = 0;
this->dirty_index = -1;
this->dirty_count = 0;
} }
//static int comparisons = 0; //static int comparisons = 0;
@ -242,6 +257,58 @@ Object* Vector_remove(Vector* this, int idx) {
} }
} }
Object* Vector_softRemove(Vector* this, int idx) {
assert(idx >= 0 && idx < this->items);
Object* removed = this->array[idx];
assert(removed);
if (removed) {
this->array[idx] = NULL;
this->dirty_count++;
if (this->dirty_index < 0 || idx < this->dirty_index) {
this->dirty_index = idx;
}
if (this->owner) {
Object_delete(removed);
return NULL;
}
}
return removed;
}
void Vector_compact(Vector* this) {
if (!Vector_isDirty(this)) {
return;
}
const int size = this->items;
assert(0 <= this->dirty_index && this->dirty_index < size);
assert(this->array[this->dirty_index] == NULL);
int idx = this->dirty_index;
/* one deletion: use memmove, which should be faster */
if (this->dirty_count == 1) {
memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx - 1) * sizeof(this->array[0]));
} else {
/* multiple deletions */
for (int i = idx + 1; i < size; i++) {
if (this->array[i]) {
this->array[idx++] = this->array[i];
}
}
}
this->items -= this->dirty_count;
this->dirty_index = -1;
this->dirty_count = 0;
assert(Vector_isConsistent(this));
}
void Vector_moveUp(Vector* this, int idx) { void Vector_moveUp(Vector* this, int idx) {
assert(idx >= 0 && idx < this->items); assert(idx >= 0 && idx < this->items);
assert(Vector_isConsistent(this)); assert(Vector_isConsistent(this));

View File

@ -22,6 +22,11 @@ typedef struct Vector_ {
int arraySize; int arraySize;
int growthRate; int growthRate;
int items; int items;
/* lowest index of a pending soft remove/delete operation,
used to speed up compaction */
int dirty_index;
/* count of soft deletes, required for Vector_count to work in debug mode */
int dirty_count;
bool owner; bool owner;
} Vector; } Vector;
@ -44,6 +49,15 @@ Object* Vector_take(Vector* this, int idx);
Object* Vector_remove(Vector* this, int idx); Object* Vector_remove(Vector* this, int idx);
/* Vector_softRemove marks the item at index idx for deletion without
reclaiming any space. If owned, the item is immediately freed.
Vector_compact must be called to reclaim space.*/
Object* Vector_softRemove(Vector* this, int idx);
/* Vector_compact reclaims space free'd up by Vector_softRemove, if any. */
void Vector_compact(Vector* this);
void Vector_moveUp(Vector* this, int idx); void Vector_moveUp(Vector* this, int idx);
void Vector_moveDown(Vector* this, int idx); void Vector_moveDown(Vector* this, int idx);
@ -54,7 +68,11 @@ void Vector_set(Vector* this, int idx, void* data_);
Object* Vector_get(const Vector* this, int idx); Object* Vector_get(const Vector* this, int idx);
int Vector_size(const Vector* this); int Vector_size(const Vector* this);
unsigned int Vector_count(const Vector* this);
/* Vector_countEquals returns true if the number of non-NULL items
in the Vector is equal to expectedCount. This is only for debugging
and consistency checks. */
bool Vector_countEquals(const Vector* this, unsigned int expectedCount);
#else /* NDEBUG */ #else /* NDEBUG */

View File

@ -115,7 +115,9 @@ inline bool String_contains_i(const char* s1, const char* s2, bool multi) {
char* String_cat(const char* s1, const char* s2) { char* String_cat(const char* s1, const char* s2) {
const size_t l1 = strlen(s1); const size_t l1 = strlen(s1);
const size_t l2 = strlen(s2); const size_t l2 = strlen(s2);
assert(SIZE_MAX - l1 > l2); if (SIZE_MAX - l1 <= l2) {
fail();
}
char* out = xMalloc(l1 + l2 + 1); char* out = xMalloc(l1 + l2 + 1);
memcpy(out, s1, l1); memcpy(out, s1, l1);
memcpy(out + l1, s2, l2); memcpy(out + l1, s2, l2);

View File

@ -17,60 +17,61 @@ in the source distribution for its full text.
#include "Compat.h" #include "Compat.h"
#include "Macros.h" #include "Macros.h"
void fail(void) ATTR_NORETURN; void fail(void) ATTR_NORETURN;
void* xMalloc(size_t size) ATTR_ALLOC_SIZE1(1) ATTR_MALLOC; void *xMalloc(size_t size) ATTR_ALLOC_SIZE1(1) ATTR_MALLOC;
void* xMallocArray(size_t nmemb, size_t size) ATTR_ALLOC_SIZE2(1, 2) ATTR_MALLOC; void *xMallocArray(size_t nmemb, size_t size) ATTR_ALLOC_SIZE2(1, 2) ATTR_MALLOC;
void* xCalloc(size_t nmemb, size_t size) ATTR_ALLOC_SIZE2(1, 2) ATTR_MALLOC; void *xCalloc(size_t nmemb, size_t size) ATTR_ALLOC_SIZE2(1, 2) ATTR_MALLOC;
void* xRealloc(void* ptr, size_t size) ATTR_ALLOC_SIZE1(2); void *xRealloc(void *ptr, size_t size) ATTR_ALLOC_SIZE1(2);
void* xReallocArray(void* ptr, size_t nmemb, size_t size) ATTR_ALLOC_SIZE2(2, 3); void *xReallocArray(void *ptr, size_t nmemb, size_t size) ATTR_ALLOC_SIZE2(2, 3);
void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size) ATTR_ALLOC_SIZE2(3, 4); void *xReallocArrayZero(void *ptr, size_t prevmemb, size_t newmemb, size_t size) ATTR_ALLOC_SIZE2(3, 4);
/* /*
* String_startsWith gives better performance if strlen(match) can be computed * String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :) * at compile time (e.g. when they are immutable string literals). :)
*/ */
static inline bool String_startsWith(const char* s, const char* match) { static inline bool String_startsWith(const char *s, const char *match)
{
return strncmp(s, match, strlen(match)) == 0; return strncmp(s, match, strlen(match)) == 0;
} }
bool String_contains_i(const char* s1, const char* s2, bool multi); bool String_contains_i(const char *s1, const char *s2, bool multi);
static inline bool String_eq(const char* s1, const char* s2) { static inline bool String_eq(const char *s1, const char *s2)
{
return strcmp(s1, s2) == 0; return strcmp(s1, s2) == 0;
} }
char* String_cat(const char* s1, const char* s2) ATTR_MALLOC; char *String_cat(const char *s1, const char *s2) ATTR_MALLOC;
char* String_trim(const char* in) ATTR_MALLOC; char *String_trim(const char *in) ATTR_MALLOC;
char** String_split(const char* s, char sep, size_t* n); char **String_split(const char *s, char sep, size_t *n);
void String_freeArray(char** s); void String_freeArray(char **s);
char* String_readLine(FILE* fd) ATTR_MALLOC; char *String_readLine(FILE *fd) ATTR_MALLOC;
/* Always null-terminates dest. Caller must pass a strictly positive size. */ /* Always null-terminates dest. Caller must pass a strictly positive size. */
size_t String_safeStrncpy(char* restrict dest, const char* restrict src, size_t size); size_t String_safeStrncpy(char *restrict dest, const char *restrict src, size_t size);
ATTR_FORMAT(printf, 2, 3) ATTR_FORMAT(printf, 2, 3)
int xAsprintf(char** strp, const char* fmt, ...); int xAsprintf(char **strp, const char *fmt, ...);
ATTR_FORMAT(printf, 3, 4) ATTR_FORMAT(printf, 3, 4)
int xSnprintf(char* buf, size_t len, const char* fmt, ...); int xSnprintf(char *buf, size_t len, const char *fmt, ...);
char* xStrdup(const char* str) ATTR_NONNULL ATTR_MALLOC; char *xStrdup(const char *str) ATTR_NONNULL ATTR_MALLOC;
void free_and_xStrdup(char** ptr, const char* str); void free_and_xStrdup(char **ptr, const char *str);
char* xStrndup(const char* str, size_t len) ATTR_NONNULL ATTR_MALLOC; char *xStrndup(const char *str, size_t len) ATTR_NONNULL ATTR_MALLOC;
ssize_t xReadfile(const char* pathname, void* buffer, size_t count); ssize_t xReadfile(const char *pathname, void *buffer, size_t count);
ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count); ssize_t xReadfileat(openat_arg_t dirfd, const char *pathname, void *buffer, size_t count);
#endif #endif

View File

@ -6,7 +6,7 @@
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
AC_PREREQ([2.69]) AC_PREREQ([2.69])
AC_INIT([htop], [3.2.0], [htop@groups.io], [], [https://htop.dev/]) AC_INIT([htop], [3.2.1], [htop@groups.io], [], [https://htop.dev/])
AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])

View File

@ -39,7 +39,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },

View File

@ -219,7 +219,7 @@ The primary user documentation should be the man file which you can find in `hto
Additional documentation, like this file, should be written in gh-style markdown. Additional documentation, like this file, should be written in gh-style markdown.
Make each sentence one line. Make each sentence one line.
Markdown will combined these in output formats. Markdown will combine these in output formats.
It does only insert a paragraph if you insert a blank line into the source file. It does only insert a paragraph if you insert a blank line into the source file.
This way git can better diff and present the changes when documentation is altered. This way git can better diff and present the changes when documentation is altered.

View File

@ -38,7 +38,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },

View File

@ -37,7 +37,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },

View File

@ -227,6 +227,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) {
void Platform_setMemoryValues(Meter* this) { void Platform_setMemoryValues(Meter* this) {
const ProcessList* pl = this->pl; const ProcessList* pl = this->pl;
const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl;
this->total = pl->totalMem; this->total = pl->totalMem;
this->values[0] = pl->usedMem; this->values[0] = pl->usedMem;
@ -234,6 +235,16 @@ void Platform_setMemoryValues(Meter* this) {
// this->values[2] = "shared memory, like tmpfs and shm" // this->values[2] = "shared memory, like tmpfs and shm"
this->values[3] = pl->cachedMem; this->values[3] = pl->cachedMem;
// this->values[4] = "available memory" // this->values[4] = "available memory"
if (fpl->zfs.enabled) {
// ZFS does not shrink below the value of zfs_arc_min.
unsigned long long int shrinkableSize = 0;
if (fpl->zfs.size > fpl->zfs.min)
shrinkableSize = fpl->zfs.size - fpl->zfs.min;
this->values[0] -= shrinkableSize;
this->values[3] += shrinkableSize;
// this->values[4] += shrinkableSize;
}
} }
void Platform_setSwapValues(Meter* this) { void Platform_setSwapValues(Meter* this) {

View File

@ -15,6 +15,7 @@ in the source distribution for its full text.
static int MIB_kstat_zfs_misc_arcstats_size[5]; static int MIB_kstat_zfs_misc_arcstats_size[5];
static int MIB_kstat_zfs_misc_arcstats_c_min[5];
static int MIB_kstat_zfs_misc_arcstats_c_max[5]; static int MIB_kstat_zfs_misc_arcstats_c_max[5];
static int MIB_kstat_zfs_misc_arcstats_mfu_size[5]; static int MIB_kstat_zfs_misc_arcstats_mfu_size[5];
static int MIB_kstat_zfs_misc_arcstats_mru_size[5]; static int MIB_kstat_zfs_misc_arcstats_mru_size[5];
@ -35,6 +36,7 @@ void openzfs_sysctl_init(ZfsArcStats* stats) {
len = 5; len = 5;
sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len);
sysctlnametomib("kstat.zfs.misc.arcstats.c_min", MIB_kstat_zfs_misc_arcstats_c_min, &len);
sysctlnametomib("kstat.zfs.misc.arcstats.c_max", MIB_kstat_zfs_misc_arcstats_c_max, &len); sysctlnametomib("kstat.zfs.misc.arcstats.c_max", MIB_kstat_zfs_misc_arcstats_c_max, &len);
sysctlnametomib("kstat.zfs.misc.arcstats.mfu_size", MIB_kstat_zfs_misc_arcstats_mfu_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.mfu_size", MIB_kstat_zfs_misc_arcstats_mfu_size, &len);
sysctlnametomib("kstat.zfs.misc.arcstats.mru_size", MIB_kstat_zfs_misc_arcstats_mru_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.mru_size", MIB_kstat_zfs_misc_arcstats_mru_size, &len);
@ -61,6 +63,10 @@ void openzfs_sysctl_updateArcStats(ZfsArcStats* stats) {
sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len, NULL, 0); sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len, NULL, 0);
stats->size /= 1024; stats->size /= 1024;
len = sizeof(stats->min);
sysctl(MIB_kstat_zfs_misc_arcstats_c_min, 5, &(stats->min), &len, NULL, 0);
stats->min /= 1024;
len = sizeof(stats->max); len = sizeof(stats->max);
sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len, NULL, 0); sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len, NULL, 0);
stats->max /= 1024; stats->max /= 1024;

View File

@ -57,7 +57,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, }, [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, },
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, }, [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
@ -86,9 +86,9 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, },
[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 = "CPUD%", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = " IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWPD%", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, }, [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
#endif #endif
[M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, }, [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, }, [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
@ -270,9 +270,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
break; break;
} }
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 4, &attr); break; case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 5, &attr); break;
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 4, &attr); break; case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 5, &attr); break;
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 4, &attr); break; case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break;
#endif #endif
case CTXT: case CTXT:
if (lp->ctxt_diff > 1000) { if (lp->ctxt_diff > 1000) {

View File

@ -166,6 +166,28 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
#endif #endif
static unsigned int scanAvailableCPUsFromCPUinfo(LinuxProcessList* this) {
FILE* file = fopen(PROCCPUINFOFILE, "r");
if (file == NULL)
return this->super.existingCPUs;
unsigned int availableCPUs = 0;
while (!feof(file)) {
char buffer[PROC_LINE_LENGTH];
if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
break;
if (String_startsWith(buffer, "processor"))
availableCPUs++;
}
fclose(file);
return availableCPUs ? availableCPUs : 1;
}
static void LinuxProcessList_updateCPUcount(ProcessList* super) { static void LinuxProcessList_updateCPUcount(ProcessList* super) {
/* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF /* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF
* https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD
@ -240,6 +262,12 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
if (existing < 1) if (existing < 1)
return; return;
if (Running_containerized) {
/* LXC munges /proc/cpuinfo but not the /sys/devices/system/cpu/ files,
* so limit the visible CPUs to what the guest has been configured to see: */
currExisting = active = scanAvailableCPUsFromCPUinfo(this);
}
#ifdef HAVE_SENSORS_SENSORS_H #ifdef HAVE_SENSORS_SENSORS_H
/* When started with offline CPUs, libsensors does not monitor those, /* When started with offline CPUs, libsensors does not monitor those,
* even when they become online. */ * even when they become online. */
@ -248,7 +276,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
#endif #endif
super->activeCPUs = active; super->activeCPUs = active;
assert(existing == currExisting); assert(Running_containerized || (existing == currExisting));
super->existingCPUs = currExisting; super->existingCPUs = currExisting;
} }
@ -1323,7 +1351,8 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
if (process->procExeDeleted) if (process->procExeDeleted)
filename[filenameLen - markerLen] = '\0'; filename[filenameLen - markerLen] = '\0';
process->mergedCommand.exeChanged |= oldExeDeleted ^ process->procExeDeleted; if (oldExeDeleted != process->procExeDeleted)
process->mergedCommand.lastUpdate = 0;
} }
Process_updateExe(process, filename); Process_updateExe(process, filename);
@ -1389,6 +1418,21 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo
return out; return out;
} }
static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int seconds) {
assert(pl->realtimeMs > 0);
/* Starttime might not yet be parsed */
if (proc->starttime_ctime <= 0)
return false;
uint64_t realtime = pl->realtimeMs / 1000;
if (realtime < (uint64_t)proc->starttime_ctime)
return false;
return realtime - proc->starttime_ctime > seconds;
}
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) { static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) {
ProcessList* pl = (ProcessList*) this; ProcessList* pl = (ProcessList*) this;
const struct dirent* entry; const struct dirent* entry;
@ -1446,6 +1490,15 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
if (parent && pid == parent->pid) if (parent && pid == parent->pid)
continue; continue;
#ifdef HAVE_OPENAT
int procFd = openat(dirFd, entry->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (procFd < 0)
continue;
#else
char procFd[4096];
xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
#endif
bool preExisting; bool preExisting;
Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new); Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
LinuxProcess* lp = (LinuxProcess*) proc; LinuxProcess* lp = (LinuxProcess*) proc;
@ -1453,15 +1506,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
proc->tgid = parent ? parent->pid : pid; proc->tgid = parent ? parent->pid : pid;
proc->isUserlandThread = proc->pid != proc->tgid; proc->isUserlandThread = proc->pid != proc->tgid;
#ifdef HAVE_OPENAT
int procFd = openat(dirFd, entry->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (procFd < 0)
goto errorReadingProcess;
#else
char procFd[4096];
xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
#endif
LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period); LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period);
/* /*
@ -1497,7 +1541,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
bool prev = proc->usesDeletedLib; bool prev = proc->usesDeletedLib;
if (!proc->isKernelThread && !proc->isUserlandThread && if (!proc->isKernelThread && !proc->isUserlandThread &&
((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted))) { ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(pl, proc, 10)))) {
// Check if we really should recalculate the M_LRS value for this process // Check if we really should recalculate the M_LRS value for this process
uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime; uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime;
@ -1514,7 +1558,8 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
lp->m_lrs = (proc->isUserlandThread && parent) ? ((const LinuxProcess*)parent)->m_lrs : 0; lp->m_lrs = (proc->isUserlandThread && parent) ? ((const LinuxProcess*)parent)->m_lrs : 0;
} }
proc->mergedCommand.exeChanged |= prev ^ proc->usesDeletedLib; if (prev != proc->usesDeletedLib)
proc->mergedCommand.lastUpdate = 0;
} }
if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {
@ -1653,8 +1698,13 @@ errorReadingProcess:
#endif #endif
if (preExisting) { if (preExisting) {
ProcessList_remove(pl, proc); /*
* The only real reason for coming here (apart from Linux violating the /proc API)
* would be the process going away with its /proc files disappearing (!HAVE_OPENAT).
* However, we want to keep in the process list for now for the "highlight dying" mode.
*/
} else { } else {
/* A really short-lived process that we don't have full info about */
Process_delete((Object*)proc); Process_delete((Object*)proc);
} }
} }
@ -1874,6 +1924,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
switch (buffer[0]) { switch (buffer[0]) {
case 'c': case 'c':
tryRead("c_min", &lpl->zfs.min);
tryRead("c_max", &lpl->zfs.max); tryRead("c_max", &lpl->zfs.max);
tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed); tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed);
break; break;
@ -1908,6 +1959,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0); lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0);
lpl->zfs.size /= 1024; lpl->zfs.size /= 1024;
lpl->zfs.min /= 1024;
lpl->zfs.max /= 1024; lpl->zfs.max /= 1024;
lpl->zfs.MFU /= 1024; lpl->zfs.MFU /= 1024;
lpl->zfs.MRU /= 1024; lpl->zfs.MRU /= 1024;
@ -2101,16 +2153,11 @@ static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) {
if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
break; break;
if ( if (sscanf(buffer, "processor : %d", &cpuid) == 1) {
(sscanf(buffer, "processor : %d", &cpuid) == 1) ||
(sscanf(buffer, "processor: %d", &cpuid) == 1)
) {
continue; continue;
} else if ( } else if (
(sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
(sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) || (sscanf(buffer, "clock : %lfMHz", &frequency) == 1)
(sscanf(buffer, "clock : %lfMHz", &frequency) == 1) ||
(sscanf(buffer, "clock: %lfMHz", &frequency) == 1)
) { ) {
if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) { if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) {
continue; continue;

File diff suppressed because it is too large Load Diff

View File

@ -34,10 +34,9 @@ in the source distribution for its full text.
/* GNU/Hurd does not have PATH_MAX in limits.h */ /* GNU/Hurd does not have PATH_MAX in limits.h */
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 4096 #define PATH_MAX 4096
#endif #endif
extern const ScreenDefaults Platform_defaultScreens[]; extern const ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens; extern const unsigned int Platform_numberOfDefaultScreens;
@ -46,88 +45,98 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals; extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[]; extern const MeterClass *const Platform_meterTypes[];
bool Platform_init(void); bool Platform_init(void);
void Platform_done(void); void Platform_done(void);
void Platform_setBindings(Htop_Action* keys); extern bool Running_containerized;
void Platform_setBindings(Htop_Action *keys);
int Platform_getUptime(void); int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen); float Platform_getTemp(void);
float Platform_getFreq(void);
void Platform_getLoadAverage(double *one, double *five, double *fifteen);
int Platform_getMaxPid(void); int Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, unsigned int cpu); double Platform_setCPUValues(Meter *this, unsigned int cpu);
void Platform_setMemoryValues(Meter* this); void Platform_setMemoryValues(Meter *this);
void Platform_setSwapValues(Meter* this); void Platform_setSwapValues(Meter *this);
void Platform_setZramValues(Meter* this); void Platform_setZramValues(Meter *this);
void Platform_setZfsArcValues(Meter* this); void Platform_setZfsArcValues(Meter *this);
void Platform_setZfsCompressedArcValues(Meter* this); void Platform_setZfsCompressedArcValues(Meter *this);
char* Platform_getProcessEnv(pid_t pid); char *Platform_getProcessEnv(pid_t pid);
char* Platform_getInodeFilename(pid_t pid, ino_t inode); char *Platform_getInodeFilename(pid_t pid, ino_t inode);
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); FileLocks_ProcessData *Platform_getProcessLocks(pid_t pid);
void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred); void Platform_getPressureStall(const char *file, bool some, double *ten, double *sixty, double *threehundred);
bool Platform_getDiskIO(DiskIOData* data); bool Platform_getDiskIO(DiskIOData *data);
bool Platform_getNetworkIO(NetworkIOData* data); bool Platform_getNetworkIO(NetworkIOData *data);
void Platform_getBattery(double* percent, ACPresence* isOnAC); void Platform_getBattery(double *percent, ACPresence *isOnAC);
static inline void Platform_getHostname(char* buffer, size_t size) { static inline void Platform_getHostname(char *buffer, size_t size)
{
Generic_hostname(buffer, size); Generic_hostname(buffer, size);
} }
static inline void Platform_getRelease(char** string) { static inline void Platform_getRelease(char **string)
{
*string = Generic_uname(); *string = Generic_uname();
} }
#ifdef HAVE_LIBCAP #ifdef HAVE_LIBCAP
#define PLATFORM_LONG_OPTIONS \ #define PLATFORM_LONG_OPTIONS \
{"drop-capabilities", optional_argument, 0, 160}, {"drop-capabilities", optional_argument, 0, 160},
#else #else
#define PLATFORM_LONG_OPTIONS #define PLATFORM_LONG_OPTIONS
#endif #endif
void Platform_longOptionsUsage(const char* name); void Platform_longOptionsUsage(const char *name);
CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv); CommandLineStatus Platform_getLongOption(int opt, int argc, char **argv);
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) { static inline void Platform_gettime_realtime(struct timeval *tv, uint64_t *msec)
{
Generic_gettime_realtime(tv, msec); Generic_gettime_realtime(tv, msec);
} }
static inline void Platform_gettime_monotonic(uint64_t* msec) { static inline void Platform_gettime_monotonic(uint64_t *msec)
{
Generic_gettime_monotonic(msec); Generic_gettime_monotonic(msec);
} }
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } static inline Hashtable *Platform_dynamicMeters(void) { return NULL; }
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable *table) {}
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter *meter) {}
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter *meter) {}
static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter *meter, ATTR_UNUSED RichString *out) {}
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } static inline Hashtable *Platform_dynamicColumns(void) { return NULL; }
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable *table) {}
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } static inline const char *Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process *proc, ATTR_UNUSED RichString *str, ATTR_UNUSED unsigned int key) { return false; }
#endif #endif

View File

@ -219,7 +219,10 @@ static void updateViaExec(void) {
exit(1); exit(1);
dup2(fdnull, STDERR_FILENO); dup2(fdnull, STDERR_FILENO);
close(fdnull); close(fdnull);
execlp("systemctl", // Use of NULL in variadic functions must have a pointer cast.
// The NULL constant is not required by standard to have a pointer type.
execlp(
"systemctl",
"systemctl", "systemctl",
"show", "show",
"--property=SystemState", "--property=SystemState",
@ -227,7 +230,7 @@ static void updateViaExec(void) {
"--property=NNames", "--property=NNames",
"--property=NJobs", "--property=NJobs",
"--property=NInstalledJobs", "--property=NInstalledJobs",
NULL); (char *)NULL);
exit(127); exit(127);
} }
close(fdpair[1]); close(fdpair[1]);

View File

@ -144,7 +144,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
}, },
[PERCENT_CPU] = { [PERCENT_CPU] = {
.name = "PERCENT_CPU", .name = "PERCENT_CPU",
.title = "CPU% ", .title = " CPU%",
.description = "Percentage of the CPU time the process used in the last sampling", .description = "Percentage of the CPU time the process used in the last sampling",
.flags = 0, .flags = 0,
.defaultSortDesc = true, .defaultSortDesc = true,

View File

@ -311,7 +311,13 @@ char* Platform_getProcessEnv(pid_t pid) {
for (char** p = ptr; *p; p++) { for (char** p = ptr; *p; p++) {
size_t len = strlen(*p) + 1; size_t len = strlen(*p) + 1;
if (size + len > capacity) { while (size + len > capacity) {
if (capacity > (SIZE_MAX / 2)) {
free(env);
env = NULL;
goto end;
}
capacity *= 2; capacity *= 2;
env = xRealloc(env, capacity); env = xRealloc(env, capacity);
} }
@ -327,6 +333,7 @@ char* Platform_getProcessEnv(pid_t pid) {
env[size + 1] = 0; env[size + 1] = 0;
} }
end:
(void) kvm_close(kt); (void) kvm_close(kt);
return env; return env;
} }

View File

@ -142,7 +142,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
}, },
[PERCENT_CPU] = { [PERCENT_CPU] = {
.name = "PERCENT_CPU", .name = "PERCENT_CPU",
.title = "CPU% ", .title = " CPU%",
.description = "Percentage of the CPU time the process used in the last sampling", .description = "Percentage of the CPU time the process used in the last sampling",
.flags = 0, .flags = 0,
.defaultSortDesc = true, .defaultSortDesc = true,

View File

@ -269,7 +269,13 @@ char* Platform_getProcessEnv(pid_t pid) {
for (char** p = ptr; *p; p++) { for (char** p = ptr; *p; p++) {
size_t len = strlen(*p) + 1; size_t len = strlen(*p) + 1;
if (size + len > capacity) { while (size + len > capacity) {
if (capacity > (SIZE_MAX / 2)) {
free(env);
env = NULL;
goto end;
}
capacity *= 2; capacity *= 2;
env = xRealloc(env, capacity); env = xRealloc(env, capacity);
} }
@ -285,6 +291,7 @@ char* Platform_getProcessEnv(pid_t pid) {
env[size + 1] = 0; env[size + 1] = 0;
} }
end:
(void) kvm_close(kt); (void) kvm_close(kt);
return env; return env;
} }

View File

@ -81,6 +81,7 @@ typedef enum PCPMetric_ {
PCP_ZFS_ARC_BONUS_SIZE, /* zfs.arc.bonus_size */ PCP_ZFS_ARC_BONUS_SIZE, /* zfs.arc.bonus_size */
PCP_ZFS_ARC_COMPRESSED_SIZE, /* zfs.arc.compressed_size */ PCP_ZFS_ARC_COMPRESSED_SIZE, /* zfs.arc.compressed_size */
PCP_ZFS_ARC_UNCOMPRESSED_SIZE, /* zfs.arc.uncompressed_size */ PCP_ZFS_ARC_UNCOMPRESSED_SIZE, /* zfs.arc.uncompressed_size */
PCP_ZFS_ARC_C_MIN, /* zfs.arc.c_min */
PCP_ZFS_ARC_C_MAX, /* zfs.arc.c_max */ PCP_ZFS_ARC_C_MAX, /* zfs.arc.c_max */
PCP_ZFS_ARC_DBUF_SIZE, /* zfs.arc.dbuf_size */ PCP_ZFS_ARC_DBUF_SIZE, /* zfs.arc.dbuf_size */
PCP_ZFS_ARC_DNODE_SIZE, /* zfs.arc.dnode_size */ PCP_ZFS_ARC_DNODE_SIZE, /* zfs.arc.dnode_size */

View File

@ -54,7 +54,7 @@ const ProcessFieldData Process_fields[] = {
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, }, [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, },
[M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, }, [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
@ -74,7 +74,7 @@ const ProcessFieldData Process_fields[] = {
[CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, [CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, },
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, },
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, .defaultSortDesc = true, }, [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, .defaultSortDesc = true, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, .defaultSortDesc = true, }, [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = " IOD% ", .description = "Block I/O delay %", .flags = 0, .defaultSortDesc = true, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, .defaultSortDesc = true, }, [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, .defaultSortDesc = true, },
[M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, }, [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, }, [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },

View File

@ -601,6 +601,8 @@ static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) {
memset(&this->zfs, 0, sizeof(ZfsArcStats)); memset(&this->zfs, 0, sizeof(ZfsArcStats));
if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64)) if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
this->zfs.anon = value.ull / ONE_K; this->zfs.anon = value.ull / ONE_K;
if (PCPMetric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64))
this->zfs.min = value.ull / ONE_K;
if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64)) if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64))
this->zfs.max = value.ull / ONE_K; this->zfs.max = value.ull / ONE_K;
if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64)) if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))

View File

@ -178,6 +178,7 @@ static const char* Platform_metricNames[] = {
[PCP_ZFS_ARC_BONUS_SIZE] = "zfs.arc.bonus_size", [PCP_ZFS_ARC_BONUS_SIZE] = "zfs.arc.bonus_size",
[PCP_ZFS_ARC_COMPRESSED_SIZE] = "zfs.arc.compressed_size", [PCP_ZFS_ARC_COMPRESSED_SIZE] = "zfs.arc.compressed_size",
[PCP_ZFS_ARC_UNCOMPRESSED_SIZE] = "zfs.arc.uncompressed_size", [PCP_ZFS_ARC_UNCOMPRESSED_SIZE] = "zfs.arc.uncompressed_size",
[PCP_ZFS_ARC_C_MIN] = "zfs.arc.c_min",
[PCP_ZFS_ARC_C_MAX] = "zfs.arc.c_max", [PCP_ZFS_ARC_C_MAX] = "zfs.arc.c_max",
[PCP_ZFS_ARC_DBUF_SIZE] = "zfs.arc.dbuf_size", [PCP_ZFS_ARC_DBUF_SIZE] = "zfs.arc.dbuf_size",
[PCP_ZFS_ARC_DNODE_SIZE] = "zfs.arc.dnode_size", [PCP_ZFS_ARC_DNODE_SIZE] = "zfs.arc.dnode_size",
@ -510,8 +511,13 @@ void Platform_setMemoryValues(Meter* this) {
this->values[4] = pl->availableMem; this->values[4] = pl->availableMem;
if (ppl->zfs.enabled != 0) { if (ppl->zfs.enabled != 0) {
this->values[0] -= ppl->zfs.size; // ZFS does not shrink below the value of zfs_arc_min.
this->values[3] += ppl->zfs.size; unsigned long long int shrinkableSize = 0;
if (ppl->zfs.size > ppl->zfs.min)
shrinkableSize = ppl->zfs.size - ppl->zfs.min;
this->values[0] -= shrinkableSize;
this->values[3] += shrinkableSize;
this->values[4] += shrinkableSize;
} }
} }

View File

@ -267,16 +267,21 @@ static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr
envAccum* accump = accum; envAccum* accump = accum;
(void) Phandle; (void) Phandle;
(void) addr; (void) addr;
size_t thissz = strlen(str); size_t thissz = strlen(str);
if ((thissz + 2) > (accump->capacity - accump->size)) {
accump->env = xRealloc(accump->env, accump->capacity *= 2); while ((thissz + 2) > (accump->capacity - accump->size)) {
} if (accump->capacity > (SIZE_MAX / 2))
if ((thissz + 2) > (accump->capacity - accump->size)) {
return 1; return 1;
accump->capacity *= 2;
accump->env = xRealloc(accump->env, accump->capacity);
} }
strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size));
strlcpy( accump->env + accump->size, str, accump->capacity - accump->size);
strncpy( accump->env + accump->size + thissz + 1, "\n", 2); strncpy( accump->env + accump->size + thissz + 1, "\n", 2);
accump->size = accump->size + thissz + 1;
accump->size += thissz + 1;
return 0; return 0;
} }
@ -299,7 +304,8 @@ char* Platform_getProcessEnv(pid_t pid) {
Prelease(Phandle, 0); Prelease(Phandle, 0);
strncpy( envBuilder.env + envBuilder.size, "\0", 1); strncpy( envBuilder.env + envBuilder.size, "\0", 1);
return envBuilder.env;
return xRealloc(envBuilder.env, envBuilder.size + 1);
} }
char* Platform_getInodeFilename(pid_t pid, ino_t inode) { char* Platform_getInodeFilename(pid_t pid, ino_t inode) {

View File

@ -40,7 +40,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },

View File

@ -468,7 +468,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu / (double)32768) * (double)100.0; proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu / (double)32768) * (double)100.0;
Process_updateCPUFieldWidths(proc->percent_cpu); Process_updateCPUFieldWidths(proc->percent_cpu);
proc->time = _psinfo->pr_time.tv_sec; proc->time = _psinfo->pr_time.tv_sec * 100 + _psinfo->pr_time.tv_nsec / 10000000;
if (!preExisting) { // Tasks done only for NEW processes if (!preExisting) { // Tasks done only for NEW processes
proc->isUserlandThread = false; proc->isUserlandThread = false;
proc->starttime_ctime = _psinfo->pr_start.tv_sec; proc->starttime_ctime = _psinfo->pr_start.tv_sec;
@ -497,7 +497,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu / (double)32768) * (double)100.0; proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu / (double)32768) * (double)100.0;
Process_updateCPUFieldWidths(proc->percent_cpu); Process_updateCPUFieldWidths(proc->percent_cpu);
proc->time = _lwpsinfo->pr_time.tv_sec; proc->time = _lwpsinfo->pr_time.tv_sec * 100 + _lwpsinfo->pr_time.tv_nsec / 10000000;
if (!preExisting) { // Tasks done only for NEW LWPs if (!preExisting) { // Tasks done only for NEW LWPs
proc->isUserlandThread = true; proc->isUserlandThread = true;
proc->ppid = _psinfo->pr_pid * 1024; proc->ppid = _psinfo->pr_pid * 1024;

View File

@ -35,7 +35,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = " CPU%", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },

View File

@ -10,6 +10,7 @@ in the source distribution for its full text.
typedef struct ZfsArcStats_ { typedef struct ZfsArcStats_ {
int enabled; int enabled;
int isCompressed; int isCompressed;
unsigned long long int min;
unsigned long long int max; unsigned long long int max;
unsigned long long int size; unsigned long long int size;
unsigned long long int MFU; unsigned long long int MFU;