summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Saponara <as@php.net>2020-10-30 21:56:16 -0400
committerAdam Saponara <as@php.net>2020-10-30 21:56:16 -0400
commitdde71c6637905e1707bd1020c93e930f4b0a480b (patch)
treeef65023b640f292ac50a7e0db38babbb78ef8eea
parentbbf01054bf943db4394027d77915f9625ebde81e (diff)
Highlight new and old processes (#74)
-rw-r--r--CRT.c12
-rw-r--r--CRT.h2
-rw-r--r--DisplayOptionsPanel.c1
-rw-r--r--Panel.c12
-rw-r--r--Process.c17
-rw-r--r--Process.h11
-rw-r--r--ProcessList.c42
-rw-r--r--ProcessList.h2
-rw-r--r--RichString.h1
-rw-r--r--ScreenManager.c1
-rw-r--r--Settings.c7
-rw-r--r--Settings.h2
-rw-r--r--htop.1.in5
-rw-r--r--htop.c75
14 files changed, 155 insertions, 35 deletions
diff --git a/CRT.c b/CRT.c
index 888444cc..28a2add7 100644
--- a/CRT.c
+++ b/CRT.c
@@ -111,6 +111,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red,Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green,Black),
+ [PROCESS_NEW] = ColorPair(Black,Green),
+ [PROCESS_TOMB] = ColorPair(Black,Red),
[PROCESS_THREAD] = ColorPair(Green,Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green,Black),
[BAR_BORDER] = A_BOLD,
@@ -188,6 +190,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD,
[PROCESS_HIGH_PRIORITY] = A_BOLD,
[PROCESS_LOW_PRIORITY] = A_DIM,
+ [PROCESS_NEW] = A_BOLD,
+ [PROCESS_TOMB] = A_DIM,
[PROCESS_THREAD] = A_BOLD,
[PROCESS_THREAD_BASENAME] = A_REVERSE,
[BAR_BORDER] = A_BOLD,
@@ -265,6 +269,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red,White),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red,White),
[PROCESS_LOW_PRIORITY] = ColorPair(Green,White),
+ [PROCESS_NEW] = ColorPair(White,Green),
+ [PROCESS_TOMB] = ColorPair(White,Red),
[PROCESS_THREAD] = ColorPair(Blue,White),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue,White),
[BAR_BORDER] = ColorPair(Blue,White),
@@ -342,6 +348,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red,Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green,Black),
+ [PROCESS_NEW] = ColorPair(Black,Green),
+ [PROCESS_TOMB] = ColorPair(Black,Red),
[PROCESS_THREAD] = ColorPair(Blue,Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue,Black),
[BAR_BORDER] = ColorPair(Blue,Black),
@@ -419,6 +427,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Blue),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red,Blue),
[PROCESS_LOW_PRIORITY] = ColorPair(Green,Blue),
+ [PROCESS_NEW] = ColorPair(Blue,Green),
+ [PROCESS_TOMB] = ColorPair(Blue,Red),
[PROCESS_THREAD] = ColorPair(Green,Blue),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green,Blue),
[BAR_BORDER] = A_BOLD | ColorPair(Yellow,Blue),
@@ -498,6 +508,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Black),
[PROCESS_HIGH_PRIORITY] = ColorPair(Red,Black),
[PROCESS_LOW_PRIORITY] = ColorPair(Green,Black),
+ [PROCESS_NEW] = ColorPair(Black,Green),
+ [PROCESS_TOMB] = ColorPair(Black,Red),
[BAR_BORDER] = A_BOLD | ColorPair(Green,Black),
[BAR_SHADOW] = ColorPair(Cyan,Black),
[SWAP] = ColorPair(Red,Black),
diff --git a/CRT.h b/CRT.h
index 1cdc209c..818148ef 100644
--- a/CRT.h
+++ b/CRT.h
@@ -72,6 +72,8 @@ typedef enum ColorElements_ {
PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY,
+ PROCESS_NEW,
+ PROCESS_TOMB,
PROCESS_THREAD,
PROCESS_THREAD_BASENAME,
BAR_BORDER,
diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c
index 64fd303c..e539906c 100644
--- a/DisplayOptionsPanel.c
+++ b/DisplayOptionsPanel.c
@@ -93,6 +93,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse)));
+ Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight new and old processes"), &(settings->highlightChanges)));
#ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity)));
#endif
diff --git a/Panel.c b/Panel.c
index 02440d85..f3a48bc9 100644
--- a/Panel.c
+++ b/Panel.c
@@ -266,16 +266,18 @@ void Panel_draw(Panel* this, bool focus) {
Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item);
int amt = MINIMUM(itemLen - scrollH, this->w);
- bool selected = (i == this->selected);
- if (selected) {
- attrset(selectionColor);
- RichString_setAttr(&item, selectionColor);
+ if (i == this->selected) {
+ item.highlightAttr = selectionColor;
+ }
+ if (item.highlightAttr) {
+ attrset(item.highlightAttr);
+ RichString_setAttr(&item, item.highlightAttr);
this->selectedLen = itemLen;
}
mvhline(y + line, x, ' ', this->w);
if (amt > 0)
RichString_printoffnVal(item, y + line, x, scrollH, amt);
- if (selected)
+ if (item.highlightAttr)
attrset(CRT_colors[RESET_COLOR]);
RichString_end(item);
line++;
diff --git a/Process.c b/Process.c
index 842232f7..f78720a5 100644
--- a/Process.c
+++ b/Process.c
@@ -6,6 +6,7 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+
#include "config.h" // IWYU pragma: keep
#include "Process.h"
@@ -381,6 +382,12 @@ void Process_display(const Object* cast, RichString* out) {
RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
if (this->tag == true)
RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
+ if (this->settings->highlightChanges) {
+ if (Process_isNew(this))
+ out->highlightAttr = CRT_colors[PROCESS_NEW];
+ if (Process_isTomb(this))
+ out->highlightAttr = CRT_colors[PROCESS_TOMB];
+ }
assert(out->chlen > 0);
}
@@ -413,6 +420,16 @@ void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true;
}
+bool Process_isNew(const Process* this) {
+ if (this->processList && this->processList->scanTs >= this->seenTs)
+ return (this->processList->scanTs - this->seenTs <= this->processList->settings->highlightDelaySecs);
+ return false;
+}
+
+bool Process_isTomb(const Process* this) {
+ return (this->tombTs > 0);
+}
+
bool Process_setPriority(Process* this, int priority) {
CRT_dropPrivileges();
int old_prio = getpriority(PRIO_PROCESS, this->pid);
diff --git a/Process.h b/Process.h
index b2c82080..e3ff333f 100644
--- a/Process.h
+++ b/Process.h
@@ -10,17 +10,18 @@ in the source distribution for its full text.
#include <stdbool.h>
#include <sys/types.h>
+#include <time.h>
#include "Object.h"
#include "RichString.h"
-
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
#define PROCESS_FLAG_IO 0x0001
+#define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
@@ -59,6 +60,7 @@ struct Settings_;
typedef struct Process_ {
Object super;
+ const struct ProcessList_* processList;
const struct Settings_* settings;
unsigned long long int time;
@@ -99,6 +101,9 @@ typedef struct Process_ {
int exit_signal;
+ time_t seenTs;
+ time_t tombTs;
+
unsigned long int minflt;
unsigned long int majflt;
} Process;
@@ -172,6 +177,10 @@ void Process_init(Process* this, const struct Settings_* settings);
void Process_toggleTag(Process* this);
+bool Process_isNew(const Process* this);
+
+bool Process_isTomb(const Process* this);
+
bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, Arg delta);
diff --git a/ProcessList.c b/ProcessList.c
index dac86cb8..1ae7b9cc 100644
--- a/ProcessList.c
+++ b/ProcessList.c
@@ -9,11 +9,11 @@ in the source distribution for its full text.
#include <assert.h>
#include <string.h>
+#include <time.h>
#include "CRT.h"
#include "XUtils.h"
-
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
this->processes = Vector_new(klass, true, DEFAULT_SIZE);
this->processTable = Hashtable_new(140, false);
@@ -27,6 +27,9 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users
// set later by platform-specific code
this->cpuCount = 0;
+ this->scanTs = 0;
+ this->firstScanTs = 0;
+
#ifdef HAVE_LIBHWLOC
this->topologyOk = false;
if (hwloc_topology_init(&this->topology) == 0) {
@@ -81,6 +84,14 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) {
void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL);
+ p->processList = this;
+
+ if (this->scanTs == this->firstScanTs) {
+ // prevent highlighting processes found in first scan
+ p->seenTs = this->firstScanTs - this->settings->highlightDelaySecs - 1;
+ } else {
+ p->seenTs = this->scanTs;
+ }
Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p);
@@ -283,6 +294,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
}
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
+ struct timespec now;
// in pause mode only gather global data for meters (CPU/memory/...)
if (pauseProcessUpdate) {
@@ -302,13 +314,35 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
this->kernelThreads = 0;
this->runningTasks = 0;
+
+ // set scanTs
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
+ if (this->firstScanTs == 0) {
+ this->firstScanTs = now.tv_sec;
+ }
+ this->scanTs = now.tv_sec;
+ }
+
ProcessList_goThroughEntries(this, false);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i);
- if (p->updated == false)
- ProcessList_remove(this, p);
- else
+ if (p->tombTs > 0) {
+ // remove tombed process
+ if (this->scanTs >= p->tombTs) {
+ ProcessList_remove(this, p);
+ }
+ } else if (p->updated == false) {
+ // process no longer exists
+ if (this->settings->highlightChanges) {
+ // mark tombed
+ p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
+ } else {
+ // immediately remove
+ ProcessList_remove(this, p);
+ }
+ } else {
p->updated = false;
+ }
}
}
diff --git a/ProcessList.h b/ProcessList.h
index 6b075fa7..db0549a6 100644
--- a/ProcessList.h
+++ b/ProcessList.h
@@ -70,6 +70,8 @@ typedef struct ProcessList_ {
int cpuCount;
+ time_t scanTs;
+ time_t firstScanTs;
} ProcessList;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
diff --git a/RichString.h b/RichString.h
index 48c1e749..9ee3a2d0 100644
--- a/RichString.h
+++ b/RichString.h
@@ -39,6 +39,7 @@ typedef struct RichString_ {
int chlen;
CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN+1];
+ int highlightAttr;
} RichString;
void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
diff --git a/ScreenManager.c b/ScreenManager.c
index bc5f66ac..2d43babc 100644
--- a/ScreenManager.c
+++ b/ScreenManager.c
@@ -102,6 +102,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
struct timeval tv;
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
+
*timedOut = (newTime - *oldTime > this->settings->delay);
*rescan = *rescan || *timedOut;
if (newTime < *oldTime) *rescan = true; // clock was adjusted?
diff --git a/Settings.c b/Settings.c
index 9ac27565..a0a05042 100644
--- a/Settings.c
+++ b/Settings.c
@@ -158,6 +158,10 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) {
this->highlightThreads = atoi(option[1]);
+ } else if (String_eq(option[0], "highlight_changes")) {
+ this->highlightChanges = atoi(option[1]);
+ } else if (String_eq(option[0], "highlight_changes_delay_secs")) {
+ this->highlightDelaySecs = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) {
@@ -265,6 +269,8 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
+ fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
+ fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
@@ -307,6 +313,7 @@ Settings* Settings_new(int initialCpuCount) {
this->updateProcessNames = false;
this->showProgramPath = true;
this->highlightThreads = true;
+ this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
diff --git a/Settings.h b/Settings.h
index bbd40e16..b04a4308 100644
--- a/Settings.h
+++ b/Settings.h
@@ -48,6 +48,8 @@ typedef struct Settings_ {
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
+ bool highlightChanges;
+ int highlightDelaySecs;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
diff --git a/htop.1.in b/htop.1.in
index 3e95c201..b688d87d 100644
--- a/htop.1.in
+++ b/htop.1.in
@@ -4,7 +4,7 @@ htop \- interactive process viewer
.SH "SYNOPSIS"
.LP
.B htop
-.RB [ \-dCFhpustv ]
+.RB [ \-dCFhpustvH ]
.SH "DESCRIPTION"
.LP
.B htop
@@ -62,6 +62,9 @@ Output version information and exit
.TP
\fB\-t \-\-tree
Show processes in tree view
+.TP
+\fB\-H \-\-highlight-changes=DELAY\fR
+Highlight new and old processes
.SH "INTERACTIVE COMMANDS"
.LP
The following commands are supported while in
diff --git a/htop.c b/htop.c
index e4d437b9..351f5867 100644
--- a/htop.c
+++ b/htop.c
@@ -46,17 +46,18 @@ static void printHelpFlag(void) {
fputs("htop " VERSION "\n"
COPYRIGHT "\n"
"Released under the GNU GPLv2.\n\n"
- "-C --no-color Use a monochrome color scheme\n"
- "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
- "-F --filter=FILTER Show only the commands matching the given filter\n"
- "-h --help Print this help screen\n"
- "-M --no-mouse Disable the mouse\n"
- "-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
- "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
- "-t --tree Show the tree view by default\n"
- "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
- "-U --no-unicode Do not use unicode but plain ASCII\n"
- "-V --version Print version info\n"
+ "-C --no-color Use a monochrome color scheme\n"
+ "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
+ "-F --filter=FILTER Show only the commands matching the given filter\n"
+ "-h --help Print this help screen\n"
+ "-M --no-mouse Disable the mouse\n"
+ "-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
+ "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
+ "-t --tree Show the tree view by default\n"
+ "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
+ "-U --no-unicode Do not use unicode but plain ASCII\n"
+ "-V --version Print version info\n"
+ "-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"\n"
"Long options may be passed with a single dash.\n\n"
"Press F1 inside htop for online help.\n"
@@ -76,6 +77,8 @@ typedef struct CommandLineSettings_ {
bool enableMouse;
bool treeView;
bool allowUnicode;
+ bool highlightChanges;
+ int highlightDelaySecs;
} CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) {
@@ -90,28 +93,31 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.enableMouse = true,
.treeView = false,
.allowUnicode = true,
+ .highlightChanges = false,
+ .highlightDelaySecs = -1,
};
static struct option long_opts[] =
{
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {"delay", required_argument, 0, 'd'},
- {"sort-key", required_argument, 0, 's'},
- {"user", optional_argument, 0, 'u'},
- {"no-color", no_argument, 0, 'C'},
- {"no-colour", no_argument, 0, 'C'},
- {"no-mouse", no_argument, 0, 'M'},
- {"no-unicode", no_argument, 0, 'U'},
- {"tree", no_argument, 0, 't'},
- {"pid", required_argument, 0, 'p'},
- {"filter", required_argument, 0, 'F'},
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {"delay", required_argument, 0, 'd'},
+ {"sort-key", required_argument, 0, 's'},
+ {"user", optional_argument, 0, 'u'},
+ {"no-color", no_argument, 0, 'C'},
+ {"no-colour", no_argument, 0, 'C'},
+ {"no-mouse", no_argument, 0, 'M'},
+ {"no-unicode", no_argument, 0, 'U'},
+ {"tree", no_argument, 0, 't'},
+ {"pid", required_argument, 0, 'p'},
+ {"filter", required_argument, 0, 'F'},
+ {"highlight-changes", optional_argument, 0, 'H'},
{0,0,0,0}
};
int opt, opti=0;
/* Parse arguments */
- while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:", long_opts, &opti))) {
+ while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) {
if (opt == EOF) break;
switch (opt) {
case 'h':
@@ -198,6 +204,23 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
break;
}
+ case 'H': {
+ const char *delay = optarg;
+ if (!delay && optind < argc && argv[optind] != NULL &&
+ (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
+ delay = argv[optind++];
+ }
+ if (delay) {
+ if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
+ if (flags.highlightDelaySecs < 1) flags.highlightDelaySecs = 1;
+ } else {
+ fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
+ exit(1);
+ }
+ }
+ flags.highlightChanges = true;
+ break;
+ }
default:
exit(1);
}
@@ -271,6 +294,10 @@ int main(int argc, char** argv) {
settings->enableMouse = false;
if (flags.treeView)
settings->treeView = true;
+ if (flags.highlightChanges)
+ settings->highlightChanges = true;
+ if (flags.highlightDelaySecs != -1)
+ settings->highlightDelaySecs = flags.highlightDelaySecs;
CRT_init(settings->delay, settings->colorScheme, flags.allowUnicode);

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