aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Lange <DLange@git.local>2022-02-04 11:23:02 +0100
committerDaniel Lange <DLange@git.local>2022-02-04 11:23:02 +0100
commita6822e98434cf7da6fab033898094976d881ee0f (patch)
tree2b0f2ac3ad8c9e9a8c01bb692d9bdb51ce91e83b
parent30ce3b4c264c51c98f280e88e23792ff7deb2317 (diff)
downloaddebian_htop-a6822e98434cf7da6fab033898094976d881ee0f.tar.gz
debian_htop-a6822e98434cf7da6fab033898094976d881ee0f.tar.bz2
debian_htop-a6822e98434cf7da6fab033898094976d881ee0f.zip
New upstream version 3.1.2upstream/3.1.2
-rw-r--r--.github/workflows/build_release.yml51
-rw-r--r--ChangeLog15
-rw-r--r--ColorsPanel.c2
-rw-r--r--CommandLine.c90
-rw-r--r--CommandLine.h5
-rw-r--r--Compat.c2
-rw-r--r--DiskIOMeter.c6
-rw-r--r--DisplayOptionsPanel.c2
-rw-r--r--Header.c2
-rw-r--r--HeaderOptionsPanel.c2
-rw-r--r--InfoScreen.c2
-rw-r--r--Makefile.am2
-rw-r--r--NetworkIOMeter.c6
-rw-r--r--OpenFilesScreen.c53
-rw-r--r--Process.c116
-rw-r--r--Process.h43
-rw-r--r--ProcessList.c24
-rw-r--r--ScreenManager.c9
-rw-r--r--configure.ac2
-rw-r--r--darwin/DarwinProcess.c22
-rw-r--r--darwin/Platform.c4
-rw-r--r--darwin/Platform.h7
-rw-r--r--dragonflybsd/DragonFlyBSDProcess.c4
-rw-r--r--dragonflybsd/DragonFlyBSDProcessList.c41
-rw-r--r--dragonflybsd/Platform.c3
-rw-r--r--dragonflybsd/Platform.h7
-rw-r--r--freebsd/FreeBSDProcess.c4
-rw-r--r--freebsd/FreeBSDProcessList.c23
-rw-r--r--freebsd/Platform.c4
-rw-r--r--freebsd/Platform.h7
-rw-r--r--generic/gettime.c8
-rw-r--r--htop.1.in42
-rw-r--r--linux/CGroupUtils.c317
-rw-r--r--linux/CGroupUtils.h16
-rw-r--r--linux/LinuxProcess.c17
-rw-r--r--linux/LinuxProcess.h4
-rw-r--r--linux/LinuxProcessList.c95
-rw-r--r--linux/Platform.c254
-rw-r--r--linux/Platform.h6
-rw-r--r--linux/ProcessField.h1
-rw-r--r--netbsd/NetBSDProcess.c6
-rw-r--r--netbsd/NetBSDProcessList.c29
-rw-r--r--netbsd/Platform.c3
-rw-r--r--netbsd/Platform.h7
-rw-r--r--openbsd/OpenBSDProcess.c6
-rw-r--r--openbsd/OpenBSDProcessList.c21
-rw-r--r--openbsd/Platform.c3
-rw-r--r--openbsd/Platform.h7
-rw-r--r--pcp/PCPProcess.c4
-rw-r--r--pcp/PCPProcessList.c31
-rw-r--r--pcp/Platform.c25
-rw-r--r--pcp/Platform.h5
-rw-r--r--solaris/Platform.c3
-rw-r--r--solaris/Platform.h7
-rw-r--r--solaris/SolarisProcess.c4
-rw-r--r--solaris/SolarisProcessList.c19
-rw-r--r--unsupported/Platform.c3
-rw-r--r--unsupported/Platform.h7
-rw-r--r--unsupported/UnsupportedProcess.c4
-rw-r--r--unsupported/UnsupportedProcessList.c2
60 files changed, 1056 insertions, 460 deletions
diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml
new file mode 100644
index 0000000..8c87915
--- /dev/null
+++ b/.github/workflows/build_release.yml
@@ -0,0 +1,51 @@
+name: Build Source Release
+
+# Trigger whenever a release is created
+on:
+ release:
+ types:
+ - created
+
+jobs:
+ build:
+ name: build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: true
+
+ - name: archive
+ id: archive
+ run: |
+ VERSION=${{ github.event.release.tag_name }}
+ PKGNAME="htop-$VERSION"
+ SHASUM=$PKGNAME.tar.xz.sha256
+ autoreconf -i
+ mkdir -p /tmp/$PKGNAME
+ mv * /tmp/$PKGNAME
+ mv /tmp/$PKGNAME .
+ TARBALL=$PKGNAME.tar.xz
+ tar cJf $TARBALL $PKGNAME
+ sha256sum $TARBALL > $SHASUM
+ echo "::set-output name=tarball::$TARBALL"
+ echo "::set-output name=shasum::$SHASUM"
+ - name: upload tarball
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ./${{ steps.archive.outputs.tarball }}
+ asset_name: ${{ steps.archive.outputs.tarball }}
+ asset_content_type: application/gzip
+
+ - name: upload shasum
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ./${{ steps.archive.outputs.shasum }}
+ asset_name: ${{ steps.archive.outputs.shasum }}
+ asset_content_type: text/plain
diff --git a/ChangeLog b/ChangeLog
index 6f0a7da..a7f92bb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+What's new in version 3.1.2
+
+* Bugfix for crash when storing modified settings at exit
+* Generate xz-compressed source tarball (with configure) using github actions
+* Allow -u UID with numerical value as argument
+* Added documentation for obsolete/state libraries/program files highlighting
+* Some obsolete/stale library highlighting refinements
+* Column width issues resolved
+* Dynamic UID column sizing improved
+* Discard stale information from Disk and Network I/O meters
+* Refined Linux kernel thread detection
+* Reworked process state handling
+* New CCGROUP column showing abbreviated cgroup name
+* New OFFSET column in the list of open files screen
+
What's new in version 3.1.1
* Update license headers to explicitly say GPLv2+
diff --git a/ColorsPanel.c b/ColorsPanel.c
index 0ea2158..50188f6 100644
--- a/ColorsPanel.c
+++ b/ColorsPanel.c
@@ -52,7 +52,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
HandlerResult result = IGNORED;
int mark;
- switch(ch) {
+ switch (ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
diff --git a/CommandLine.c b/CommandLine.c
index a352e4e..b2ad06e 100644
--- a/CommandLine.c
+++ b/CommandLine.c
@@ -11,6 +11,7 @@ in the source distribution for its full text.
#include "CommandLine.h"
#include <assert.h>
+#include <ctype.h>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
@@ -85,9 +86,9 @@ typedef struct CommandLineSettings_ {
bool readonly;
} CommandLineSettings;
-static CommandLineSettings parseArguments(const char* program, int argc, char** argv) {
+static CommandLineStatus parseArguments(const char* program, int argc, char** argv, CommandLineSettings* flags) {
- CommandLineSettings flags = {
+ *flags = (CommandLineSettings) {
.pidMatchList = NULL,
.commFilter = NULL,
.userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
@@ -130,10 +131,10 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
switch (opt) {
case 'h':
printHelpFlag(program);
- exit(0);
+ return STATUS_OK_EXIT;
case 'V':
printVersionFlag(program);
- exit(0);
+ return STATUS_OK_EXIT;
case 's':
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
if (String_eq(optarg, "help")) {
@@ -142,29 +143,29 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
const char* description = Process_fields[j].description;
if (name) printf("%19s %s\n", name, description);
}
- exit(0);
+ return STATUS_OK_EXIT;
}
- flags.sortKey = 0;
+ flags->sortKey = 0;
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
if (Process_fields[j].name == NULL)
continue;
if (String_eq(optarg, Process_fields[j].name)) {
- flags.sortKey = j;
+ flags->sortKey = j;
break;
}
}
- if (flags.sortKey == 0) {
+ if (flags->sortKey == 0) {
fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
- exit(1);
+ return STATUS_ERROR_EXIT;
}
break;
case 'd':
- if (sscanf(optarg, "%16d", &(flags.delay)) == 1) {
- if (flags.delay < 1) flags.delay = 1;
- if (flags.delay > 100) flags.delay = 100;
+ if (sscanf(optarg, "%16d", &(flags->delay)) == 1) {
+ if (flags->delay < 1) flags->delay = 1;
+ if (flags->delay > 100) flags->delay = 100;
} else {
fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
- exit(1);
+ return STATUS_ERROR_EXIT;
}
break;
case 'u':
@@ -176,26 +177,30 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
}
if (!username) {
- flags.userId = geteuid();
- } else if (!Action_setUserOnly(username, &(flags.userId))) {
- fprintf(stderr, "Error: invalid user \"%s\".\n", username);
- exit(1);
+ flags->userId = geteuid();
+ } else if (!Action_setUserOnly(username, &(flags->userId))) {
+ for (const char *itr = username; *itr; ++itr)
+ if (!isdigit((unsigned char)*itr)) {
+ fprintf(stderr, "Error: invalid user \"%s\".\n", username);
+ return STATUS_ERROR_EXIT;
+ }
+ flags->userId = atol(username);
}
break;
}
case 'C':
- flags.useColors = false;
+ flags->useColors = false;
break;
case 'M':
#ifdef HAVE_GETMOUSE
- flags.enableMouse = false;
+ flags->enableMouse = false;
#endif
break;
case 'U':
- flags.allowUnicode = false;
+ flags->allowUnicode = false;
break;
case 't':
- flags.treeView = true;
+ flags->treeView = true;
break;
case 'p': {
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
@@ -203,14 +208,14 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
char* saveptr;
const char* pid = strtok_r(argCopy, ",", &saveptr);
- if (!flags.pidMatchList) {
- flags.pidMatchList = Hashtable_new(8, false);
+ if (!flags->pidMatchList) {
+ flags->pidMatchList = Hashtable_new(8, false);
}
while(pid) {
unsigned int num_pid = atoi(pid);
- // deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here
- Hashtable_put(flags.pidMatchList, num_pid, (void *) 1);
+ // deepcode ignore CastIntegerToAddress: we just want a non-NULL pointer here
+ Hashtable_put(flags->pidMatchList, num_pid, (void *) 1);
pid = strtok_r(NULL, ",", &saveptr);
}
free(argCopy);
@@ -219,7 +224,7 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
}
case 'F': {
assert(optarg);
- free_and_xStrdup(&flags.commFilter, optarg);
+ free_and_xStrdup(&flags->commFilter, optarg);
break;
}
case 'H': {
@@ -229,28 +234,30 @@ static CommandLineSettings parseArguments(const char* program, int argc, char**
delay = argv[optind++];
}
if (delay) {
- if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
- if (flags.highlightDelaySecs < 1)
- flags.highlightDelaySecs = 1;
+ 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);
+ return STATUS_ERROR_EXIT;
}
}
- flags.highlightChanges = true;
+ flags->highlightChanges = true;
break;
}
case 128:
- flags.readonly = true;
+ flags->readonly = true;
break;
- default:
- if (Platform_getLongOption(opt, argc, argv) == false)
- exit(1);
- break;
+ default: {
+ CommandLineStatus status;
+ if ((status = Platform_getLongOption(opt, argc, argv)) != STATUS_OK)
+ return status;
+ break;
+ }
}
}
- return flags;
+ return STATUS_OK;
}
static void CommandLine_delay(ProcessList* pl, unsigned long millisec) {
@@ -283,12 +290,17 @@ int CommandLine_run(const char* name, int argc, char** argv) {
else
setlocale(LC_CTYPE, "");
- CommandLineSettings flags = parseArguments(name, argc, argv);
+ CommandLineStatus status = STATUS_OK;
+ CommandLineSettings flags = { 0 };
+
+ if ((status = parseArguments(name, argc, argv, &flags)) != STATUS_OK)
+ return status != STATUS_OK_EXIT ? 1 : 0;
if (flags.readonly)
Settings_enableReadonly();
- Platform_init();
+ if (!Platform_init())
+ return 1;
Process_setupColumnWidths();
diff --git a/CommandLine.h b/CommandLine.h
index 99579f5..fbdede8 100644
--- a/CommandLine.h
+++ b/CommandLine.h
@@ -8,6 +8,11 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
+typedef enum {
+ STATUS_OK,
+ STATUS_ERROR_EXIT,
+ STATUS_OK_EXIT
+} CommandLineStatus;
int CommandLine_run(const char* name, int argc, char** argv);
diff --git a/Compat.c b/Compat.c
index 2d06c48..8c88138 100644
--- a/Compat.c
+++ b/Compat.c
@@ -44,7 +44,7 @@ int Compat_faccessat(int dirfd,
// Fallback to stat(2)/lstat(2) depending on flags
struct stat statinfo;
- if(flags) {
+ if (flags) {
ret = lstat(pathname, &statinfo);
} else {
ret = stat(pathname, &statinfo);
diff --git a/DiskIOMeter.c b/DiskIOMeter.c
index c5f1476..11fb791 100644
--- a/DiskIOMeter.c
+++ b/DiskIOMeter.c
@@ -81,6 +81,12 @@ static void DiskIOMeter_updateValues(Meter* this) {
cached_msTimeSpend_total = data.totalMsTimeSpend;
}
+ if (passedTimeInMs > 30000) {
+ // Triggers for the first initialization and
+ // when there was a long time we did not collect updates
+ hasData = false;
+ }
+
this->values[0] = cached_utilisation_diff;
this->total = MAXIMUM(this->values[0], 100.0); /* fix total after (initial) spike */
diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c
index 516fb50..8212120 100644
--- a/DisplayOptionsPanel.c
+++ b/DisplayOptionsPanel.c
@@ -107,7 +107,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
- Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs", &(settings->highlightDeletedExe)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs (red) / libraries (yellow)", &(settings->highlightDeletedExe)));
Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));
diff --git a/Header.c b/Header.c
index 1652520..c557a45 100644
--- a/Header.c
+++ b/Header.c
@@ -145,7 +145,7 @@ void Header_writeBackToSettings(const Header* this) {
const Vector* vec = this->columns[col];
int len = Vector_size(vec);
- colSettings->names = len ? xCalloc(len, sizeof(char*)) : NULL;
+ colSettings->names = len ? xCalloc(len + 1, sizeof(char*)) : NULL;
colSettings->modes = len ? xCalloc(len, sizeof(int)) : NULL;
colSettings->len = len;
diff --git a/HeaderOptionsPanel.c b/HeaderOptionsPanel.c
index d8148df..22bcd09 100644
--- a/HeaderOptionsPanel.c
+++ b/HeaderOptionsPanel.c
@@ -35,7 +35,7 @@ static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
HandlerResult result = IGNORED;
int mark;
- switch(ch) {
+ switch (ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
diff --git a/InfoScreen.c b/InfoScreen.c
index a62b7c0..f431f79 100644
--- a/InfoScreen.c
+++ b/InfoScreen.c
@@ -135,7 +135,7 @@ void InfoScreen_run(InfoScreen* this) {
continue;
}
- switch(ch) {
+ switch (ch) {
case ERR:
continue;
case KEY_F(3):
diff --git a/Makefile.am b/Makefile.am
index 7ed500c..2a9cc29 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -153,6 +153,7 @@ linux_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
+ linux/CGroupUtils.h \
linux/HugePageMeter.h \
linux/IOPriority.h \
linux/IOPriorityPanel.h \
@@ -174,6 +175,7 @@ linux_platform_sources = \
generic/gettime.c \
generic/hostname.c \
generic/uname.c \
+ linux/CGroupUtils.c \
linux/HugePageMeter.c \
linux/IOPriorityPanel.c \
linux/LibSensors.c \
diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c
index dcba78d..6c429c3 100644
--- a/NetworkIOMeter.c
+++ b/NetworkIOMeter.c
@@ -85,6 +85,12 @@ static void NetworkIOMeter_updateValues(Meter* this) {
cached_txp_total = data.packetsTransmitted;
}
+ if (passedTimeInMs > 30000) {
+ // Triggers for the first initialization and
+ // when there was a long time we did not collect updates
+ hasData = false;
+ }
+
this->values[0] = cached_rxb_diff;
this->values[1] = cached_txb_diff;
if (cached_rxb_diff + cached_txb_diff > this->total) {
diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c
index e0bede0..34367eb 100644
--- a/OpenFilesScreen.c
+++ b/OpenFilesScreen.c
@@ -10,12 +10,14 @@ in the source distribution for its full text.
#include "OpenFilesScreen.h"
#include <fcntl.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/stat.h>
#include "Macros.h"
#include "Panel.h"
@@ -25,7 +27,7 @@ in the source distribution for its full text.
typedef struct OpenFiles_Data_ {
- char* data[7];
+ char* data[8];
} OpenFiles_Data;
typedef struct OpenFiles_ProcessData_ {
@@ -55,6 +57,8 @@ static size_t getIndexForType(char type) {
return 5;
case 't':
return 6;
+ case 'o':
+ return 7;
}
/* should never reach here */
@@ -74,7 +78,7 @@ OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
} else {
this->pid = process->pid;
}
- return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE NODE NAME");
+ return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE OFFSET NODE NAME");
}
void OpenFilesScreen_delete(Object* this) {
@@ -115,13 +119,14 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
close(fdnull);
char buffer[32] = {0};
xSnprintf(buffer, sizeof(buffer), "%d", pid);
- execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
+ execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
OpenFiles_Data* item = &(pdata->data);
OpenFiles_FileData* fdata = NULL;
+ bool lsofIncludesFileSize = false;
FILE* fd = fdopen(fdpair[0], "r");
if (!fd) {
@@ -155,8 +160,17 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
case 't': /* file's type */
{
size_t index = getIndexForType(cmd);
- free(item->data[index]);
- item->data[index] = xStrdup(line + 1);
+ free_and_xStrdup(&item->data[index], line + 1);
+ break;
+ }
+ case 'o': /* file's offset */
+ {
+ size_t index = getIndexForType(cmd);
+ if (String_startsWith(line + 1, "0t")) {
+ free_and_xStrdup(&item->data[index], line + 3);
+ } else {
+ free_and_xStrdup(&item->data[index], line + 1);
+ }
break;
}
case 'c': /* process command name */
@@ -166,7 +180,6 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
case 'k': /* link count */
case 'l': /* file's lock status */
case 'L': /* process login name */
- case 'o': /* file's offset */
case 'p': /* process ID */
case 'P': /* protocol name */
case 'R': /* parent process ID */
@@ -175,6 +188,10 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
/* ignore */
break;
}
+
+ if (cmd == 's')
+ lsofIncludesFileSize = true;
+
free(line);
}
fclose(fd);
@@ -191,6 +208,25 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
pdata->error = WEXITSTATUS(wstatus);
}
+ /* We got all information we need; no post-processing needed */
+ if (lsofIncludesFileSize)
+ return pdata;
+
+ /* On linux, `lsof -o -F` omits SIZE, so add it back. */
+ /* On macOS, `lsof -o -F` includes SIZE, so this block isn't needed. If no open files have a filesize, this will still run, unfortunately. */
+ size_t fileSizeIndex = getIndexForType('s');
+ for (fdata = pdata->files; fdata != NULL; fdata = fdata->next) {
+ item = &fdata->data;
+ const char* filename = getDataForType(item, 'n');
+
+ struct stat st;
+ if (stat(filename, &st) == 0) {
+ char fileSizeBuf[21]; /* 20 (long long) + 1 (NULL) */
+ xSnprintf(fileSizeBuf, sizeof(fileSizeBuf), "%"PRIu64, st.st_size); /* st.st_size is long long on macOS, long on linux */
+ free_and_xStrdup(&item->data[fileSizeIndex], fileSizeBuf);
+ }
+ }
+
return pdata;
}
@@ -213,14 +249,15 @@ static void OpenFilesScreen_scan(InfoScreen* this) {
while (fdata) {
OpenFiles_Data* data = &fdata->data;
size_t lenN = strlen(getDataForType(data, 'n'));
- size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/;
+ size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + 10 + lenN + 8 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
- xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s",
+ xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %10.10s %s",
getDataForType(data, 'f'),
getDataForType(data, 't'),
getDataForType(data, 'a'),
getDataForType(data, 'D'),
getDataForType(data, 's'),
+ getDataForType(data, 'o'),
getDataForType(data, 'i'),
getDataForType(data, 'n'));
InfoScreen_addLine(this, entry);
diff --git a/Process.c b/Process.c
index f0fc67f..6be36b7 100644
--- a/Process.c
+++ b/Process.c
@@ -41,17 +41,33 @@ static const char* const kthreadID = "KTHREAD";
static uid_t Process_getuid = (uid_t)-1;
-int Process_pidDigits = 7;
+int Process_pidDigits = PROCESS_MIN_PID_DIGITS;
+int Process_uidDigits = PROCESS_MIN_UID_DIGITS;
void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid();
if (maxPid == -1)
return;
+ if (maxPid < (int)pow(10, PROCESS_MIN_PID_DIGITS)) {
+ Process_pidDigits = PROCESS_MIN_PID_DIGITS;
+ return;
+ }
+
Process_pidDigits = ceil(log10(maxPid));
assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
}
+void Process_setUidColumnWidth(uid_t maxUid) {
+ if (maxUid < (uid_t)pow(10, PROCESS_MIN_UID_DIGITS)) {
+ Process_uidDigits = PROCESS_MIN_UID_DIGITS;
+ return;
+ }
+
+ Process_uidDigits = ceil(log10(maxUid));
+ assert(Process_uidDigits <= PROCESS_MAX_UID_DIGITS);
+}
+
void Process_printBytes(RichString* str, unsigned long long number, bool coloring) {
char buffer[16];
int len;
@@ -399,7 +415,7 @@ void Process_makeCommandStr(Process* this) {
* - a user thread and showThreadNames is not set */
if (Process_isKernelThread(this))
return;
- if (this->state == 'Z' && !this->mergedCommand.str)
+ if (this->state == ZOMBIE && !this->mergedCommand.str)
return;
if (Process_isUserlandThread(this) && settings->showThreadNames && (showThreadNames == mc->prevShowThreadNames))
return;
@@ -733,6 +749,28 @@ void Process_printPercentage(float val, char* buffer, int n, int* attr) {
}
}
+static inline char processStateChar(ProcessState state) {
+ switch (state) {
+ case UNKNOWN: return '?';
+ case RUNNABLE: return 'U';
+ case RUNNING: return 'R';
+ case QUEUED: return 'Q';
+ case WAITING: return 'W';
+ case UNINTERRUPTIBLE_WAIT: return 'D';
+ case BLOCKED: return 'B';
+ case PAGING: return 'P';
+ case STOPPED: return 'T';
+ case TRACED: return 't';
+ case ZOMBIE: return 'Z';
+ case DEFUNCT: return 'X';
+ case IDLE: return 'I';
+ case SLEEPING: return 'S';
+ default:
+ assert(0);
+ return '!';
+ }
+}
+
void Process_writeField(const Process* this, RichString* str, ProcessField field) {
char buffer[256];
size_t n = sizeof(buffer);
@@ -867,25 +905,35 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE:
- xSnprintf(buffer, n, "%c ", this->state);
+ xSnprintf(buffer, n, "%c ", processStateChar(this->state));
switch (this->state) {
-#ifdef HTOP_NETBSD
- case 'P':
-#else
- case 'R':
-#endif
+ case RUNNABLE:
+ case RUNNING:
+ case TRACED:
attr = CRT_colors[PROCESS_RUN_STATE];
break;
- case 'D':
+
+ case BLOCKED:
+ case DEFUNCT:
+ case STOPPED:
+ case ZOMBIE:
attr = CRT_colors[PROCESS_D_STATE];
break;
- case 'I':
- case 'S':
+
+ case QUEUED:
+ case WAITING:
+ case UNINTERRUPTIBLE_WAIT:
+ case IDLE:
+ case SLEEPING:
attr = CRT_colors[PROCESS_SHADOW];
break;
+
+ case UNKNOWN:
+ case PAGING:
+ break;
}
break;
- case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
+ case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break;
case TIME: Process_printTime(str, this->time, coloring); return;
case TGID:
if (this->tgid == this->pid)
@@ -908,11 +956,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
- Process_printLeftAlignedField(str, attr, this->user, 9);
+ Process_printLeftAlignedField(str, attr, this->user, 10);
return;
}
- xSnprintf(buffer, n, "%-9d ", this->st_uid);
+ xSnprintf(buffer, n, "%-10d ", this->st_uid);
break;
default:
if (DynamicColumn_writeField(this, str, field))
@@ -1056,44 +1104,6 @@ int Process_compare(const void* v1, const void* v2) {
return (Settings_getActiveDirection(settings) == 1) ? result : -result;
}
-static uint8_t stateCompareValue(char state) {
- switch (state) {
-
- case 'S':
- return 10;
-
- case 'I':
- return 9;
-
- case 'X':
- return 8;
-
- case 'Z':
- return 7;
-
- case 't':
- return 6;
-
- case 'T':
- return 5;
-
- case 'L':
- return 4;
-
- case 'D':
- return 3;
-
- case 'R':
- return 2;
-
- case '?':
- return 1;
-
- default:
- return 0;
- }
-}
-
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
int r;
@@ -1148,7 +1158,7 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField
r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
case STATE:
- return SPACESHIP_NUMBER(stateCompareValue(p1->state), stateCompareValue(p2->state));
+ return SPACESHIP_NUMBER(p1->state, p2->state);
case ST_UID:
return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
case TIME:
diff --git a/Process.h b/Process.h
index 4080d98..26f6434 100644
--- a/Process.h
+++ b/Process.h
@@ -60,6 +60,26 @@ typedef enum ProcessField_ {
LAST_PROCESSFIELD
} ProcessField;
+/* Core process states (shared by platforms)
+ * NOTE: The enum has an ordering that is important!
+ * See processStateChar in process.c for ProcessSate -> letter mapping */
+typedef enum ProcessState_ {
+ UNKNOWN = 1,
+ RUNNABLE,
+ RUNNING,
+ QUEUED,
+ WAITING,
+ UNINTERRUPTIBLE_WAIT,
+ BLOCKED,
+ PAGING,
+ STOPPED,
+ TRACED,
+ ZOMBIE,
+ DEFUNCT,
+ IDLE,
+ SLEEPING
+} ProcessState;
+
struct Settings_;
/* Holds information about regions of the cmdline that should be
@@ -202,20 +222,8 @@ typedef struct Process_ {
/* Number of major faults the process has made which have required loading a memory page from disk */
unsigned long int majflt;
- /*
- * Process state (platform dependent):
- * D - Waiting
- * I - Idle
- * L - Acquiring lock
- * R - Running
- * S - Sleeping
- * T - Stopped (on a signal)
- * X - Dead
- * Z - Zombie
- * t - Tracing stop
- * ? - Unknown
- */
- char state;
+ /* Process state enum field (platform dependent) */
+ ProcessState state;
/* Whether the process was updated during the current scan */
bool updated;
@@ -278,8 +286,12 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
int Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
extern const ProcessFieldData Process_fields[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
extern int Process_pidDigits;
+extern int Process_uidDigits;
typedef Process* (*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
@@ -337,6 +349,9 @@ static inline bool Process_isThread(const Process* this) {
void Process_setupColumnWidths(void);
+/* Sets the size of the UID column based on the passed UID */
+void Process_setUidColumnWidth(uid_t maxUid);
+
/* Takes number in bytes (base 1024). Prints 6 columns. */
void Process_printBytes(RichString* str, unsigned long long number, bool coloring);
diff --git a/ProcessList.c b/ProcessList.c
index 1ce6b76..c4c759d 100644
--- a/ProcessList.c
+++ b/ProcessList.c
@@ -105,13 +105,19 @@ static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessFiel
if (!title)
return "- ";
- if (!Process_fields[field].pidColumn)
- return title;
+ if (Process_fields[field].pidColumn) {
+ static char titleBuffer[PROCESS_MAX_PID_DIGITS + sizeof(" ")];
+ xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
+ return titleBuffer;
+ }
- static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
- xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
+ if (field == ST_UID) {
+ static char titleBuffer[PROCESS_MAX_UID_DIGITS + sizeof(" ")];
+ xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
+ return titleBuffer;
+ }
- return titleBuffer;
+ return title;
}
void ProcessList_printHeader(const ProcessList* this, RichString* header) {
@@ -626,10 +632,15 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
ProcessList_goThroughEntries(this, false);
+ uid_t maxUid = 0;
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i);
Process_makeCommandStr(p);
+ // keep track of the highest UID for column scaling
+ if (p->st_uid > maxUid)
+ maxUid = p->st_uid;
+
if (p->tombStampMs > 0) {
// remove tombed process
if (this->monotonicMs >= p->tombStampMs) {
@@ -647,6 +658,9 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
}
}
+ // Set UID column width based on max UID.
+ Process_setUidColumnWidth(maxUid);
+
if (this->settings->treeView) {
// Clear out the hashtable to avoid any left-over processes from previous build
//
diff --git a/ScreenManager.c b/ScreenManager.c
index 54dec64..96e9c47 100644
--- a/ScreenManager.c
+++ b/ScreenManager.c
@@ -88,7 +88,7 @@ void ScreenManager_resize(ScreenManager* this) {
Panel_move(panel, lastX, y1_header);
}
-static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) {
+static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool *force_redraw) {
ProcessList* pl = this->header->pl;
Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
@@ -103,6 +103,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) {
*oldTime = newTime;
+ int oldUidDigits = Process_uidDigits;
// scan processes first - some header values are calculated there
ProcessList_scan(pl, this->state->pauseProcessUpdate);
// always update header, especially to avoid gaps in graph meters
@@ -111,6 +112,10 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
ProcessList_sort(pl);
*sortTimeout = 1;
}
+ // force redraw if the number of UID digits was changed
+ if (Process_uidDigits != oldUidDigits) {
+ *force_redraw = true;
+ }
*redraw = true;
}
if (*redraw) {
@@ -153,7 +158,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
while (!quit) {
if (this->header) {
- checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
+ checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut, &force_redraw);
}
if (redraw || force_redraw) {
diff --git a/configure.ac b/configure.ac
index 1b72222..0e69096 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@
# ----------------------------------------------------------------------
AC_PREREQ([2.69])
-AC_INIT([htop], [3.1.1], [htop@groups.io], [], [https://htop.dev/])
+AC_INIT([htop], [3.1.2], [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 62b6651..95ae960 100644
--- a/darwin/DarwinProcess.c
+++ b/darwin/DarwinProcess.c
@@ -37,11 +37,11 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[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, },
+ [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, },
[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, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
@@ -331,7 +331,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->nice = ep->p_nice;
proc->priority = ep->p_priority;
- proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?';
+ proc->state = (ep->p_stat == SZOMB) ? ZOMBIE : UNKNOWN;
/* Make sure the updated flag is set */
proc->updated = true;
@@ -386,7 +386,7 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
return;
}
- if (proc->state == 'Z') {
+ if (proc->state == ZOMBIE) {
return;
}
@@ -430,15 +430,15 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
mach_port_deallocate(mach_task_self(), port);
- char state = '?';
+ /* Taken from: https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/thread_info.h#L129 */
switch (run_state) {
- case TH_STATE_RUNNING: state = 'R'; break;
- case TH_STATE_STOPPED: state = 'S'; break;
- case TH_STATE_WAITING: state = 'W'; break;
- case TH_STATE_UNINTERRUPTIBLE: state = 'U'; break;
- case TH_STATE_HALTED: state = 'H'; break;
+ case TH_STATE_RUNNING: proc->state = RUNNING; break;
+ case TH_STATE_STOPPED: proc->state = STOPPED; break;
+ case TH_STATE_WAITING: proc->state = WAITING; break;
+ case TH_STATE_UNINTERRUPTIBLE: proc->state = UNINTERRUPTIBLE_WAIT; break;
+ case TH_STATE_HALTED: proc->state = BLOCKED; break;
+ default: proc->state = UNKNOWN;
}
- proc->state = state;
}
diff --git a/darwin/Platform.c b/darwin/Platform.c
index 3f596a3..152f617 100644
--- a/darwin/Platform.c
+++ b/darwin/Platform.c
@@ -126,7 +126,7 @@ static double Platform_nanosecondsPerMachTick = 1.0;
static double Platform_nanosecondsPerSchedulerTick = -1;
-void Platform_init(void) {
+bool Platform_init(void) {
Platform_nanosecondsPerMachTick = Platform_calculateNanosecondsPerMachTick();
// Determine the number of scheduler clock ticks per second
@@ -139,6 +139,8 @@ void Platform_init(void) {
const double nanos_per_sec = 1e9;
Platform_nanosecondsPerSchedulerTick = nanos_per_sec / scheduler_ticks_per_sec;
+
+ return true;
}
// Converts ticks in the Mach "timebase" to nanoseconds.
diff --git a/darwin/Platform.h b/darwin/Platform.h
index c03a9b4..fe75db0 100644
--- a/darwin/Platform.h
+++ b/darwin/Platform.h
@@ -19,6 +19,7 @@ in the source distribution for its full text.
#include "NetworkIOMeter.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "darwin/DarwinProcess.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
@@ -33,7 +34,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
// Converts ticks in the Mach "timebase" to nanoseconds.
// See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick` constant.
@@ -87,8 +88,8 @@ static inline void Platform_getRelease(char** string) {
static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }
-static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
- return false;
+static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
+ return STATUS_ERROR_EXIT;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/dragonflybsd/DragonFlyBSDProcess.c b/dragonflybsd/DragonFlyBSDProcess.c
index a091cc6..ceb346c 100644
--- a/dragonflybsd/DragonFlyBSDProcess.c
+++ b/dragonflybsd/DragonFlyBSDProcess.c
@@ -37,11 +37,11 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[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, },
+ [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, },
[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, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c
index e44c164..86586a8 100644
--- a/dragonflybsd/DragonFlyBSDProcessList.c
+++ b/dragonflybsd/DragonFlyBSDProcessList.c
@@ -79,8 +79,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H
size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len);
- dfpl->cp_time_o = xCalloc(cpus, sizeof_cp_time_array);
- dfpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array);
+ dfpl->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long));
+ dfpl->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long));
len = sizeof_cp_time_array;
// fetch initial single (or average) CPU clicks from kernel
@@ -542,60 +542,61 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
}
// would be nice if we could store multiple states in proc->state (as enum) and have writeField render them
+ /* Taken from: https://github.com/DragonFlyBSD/DragonFlyBSD/blob/c163a4d7ee9c6857ee4e04a3a2cbb50c3de29da1/sys/sys/proc_common.h */
switch (kproc->kp_stat) {
- case SIDL: proc->state = 'I'; isIdleProcess = true; break;
+ case SIDL: proc->state = IDLE; isIdleProcess = true; break;
case SACTIVE:
switch (kproc->kp_lwp.kl_stat) {
case LSSLEEP:
if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptible wait short/long
if (kproc->kp_lwp.kl_slptime >= MAXSLP) {
- proc->state = 'I';
+ proc->state = IDLE;
isIdleProcess = true;
} else {
- proc->state = 'S';
+ proc->state = SLEEPING;
}
else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptible lwkt wait
- proc->state = 'S';
+ proc->state = SLEEPING;
else if (kproc->kp_paddr) // uninterruptible wait
- proc->state = 'D';
+ proc->state = UNINTERRUPTIBLE_WAIT;
else // uninterruptible lwkt wait
- proc->state = 'B';
+ proc->state = UNINTERRUPTIBLE_WAIT;
break;
case LSRUN:
if (kproc->kp_lwp.kl_stat == LSRUN) {
if (!(kproc->kp_lwp.kl_tdflags & (TDF_RUNNING | TDF_RUNQ)))
- proc->state = 'Q';
+ proc->state = QUEUED;
else
- proc->state = 'R';
+ proc->state = RUNNING;
}
break;
case LSSTOP:
- proc->state = 'T';
+ proc->state = STOPPED;
break;
default:
- proc->state = 'A';
+ proc->state = PAGING;
break;
}
break;
- case SSTOP: proc->state = 'T'; break;
- case SZOMB: proc->state = 'Z'; break;
- case SCORE: proc->state = 'C'; break;
- default: proc->state = '?';
+ case SSTOP: proc->state = STOPPED; break;
+ case SZOMB: proc->state = ZOMBIE; break;
+ case SCORE: proc->state = BLOCKED; break;
+ default: proc->state = UNKNOWN;
}
if (kproc->kp_flags & P_SWAPPEDOUT)
- proc->state = 'W';
+ proc->state = SLEEPING;
if (kproc->kp_flags & P_TRACED)
- proc->state = 'T';
+ proc->state = TRACED;
if (kproc->kp_flags & P_JAILED)
- proc->state = 'J';
+ proc->state = TRACED;
if (Process_isKernelThread(proc))
super->kernelThreads++;
super->totalTasks++;
- if (proc->state == 'R')
+ if (proc->state == RUNNING)
super->runningTasks++;
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c
index bb603cb..4941445 100644
--- a/dragonflybsd/Platform.c
+++ b/dragonflybsd/Platform.c
@@ -105,8 +105,9 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
-void Platform_init(void) {
+bool Platform_init(void) {
/* no platform-specific setup needed */
+ return true;
}
void Platform_done(void) {
diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h
index f3f2ec5..281a7ee 100644
--- a/dragonflybsd/Platform.h
+++ b/dragonflybsd/Platform.h
@@ -23,6 +23,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@@ -36,7 +37,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
void Platform_done(void);
@@ -78,8 +79,8 @@ static inline void Platform_getRelease(char** string) {
static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }
-static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
- return false;
+static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
+ return STATUS_ERROR_EXIT;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c
index 3fa55ae..345edff 100644
--- a/freebsd/FreeBSDProcess.c
+++ b/freebsd/FreeBSDProcess.c
@@ -36,11 +36,11 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[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, },
+ [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, },
[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, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c
index 9bbfccb..507f480 100644
--- a/freebsd/FreeBSDProcessList.c
+++ b/freebsd/FreeBSDProcessList.c
@@ -109,8 +109,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H
size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len);
- fpl->cp_time_o = xCalloc(cpus, sizeof_cp_time_array);
- fpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array);
+ fpl->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long));
+ fpl->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long));
len = sizeof_cp_time_array;
// fetch initial single (or average) CPU clicks from kernel
@@ -578,15 +578,16 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc->nice = PRIO_MAX + 1 + kproc->ki_pri.pri_level - PRI_MIN_IDLE;
}
+ /* Taken from: https://github.com/freebsd/freebsd-src/blob/1ad2d87778970582854082bcedd2df0394fd4933/sys/sys/proc.h#L851 */
switch (kproc->ki_stat) {
- case SIDL: proc->state = 'I'; break;
- case SRUN: proc->state = 'R'; break;
- case SSLEEP: proc->state = 'S'; break;
- case SSTOP: proc->state = 'T'; break;
- case SZOMB: proc->state = 'Z'; break;
- case SWAIT: proc->state = 'D'; break;
- case SLOCK: proc->state = 'L'; break;
- default: proc->state = '?';
+ case SIDL: proc->state = IDLE; break;
+ case SRUN: proc->state = RUNNING; break;
+ case SSLEEP: proc->state = SLEEPING; break;
+ case SSTOP: proc->state = STOPPED; break;
+ case SZOMB: proc->state = ZOMBIE; break;
+ case SWAIT: proc->state = WAITING; break;
+ case SLOCK: proc->state = BLOCKED; break;
+ default: proc->state = UNKNOWN;
}
if (Process_isKernelThread(proc))
@@ -595,7 +596,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
super->totalTasks++;
- if (proc->state == 'R')
+ if (proc->state == RUNNING)
super->runningTasks++;
proc->updated = true;
}
diff --git a/freebsd/Platform.c b/freebsd/Platform.c
index 9c88b40..6c82bc2 100644
--- a/freebsd/Platform.c
+++ b/freebsd/Platform.c
@@ -127,8 +127,9 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
-void Platform_init(void) {
+bool Platform_init(void) {
/* no platform-specific setup needed */
+ return true;
}
void Platform_done(void) {
@@ -284,6 +285,7 @@ bool Platform_getDiskIO(DiskIOData* data) {
if (devstat_checkversion(NULL) < 0)
return false;
+ // use static to plug memory leak; see #841
static struct devinfo info = { 0 };
struct statinfo current = { .dinfo = &info };
diff --git a/freebsd/Platform.h b/freebsd/Platform.h
index e99232e..a1aa31b 100644
--- a/freebsd/Platform.h
+++ b/freebsd/Platform.h
@@ -19,6 +19,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@@ -32,7 +33,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
void Platform_done(void);
@@ -78,8 +79,8 @@ static inline void Platform_getRelease(char** string) {
static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }
-static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
- return false;
+static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
+ return STATUS_ERROR_EXIT;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/generic/gettime.c b/generic/gettime.c
index 975457f..b7c4885 100644
--- a/generic/gettime.c
+++ b/generic/gettime.c
@@ -49,9 +49,13 @@ void Generic_gettime_monotonic(uint64_t* msec) {
else
*msec = 0;
-#else
+#else /* lower resolution gettimeofday() should be always available */
-# error "No monotonic clock available"
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == 0)
+ *msec = ((uint64_t)tv.tv_sec * 1000) + ((uint64_t)tv.tv_usec / 1000);
+ else
+ *msec = 0;
#endif
}
diff --git a/htop.1.in b/htop.1.in
index 9c66b56..49c4a52 100644
--- a/htop.1.in
+++ b/htop.1.in
@@ -62,7 +62,7 @@ Show only the given PIDs
Sort by this column (use \-\-sort\-key help for a column list).
This will force a list view unless you specify -t at the same time.
.TP
-\fB\-u \-\-user=USERNAME\fR
+\fB\-u \-\-user=USERNAME|UID\fR
Show only the processes of a given user
.TP
\fB\-U \-\-no-unicode\fR
@@ -285,19 +285,31 @@ is active, the executable path (/proc/[pid]/exe) and the command name
(/proc/[pid]/comm) are also shown merged with the command line, if available.
The program basename is highlighted if set in the configuration. Additional
-highlighting can be configured for stale executables (cf. Exe column below).
+highlighting can be configured for stale executables (cf. EXE column below).
.TP
-.B Comm
+.B COMM
The command name of the process obtained from /proc/[pid]/comm, if readable.
.TP
-.B Exe
+.B EXE
The abbreviated basename of the executable of the process, obtained from
/proc/[pid]/exe, if readable. htop is able to read this file on linux for ALL
the processes only if it has the capability CAP_SYS_PTRACE or root privileges.
The basename is marked in red if the executable used to run the process has
-been replaced or deleted on disk since the process started. This additional
-markup can be configured.
+been replaced or deleted on disk since the process started. The information is
+obtained by processing the contents of /proc/[pid]/exe.
+
+Furthermore the basename is marked in yellow if any library is reported as having
+been replaced or deleted on disk since it was last loaded. The information is
+obtained by processing the contents of /proc/[pid]/maps.
+
+When deciding the color the replacement of the main executable always takes
+precedence over replacement of any other library. If only the memory map indicates
+a replacement of the main executable, this will show as if any other library had
+been replaced or deleted.
+
+This additional color markup can be configured in the "Display Options" section of
+the setup screen.
.TP
.B PID
The process ID.
@@ -476,7 +488,23 @@ The I/O rate of write(2) in bytes per second, for the process.
The I/O rate, IO_READ_RATE + IO_WRITE_RATE (see above).
.TP
.B CGROUP
-Which cgroup the process is in.
+Which cgroup the process is in. For a shortened view see the CCGROUP column below.
+.TP
+.B CCGROUP
+Shortened view of the cgroup name that the process is in.
+This performs some pattern-based replacements to shorten the displayed string and thus condense the information.
+ \fB/*.slice\fR is shortened to \fB/[*]\fR (exceptions below)
+ \fB/system.slice\fR is shortened to \fB/[S]\fR
+ \fB/user.slice\fR is shortened to \fB/[U]\fR
+ \fB/user-*.slice\fR is shortened to \fB/[U:*]\fR (directly preceeding \fB/[U]\fR before dropped)
+ \fB/machine.slice\fR is shortened to \fB/[M]\fR
+ \fB/machine-*.scope\fR is shortened to \fB/[SNC:*]\fR (SNC: systemd nspawn container), uppercase for the monitor
+ \fB/lxc.monitor.*\fR is shortened to \fB/[LXC:*]\fR
+ \fB/lxc.payload.*\fR is shortened to \fB/[lxc:*]\fR
+ \fB/*.scope\fR is shortened to \fB/!*\fR
+ \fB/*.service\fR is shortened to \fB/*\fR (suffix removed)
+
+Encountered escape sequences (e.g. from systemd) inside the cgroup name are not decoded.
.TP
.B OOM
OOM killer score.
diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c
new file mode 100644
index 0000000..6f3b6fe
--- /dev/null
+++ b/linux/CGroupUtils.c
@@ -0,0 +1,317 @@
+/*
+htop - CGroupUtils.h
+(C) 2021 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "linux/CGroupUtils.h"
+
+#include "XUtils.h"
+
+
+typedef struct StrBuf_state {
+ char *buf;
+ size_t size;
+ size_t pos;
+} StrBuf_state;
+
+typedef bool (*StrBuf_putc_t)(StrBuf_state* p, char c);
+
+static bool StrBuf_putc_count(StrBuf_state* p, ATTR_UNUSED char c) {
+ p->pos++;
+ return true;
+}
+
+static bool StrBuf_putc_write(StrBuf_state* p, char c) {
+ if (p->pos >= p->size)
+ return false;
+
+ p->buf[p->pos] = c;
+ p->pos++;
+ return true;
+}
+
+static bool StrBuf_putsn(StrBuf_state* p, StrBuf_putc_t w, const char* s, size_t count) {
+ while (count--)
+ if (!w(p, *s++))
+ return false;
+
+ return true;
+}
+
+static bool StrBuf_putsz(StrBuf_state* p, StrBuf_putc_t w, const char* s) {
+ while (*s)
+ if (!w(p, *s++))
+ return false;
+
+ return true;
+}
+
+static bool Label_checkEqual(const char* labelStart, size_t labelLen, const char* expected) {
+ return labelLen == strlen(expected) && String_startsWith(labelStart, expected);
+}
+
+static bool Label_checkPrefix(const char* labelStart, size_t labelLen, const char* expected) {
+ return labelLen > strlen(expected) && String_startsWith(labelStart, expected);
+}
+
+static bool Label_checkSuffix(const char* labelStart, size_t labelLen, const char* expected) {
+ return labelLen > strlen(expected) && String_startsWith(labelStart + labelLen - strlen(expected), expected);
+}
+
+static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrBuf_putc_t w) {
+ const char* str_slice_suffix = ".slice";
+ const char* str_system_slice = "system.slice";
+ const char* str_user_slice = "user.slice";
+ const char* str_machine_slice = "machine.slice";
+ const char* str_user_slice_prefix = "/user-";
+
+ const char* str_lxc_monitor_legacy = "lxc.monitor";
+ const char* str_lxc_payload_legacy = "lxc.payload";
+ const char* str_lxc_monitor_prefix = "lxc.monitor.";
+ const char* str_lxc_payload_prefix = "lxc.payload.";
+
+ const char* str_nspawn_scope_prefix = "machine-";
+ const char* str_nspawn_monitor_label = "/supervisor";
+ const char* str_nspawn_payload_label = "/payload";
+
+ const char* str_service_suffix = ".service";
+ const char* str_scope_suffix = ".scope";
+
+ while (*cgroup) {
+ if ('/' == *cgroup) {
+ while ('/' == *cgroup)
+ cgroup++;
+
+ if (!w(s, '/'))
+ return false;
+
+ continue;
+ }
+
+ const char* labelStart = cgroup;
+ const char* nextSlash = strchrnul(labelStart, '/');
+ const size_t labelLen = nextSlash - labelStart;
+
+ if (Label_checkEqual(labelStart, labelLen, str_system_slice)) {
+ cgroup = nextSlash;
+
+ if (!StrBuf_putsz(s, w, "[S]"))
+ return false;
+
+ continue;
+ }
+
+ if (Label_checkEqual(labelStart, labelLen, str_machine_slice)) {
+ cgroup = nextSlash;
+
+ if (!StrBuf_putsz(s, w, "[M]"))
+ return false;
+
+ continue;
+ }
+
+ if (Label_checkEqual(labelStart, labelLen, str_user_slice)) {
+ cgroup = nextSlash;
+
+ if (!StrBuf_putsz(s, w, "[U]"))
+ return false;
+
+ if (!String_startsWith(cgroup, str_user_slice_prefix))
+ continue;
+
+ const char* userSliceSlash = strchrnul(cgroup + strlen(str_user_slice_prefix), '/');
+ const char* sliceSpec = userSliceSlash - strlen(str_slice_suffix);
+
+ if (!String_startsWith(sliceSpec, str_slice_suffix))
+ continue;
+
+ const size_t sliceNameLen = sliceSpec - (cgroup + strlen(str_user_slice_prefix));
+
+ s->pos--;
+ if (!w(s, ':'))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_user_slice_prefix), sliceNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = userSliceSlash;
+
+ continue;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_slice_suffix)) {
+ const size_t sliceNameLen = labelLen - strlen(str_slice_suffix);
+
+ if (!w(s, '['))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup, sliceNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ if (Label_checkPrefix(labelStart, labelLen, str_lxc_payload_prefix)) {
+ const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix);
+
+ if (!StrBuf_putsz(s, w, "[lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ if (Label_checkPrefix(labelStart, labelLen, str_lxc_monitor_prefix)) {
+ const size_t cgroupNameLen = labelLen - strlen(str_lxc_monitor_prefix);
+
+ if (!StrBuf_putsz(s, w, "[LXC:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_monitor_prefix), cgroupNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ // LXC legacy cgroup naming
+ if (Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy) ||
+ Label_checkEqual(labelStart, labelLen, str_lxc_payload_legacy)) {
+ bool isMonitor = Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy);
+
+ labelStart = nextSlash;
+ while (*labelStart == '/')
+ labelStart++;
+
+ nextSlash = strchrnul(labelStart, '/');
+ if (nextSlash - labelStart > 0) {
+ if (!StrBuf_putsz(s, w, isMonitor ? "[LXC:" : "[lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, labelStart, nextSlash - labelStart))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+ continue;
+ }
+
+ labelStart = cgroup;
+ nextSlash = labelStart + labelLen;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_service_suffix)) {
+ const size_t serviceNameLen = labelLen - strlen(str_service_suffix);
+
+ if (String_startsWith(cgroup, "user@")) {
+ cgroup = nextSlash;
+
+ while(*cgroup == '/')
+ cgroup++;
+
+ continue;
+ }
+
+ if (!StrBuf_putsn(s, w, cgroup, serviceNameLen))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) {
+ const size_t scopeNameLen = labelLen - strlen(str_scope_suffix);
+
+ if (Label_checkPrefix(labelStart, scopeNameLen, str_nspawn_scope_prefix)) {
+ const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix);
+
+ const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label);
+
+ if (!StrBuf_putsz(s, w, is_monitor ? "[SNC:" : "[snc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+ if (String_startsWith(nextSlash, str_nspawn_monitor_label))
+ cgroup += strlen(str_nspawn_monitor_label);
+ else if (String_startsWith(nextSlash, str_nspawn_payload_label))
+ cgroup += strlen(str_nspawn_payload_label);
+
+ continue;
+ }
+
+ if (!w(s, '!'))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup, scopeNameLen))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ // Default behavior: Copy the full label
+ cgroup = labelStart;
+
+ if (!StrBuf_putsn(s, w, cgroup, labelLen))
+ return false;
+
+ cgroup = nextSlash;
+ }
+
+ return true;
+}
+
+char* CGroup_filterName(const char *cgroup) {
+ StrBuf_state s = {
+ .buf = NULL,
+ .size = 0,
+ .pos = 0,
+ };
+
+ if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_count)) {
+ return NULL;
+ }
+
+ s.buf = xCalloc(s.pos + 1, sizeof(char));
+ s.size = s.pos;
+ s.pos = 0;
+
+ if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_write)) {
+ free(s.buf);
+ return NULL;
+ }
+
+ s.buf[s.size] = '\0';
+ return s.buf;
+}
diff --git a/linux/CGroupUtils.h b/linux/CGroupUtils.h
new file mode 100644
index 0000000..db2df7f
--- /dev/null
+++ b/linux/CGroupUtils.h
@@ -0,0 +1,16 @@
+#ifndef HEADER_CGroupUtils
+#define HEADER_CGroupUtils
+/*
+htop - CGroupUtils.h
+(C) 2021 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include <stdbool.h>
+#include <stddef.h>
+
+
+char* CGroup_filterName(const char *cgroup);
+
+#endif /* HEADER_CGroupUtils */
diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c
index 49ae541..ba2dbd4 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -56,11 +56,11 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, .defaultSortDesc = true, },
[M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, },
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, },
- [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
+ [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, },
[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, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
@@ -81,7 +81,8 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
- [CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, },
+ [CGROUP] = { .name = "CGROUP", .title = "CGROUP (raw) ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, },
+ [CCGROUP] = { .name = "CCGROUP", .title = "CGROUP (compressed) ", .description = "Which cgroup the process is in (condensed to essentials)", .flags = PROCESS_FLAG_LINUX_CGROUP, },
[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
@@ -93,7 +94,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
[M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
[CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, .defaultSortDesc = true, },
- [SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
+ [SECATTR] = { .name = "SECATTR", .title = "Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
[CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, },
@@ -111,6 +112,7 @@ Process* LinuxProcess_new(const Settings* settings) {
void Process_delete(Object* cast) {
LinuxProcess* this = (LinuxProcess*) cast;
Process_done((Process*)cast);
+ free(this->cgroup_short);
free(this->cgroup);
#ifdef HAVE_OPENVZ
free(this->ctid);
@@ -246,7 +248,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
#ifdef HAVE_VSERVER
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
#endif
- case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup ? lp->cgroup : ""); break;
+ case CGROUP: xSnprintf(buffer, n, "%-35.35s ", lp->cgroup ? lp->cgroup : "N/A"); break;
+ case CCGROUP: xSnprintf(buffer, n, "%-35.35s ", lp->cgroup_short ? lp->cgroup_short : (lp->cgroup ? lp->cgroup : "N/A")); break;
case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break;
case IO_PRIORITY: {
int klass = IOPriority_class(lp->ioPriority);
@@ -277,7 +280,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
}
xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
break;
- case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break;
+ case SECATTR: snprintf(buffer, n, "%-30.30s ", lp->secattr ? lp->secattr : "?"); break;
case AUTOGROUP_ID:
if (lp->autogroup_id != -1) {
xSnprintf(buffer, n, "%4ld ", lp->autogroup_id);
@@ -370,6 +373,8 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
#endif
case CGROUP:
return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup);
+ case CCGROUP:
+ return SPACESHIP_NULLSTR(p1->cgroup_short, p2->cgroup_short);
case OOM:
return SPACESHIP_NUMBER(p1->oom, p2->oom);
#ifdef HAVE_DELAYACCT
diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h
index 577b903..3e5d380 100644
--- a/linux/LinuxProcess.h
+++ b/linux/LinuxProcess.h
@@ -48,6 +48,9 @@ typedef struct LinuxProcess_ {
long m_drs;
long m_lrs;
+ /* Process flags */
+ unsigned long int flags;
+
/* Data read (in bytes) */
unsigned long long io_rchar;
@@ -86,6 +89,7 @@ typedef struct LinuxProcess_ {
unsigned int vxid;
#endif
char* cgroup;
+ char* cgroup_short;
unsigned int oom;
#ifdef HAVE_DELAYACCT
unsigned long long int delay_read_time;
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index dbbc57d..3bfe7db 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -45,6 +45,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "Settings.h"
#include "XUtils.h"
+#include "linux/CGroupUtils.h"
#include "linux/LinuxProcess.h"
#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep
@@ -62,6 +63,10 @@ in the source distribution for its full text.
#define O_PATH 010000000 // declare for ancient glibc versions
#endif
+/* Not exposed yet. Defined at include/linux/sched.h */
+#ifndef PF_KTHREAD
+#define PF_KTHREAD 0x00200000
+#endif
static long long btime = -1;
@@ -310,6 +315,22 @@ static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long
return t * 100 / jiffy;
}
+/* Taken from: https://github.com/torvalds/linux/blob/64570fbc14f8d7cb3fe3995f20e26bc25ce4b2cc/fs/proc/array.c#L120 */
+static inline ProcessState LinuxProcessList_getProcessState(char state) {
+ switch (state) {
+ case 'S': return SLEEPING;
+ case 'X': return DEFUNCT;
+ case 'Z': return ZOMBIE;
+ case 't': return TRACED;
+ case 'T': return STOPPED;
+ case 'D': return UNINTERRUPTIBLE_WAIT;
+ case 'R': return RUNNING;
+ case 'P': return BLOCKED;
+ case 'I': return IDLE;
+ default: return UNKNOWN;
+ }
+}
+
static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
@@ -335,7 +356,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location = end + 2;
/* (3) state - %c */
- process->state = location[0];
+ process->state = LinuxProcessList_getProcessState(location[0]);
location += 2;
/* (4) ppid - %d */
@@ -358,8 +379,9 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
process->tpgid = strtol(location, &location, 10);
location += 1;
- /* Skip (9) flags - %u */
- location = strchr(location, ' ') + 1;
+ /* (9) flags - %u */
+ lp->flags = strtoul(location, &location, 10);
+ location += 1;
/* (10) minflt - %lu */
process->minflt = strtoull(location, &location, 10);
@@ -654,6 +676,11 @@ static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd
if (String_startsWith(readptr, "/memfd:"))
continue;
+ /* Virtualbox maps /dev/zero for memory allocation. That results in
+ * false positive, so ignore. */
+ if (String_eq(readptr, "/dev/zero (deleted)\n"))
+ continue;
+
if (strstr(readptr, " (deleted)\n")) {
proc->usesDeletedLib = true;
if (!calcSize)
@@ -834,6 +861,10 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
free(process->cgroup);
process->cgroup = NULL;
}
+ if (process->cgroup_short) {
+ free(process->cgroup_short);
+ process->cgroup_short = NULL;
+ }
return;
}
char output[PROC_LINE_LENGTH + 1];
@@ -846,9 +877,16 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
if (!ok)
break;
- char* group = strchr(buffer, ':');
- if (!group)
- break;
+ char* group = buffer;
+ for (size_t i = 0; i < 2; i++) {
+ group = strchrnul(group, ':');
+ if (!*group)
+ break;
+ group++;
+ }
+
+ char* eol = strchrnul(group, '\n');
+ *eol = '\0';
if (at != output) {
*at = ';';
@@ -859,7 +897,22 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t
left -= wrote;
}
fclose(file);
+
+ bool changed = !process->cgroup || !String_eq(process->cgroup, output);
+
free_and_xStrdup(&process->cgroup, output);
+
+ if (!changed)
+ return;
+
+ char* cgroup_short = CGroup_filterName(process->cgroup);
+ if (cgroup_short) {
+ free_and_xStrdup(&process->cgroup_short, cgroup_short);
+ free(cgroup_short);
+ } else {
+ free(process->cgroup_short);
+ process->cgroup_short = NULL;
+ }
}
#ifdef HAVE_VSERVER
@@ -1089,17 +1142,9 @@ delayacct_failure:
static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) {
char command[4096 + 1]; // max cmdline length on Linux
ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command));
- if (amtRead < 0)
+ if (amtRead <= 0)
return false;
- if (amtRead == 0) {
- if (process->state != 'Z') {
- process->isKernelThread = true;
- }
- Process_updateCmdline(process, NULL, 0, 0);
- return true;
- }
-
int tokenEnd = 0;
int tokenStart = 0;
int lastChar = 0;
@@ -1467,6 +1512,10 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
if (! LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand)))
goto errorReadingProcess;
+ if (lp->flags & PF_KTHREAD) {
+ proc->isKernelThread = true;
+ }
+
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
free(proc->tty_name);
proc->tty_name = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
@@ -1498,17 +1547,21 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
#endif
- if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
- goto errorReadingProcess;
+ if (proc->isKernelThread) {
+ Process_updateCmdline(proc, NULL, 0, 0);
+ } else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
+ Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
Process_fillStarttimeBuffer(proc);
ProcessList_add(pl, proc);
} else {
- if (settings->updateProcessNames && proc->state != 'Z') {
- if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
- goto errorReadingProcess;
+ if (settings->updateProcessNames && proc->state != ZOMBIE) {
+ if (proc->isKernelThread) {
+ Process_updateCmdline(proc, NULL, 0, 0);
+ } else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
+ Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
}
}
@@ -1544,7 +1597,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
if (!proc->cmdline && statCommand[0] &&
- (proc->state == 'Z' || Process_isKernelThread(proc) || settings->showThreadNames)) {
+ (proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
diff --git a/linux/Platform.c b/linux/Platform.c
index e305e7f..93953e8 100644
--- a/linux/Platform.c
+++ b/linux/Platform.c
@@ -611,133 +611,85 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
// Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
-#define MAX_BATTERIES 64
#define PROC_BATTERY_DIR PROCDIR "/acpi/battery"
#define PROC_POWERSUPPLY_DIR PROCDIR "/acpi/ac_adapter"
+#define PROC_POWERSUPPLY_ACSTATE_FILE PROC_POWERSUPPLY_DIR "/AC/state"
#define SYS_POWERSUPPLY_DIR "/sys/class/power_supply"
// ----------------------------------------
// READ FROM /proc
// ----------------------------------------
-static unsigned long int parseBatInfo(const char* fileName, const unsigned short int lineNum, const unsigned short int wordNum) {
- const char batteryPath[] = PROC_BATTERY_DIR;
- DIR* batteryDir = opendir(batteryPath);
+static double Platform_Battery_getProcBatInfo(void) {
+ DIR* batteryDir = opendir(PROC_BATTERY_DIR);
if (!batteryDir)
- return 0;
-
- char* batteries[MAX_BATTERIES];
- unsigned int nBatteries = 0;
- memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
+ return NAN;
- while (nBatteries < MAX_BATTERIES) {
- const struct dirent* dirEntry = readdir(batteryDir);
- if (!dirEntry)
- break;
+ uint64_t totalFull = 0;
+ uint64_t totalRemain = 0;
+ struct dirent* dirEntry = NULL;
+ while ((dirEntry = readdir(batteryDir))) {
const char* entryName = dirEntry->d_name;
if (!String_startsWith(entryName, "BAT"))
continue;
- batteries[nBatteries] = xStrdup(entryName);
- nBatteries++;
- }
- closedir(batteryDir);
+ char filePath[256];
+ char bufInfo[1024] = {0};
+ xSnprintf(filePath, sizeof(filePath), "%s/%s/info", PROC_BATTERY_DIR, entryName);
+ ssize_t r = xReadfile(filePath, bufInfo, sizeof(bufInfo));
+ if (r < 0)
+ continue;
- unsigned long int total = 0;
- for (unsigned int i = 0; i < nBatteries; i++) {
- char infoPath[30];
- xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
+ char bufState[1024] = {0};
+ xSnprintf(filePath, sizeof(filePath), "%s/%s/state", PROC_BATTERY_DIR, entryName);
+ r = xReadfile(filePath, bufState, sizeof(bufState));
+ if (r < 0)
+ continue;
- FILE* file = fopen(infoPath, "r");
- if (!file)
- break;
+ const char* line;
+
+ //Getting total charge for all batteries
+ char* buf = bufInfo;
+ while ((line = strsep(&buf, "\n")) != NULL) {
+ char field[100] = {0};
+ int val = 0;
+ if (2 != sscanf(line, "%99[^:]:%d", field, &val))
+ continue;
- char* line = NULL;
- for (unsigned short int j = 0; j < lineNum; j++) {
- free(line);
- line = String_readLine(file);
- if (!line)
+ if (String_eq(field, "last full capacity")) {
+ totalFull += val;
break;
+ }
}
- fclose(file);
-
- if (!line)
- break;
-
- char* foundNumStr = String_getToken(line, wordNum);
- const unsigned long int foundNum = atoi(foundNumStr);
- free(foundNumStr);
- free(line);
+ //Getting remaining charge for all batteries
+ buf = bufState;
+ while ((line = strsep(&buf, "\n")) != NULL) {
+ char field[100] = {0};
+ int val = 0;
+ if (2 != sscanf(line, "%99[^:]:%d", field, &val))
+ continue;
- total += foundNum;
+ if (String_eq(field, "remaining capacity")) {
+ totalRemain += val;
+ break;
+ }
+ }
}
- for (unsigned int i = 0; i < nBatteries; i++)
- free(batteries[i]);
+ closedir(batteryDir);
- return total;
+ return totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
}
static ACPresence procAcpiCheck(void) {
- ACPresence isOn = AC_ERROR;
- const char* power_supplyPath = PROC_POWERSUPPLY_DIR;
- DIR* dir = opendir(power_supplyPath);
- if (!dir)
+ char buffer[1024] = {0};
+ ssize_t r = xReadfile(PROC_POWERSUPPLY_ACSTATE_FILE, buffer, sizeof(buffer));
+ if (r < 1)
return AC_ERROR;
- for (;;) {
- const struct dirent* dirEntry = readdir(dir);
- if (!dirEntry)
- break;
-
- const char* entryName = dirEntry->d_name;
-
- if (entryName[0] != 'A')
- continue;
-
- char statePath[256];
- xSnprintf(statePath, sizeof(statePath), "%s/%s/state", power_supplyPath, entryName);
- FILE* file = fopen(statePath, "r");
- if (!file) {
- isOn = AC_ERROR;
- continue;
- }
- char* line = String_readLine(file);
-
- fclose(file);
-
- if (!line)
- continue;
-
- char* isOnline = String_getToken(line, 2);
- free(line);
-
- if (String_eq(isOnline, "on-line"))
- isOn = AC_PRESENT;
- else
- isOn = AC_ABSENT;
- free(isOnline);
- if (isOn == AC_PRESENT)
- break;
- }
-
- closedir(dir);
-
- return isOn;
-}
-
-static double Platform_Battery_getProcBatInfo(void) {
- const unsigned long int totalFull = parseBatInfo("info", 3, 4);
- if (totalFull == 0)
- return NAN;
-
- const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
- if (totalRemain == 0)
- return NAN;
-
- return totalRemain * 100.0 / (double) totalFull;
+ return String_eq(buffer, "on-line") ? AC_PRESENT : AC_ABSENT;
}
static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
@@ -750,7 +702,6 @@ static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
// ----------------------------------------
static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
-
*percent = NAN;
*isOnAC = AC_ERROR;
@@ -758,68 +709,52 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
if (!dir)
return;
- unsigned long int totalFull = 0;
- unsigned long int totalRemain = 0;
-
- for (;;) {
- const struct dirent* dirEntry = readdir(dir);
- if (!dirEntry)
- break;
+ uint64_t totalFull = 0;
+ uint64_t totalRemain = 0;
+ struct dirent* dirEntry = NULL;
+ while ((dirEntry = readdir(dir))) {
const char* entryName = dirEntry->d_name;
- char filePath[256];
-
- xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
- char type[8];
- ssize_t r = xReadfile(filePath, type, sizeof(type));
- if (r < 3)
- continue;
-
- if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') {
+ if (String_startsWith(entryName, "BAT")) {
+ char buffer[1024] = {0};
+ char filePath[256];
xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
- char buffer[1024];
- r = xReadfile(filePath, buffer, sizeof(buffer));
- if (r < 0) {
- closedir(dir);
- return;
- }
+ ssize_t r = xReadfile(filePath, buffer, sizeof(buffer));
+ if (r < 0)
+ continue;
- char* buf = buffer;
- const char* line;
bool full = false;
bool now = false;
- int fullSize = 0;
- double capacityLevel = NAN;
- #define match(str,prefix) \
- (String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL)
+ double fullCharge = 0;
+ double capacityLevel = NAN;
+ const char* line;
+ char* buf = buffer;
while ((line = strsep(&buf, "\n")) != NULL) {
- const char* ps = match(line, "POWER_SUPPLY_");
- if (!ps)
+ char field[100] = {0};
+ int val = 0;
+ if (2 != sscanf(line, "POWER_SUPPLY_%99[^=]=%d", field, &val))
continue;
- const char* capacity = match(ps, "CAPACITY=");
- if (capacity)
- capacityLevel = atoi(capacity) / 100.0;
- const char* energy = match(ps, "ENERGY_");
- if (!energy)
- energy = match(ps, "CHARGE_");
- if (!energy)
+
+ if (String_eq(field, "CAPACITY")) {
+ capacityLevel = val / 100.0;
continue;
- const char* value = (!full) ? match(energy, "FULL=") : NULL;
- if (value) {
- fullSize = atoi(value);
- totalFull += fullSize;
+ }
+
+ if (String_eq(field, "ENERGY_FULL") || String_eq(field, "CHARGE_FULL")) {
+ fullCharge = val;
+ totalFull += fullCharge;
full = true;
if (now)
break;
continue;
}
- value = (!now) ? match(energy, "NOW=") : NULL;
- if (value) {
- totalRemain += atoi(value);
+
+ if (String_eq(field, "ENERGY_NOW") || String_eq(field, "CHARGE_NOW")) {
+ totalRemain += val;
now = true;
if (full)
break;
@@ -827,23 +762,21 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
}
}
- #undef match
-
if (!now && full && !isnan(capacityLevel))
- totalRemain += (capacityLevel * fullSize);
+ totalRemain += capacityLevel * fullCharge;
- } else if (entryName[0] == 'A') {
+ } else if (String_startsWith(entryName, "AC")) {
+ char buffer[2] = {0};
if (*isOnAC != AC_ERROR)
continue;
- xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
-
- char buffer[2];
+ char filePath[256];
+ xSnprintf(filePath, sizeof(filePath), SYS_POWERSUPPLY_DIR "/%s/online", entryName);
- r = xReadfile(filePath, buffer, sizeof(buffer));
+ ssize_t r = xReadfile(filePath, buffer, sizeof(buffer));
if (r < 1) {
- closedir(dir);
- return;
+ *isOnAC = AC_ERROR;
+ continue;
}
if (buffer[0] == '0')
@@ -852,6 +785,7 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
*isOnAC = AC_PRESENT;
}
}
+
closedir(dir);
*percent = totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
@@ -901,7 +835,7 @@ void Platform_longOptionsUsage(const char* name)
#endif
}
-bool Platform_getLongOption(int opt, int argc, char** argv) {
+CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
#ifndef HAVE_LIBCAP
(void) argc;
(void) argv;
@@ -924,16 +858,16 @@ bool Platform_getLongOption(int opt, int argc, char** argv) {
Platform_capabilitiesMode = CAP_MODE_STRICT;
} else {
fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
- exit(1);
+ return STATUS_ERROR_EXIT;
}
- return true;
+ return STATUS_OK;
}
#endif
default:
break;
}
- return false;
+ return STATUS_ERROR_EXIT;
}
#ifdef HAVE_LIBCAP
@@ -1022,20 +956,22 @@ static int dropCapabilities(enum CapMode mode) {
}
#endif
-void Platform_init(void) {
+bool Platform_init(void) {
#ifdef HAVE_LIBCAP
if (dropCapabilities(Platform_capabilitiesMode) < 0)
- exit(1);
+ return false;
#endif
if (access(PROCDIR, R_OK) != 0) {
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
- exit(1);
+ return false;
}
#ifdef HAVE_SENSORS_SENSORS_H
LibSensors_init();
#endif
+
+ return true;
}
void Platform_done(void) {
diff --git a/linux/Platform.h b/linux/Platform.h
index 9c4e65d..2e2fb3e 100644
--- a/linux/Platform.h
+++ b/linux/Platform.h
@@ -27,6 +27,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "RichString.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@@ -45,8 +46,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
-
+bool Platform_init(void);
void Platform_done(void);
void Platform_setBindings(Htop_Action* keys);
@@ -100,7 +100,7 @@ static inline void Platform_getRelease(char** string) {
void Platform_longOptionsUsage(const char* name);
-bool Platform_getLongOption(int opt, int argc, char** argv);
+CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv);
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
diff --git a/linux/ProcessField.h b/linux/ProcessField.h
index 7047592..17cafa9 100644
--- a/linux/ProcessField.h
+++ b/linux/ProcessField.h
@@ -45,6 +45,7 @@ in the source distribution for its full text.
SECATTR = 123, \
AUTOGROUP_ID = 127, \
AUTOGROUP_NICE = 128, \
+ CCGROUP = 129, \
// End of list
diff --git a/netbsd/NetBSDProcess.c b/netbsd/NetBSDProcess.c
index 1597ed3..8d2b4c4 100644
--- a/netbsd/NetBSDProcess.c
+++ b/netbsd/NetBSDProcess.c
@@ -138,7 +138,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
},
[ST_UID] = {
.name = "ST_UID",
- .title = " UID ",
+ .title = "UID",
.description = "User ID of the process owner",
.flags = 0,
},
@@ -165,7 +165,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
},
[USER] = {
.name = "USER",
- .title = "USER ",
+ .title = "USER ",
.description = "Username of the process owner (or user ID if name cannot be determined)",
.flags = 0,
},
@@ -211,7 +211,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
};
Process* NetBSDProcess_new(const Settings* settings) {
- NetBSDProcess* this = xCalloc(sizeof(NetBSDProcess), 1);
+ NetBSDProcess* this = xCalloc(1, sizeof(NetBSDProcess));
Object_setClass(this, Class(NetBSDProcess));
Process_init(&this->super, settings);
return &this->super;
diff --git a/netbsd/NetBSDProcessList.c b/netbsd/NetBSDProcessList.c
index 379a491..ab0f0b7 100644
--- a/netbsd/NetBSDProcessList.c
+++ b/netbsd/NetBSDProcessList.c
@@ -331,31 +331,33 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
int nlwps = 0;
const struct kinfo_lwp* klwps = kvm_getlwps(this->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps);
+ /* TODO: According to the link below, SDYING should be a regarded state */
+ /* Taken from: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/proc.h */
switch (kproc->p_realstat) {
- case SIDL: proc->state = 'I'; break;
+ case SIDL: proc->state = IDLE; break;
case SACTIVE:
// We only consider the first LWP with a one of the below states.
for (int j = 0; j < nlwps; j++) {
if (klwps) {
switch (klwps[j].l_stat) {
- case LSONPROC: proc->state = 'P'; break;
- case LSRUN: proc->state = 'R'; break;
- case LSSLEEP: proc->state = 'S'; break;
- case LSSTOP: proc->state = 'T'; break;
- default: proc->state = '?';
+ case LSONPROC: proc->state = RUNNING; break;
+ case LSRUN: proc->state = RUNNABLE; break;
+ case LSSLEEP: proc->state = SLEEPING; break;
+ case LSSTOP: proc->state = STOPPED; break;
+ default: proc->state = UNKNOWN;
}
- if (proc->state != '?')
+ if (proc->state != UNKNOWN)
break;
} else {
- proc->state = '?';
+ proc->state = UNKNOWN;
break;
}
}
break;
- case SSTOP: proc->state = 'T'; break;
- case SZOMB: proc->state = 'Z'; break;
- case SDEAD: proc->state = 'D'; break;
- default: proc->state = '?';
+ case SSTOP: proc->state = STOPPED; break;
+ case SZOMB: proc->state = ZOMBIE; break;
+ case SDEAD: proc->state = DEFUNCT; break;
+ default: proc->state = UNKNOWN;
}
if (Process_isKernelThread(proc)) {
@@ -365,8 +367,7 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
}
this->super.totalTasks++;
- // SRUN ('R') means runnable, not running
- if (proc->state == 'P') {
+ if (proc->state == RUNNING) {
this->super.runningTasks++;
}
proc->updated = true;
diff --git a/netbsd/Platform.c b/netbsd/Platform.c
index ac81464..3b27548 100644
--- a/netbsd/Platform.c
+++ b/netbsd/Platform.c
@@ -174,8 +174,9 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
-void Platform_init(void) {
+bool Platform_init(void) {
/* no platform-specific setup needed */
+ return true;
}
void Platform_done(void) {
diff --git a/netbsd/Platform.h b/netbsd/Platform.h
index 1d1115e..e650bcd 100644
--- a/netbsd/Platform.h
+++ b/netbsd/Platform.h
@@ -24,6 +24,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@@ -42,7 +43,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
void Platform_done(void);
@@ -82,8 +83,8 @@ static inline void Platform_getRelease(char** string) {
static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }
-static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
- return false;
+static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
+ return STATUS_ERROR_EXIT;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c
index 52dcb0e..ac3def3 100644
--- a/openbsd/OpenBSDProcess.c
+++ b/openbsd/OpenBSDProcess.c
@@ -136,7 +136,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
},
[ST_UID] = {
.name = "ST_UID",
- .title = " UID ",
+ .title = "UID",
.description = "User ID of the process owner",
.flags = 0,
},
@@ -163,7 +163,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
},
[USER] = {
.name = "USER",
- .title = "USER ",
+ .title = "USER ",
.description = "Username of the process owner (or user ID if name cannot be determined)",
.flags = 0,
},
@@ -203,7 +203,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
};
Process* OpenBSDProcess_new(const Settings* settings) {
- OpenBSDProcess* this = xCalloc(sizeof(OpenBSDProcess), 1);
+ OpenBSDProcess* this = xCalloc(1, sizeof(OpenBSDProcess));
Object_setClass(this, Class(OpenBSDProcess));
Process_init(&this->super, settings);
return &this->super;
diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c
index 476af61..af7879e 100644
--- a/openbsd/OpenBSDProcessList.c
+++ b/openbsd/OpenBSDProcessList.c
@@ -345,15 +345,16 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid);
}
+ /* Taken from: https://github.com/openbsd/src/blob/6a38af0976a6870911f4b2de19d8da28723a5778/sys/sys/proc.h#L420 */
switch (kproc->p_stat) {
- case SIDL: proc->state = 'I'; break;
- case SRUN: proc->state = 'P'; break;
- case SSLEEP: proc->state = 'S'; break;
- case SSTOP: proc->state = 'T'; break;
- case SZOMB: proc->state = 'Z'; break;
- case SDEAD: proc->state = 'D'; break;
- case SONPROC: proc->state = 'R'; break;
- default: proc->state = '?';
+ case SIDL: proc->state = IDLE; break;
+ case SRUN: proc->state = RUNNABLE; break;
+ case SSLEEP: proc->state = SLEEPING; break;
+ case SSTOP: proc->state = STOPPED; break;
+ case SZOMB: proc->state = ZOMBIE; break;
+ case SDEAD: proc->state = DEFUNCT; break;
+ case SONPROC: proc->state = RUNNING; break;
+ default: proc->state = UNKNOWN;
}
if (Process_isKernelThread(proc)) {
@@ -363,7 +364,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
}
this->super.totalTasks++;
- if (proc->state == 'R') {
+ if (proc->state == RUNNING) {
this->super.runningTasks++;
}
@@ -388,7 +389,7 @@ static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) {
unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS];
- // XXX Not sure if CP_SPIN should be added to sysAllTime.
+ // XXX Not sure if CP_SPIN should be added to sysAllTime.
// See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c
#ifdef CP_SPIN
sysAllTime += times[CP_SPIN];
diff --git a/openbsd/Platform.c b/openbsd/Platform.c
index b941ba7..15467e1 100644
--- a/openbsd/Platform.c
+++ b/openbsd/Platform.c
@@ -121,8 +121,9 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
-void Platform_init(void) {
+bool Platform_init(void) {
/* no platform-specific setup needed */
+ return true;
}
void Platform_done(void) {
diff --git a/openbsd/Platform.h b/openbsd/Platform.h
index b6823b5..fd6a657 100644
--- a/openbsd/Platform.h
+++ b/openbsd/Platform.h
@@ -20,6 +20,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@@ -34,7 +35,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
void Platform_done(void);
@@ -76,8 +77,8 @@ static inline void Platform_getRelease(char** string) {
static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }
-static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
- return false;
+static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
+ return STATUS_ERROR_EXIT;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/pcp/PCPProcess.c b/pcp/PCPProcess.c
index a983a96..e686d51 100644
--- a/pcp/PCPProcess.c
+++ b/pcp/PCPProcess.c
@@ -53,11 +53,11 @@ const ProcessFieldData Process_fields[] = {
[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 (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, },
[M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, },
- [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
+ [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, },
[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, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c
index 554e43d..cae097f 100644
--- a/pcp/PCPProcessList.c
+++ b/pcp/PCPProcessList.c
@@ -142,10 +142,27 @@ static inline char Metric_instance_char(int metric, int pid, int offset, char fa
return fallback;
}
+static inline ProcessState PCPProcessList_getProcessState(char state) {
+ switch (state) {
+ case '?': return UNKNOWN;
+ case 'R': return RUNNING;
+ case 'W': return WAITING;
+ case 'D': return UNINTERRUPTIBLE_WAIT;
+ case 'P': return PAGING;
+ case 'T': return STOPPED;
+ case 't': return TRACED;
+ case 'Z': return ZOMBIE;
+ case 'X': return DEFUNCT;
+ case 'I': return IDLE;
+ case 'S': return SLEEPING;
+ default: return UNKNOWN;
+ }
+}
+
static void PCPProcessList_updateID(Process* process, int pid, int offset) {
process->tgid = Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1);
process->ppid = Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1);
- process->state = Metric_instance_char(PCP_PROC_STATE, pid, offset, '?');
+ process->state = PCPProcessList_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?'));
}
static void PCPProcessList_updateInfo(Process* process, int pid, int offset, char* command, size_t commLen) {
@@ -283,7 +300,7 @@ static void PCPProcessList_updateUsername(Process* process, int pid, int offset,
static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, const char* comm) {
pmAtomValue value;
if (!PCPMetric_instance(PCP_PROC_PSARGS, pid, offset, &value, PM_TYPE_STRING)) {
- if (process->state != 'Z')
+ if (process->state != ZOMBIE)
process->isKernelThread = true;
Process_updateCmdline(process, NULL, 0, 0);
return;
@@ -351,7 +368,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) {
proc->updated = true;
proc->show = false;
- if (proc->state == 'R')
+ if (proc->state == RUNNING)
pl->runningTasks++;
pl->kernelThreads++;
pl->totalTasks++;
@@ -360,7 +377,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
proc->updated = true;
proc->show = false;
- if (proc->state == 'R')
+ if (proc->state == RUNNING)
pl->runningTasks++;
pl->userlandThreads++;
pl->totalTasks++;
@@ -398,7 +415,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
PCPProcessList_updateCmdline(proc, pid, offset, command);
Process_fillStarttimeBuffer(proc);
ProcessList_add(pl, proc);
- } else if (settings->updateProcessNames && proc->state != 'Z') {
+ } else if (settings->updateProcessNames && proc->state != ZOMBIE) {
PCPProcessList_updateCmdline(proc, pid, offset, command);
}
@@ -420,7 +437,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP)
PCPProcessList_readAutogroup(pp, pid, offset);
- if (proc->state == 'Z' && !proc->cmdline && command[0]) {
+ if (proc->state == ZOMBIE && !proc->cmdline && command[0]) {
Process_updateCmdline(proc, command, 0, strlen(command));
} else if (Process_isThread(proc)) {
if ((settings->showThreadNames || Process_isKernelThread(proc)) && command[0]) {
@@ -439,7 +456,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period,
(hideUserlandThreads && Process_isUserlandThread(proc)));
pl->totalTasks++;
- if (proc->state == 'R')
+ if (proc->state == RUNNING)
pl->runningTasks++;
proc->updated = true;
}
diff --git a/pcp/Platform.c b/pcp/Platform.c
index cd7b1f4..150660a 100644
--- a/pcp/Platform.c
+++ b/pcp/Platform.c
@@ -257,7 +257,7 @@ size_t Platform_addMetric(PCPMetric id, const char* name) {
/* global state from the environment and command line arguments */
pmOptions opts;
-void Platform_init(void) {
+bool Platform_init(void) {
const char* source;
if (opts.context == PM_CONTEXT_ARCHIVE) {
source = opts.archives[0];
@@ -277,12 +277,12 @@ void Platform_init(void) {
}
if (sts < 0) {
fprintf(stderr, "Cannot setup PCP metric source: %s\n", pmErrStr(sts));
- exit(1);
+ return false;
}
/* setup timezones and other general startup preparation completion */
if (pmGetContextOptions(sts, &opts) < 0 || opts.errors) {
pmflush();
- exit(1);
+ return false;
}
pcp = xCalloc(1, sizeof(Platform));
@@ -309,7 +309,8 @@ void Platform_init(void) {
sts = pmLookupName(pcp->totalMetrics, pcp->names, pcp->pmids);
if (sts < 0) {
fprintf(stderr, "Error: cannot lookup metric names: %s\n", pmErrStr(sts));
- exit(1);
+ Platform_done();
+ return false;
}
for (size_t i = 0; i < pcp->totalMetrics; i++) {
@@ -361,6 +362,8 @@ void Platform_init(void) {
Platform_getRelease(0);
Platform_getMaxCPU();
Platform_getMaxPid();
+
+ return true;
}
void Platform_dynamicColumnsDone(Hashtable* columns) {
@@ -697,16 +700,16 @@ void Platform_longOptionsUsage(ATTR_UNUSED const char* name) {
" --timezone=TZ set reporting timezone\n");
}
-bool Platform_getLongOption(int opt, ATTR_UNUSED int argc, char** argv) {
+CommandLineStatus Platform_getLongOption(int opt, ATTR_UNUSED int argc, char** argv) {
/* libpcp export without a header definition */
extern void __pmAddOptHost(pmOptions*, char*);
switch (opt) {
case PLATFORM_LONGOPT_HOST: /* --host=HOSTSPEC */
if (argv[optind][0] == '\0')
- return false;
+ return STATUS_ERROR_EXIT;
__pmAddOptHost(&opts, optarg);
- return true;
+ return STATUS_OK;
case PLATFORM_LONGOPT_HOSTZONE: /* --hostzone */
if (opts.timezone) {
@@ -715,23 +718,23 @@ bool Platform_getLongOption(int opt, ATTR_UNUSED int argc, char** argv) {
} else {
opts.tzflag = 1;
}
- return true;
+ return STATUS_OK;
case PLATFORM_LONGOPT_TIMEZONE: /* --timezone=TZ */
if (argv[optind][0] == '\0')
- return false;
+ return STATUS_ERROR_EXIT;
if (opts.tzflag) {
pmprintf("%s: at most one of -Z and -z allowed\n", pmGetProgname());
opts.errors++;
} else {
opts.timezone = optarg;
}
- return true;
+ return STATUS_OK;
default:
break;
}
- return false;
+ return STATUS_ERROR_EXIT;
}
void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/pcp/Platform.h b/pcp/Platform.h
index 37a8799..ad38cbb 100644
--- a/pcp/Platform.h
+++ b/pcp/Platform.h
@@ -34,6 +34,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "RichString.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "pcp/PCPDynamicColumn.h"
#include "pcp/PCPDynamicMeter.h"
@@ -67,7 +68,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
void Platform_done(void);
@@ -126,7 +127,7 @@ enum {
void Platform_longOptionsUsage(const char* name);
-bool Platform_getLongOption(int opt, int argc, char** argv);
+CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv);
extern pmOptions opts;
diff --git a/solaris/Platform.c b/solaris/Platform.c
index eabbab5..cdc79ae 100644
--- a/solaris/Platform.c
+++ b/solaris/Platform.c
@@ -122,8 +122,9 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
-void Platform_init(void) {
+bool Platform_init(void) {
/* no platform-specific setup needed */
+ return true;
}
void Platform_done(void) {
diff --git a/solaris/Platform.h b/solaris/Platform.h
index a60b978..b140788 100644
--- a/solaris/Platform.h
+++ b/solaris/Platform.h
@@ -35,6 +35,7 @@ in the source distribution for its full text.
#include "NetworkIOMeter.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@@ -59,7 +60,7 @@ extern const ProcessField Platform_defaultFields[];
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
void Platform_done(void);
@@ -105,8 +106,8 @@ static inline void Platform_getRelease(char** string) {
static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }
-static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
- return false;
+static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
+ return STATUS_ERROR_EXIT;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/solaris/SolarisProcess.c b/solaris/SolarisProcess.c
index c1f4958..b0f6d94 100644
--- a/solaris/SolarisProcess.c
+++ b/solaris/SolarisProcess.c
@@ -39,11 +39,11 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[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, },
+ [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, },
[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, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c
index 33c6477..71e85fc 100644
--- a/solaris/SolarisProcessList.c
+++ b/solaris/SolarisProcessList.c
@@ -363,6 +363,19 @@ static void SolarisProcessList_updateCwd(pid_t pid, Process* proc) {
free_and_xStrdup(&proc->procCwd, target);
}
+/* Taken from: https://docs.oracle.com/cd/E19253-01/817-6223/6mlkidlom/index.html#tbl-sched-state */
+static inline ProcessState SolarisProcessList_getProcessState(char state) {
+ switch (state) {
+ case 'S': return SLEEPING;
+ case 'R': return RUNNABLE;
+ case 'O': return RUNNING;
+ case 'Z': return ZOMBIE;
+ case 'T': return STOPPED;
+ case 'I': return IDLE;
+ default: return UNKNOWN;
+ }
+}
+
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
@@ -402,7 +415,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
proc->priority = _lwpsinfo->pr_pri;
proc->nice = _lwpsinfo->pr_nice - NZERO;
proc->processor = _lwpsinfo->pr_onpro;
- proc->state = _lwpsinfo->pr_sname;
+ proc->state = SolarisProcessList_getProcessState(_lwpsinfo->pr_sname);
// NOTE: This 'percentage' is a 16-bit BINARY FRACTIONS where 1.0 = 0x8000
// Source: https://docs.oracle.com/cd/E19253-01/816-5174/proc-4/index.html
// (accessed on 18 November 2017)
@@ -462,11 +475,11 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
if (proc->isKernelThread && !pl->settings->hideKernelThreads) {
pl->kernelThreads += proc->nlwp;
pl->totalTasks += proc->nlwp + 1;
- if (proc->state == 'O') {
+ if (proc->state == RUNNING) {
pl->runningTasks++;
}
} else if (!proc->isKernelThread) {
- if (proc->state == 'O') {
+ if (proc->state == RUNNING) {
pl->runningTasks++;
}
if (pl->settings->hideUserlandThreads) {
diff --git a/unsupported/Platform.c b/unsupported/Platform.c
index 9be56ea..e55de4a 100644
--- a/unsupported/Platform.c
+++ b/unsupported/Platform.c
@@ -68,8 +68,9 @@ const MeterClass* const Platform_meterTypes[] = {
static const char Platform_unsupported[] = "unsupported";
-void Platform_init(void) {
+bool Platform_init(void) {
/* no platform-specific setup needed */
+ return true;
}
void Platform_done(void) {
diff --git a/unsupported/Platform.h b/unsupported/Platform.h
index ace7aae..7f4ad9a 100644
--- a/unsupported/Platform.h
+++ b/unsupported/Platform.h
@@ -15,6 +15,7 @@ in the source distribution for its full text.
#include "NetworkIOMeter.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "unsupported/UnsupportedProcess.h"
@@ -27,7 +28,7 @@ extern const ProcessField Platform_defaultFields[];
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
+bool Platform_init(void);
void Platform_done(void);
@@ -65,8 +66,8 @@ void Platform_getRelease(char** string);
static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }
-static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
- return false;
+static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
+ return STATUS_ERROR_EXIT;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
diff --git a/unsupported/UnsupportedProcess.c b/unsupported/UnsupportedProcess.c
index af88a3e..b1f63c6 100644
--- a/unsupported/UnsupportedProcess.c
+++ b/unsupported/UnsupportedProcess.c
@@ -34,11 +34,11 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[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, },
+ [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, },
[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, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c
index 470c65b..b64de41 100644
--- a/unsupported/UnsupportedProcessList.c
+++ b/unsupported/UnsupportedProcessList.c
@@ -57,7 +57,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc->updated = true;
- proc->state = 'R';
+ proc->state = RUNNING;
proc->isKernelThread = false;
proc->isUserlandThread = false;
proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */

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