aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Lange <DLange@git.local>2022-06-03 08:55:20 +0200
committerDaniel Lange <DLange@git.local>2022-06-03 08:55:20 +0200
commit937052b231259a47d881d539ad5748245ef55b99 (patch)
tree41071229fa007d8cf6902bb9253d2acd8cc66979
parent1b805a31720727008b32b1129a167758519fd4db (diff)
downloaddebian_htop-upstream.tar.gz
debian_htop-upstream.tar.bz2
debian_htop-upstream.zip
New upstream version 3.2.1upstream/3.2.1upstream
-rw-r--r--Action.c83
-rw-r--r--AvailableMetersPanel.c1
-rw-r--r--CRT.c22
-rw-r--r--CRT.h2
-rw-r--r--ChangeLog20
-rw-r--r--ColorsPanel.c1
-rw-r--r--DisplayOptionsPanel.c14
-rw-r--r--Hashtable.c2
-rw-r--r--HeaderOptionsPanel.c1
-rw-r--r--MetersPanel.c1
-rw-r--r--OpenFilesScreen.c4
-rw-r--r--OptionItem.c24
-rw-r--r--OptionItem.h10
-rw-r--r--Process.c101
-rw-r--r--Process.h17
-rw-r--r--ProcessList.c62
-rw-r--r--ProcessList.h2
-rw-r--r--ScreensPanel.c1
-rw-r--r--Settings.c2
-rw-r--r--Settings.h1
-rw-r--r--SignalsPanel.c2
-rw-r--r--SignalsPanel.h4
-rw-r--r--TraceScreen.c4
-rw-r--r--Vector.c81
-rw-r--r--Vector.h20
-rw-r--r--XUtils.c4
-rw-r--r--configure.ac2
-rw-r--r--darwin/DarwinProcess.c2
-rw-r--r--docs/styleguide.md2
-rw-r--r--dragonflybsd/DragonFlyBSDProcess.c2
-rw-r--r--freebsd/FreeBSDProcess.c2
-rw-r--r--freebsd/Platform.c11
-rw-r--r--generic/openzfs_sysctl.c6
-rw-r--r--linux/LinuxProcess.c14
-rw-r--r--linux/LinuxProcessList.c87
-rw-r--r--linux/Platform.c11
-rw-r--r--linux/Platform.h2
-rw-r--r--linux/SystemdMeter.c21
-rw-r--r--netbsd/NetBSDProcess.c2
-rw-r--r--netbsd/Platform.c9
-rw-r--r--openbsd/OpenBSDProcess.c2
-rw-r--r--openbsd/Platform.c9
-rw-r--r--pcp/PCPMetric.h1
-rw-r--r--pcp/PCPProcess.c4
-rw-r--r--pcp/PCPProcessList.c2
-rw-r--r--pcp/Platform.c10
-rw-r--r--solaris/Platform.c22
-rw-r--r--solaris/SolarisProcess.c2
-rw-r--r--solaris/SolarisProcessList.c4
-rw-r--r--unsupported/UnsupportedProcess.c2
-rw-r--r--zfs/ZfsArcStats.h1
51 files changed, 507 insertions, 211 deletions
diff --git a/Action.c b/Action.c
index ce3cd13..b765248 100644
--- a/Action.c
+++ b/Action.c
@@ -88,6 +88,7 @@ static void Action_runSetup(State* st) {
ScreenManager_run(scr, NULL, NULL, "Setup");
ScreenManager_delete(scr);
if (st->settings->changed) {
+ CRT_setMouse(st->settings->enableMouse);
Header_writeBackToSettings(st->header);
}
}
@@ -241,6 +242,9 @@ static Htop_Reaction actionToggleTreeView(State* st) {
static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
ScreenSettings* ss = st->settings->ss;
+ if (!ss->treeView) {
+ return HTOP_OK;
+ }
ss->allBranchesCollapsed = !ss->allBranchesCollapsed;
if (ss->allBranchesCollapsed)
ProcessList_collapseAllBranches(st->pl);
@@ -533,7 +537,7 @@ static const struct {
{ .key = " U: ", .roInactive = false, .info = "untag all processes" },
{ .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" },
{ .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))
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
#endif
@@ -570,46 +574,57 @@ static Htop_Reaction actionHelp(State* st) {
line++;
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], "[");
+ addbartext(CRT_colors[CPU_NICE_TEXT], "", "low");
+ addbartext(CRT_colors[CPU_NORMAL], "/", "normal");
+ addbartext(CRT_colors[CPU_SYSTEM], "/", "kernel");
if (st->settings->detailedCPUTime) {
- addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
- addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
- addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
- addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
- addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
- addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
- addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
- addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
- addattrstr(CRT_colors[BAR_SHADOW], " used%");
+ addbartext(CRT_colors[CPU_IRQ], "/", "irq");
+ addbartext(CRT_colors[CPU_SOFTIRQ], "/", "soft-irq");
+ addbartext(CRT_colors[CPU_STEAL], "/", "steal");
+ addbartext(CRT_colors[CPU_GUEST], "/", "guest");
+ addbartext(CRT_colors[CPU_IOWAIT], "/", "io-wait");
+ addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
} else {
- addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
- addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
- addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
- addattrstr(CRT_colors[CPU_GUEST], "virtualized");
- addattrstr(CRT_colors[BAR_SHADOW], " used%");
+ addbartext(CRT_colors[CPU_GUEST], "/", "guest");
+ addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
}
addattrstr(CRT_colors[BAR_BORDER], "]");
+
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Memory bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
- addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
- addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
- addattrstr(CRT_colors[MEMORY_SHARED], "shared"); addstr("/");
- addattrstr(CRT_colors[MEMORY_CACHE], "cache");
- addattrstr(CRT_colors[BAR_SHADOW], " used/total");
+ addbartext(CRT_colors[MEMORY_USED], "", "used");
+ addbartext(CRT_colors[MEMORY_BUFFERS_TEXT], "/", "buffers");
+ addbartext(CRT_colors[MEMORY_SHARED], "/", "shared");
+ addbartext(CRT_colors[MEMORY_CACHE], "/", "cache");
+ addbartext(CRT_colors[BAR_SHADOW], " ", "used");
+ addbartext(CRT_colors[BAR_SHADOW], "/", "total");
addattrstr(CRT_colors[BAR_BORDER], "]");
+
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
- addattrstr(CRT_colors[SWAP], "used");
+ addbartext(CRT_colors[SWAP], "", "used");
#ifdef HTOP_LINUX
- addstr("/");
- addattrstr(CRT_colors[SWAP_CACHE], "cache");
- addattrstr(CRT_colors[BAR_SHADOW], " used/total");
+ addbartext(CRT_colors[SWAP_CACHE], "/", "cache");
#else
- addattrstr(CRT_colors[BAR_SHADOW], " used/total");
+ addbartext(CRT_colors[SWAP_CACHE], " ", "");
#endif
+ addbartext(CRT_colors[BAR_SHADOW], " ", "used");
+ addbartext(CRT_colors[BAR_SHADOW], "/", "total");
addattrstr(CRT_colors[BAR_BORDER], "]");
+
+ line++;
+
+#undef addbartext
+
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
@@ -617,9 +632,23 @@ static Htop_Reaction actionHelp(State* st) {
}
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)
+
+ 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]);
- line++;
+#undef addattrstatestr
+
+ line += 2;
const bool readonly = Settings_isReadonly();
diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c
index ae289ec..368f494 100644
--- a/AvailableMetersPanel.c
+++ b/AvailableMetersPanel.c
@@ -79,6 +79,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
}
if (update) {
this->settings->changed = true;
+ this->settings->lastUpdate++;
Header_calculateHeight(header);
Header_updateData(header);
Header_draw(header);
diff --git a/CRT.c b/CRT.c
index 583b626..868f237 100644
--- a/CRT.c
+++ b/CRT.c
@@ -900,6 +900,20 @@ void CRT_resetSignalHandlers(void) {
signal(SIGQUIT, SIG_DFL);
}
+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();
@@ -993,13 +1007,7 @@ IGNORE_WCASTQUAL_END
#endif
CRT_treeStrAscii;
-#ifdef HAVE_GETMOUSE
-#if NCURSES_MOUSE_VERSION > 1
- mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
-#else
- mousemask(BUTTON1_RELEASED, NULL);
-#endif
-#endif
+ CRT_setMouse(settings->enableMouse);
CRT_degreeSign = initDegreeSign();
}
diff --git a/CRT.h b/CRT.h
index eae4722..297e896 100644
--- a/CRT.h
+++ b/CRT.h
@@ -185,6 +185,8 @@ extern int CRT_scrollWheelVAmount;
extern ColorScheme CRT_colorScheme;
+void CRT_setMouse(bool enabled);
+
void CRT_init(const Settings* settings, bool allowUnicode);
void CRT_done(void);
diff --git a/ChangeLog b/ChangeLog
index a7f4526..8f5266c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
* Support for displaying multiple tabs in the user interface
diff --git a/ColorsPanel.c b/ColorsPanel.c
index 50188f6..5900884 100644
--- a/ColorsPanel.c
+++ b/ColorsPanel.c
@@ -68,6 +68,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
this->settings->colorScheme = mark;
this->settings->changed = true;
+ this->settings->lastUpdate++;
CRT_setColors(mark);
clear();
diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c
index f5e64b1..fc23cb6 100644
--- a/DisplayOptionsPanel.c
+++ b/DisplayOptionsPanel.c
@@ -18,6 +18,7 @@ in the source distribution for its full text.
#include "Object.h"
#include "OptionItem.h"
#include "ProvideCurses.h"
+#include "ScreensPanel.h"
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@@ -43,6 +44,8 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
case KEY_RECLICK:
case ' ':
switch (OptionItem_kind(selected)) {
+ case OPTION_ITEM_TEXT:
+ break;
case OPTION_ITEM_CHECK:
CheckItem_toggle((CheckItem*)selected);
result = HANDLED;
@@ -69,6 +72,7 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED) {
this->settings->changed = true;
+ this->settings->lastUpdate++;
Header* header = this->scr->header;
Header_calculateHeight(header);
Header_reinit(header);
@@ -97,9 +101,17 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr;
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 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("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
diff --git a/Hashtable.c b/Hashtable.c
index a0cfc9e..2756b23 100644
--- a/Hashtable.c
+++ b/Hashtable.c
@@ -52,7 +52,7 @@ static void Hashtable_dump(const Hashtable* this) {
i,
this->buckets[i].key,
this->buckets[i].probe,
- this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)");
+ this->buckets[i].value);
if (this->buckets[i].value)
items++;
diff --git a/HeaderOptionsPanel.c b/HeaderOptionsPanel.c
index 22bcd09..25d1ddb 100644
--- a/HeaderOptionsPanel.c
+++ b/HeaderOptionsPanel.c
@@ -52,6 +52,7 @@ static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
Header_setLayout(this->scr->header, mark);
this->settings->changed = true;
+ this->settings->lastUpdate++;
ScreenManager_resize(this->scr);
diff --git a/MetersPanel.c b/MetersPanel.c
index 1bdefdb..97da630 100644
--- a/MetersPanel.c
+++ b/MetersPanel.c
@@ -184,6 +184,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED || sideMove) {
Header* header = this->scr->header;
this->settings->changed = true;
+ this->settings->lastUpdate++;
Header_calculateHeight(header);
ScreenManager_resize(this->scr);
}
diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c
index 2d19169..787e17b 100644
--- a/OpenFilesScreen.c
+++ b/OpenFilesScreen.c
@@ -120,7 +120,9 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
close(fdnull);
char buffer[32] = {0};
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);
}
close(fdpair[1]);
diff --git a/OptionItem.c b/OptionItem.c
index 7ab6900..962c0a9 100644
--- a/OptionItem.c
+++ b/OptionItem.c
@@ -25,6 +25,13 @@ static void OptionItem_delete(Object* cast) {
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) {
const CheckItem* this = (const CheckItem*)cast;
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 = {
.super = {
.extends = Class(OptionItem),
@@ -77,6 +94,7 @@ const OptionItemClass CheckItem_class = {
.kind = OPTION_ITEM_CHECK
};
+
const OptionItemClass NumberItem_class = {
.super = {
.extends = Class(OptionItem),
@@ -86,6 +104,12 @@ const OptionItemClass NumberItem_class = {
.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* this = AllocThis(CheckItem);
this->super.text = xStrdup(text);
diff --git a/OptionItem.h b/OptionItem.h
index 35c8506..ba28775 100644
--- a/OptionItem.h
+++ b/OptionItem.h
@@ -13,6 +13,7 @@ in the source distribution for its full text.
enum OptionItemType {
+ OPTION_ITEM_TEXT,
OPTION_ITEM_CHECK,
OPTION_ITEM_NUMBER,
};
@@ -32,6 +33,12 @@ typedef struct OptionItem_ {
char* text;
} OptionItem;
+typedef struct TextItem_ {
+ OptionItem super;
+
+ char* text;
+} TextItem;
+
typedef struct CheckItem_ {
OptionItem super;
@@ -51,9 +58,12 @@ typedef struct NumberItem_ {
} NumberItem;
extern const OptionItemClass OptionItem_class;
+extern const OptionItemClass TextItem_class;
extern const OptionItemClass CheckItem_class;
extern const OptionItemClass NumberItem_class;
+TextItem* TextItem_new(const char* text);
+
CheckItem* CheckItem_newByRef(const char* text, bool* ref);
CheckItem* CheckItem_newByVal(const char* text, bool value);
bool CheckItem_get(const CheckItem* this);
diff --git a/Process.c b/Process.c
index 6c7b326..2a4c809 100644
--- a/Process.c
+++ b/Process.c
@@ -414,6 +414,8 @@ void Process_makeCommandStr(Process* this) {
bool stripExeFromCmdline = settings->stripExeFromCmdline;
bool showThreadNames = settings->showThreadNames;
+ uint64_t settingsStamp = settings->lastUpdate;
+
/* Nothing to do to (Re)Generate the Command string, if the process is:
* - a kernel thread, or
* - a zombie from before being under htop's watch, or
@@ -422,52 +424,27 @@ void Process_makeCommandStr(Process* this) {
return;
if (this->state == ZOMBIE && !this->mergedCommand.str)
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.
* Its content is based on the fields cmdline, comm, and exe. */
- if (
- mc->prevMergeSet == showMergedCommand &&
- mc->prevPathSet == showProgramPath &&
- mc->prevCommSet == searchCommInCmdline &&
- mc->prevCmdlineSet == stripExeFromCmdline &&
- mc->prevShowThreadNames == showThreadNames &&
- !mc->cmdlineChanged &&
- !mc->commChanged &&
- !mc->exeChanged
- ) {
+ if (mc->lastUpdate >= settingsStamp)
return;
- }
+
+ mc->lastUpdate = settingsStamp;
/* The field separtor "│" has been chosen such that it will not match any
* valid string used for searching or filtering */
const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT];
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 */
- size_t maxLen = 2 * SEPARATOR_LEN + 1;
- maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
- maxLen += this->procComm ? strlen(this->procComm) : 0;
- maxLen += this->procExe ? strlen(this->procExe) : 0;
-
- mc->str = xCalloc(1, maxLen);
- }
+ /* Accommodate the column text, two field separators and terminating NUL */
+ size_t maxLen = 2 * SEPARATOR_LEN + 1;
+ maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
+ maxLen += this->procComm ? strlen(this->procComm) : 0;
+ maxLen += this->procExe ? strlen(this->procExe) : 0;
- /* 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;
+ free(mc->str);
+ mc->str = xCalloc(1, maxLen);
/* Reset all locations that need extra handling when actually displaying */
mc->highlightCount = 0;
@@ -601,11 +578,15 @@ void Process_makeCommandStr(Process* this) {
}
if (matchLen) {
- /* strip the matched exe prefix */
- cmdline += matchLen;
+ if (stripExeFromCmdline) {
+ /* strip the matched exe prefix */
+ cmdline += matchLen;
- commStart -= matchLen;
- commEnd -= matchLen;
+ commStart -= matchLen;
+ commEnd -= matchLen;
+ } else {
+ matchLen = 0;
+ }
}
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) {
if (val >= 0) {
- if (val < 99.9F) {
- if (val < 0.05F) {
- *attr = CRT_colors[PROCESS_SHADOW];
- }
- xSnprintf(buffer, n, "%*.1f ", width, val);
- } else {
+ if (val < 0.05F)
+ *attr = CRT_colors[PROCESS_SHADOW];
+ else if (val >= 99.9F)
*attr = CRT_colors[PROCESS_MEGABYTES];
- if (val < 100.0F)
- val = 100.0F; // Don't round down and display "val" as "99".
- xSnprintf(buffer, n, "%*.0f ", width, val);
+
+ int precision = 1;
+
+ // 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 {
*attr = CRT_colors[PROCESS_SHADOW];
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;
}
-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) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
@@ -1204,7 +1181,8 @@ void Process_updateComm(Process* this, const char* comm) {
free(this->procComm);
this->procComm = comm ? xStrdup(comm) : NULL;
- this->mergedCommand.commChanged = true;
+
+ this->mergedCommand.lastUpdate = 0;
}
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->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd);
this->cmdlineBasenameEnd = basenameEnd;
- this->mergedCommand.cmdlineChanged = true;
+
+ this->mergedCommand.lastUpdate = 0;
}
void Process_updateExe(Process* this, const char* exe) {
@@ -1263,7 +1242,8 @@ void Process_updateExe(Process* this, const char* exe) {
this->procExe = NULL;
this->procExeBasenameOffset = 0;
}
- this->mergedCommand.exeChanged = true;
+
+ this->mergedCommand.lastUpdate = 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) {
- if (percentage < 99.9) {
+ if (percentage < 99.9F) {
Process_updateFieldWidth(PERCENT_CPU, 4);
Process_updateFieldWidth(PERCENT_NORM_CPU, 4);
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_NORM_CPU, width);
diff --git a/Process.h b/Process.h
index e1408c2..a1ca50f 100644
--- a/Process.h
+++ b/Process.h
@@ -96,17 +96,10 @@ typedef struct ProcessCmdlineHighlight_ {
* Process_writeCommand to color the string. str will be NULL for kernel
* threads and zombies */
typedef struct ProcessMergedCommand_ {
+ uint64_t lastUpdate; /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */
char* str; /* merged Command string */
size_t highlightCount; /* how many 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;
typedef struct Process_ {
@@ -293,7 +286,7 @@ extern uint8_t Process_fieldWidths[LAST_PROCESSFIELD];
#define PROCESS_MIN_PID_DIGITS 5
#define PROCESS_MAX_PID_DIGITS 19
#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_uidDigits;
@@ -394,7 +387,11 @@ bool Process_changePriorityBy(Process* this, Arg delta);
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);
diff --git a/ProcessList.c b/ProcessList.c
index 2e5ad7c..bbaddd8 100644
--- a/ProcessList.c
+++ b/ProcessList.c
@@ -82,41 +82,45 @@ void ProcessList_setPanel(ProcessList* 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);
if (column == NULL)
return "- ";
- static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
int width = column->width;
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
- xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
+ xSnprintf(titleBuffer, titleBufferSize, "%*s", width, column->heading);
return titleBuffer;
}
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)
- return alignedDynamicColumnTitle(this, field);
+ return alignedDynamicColumnTitle(this, field, titleBuffer, sizeof(titleBuffer));
const char* title = Process_fields[field].title;
if (!title)
return "- ";
if (Process_fields[field].pidColumn) {
- static char titleBuffer[PROCESS_MAX_PID_DIGITS + sizeof(" ")];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
return titleBuffer;
}
if (field == ST_UID) {
- static char titleBuffer[PROCESS_MAX_UID_DIGITS + sizeof(" ")];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
return titleBuffer;
}
if (Process_fields[field].autoWidth) {
- static char titleBuffer[UINT8_MAX + 1];
- xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title);
+ 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);
return titleBuffer;
}
@@ -158,7 +162,7 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) {
}
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);
p->processList = this;
@@ -168,25 +172,23 @@ void ProcessList_add(ProcessList* this, Process* p) {
Vector_add(this->processes, 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_count(this->processTable) == Vector_count(this->processes));
+ assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
}
-void ProcessList_remove(ProcessList* this, const Process* p) {
- assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
- assert(Hashtable_get(this->processTable, p->pid) != NULL);
-
- const Process* pp = Hashtable_remove(this->processTable, p->pid);
- assert(pp == p); (void)pp;
-
+// ProcessList_removeIndex removes Process p from the list's map and soft deletes
+// it from its vector. Vector_compact *must* be called once the caller is done
+// removing items.
+// Should only be called from ProcessList_scan to avoid breaking dying process highlighting.
+static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx) {
pid_t pid = p->pid;
- int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
- assert(idx != -1);
- if (idx >= 0) {
- Vector_remove(this->processes, idx);
- }
+ assert(p == (Process*)Vector_get(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) {
this->following = -1;
@@ -194,7 +196,7 @@ void ProcessList_remove(ProcessList* this, const Process* p) {
}
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) {
@@ -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) {
+ 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);
for (int i = 0; i < size; 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);
*preExisting = proc != NULL;
if (proc) {
- assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
+ assert(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -1);
assert(proc->pid == pid);
} else {
proc = constructor(this->settings);
@@ -484,7 +489,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
if (p->tombStampMs > 0) {
// remove tombed process
if (this->monotonicMs >= p->tombStampMs) {
- ProcessList_remove(this, p);
+ ProcessList_removeIndex(this, p, i);
}
} else if (p->updated == false) {
// process no longer exists
@@ -493,11 +498,14 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
} else {
// 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.
Process_setUidColumnWidth(maxUid);
}
diff --git a/ProcessList.h b/ProcessList.h
index c420038..419dea8 100644
--- a/ProcessList.h
+++ b/ProcessList.h
@@ -106,8 +106,6 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header);
void ProcessList_add(ProcessList* this, Process* p);
-void ProcessList_remove(ProcessList* this, const Process* p);
-
void ProcessList_updateDisplayList(ProcessList* this);
ProcessField ProcessList_keyAt(const ProcessList* this, int at);
diff --git a/ScreensPanel.c b/ScreensPanel.c
index 1427fef..785c387 100644
--- a/ScreensPanel.c
+++ b/ScreensPanel.c
@@ -315,6 +315,7 @@ void ScreensPanel_update(Panel* super) {
ScreensPanel* this = (ScreensPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
+ this->settings->lastUpdate++;
this->settings->screens = xReallocArray(this->settings->screens, size + 1, sizeof(ScreenSettings*));
for (int i = 0; i < size; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
diff --git a/Settings.c b/Settings.c
index a630374..7d6fca4 100644
--- a/Settings.c
+++ b/Settings.c
@@ -762,6 +762,8 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
+ this->lastUpdate = 1;
+
return this;
}
diff --git a/Settings.h b/Settings.h
index 345a535..facd3f2 100644
--- a/Settings.h
+++ b/Settings.h
@@ -96,6 +96,7 @@ typedef struct Settings_ {
#endif
bool changed;
+ uint64_t lastUpdate;
} Settings;
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
diff --git a/SignalsPanel.c b/SignalsPanel.c
index cb71130..7e21ce9 100644
--- a/SignalsPanel.c
+++ b/SignalsPanel.c
@@ -6,8 +6,8 @@ in the source distribution for its full text.
*/
#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 "FunctionBar.h"
diff --git a/SignalsPanel.h b/SignalsPanel.h
index 7b9303f..529043a 100644
--- a/SignalsPanel.h
+++ b/SignalsPanel.h
@@ -7,7 +7,11 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
+#ifndef HTOP_SOLARIS
#include <signal.h>
+#endif
#include "Panel.h"
diff --git a/TraceScreen.c b/TraceScreen.c
index c3a9449..90400b4 100644
--- a/TraceScreen.c
+++ b/TraceScreen.c
@@ -90,7 +90,9 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
char buffer[32] = {0};
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 ...
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
diff --git a/Vector.c b/Vector.c
index 52ed44a..eddbc9a 100644
--- a/Vector.c
+++ b/Vector.c
@@ -29,6 +29,8 @@ Vector* Vector_new(const ObjectClass* type, bool owner, int size) {
this->items = 0;
this->type = type;
this->owner = owner;
+ this->dirty_index = -1;
+ this->dirty_count = 0;
return this;
}
@@ -44,10 +46,21 @@ void Vector_delete(Vector* 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
static bool Vector_isConsistent(const Vector* this) {
assert(this->items <= this->arraySize);
+ assert(!Vector_isDirty(this));
if (this->owner) {
for (int i = 0; i < this->items; i++) {
@@ -60,15 +73,14 @@ static bool Vector_isConsistent(const Vector* this) {
return true;
}
-unsigned int Vector_count(const Vector* this) {
- unsigned int items = 0;
+bool Vector_countEquals(const Vector* this, unsigned int expectedCount) {
+ unsigned int n = 0;
for (int i = 0; i < this->items; i++) {
if (this->array[i]) {
- items++;
+ n++;
}
}
- assert(items == (unsigned int)this->items);
- return items;
+ return n == expectedCount;
}
Object* Vector_get(const Vector* this, int idx) {
@@ -88,13 +100,16 @@ int Vector_size(const Vector* this) {
void Vector_prune(Vector* this) {
assert(Vector_isConsistent(this));
if (this->owner) {
- for (int i = 0; i < this->items; i++)
+ for (int i = 0; i < this->items; i++) {
if (this->array[i]) {
Object_delete(this->array[i]);
- //this->array[i] = NULL;
+ this->array[i] = NULL;
}
+ }
}
this->items = 0;
+ this->dirty_index = -1;
+ this->dirty_count = 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) {
assert(idx >= 0 && idx < this->items);
assert(Vector_isConsistent(this));
diff --git a/Vector.h b/Vector.h
index 0fdb706..b7b3903 100644
--- a/Vector.h
+++ b/Vector.h
@@ -22,6 +22,11 @@ typedef struct Vector_ {
int arraySize;
int growthRate;
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;
} Vector;
@@ -44,6 +49,15 @@ Object* Vector_take(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_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);
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 */
diff --git a/XUtils.c b/XUtils.c
index b34a8a7..2012b6c 100644
--- a/XUtils.c
+++ b/XUtils.c
@@ -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) {
const size_t l1 = strlen(s1);
const size_t l2 = strlen(s2);
- assert(SIZE_MAX - l1 > l2);
+ if (SIZE_MAX - l1 <= l2) {
+ fail();
+ }
char* out = xMalloc(l1 + l2 + 1);
memcpy(out, s1, l1);
memcpy(out + l1, s2, l2);
diff --git a/configure.ac b/configure.ac
index e7e47e3..7a52ab5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@
# ----------------------------------------------------------------------
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_AUX_DIR([build-aux])
diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c
index 71b9add..d0204dd 100644
--- a/darwin/DarwinProcess.c
+++ b/darwin/DarwinProcess.c
@@ -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_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, },
- [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_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, },
diff --git a/docs/styleguide.md b/docs/styleguide.md
index 18c53af..977ee38 100644
--- a/docs/styleguide.md
+++ b/docs/styleguide.md
@@ -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.
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.
This way git can better diff and present the changes when documentation is altered.
diff --git a/dragonflybsd/DragonFlyBSDProcess.c b/dragonflybsd/DragonFlyBSDProcess.c
index e11348d..7ff9245 100644
--- a/dragonflybsd/DragonFlyBSDProcess.c
+++ b/dragonflybsd/DragonFlyBSDProcess.c
@@ -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_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, },
- [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_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, },
diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c
index 2746540..4eccfe7 100644
--- a/freebsd/FreeBSDProcess.c
+++ b/freebsd/FreeBSDProcess.c
@@ -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_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, },
- [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_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, },
diff --git a/freebsd/Platform.c b/freebsd/Platform.c
index d21fc80..1a731b7 100644
--- a/freebsd/Platform.c
+++ b/freebsd/Platform.c
@@ -227,6 +227,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) {
void Platform_setMemoryValues(Meter* this) {
const ProcessList* pl = this->pl;
+ const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl;
this->total = pl->totalMem;
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[3] = pl->cachedMem;
// 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) {
diff --git a/generic/openzfs_sysctl.c b/generic/openzfs_sysctl.c
index d088da7..bcd37dc 100644
--- a/generic/openzfs_sysctl.c
+++ b/generic/openzfs_sysctl.c
@@ -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_c_min[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_mru_size[5];
@@ -35,6 +36,7 @@ void openzfs_sysctl_init(ZfsArcStats* stats) {
len = 5;
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.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);
@@ -61,6 +63,10 @@ void openzfs_sysctl_updateArcStats(ZfsArcStats* stats) {
sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len, NULL, 0);
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);
sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len, NULL, 0);
stats->max /= 1024;
diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c
index 299b167..92be326 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -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_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, },
- [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_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, },
@@ -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, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT
- [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD%", .description = "CPU delay %", .flags = 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_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_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
#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_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;
}
#ifdef HAVE_DELAYACCT
- case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 4, &attr); break;
- case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 4, &attr); break;
- case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_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, 5, &attr); break;
+ case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break;
#endif
case CTXT:
if (lp->ctxt_diff > 1000) {
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index 5e18f6d..45b045c 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -166,6 +166,28 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
#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) {
/* 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
@@ -240,6 +262,12 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
if (existing < 1)
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
/* When started with offline CPUs, libsensors does not monitor those,
* even when they become online. */
@@ -248,7 +276,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
#endif
super->activeCPUs = active;
- assert(existing == currExisting);
+ assert(Running_containerized || (existing == currExisting));
super->existingCPUs = currExisting;
}
@@ -1323,7 +1351,8 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
if (process->procExeDeleted)
filename[filenameLen - markerLen] = '\0';
- process->mergedCommand.exeChanged |= oldExeDeleted ^ process->procExeDeleted;
+ if (oldExeDeleted != process->procExeDeleted)
+ process->mergedCommand.lastUpdate = 0;
}
Process_updateExe(process, filename);
@@ -1389,6 +1418,21 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo
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) {
ProcessList* pl = (ProcessList*) this;
const struct dirent* entry;
@@ -1446,22 +1490,22 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
if (parent && pid == parent->pid)
continue;
- bool preExisting;
- Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
- LinuxProcess* lp = (LinuxProcess*) proc;
-
- proc->tgid = parent ? parent->pid : pid;
- 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;
+ continue;
#else
char procFd[4096];
xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
#endif
+ bool preExisting;
+ Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
+ LinuxProcess* lp = (LinuxProcess*) proc;
+
+ proc->tgid = parent ? parent->pid : pid;
+ proc->isUserlandThread = proc->pid != proc->tgid;
+
LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period);
/*
@@ -1497,7 +1541,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
bool prev = proc->usesDeletedLib;
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
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;
}
- proc->mergedCommand.exeChanged |= prev ^ proc->usesDeletedLib;
+ if (prev != proc->usesDeletedLib)
+ proc->mergedCommand.lastUpdate = 0;
}
if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {
@@ -1653,8 +1698,13 @@ errorReadingProcess:
#endif
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 {
+ /* A really short-lived process that we don't have full info about */
Process_delete((Object*)proc);
}
}
@@ -1874,6 +1924,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
switch (buffer[0]) {
case 'c':
+ tryRead("c_min", &lpl->zfs.min);
tryRead("c_max", &lpl->zfs.max);
tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed);
break;
@@ -1908,6 +1959,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0);
lpl->zfs.size /= 1024;
+ lpl->zfs.min /= 1024;
lpl->zfs.max /= 1024;
lpl->zfs.MFU /= 1024;
lpl->zfs.MRU /= 1024;
@@ -2101,16 +2153,11 @@ static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) {
if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
break;
- if (
- (sscanf(buffer, "processor : %d", &cpuid) == 1) ||
- (sscanf(buffer, "processor: %d", &cpuid) == 1)
- ) {
+ if (sscanf(buffer, "processor : %d", &cpuid) == 1) {
continue;
} else if (
(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)) {
continue;
diff --git a/linux/Platform.c b/linux/Platform.c
index 775f9ae..38b66e8 100644
--- a/linux/Platform.c
+++ b/linux/Platform.c
@@ -358,8 +358,13 @@ void Platform_setMemoryValues(Meter* this) {
this->values[4] = pl->availableMem;
if (lpl->zfs.enabled != 0 && !Running_containerized) {
- this->values[0] -= lpl->zfs.size;
- this->values[3] += lpl->zfs.size;
+ // ZFS does not shrink below the value of zfs_arc_min.
+ unsigned long long int shrinkableSize = 0;
+ if (lpl->zfs.size > lpl->zfs.min)
+ shrinkableSize = lpl->zfs.size - lpl->zfs.min;
+ this->values[0] -= shrinkableSize;
+ this->values[3] += shrinkableSize;
+ this->values[4] += shrinkableSize;
}
}
@@ -1035,7 +1040,7 @@ bool Platform_init(void) {
char lineBuffer[256];
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
// detect lxc or overlayfs and guess that this means we are running containerized
- if (String_startsWith(lineBuffer, "lxcfs ") || String_startsWith(lineBuffer, "overlay ")) {
+ if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay ")) {
Running_containerized = true;
break;
}
diff --git a/linux/Platform.h b/linux/Platform.h
index f2c314f..e6fa161 100644
--- a/linux/Platform.h
+++ b/linux/Platform.h
@@ -51,6 +51,8 @@ extern const MeterClass* const Platform_meterTypes[];
bool Platform_init(void);
void Platform_done(void);
+extern bool Running_containerized;
+
void Platform_setBindings(Htop_Action* keys);
int Platform_getUptime(void);
diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c
index 567cfc7..53ae2d2 100644
--- a/linux/SystemdMeter.c
+++ b/linux/SystemdMeter.c
@@ -219,15 +219,18 @@ static void updateViaExec(void) {
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
- execlp("systemctl",
- "systemctl",
- "show",
- "--property=SystemState",
- "--property=NFailedUnits",
- "--property=NNames",
- "--property=NJobs",
- "--property=NInstalledJobs",
- 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(
+ "systemctl",
+ "systemctl",
+ "show",
+ "--property=SystemState",
+ "--property=NFailedUnits",
+ "--property=NNames",
+ "--property=NJobs",
+ "--property=NInstalledJobs",
+ (char *)NULL);
exit(127);
}
close(fdpair[1]);
diff --git a/netbsd/NetBSDProcess.c b/netbsd/NetBSDProcess.c
index a7d59be..4d4ac4e 100644
--- a/netbsd/NetBSDProcess.c
+++ b/netbsd/NetBSDProcess.c
@@ -144,7 +144,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
},
[PERCENT_CPU] = {
.name = "PERCENT_CPU",
- .title = "CPU% ",
+ .title = " CPU%",
.description = "Percentage of the CPU time the process used in the last sampling",
.flags = 0,
.defaultSortDesc = true,
diff --git a/netbsd/Platform.c b/netbsd/Platform.c
index 1812ddd..cf6079d 100644
--- a/netbsd/Platform.c
+++ b/netbsd/Platform.c
@@ -311,7 +311,13 @@ char* Platform_getProcessEnv(pid_t pid) {
for (char** p = ptr; *p; p++) {
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;
env = xRealloc(env, capacity);
}
@@ -327,6 +333,7 @@ char* Platform_getProcessEnv(pid_t pid) {
env[size + 1] = 0;
}
+end:
(void) kvm_close(kt);
return env;
}
diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c
index feef7c4..c2f2ed4 100644
--- a/openbsd/OpenBSDProcess.c
+++ b/openbsd/OpenBSDProcess.c
@@ -142,7 +142,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
},
[PERCENT_CPU] = {
.name = "PERCENT_CPU",
- .title = "CPU% ",
+ .title = " CPU%",
.description = "Percentage of the CPU time the process used in the last sampling",
.flags = 0,
.defaultSortDesc = true,
diff --git a/openbsd/Platform.c b/openbsd/Platform.c
index db9780c..b222bee 100644
--- a/openbsd/Platform.c
+++ b/openbsd/Platform.c
@@ -269,7 +269,13 @@ char* Platform_getProcessEnv(pid_t pid) {
for (char** p = ptr; *p; p++) {
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;
env = xRealloc(env, capacity);
}
@@ -285,6 +291,7 @@ char* Platform_getProcessEnv(pid_t pid) {
env[size + 1] = 0;
}
+end:
(void) kvm_close(kt);
return env;
}
diff --git a/pcp/PCPMetric.h b/pcp/PCPMetric.h
index aa0e2a7..84ccbb9 100644
--- a/pcp/PCPMetric.h
+++ b/pcp/PCPMetric.h
@@ -81,6 +81,7 @@ typedef enum PCPMetric_ {
PCP_ZFS_ARC_BONUS_SIZE, /* zfs.arc.bonus_size */
PCP_ZFS_ARC_COMPRESSED_SIZE, /* zfs.arc.compressed_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_DBUF_SIZE, /* zfs.arc.dbuf_size */
PCP_ZFS_ARC_DNODE_SIZE, /* zfs.arc.dnode_size */
diff --git a/pcp/PCPProcess.c b/pcp/PCPProcess.c
index d0bcfbb..b8b87ca 100644
--- a/pcp/PCPProcess.c
+++ b/pcp/PCPProcess.c
@@ -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_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, },
- [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_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, },
@@ -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, },
[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_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, },
[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, },
diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c
index ca82575..045f7ae 100644
--- a/pcp/PCPProcessList.c
+++ b/pcp/PCPProcessList.c
@@ -601,6 +601,8 @@ static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) {
memset(&this->zfs, 0, sizeof(ZfsArcStats));
if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
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))
this->zfs.max = value.ull / ONE_K;
if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))
diff --git a/pcp/Platform.c b/pcp/Platform.c
index b3d6ed4..342bf43 100644
--- a/pcp/Platform.c
+++ b/pcp/Platform.c
@@ -178,6 +178,7 @@ static const char* Platform_metricNames[] = {
[PCP_ZFS_ARC_BONUS_SIZE] = "zfs.arc.bonus_size",
[PCP_ZFS_ARC_COMPRESSED_SIZE] = "zfs.arc.compressed_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_DBUF_SIZE] = "zfs.arc.dbuf_size",
[PCP_ZFS_ARC_DNODE_SIZE] = "zfs.arc.dnode_size",
@@ -510,8 +511,13 @@ void Platform_setMemoryValues(Meter* this) {
this->values[4] = pl->availableMem;
if (ppl->zfs.enabled != 0) {
- this->values[0] -= ppl->zfs.size;
- this->values[3] += ppl->zfs.size;
+ // ZFS does not shrink below the value of zfs_arc_min.
+ 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;
}
}
diff --git a/solaris/Platform.c b/solaris/Platform.c
index 20b4d13..9c5acb5 100644
--- a/solaris/Platform.c
+++ b/solaris/Platform.c
@@ -267,16 +267,21 @@ static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr
envAccum* accump = accum;
(void) Phandle;
(void) addr;
+
size_t thissz = strlen(str);
- if ((thissz + 2) > (accump->capacity - accump->size)) {
- accump->env = xRealloc(accump->env, accump->capacity *= 2);
- }
- if ((thissz + 2) > (accump->capacity - accump->size)) {
- return 1;
+
+ while ((thissz + 2) > (accump->capacity - accump->size)) {
+ if (accump->capacity > (SIZE_MAX / 2))
+ 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);
- accump->size = accump->size + thissz + 1;
+
+ accump->size += thissz + 1;
return 0;
}
@@ -299,7 +304,8 @@ char* Platform_getProcessEnv(pid_t pid) {
Prelease(Phandle, 0);
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) {
diff --git a/solaris/SolarisProcess.c b/solaris/SolarisProcess.c
index ae8bd70..840757e 100644
--- a/solaris/SolarisProcess.c
+++ b/solaris/SolarisProcess.c
@@ -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_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, },
- [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_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, },
diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c
index 0c619ae..905cfbd 100644
--- a/solaris/SolarisProcessList.c
+++ b/solaris/SolarisProcessList.c
@@ -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;
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
proc->isUserlandThread = false;
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;
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
proc->isUserlandThread = true;
proc->ppid = _psinfo->pr_pid * 1024;
diff --git a/unsupported/UnsupportedProcess.c b/unsupported/UnsupportedProcess.c
index 5e27fe9..2aca048 100644
--- a/unsupported/UnsupportedProcess.c
+++ b/unsupported/UnsupportedProcess.c
@@ -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_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, },
- [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_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, },
diff --git a/zfs/ZfsArcStats.h b/zfs/ZfsArcStats.h
index e2cb411..1fe7236 100644
--- a/zfs/ZfsArcStats.h
+++ b/zfs/ZfsArcStats.h
@@ -10,6 +10,7 @@ in the source distribution for its full text.
typedef struct ZfsArcStats_ {
int enabled;
int isCompressed;
+ unsigned long long int min;
unsigned long long int max;
unsigned long long int size;
unsigned long long int MFU;

© 2014-2022 Faster IT GmbH | imprint | privacy policy