86 Commits
3.0.3 ... 3.0.4

Author SHA1 Message Date
0b989ee38c Bump version number for 3.0.4 release 2020-12-22 17:39:42 +11:00
3fb0024fd3 Merge branch 'misc' of https://github.com/cgzones/htop into cgzones-misc 2020-12-22 17:30:29 +11:00
dfb9b82607 Resolve clang-analyzer signed/unsigned comparison CI failure 2020-12-22 16:58:17 +11:00
fc7aead36b Merge branch 'harden_makecommandstr' of https://github.com/BenBE/htop into BenBE-harden_makecommandstr 2020-12-22 16:55:11 +11:00
737cd6167a Merge branch 'resize_bar' of https://github.com/cgzones/htop into cgzones-resize_bar 2020-12-22 15:25:08 +11:00
6502b02666 DiD: Ensure string offsets are inside string boundaries 2020-12-21 22:35:38 +01:00
cdfd407e2e Panel_init: initialize selectedLen member 2020-12-21 22:34:50 +01:00
64c05a1ed5 EnvScreen: mark local class functions static 2020-12-21 22:34:50 +01:00
a7612b0b7d TraceScreen: mark local class functions static 2020-12-21 22:34:50 +01:00
3ec8f67ab2 InfoScreen: drop unused member 2020-12-21 22:34:50 +01:00
10c6810bff Avoid NULL dereference on zombie processes
Fixes #361
2020-12-21 22:29:18 +01:00
068561351f Document dynamic bindings and assumed external configuration 2020-12-21 22:15:28 +01:00
9b8b380c32 De-lazy @cgzones :) 2020-12-21 20:40:00 +01:00
a09ad6b8b4 Action: sort key binding assignments
Avoid accidental duplicate usage.
2020-12-21 15:53:42 +01:00
9a86577cf2 DragonFlyBSD update
- move some functions to file scope
- drop unused global variable
2020-12-20 18:32:04 +01:00
8db8b9edac DragonFlyBSD update
- drop unused kinfo includes and link argument
- detect kvm library necessity at configure step
- fix variable typo
2020-12-20 18:22:41 +01:00
4a73e80338 Make remaining number literals use uppercase 2020-12-20 17:17:51 +01:00
5fa1c7040d Minor typo and comment clarification 2020-12-20 17:15:51 +01:00
3f9c63d5c0 MetersPanel: drop color interruption in FunctionBar 2020-12-20 17:02:20 +01:00
358d20687f Use variable-length-array instead of small dynamic allocation 2020-12-20 17:01:50 +01:00
e3862aa67e Rework drawing of FunctionBar
Draw the FunctionBar within Panel_draw instead of manually throughout
the code.
Add an optional PanelClass function drawFunctionBar, to allow specific
panels to override the default FunctionBar_draw call.
Rework the code on color change, to really change all colors (selection
markers and panel headers).

Closes: #402
2020-12-20 17:01:50 +01:00
7e7a53c415 Mark event arrays const 2020-12-20 16:58:37 +01:00
6b100b0cf4 Use upper case numeric literals
See https://rules.sonarsource.com/c/RSPEC-818
2020-12-20 16:58:17 +01:00
6e46fd6f1f BarMeter: rework text padding
In case the text is too long for the bar, try to fit by truncating at a
space character.

E.g.
    [|24.1% 2000Mhz 40°C]
    [24.1% 2000Mhz 40°C]
    [||||24.1% 2000Mhz]
    [|||24.1% 2000Mhz]
    [||24.1% 2000Mhz]
    [|24.1% 2000Mhz]
    [24.1% 2000Mhz]
    [||||   24.1%]
    [||||  24.1%]
    [|||| 24.1%]
    [||||24.1%]
    [|||24.1%]
    [||24.1%]
    [|24.1%]
    [24.1%]
    [24.1]
    [24.]
    [24]
    [2]
2020-12-20 16:55:17 +01:00
22da57d621 CPUMeter: drop minimum width of CPU usage in bar mode
The usage percentage is the first text, no need to set a minimum width.
The BarMeter does already add padding.
2020-12-20 16:55:17 +01:00
c5e31ba4aa Meter: fix artifacts with very tiny width
- The Bar Meter might override the right border
- The TextMeter might wrap-around into the next line
2020-12-20 16:55:17 +01:00
f878f302ca Remove duplicate newline in CRT_fatalError calls 2020-12-19 21:30:39 +01:00
67ccd6b909 Unhardcode tick-to-ms conversion
Division by 100000.0 worked because `sysconf(_SC_CLK_TCK)` happened to be 100.

By unhardcoding:

1) It becomes more clear what this 100000.0 figure comes from.
2) It protects against bugs in the case `sysconf(_SC_CLK_TCK)` ever changes.
2020-12-19 21:30:39 +01:00
f614b8a19f Mark Platform_defaultFields const 2020-12-19 21:13:32 +01:00
c150e4bde9 Enable -Wformat=2
Now that the global format variable Process_pidFormat is gone, enable
the compiler warning -Wformat=2.
2020-12-19 21:13:32 +01:00
9f68c8d341 Merge Process_pidColumns into Process_fields and rework auto-fit for PID-like columns 2020-12-19 21:13:32 +01:00
89473cc9ae Rework enum ProcessField
Use only one enum instead of a global and a platform specific one.
Drop Platform_numberOfFields global variable.
Set known size of Process_fields array
2020-12-19 21:13:32 +01:00
d872e36308 LinuxProcess: drop dead Process columns 2020-12-19 21:13:32 +01:00
77db240b48 Split boilerplate and platform-independent field comparison
This acheives two things:
- Allows for simple tie-breaking if values compare equal (needed to make sorting the tree-view stable)
- Allows for platform-dependent overriding of the sort-order for specific fields

Also fixes a small oversight on DragonFlyBSD when default-sorting.
2020-12-19 16:02:34 +01:00
2327260ee8 Separate tree and list sort orders
Implements the suggestion from https://github.com/htop-dev/htop/issues/399#issuecomment-747861013

Thanks to the refactors from 0bd5c8fb5da and 6393baa74e5, this was really easy
and clean to do.

It maintains the "Tree view always by PID" option in the Settings, which
results in some specific behaviors such as "clicking on the column header to
exit tree view" and "picking a new sort order to exit tree view", for the sake
of the muscle memory of long time htop users. :)
2020-12-19 16:02:34 +01:00
e8c6994f40 Add "Tree view is always sorted by PID" option to mimic htop 2 behavior 2020-12-19 16:02:34 +01:00
3d1703f16f Invert Process_compare resolution so that superclass matches run first
* This removes duplicated code that adjusts the sort direction from every
  OS-specific folder.
* Most fields in a regular htop screen are OS-independent, so trying
  Process_compare first and only falling back to the OS-specific
  compareByKey function if it's an OS-specific field makes sense.
* This will allow us to override the sortKey in a global way without having
  to edit each OS-specific file.
2020-12-19 16:02:34 +01:00
52fa4e7ee4 Fix typo 2020-12-18 23:35:28 +01:00
27b8d81ed2 ProcessList: save scan time in millisecond
The delay is saved in deciseconds, use a bigger resolution to avoid
timing irregularities.
2020-12-18 22:43:21 +01:00
26993d2d2b Support clock_gettime() on OSX El Capitan and earlier 2020-12-18 22:43:21 +01:00
0401df8cbd Update key mapping documentation for sorting 2020-12-18 07:37:23 +01:00
0cb257586a Move macro definitions close to usage 2020-12-16 19:13:56 +01:00
1193c6e349 Use common naming for bare enum types 2020-12-16 19:13:56 +01:00
edd6130be7 MainPanel: use actual KEY_RESIZE instead of KEY_SHUFFLE
KEY_RESIZE (0632) is equal to KEY_SHUFFLE (0x19a)
2020-12-16 19:12:50 +01:00
107e3c8aa5 MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
KEY_SHUFFLE might get send from time to time, e.g. in a tmux session.
2020-12-15 14:23:09 +01:00
4eeeb63647 LibSensors: fix unversioned libsensors library name 2020-12-15 13:54:32 +01:00
eb36385a6b LibSensors: restore temperature for Raspberry Pi
sensors output:
  cpu_thermal-virtual-0
  Adapter: Virtual device
  temp1:        +58.0 C  (crit = +90.0 C)
2020-12-15 13:46:46 +01:00
79970f05f3 Meter: restore non-wide-character build
Use mbstowcs() only with wide ncurses support.

Closes: #401
2020-12-15 12:05:39 +01:00
61b8e31b41 Misc CRT cleanup 2020-12-14 21:11:20 +01:00
c9583c692d Handle absence of package CPU temperature
Resolves: #389
2020-12-14 21:07:07 +01:00
4507911cc3 Merge pull request #398 from natoscott/harden-linux-btime-init
Harden the extraction of boot time for the Linux platform
2020-12-14 17:53:24 +11:00
b7836515e8 Harden the extraction of boot time for the Linux platform
There is a possible path - albeit theoretical really - through
the btime initialization code in Linux ProcessList_new(), when
String_startsWith() is always false, which can result in btime
not being initialized.

This commit refactors the code to remove that possibility.
2020-12-14 12:16:32 +11:00
a3db2da4a7 Cleanup initialization of jiffies on the Linux platform
Small cleanups - add error handling, remove a local static
variable and refactor LinuxProcess_adjustTime (also rename
it, as its in LinuxProcessList.c not LinuxProcess.c) - and
while there, move the related 'btime' global variable into
LinuxProcessList.c so it can be made static.

Resolves https://github.com/htop-dev/htop/issues/384
2020-12-14 11:56:13 +11:00
cf982f2928 Merge pull request #395 from natoscott/man-page-linting
Remove superflous breaks around man page section heads
2020-12-14 11:48:01 +11:00
8d69a9a53e Simplify initialization of the Linux haveSmapsRollup variable 2020-12-14 01:46:29 +01:00
366b78edd9 Remove superflous breaks around man page section heads
There is no need to start a paragraph explicitly after
a section header (SH) in troff - some man linters will
complain about this as well.
2020-12-14 11:03:46 +11:00
f8a610e6e1 Merge branch 'fix-dlopen-libsensors-debian' of fasterit/htop 2020-12-13 20:09:06 +01:00
4b1a4a4ebd Merge branch 'fix_mach_timebase' of benbe/htop 2020-12-13 20:02:38 +01:00
3655b6ca0b Add column in darwin to indicate whether the the process is running under translation 2020-12-13 17:58:16 +01:00
1506283aff Move Process_fields from darwin/Platform to darwin/DarwinProcess 2020-12-13 17:58:16 +01:00
4b877eb16a Move Process_fields from unsupported/Platform to unsupported/UnsupportedProcess 2020-12-13 17:58:16 +01:00
f32f0188cd Correct timebase for non-x86 CPUs on Darwin
Fixes: #368
2020-12-13 11:47:34 +01:00
e65cdf947c Sort include in Darwin platform headers 2020-12-13 11:47:34 +01:00
ab60f59ed8 Check if clock_gettime needs linking of librt 2020-12-13 00:55:50 +01:00
8149823d56 Define O_PATH if not already defined 2020-12-13 00:55:50 +01:00
12421f460a Fix dlopen issue for libsensors5 in Debian Buster, Bullseye
libsensors.so is provided only by the -dev package, so search for
libsensors.so.5 (installed from the libsensors5 package) explicitly

see: dpkg-query -S libsensors.so
2020-12-12 20:08:17 +01:00
880eecabf5 Indentation and line continuation fixes in configure.ac 2020-12-12 19:49:52 +01:00
738d31b903 Add sys/dirent.h to iwyu/htop.imp 2020-12-11 20:57:19 +01:00
28bc087d8a Drop redundant sys/dirent.h include
sys/dirent.h is included by dirent.h in FreeBSD, and does not exist in Debian GNU/kFreeBSD
2020-12-11 20:57:19 +01:00
2700d99069 Merge pull request #379 from natoscott/streamline-pagesize-variables
Cull the definitions of pageSize and pageSizeKB from CRT.c
2020-12-11 11:06:40 +11:00
75e9f9a8d9 Cull the definitions of pageSize and pageSizeKB from CRT.c
By storing the per-process m_resident and m_virt values in the form
htop wants to display them in (KB, not pages), we no longer need to
have definitions of pageSize and pageSizeKB in the common CRT code.

These variables were never really CRT (i.e. display) related in the
first place.  It turns out the darwin platform code doesn't need to
use these at all (the process values are extracted from the kernel
in bytes not pages) and the other platforms can each use their own
local pagesize variables, in more appropriate locations.

Some platforms were actually already doing this, so this change is
removing duplication of logic and variables there.
2020-12-10 11:57:48 +11:00
db5687a355 Sort in paused mode after inverting sort order 2020-12-09 13:43:07 +01:00
7b739b6292 Fix pause mode ("Z") in tree view 2020-12-09 13:28:15 +01:00
ded9c5d363 PSI Meter: use constant width and only print ten-duration as bar 2020-12-08 23:09:35 +01:00
2d231d77ca Process: simplify 2020-12-08 22:37:15 +01:00
f6613db5cd Additional code simplification
Additional correction for #375
2020-12-08 21:24:19 +01:00
4c44a70f96 Fix broken tree display on inverted sort order
Fixes #375
2020-12-08 21:12:54 +01:00
157086e750 Split RichString_(append|appendn|write) into wide and ascii
RichString_writeFrom takes a top spot during performance analysis due to the
calls to mbstowcs() and iswprint().

Most of the time we know in advance that we are only going to print regular
ASCII characters.
2020-12-08 20:58:40 +01:00
5506925b34 Use sizeof buffer instead of magic number 2020-12-08 16:36:00 +01:00
c6d9fa279b travis CI: drop macOS and Linux builds
They are covered by GitHub CI
Also testing on s390x does not serve much
2020-12-08 16:07:45 +01:00
dcf7ad386c GitHub CI: add macOS build 2020-12-08 16:07:45 +01:00
30bf212185 Merge branch 'gentoo' of cgzones/htop 2020-12-07 16:29:52 +01:00
05969998c1 SELinuxMeter: silence comparison warning on 32-bit
linux/SELinuxMeter.c: In function ‘hasSELinuxMount’:
linux/SELinuxMeter.c:38:21: warning: comparison of integer expressions of different signedness: ‘__fsword_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]
   38 |    if (sfbuf.f_type != SELINUX_MAGIC) {
      |                     ^~

Origin: 7df27b78e9/libselinux/src/init.c (L40)
2020-12-07 16:05:12 +01:00
ead978bce6 configure: check for additional linker flags for keypad(3)
Gentoo requires an explicit addition of -ltinfo

Resolves: https://bugs.gentoo.org/show_bug.cgi?id=690840
2020-12-07 15:33:16 +01:00
4f88d38256 Correct the version of htop development repo 2020-12-07 19:57:44 +11:00
f03f48a0fb Change version string to note development repo build 2020-12-07 12:16:06 +11:00
101 changed files with 1522 additions and 1312 deletions

View File

@ -102,6 +102,23 @@ jobs:
- name: Build
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)"
build-macos-latest-clang:
runs-on: macOS-latest
env:
CC: clang
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: brew install automake
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure --enable-werror
- name: Build
run: make -k
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror"
whitespace_check:
runs-on: ubuntu-latest
steps:

View File

@ -6,17 +6,6 @@ compiler:
os:
- freebsd
- linux
- osx
arch:
- amd64
- s390x
before_script:
if [[ ${TRAVIS_CPU_ARCH} == 's390x' ]]; then
sudo apt-get update && sudo apt-get install -y libncursesw5-dev ;
fi
script:
- ./autogen.sh

114
Action.c
View File

@ -158,8 +158,7 @@ static bool collapseIntoParent(Panel* panel) {
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
Settings_setSortKey(settings, sortKey);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
@ -171,7 +170,7 @@ static Htop_Reaction sortBy(State* st) {
for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey)
if (fields[i] == Settings_getActiveSortKey(st->settings))
Panel_setSelected(sortPanel, i);
free(name);
@ -231,7 +230,7 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) {
st->settings->direction = 1;
st->settings->treeDirection = 1;
}
ProcessList_expandTree(st->pl);
@ -273,6 +272,8 @@ static Htop_Reaction actionLowerPriority(State* st) {
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
@ -340,7 +341,7 @@ static Htop_Reaction actionKill(State* st) {
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true, true);
Panel_draw(st->panel, false, true, true);
refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
napms(500);
@ -371,7 +372,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]);
Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW);
return HTOP_KEEP_FOLLOWING;
}
@ -460,7 +461,7 @@ static const struct {
{ .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" },
{ .key = " F: ", .info = "cursor follows process" },
{ .key = " F6 + -: ", .info = "expand/collapse tree" },
{ .key = " + -: ", .info = "expand/collapse tree" },
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
{ .key = " I: ", .info = "invert sort order" },
{ .key = " F6 > .: ", .info = "select sort column" },
@ -638,60 +639,59 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
}
void Action_setBindings(Htop_Action* keys) {
keys[KEY_RESIZE] = actionResize;
keys['M'] = actionSortByMemory;
keys['T'] = actionSortByTime;
keys['P'] = actionSortByCPU;
keys['H'] = actionToggleUserlandThreads;
keys['K'] = actionToggleKernelThreads;
keys['p'] = actionToggleProgramPath;
keys['m'] = actionToggleMergedCommand;
keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter;
keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch;
keys['n'] = actionIncNext;
keys['N'] = actionIncPrev;
keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority;
keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder;
keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
keys['>'] = actionSetSortColumn;
keys['.'] = actionSetSortColumn;
keys[KEY_F(10)] = actionQuit;
keys['q'] = actionQuit;
keys['a'] = actionSetAffinity;
keys[KEY_F(9)] = actionKill;
keys['k'] = actionKill;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
keys['x'] = actionShowLocks;
keys['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag;
keys['\014'] = actionRedraw; // Ctrl+L
keys[KEY_F(1)] = actionHelp;
keys['h'] = actionHelp;
keys['+'] = actionExpandOrCollapse;
keys[','] = actionSetSortColumn;
keys['-'] = actionExpandOrCollapse;
keys['.'] = actionSetSortColumn;
keys['/'] = actionIncSearch;
keys['<'] = actionSetSortColumn;
keys['='] = actionExpandOrCollapse;
keys['>'] = actionSetSortColumn;
keys['?'] = actionHelp;
keys['C'] = actionSetup;
keys['F'] = Action_follow;
keys['H'] = actionToggleUserlandThreads;
keys['I'] = actionInvertSortOrder;
keys['K'] = actionToggleKernelThreads;
keys['M'] = actionSortByMemory;
keys['N'] = actionIncPrev;
keys['P'] = actionSortByCPU;
keys['S'] = actionSetup;
keys['T'] = actionSortByTime;
keys['U'] = actionUntagAll;
keys['Z'] = actionTogglePauseProcessUpdate;
keys['['] = actionLowerPriority;
keys['\014'] = actionRedraw; // Ctrl+L
keys['\177'] = actionCollapseIntoParent;
keys['\\'] = actionIncFilter;
keys[']'] = actionHigherPriority;
keys['a'] = actionSetAffinity;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['h'] = actionHelp;
keys['k'] = actionKill;
keys['l'] = actionLsof;
keys['m'] = actionToggleMergedCommand;
keys['n'] = actionIncNext;
keys['p'] = actionToggleProgramPath;
keys['q'] = actionQuit;
keys['s'] = actionStrace;
keys['t'] = actionToggleTreeView;
keys['u'] = actionFilterByUser;
keys['w'] = actionShowCommandScreen;
keys['Z'] = actionTogglePauseProcessUpdate;
keys['x'] = actionShowLocks;
keys[KEY_F(1)] = actionHelp;
keys[KEY_F(2)] = actionSetup;
keys[KEY_F(3)] = actionIncSearch;
keys[KEY_F(4)] = actionIncFilter;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(7)] = actionHigherPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys[KEY_F(9)] = actionKill;
keys[KEY_F(10)] = actionQuit;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys[KEY_RESIZE] = actionResize;
}

View File

@ -59,25 +59,25 @@ static void MaskItem_delete(Object* cast) {
static void MaskItem_display(const Object* cast, RichString* out) {
const MaskItem* this = (const MaskItem*)cast;
assert (this != NULL);
RichString_append(out, CRT_colors[CHECK_BOX], "[");
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
if (this->value == 2) {
RichString_append(out, CRT_colors[CHECK_MARK], "x");
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else if (this->value == 1) {
RichString_append(out, CRT_colors[CHECK_MARK], "o");
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
} else {
RichString_append(out, CRT_colors[CHECK_MARK], " ");
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
}
RichString_append(out, CRT_colors[CHECK_BOX], "]");
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent) {
RichString_append(out, CRT_colors[PROCESS_TREE], this->indent);
RichString_append(out, CRT_colors[PROCESS_TREE],
this->sub_tree == 2
? CRT_treeStr[TREE_STR_OPEN]
: CRT_treeStr[TREE_STR_SHUT]);
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
RichString_appendWide(out, CRT_colors[PROCESS_TREE],
this->sub_tree == 2
? CRT_treeStr[TREE_STR_OPEN]
: CRT_treeStr[TREE_STR_SHUT]);
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
}
RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
}
static const ObjectClass MaskItem_class = {
@ -173,7 +173,6 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
Panel* super = (Panel*) this;
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
FunctionBar_draw(super->currentBar);
int oldSelected = Panel_getSelectedIndex(super);
Panel_prune(super);
@ -281,7 +280,7 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
indent_buf[0] = '\0';
if (depth > 0) {
for (unsigned i = 1; i < depth; i++) {
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
size_t len = strlen(&indent_buf[off]);
off += len;
left -= len;
@ -323,9 +322,9 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
if (obj->next_sibling) {
indent |= (1u << obj->depth);
indent |= (1U << obj->depth);
} else {
indent &= ~(1u << obj->depth);
indent &= ~(1U << obj->depth);
}
for (unsigned i = 0; i < obj->arity; i++) {

View File

@ -77,7 +77,7 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
Panel_setHeader(super, "Available Columns");
for (int i = 1; i < Platform_numberOfFields; i++) {
for (int i = 1; i < LAST_PROCESSFIELD; i++) {
if (i != COMM && Process_fields[i].description) {
char description[256];
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);

View File

@ -35,7 +35,6 @@ static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, c
Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true);
FunctionBar_draw(panel->currentBar);
}
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {

View File

@ -35,24 +35,21 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
this->values[0] = percent;
const char *onAcText, *onBatteryText, *unknownText;
unknownText = "%.1f%%";
if (this->mode == TEXT_METERMODE) {
onAcText = "%.1f%% (Running on A/C)";
onBatteryText = "%.1f%% (Running on battery)";
} else {
onAcText = "%.1f%%(A/C)";
onBatteryText = "%.1f%%(bat)";
const char* text;
switch (isOnAC) {
case AC_PRESENT:
text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)";
break;
case AC_ABSENT:
text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)";
break;
case AC_ERROR:
default:
text = "";
break;
}
if (isOnAC == AC_PRESENT) {
xSnprintf(buffer, len, onAcText, percent);
} else if (isOnAC == AC_ABSENT) {
xSnprintf(buffer, len, onBatteryText, percent);
} else {
xSnprintf(buffer, len, unknownText, percent);
}
xSnprintf(buffer, len, "%.1f%%%s", percent, text);
}
const MeterClass BatteryMeter_class = {

View File

@ -67,7 +67,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
double percent = Platform_setCPUValues(this, cpu);
if (this->pl->settings->showCPUUsage) {
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%5.1f%%", percent);
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
}
if (this->pl->settings->showCPUFrequency) {
@ -104,49 +104,49 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast;
RichString_prune(out);
if (this->param > this->pl->cpuCount) {
RichString_append(out, CRT_colors[METER_TEXT], "absent");
RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent");
return;
}
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
RichString_append(out, CRT_colors[METER_TEXT], ":");
RichString_append(out, CRT_colors[CPU_NORMAL], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], ":");
RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer);
if (this->pl->settings->detailedCPUTime) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sy:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "ni:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:");
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "hi:");
RichString_append(out, CRT_colors[CPU_IRQ], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:");
RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:");
RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (!isnan(this->values[CPU_METER_STEAL])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
RichString_append(out, CRT_colors[METER_TEXT], "st:");
RichString_append(out, CRT_colors[CPU_STEAL], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:");
RichString_appendAscii(out, CRT_colors[CPU_STEAL], buffer);
}
if (!isnan(this->values[CPU_METER_GUEST])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
RichString_append(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
}
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
RichString_append(out, CRT_colors[METER_TEXT], "wa:");
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer);
} else {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:");
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (!isnan(this->values[CPU_METER_IRQ])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "vir:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
}
}
@ -161,8 +161,8 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
} else {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
}
RichString_append(out, CRT_colors[METER_TEXT], "temp:");
RichString_append(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
}
#endif
}

56
CRT.c
View File

@ -41,28 +41,26 @@ in the source distribution for its full text.
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
static const char* const CRT_treeStrAscii[TREE_STR_COUNT] = {
"-", // TREE_STR_HORZ
"|", // TREE_STR_VERT
"`", // TREE_STR_RTEE
"`", // TREE_STR_BEND
",", // TREE_STR_TEND
"+", // TREE_STR_OPEN
"-", // TREE_STR_SHUT
static const char* const CRT_treeStrAscii[LAST_TREE_STR] = {
[TREE_STR_VERT] = "|",
[TREE_STR_RTEE] = "`",
[TREE_STR_BEND] = "`",
[TREE_STR_TEND] = ",",
[TREE_STR_OPEN] = "+",
[TREE_STR_SHUT] = "-",
};
#ifdef HAVE_LIBNCURSESW
static const char* const CRT_treeStrUtf8[TREE_STR_COUNT] = {
"\xe2\x94\x80", // TREE_STR_HORZ ─
"\xe2\x94\x82", // TREE_STR_VERT │
"\xe2\x94\x9c", // TREE_STR_RTEE ├
"\xe2\x94\x94", // TREE_STR_BEND └
"\xe2\x94\x8c", // TREE_STR_TEND ┌
"+", // TREE_STR_OPEN +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
// WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when
// Unicode 13 is common
"\xe2\x94\x80", // TREE_STR_SHUT ─
static const char* const CRT_treeStrUtf8[LAST_TREE_STR] = {
[TREE_STR_VERT] = "\xe2\x94\x82", //
[TREE_STR_RTEE] = "\xe2\x94\x9c", //
[TREE_STR_BEND] = "\xe2\x94\x94", //
[TREE_STR_TEND] = "\xe2\x94\x8c", //
[TREE_STR_OPEN] = "+", // +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
// WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when
// Unicode 13 is common
[TREE_STR_SHUT] = "\xe2\x94\x80", // ─
};
bool CRT_utf8 = false;
@ -92,7 +90,7 @@ static const char* initDegreeSign(void) {
const int* CRT_colors;
int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[COLORSCHEME_DEFAULT] = {
[RESET_COLOR] = ColorPair(White, Black),
[DEFAULT_COLOR] = ColorPair(White, Black),
@ -604,12 +602,7 @@ int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;
const char* CRT_termType;
int CRT_colorScheme = 0;
long CRT_pageSize = -1;
long CRT_pageSizeKB = -1;
ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
ATTR_NORETURN
static void CRT_handleSIGTERM(int sgn) {
@ -676,14 +669,14 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
start_color();
}
CRT_termType = getenv("TERM");
if (String_eq(CRT_termType, "linux")) {
const char* termType = getenv("TERM");
if (termType && String_eq(termType, "linux")) {
CRT_scrollHAmount = 20;
} else {
CRT_scrollHAmount = 5;
}
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) {
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME);
@ -746,11 +739,6 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
mousemask(BUTTON1_RELEASED, NULL);
#endif
CRT_pageSize = sysconf(_SC_PAGESIZE);
if (CRT_pageSize == -1)
CRT_fatalError("Fatal error: Can not get PAGE_SIZE by sysconf(_SC_PAGESIZE)");
CRT_pageSizeKB = CRT_pageSize / 1024;
CRT_degreeSign = initDegreeSign();
}
@ -760,7 +748,7 @@ void CRT_done() {
}
void CRT_fatalError(const char* note) {
char* sysMsg = strerror(errno);
const char* sysMsg = strerror(errno);
CRT_done();
fprintf(stderr, "%s: %s\n", note, sysMsg);
exit(2);

20
CRT.h
View File

@ -16,26 +16,25 @@ in the source distribution for its full text.
typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT,
TREE_STR_RTEE,
TREE_STR_BEND,
TREE_STR_TEND,
TREE_STR_OPEN,
TREE_STR_SHUT,
TREE_STR_COUNT
LAST_TREE_STR
} TreeStr;
typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0,
typedef enum ColorScheme_ {
COLORSCHEME_DEFAULT,
COLORSCHEME_MONOCHROME,
COLORSCHEME_BLACKONWHITE,
COLORSCHEME_LIGHTTERMINAL,
COLORSCHEME_MIDNIGHT,
COLORSCHEME_BLACKNIGHT,
COLORSCHEME_BROKENGRAY,
LAST_COLORSCHEME,
} ColorSchemes;
LAST_COLORSCHEME
} ColorScheme;
typedef enum ColorElements_ {
RESET_COLOR,
@ -144,20 +143,13 @@ extern const char* const* CRT_treeStr;
extern const int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
extern const char* CRT_termType;
extern int CRT_colorScheme;
extern long CRT_pageSize;
extern long CRT_pageSizeKB;
extern ColorScheme CRT_colorScheme;
#ifdef HAVE_SETUID_ENABLED

View File

@ -1,3 +1,34 @@
What's new in version 3.0.4
* Separate tree and list sort orders
* Invert Process_compare so that superclass matches run first
(thanks to Hisham Muhammad)
* Unhardcode Mac OS tick-to-milliseconds conversion
(thanks to Alexander Momchilov)
* Check if clock_gettime needs linking of librt
* Define O_PATH if not already defined
(thanks to Chris Burr)
* Add column on Mac for processes running under translation
(thanks to Dániel Bakai)
* Configure check for additional linker flags for keypad(3)
* PSI Meter: constant width and only print ten-duration as bar
* Sort in paused mode after inverting sort order
* Handle absence of package CPU temperature
* Meter: restore non-wide-character build
* LibSensors: restore temperature for Raspberry Pi
* MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
* BarMeter: rework text padding
* Panel: rework drawing of FunctionBar
* Meter: fix artifacts with very tiny width
* DragonFlyBSD updates
* BUGFIX: Fix dlopen issue for libsensors5 for some platforms
* BUGFIX: Fix broken tree display on inverted sort order
* BUGFIX: Fix pause mode ("Z") in tree view
* BUGFIX: Correct timebase for non-x86 CPUs on Darwin
* BUGFIX: Avoid NULL dereference on zombie processes
* Document dynamic bindings and assumed external configuration
* Update key mapping documentation for sorting
What's new in version 3.0.3
* Process sorting in 'tree' mode

View File

@ -60,26 +60,21 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
case KEY_MOUSE:
case KEY_RECLICK:
case ' ':
assert(mark >= 0);
assert(mark < LAST_COLORSCHEME);
for (int i = 0; ColorSchemeNames[i] != NULL; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
this->settings->colorScheme = mark;
result = HANDLED;
}
if (result == HANDLED) {
this->settings->changed = true;
const Header* header = this->scr->header;
CRT_setColors(mark);
clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header);
FunctionBar_draw(super->currentBar);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
result = HANDLED | REDRAW;
}
return result;
}
@ -100,6 +95,8 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
this->settings = settings;
this->scr = scr;
assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);
Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false));

View File

@ -44,7 +44,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
{
if (selected < size - 1) {
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
if (selectedItem)
selectedItem->moving = this->moving;

View File

@ -17,9 +17,10 @@ static void CommandScreen_scan(InfoScreen* this) {
Panel_prune(panel);
const char* p = Process_getCommand(this->process);
char* line = xMalloc(COLS + 1);
char line[COLS + 1];
int line_offset = 0, last_spc = -1, len;
for (; *p != '\0'; p++, line_offset++) {
assert(line_offset >= 0 && (size_t)line_offset < sizeof(line));
line[line_offset] = *p;
if (*p == ' ') {
last_spc = line_offset;
@ -41,7 +42,6 @@ static void CommandScreen_scan(InfoScreen* this) {
InfoScreen_addLine(this, line);
}
free(line);
Panel_setSelected(panel, idx);
}

View File

@ -11,12 +11,18 @@ in the source distribution for its full text.
#include <errno.h>
#include <fcntl.h> // IWYU pragma: keep
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h> // IWYU pragma: keep
#include "XUtils.h" // IWYU pragma: keep
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
#include <mach/clock.h>
#include <mach/mach.h>
#endif
int Compat_faccessat(int dirfd,
const char* pathname,
@ -117,3 +123,31 @@ ssize_t Compat_readlinkat(int dirfd,
#endif
}
int Compat_clock_monotonic_gettime(struct timespec *tp) {
#if defined(HAVE_CLOCK_GETTIME)
return clock_gettime(CLOCK_MONOTONIC, tp);
#elif defined(HAVE_HOST_GET_CLOCK_SERVICE)
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
tp->tv_sec = mts.tv_sec;
tp->tv_nsec = mts.tv_nsec;
return 0;
#else
#error No Compat_clock_monotonic_gettime() implementation!
#endif
}

View File

@ -56,4 +56,6 @@ ssize_t Compat_readlinkat(int dirfd,
char* buf,
size_t bufsize);
int Compat_clock_monotonic_gettime(struct timespec *tp);
#endif /* HEADER_Compat */

View File

@ -88,7 +88,7 @@ static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return;
}
@ -96,15 +96,15 @@ static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out)
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
RichString_write(out, CRT_colors[color], buffer);
RichString_writeAscii(out, CRT_colors[color], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " read: ");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " write: ");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
}
const MeterClass DiskIOMeter_class = {

View File

@ -97,6 +97,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));

View File

@ -14,15 +14,6 @@
#include "XUtils.h"
const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};
EnvScreen* EnvScreen_new(Process* process) {
EnvScreen* this = xMalloc(sizeof(EnvScreen));
Object_setClass(this, Class(EnvScreen));
@ -33,11 +24,11 @@ void EnvScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
void EnvScreen_draw(InfoScreen* this) {
static void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
}
void EnvScreen_scan(InfoScreen* this) {
static void EnvScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
@ -59,3 +50,12 @@ void EnvScreen_scan(InfoScreen* this) {
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}
const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};

View File

@ -15,8 +15,4 @@ EnvScreen* EnvScreen_new(Process* process);
void EnvScreen_delete(Object* this);
void EnvScreen_draw(InfoScreen* this);
void EnvScreen_scan(InfoScreen* this);
#endif

View File

@ -112,10 +112,11 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr
attrset(attr);
}
mvaddstr(LINES - 1, x, buffer);
attrset(CRT_colors[RESET_COLOR]);
x += strlen(buffer);
}
attrset(CRT_colors[RESET_COLOR]);
if (setCursor) {
CRT_cursorX = x;
curs_set(1);
@ -132,10 +133,10 @@ void FunctionBar_append(const char* buffer, int attr) {
} else {
attrset(attr);
}
mvaddstr(LINES - 1, currentLen, buffer);
mvaddstr(LINES - 1, currentLen + 1, buffer);
attrset(CRT_colors[RESET_COLOR]);
currentLen += strlen(buffer);
currentLen += strlen(buffer) + 1;
}
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -31,7 +31,7 @@ void IncSet_reset(IncSet* this, IncType type) {
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR};
static const int searchEvents[] = {KEY_F(3), 27, ERR};
static inline void IncMode_initSearch(IncMode* search) {
memset(search, 0, sizeof(IncMode));
@ -41,7 +41,7 @@ static inline void IncMode_initSearch(IncMode* search) {
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* const filterKeys[] = {"Enter", "Esc", " "};
static int filterEvents[] = {13, 27, ERR};
static const int filterEvents[] = {13, 27, ERR};
static inline void IncMode_initFilter(IncMode* filter) {
memset(filter, 0, sizeof(IncMode));
@ -54,12 +54,13 @@ static inline void IncMode_done(IncMode* mode) {
}
IncSet* IncSet_new(FunctionBar* bar) {
IncSet* this = xCalloc(1, sizeof(IncSet));
IncSet* this = xMalloc(sizeof(IncSet));
IncMode_initSearch(&(this->modes[INC_SEARCH]));
IncMode_initFilter(&(this->modes[INC_FILTER]));
this->active = NULL;
this->filtering = false;
this->defaultBar = bar;
this->filtering = false;
this->found = false;
return this;
}
@ -99,20 +100,14 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel);
bool found = false;
for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
found = true;
break;
return true;
}
}
FunctionBar_drawExtra(mode->bar,
mode->buffer,
found ? -1 : CRT_colors[FAILED_SEARCH],
true);
return found;
return false;
}
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
@ -202,7 +197,6 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
}
this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar);
doSearch = false;
}
if (doSearch) {
@ -221,13 +215,12 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(const IncSet* this) {
if (this->active) {
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
} else {
FunctionBar_draw(this->defaultBar);
}

View File

@ -19,7 +19,7 @@ static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
static const int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
this->process = process;
@ -44,8 +44,10 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
char* title = xMalloc(COLS + 1);
int len = vsnprintf(title, COLS + 1, fmt, ap);
char title[COLS + 1];
int len = vsnprintf(title, sizeof(title), fmt, ap);
va_end(ap);
if (len > COLS) {
memset(&title[COLS - 3], '.', 3);
}
@ -54,11 +56,9 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
mvhline(0, 0, ' ', COLS);
mvwprintw(stdscr, 0, 0, title);
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true, true);
Panel_draw(this->display, true, true, true);
IncSet_drawBar(this->inc);
free(title);
va_end(ap);
}
void InfoScreen_addLine(InfoScreen* this, const char* line) {
@ -89,7 +89,8 @@ void InfoScreen_run(InfoScreen* this) {
bool looping = true;
while (looping) {
Panel_draw(panel, true, true);
Panel_draw(panel, false, true, true);
IncSet_drawBar(this->inc);
if (this->inc->active) {
(void) move(LINES - 1, CRT_cursorX);

View File

@ -16,7 +16,6 @@ typedef struct InfoScreen_ {
Object super;
const Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
} InfoScreen;

View File

@ -29,7 +29,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
assert (this != NULL);
if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR],
RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" :
#endif
@ -37,7 +37,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
} else {
RichString_prune(out);
}
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value);
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
}
ListItem* ListItem_new(const char* value, int key) {

View File

@ -33,11 +33,11 @@ static void LoadAverageMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
RichString_writeAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
}
static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
@ -53,7 +53,7 @@ static void LoadMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer);
RichString_writeAscii(out, CRT_colors[LOAD], buffer);
}
const MeterClass LoadAverageMeter_class = {

View File

@ -25,7 +25,7 @@ static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filt
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
FunctionBar_setLabel(bar, KEY_F(5), mode ? "Sorted" : "Tree ");
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
}
void MainPanel_pidSearch(MainPanel* this, int ch) {
@ -51,7 +51,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
if (ch != ERR)
if (ch != ERR && ch != KEY_RESIZE)
this->state->hideProcessSelection = false;
if (EVENT_IS_HEADER_CLICK(ch)) {
@ -60,7 +60,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
if (settings->treeView && settings->treeViewAlwaysByPID) {
settings->treeView = false;
settings->direction = 1;
reaction |= Action_setSortKey(settings, field);
} else if (field == Settings_getActiveSortKey(settings)) {
Settings_invertSortOrder(settings);
} else {
reaction |= Action_setSortKey(settings, field);
@ -96,16 +100,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
}
if (reaction & HTOP_REFRESH) {
result |= REDRAW;
result |= REFRESH;
}
if (reaction & HTOP_RECALCULATE) {
result |= RESCAN;
@ -118,7 +118,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
if (!(reaction & HTOP_KEEP_FOLLOWING)) {
this->state->pl->following = -1;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
}
return result;
}
@ -160,12 +160,21 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
return ok;
}
static void MainPanel_drawFunctionBar(Panel* super) {
MainPanel* this = (MainPanel*) super;
IncSet_drawBar(this->inc);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
}
const PanelClass MainPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MainPanel_delete
},
.eventHandler = MainPanel_eventHandler
.eventHandler = MainPanel_eventHandler,
.drawFunctionBar = MainPanel_drawFunctionBar
};
MainPanel* MainPanel_new() {

View File

@ -133,6 +133,7 @@ linux_platform_headers = \
linux/LinuxProcessList.h \
linux/Platform.h \
linux/PressureStallMeter.h \
linux/ProcessField.h \
linux/SELinuxMeter.h \
linux/SystemdMeter.h \
linux/ZramMeter.h \
@ -164,9 +165,10 @@ endif
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/Platform.h \
freebsd/ProcessField.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
@ -184,14 +186,16 @@ endif
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/Platform.h \
dragonflybsd/ProcessField.h
if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c
myhtopplatsources = \
dragonflybsd/Platform.c \
dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
@ -200,9 +204,10 @@ endif
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h
openbsd/OpenBSDProcess.h \
openbsd/Platform.h \
openbsd/ProcessField.h
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
@ -215,9 +220,10 @@ endif
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/Platform.h \
darwin/ProcessField.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
@ -237,6 +243,7 @@ endif
solaris_platform_headers = \
solaris/Platform.h \
solaris/ProcessField.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
zfs/ZfsArcMeter.h \
@ -256,6 +263,7 @@ endif
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/ProcessField.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h

View File

@ -34,18 +34,18 @@ static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void MemoryMeter_display(const Object* cast, RichString* out) {
char buffer[50];
const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50);
RichString_append(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[MEMORY_USED], buffer);
Meter_humanUnit(buffer, this->values[1], 50);
RichString_append(out, CRT_colors[METER_TEXT], " buffers:");
RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
Meter_humanUnit(buffer, this->values[2], 50);
RichString_append(out, CRT_colors[METER_TEXT], " cache:");
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer);
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:");
RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer);
}
const MeterClass MemoryMeter_class = {

59
Meter.c
View File

@ -100,7 +100,7 @@ static inline void Meter_displayBuffer(const Meter* this, const char* buffer, Ri
if (Object_displayFn(this)) {
Object_display(this, out);
} else {
RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer);
}
}
@ -156,16 +156,20 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, sizeof(buffer));
(void) w;
attrset(CRT_colors[METER_TEXT]);
mvaddstr(y, x, this->caption);
mvaddnstr(y, x, this->caption, w - 1);
attrset(CRT_colors[RESET_COLOR]);
int captionLen = strlen(this->caption);
x += captionLen;
attrset(CRT_colors[RESET_COLOR]);
w -= captionLen;
if (w <= 0)
return;
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
RichString_printVal(out, y, x);
RichString_printoffnVal(out, y, x, 0, w - 1);
RichString_end(out);
}
@ -185,7 +189,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
w -= captionLen;
attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '[');
mvaddch(y, x + w, ']');
mvaddch(y, x + MAXIMUM(w, 0), ']');
attrset(CRT_colors[RESET_COLOR]);
w--;
@ -195,14 +199,29 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
return;
// The text in the bar is right aligned;
// calculate needed padding and generate leading spaces
const int textLen = mbstowcs(NULL, buffer, 0);
const int padding = CLAMP(w - textLen, 0, w);
// Pad with maximal spaces and then calculate needed starting position offset
RichString_begin(bar);
RichString_appendChr(&bar, ' ', padding);
RichString_append(&bar, 0, buffer);
assert(RichString_sizeVal(bar) >= w);
RichString_appendChr(&bar, ' ', w);
RichString_appendWide(&bar, 0, buffer);
int startPos = RichString_sizeVal(bar) - w;
if (startPos > w) {
// Text is too large for bar
// Truncate meter text at a space character
for (int pos = 2 * w; pos > w; pos--) {
if (RichString_getCharVal(bar, pos) == ' ') {
while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ')
pos--;
startPos = pos - w;
break;
}
}
// If still to large, print the start not the end
startPos = MINIMUM(startPos, w);
}
assert(startPos >= 0);
assert(startPos <= w);
assert(startPos + w <= RichString_sizeVal(bar));
int blockSizes[10];
@ -220,11 +239,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// (Control against invalid values)
nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++)
if (RichString_getCharVal(bar, j) == ' ') {
if (RichString_getCharVal(bar, startPos + j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
RichString_setChar(&bar, j, BarMeterMode_characters[i]);
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
} else {
RichString_setChar(&bar, j, '|');
RichString_setChar(&bar, startPos + j, '|');
}
}
offset = nextOffset;
@ -233,14 +252,14 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// ...then print the buffer.
offset = 0;
for (uint8_t i = 0; i < this->curItems; i++) {
RichString_setAttrn(&bar, CRT_colors[Meter_attributes(this)[i]], offset, offset + blockSizes[i] - 1);
RichString_printoffnVal(bar, y, x + offset, offset, blockSizes[i]);
RichString_setAttrn(&bar, CRT_colors[Meter_attributes(this)[i]], startPos + offset, startPos + offset + blockSizes[i] - 1);
RichString_printoffnVal(bar, y, x + offset, startPos + offset, MINIMUM(blockSizes[i], w - offset));
offset += blockSizes[i];
offset = CLAMP(offset, 0, w);
}
if (offset < w) {
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], offset, w - 1);
RichString_printoffnVal(bar, y, x + offset, offset, w - offset);
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, startPos + w - 1);
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
}
RichString_end(bar);

View File

@ -20,9 +20,9 @@ in the source distribution for its full text.
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
static const char* const MetersKeys[] = {"Space", "Enter", " ", "Del", "F10"};
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
static const char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"};
static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
@ -30,7 +30,7 @@ static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// considered "Ambiguous characters".
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL};
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"};
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static const int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static FunctionBar* Meters_movingBar = NULL;
void MetersPanel_cleanup() {
@ -55,13 +55,12 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
selected->moving = moving;
}
if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(super);
} else {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW);
super->currentBar = Meters_movingBar;
}
FunctionBar_draw(this->super.currentBar);
}
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {

View File

@ -88,24 +88,24 @@ static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, s
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return;
}
char buffer[64];
RichString_write(out, CRT_colors[METER_TEXT], "rx: ");
RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
RichString_append(out, CRT_colors[METER_TEXT], " tx: ");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: ");
Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
RichString_append(out, CRT_colors[METER_TEXT], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], buffer);
}
const MeterClass NetworkIOMeter_class = {

View File

@ -29,14 +29,14 @@ static void CheckItem_display(const Object* cast, RichString* out) {
const CheckItem* this = (const CheckItem*)cast;
assert (this != NULL);
RichString_write(out, CRT_colors[CHECK_BOX], "[");
RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this)) {
RichString_append(out, CRT_colors[CHECK_MARK], "x");
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else {
RichString_append(out, CRT_colors[CHECK_MARK], " ");
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
}
RichString_append(out, CRT_colors[CHECK_BOX], "] ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "] ");
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
}
static void NumberItem_display(const Object* cast, RichString* out) {
@ -44,7 +44,7 @@ static void NumberItem_display(const Object* cast, RichString* out) {
assert (this != NULL);
char buffer[12];
RichString_write(out, CRT_colors[CHECK_BOX], "[");
RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
int written;
if (this->scale < 0) {
written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
@ -53,12 +53,12 @@ static void NumberItem_display(const Object* cast, RichString* out) {
} else {
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
}
RichString_append(out, CRT_colors[CHECK_MARK], buffer);
RichString_append(out, CRT_colors[CHECK_BOX], "]");
RichString_appendAscii(out, CRT_colors[CHECK_MARK], buffer);
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
for (int i = written; i < 5; i++) {
RichString_append(out, CRT_colors[CHECK_BOX], " ");
RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
}
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
}
const OptionItemClass OptionItem_class = {

28
Panel.c
View File

@ -55,11 +55,13 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
this->scrollH = 0;
this->selected = 0;
this->oldSelected = 0;
this->selectedLen = 0;
this->needsRedraw = true;
this->wasFocus = false;
RichString_beginAllocated(this->header);
this->defaultBar = fuBar;
this->currentBar = fuBar;
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS];
this->selectionColorId = PANEL_SELECTION_FOCUS;
}
void Panel_done(Panel* this) {
@ -70,8 +72,8 @@ void Panel_done(Panel* this) {
RichString_end(this->header);
}
void Panel_setSelectionColor(Panel* this, int color) {
this->selectionColor = color;
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
this->selectionColorId = colorId;
}
RichString* Panel_getHeader(Panel* this) {
@ -82,7 +84,7 @@ RichString* Panel_getHeader(Panel* this) {
}
inline void Panel_setHeader(Panel* this, const char* header) {
RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
this->needsRedraw = true;
}
@ -217,7 +219,7 @@ void Panel_splice(Panel* this, Vector* from) {
this->needsRedraw = true;
}
void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected) {
assert (this != NULL);
int size = Vector_size(this->items);
@ -234,6 +236,7 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
attrset(attr);
mvhline(y, x, ' ', this->w);
if (scrollH < headerLen) {
RichString_setAttr(&this->header, attr);
RichString_printoffnVal(this->header, y, x, scrollH,
MINIMUM(headerLen - scrollH, this->w));
}
@ -262,10 +265,10 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
int upTo = MINIMUM(first + h, size);
int selectionColor = focus
? this->selectionColor
? CRT_colors[this->selectionColorId]
: CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) {
if (this->needsRedraw || force_redraw) {
int line = 0;
for (int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i);
@ -293,7 +296,6 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
mvhline(y + line, x, ' ', this->w);
line++;
}
this->needsRedraw = false;
} else {
Object* oldObj = Vector_get(this->items, this->oldSelected);
@ -319,7 +321,17 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
RichString_end(new);
RichString_end(old);
}
if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
if (Panel_drawFunctionBarFn(this))
Panel_drawFunctionBar(this);
else
FunctionBar_draw(this->currentBar);
}
this->oldSelected = this->selected;
this->wasFocus = focus;
this->needsRedraw = false;
move(0, 0);
}

27
Panel.h
View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include <stdbool.h>
#include "CRT.h"
#include "FunctionBar.h"
#include "Object.h"
#include "RichString.h"
@ -22,9 +23,10 @@ typedef enum HandlerResult_ {
HANDLED = 0x01,
IGNORED = 0x02,
BREAK_LOOP = 0x04,
REDRAW = 0x08,
RESCAN = 0x10,
SYNTH_KEY = 0x20,
REFRESH = 0x08,
REDRAW = 0x10,
RESCAN = 0x20,
SYNTH_KEY = 0x40,
} HandlerResult;
#define EVENT_SET_SELECTED (-1)
@ -33,16 +35,20 @@ typedef enum HandlerResult_ {
#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
typedef void (*Panel_DrawFunctionBar)(Panel*);
typedef struct PanelClass_ {
const ObjectClass super;
const Panel_EventHandler eventHandler;
const Panel_DrawFunctionBar drawFunctionBar;
} PanelClass;
#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_))
#define Panel_drawFunctionBarFn(this_) As_Panel(this_)->drawFunctionBar
#define Panel_drawFunctionBar(this_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_)))
struct Panel_ {
Object super;
@ -55,10 +61,11 @@ struct Panel_ {
int scrollV;
short scrollH;
bool needsRedraw;
bool wasFocus;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
int selectionColor;
ColorElements selectionColorId;
};
#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
@ -75,7 +82,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
void Panel_done(Panel* this);
void Panel_setSelectionColor(Panel* this, int color);
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
RichString* Panel_getHeader(Panel* this);
@ -109,7 +116,7 @@ int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool focus, bool highlightSelected);
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected);
void Panel_splice(Panel* this, Vector* from);

133
Process.c
View File

@ -38,23 +38,15 @@ in the source distribution for its full text.
static uid_t Process_getuid = (uid_t)-1;
char Process_pidFormat[20] = "%7d ";
static char Process_titleBuffer[20][20];
int Process_pidDigits = 7;
void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid();
if (maxPid == -1)
return;
int digits = ceil(log10(maxPid));
assert(digits < 20);
for (int i = 0; Process_pidColumns[i].label; i++) {
assert(i < 20);
xSnprintf(Process_titleBuffer[i], 20, "%*s ", digits, Process_pidColumns[i].label);
Process_fields[Process_pidColumns[i].id].title = Process_titleBuffer[i];
}
xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits);
Process_pidDigits = ceil(log10(maxPid));
assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
}
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) {
@ -74,53 +66,53 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
if (number < 1000) {
//Plain number, no markings
len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
RichString_appendn(str, processColor, buffer, len);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 100000) {
//2 digit MB, 3 digit KB
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len);
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
RichString_appendn(str, processColor, buffer, len);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 1000 * ONE_K) {
//3 digit MB
number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len);
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (number < 10000 * ONE_K) {
//1 digit GB, 3 digit MB
number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, processGigabytesColor, buffer, len);
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len);
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (number < 100000 * ONE_K) {
//2 digit GB, 1 digit MB
number /= 100 * ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10);
RichString_appendn(str, processGigabytesColor, buffer, len);
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 10;
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
RichString_appendn(str, processMegabytesColor, buffer, len);
RichString_append(str, processGigabytesColor, "G ");
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
RichString_appendAscii(str, processGigabytesColor, "G ");
} else if (number < 1000 * ONE_M) {
//3 digit GB
number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len);
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
} else if (number < 10000ULL * ONE_M) {
//1 digit TB, 3 digit GB
number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, largeNumberColor, buffer, len);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len);
RichString_appendnAscii(str, processGigabytesColor, buffer, len);
} else {
//2 digit TB and above
len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
}
}
@ -139,26 +131,25 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
}
if (number == ULLONG_MAX) {
int len = xSnprintf(buffer, sizeof(buffer), " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
} else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12);
RichString_appendnAscii(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4);
RichString_appendnAscii(str, largeNumberColor, buffer, 8);
RichString_appendnAscii(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4);
RichString_appendnAscii(str, largeNumberColor, buffer, 5);
RichString_appendnAscii(str, processMegabytesColor, buffer+5, 3);
RichString_appendnAscii(str, processColor, buffer+8, 4);
} else {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3);
RichString_appendn(str, processShadowColor, buffer+8, 4);
RichString_appendnAscii(str, largeNumberColor, buffer, 2);
RichString_appendnAscii(str, processMegabytesColor, buffer+2, 3);
RichString_appendnAscii(str, processColor, buffer+5, 3);
RichString_appendnAscii(str, processShadowColor, buffer+8, 4);
}
}
@ -172,16 +163,16 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
char buffer[10];
if (hours >= 100) {
xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
} else {
if (hours) {
xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
} else {
xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths);
}
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
RichString_appendAscii(str, CRT_colors[DEFAULT_COLOR], buffer);
}
}
@ -216,7 +207,7 @@ static inline void Process_writeCommand(const Process* this, int attr, int basea
finish += start - 1;
}
RichString_append(str, attr, comm);
RichString_appendWide(str, attr, comm);
if (this->settings->highlightBaseName) {
RichString_setAttrn(str, baseattr, start, finish);
@ -234,23 +225,22 @@ void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, in
}
if (isnan(rate)) {
int len = xSnprintf(buffer, n, " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
} else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(str, processColor, buffer, len);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_M) {
int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K);
RichString_appendn(str, processColor, buffer, len);
RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
RichString_appendn(str, processMegabytesColor, buffer, len);
RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (rate < ONE_T) {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
} else {
int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
RichString_appendn(str, largeNumberColor, buffer, len);
RichString_appendnAscii(str, largeNumberColor, buffer, len);
}
}
@ -320,17 +310,18 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
buf += written;
n -= written;
}
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str);
return;
}
}
case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return;
case MINFLT: Process_colorNumber(str, this->minflt, coloring); return;
case M_RESIDENT: Process_humanNumber(str, this->m_resident * CRT_pageSizeKB, coloring); return;
case M_VIRT: Process_humanNumber(str, this->m_virt * CRT_pageSizeKB, coloring); return;
case M_RESIDENT: Process_humanNumber(str, this->m_resident, coloring); return;
case M_VIRT: Process_humanNumber(str, this->m_virt, coloring); return;
case NICE: {
xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
@ -339,9 +330,9 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
break;
}
case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break;
case PGRP: xSnprintf(buffer, n, Process_pidFormat, this->pgrp); break;
case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break;
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
case PRIORITY: {
if(this->priority <= -100)
xSnprintf(buffer, n, " RT ");
@ -350,7 +341,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
break;
}
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
case SESSION: xSnprintf(buffer, n, Process_pidFormat, this->session); break;
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);
@ -366,8 +357,8 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
}
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return;
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid); break;
case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break;
case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: {
if (Process_getuid != this->st_uid)
@ -386,7 +377,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
default:
xSnprintf(buffer, n, "- ");
}
RichString_append(str, attr, buffer);
RichString_appendWide(str, attr, buffer);
}
void Process_display(const Object* cast, RichString* out) {
@ -449,13 +440,13 @@ void Process_init(Process* this, const struct Settings_* settings) {
}
void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true;
this->tag = !this->tag;
}
bool Process_isNew(const Process* this) {
assert(this->processList);
if (this->processList->scanTs >= this->seenTs) {
return this->processList->scanTs - this->seenTs <= this->processList->settings->highlightDelaySecs;
return this->processList->scanTs - this->seenTs <= 1000 * this->processList->settings->highlightDelaySecs;
}
return false;
}
@ -489,15 +480,15 @@ bool Process_sendSignal(Process* this, Arg sgn) {
long Process_pidCompare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2;
return (p1->pid - p2->pid);
return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
long Process_compare(const void* v1, const void* v2) {
const Process *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
int r;
if (settings->direction == 1) {
if (Settings_getActiveDirection(settings) == 1) {
p1 = (const Process*)v1;
p2 = (const Process*)v2;
} else {
@ -505,7 +496,21 @@ long Process_compare(const void* v1, const void* v2) {
p1 = (const Process*)v2;
}
switch (settings->sortKey) {
ProcessField key = Settings_getActiveSortKey(settings);
long result = Process_compareByKey(p1, p2, key);
// Implement tie-breaker (needed to make tree mode more stable)
if (!result)
result = SPACESHIP_NUMBER(p1->pid, p2->pid);
return result;
}
long Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
int r;
switch (key) {
case PERCENT_CPU:
case PERCENT_NORM_CPU:
return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu);

View File

@ -13,17 +13,14 @@ in the source distribution for its full text.
#include <sys/types.h>
#include "Object.h"
#include "ProcessField.h"
#include "RichString.h"
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
#define PROCESS_FLAG_IO 0x0001
#define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessFields {
typedef enum ProcessField_ {
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2,
@ -49,12 +46,12 @@ typedef enum ProcessFields {
NLWP = 51,
TGID = 52,
PERCENT_NORM_CPU = 53,
} ProcessField;
typedef struct ProcessPidColumn_ {
int id;
const char* label;
} ProcessPidColumn;
/* Platform specific fields, defined in ${platform}/ProcessField.h */
PLATFORM_PROCESS_FIELDS
LAST_PROCESSFIELD
} ProcessField;
struct Settings_;
@ -120,6 +117,7 @@ typedef struct ProcessFieldData_ {
const char* title;
const char* description;
uint32_t flags;
bool pidColumn;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -127,23 +125,26 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
bool Process_isThread(const Process* this);
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
#define PROCESS_MAX_PID_DIGITS 19
extern int Process_pidDigits;
typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef long (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
const Process_CompareByKey compareByKey;
const Process_GetCommandStr getCommandStr;
} ProcessClass;
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
static inline pid_t Process_getParentPid(const Process* this) {
return this->tgid == this->pid ? this->ppid : this->tgid;
@ -200,4 +201,6 @@ bool Process_sendSignal(Process* this, Arg sgn);
long Process_pidCompare(const void* v1, const void* v2);
long Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
#endif

View File

@ -10,8 +10,8 @@ in the source distribution for its full text.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "Compat.h"
#include "CRT.h"
#include "Hashtable.h"
#include "Macros.h"
@ -79,22 +79,41 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
this->panel = panel;
}
static const char* alignedProcessFieldTitle(ProcessField field) {
const char* title = Process_fields[field].title;
if (!title)
return "- ";
if (!Process_fields[field].pidColumn)
return title;
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
return titleBuffer;
}
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
const ProcessField* fields = this->settings->fields;
const Settings* settings = this->settings;
const ProcessField* fields = settings->fields;
ProcessField key = Settings_getActiveSortKey(settings);
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
if (!field) {
field = "- ";
int color;
if (settings->treeView && settings->treeViewAlwaysByPID) {
color = CRT_colors[PANEL_HEADER_FOCUS];
} else if (key == fields[i]) {
color = CRT_colors[PANEL_SELECTION_FOCUS];
} else {
color = CRT_colors[PANEL_HEADER_FOCUS];
}
int color = (this->settings->sortKey == fields[i]) ?
CRT_colors[PANEL_SELECTION_FOCUS] : CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, color, field);
if (COMM == fields[i] && this->settings->showMergedCommand) {
RichString_append(header, color, "(merged)");
RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i]));
if (COMM == fields[i] && settings->showMergedCommand) {
RichString_appendAscii(header, color, "(merged)");
}
}
}
@ -149,7 +168,7 @@ int ProcessList_size(ProcessList* this) {
//
// The algorithm is based on `depth-first search`,
// even though `breadth-first search` approach may be more efficient on first glance,
// after comparision it may be not, as it's not safe to go deeper without first updating the tree structure.
// after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
// If it would be safe that approach would likely bring an advantage in performance.
//
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
@ -347,7 +366,7 @@ static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2)
static void ProcessList_buildTree(ProcessList* this) {
int node_counter = 1;
int node_index = 0;
int direction = this->settings->direction;
int direction = Settings_getActiveDirection(this->settings);
// Sort by PID
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
@ -446,12 +465,7 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
const ProcessField* fields = this->settings->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title;
if (!title) {
title = "- ";
}
int len = strlen(title);
int len = strlen(alignedProcessFieldTitle(field));
if (at >= x && at <= x + len) {
return field;
}
@ -479,23 +493,20 @@ void ProcessList_rebuildPanel(ProcessList* this) {
int size = ProcessList_size(this);
int idx = 0;
for (int i = 0; i < size; i++) {
bool hidden = false;
Process* p = ProcessList_get(this, i);
if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
hidden = true;
continue;
if (!hidden) {
Panel_set(this->panel, idx, (Object*)p);
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
Panel_setSelected(this->panel, idx);
this->panel->scrollV = currScrollV;
}
idx++;
Panel_set(this->panel, idx, (Object*)p);
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
Panel_setSelected(this->panel, idx);
this->panel->scrollV = currScrollV;
}
idx++;
}
}
@ -541,8 +552,10 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
if (!firstScanDone) {
this->scanTs = 0;
firstScanDone = true;
} else if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
this->scanTs = now.tv_sec;
} else if (Compat_clock_monotonic_gettime(&now) == 0) {
// save time in millisecond, so with a delay in deciseconds
// there are no irregularities
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
}
ProcessList_goThroughEntries(this, false);
@ -558,7 +571,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
// process no longer exists
if (this->settings->highlightChanges && p->wasShown) {
// mark tombed
p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs;
} else {
// immediately remove
ProcessList_remove(this, p);

View File

@ -7,6 +7,7 @@ in the source distribution for its full text.
#include "RichString.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@ -47,7 +48,7 @@ static void RichString_setLen(RichString* this, int len) {
#ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
static inline void RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
wchar_t data[len + 1];
len = mbstowcs(data, data_c, len);
if (len < 0)
@ -60,6 +61,14 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
}
}
static inline void RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
int newLen = from + len;
RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } };
}
}
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
cchar_t* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
@ -82,7 +91,7 @@ int RichString_findChar(RichString* this, char c, int start) {
#else /* HAVE_LIBNCURSESW */
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
static inline void RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
int newLen = from + len;
RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) {
@ -91,6 +100,10 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
this->chptr[newLen] = 0;
}
static inline void RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
RichString_writeFromWide(this, attrs, data_c, from, len);
}
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
chtype* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
@ -132,14 +145,26 @@ void RichString_setAttr(RichString* this, int attrs) {
RichString_setAttrn(this, attrs, 0, this->chlen - 1);
}
void RichString_append(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, this->chlen, strlen(data));
void RichString_appendWide(RichString* this, int attrs, const char* data) {
RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data));
}
void RichString_appendn(RichString* this, int attrs, const char* data, int len) {
RichString_writeFrom(this, attrs, data, this->chlen, len);
void RichString_appendnWide(RichString* this, int attrs, const char* data, int len) {
RichString_writeFromWide(this, attrs, data, this->chlen, len);
}
void RichString_write(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, 0, strlen(data));
void RichString_writeWide(RichString* this, int attrs, const char* data) {
RichString_writeFromWide(this, attrs, data, 0, strlen(data));
}
void RichString_appendAscii(RichString* this, int attrs, const char* data) {
RichString_writeFromAscii(this, attrs, data, this->chlen, strlen(data));
}
void RichString_appendnAscii(RichString* this, int attrs, const char* data, int len) {
RichString_writeFromAscii(this, attrs, data, this->chlen, len);
}
void RichString_writeAscii(RichString* this, int attrs, const char* data) {
RichString_writeFromAscii(this, attrs, data, 0, strlen(data));
}

View File

@ -52,10 +52,16 @@ void RichString_setAttr(RichString* this, int attrs);
void RichString_appendChr(RichString* this, char c, int count);
void RichString_append(RichString* this, int attrs, const char* data);
void RichString_appendWide(RichString* this, int attrs, const char* data);
void RichString_appendn(RichString* this, int attrs, const char* data, int len);
void RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
void RichString_write(RichString* this, int attrs, const char* data);
void RichString_writeWide(RichString* this, int attrs, const char* data);
void RichString_appendAscii(RichString* this, int attrs, const char* data);
void RichString_appendnAscii(RichString* this, int attrs, const char* data, int len);
void RichString_writeAscii(RichString* this, int attrs, const char* data);
#endif

View File

@ -106,7 +106,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl, this->state->pauseProcessUpdate);
if (*sortTimeout == 0 || this->settings->treeView) {
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
@ -119,29 +119,20 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
*rescan = false;
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection));
Panel_draw(panel, force_redraw, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection));
mvvline(panel->y, panel->x + panel->w, ' ', panel->h + 1);
}
}
static Panel* setCurrentPanel(const ScreenManager* this, Panel* panel) {
FunctionBar_draw(panel->currentBar);
if (panel == this->state->panel && this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
return panel;
}
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false;
int focus = 0;
Panel* panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
Panel* panelFocus = (Panel*) Vector_get(this->panels, focus);
double oldTime = 0.0;
@ -150,6 +141,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool timedOut = true;
bool redraw = true;
bool force_redraw = false;
bool rescan = false;
int sortTimeout = 0;
int resetSortTimeout = 5;
@ -159,8 +151,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
}
if (redraw) {
ScreenManager_drawPanels(this, focus);
if (redraw || force_redraw) {
ScreenManager_drawPanels(this, focus, force_redraw);
force_redraw = false;
}
int prevCh = ch;
@ -187,7 +180,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(this, panel);
panelFocus = panel;
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@ -234,9 +227,12 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (result & SYNTH_KEY) {
ch = result >> 16;
}
if (result & REDRAW) {
if (result & REFRESH) {
sortTimeout = 0;
}
if (result & REDRAW) {
force_redraw = true;
}
if (result & RESCAN) {
rescan = true;
sortTimeout = 0;
@ -269,7 +265,7 @@ tryLeft:
focus--;
}
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus > 0) {
goto tryLeft;
}
@ -290,7 +286,7 @@ tryRight:
focus++;
}
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
goto tryRight;
}

View File

@ -96,10 +96,10 @@ static void readFields(ProcessField* fields, uint32_t* flags, const char* line)
free(trim);
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && id < Platform_numberOfFields && Process_fields[id].name) {
if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
fields[j] = id;
*flags |= Process_fields[id].flags;
j++;
@ -137,10 +137,17 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "tree_sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->treeSortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_sort_direction")) {
this->treeDirection = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "tree_view_always_by_pid")) {
this->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) {
@ -273,6 +280,8 @@ bool Settings_write(Settings* this) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1);
fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
@ -287,6 +296,7 @@ bool Settings_write(Settings* this) {
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "tree_view_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
@ -316,7 +326,9 @@ Settings* Settings_new(int initialCpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->treeSortKey = PID;
this->direction = 1;
this->treeDirection = 1;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
@ -343,11 +355,11 @@ Settings* Settings_new(int initialCpuCount) {
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
this->fields = xCalloc(Platform_numberOfFields + 1, sizeof(ProcessField));
this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
const ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
@ -427,9 +439,17 @@ Settings* Settings_new(int initialCpuCount) {
}
void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1) {
this->direction = -1;
} else {
int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
*attr = (*attr == 1) ? -1 : 1;
}
void Settings_setSortKey(Settings* this, ProcessField sortKey) {
if (this->treeViewAlwaysByPID || !this->treeView) {
this->sortKey = sortKey;
this->direction = 1;
this->treeView = false;
} else {
this->treeSortKey = sortKey;
this->treeDirection = 1;
}
}

View File

@ -33,7 +33,9 @@ typedef struct Settings_ {
int delay;
int direction;
int treeDirection;
ProcessField sortKey;
ProcessField treeSortKey;
bool countCPUsFromOne;
bool detailedCPUTime;
@ -44,6 +46,7 @@ typedef struct Settings_ {
bool degreeFahrenheit;
#endif
bool treeView;
bool treeViewAlwaysByPID;
bool showProgramPath;
bool shadowOtherUsers;
bool showThreadNames;
@ -70,6 +73,16 @@ typedef struct Settings_ {
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
static inline ProcessField Settings_getActiveSortKey(const Settings* this) {
return (this->treeView)
? (this->treeViewAlwaysByPID ? PID : this->treeSortKey)
: this->sortKey;
}
static inline int Settings_getActiveDirection(const Settings* this) {
return this->treeView ? this->treeDirection : this->direction;
}
void Settings_delete(Settings* this);
bool Settings_write(Settings* this);
@ -78,4 +91,6 @@ Settings* Settings_new(int initialCpuCount);
void Settings_invertSortOrder(Settings* this);
void Settings_setSortKey(Settings* this, ProcessField sortKey);
#endif

View File

@ -32,12 +32,12 @@ static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void SwapMeter_display(const Object* cast, RichString* out) {
char buffer[50];
const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50);
RichString_append(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer);
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
}
const MeterClass SwapMeter_class = {

View File

@ -46,7 +46,7 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
int processes = (int) this->values[2];
xSnprintf(buffer, sizeof(buffer), "%d", processes);
RichString_write(out, CRT_colors[METER_VALUE], buffer);
RichString_writeAscii(out, CRT_colors[METER_VALUE], buffer);
int threadValueColor = CRT_colors[METER_VALUE];
int threadCaptionColor = CRT_colors[METER_TEXT];
if (settings->highlightThreads) {
@ -54,21 +54,21 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
threadCaptionColor = CRT_colors[PROCESS_THREAD];
}
if (!settings->hideUserlandThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", ");
RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " thr");
RichString_appendAscii(out, threadValueColor, buffer);
RichString_appendAscii(out, threadCaptionColor, " thr");
}
if (!settings->hideKernelThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", ");
RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " kthr");
RichString_appendAscii(out, threadValueColor, buffer);
RichString_appendAscii(out, threadCaptionColor, " kthr");
}
RichString_append(out, CRT_colors[METER_TEXT], "; ");
RichString_appendAscii(out, CRT_colors[METER_TEXT], "; ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]);
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " running");
RichString_appendAscii(out, CRT_colors[TASKS_RUNNING], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " running");
}
const MeterClass TasksMeter_class = {

View File

@ -33,17 +33,7 @@ static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoSc
static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
const InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
},
.draw = TraceScreen_draw,
.onErr = TraceScreen_updateTrace,
.onKey = TraceScreen_onKey,
};
static const int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
TraceScreen* TraceScreen_new(Process* process) {
// This initializes all TraceScreen variables to "false" so only default = true ones need to be set below
@ -70,12 +60,8 @@ void TraceScreen_delete(Object* cast) {
free(InfoScreen_done((InfoScreen*)this));
}
void TraceScreen_draw(InfoScreen* this) {
attrset(CRT_colors[PANEL_HEADER_FOCUS]);
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
static void TraceScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
}
bool TraceScreen_forkTracer(TraceScreen* this) {
@ -131,7 +117,7 @@ err:
return false;
}
void TraceScreen_updateTrace(InfoScreen* super) {
static void TraceScreen_updateTrace(InfoScreen* super) {
TraceScreen* this = (TraceScreen*) super;
char buffer[1025];
@ -176,7 +162,7 @@ void TraceScreen_updateTrace(InfoScreen* super) {
}
}
bool TraceScreen_onKey(InfoScreen* super, int ch) {
static bool TraceScreen_onKey(InfoScreen* super, int ch) {
TraceScreen* this = (TraceScreen*) super;
switch(ch) {
case 'f':
@ -195,3 +181,13 @@ bool TraceScreen_onKey(InfoScreen* super, int ch) {
this->follow = false;
return false;
}
const InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
},
.draw = TraceScreen_draw,
.onErr = TraceScreen_updateTrace,
.onKey = TraceScreen_onKey,
};

View File

@ -32,12 +32,6 @@ TraceScreen* TraceScreen_new(Process* process);
void TraceScreen_delete(Object* cast);
void TraceScreen_draw(InfoScreen* this);
bool TraceScreen_forkTracer(TraceScreen* this);
void TraceScreen_updateTrace(InfoScreen* super);
bool TraceScreen_onKey(InfoScreen* super, int ch);
#endif

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
AC_INIT([htop],[3.0.3],[htop@groups.io])
AC_INIT([htop],[3.0.4],[htop@groups.io])
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.])
@ -93,13 +93,16 @@ AC_TYPE_UINT64_T
AC_FUNC_CLOSEDIR_VOID
AC_FUNC_STAT
AC_CHECK_FUNCS([\
clock_gettime\
faccessat\
fstatat\
host_get_clock_service\
openat\
readlinkat\
])
AC_SEARCH_LIBS([dlopen], [dl dld])
AC_SEARCH_LIBS([clock_gettime], [rt])
save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99"
@ -165,7 +168,7 @@ m4_define([HTOP_CHECK_SCRIPT],
LIBS="$htop_config_script_libs $LIBS "
htop_script_success=yes
], [
CFLAGS="$htop_save_CFLAGS"
CFLAGS="$htop_save_CFLAGS"
])
LDFLAGS="$htop_save_LDFLAGS"
fi
@ -188,15 +191,15 @@ AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS
@ -209,14 +212,18 @@ if test "x$enable_unicode" = xyes; then
HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW],
missing_libraries="$missing_libraries libncursesw"
AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.])
missing_libraries="$missing_libraries libncursesw"
AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.])
)))))))
AC_CHECK_HEADERS([ncursesw/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
[AC_CHECK_HEADERS([ncurses/curses.h],[:],
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
# check if additional linker flags are needed for keypad(3)
# (at this point we already link against a working ncurses library with wide character support)
AC_SEARCH_LIBS([keypad], [tinfow tinfo])
else
HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config",
HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config",
@ -229,6 +236,19 @@ else
[AC_CHECK_HEADERS([ncurses/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
# check if additional linker flags are needed for keypad(3)
# (at this point we already link against a working ncurses library)
AC_SEARCH_LIBS([keypad], [tinfo])
fi
if test "$my_htop_platform" = "darwin"; then
AC_CHECK_HEADERS([mach/mach_time.h])
AC_CHECK_FUNCS([mach_timebase_info])
fi
if test "$my_htop_platform" = "dragonflybsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi
if test "$my_htop_platform" = "freebsd"; then
@ -246,8 +266,7 @@ if test "$my_htop_platform" = "solaris"; then
fi
AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no")
if test "x$enable_hwloc" = xyes
then
if test "x$enable_hwloc" = xyes; then
AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
fi
@ -269,9 +288,9 @@ if test "x$enable_linux_affinity" = xcheck; then
if (errno == ENOSYS) return 1;
]])],
[enable_linux_affinity=yes
AC_MSG_RESULT([yes])],
AC_MSG_RESULT([yes])],
[enable_linux_affinity=no
AC_MSG_RESULT([no])],
AC_MSG_RESULT([no])],
[AC_MSG_RESULT([yes (assumed while cross compiling)])])
fi
fi
@ -279,20 +298,17 @@ if test "x$enable_linux_affinity" = xyes; then
AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.])
fi
if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes
then
if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes; then
AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.])
fi
AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no")
if test "x$enable_setuid" = xyes
then
if test "x$enable_setuid" = xyes; then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes
then
if test "x$enable_delayacct" = xyes; then
m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
@ -321,6 +337,7 @@ AM_CFLAGS="\
-Wcast-qual\
-Wextra\
-Wfloat-equal\
-Wformat=2\
-Wmissing-format-attribute\
-Wmissing-noreturn\
-Wmissing-prototypes\
@ -346,10 +363,10 @@ AC_SUBST([AM_CPPFLAGS])
# Bail out on errors.
# ----------------------------------------------------------------------
if test ! -z "$missing_libraries"; then
AC_MSG_ERROR([missing libraries: $missing_libraries])
AC_MSG_ERROR([missing libraries: $missing_libraries])
fi
if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers])
AC_MSG_ERROR([missing headers: $missing_headers])
fi
AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2019 Hisham Muhammad. (C) 2020 htop dev team.", [Copyright message.])
@ -367,8 +384,7 @@ AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1])
AC_OUTPUT
if test "$my_htop_platform" = "unsupported"
then
if test "$my_htop_platform" = "unsupported"; then
echo ""
echo "****************************************************************"
echo "WARNING! This platform is not currently supported by htop."

View File

@ -14,17 +14,37 @@ in the source distribution for its full text.
#include <mach/mach.h>
#include "CRT.h"
#include "Platform.h"
#include "Process.h"
const ProcessClass DarwinProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = Process_writeField,
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, },
};
Process* DarwinProcess_new(const Settings* settings) {
@ -35,6 +55,7 @@ Process* DarwinProcess_new(const Settings* settings) {
this->utime = 0;
this->stime = 0;
this->taskAccess = true;
this->translated = false;
return &this->super;
}
@ -46,6 +67,34 @@ void Process_delete(Object* cast) {
free(this);
}
static void DarwinProcess_writeField(const Process* this, RichString* str, ProcessField field) {
const DarwinProcess* dp = (const DarwinProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch (field) {
// add Platform-specific fields here
case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break;
default:
Process_writeField(this, str, field);
return;
}
RichString_appendWide(str, attr, buffer);
}
static long DarwinProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const DarwinProcess* p1 = (const DarwinProcess*)v1;
const DarwinProcess* p2 = (const DarwinProcess*)v2;
switch (key) {
// add Platform-specific fields here
case TRANSLATED:
return SPACESHIP_NUMBER(p1->translated, p2->translated);
default:
return Process_compareByKey_Base(v1, v2, key);
}
}
bool Process_isThread(const Process* this) {
(void) this;
return false;
@ -195,6 +244,8 @@ ERROR_A:
}
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) {
DarwinProcess* dp = (DarwinProcess*)proc;
const struct extern_proc* ep = &ps->kp_proc;
/* UNSET HERE :
@ -223,6 +274,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
/* e_tdev = (major << 24) | (minor & 0xffffff) */
/* e_tdev == -1 for "no device" */
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
dp->translated = ps->kp_proc.p_flag & P_TRANSLATED;
proc->starttime_ctime = ep->p_starttime.tv_sec;
Process_fillStarttimeBuffer(proc);
@ -240,26 +292,24 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->updated = true;
}
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl) {
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval) {
struct proc_taskinfo pti;
if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
if (0 != proc->utime || 0 != proc->stime) {
uint64_t diff = (pti.pti_total_system - proc->stime)
+ (pti.pti_total_user - proc->utime);
uint64_t total_existing_time = proc->stime + proc->utime;
uint64_t total_current_time = pti.pti_total_system + pti.pti_total_user;
proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount
/ ((double)dpl->global_diff * 100000.0);
// fprintf(stderr, "%f %llu %llu %llu %llu %llu\n", proc->super.percent_cpu,
// proc->stime, proc->utime, pti.pti_total_system, pti.pti_total_user, dpl->global_diff);
// exit(7);
if (total_existing_time && 1E-6 < time_interval) {
uint64_t total_time_diff = total_current_time - total_existing_time;
proc->super.percent_cpu = ((double)total_time_diff / time_interval) * 100.0;
} else {
proc->super.percent_cpu = 0.0;
}
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
proc->super.time = total_current_time / 10000000;
proc->super.nlwp = pti.pti_threadnum;
proc->super.m_virt = pti.pti_virtual_size / CRT_pageSize;
proc->super.m_resident = pti.pti_resident_size / CRT_pageSize;
proc->super.m_virt = pti.pti_virtual_size / ONE_K;
proc->super.m_resident = pti.pti_resident_size / ONE_K;
proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
/ (double)dpl->host_info.max_mem;
@ -341,3 +391,15 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
}
proc->state = state;
}
const ProcessClass DarwinProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = DarwinProcess_writeField,
.compareByKey = DarwinProcess_compareByKey,
};

View File

@ -19,10 +19,13 @@ typedef struct DarwinProcess_ {
uint64_t utime;
uint64_t stime;
bool taskAccess;
bool translated;
} DarwinProcess;
extern const ProcessClass DarwinProcess_class;
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* DarwinProcess_new(const Settings* settings);
void Process_delete(Object* cast);
@ -31,7 +34,7 @@ bool Process_isThread(const Process* this);
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists);
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl);
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval);
/*
* Scan threads for process state information.

View File

@ -21,6 +21,7 @@ in the source distribution for its full text.
#include "CRT.h"
#include "DarwinProcess.h"
#include "Platform.h"
#include "ProcessList.h"
#include "zfs/openzfs_sysctl.h"
#include "zfs/ZfsArcStats.h"
@ -71,14 +72,14 @@ static void ProcessList_getHostInfo(host_basic_info_data_t* p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n");
CRT_fatalError("Unable to retrieve host info");
}
}
static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) {
if (NULL != p && NULL != *p) {
if (0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n");
CRT_fatalError("Unable to free old CPU load information");
}
*p = NULL;
}
@ -90,7 +91,7 @@ static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) {
// TODO Improving the accuracy of the load counts woule help a lot.
if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) {
CRT_fatalError("Unable to retrieve CPU info\n");
CRT_fatalError("Unable to retrieve CPU info");
}
return cpu_count;
@ -100,7 +101,7 @@ static void ProcessList_getVMStats(vm_statistics_t p) {
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) {
CRT_fatalError("Unable to retrieve VM statistics\n");
CRT_fatalError("Unable to retrieve VM statistics");
}
}
@ -131,7 +132,7 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) {
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidMatchList, userId);
ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId);
/* Initialize the CPU information */
this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load);
@ -158,6 +159,11 @@ void ProcessList_delete(ProcessList* this) {
free(this);
}
static double ticksToNanoseconds(const double ticks) {
const double nanos_per_sec = 1e9;
return ticks / (double) Platform_clockTicksPerSec * Platform_timebaseToNS * nanos_per_sec;
}
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
DarwinProcessList* dpl = (DarwinProcessList*)super;
bool preExisting = true;
@ -185,6 +191,8 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
}
}
const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount;
/* Clear the thread counts */
super->kernelThreads = 0;
super->userlandThreads = 0;
@ -204,7 +212,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
DarwinProcess_setFromLibprocPidinfo(proc, dpl, time_interval);
// Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);

View File

@ -6,32 +6,42 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include "Macros.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "ProcessLocksScreen.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "DarwinProcessList.h"
#include "config.h" // IWYU pragma: keep
#include "Platform.h"
#include <errno.h>
#include <math.h>
#include <stdlib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <unistd.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPSKeys.h>
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
#include "ClockMeter.h"
#include "CPUMeter.h"
#include "CRT.h"
#include "DarwinProcessList.h"
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "LoadAverageMeter.h"
#include "Macros.h"
#include "MemoryMeter.h"
#include "ProcessLocksScreen.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#ifdef HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
@ -71,35 +81,6 @@ const SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
@ -131,10 +112,28 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
int Platform_numberOfFields = 100;
double Platform_timebaseToNS = 1.0;
long Platform_clockTicksPerSec = -1;
void Platform_init(void) {
/* no platform-specific setup needed */
// Check if we can determine the timebase used on this system.
// If the API is unavailable assume we get our timebase in nanoseconds.
#ifdef HAVE_MACH_TIMEBASE_INFO
mach_timebase_info_data_t info;
mach_timebase_info(&info);
Platform_timebaseToNS = (double)info.numer / (double)info.denom;
#else
Platform_timebaseToNS = 1.0;
#endif
// Determine the number of clock ticks per second
errno = 0;
Platform_clockTicksPerSec = sysconf(_SC_CLK_TCK);
if (errno || Platform_clockTicksPerSec < 1) {
CRT_fatalError("Unable to retrieve clock tick rate");
}
}
void Platform_done(void) {
@ -179,16 +178,6 @@ int Platform_getMaxPid() {
return 99999;
}
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
static double Platform_setCPUAverageValues(Meter* mtr) {
const ProcessList* dpl = mtr->pl;
int cpus = dpl->cpuCount;

View File

@ -19,11 +19,12 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[];
extern const ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern double Platform_timebaseToNS;
extern long Platform_clockTicksPerSec;
extern const SignalItem Platform_signals[];
@ -43,8 +44,6 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid(void);
extern ProcessPidColumn Process_pidColumns[];
double Platform_setCPUValues(Meter* mtr, int cpu);
void Platform_setMemoryValues(Meter* mtr);

16
darwin/ProcessField.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef HEADER_DarwinProcessField
#define HEADER_DarwinProcessField
/*
htop - darwin/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
TRANSLATED = 100, \
// End of list
#endif /* HEADER_DarwinProcessField */

View File

@ -18,26 +18,16 @@ in the source distribution for its full text.
#include <sys/syscall.h>
const ProcessClass DragonFlyBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = DragonFlyBSDProcess_compare
},
.writeField = DragonFlyBSDProcess_writeField,
};
ProcessFieldData Process_fields[] = {
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
@ -53,21 +43,9 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = JID, .label = "JID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
Process* DragonFlyBSDProcess_new(const Settings* settings) {
@ -84,15 +62,15 @@ void Process_delete(Object* cast) {
free(this);
}
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
static void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
size_t n = sizeof(buffer) - 1;
switch (field) {
// add Platform-specific fields here
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
case JAIL: {
xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') {
@ -105,29 +83,21 @@ void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, Proces
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
RichString_appendWide(str, attr, buffer);
}
long DragonFlyBSDProcess_compare(const void* v1, const void* v2) {
const DragonFlyBSDProcess *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
static long DragonFlyBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const DragonFlyBSDProcess* p1 = (const DragonFlyBSDProcess*)v1;
const DragonFlyBSDProcess* p2 = (const DragonFlyBSDProcess*)v2;
if (settings->direction == 1) {
p1 = (const DragonFlyBSDProcess*)v1;
p2 = (const DragonFlyBSDProcess*)v2;
} else {
p2 = (const DragonFlyBSDProcess*)v1;
p1 = (const DragonFlyBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
switch (key) {
// add Platform-specific fields here
case JID:
return SPACESHIP_NUMBER(p1->jid, p2->jid);
case JAIL:
return SPACESHIP_NULLSTR(p1->jname, p2->jname);
default:
return Process_compare(v1, v2);
return Process_compareByKey_Base(v1, v2, key);
}
}
@ -140,3 +110,14 @@ bool Process_isThread(const Process* this) {
return (Process_isUserlandThread(this));
}
}
const ProcessClass DragonFlyBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = DragonFlyBSDProcess_writeField,
.compareByKey = DragonFlyBSDProcess_compareByKey
};

View File

@ -8,13 +8,6 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
typedef enum DragonFlyBSDProcessFields {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} DragonFlyBSDProcessField;
typedef struct DragonFlyBSDProcess_ {
Process super;
int kernel;
@ -29,18 +22,12 @@ typedef struct DragonFlyBSDProcess_ {
extern const ProcessClass DragonFlyBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* DragonFlyBSDProcess_new(const Settings* settings);
void Process_delete(Object* cast);
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field);
long DragonFlyBSDProcess_compare(const void* v1, const void* v2);
bool Process_isThread(const Process* this);
#endif

View File

@ -55,12 +55,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) {
pageSize = CRT_pageSize;
pageSizeKb = CRT_pageSizeKB;
} else {
pageSizeKb = pageSize / ONE_K;
}
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
CRT_fatalError("Cannot get pagesize by sysctl");
pageSizeKb = pageSize / ONE_K;
// usable page count vm.stats.vm.v_page_count
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
@ -265,7 +262,7 @@ static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
pl->usedSwap *= pageSizeKb;
}
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
static char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) {
char** argv = kvm_getargv(kd, kproc, 0);
if (!argv) {
return xStrdup(kproc->kp_comm);
@ -346,7 +343,7 @@ retry:
free(jls);
}
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
char* hostname;
char* jname;
@ -376,10 +373,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
int count = 0;
// TODO Kernel Threads seem to be skipped, need to figure out the correct flag
struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i];
const struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false;
bool ATTR_UNUSED isIdleProcess = false;
@ -433,13 +430,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
}
}
proc->m_virt = kproc->kp_vm_map_size / pageSize;
proc->m_resident = kproc->kp_vm_rssize;
proc->m_virt = kproc->kp_vm_map_size / ONE_K;
proc->m_resident = kproc->kp_vm_rssize * pageSizeKb;
proc->nlwp = kproc->kp_nthreads; // number of lwp thread
proc->time = (kproc->kp_swtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem);
proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
if (proc->percent_cpu > 0.1) {
// system idle process should own all CPU time left regardless of CPU count

View File

@ -11,16 +11,12 @@ in the source distribution for its full text.
#include <kvm.h>
#include <sys/param.h>
#include <osreldate.h>
#include <sys/kinfo.h>
#include <kinfo.h>
#include <sys/jail.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include "Hashtable.h"
#include "DragonFlyBSDProcess.h"
#define JAIL_ERRMSGLEN 1024
extern char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
@ -55,10 +51,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* this);
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd);
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid);
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif

View File

@ -31,9 +31,7 @@ in the source distribution for its full text.
#include <math.h>
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },

View File

@ -17,11 +17,8 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const ProcessField Platform_defaultFields[];
extern const SignalItem Platform_signals[];

View File

@ -0,0 +1,17 @@
#ifndef HEADER_DragonFlyBSDProcessField
#define HEADER_DragonFlyBSDProcessField
/*
htop - dragonflybsd/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
JID = 100, \
JAIL = 101, \
// End of list
#endif /* HEADER_DragonFlyBSDProcessField */

View File

@ -18,16 +18,16 @@ in the source distribution for its full text.
const char* const nodevStr = "nodev";
ProcessFieldData Process_fields[] = {
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
@ -44,21 +44,9 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = JID, .label = "JID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
Process* FreeBSDProcess_new(const Settings* settings) {
@ -80,9 +68,9 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
switch (field) {
// add FreeBSD-specific fields here
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
case JAIL: {
xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') {
@ -105,22 +93,14 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
RichString_appendWide(str, attr, buffer);
}
static long FreeBSDProcess_compare(const void* v1, const void* v2) {
const FreeBSDProcess *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
static long FreeBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const FreeBSDProcess* p1 = (const FreeBSDProcess*)v1;
const FreeBSDProcess* p2 = (const FreeBSDProcess*)v2;
if (settings->direction == 1) {
p1 = (const FreeBSDProcess*)v1;
p2 = (const FreeBSDProcess*)v2;
} else {
p2 = (const FreeBSDProcess*)v1;
p1 = (const FreeBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
switch (key) {
// add FreeBSD-specific fields here
case JID:
return SPACESHIP_NUMBER(p1->jid, p2->jid);
@ -129,7 +109,7 @@ static long FreeBSDProcess_compare(const void* v1, const void* v2) {
case TTY_NR:
return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath);
default:
return Process_compare(v1, v2);
return Process_compareByKey_Base(v1, v2, key);
}
}
@ -148,7 +128,8 @@ const ProcessClass FreeBSDProcess_class = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = FreeBSDProcess_compare
.compare = Process_compare
},
.writeField = FreeBSDProcess_writeField,
.compareByKey = FreeBSDProcess_compareByKey
};

View File

@ -18,13 +18,6 @@ in the source distribution for its full text.
extern const char* const nodevStr;
typedef enum FreeBSDProcessFields_ {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} FreeBSDProcessField;
typedef struct FreeBSDProcess_ {
Process super;
int kernel;
@ -43,9 +36,7 @@ static inline bool Process_isUserlandThread(const Process* this) {
extern const ProcessClass FreeBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* FreeBSDProcess_new(const Settings* settings);

View File

@ -14,7 +14,6 @@ in the source distribution for its full text.
#include <stdlib.h>
#include <string.h>
#include <sys/_iovec.h>
#include <sys/dirent.h>
#include <sys/errno.h>
#include <sys/param.h> // needs to be included before <sys/jail.h> for MAXPATHLEN
#include <sys/jail.h>
@ -72,12 +71,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) {
pageSize = CRT_pageSize;
pageSizeKb = CRT_pageSize;
} else {
pageSizeKb = pageSize / ONE_K;
}
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
CRT_fatalError("Cannot get pagesize by sysctl");
pageSizeKb = pageSize / ONE_K;
// usable page count vm.stats.vm.v_page_count
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
@ -526,13 +522,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
}
// from FreeBSD source /src/usr.bin/top/machine.c
proc->m_virt = kproc->ki_size / pageSize;
proc->m_resident = kproc->ki_rssize;
proc->m_virt = kproc->ki_size / ONE_K;
proc->m_resident = kproc->ki_rssize * pageSizeKb;
proc->nlwp = kproc->ki_numthreads;
proc->time = (kproc->ki_runtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem);
proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
/*
* TODO

View File

@ -47,9 +47,7 @@ in the source distribution for its full text.
#include "zfs/ZfsCompressedArcMeter.h"
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },

View File

@ -19,11 +19,7 @@ in the source distribution for its full text.
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const ProcessField Platform_defaultFields[];
extern const SignalItem Platform_signals[];

17
freebsd/ProcessField.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef HEADER_FreeBSDProcessField
#define HEADER_FreeBSDProcessField
/*
htop - freebsd/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
JID = 100, \
JAIL = 101, \
// End of list
#endif /* HEADER_FreeBSDProcessField */

View File

@ -2,11 +2,9 @@
.SH "NAME"
htop \- interactive process viewer
.SH "SYNOPSIS"
.LP
.B htop
.RB [ \-dCFhpustvH ]
.SH "DESCRIPTION"
.LP
.B htop
is a cross-platform ncurses-based process viewer.
.LP
@ -22,9 +20,7 @@ Tasks related to processes (killing, renicing) can be done without
entering their PIDs.
.br
.SH "COMMAND-LINE OPTIONS"
.LP
Mandatory arguments to long options are mandatory for short options too.
.LP
.TP
\fB\-d \-\-delay=DELAY\fR
Delay between updates, in tenths of seconds. If the delay value is
@ -66,10 +62,8 @@ Show processes in tree view
\fB\-H \-\-highlight-changes=DELAY\fR
Highlight new and old processes
.SH "INTERACTIVE COMMANDS"
.LP
The following commands are supported while in
.BR htop :
.LP
.TP 5
.B Up, Alt-k
Select (highlight) the previous process in the process list. Scroll the list
@ -157,11 +151,9 @@ between them as a tree. Toggling the key will switch between tree and
your previously selected sort view. Selecting a sort view will exit
tree view.
.TP
.B F6
On sorted view, select a field for sorting, also accessible through < and >.
.B F6, <, >
Selects a field for sorting, also accessible through < and >.
The current sort field is indicated by a highlight in the header.
On tree view, expand or collapse the current subtree. A "+" indicator in the
tree node indicates that it is collapsed.
.TP
.B F7, ]
Increase the selected process's priority (subtract from 'nice' value).
@ -232,7 +224,6 @@ Refresh: redraw screen and recalculate values.
PID search: type in process ID and the selection highlight will be moved to it.
.PD
.SH "COLUMNS"
.LP
The following columns can display data about each process. A value of '\-' in
all the rows indicates that a column is unsupported on your system, or
currently unimplemented in
@ -242,7 +233,6 @@ The names below are the ones used in the
shown in
.BR htop 's
main screen, it is shown below in parenthesis.
.LP
.TP 5
.B Command
The full command line of the process (i.e. program name and arguments). If the
@ -460,8 +450,48 @@ The executable file of the process as reported by the kernel. Requires CAP_SYS_P
.TP
.B All other flags
Currently unsupported (always displays '-').
.SH "EXTERNAL LIBRARIES"
While
.B htop
depends on most of the libraries it uses at build time there are two
noteworthy exceptions to this rule. These exceptions both relate to
data displayed in meters displayed in the header of
.B htop
and were intentionally created as optional runtime dependencies instead.
These exceptions are described below:
.TP
.B libsystemd
The bindings for libsystemd are used in the SystemD meter to determine
the number of active services and the overall system state. Looking for
the functions to determine these information at runtime allows for
builds to support these meters without forcing the package manager
to install these libraries on systems that otherwise don't use systemd.
Summary: no build time dependency, optional runtime dependency on
.B libsystemd
via dynamic loading, with
.B systemctl(1)
fallback.
.TP
.B libsensors
The bindings for libsensors are used for the CPU temperature readings
in the CPU usage meters if displaying the temperature is enabled through
the setup screen. In order for
.B htop
to show these temperatures correctly though, a proper configuration
of libsensors through its usual configuration files is assumed and that
all CPU cores correspond to temperature sensors from the
.B coretemp
driver with core 0 corresponding to a sensor labelled "Core 0". The
package temperature may be given as "Package id 0". If missing it is
inferred as the maximum value from the available per-core readings.
Summary: build time dependency on
.B libsensors(3)
C header files, optional runtime dependency on
.B libsensors(3)
via dynamic loading.
.SH "CONFIG FILE"
.LP
By default
.B htop
reads its configuration from the XDG-compliant path
@ -479,7 +509,6 @@ You may override the location of the configuration file using the $HTOPRC
environment variable (so you can have multiple configurations for different
machines that share the same home directory, for example).
.SH "MEMORY SIZES"
.LP
Memory sizes in
.B htop
are displayed in a human-readable form.
@ -497,7 +526,6 @@ space and make memory size representations consistent throughout
and
.BR limits.conf (5).
.SH "AUTHORS"
.LP
.B htop
was originally developed by Hisham Muhammad.
Nowadays it is maintained by the community at <htop@groups.io>.

13
htop.c
View File

@ -125,14 +125,14 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
case 's':
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
if (String_eq(optarg, "help")) {
for (int j = 1; j < Platform_numberOfFields; j++) {
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
const char* name = Process_fields[j].name;
if (name) printf ("%s\n", name);
}
exit(0);
}
flags.sortKey = 0;
for (int j = 1; j < Platform_numberOfFields; j++) {
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
if (Process_fields[j].name == NULL)
continue;
if (String_eq(optarg, Process_fields[j].name)) {
@ -298,9 +298,12 @@ int main(int argc, char** argv) {
if (flags.highlightDelaySecs != -1)
settings->highlightDelaySecs = flags.highlightDelaySecs;
if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey;
settings->treeView = false;
settings->direction = 1;
// -t -s <key> means "tree sorted by key"
// -s <key> means "list sorted by key" (previous existing behavior)
if (!flags.treeView) {
settings->treeView = false;
}
Settings_setSortKey(settings, flags.sortKey);
}
CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode);

View File

@ -8,6 +8,8 @@
{ include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] },
{ include: ["<sys/dirent.h>", "private", "<dirent.h>", "public"] },
{ include: ["<sys/signal.h>", "private", "<signal.h>", "public"] },
{ include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] },

View File

@ -4,6 +4,7 @@
#include <dlfcn.h>
#include <errno.h>
#include <math.h>
#include <sensors/sensors.h>
#include "XUtils.h"
@ -16,14 +17,19 @@ static int (*sym_sensors_snprintf_chip_name)(char*, size_t, const sensors_chip_n
static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*);
static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type);
static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*);
static char* (*sym_sensors_get_label)(const sensors_chip_name*, const sensors_feature*);
static void* dlopenHandle = NULL;
int LibSensors_init(FILE* input) {
if (!dlopenHandle) {
dlopenHandle = dlopen("libsensors.so", RTLD_LAZY);
if (!dlopenHandle)
goto dlfailure;
if (!dlopenHandle) {
/* Debian contains no unversioned .so in libsensors5, only in the -dev package, so work around that: */
dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY);
if (!dlopenHandle)
goto dlfailure;
}
/* Clear any errors */
dlerror();
@ -41,6 +47,7 @@ int LibSensors_init(FILE* input) {
resolve(sensors_get_features);
resolve(sensors_get_subfeature);
resolve(sensors_get_value);
resolve(sensors_get_label);
#undef resolve
}
@ -64,11 +71,14 @@ void LibSensors_cleanup(void) {
}
}
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
if (!dlopenHandle)
return -ENOTSUP;
void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) {
for (unsigned int i = 0; i <= cpuCount; i++)
cpus[i].temperature = NAN;
int tempCount = 0;
if (!dlopenHandle)
return;
unsigned int coreTempCount = 0;
int n = 0;
for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
@ -82,7 +92,25 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
if (feature->type != SENSORS_FEATURE_TEMP)
continue;
if (feature->number > cpuCount)
char* label = sym_sensors_get_label(chip, feature);
if (!label)
continue;
unsigned int tempId;
if (String_startsWith(label, "Package ")) {
tempId = 0;
} else if (String_startsWith(label, "temp")) {
/* Raspberry Pi has only temp1 */
tempId = 0;
} else if (String_startsWith(label, "Core ")) {
tempId = 1 + atoi(label + strlen("Core "));
} else {
tempId = UINT_MAX;
}
free(label);
if (tempId > cpuCount)
continue;
const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
@ -92,13 +120,43 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
if (r != 0)
continue;
cpus[feature->number].temperature = temp;
tempCount++;
cpus[tempId].temperature = temp;
if (tempId > 0)
coreTempCount++;
}
}
}
return tempCount;
const double packageTemp = cpus[0].temperature;
/* Only package temperature - copy to all cpus */
if (coreTempCount == 0 && !isnan(packageTemp)) {
for (unsigned int i = 1; i <= cpuCount; i++)
cpus[i].temperature = packageTemp;
return;
}
/* No package temperature - set to max core temperature */
if (isnan(packageTemp) && coreTempCount != 0) {
double maxTemp = NAN;
for (unsigned int i = 1; i <= cpuCount; i++) {
const double coreTemp = cpus[i].temperature;
if (isnan(coreTemp))
continue;
maxTemp = MAXIMUM(maxTemp, coreTemp);
}
cpus[0].temperature = maxTemp;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
const unsigned int delta = cpuCount / 2;
if (coreTempCount == delta) {
for (unsigned int i = 1; i <= delta; i++)
cpus[i + delta].temperature = cpus[i].temperature;
}
}
#endif /* HAVE_SENSORS_SENSORS_H */

View File

@ -11,6 +11,6 @@
int LibSensors_init(FILE* input);
void LibSensors_cleanup(void);
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount);
void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount);
#endif /* HEADER_LibSensors */

View File

@ -24,22 +24,22 @@ in the source distribution for its full text.
/* semi-global */
long long btime;
int pageSize;
int pageSizeKB;
/* Used to identify kernel threads in Comm and Exe columns */
static const char *const kthreadID = "KTHREAD";
ProcessFieldData Process_fields[] = {
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
@ -50,24 +50,7 @@ ProcessFieldData Process_fields[] = {
[CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[ITREALVALUE] = { .name = "ITREALVALUE", .title = NULL, .description = NULL, .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[VSIZE] = { .name = "VSIZE", .title = NULL, .description = NULL, .flags = 0, },
[RSS] = { .name = "RSS", .title = NULL, .description = NULL, .flags = 0, },
[RLIM] = { .name = "RLIM", .title = NULL, .description = NULL, .flags = 0, },
[STARTCODE] = { .name = "STARTCODE", .title = NULL, .description = NULL, .flags = 0, },
[ENDCODE] = { .name = "ENDCODE", .title = NULL, .description = NULL, .flags = 0, },
[STARTSTACK] = { .name = "STARTSTACK", .title = NULL, .description = NULL, .flags = 0, },
[KSTKESP] = { .name = "KSTKESP", .title = NULL, .description = NULL, .flags = 0, },
[KSTKEIP] = { .name = "KSTKEIP", .title = NULL, .description = NULL, .flags = 0, },
[SIGNAL] = { .name = "SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
[BLOCKED] = { .name = "BLOCKED", .title = NULL, .description = NULL, .flags = 0, },
[SSIGIGNORE] = { .name = "SIGIGNORE", .title = NULL, .description = NULL, .flags = 0, },
[SIGCATCH] = { .name = "SIGCATCH", .title = NULL, .description = NULL, .flags = 0, },
[WCHAN] = { .name = "WCHAN", .title = NULL, .description = NULL, .flags = 0, },
[NSWAP] = { .name = "NSWAP", .title = NULL, .description = NULL, .flags = 0, },
[CNSWAP] = { .name = "CNSWAP", .title = NULL, .description = NULL, .flags = 0, },
[EXIT_SIGNAL] = { .name = "EXIT_SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
[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, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
@ -83,10 +66,10 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
#ifdef HAVE_OPENVZ
[CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
[VPID] = { .name = "VPID", .title = " VPID ", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
[VPID] = { .name = "VPID", .title = "VPID", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, .pidColumn = true, },
#endif
#ifdef HAVE_VSERVER
[VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, },
@ -117,20 +100,6 @@ ProcessFieldData Process_fields[] = {
[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_LINUX_CWD, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
#ifdef HAVE_OPENVZ
{ .id = VPID, .label = "VPID" },
#endif
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
/* This function returns the string displayed in Command column, so that sorting
@ -183,6 +152,11 @@ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) {
return this->ioPriority;
}
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
IOPriority ioprio = 0;
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
@ -394,6 +368,16 @@ void LinuxProcess_makeCommandStr(Process* this) {
char *str = strStart;
int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset;
int cmdlineBasenameEnd = lp->procCmdlineBasenameEnd;
if (!cmdline) {
cmdlineBasenameOffset = 0;
cmdlineBasenameEnd = 0;
cmdline = "(zombie)";
}
assert(cmdlineBasenameOffset >= 0);
assert(cmdlineBasenameOffset <= (int)strlen(cmdline));
if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
@ -411,11 +395,11 @@ void LinuxProcess_makeCommandStr(Process* this) {
if (showProgramPath) {
(void) stpcpyWithNewlineConversion(str, cmdline);
mc->baseStart = cmdlineBasenameOffset;
mc->baseEnd = lp->procCmdlineBasenameEnd;
mc->baseEnd = cmdlineBasenameEnd;
} else {
(void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset);
mc->baseStart = 0;
mc->baseEnd = lp->procCmdlineBasenameEnd - cmdlineBasenameOffset;
mc->baseEnd = cmdlineBasenameEnd - cmdlineBasenameOffset;
}
if (mc->sep1) {
@ -430,6 +414,9 @@ void LinuxProcess_makeCommandStr(Process* this) {
int exeBasenameOffset = lp->procExeBasenameOffset;
int exeBasenameLen = exeLen - exeBasenameOffset;
assert(exeBasenameOffset >= 0);
assert(exeBasenameOffset <= (int)strlen(procExe));
/* Start with copying exe */
if (showProgramPath) {
str = stpcpy(str, procExe);
@ -536,7 +523,7 @@ static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAtt
if(lp->procExeDeleted)
baseAttr = CRT_colors[FAILED_READ];
RichString_append(str, attr, lp->mergedCommand.str);
RichString_appendWide(str, attr, lp->mergedCommand.str);
if (lp->mergedCommand.commEnd) {
if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) {
@ -605,10 +592,11 @@ static void LinuxProcess_writeCommandField(const Process *this, RichString *str,
buf = stpcpy(buf, " ");
}
}
n -= (buf - buffer);
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
LinuxProcess_writeCommand(this, attr, baseattr, str);
}
}
@ -619,7 +607,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
size_t n = sizeof(buffer) - 1;
switch ((int)field) {
switch (field) {
case TTY_NR: {
if (lp->ttyDevice) {
xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
@ -631,19 +619,19 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
}
case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
case M_DRS: Process_humanNumber(str, lp->m_drs * CRT_pageSizeKB, coloring); return;
case M_DT: Process_humanNumber(str, lp->m_dt * CRT_pageSizeKB, coloring); return;
case M_DRS: Process_humanNumber(str, lp->m_drs * pageSizeKB, coloring); return;
case M_DT: Process_humanNumber(str, lp->m_dt * pageSizeKB, coloring); return;
case M_LRS:
if (lp->m_lrs) {
Process_humanNumber(str, lp->m_lrs * CRT_pageSizeKB, coloring);
Process_humanNumber(str, lp->m_lrs * pageSizeKB, coloring);
return;
}
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
break;
case M_TRS: Process_humanNumber(str, lp->m_trs * CRT_pageSizeKB, coloring); return;
case M_SHARE: Process_humanNumber(str, lp->m_share * CRT_pageSizeKB, coloring); return;
case M_TRS: Process_humanNumber(str, lp->m_trs * pageSizeKB, coloring); return;
case M_SHARE: Process_humanNumber(str, lp->m_share * pageSizeKB, coloring); return;
case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return;
case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return;
case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return;
@ -674,7 +662,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
}
#ifdef HAVE_OPENVZ
case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break;
case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break;
case VPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, lp->vpid); break;
#endif
#ifdef HAVE_VSERVER
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
@ -757,22 +745,14 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
RichString_appendWide(str, attr, buffer);
}
static long LinuxProcess_compare(const void* v1, const void* v2) {
const LinuxProcess *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
static long LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const LinuxProcess* p1 = (const LinuxProcess*)v1;
const LinuxProcess* p2 = (const LinuxProcess*)v2;
if (settings->direction == 1) {
p1 = (const LinuxProcess*)v1;
p2 = (const LinuxProcess*)v2;
} else {
p2 = (const LinuxProcess*)v1;
p1 = (const LinuxProcess*)v2;
}
switch ((int)settings->sortKey) {
switch (key) {
case M_DRS:
return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs);
case M_DT:
@ -858,7 +838,7 @@ static long LinuxProcess_compare(const void* v1, const void* v2) {
case CWD:
return SPACESHIP_NULLSTR(p1->cwd, p2->cwd);
default:
return Process_compare(v1, v2);
return Process_compareByKey_Base(v1, v2, key);
}
}
@ -871,8 +851,9 @@ const ProcessClass LinuxProcess_class = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = LinuxProcess_compare
.compare = Process_compare
},
.writeField = LinuxProcess_writeField,
.getCommandStr = LinuxProcess_getCommandStr
.getCommandStr = LinuxProcess_getCommandStr,
.compareByKey = LinuxProcess_compareByKey
};

View File

@ -29,74 +29,6 @@ in the source distribution for its full text.
#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000
#define PROCESS_FLAG_LINUX_CWD 0x00020000
typedef enum UnsupportedProcessFields {
FLAGS = 9,
ITREALVALUE = 20,
VSIZE = 22,
RSS = 23,
RLIM = 24,
STARTCODE = 25,
ENDCODE = 26,
STARTSTACK = 27,
KSTKESP = 28,
KSTKEIP = 29,
SIGNAL = 30,
BLOCKED = 31,
SSIGIGNORE = 32,
SIGCATCH = 33,
WCHAN = 34,
NSWAP = 35,
CNSWAP = 36,
EXIT_SIGNAL = 37,
} UnsupportedProcessField;
typedef enum LinuxProcessFields {
CMINFLT = 11,
CMAJFLT = 13,
UTIME = 14,
STIME = 15,
CUTIME = 16,
CSTIME = 17,
M_SHARE = 41,
M_TRS = 42,
M_DRS = 43,
M_LRS = 44,
M_DT = 45,
#ifdef HAVE_OPENVZ
CTID = 100,
VPID = 101,
#endif
#ifdef HAVE_VSERVER
VXID = 102,
#endif
RCHAR = 103,
WCHAR = 104,
SYSCR = 105,
SYSCW = 106,
RBYTES = 107,
WBYTES = 108,
CNCLWB = 109,
IO_READ_RATE = 110,
IO_WRITE_RATE = 111,
IO_RATE = 112,
CGROUP = 113,
OOM = 114,
IO_PRIORITY = 115,
#ifdef HAVE_DELAYACCT
PERCENT_CPU_DELAY = 116,
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
M_PSS = 119,
M_SWAP = 120,
M_PSSWP = 121,
CTXT = 122,
SECATTR = 123,
PROC_COMM = 124,
PROC_EXE = 125,
CWD = 126,
LAST_PROCESSFIELD = 127,
} LinuxProcessField;
/* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It
* contains the merged Command string, and the information needed by
@ -191,11 +123,11 @@ static inline bool Process_isUserlandThread(const Process* this) {
return this->pid != this->tgid;
}
extern long long btime;
extern int pageSize;
extern ProcessFieldData Process_fields[];
extern int pageSizeKB;
extern ProcessPidColumn Process_pidColumns[];
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern const ProcessClass LinuxProcess_class;

View File

@ -57,6 +57,16 @@ in the source distribution for its full text.
#endif
// CentOS 6's kernel doesn't provide a definition of O_PATH
// based on definition taken from uapi/asm-generic/fcnth.h in Linux kernel tree
#ifndef O_PATH
# define O_PATH 010000000
#endif
static long long btime;
static long jiffy;
static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
assert(String_eq(mode, "r")); /* only currently supported mode */
@ -202,35 +212,39 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
LinuxProcessList_initNetlinkSocket(this);
#endif
// Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+)
FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r");
if (file != NULL) {
this->haveSmapsRollup = true;
fclose(file);
} else {
this->haveSmapsRollup = false;
}
// Initialize page size
pageSize = sysconf(_SC_PAGESIZE);
if (pageSize == -1)
CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
pageSizeKB = pageSize / ONE_K;
// Read btime
// Initialize clock ticks
jiffy = sysconf(_SC_CLK_TCK);
if (jiffy == -1)
CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
// Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+)
this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0);
// Read btime (the kernel boot time, as number of seconds since the epoch)
{
FILE* statfile = fopen(PROCSTATFILE, "r");
if (statfile == NULL) {
if (statfile == NULL)
CRT_fatalError("Cannot open " PROCSTATFILE);
}
while (true) {
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, sizeof(buffer), statfile) == NULL) {
CRT_fatalError("No btime in " PROCSTATFILE);
} else if (String_startsWith(buffer, "btime ")) {
if (sscanf(buffer, "btime %lld\n", &btime) != 1) {
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
}
if (fgets(buffer, sizeof(buffer), statfile) == NULL)
break;
}
if (String_startsWith(buffer, "btime ") == false)
continue;
if (sscanf(buffer, "btime %lld\n", &btime) == 1)
break;
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
}
fclose(statfile);
if (!btime)
CRT_fatalError("No btime in " PROCSTATFILE);
}
// Initialize CPU count
@ -267,16 +281,7 @@ void ProcessList_delete(ProcessList* pl) {
free(this);
}
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
static long jiffy = -1;
if (jiffy == -1) {
errno = 0;
jiffy = sysconf(_SC_CLK_TCK);
if (errno || -1 == jiffy) {
jiffy = -1;
return t; // Assume 100Hz clock
}
}
static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) {
return t * 100 / jiffy;
}
@ -329,13 +334,13 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location += 1;
lp->cmajflt = strtoull(location, &location, 10);
location += 1;
lp->utime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
lp->stime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
lp->cutime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
lp->cstime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1;
process->priority = strtol(location, &location, 10);
location += 1;
@ -345,7 +350,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location += 1;
location = strchr(location, ' ') + 1;
if (process->starttime_ctime == 0) {
process->starttime_ctime = btime + LinuxProcess_adjustTime(strtoll(location, &location, 10)) / 100;
process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100;
} else {
location = strchr(location, ' ') + 1;
}
@ -573,7 +578,7 @@ static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
Hashtable_delete(ht);
return total_size / CRT_pageSize;
return total_size / pageSize;
}
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) {
@ -593,6 +598,9 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p
fclose(statmfile);
if (r == 7) {
process->super.m_virt *= pageSizeKB;
process->super.m_resident *= pageSizeKB;
if (tmp_m_lrs) {
process->m_lrs = tmp_m_lrs;
} else if (performLookup) {
@ -1363,9 +1371,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
/* period might be 0 after system sleep */
float percent_cpu = (period < 1e-6) ? 0.0f : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
proc->percent_cpu = CLAMP(percent_cpu, 0.0f, cpus * 100.0f);
proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(pl->totalMem) * 100.0;
float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
proc->percent_cpu = CLAMP(percent_cpu, 0.0F, cpus * 100.0F);
proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0;
if (!preExisting) {
@ -1824,41 +1832,6 @@ static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
scanCPUFreqencyFromCPUinfo(this);
}
#ifdef HAVE_SENSORS_SENSORS_H
static void LinuxProcessList_scanCPUTemperature(LinuxProcessList* this) {
const int cpuCount = this->super.cpuCount;
for (int i = 0; i <= cpuCount; i++) {
this->cpus[i].temperature = NAN;
}
int r = LibSensors_getCPUTemperatures(this->cpus, cpuCount);
/* No temperature - nothing to do */
if (r <= 0)
return;
/* Only package temperature - copy to all cpus */
if (r == 1 && !isnan(this->cpus[0].temperature)) {
double packageTemp = this->cpus[0].temperature;
for (int i = 1; i <= cpuCount; i++) {
this->cpus[i].temperature = packageTemp;
}
return;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
if (r >= 2 && (r - 1) == (cpuCount / 2)) {
for (int i = cpuCount / 2 + 1; i <= cpuCount; i++) {
this->cpus[i].temperature = this->cpus[i/2].temperature;
}
return;
}
}
#endif
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
LinuxProcessList* this = (LinuxProcessList*) super;
const Settings* settings = super->settings;
@ -1876,7 +1849,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
#ifdef HAVE_SENSORS_SENSORS_H
if (settings->showCPUTemperature)
LinuxProcessList_scanCPUTemperature(this);
LibSensors_getCPUTemperatures(this->cpus, this->super.cpuCount);
#endif
// in pause mode only gather global data for meters (CPU/memory/...)

View File

@ -65,9 +65,7 @@ in the source distribution for its full text.
#endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },

View File

@ -18,9 +18,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const ProcessField Platform_defaultFields[];
extern const SignalItem Platform_signals[];

View File

@ -43,18 +43,22 @@ static void PressureStallMeter_updateValues(Meter* this, char* buffer, size_t le
}
Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]);
xSnprintf(buffer, len, "%s %s %.2lf%% %.2lf%% %.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
/* only print bar for ten (not sixty and threehundred), cause the sum is meaningless */
this->curItems = 1;
xSnprintf(buffer, len, "%s %s %5.2lf%% %5.2lf%% %5.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
}
static void PressureStallMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[0]);
RichString_write(out, CRT_colors[PRESSURE_STALL_TEN], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[1]);
RichString_append(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[2]);
RichString_append(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[0]);
RichString_writeAscii(out, CRT_colors[PRESSURE_STALL_TEN], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[1]);
RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[2]);
RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer);
}
const MeterClass PressureStallCPUSomeMeter_class = {

53
linux/ProcessField.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef HEADER_LinuxProcessField
#define HEADER_LinuxProcessField
/*
htop - linux/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
CMINFLT = 11, \
CMAJFLT = 13, \
UTIME = 14, \
STIME = 15, \
CUTIME = 16, \
CSTIME = 17, \
M_SHARE = 41, \
M_TRS = 42, \
M_DRS = 43, \
M_LRS = 44, \
M_DT = 45, \
CTID = 100, \
VPID = 101, \
VXID = 102, \
RCHAR = 103, \
WCHAR = 104, \
SYSCR = 105, \
SYSCW = 106, \
RBYTES = 107, \
WBYTES = 108, \
CNCLWB = 109, \
IO_READ_RATE = 110, \
IO_WRITE_RATE = 111, \
IO_RATE = 112, \
CGROUP = 113, \
OOM = 114, \
IO_PRIORITY = 115, \
PERCENT_CPU_DELAY = 116, \
PERCENT_IO_DELAY = 117, \
PERCENT_SWAP_DELAY = 118, \
M_PSS = 119, \
M_SWAP = 120, \
M_PSSWP = 121, \
CTXT = 122, \
SECATTR = 123, \
PROC_COMM = 124, \
PROC_EXE = 125, \
CWD = 126, \
// End of list
#endif /* HEADER_LinuxProcessField */

View File

@ -35,7 +35,7 @@ static bool hasSELinuxMount(void) {
return false;
}
if (sfbuf.f_type != SELINUX_MAGIC) {
if ((uint32_t)sfbuf.f_type != (uint32_t)SELINUX_MAGIC) {
return false;
}

View File

@ -267,9 +267,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
char buffer[16];
int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR;
RichString_write(out, CRT_colors[color], systemState ? systemState : "???");
RichString_writeAscii(out, CRT_colors[color], systemState ? systemState : "N/A");
RichString_append(out, CRT_colors[METER_TEXT], " (");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " (");
if (nFailedUnits == INVALID_VALUE) {
buffer[0] = '?';
@ -277,9 +277,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else {
xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits);
}
RichString_append(out, zeroDigitColor(nFailedUnits), buffer);
RichString_appendAscii(out, zeroDigitColor(nFailedUnits), buffer);
RichString_append(out, CRT_colors[METER_TEXT], "/");
RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nNames == INVALID_VALUE) {
buffer[0] = '?';
@ -287,9 +287,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else {
xSnprintf(buffer, sizeof(buffer), "%u", nNames);
}
RichString_append(out, valueDigitColor(nNames), buffer);
RichString_appendAscii(out, valueDigitColor(nNames), buffer);
RichString_append(out, CRT_colors[METER_TEXT], " failed) (");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) (");
if (nJobs == INVALID_VALUE) {
buffer[0] = '?';
@ -297,9 +297,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else {
xSnprintf(buffer, sizeof(buffer), "%u", nJobs);
}
RichString_append(out, zeroDigitColor(nJobs), buffer);
RichString_appendAscii(out, zeroDigitColor(nJobs), buffer);
RichString_append(out, CRT_colors[METER_TEXT], "/");
RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nInstalledJobs == INVALID_VALUE) {
buffer[0] = '?';
@ -307,9 +307,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else {
xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs);
}
RichString_append(out, valueDigitColor(nInstalledJobs), buffer);
RichString_appendAscii(out, valueDigitColor(nInstalledJobs), buffer);
RichString_append(out, CRT_colors[METER_TEXT], " jobs)");
RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)");
}
static const int SystemdMeter_attributes[] = {

View File

@ -37,17 +37,19 @@ static void ZramMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void ZramMeter_display(const Object* cast, RichString* out) {
char buffer[50];
const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE], buffer);
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " uncompressed:");
RichString_append(out, CRT_colors[METER_VALUE], buffer);
RichString_appendAscii(out, CRT_colors[METER_TEXT], " uncompressed:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
}
const MeterClass ZramMeter_class = {

View File

@ -16,7 +16,7 @@ in the source distribution for its full text.
#include "XUtils.h"
ProcessFieldData Process_fields[] = {
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = {
.name = "",
.title = NULL,
@ -25,9 +25,10 @@ ProcessFieldData Process_fields[] = {
},
[PID] = {
.name = "PID",
.title = " PID ",
.title = "PID",
.description = "Process/thread ID",
.flags = 0,
.pidColumn = true,
},
[COMM] = {
.name = "Command",
@ -43,21 +44,24 @@ ProcessFieldData Process_fields[] = {
},
[PPID] = {
.name = "PPID",
.title = " PPID ",
.title = "PPID",
.description = "Parent process ID",
.flags = 0,
.pidColumn = true,
},
[PGRP] = {
.name = "PGRP",
.title = " PGRP ",
.title = "PGRP",
.description = "Process group ID",
.flags = 0,
.pidColumn = true,
},
[SESSION] = {
.name = "SESSION",
.title = " SESN ",
.title = "SESN",
.description = "Process's session ID",
.flags = 0,
.pidColumn = true,
},
[TTY_NR] = {
.name = "TTY_NR",
@ -67,9 +71,10 @@ ProcessFieldData Process_fields[] = {
},
[TPGID] = {
.name = "TPGID",
.title = " TPGID ",
.title = "TPGID",
.description = "Process ID of the fg process group of the controlling terminal",
.flags = 0,
.pidColumn = true,
},
[MINFLT] = {
.name = "MINFLT",
@ -163,26 +168,11 @@ ProcessFieldData Process_fields[] = {
},
[TGID] = {
.name = "TGID",
.title = " TGID ",
.title = "TGID",
.description = "Thread group ID (i.e. process ID)",
.flags = 0,
.pidColumn = true,
},
[LAST_PROCESSFIELD] = {
.name = "*** report bug! ***",
.title = NULL,
.description = NULL,
.flags = 0,
},
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = 0, .label = NULL },
};
Process* OpenBSDProcess_new(const Settings* settings) {
@ -209,28 +199,20 @@ static void OpenBSDProcess_writeField(const Process* this, RichString* str, Proc
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
RichString_appendWide(str, attr, buffer);
}
static long OpenBSDProcess_compare(const void* v1, const void* v2) {
const OpenBSDProcess *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings;
if (settings->direction == 1) {
p1 = (const OpenBSDProcess*)v1;
p2 = (const OpenBSDProcess*)v2;
} else {
p2 = (const OpenBSDProcess*)v1;
p1 = (const OpenBSDProcess*)v2;
}
static long OpenBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const OpenBSDProcess* p1 = (const OpenBSDProcess*)v1;
const OpenBSDProcess* p2 = (const OpenBSDProcess*)v2;
// remove if actually used
(void)p1; (void)p2;
switch (settings->sortKey) {
switch (key) {
// add OpenBSD-specific fields here
default:
return Process_compare(v1, v2);
return Process_compareByKey_Base(v1, v2, key);
}
}
@ -239,9 +221,10 @@ const ProcessClass OpenBSDProcess_class = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = OpenBSDProcess_compare
.compare = Process_compare
},
.writeField = OpenBSDProcess_writeField,
.compareByKey = OpenBSDProcess_compareByKey
};
bool Process_isThread(const Process* this) {

View File

@ -15,11 +15,6 @@ in the source distribution for its full text.
#include "Settings.h"
typedef enum OpenBSDProcessFields_ {
// Add platform-specific fields here, with ids >= 100
LAST_PROCESSFIELD = 100,
} OpenBSDProcessField;
typedef struct OpenBSDProcess_ {
Process super;
} OpenBSDProcess;
@ -30,9 +25,7 @@ typedef struct OpenBSDProcess_ {
extern const ProcessClass OpenBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* OpenBSDProcess_new(const Settings* settings);

View File

@ -34,6 +34,8 @@ in the source distribution for its full text.
static long fscale;
static int pageSize;
static int pageSizeKB;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
const int mib[] = { CTL_HW, HW_NCPU };
@ -58,6 +60,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
err(1, "fscale sysctl call failed");
}
if ((pageSize = sysconf(_SC_PAGESIZE)) == -1)
err(1, "pagesize sysconf call failed");
pageSizeKB = pageSize / ONE_K;
for (int i = 0; i <= pl->cpuCount; i++) {
CPUData* d = opl->cpus + i;
d->totalTime = 1;
@ -94,8 +100,8 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
err(1, "uvmexp sysctl call failed");
}
pl->totalMem = uvmexp.npages * CRT_pageSizeKB;
pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * CRT_pageSizeKB;
pl->totalMem = uvmexp.npages * pageSizeKB;
pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB;
// Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9)
const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
@ -106,7 +112,7 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
err(1, "cannot get vfs.bcachestat");
}
pl->cachedMem = bcstats.numbufpages * CRT_pageSizeKB;
pl->cachedMem = bcstats.numbufpages * pageSizeKB;
/*
* Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
@ -222,9 +228,9 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
}
}
proc->m_virt = kproc->p_vm_dsize;
proc->m_resident = kproc->p_vm_rssize;
proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(this->super.totalMem) * 100.0;
proc->m_virt = kproc->p_vm_dsize * pageSizeKB;
proc->m_resident = kproc->p_vm_rssize * pageSizeKB;
proc->percent_mem = proc->m_resident / (double)(this->super.totalMem) * 100.0;
proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount * 100.0);
//proc->nlwp = kproc->p_numthreads;
proc->nice = kproc->p_nice - 20;

View File

@ -42,9 +42,7 @@ in the source distribution for its full text.
#include "XUtils.h"
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
/*
* See /usr/include/sys/signal.h

View File

@ -20,11 +20,7 @@ in the source distribution for its full text.
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const ProcessField Platform_defaultFields[];
/* see /usr/include/sys/signal.h */
extern const SignalItem Platform_signals[];

15
openbsd/ProcessField.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef HEADER_OpenBSDProcessField
#define HEADER_OpenBSDProcessField
/*
htop - openbsd/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
// End of list
#endif /* HEADER_OpenBSDProcessField */

View File

@ -86,7 +86,7 @@ const SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
ProcessField Platform_defaultFields[] = { PID, LWPID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const ProcessField Platform_defaultFields[] = { PID, LWPID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
@ -119,10 +119,6 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
int Platform_numberOfFields = LAST_PROCESSFIELD;
extern char Process_pidFormat[20];
void Platform_init(void) {
/* no platform-specific setup needed */
}

View File

@ -25,7 +25,6 @@ in the source distribution for its full text.
#define kill(pid, signal) kill(pid / 1024, signal)
extern ProcessFieldData Process_fields[];
typedef struct var kvar_t;
typedef struct envAccum_ {
@ -41,14 +40,10 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ProcessField Platform_defaultFields[];
extern const ProcessField Platform_defaultFields[];
extern const MeterClass* const Platform_meterTypes[];
extern int Platform_numberOfFields;
extern char Process_pidFormat[20];
void Platform_init(void);
void Platform_done(void);

22
solaris/ProcessField.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef HEADER_SolarisProcessField
#define HEADER_SolarisProcessField
/*
htop - solaris/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
ZONEID = 100, \
ZONE = 101, \
PROJID = 102, \
TASKID = 103, \
POOLID = 104, \
CONTID = 105, \
LWPID = 106, \
// End of list
#endif /* HEADER_SolarisProcessField */

View File

@ -23,21 +23,22 @@ const ProcessClass SolarisProcess_class = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = SolarisProcess_compare
.compare = Process_compare
},
.writeField = SolarisProcess_writeField,
.compareByKey = SolarisProcess_compareByKey
};
ProcessFieldData Process_fields[] = {
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, O onproc, Z zombie, T stopped, W waiting)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
@ -53,31 +54,14 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[ZONEID] = { .name = "ZONEID", .title = " ZONEID ", .description = "Zone ID", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[ZONEID] = { .name = "ZONEID", .title = "ZONEID", .description = "Zone ID", .flags = 0, .pidColumn = true, },
[ZONE] = { .name = "ZONE", .title = "ZONE ", .description = "Zone name", .flags = 0, },
[PROJID] = { .name = "PROJID", .title = " PRJID ", .description = "Project ID", .flags = 0, },
[TASKID] = { .name = "TASKID", .title = " TSKID ", .description = "Task ID", .flags = 0, },
[POOLID] = { .name = "POOLID", .title = " POLID ", .description = "Pool ID", .flags = 0, },
[CONTID] = { .name = "CONTID", .title = " CNTID ", .description = "Contract ID", .flags = 0, },
[LWPID] = { .name = "LWPID", .title = " LWPID ", .description = "LWP ID", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = ZONEID, .label = "ZONEID" },
{ .id = TASKID, .label = "TSKID" },
{ .id = PROJID, .label = "PRJID" },
{ .id = POOLID, .label = "POLID" },
{ .id = CONTID, .label = "CNTID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = LWPID, .label = "LWPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
[PROJID] = { .name = "PROJID", .title = "PRJID", .description = "Project ID", .flags = 0, .pidColumn = true, },
[TASKID] = { .name = "TASKID", .title = "TSKID", .description = "Task ID", .flags = 0, .pidColumn = true, },
[POOLID] = { .name = "POOLID", .title = "POLID", .description = "Pool ID", .flags = 0, .pidColumn = true, },
[CONTID] = { .name = "CONTID", .title = "CNTID", .description = "Contract ID", .flags = 0, .pidColumn = true, },
[LWPID] = { .name = "LWPID", .title = "LWPID", .description = "LWP ID", .flags = 0, .pidColumn = true, },
};
Process* SolarisProcess_new(const Settings* settings) {
@ -99,37 +83,29 @@ void SolarisProcess_writeField(const Process* this, RichString* str, ProcessFiel
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
switch (field) {
// add Solaris-specific fields here
case ZONEID: xSnprintf(buffer, n, Process_pidFormat, sp->zoneid); break;
case PROJID: xSnprintf(buffer, n, Process_pidFormat, sp->projid); break;
case TASKID: xSnprintf(buffer, n, Process_pidFormat, sp->taskid); break;
case POOLID: xSnprintf(buffer, n, Process_pidFormat, sp->poolid); break;
case CONTID: xSnprintf(buffer, n, Process_pidFormat, sp->contid); break;
case ZONEID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->zoneid); break;
case PROJID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->projid); break;
case TASKID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->taskid); break;
case POOLID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->poolid); break;
case CONTID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->contid); break;
case ZONE: xSnprintf(buffer, n, "%-*s ", ZONENAME_MAX/4, sp->zname); break;
case PID: xSnprintf(buffer, n, Process_pidFormat, sp->realpid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, sp->realppid); break;
case LWPID: xSnprintf(buffer, n, Process_pidFormat, sp->lwpid); break;
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realpid); break;
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realppid); break;
case LWPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->lwpid); break;
default:
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
RichString_appendWide(str, attr, buffer);
}
long SolarisProcess_compare(const void* v1, const void* v2) {
const SolarisProcess *p1, *p2;
const Settings* settings = ((const Process*)v1)->settings;
long SolarisProcess_compareByKey(const void* v1, const void* v2, ProcessField key) {
const SolarisProcess* p1 = (const SolarisProcess*)v1;
const SolarisProcess* p2 = (const SolarisProcess*)v2;
if (settings->direction == 1) {
p1 = (const SolarisProcess*)v1;
p2 = (const SolarisProcess*)v2;
} else {
p2 = (const SolarisProcess*)v1;
p1 = (const SolarisProcess*)v2;
}
switch ((int) settings->sortKey) {
switch (key) {
case ZONEID:
return SPACESHIP_NUMBER(p1->zoneid, p2->zoneid);
case PROJID:
@ -149,7 +125,7 @@ long SolarisProcess_compare(const void* v1, const void* v2) {
case LWPID:
return SPACESHIP_NUMBER(p1->lwpid, p2->lwpid);
default:
return Process_compare(v1, v2);
return Process_compareByKey_Base(v1, v2, key);
}
}

View File

@ -13,18 +13,6 @@ in the source distribution for its full text.
#include <sys/proc.h>
#include <libproc.h>
typedef enum SolarisProcessField_ {
// Add platform-specific fields here, with ids >= 100
ZONEID = 100,
ZONE = 101,
PROJID = 102,
TASKID = 103,
POOLID = 104,
CONTID = 105,
LWPID = 106,
LAST_PROCESSFIELD = 107,
} SolarisProcessField;
typedef struct SolarisProcess_ {
Process super;
int kernel;
@ -46,9 +34,7 @@ typedef struct SolarisProcess_ {
extern const ProcessClass SolarisProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* SolarisProcess_new(const Settings* settings);
@ -56,7 +42,7 @@ void Process_delete(Object* cast);
void SolarisProcess_writeField(const Process* this, RichString* str, ProcessField field);
long SolarisProcess_compare(const void* v1, const void* v2);
long SolarisProcess_compareByKey(const Process* v1, const Process* v2, ProcessField field);
bool Process_isThread(const Process* this);

View File

@ -25,9 +25,11 @@ in the source distribution for its full text.
#include "CRT.h"
#define MAXCMDLINE 255
static int pageSize;
static int pageSizeKB;
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) {
char* zname;
@ -50,13 +52,18 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
spl->kd = kstat_open();
pl->cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
pageSize = sysconf(_SC_PAGESIZE);
if (pageSize == -1)
CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
pageSizeKB = pageSize / 1024;
if (pl->cpuCount == 1 ) {
pl->cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
if (pl->cpuCount == -1)
CRT_fatalError("Cannot get CPU count by sysconf(_SC_NPROCESSORS_ONLN)");
else if (pl->cpuCount == 1)
spl->cpus = xRealloc(spl->cpus, sizeof(CPUData));
} else {
else
spl->cpus = xRealloc(spl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
return pl;
}
@ -169,9 +176,9 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
freemem_pgs = kstat_data_lookup(meminfo, "freemem");
pages = kstat_data_lookup(meminfo, "pagestotal");
pl->totalMem = totalmem_pgs->value.ui64 * CRT_pageSizeKB;
if (pl->totalMem > freemem_pgs->value.ui64 * CRT_pageSizeKB) {
pl->usedMem = pl->totalMem - freemem_pgs->value.ui64 * CRT_pageSizeKB;
pl->totalMem = totalmem_pgs->value.ui64 * pageSizeKB;
if (pl->totalMem > freemem_pgs->value.ui64 * pageSizeKB) {
pl->usedMem = pl->totalMem - freemem_pgs->value.ui64 * pageSizeKB;
} else {
pl->usedMem = 0; // This can happen in non-global zone (in theory)
}
@ -179,13 +186,13 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
pl->cachedMem = 0;
// Not really "buffers" but the best Solaris analogue that I can find to
// "memory in use but not by programs or the kernel itself"
pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * CRT_pageSizeKB;
pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * pageSizeKB;
} else {
// Fall back to basic sysconf if kstat isn't working
pl->totalMem = sysconf(_SC_PHYS_PAGES) * CRT_pageSize;
pl->totalMem = sysconf(_SC_PHYS_PAGES) * pageSize;
pl->buffersMem = 0;
pl->cachedMem = 0;
pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * CRT_pageSize);
pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * pageSize);
}
// Part 2 - swap
@ -215,8 +222,8 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
}
free(spathbase);
free(sl);
pl->totalSwap = totalswap * CRT_pageSizeKB;
pl->usedSwap = pl->totalSwap - (totalfree * CRT_pageSizeKB);
pl->totalSwap = totalswap * pageSizeKB;
pl->usedSwap = pl->totalSwap - (totalfree * pageSizeKB);
}
static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) {
@ -323,8 +330,8 @@ int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, void*
proc->pgrp = _psinfo->pr_pgid;
proc->nlwp = _psinfo->pr_nlwp;
proc->tty_nr = _psinfo->pr_ttydev;
proc->m_resident = _psinfo->pr_rssize / CRT_pageSizeKB;
proc->m_virt = _psinfo->pr_size / CRT_pageSizeKB;
proc->m_resident = _psinfo->pr_rssize; // KB
proc->m_virt = _psinfo->pr_size; // KB
if (!preExisting) {
sproc->realpid = _psinfo->pr_pid;

View File

@ -28,36 +28,7 @@ const SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
@ -88,12 +59,6 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};
int Platform_numberOfFields = 100;
ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
void Platform_init(void) {
/* no platform-specific setup needed */
}

View File

@ -19,18 +19,10 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ProcessField Platform_defaultFields[];
extern ProcessFieldData Process_fields[];
extern const ProcessField Platform_defaultFields[];
extern const MeterClass* const Platform_meterTypes[];
extern int Platform_numberOfFields;
extern char Process_pidFormat[20];
extern ProcessPidColumn Process_pidColumns[];
void Platform_init(void);
void Platform_done(void);

View File

@ -0,0 +1,15 @@
#ifndef HEADER_UnsupportedProcessField
#define HEADER_UnsupportedProcessField
/*
htop - unsupported/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
// End of list
#endif /* HEADER_UnsupportedProcessField */

View File

@ -9,6 +9,33 @@ in the source distribution for its full text.
#include "UnsupportedProcess.h"
#include <stdlib.h>
const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
};
Process* UnsupportedProcess_new(Settings* settings) {
Process* this = xCalloc(1, sizeof(Process));

View File

@ -11,6 +11,8 @@ in the source distribution for its full text.
#define Process_delete UnsupportedProcess_delete
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* UnsupportedProcess_new(Settings* settings);
void UnsupportedProcess_delete(Object* cast);

View File

@ -50,29 +50,29 @@ static void ZfsArcMeter_display(const Object* cast, RichString* out) {
if (this->values[5] > 0) {
char buffer[50];
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[5], 50);
RichString_append(out, CRT_colors[METER_TEXT], " Used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50);
RichString_append(out, CRT_colors[METER_TEXT], " MFU:");
RichString_append(out, CRT_colors[ZFS_MFU], buffer);
Meter_humanUnit(buffer, this->values[1], 50);
RichString_append(out, CRT_colors[METER_TEXT], " MRU:");
RichString_append(out, CRT_colors[ZFS_MRU], buffer);
Meter_humanUnit(buffer, this->values[2], 50);
RichString_append(out, CRT_colors[METER_TEXT], " Anon:");
RichString_append(out, CRT_colors[ZFS_ANON], buffer);
Meter_humanUnit(buffer, this->values[3], 50);
RichString_append(out, CRT_colors[METER_TEXT], " Hdr:");
RichString_append(out, CRT_colors[ZFS_HEADER], buffer);
Meter_humanUnit(buffer, this->values[4], 50);
RichString_append(out, CRT_colors[METER_TEXT], " Oth:");
RichString_append(out, CRT_colors[ZFS_OTHER], buffer);
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[5], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " Used:");
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " MFU:");
RichString_appendAscii(out, CRT_colors[ZFS_MFU], buffer);
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " MRU:");
RichString_appendAscii(out, CRT_colors[ZFS_MRU], buffer);
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " Anon:");
RichString_appendAscii(out, CRT_colors[ZFS_ANON], buffer);
Meter_humanUnit(buffer, this->values[3], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " Hdr:");
RichString_appendAscii(out, CRT_colors[ZFS_HEADER], buffer);
Meter_humanUnit(buffer, this->values[4], sizeof(buffer));
RichString_appendAscii(out, CRT_colors[METER_TEXT], " Oth:");
RichString_appendAscii(out, CRT_colors[ZFS_OTHER], buffer);
} else {
RichString_write(out, CRT_colors[METER_TEXT], " ");
RichString_append(out, CRT_colors[FAILED_SEARCH], "Unavailable");
RichString_writeAscii(out, CRT_colors[METER_TEXT], " ");
RichString_appendAscii(out, CRT_colors[FAILED_SEARCH], "Unavailable");
}
}

Some files were not shown because too many files have changed in this diff Show More