187 Commits

Author SHA1 Message Date
805546e291 Bump version to 3.0.0beta2 2018-02-17 15:32:24 -02:00
4aba5d878d Fix inttypes.h header 2018-02-17 15:30:15 -02:00
e48b63b585 Darwin: expose LAST_PROCESSFIELD like the other platforms 2018-02-17 15:30:15 -02:00
1748abfb4e Add more default screens 2018-02-17 15:30:15 -02:00
d0fc93d82c Only compute counters is process is shown 2018-02-17 15:30:15 -02:00
0cbdf8ba27 Implemented various performance counters 2018-02-17 15:30:15 -02:00
cf0c074cc8 Add IPC performance counter for Linux 2018-02-17 15:30:15 -02:00
9c40c9a0e7 Add perf counter object 2018-02-17 15:30:15 -02:00
c55c8c2316 configure.ac: add --enable-perfcounters 2018-02-17 15:30:15 -02:00
eb8ca9be84 Set default sort keys in default screens 2018-02-17 15:30:15 -02:00
401d19be37 Add make symbols target 2018-02-17 15:30:15 -02:00
bcf4a30ee6 Store .sort_key as a string 2018-02-17 15:30:15 -02:00
86fea8facd Match iotop's screen configuration 2018-02-17 15:30:15 -02:00
d145a3bfdd Use screen's flags when reading process data 2018-02-17 15:30:15 -02:00
b63d0d04db Screens: Fix "New Screen" option 2018-02-17 15:30:15 -02:00
117cc515fc htoprc: store screen 0's setup for improved compatibility 2018-02-17 15:30:15 -02:00
187a035a76 Add support for multiple screens, switchable using Tab 2018-02-17 15:30:15 -02:00
ece89b8df0 Begin add supporting for multiple screens 2018-02-17 15:30:15 -02:00
52b5beb4e4 Move responsibility for cursor placement to Panels 2018-02-17 15:30:15 -02:00
df9922a67e Fix preservation of LDFLAGS value during configure script
Fixes #738.
2018-02-17 14:50:55 -02:00
fcdff59f4c Update ChangeLog 2018-02-17 14:50:42 -02:00
b544c22c72 Fix issue with small terminals.
Fixes #733.
2018-02-13 06:41:44 -02:00
f37a050d3d Optimize Vector_size on non-debug builds 2018-02-05 11:01:35 +01:00
03f17688ad Handle unexpected values for character passed to isalnum
It seems that certain negative integer values can crash isalnum().
Let's protect against those.

Fixes #711.
2018-02-05 10:59:20 +01:00
a32d7528f6 Check for pkgconfig's presence before using it.
Fixes #710.
2018-02-05 10:22:16 +01:00
ac2dff2881 Fix color behavior on some terminals.
Fixes #635.
2018-02-05 07:20:27 +01:00
c50440f165 Bump version to 2.1.0 2018-02-04 20:13:55 +01:00
b0588d90ff parseBatInfo: check line for NULL before passing it to String_getToken() 2018-02-04 17:04:47 +01:00
b84ebfd4e8 Clarify we are looking for the null termination
Not for a comparison to zero
2018-02-04 17:02:30 +01:00
b86e14d3cf Typo in man page
*hightlight*
2018-02-04 17:01:39 +01:00
b34d76cd41 Fix: infinite loop in tree view on macOS
Fixes #688, the bug regressed on 584a9bc.

On Mac OS X 10.11.6, all processes have their parents since there's a
special process named "kernel_task", whose PID and PPID are 0. As a
result, `this->processes` is never changed causing infinite `while`.
2018-02-04 16:51:06 +01:00
87be623eac Add support for Linux TASK_IDLE
Linux commit 06eb61844d841d0032a9950ce7f8e783ee49c0d0 ("sched/debug:
Add explicit TASK_IDLE printing") exposes kthreads idling using
TASK_IDLE in procfs as "I (idle)".

Until now, when sorting the STATE ("S") column, htop used the raw
value of the state character for comparison, however that led to the
undesirable effect of TASK_IDLE ('I') tasks being sorted above tasks
that were running ('R').

Thus, explicitly recognize the idle process state, and sort it below
others.
2018-02-04 16:44:21 +01:00
b27712181a Darwin: disable thread reading due to bug in macOS High Sierra 2018-02-04 08:59:29 +01:00
ad99187680 htop scans /proc to determine how many running tasks exist. Since that
operation is not possible to be conducted in an atomic fashion, task
scheduling effects can lead to a count greater than the number of actual
processors; this is more easily noticed on machines with several CPUs
and under heavy workload.
This patch simply adds an upper bound on cpuCount to guarantee
consistent reports of the number of running tasks at any given time.
2018-01-23 14:11:47 -02:00
b7b66b76a5 Adds support for linux delay accounting (#667)
Adds support for showing columns with linux delay accounting.

This information can be read from the netlink interface, and thus we set up a socket to read from that when initializing the LinuxProcessList (LinuxProcessList_initNetlinkSocket). After that, for each process we call LinuxProcessList_readDelayAcctData, which sends a message thru the socket after setting up a callback to get the answer from the Kernel. That callback sets the process total delay time attribute. We then set the delay percent as the percentage of time process cpu time since last scan.
2017-12-04 00:15:29 -02:00
52831955c7 fix fallthough comments for GCC 7.x
GCC 7.x does some extended checks on fallthough for switch/case
statement. The warning looks like this:

warning: this statement may fall through [-Wimplicit-fallthrough=]

It can be told about implicit fallthough, however it does not
recognize comments within blocks, so move the comments outside.
2017-10-23 14:27:33 -04:00
ef34a83c54 Merge pull request #678 from evelikov/remove-libtool
Remove libtool references
2017-10-13 13:55:43 -07:00
4c1230b03b Remove libtool references
The project builds a single standalone binary.

There are no libraries created - be that static or shared ones.
Thus there's no need for libtool.

Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
2017-09-22 16:53:26 +01:00
e3f65c8ec2 Make 'c' key work with threads as well. 2017-09-14 17:10:39 -03:00
b9934ffa08 minor style tweak 2017-09-04 13:53:03 -03:00
65ec0bd6a6 Merge pull request #676 from wangqr/master
Find roots when constructing process tree, fix #587
2017-09-01 19:56:55 -03:00
584a9bceab Find roots when constructing process tree, fix #587 2017-09-01 21:34:05 +08:00
9487bda330 Do not use xSnprintf when the result is used. Fixes #662. 2017-08-01 15:48:43 -07:00
4300a19592 Merge pull request #659 from guoci/patch-1
make script version agnostic
2017-07-28 21:07:54 -03:00
60acda0bce make script version agnostic 2017-07-27 17:02:01 -04:00
09e241fb12 Security review: check results of snprintf.
Calls marked with xSnprintf shouldn't fail.
Abort program cleanly if any of them does.
2017-07-27 16:07:50 -03:00
3975e9ce5c Merge branch 'master' of https://github.com/hishamhm/htop 2017-07-26 16:16:10 -03:00
821d50f0b4 Merge pull request #651 from Explorer09/graph-mode-draw
Round values in graph drawing (instead of implicit truncate)
2017-07-26 16:15:59 -03:00
543d65c6ab Security review: make privilege dropping-restoring optional.
This is/was necessary only on macOS, because you needed root in order
to read the process list. This was never necessary on Linux, and
it also raises security concerns, so now it needs to be enabled
explicitly at build time.
2017-07-26 15:40:55 -03:00
f205f7004c Use regular readdir since readdir_r is deprecated and newer GCC complains. 2017-07-26 15:35:39 -03:00
ef7817c17a Merge pull request #656 from rsaxvc/master
more const usage
2017-07-26 11:24:51 -03:00
33fda93271 Merge pull request #657 from kalbasit/remove_whitespace_from_config_file
Settings: do not emit trailing whitespace
2017-07-25 13:57:55 -03:00
ce0cf3c457 Settings: do not emit trailing whitespace 2017-07-24 16:36:27 -07:00
d5faf64374 Mark some things as const
Several string pointer arrays pointed to const strings
but were not const themselves.

A few various structures and arrays were also marked const.
2017-07-22 22:34:30 -05:00
18b3e5d255 .gitignore vim & nano swp files 2017-07-22 22:34:30 -05:00
143a7de6b2 Round values in graph drawing (instead of implicit truncate) 2017-07-15 22:14:44 +08:00
e940aecfb9 Add "no perm" status when other fields fail due to lack of permission.
Thanks @Sworddragon for the heads up.
See #88.
2017-07-10 20:57:34 -03:00
1a178ad581 Merge branch 'master' of https://github.com/hishamhm/htop 2017-07-10 20:14:53 -03:00
c39f18a7be Add a clear warning about unsupported platforms.
Closes #648.
2017-07-10 20:14:25 -03:00
978c9e1698 Merge pull request #647 from Explorer09/dragonfly-pid-max
DragonFlyBSD PID_MAX is 999999.
2017-07-10 10:19:26 -03:00
e70f447d54 DragonFlyBSD PID_MAX is 999999.
See DragonFlyBSD source "sys/sys/proc.h".

Fixes #646
2017-07-07 20:38:04 +08:00
71785e2ded Set idle I/O prio to 0x6007, like ionice.
As suggested by @wolfgang42 in #100.
2017-07-05 15:20:48 -03:00
e9ecbd05bc Use class value only to display idle I/O priority.
As suggested by @wolfgang42. Fixes #100.
2017-07-05 15:18:02 -03:00
89d15399c5 Merge branch 'master' into fixedgray 2017-06-06 15:27:02 -03:00
e03e45d819 Merge pull request #624 from mklein-de/xterm-256color
make special keys also work with TERM=xterm-256color
2017-06-06 15:23:58 -03:00
45f3769887 make special keys also work with TERM=xterm-256color 2017-06-01 12:52:14 +02:00
2d8dd0b29e Work around the strange behavior of gray. 2017-05-23 14:02:34 -03:00
b1028e03c5 Merge pull request #414 from Explorer09/meters-panel-edit
Mouse-friendly functions bar for meters panel
2017-04-24 23:48:13 -03:00
0c168471b6 F6 is actually used for both actions, depending on context. 2017-04-24 23:38:28 -03:00
68b8efb83f Merge pull request #612 from ryenus/keymap
update keymap in help
2017-04-24 23:35:38 -03:00
418fea33f4 Merge pull request #613 from dkgroot/master
Initial addition of dragonflybsd (based on FreeBSD)
2017-04-24 23:34:27 -03:00
975e22144e Merge pull request #614 from dkgroot/EnhanceMakeHeader
Enh: scripts/MakeHeader script
2017-04-24 23:30:42 -03:00
50f03f5950 Enh: scripts/MakeHeader script
Only write a new .h file if something changed for the header file being created.
This prevents a lot of recompilation during development
2017-04-21 16:39:03 +02:00
cb7a06379d Enh: Add support for backtrace using execinfo 2017-04-21 16:36:19 +02:00
cba695961a Fix: TreeView 2017-04-21 16:34:40 +02:00
49af12e7c6 implement: readJailName
Note: dragonflybsd does not have 'jail_get' like freebsd does.
It does however provide a sysctl "jail.list" which returns a list of all jails.
2017-04-20 15:14:33 +02:00
b258d6e53e Initial addition of dragonflybsd (based on FreeBSD) 2017-04-19 16:19:32 +02:00
e0e84401e4 update keymap in help 2017-04-19 18:20:42 +08:00
5570748dd2 Merge pull request #610 from coypoop/patch-1
Add missing include
2017-03-28 23:56:13 -03:00
3c09082422 Merge pull request #608 from giwhub/giwhub-patch-1
Add Comment[zh_CN] and GenericName[zh_CN] into htop.desktop
2017-03-28 23:24:03 -03:00
53d7c66ac3 Add missing include
sys/time.h for struct timeval
2017-03-25 14:12:26 +03:00
3b1260ac3d Add Comment[zh_CN] and GenericName[zh_CN] into htop.desktop 2017-03-24 01:45:10 +08:00
fa30938247 Merge branch 'master' of https://github.com/hishamhm/htop 2017-02-15 22:49:13 -02:00
bb8dec1582 Cap battery at 100%.
Apparently invalid results can be returned by buggy drivers in old laptops,
as reported by @thukydides. See #596.
2017-02-15 22:47:03 -02:00
aa813c7561 Merge pull request #593 from kamyarrasta/issue/502sessionid
Issue #502 update Session ID column
2017-02-04 21:46:29 -02:00
3f6d1262c0 Issue #502 fix SID colunm header width 2017-02-05 00:10:29 +01:00
84bc00a275 Issue #502 update Session ID column 2017-02-01 00:03:55 +01:00
6141edc74b Merge pull request #584 from EliteTK/buffer-lengths
Replace all uses of sprintf with snprintf
2016-12-30 19:28:38 -02:00
2b5c1b4b13 Replace all uses of sprintf with snprintf
In all the cases where sprintf was being used within htop, snprintf
could have been used. This patch replaces all uses of sprintf with
snprintf which makes sure that if a buffer is too small to hold the
resulting string, the string is simply cut short instead of causing
a buffer overflow which leads to undefined behaviour.

`sizeof(variable)` was used in these cases, as opposed to `sizeof
variable` which is my personal preference because `sizeof(variable)`
was already used in one way or another in other parts of the code.
2016-12-29 19:42:41 +01:00
8af4d9f453 Interpret TTY_NR column on Linux,
translate dev_t to major:minor on other platforms.
Closes #316.
2016-10-01 03:09:04 -03:00
8a147dd5b4 Merge branch 'master' of https://github.com/hishamhm/htop 2016-09-16 13:37:44 -03:00
5df425867c Protect against strange values of SIGRTMIN and SIGRTMAX,
and change alignment of numbers.
2016-09-16 13:37:07 -03:00
46d8f2eef2 Merge pull request #551 from Explorer09/rt-signals
Real-time signals support (kill command)
2016-09-16 13:36:58 -03:00
8fd6228bc1 Merge pull request #551 from Explorer09/rt-signals
Real-time signals support (kill command)
2016-09-16 13:03:46 -03:00
0ce4835f95 Real-time signals support (for kill command)
SignalsPanel_new now fetches SIGRTMIN and SIGRTMAX and generates real-
time signals entries at runtime.

All signals between SIGRTMIN and SIGRTMAX are written in "SIGRTMIN+n"
notation, per discussion in pull request #551.

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-09-14 21:47:24 +08:00
823481ae22 Merge branch 'master' of https://github.com/hishamhm/htop 2016-09-11 23:29:21 -03:00
96157870af Downgrade requirement to Libtool 2.4.0.
Courtesy to OpenWRT environments as requested by @champtar in #540.
2016-09-11 23:28:17 -03:00
5a5a7c8d6b Merge pull request #555 from ivan/master
Fix column misalignment for priority -101 threads
2016-09-06 15:08:26 -03:00
07086fcf77 Fix column misalignment for priority -101 threads
BFS-patched kernels can have kernel threads with priority -101.
This change makes priority -101 display as "RT", just like priority -100.

Related: https://github.com/hishamhm/htop/issues/314
2016-09-06 09:22:38 +00:00
6d4df3d138 Merge branch 'master' of https://github.com/hishamhm/htop 2016-08-30 12:38:12 -03:00
d4a8023b72 Fix typos, by @Gelma.
Closes #546.
2016-08-30 12:37:31 -03:00
99f7ff9bd2 Merge pull request #550 from Explorer09/signals-tweaks
Mark signal tables 'const'
2016-08-30 12:17:20 -03:00
1f3d85b617 Mark signal tables 'const'
Specifically, Platform_signals[] and Platform_numberOfSignals. Both are
not supposed to be mutable. Marking them 'const' puts them into rodata
sections in binary. And for Platform_numberOfSignals, this aids
optimization (aids only Link Time Optimization for now). :)

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2016-08-30 20:41:17 +08:00
cceab15b9d free(NULL) is a valid no-op, so let's make String_freeArray(NULL) valid too. 2016-08-24 18:12:35 -03:00
bd5d37f297 Return when reading cmdline fails (e.g. zombie process) 2016-08-24 18:11:10 -03:00
16406ea330 Point users to the official release tarballs. 2016-08-23 15:43:27 -03:00
d3f575264b Merge pull request #537 from Explorer09/string-utils
Optimize Strings_startWith()
2016-08-11 00:44:42 -03:00
bf35921abb Optimize Strings_startWith()
Use strncmp() combined with a strlen() will give better performance
than a strstr in worst case. Especially when the match prefix is a
constant and not a variable.

While we are at it, replace the match() function in linux/Battery.c,
which uses a naive algorithm, with a macro that does better job by
utilizing Strings_startWith().

    $ gcc --version | head -n 1
    gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
    $ uname -m
    x86_64
    $ size htop.old htop.new
       text   data    bss    dec    hex filename
     137929  15112   3776 156817  26491 htop.old
     137784  15104   3776 156664  263f8 htop.new

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-08-11 10:49:35 +08:00
b269eb24b0 Merge pull request #534 from Explorer09/issue-532
Change scrolling behavior to make it more similar to other applications.
2016-08-09 13:06:33 -03:00
3d9868833e Fix scrolling behavior change caused by 759caf0f
Commit "Make PgDown behavior more usual." 759caf0f8f
silently changes the PageDown scrolling behavior that, instead of
scrolling one window down until the end of the window touches the end
of the list, the window simply repositions itself in a way that the
selected item always become the last item in the new window.

The commit reverts the behavior, and also fixes sanity conditionals
so that the scrollV variable will _never_ become negative or out-of-
bound.

Fixes issue #532. Also keep the problem #480 addressed.

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-08-07 07:49:37 +08:00
3cd0339423 Merge branch 'master' of https://github.com/hishamhm/htop 2016-07-28 11:39:15 -03:00
ef879b4a22 'Follow' only if element is found in search 2016-07-28 11:37:44 -03:00
f161365d72 Merge pull request #526 from Explorer09/bar-tweaks
Let BarMeterMode_characters[] be const array.
2016-07-23 13:48:50 -03:00
6b9b6db655 Let BarMeterMode_characters[] be const array. 2016-07-22 14:58:29 +08:00
f80e577c59 Changelog for htop 2.0.2 2016-07-21 17:12:45 -03:00
81552d4ab5 Preparing release 2.0.2, a minor bugfix release. 2016-07-21 16:54:41 -03:00
0108117d0b Mouse-friendly functions bar for meters panel
Before:
SpaceStyle EnterMove  DelDeleteEscDone  |
~~~~~      ~~~~~      ~~~      ~~~      |
UpUp     DnDown  LtLeft  RtRight EnterConfirmDelDeleteEscDone  |
~~       ~~      ~~      ~~      ~~~~~       ~~~      ~~~      |

After:
SpaceStyle EnterMove                                           DelDeleteF10Done
~~~~~      ~~~~~      ~~                                       ~~~      ~~~
SpaceStyle EnterLock  UpUp    DnDown  <-Left  ->Right          DelDeleteF10Done
~~~~~      ~~~~~      ~~      ~~      ~~      ~~      ~~       ~~~      ~~~

* Align 'Delete' and 'Done' to the right to match functions on other
  screens. (Accidental clicking is avoided as a side benefit.)
* You could change meter type while in moving mode. New bar now hints
  this.
* Two Enter key functions are put in the same place and so mouse clicks
  there act like functions toggle. (The wording change to 'Lock' is
  also to reflect this.)
* '<-' and '->' instead of 'Lt' and 'Rt' abbreviation as the latter is
  not widely seen and arrows shapes are obvious. :)
* 'Esc' key for 'Done' in this context may not be intuitive, comparing
  to 'F10'. While I wish there be a Cancel/Undo function for 'Esc', it
  wouldn't hurt if we write 'F10' for 'Done' on functions bar for now.
2016-07-16 21:13:46 +08:00
a41c9b3925 Reword 'Type' to 'Style' for meters.
Per @hishamhm's suggestion. This is UI change only.
In code the class names and symbols still refer them as "modes".
2016-07-16 21:13:46 +08:00
265d04821a Merge pull request #524 from ricardo0y/cross_compile_with_ncurses_config
Allow to override ncurses*-config path
2016-07-12 10:02:51 -03:00
78b82d0fdc Allow to override ncurses*-config path
This will be used when cross-compiling with ncurses*-config generated for the
target, using constructs like
HTOP_NCURSES_CONFIG_SCRIPT=/path/to/ncurses5-config

Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com>
2016-07-11 20:17:13 -03:00
7f9c82f28d Refresh whole panel on Ctrl+L in infoscreen.
Closes #520.
2016-06-23 13:25:58 -03:00
7d5ef246f1 Let's keep it simple then! 2016-06-20 12:11:49 -03:00
0fa03322a9 Dynamically adjust the size of line reads
* Dynamically adjust the size of line reads.
* Remove some more uses of fgets with arbitrary sizes.
* Fix reading of lines and width of n column.

Fixes #514.
2016-06-19 18:55:35 -03:00
52f814481c While we're at it, get rid of another sprintf. 2016-06-19 18:30:20 -03:00
b139671cbd Moving left and right needs a full redraw. 2016-06-19 18:05:18 -03:00
adcc944e35 Use "-" as the Ctrl and Alt joiner. 2016-06-17 22:14:07 -03:00
a5ef374bb8 Merge pull request #511 from Explorer09/man-page
Document Alt+{h,j,k,l} and Ctrl+{A,E} into man page
2016-06-17 11:50:15 -03:00
6dd32d3604 Document Alt+{h,j,k,l} and Ctrl+{A,E} into man page
Rewrite the scrolling part in the man page so that each key become clearer on
what it does. Also officially document the Alt+{h,j,k,l} key alternatives and
Ctrl+A, Ctrl+E, '^', '$' keys (see issue #508).
2016-06-17 10:19:19 +08:00
8cd045cc79 Merge branch 'master' of https://github.com/hishamhm/htop 2016-06-15 12:46:34 -03:00
0128d222b9 Added Ctrl+A and Ctrl+E to go to beginning and end of line.
(Also, '^' and '$')
Closes #508.
2016-06-15 12:45:23 -03:00
1a13b4d0f4 Don't store invisible trailing whitespace 2016-06-15 12:41:50 -03:00
9ec41d1b65 Merge pull request #505 from Explorer09/xStrdup
Fix xStrdup debug build failure & allow Clang to use ((nonnull))
2016-06-04 11:45:29 -03:00
c0e37bc5f5 Fix xStrdup debug build failure & allow Clang to use ((nonnull)) 2016-06-03 10:14:27 +08:00
f9c1db514d Merge pull request #504 from Explorer09/xStrdup
Fixes upon xStrdup(NULL) problem
2016-06-02 17:29:14 -03:00
3297616efa Add assert and __attribute__((nonnull)) on xStrdup
__attribute__((nonnull)) will help catching "calling with NULL" mistake on
compile time.

I also convert xStrdup into a macro, that will do assert() inline when the
code is *not* built with -DNDEBUG . For release builds (with -DNDEBUG),
preprocessor trick will ensure that generated code remains the same.
2016-06-02 20:38:24 +08:00
e288f690af Don't check if (!str) in xStrdup
This effectively reverts "Stricter strdup." 4674b4a732

If str is NULL upon the calling of strdup(), it should crash with SIGSEGV.
Just let it crash. Adding the "if (!str) fail();" code serves nothing but
bloat.
2016-06-02 18:30:18 +08:00
ea9dc4ab74 Merge branch 'master' of https://github.com/hishamhm/htop 2016-05-30 15:06:52 -03:00
112db9a609 Handle SIGQUIT. Closes #503. 2016-05-30 15:06:22 -03:00
5db1b0e9a0 Remove extra checks made redundant by XAlloc. 2016-05-30 12:22:33 -03:00
4674b4a732 Stricter strdup. 2016-05-30 12:22:22 -03:00
7ededce9b5 Silence cast warning. 2016-05-30 12:22:07 -03:00
3126cfce6c Merge pull request #500 from Explorer09/meter-tweaks
Remove redundant is-null checks on free(Meter.drawData)
2016-05-27 16:30:45 -03:00
313b3d3752 Remove redundant is-null checks on free(Meter.drawData) 2016-05-27 17:11:54 +08:00
fa0c637c55 Silence warnings about seteuid return value.
Closes #483.
2016-05-25 21:37:07 -03:00
b7ac416634 Fixes #498. 2016-05-25 21:29:06 -03:00
6cc0a8c820 Make sure a pointer fits in the argument! 2016-05-25 16:46:47 -03:00
645057d81a Use set_escdelay() to avoid problems with ESCDELAY as a macro. 2016-05-19 16:09:47 -03:00
6c1be63291 Fail early if libtool is not present. 2016-05-19 16:04:04 -03:00
642ccb49ca Merge pull request #445 from Explorer09/configure-ac
configure.ac fixes
2016-05-19 15:57:31 -03:00
6028e1b4c4 Merge pull request #496 from tcreech/lwp_hack
FreeBSD: fix multithreaded CPU% in process list
2016-05-19 15:44:57 -03:00
95d1984339 Merge pull request #485 from Cubox-/master
Fix FreeBSD CPU bars calculation
2016-05-16 19:19:36 -03:00
c0df404701 Update INSTALL text from autoconf-2.69 2016-05-08 14:35:20 +08:00
b71b07f5e0 Reorder configure macros to avoid "missing script" warning.
3 effects in this commit, with the first being the main one:

1. Fix the "`missing' script is too old or missing" warning. See:
   <https://lists.gnu.org/archive/html/automake/2010-08/msg00108.html>

2. By moving AC_CANONICAL_TARGET down in order, we are now able to
   set the directory for auxiliary scripts. For now it's still './'.
   I added the line "AC_CONFIG_AUX_DIR([.])" to show that the directory
   change is possible.

3. AC_USE_SYSTEM_EXTENSIONS includes checks from AC_PROG_CC, by moving
   the former macro down, we can save size in 'configure' by not
   generating repeated checks.
2016-05-08 14:35:20 +08:00
f0df28a470 Replace deprecated autoconf macros.
AC_HELP_STRING -> AS_HELP_STRING
AC_TRY_COMPILE -> AC_COMPILE_IFELSE([AC_LANG_PROGRAM([...])],...)
AC_CONFIG_HEADER -> AC_CONFIG_HEADERS
AC_PROG_LIBTOOL -> LT_INIT

Note: There might be more deprecated macros that I haven't noticed.
I just wish to avoid painful highlighting from my text editor (gedit)
that complains about them. :)
2016-05-08 14:35:20 +08:00
7d72715a6b Merge pull request #490 from Explorer09/macro-fixes
Fix macro Header_forEachColumn
2016-05-07 14:34:50 -03:00
54621e8b8f Fix macro Header_forEachColumn
The (this_) token was not expanded properly, but the bug was not caught
because all uses of this macro specifies (this_)=this .

Also parenthesize macro tokens to prevent further problems.
2016-05-07 14:57:51 +08:00
572546f806 Auto-follow process after a search.
See #237.
2016-05-05 10:30:06 -03:00
d464be13db Merge branch 'master' of https://github.com/hishamhm/htop 2016-05-04 22:43:03 -03:00
759caf0f8f Make PgDown behavior more usual.
Closes #480.
2016-05-04 22:41:52 -03:00
cdc91b0b33 Merge pull request #472 from jrtc27/hurd
Use Linux backend on the Hurd
2016-05-04 15:42:16 -03:00
1754a1cd48 Merge pull request #473 from fasterit/upstream/typo1
Fix a typo in the man page (Debian Lintian spelling-error-in-manpage)
2016-05-04 15:40:31 -03:00
8e6ffdb0ab Merge pull request #475 from fasterit/upstream/desktop-keywords
Add Keywords entry to .desktop file (Debian Lintian warning desktop-e…
2016-05-04 15:40:16 -03:00
c37be409a9 Improve reproducible builds.
Use a SOURCE_DATA_EPOCH friendly date.
Suggested by @fasterit in #476.
2016-05-04 15:34:49 -03:00
fa1b5d1e2e Fix a small undefined behavior detected by libubsan. 2016-05-04 15:34:22 -03:00
19f0f4db27 Merge pull request #488 from Explorer09/func-naming
Rename Meter.setValues() functions to updateValues()
2016-05-04 15:18:52 -03:00
9dea20e068 Rename Meter.setValues() functions to updateValues()
Rationale (copied from htop issue #471):
The function name "setValues" is misleading. For most OOP (object-
oriented programming) contexts, setXXX functions mean they will change
some member variables of an object into something specified in
function arguments. But in the *Meter_setValues() case, the new values
are not from the arguments, but from a hard-coded source. The caller
is not supposed to change the values[] to anything it likes, but
rather to "update" the values from the source. Hence, updateValues is
a better name for this family of functions.
2016-05-04 13:39:26 +08:00
2ea4bee66d Merge pull request #486 from mmcco/null-check
Remove needless allocation error conditions
2016-04-29 23:01:32 -03:00
1809f4be98 Remove needless allocation error conditions
These allocations were converted to use xMalloc et al. and no longer
need error checks.
2016-04-29 21:10:05 -04:00
5776cb56b4 Revert "Fix FreeBSD CPU% calculation"
This reverts commit f554f08fa9.
2016-04-28 21:42:18 +02:00
74f8b31040 Add Keywords entry to .desktop file (Debian Lintian warning desktop-entry-lacks-keywords-entry)
Debian patch from
https://anonscm.debian.org/cgit/collab-maint/htop.git/tree/debian/patches/002-lintian-warning-fix-desktop-keywords.patch
2016-04-19 19:06:03 +02:00
4d3f483b96 Fix a typo in the man page (Debian Lintian spelling-error-in-manpage)
Debian patch from
https://anonscm.debian.org/cgit/collab-maint/htop.git/tree/debian/patches/001-lintian-warning-fix-man-typo.patch
2016-04-19 18:32:39 +02:00
2de52862a6 Use Linux backend on the Hurd 2016-04-18 23:57:30 +01:00
a9508275cc Use $target_os instead of $target in configure.ac 2016-04-18 23:55:55 +01:00
306c5443ae Update header. 2016-03-31 11:01:23 -03:00
d15555ed2c Merge branch 'master' of https://github.com/hishamhm/htop 2016-03-31 00:19:59 -03:00
d64f2bdd56 If task_for_pid fails, stop trying. 2016-03-31 00:18:42 -03:00
6c2f698a47 Merge pull request #458 from Explorer09/bar-tweaks
BarMeterMode_draw() minor code improvement
2016-03-25 01:57:10 -03:00
7b3c8bc77a BarMeterMode_draw minor code improvement
Removed a loop that sets the bar[] buffer with spaces and merged that
task to the snprintf() call just below. No need for the barOffset
variable. Display behavior is unchanged.

Size comparision (when compiled on Ubuntu 14.04 64-bit):

    $ size htop.old htop.new
       text    data     bss     dec     hex filename
     137312   15112    3776  156200   26228 htop.old
     137216   15112    3776  156104   261c8 htop.new
2016-03-22 14:52:31 +08:00
3a4c0fa2d6 Merge pull request #452 from Explorer09/cpu-meter-tweaks
Assert (Platform_meterTypes[0] == &CPUMeter_class)
2016-03-20 05:02:07 -03:00
328de35623 Assert (Platform_meterTypes[0]==&CPUMeter_class)
Just assume Platform_meterTypes[0] is always &CPUMeter_class for every
platform. This removes a conditional in AvailableMetersPanel_new().

Also adds some comments about the logic here. Without assuming
Platform_meterTypes[0], the (int i=1) clause in this for loop will not
make sense. (I.e. Why not (int i=0)? )

Also replaced a sprintf() call with safer snprintf() in code further
below.
2016-03-19 15:01:13 +08:00
c8a735e471 Merge pull request #444 from Explorer09/meter-maxitems
Explicit "maxItems" property of meters
2016-03-13 20:57:08 -03:00
99fb3070a2 Explicit "maxItems" property of meters
Two changes in this commit:
- All meters now explicitly specify "maxItems" property, even for just
  1 item. (Exception is "container" CPU meter classes, which use
  CUSTOM_METERMODE.)
- "maxItems" being 0 is now allowed. This will let bar meters and graph
  meters render an empty meter.
2016-03-11 10:54:34 +08:00
7d3f67e822 Revert 5c593fae42 (xCalloc)
calloc() allows 'nmemb' or 'size' to be zero, in which case NULL may be
returned. Letting htop die because of either argument being zero doesn't
make sense.

As a side note: As size_t is unsigned, compiler should be able to optimize
conditional (nmemb > 0 && size > 0) to (nmemb && size). This theorically
shouldn't increase code size too much.
2016-03-11 10:43:31 +08:00
3283c6d23c Merge pull request #441 from Explorer09/issue-438
Avoid overlapping key values defined by curses (Real fix).
2016-03-09 02:03:11 -03:00
8a928c8b89 Avoid overlapping key values defined by curses (Real fix).
Real fix for issue #438.
2016-03-09 10:16:34 +08:00
f295a52ed9 Avoid overlapping key values defined by curses.
Closes #438.
2016-03-08 12:23:18 -03:00
cc8375f9ea FreeBSD: use KERN_PROC_PROC with kvm_getprocs to avoid erroneously combining LWPs 2016-02-28 22:41:50 -05:00
105 changed files with 4018 additions and 836 deletions

8
.gitignore vendored
View File

@ -4,6 +4,12 @@ htop
# all object files
*.o
# skip all backups
*.bak
*~
.*.sw?
# skip coverage files
*.gcda
*/*.gcda
*.gcno
@ -22,6 +28,7 @@ config.h
config.h.in
config.log
config.status
config.cache
config.sub
configure
depcomp
@ -32,4 +39,3 @@ ltmain.sh
m4/
missing
stamp-h1

View File

@ -142,7 +142,7 @@ static void tagAllChildren(Panel* panel, Process* parent) {
pid_t ppid = parent->pid;
for (int i = 0; i < Panel_size(panel); i++) {
Process* p = (Process*) Panel_get(panel, i);
if (!p->tag && p->ppid == ppid) {
if (!p->tag && Process_isChildOf(p, ppid)) {
tagAllChildren(panel, p);
}
}
@ -156,9 +156,10 @@ static bool expandCollapse(Panel* panel) {
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
settings->treeView = false;
ScreenSettings* ss = settings->ss;
ss->sortKey = sortKey;
ss->direction = 1;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
@ -166,11 +167,12 @@ static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields;
ScreenSettings* ss = st->settings->ss;
ProcessField* fields = ss->fields;
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] == ss->sortKey)
Panel_setSelected(sortPanel, i);
free(name);
}
@ -218,8 +220,9 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
}
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) st->settings->direction = 1;
ScreenSettings* ss = st->settings->ss;
ss->treeView = !ss->treeView;
if (ss->treeView) ss->direction = 1;
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@ -247,7 +250,7 @@ static Htop_Reaction actionLowerPriority(State* st) {
}
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
ScreenSettings_invertSortOrder(st->settings->ss);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
@ -261,13 +264,23 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
@ -328,7 +341,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionFollow(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]);
return HTOP_KEEP_FOLLOWING;
@ -381,7 +394,7 @@ static Htop_Reaction actionRedraw() {
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static struct { const char* key; const char* info; } helpLeft[] = {
static const struct { const char* key; const char* info; } helpLeft[] = {
{ .key = " Arrows: ", .info = "scroll process list" },
{ .key = " Digits: ", .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" },
@ -395,11 +408,11 @@ static struct { const char* key; const char* info; } helpLeft[] = {
{ .key = " F6 + -: ", .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" },
{ .key = " F6 > .: ", .info = "select sort column" },
{ .key = NULL, .info = NULL }
};
static struct { const char* key; const char* info; } helpRight[] = {
static const struct { const char* key; const char* info; } helpRight[] = {
{ .key = " Space: ", .info = "tag process" },
{ .key = " c: ", .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" },
@ -414,7 +427,7 @@ static struct { const char* key; const char* info; } helpRight[] = {
{ .key = " l: ", .info = "list open files with lsof" },
{ .key = " s: ", .info = "trace syscalls with strace" },
{ .key = " ", .info = "" },
{ .key = " F2 S: ", .info = "setup" },
{ .key = " F2 C S: ", .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" },
{ .key = NULL, .info = NULL }
@ -557,7 +570,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['u'] = actionFilterByUser;
keys['F'] = actionFollow;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
@ -571,5 +584,6 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
}

View File

@ -49,6 +49,8 @@ Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ----------------------------------------
Htop_Reaction Action_follow(State* st);
void Action_setBindings(Htop_Action* keys);

View File

@ -52,7 +52,7 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity) {
int curCpu = 0;
for (int i = 0; i < pl->cpuCount; i++) {
char number[10];
snprintf(number, 9, "%d", Settings_cpuId(pl->settings, i));
xSnprintf(number, 9, "%d", Settings_cpuId(pl->settings, i));
bool mode;
if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
mode = true;

View File

@ -26,7 +26,7 @@ typedef struct AvailableColumnsPanel_ {
}*/
static const char* AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
static void AvailableColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -81,7 +81,7 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
for (int i = 1; i < Platform_numberOfFields; i++) {
if (i != COMM && Process_fields[i].description) {
char description[256];
snprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
Panel_add(super, (Object*) ListItem_new(description, i));
}
}

View File

@ -112,20 +112,22 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
this->scr = scr;
Panel_setHeader(super, "Available meters");
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will
// handle separately in the code below.
for (int i = 1; Platform_meterTypes[i]; i++) {
MeterClass* type = Platform_meterTypes[i];
if (type != &CPUMeter_class) {
assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16));
}
}
// Handle (&CPUMeter_class)
MeterClass* type = &CPUMeter_class;
int cpus = pl->cpuCount;
if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) {
char buffer[50];
sprintf(buffer, "%s %d", type->uiName, i);
xSnprintf(buffer, 50, "%s %d", type->uiName, i);
Panel_add(super, (Object*) ListItem_new(buffer, i));
}
} else {

View File

@ -32,7 +32,7 @@ int BatteryMeter_attributes[] = {
BATTERY
};
static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
ACPresence isOnAC;
double percent;
@ -40,7 +40,7 @@ static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
if (percent == -1) {
this->values[0] = 0;
snprintf(buffer, len, "n/a");
xSnprintf(buffer, len, "n/a");
return;
}
@ -58,11 +58,11 @@ static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
}
if (isOnAC == AC_PRESENT) {
snprintf(buffer, len, onAcText, percent);
xSnprintf(buffer, len, onAcText, percent);
} else if (isOnAC == AC_ABSENT) {
snprintf(buffer, len, onBatteryText, percent);
xSnprintf(buffer, len, onBatteryText, percent);
} else {
snprintf(buffer, len, unknownText, percent);
xSnprintf(buffer, len, unknownText, percent);
}
return;
@ -73,8 +73,9 @@ MeterClass BatteryMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = BatteryMeter_setValues,
.updateValues = BatteryMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = BatteryMeter_attributes,
.name = "Battery",

View File

@ -48,22 +48,22 @@ static void CPUMeter_init(Meter* this) {
int cpu = this->param;
if (this->pl->cpuCount > 1) {
char caption[10];
sprintf(caption, "%-3d", Settings_cpuId(this->pl->settings, cpu - 1));
xSnprintf(caption, sizeof(caption), "%-3d", Settings_cpuId(this->pl->settings, cpu - 1));
Meter_setCaption(this, caption);
}
if (this->param == 0)
Meter_setCaption(this, "Avg");
}
static void CPUMeter_setValues(Meter* this, char* buffer, int size) {
static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
int cpu = this->param;
if (cpu > this->pl->cpuCount) {
snprintf(buffer, size, "absent");
xSnprintf(buffer, size, "absent");
return;
}
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
double percent = Platform_setCPUValues(this, cpu);
snprintf(buffer, size, "%5.1f%%", percent);
xSnprintf(buffer, size, "%5.1f%%", percent);
}
static void CPUMeter_display(Object* cast, RichString* out) {
@ -74,44 +74,44 @@ static void CPUMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[METER_TEXT], "absent");
return;
}
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_NORMAL]);
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);
if (this->pl->settings->detailedCPUTime) {
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_KERNEL]);
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_KERNEL], buffer);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_NICE]);
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);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_IRQ]);
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);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
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);
if (this->values[CPU_METER_STEAL]) {
sprintf(buffer, "%5.1f%% ", 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);
}
if (this->values[CPU_METER_GUEST]) {
sprintf(buffer, "%5.1f%% ", 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);
}
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
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);
} else {
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_KERNEL]);
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_KERNEL], buffer);
sprintf(buffer, "%5.1f%% ", this->values[CPU_METER_NICE]);
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);
if (this->values[CPU_METER_IRQ]) {
sprintf(buffer, "%5.1f%% ", 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);
}
@ -215,7 +215,7 @@ MeterClass CPUMeter_class = {
.delete = Meter_delete,
.display = CPUMeter_display
},
.setValues = CPUMeter_setValues,
.updateValues = CPUMeter_updateValues,
.defaultMode = BAR_METERMODE,
.maxItems = CPU_METER_ITEMCOUNT,
.total = 100.0,
@ -312,8 +312,8 @@ MeterClass LeftCPUs2Meter_class = {
.total = 100.0,
.attributes = CPUMeter_attributes,
.name = "LeftCPUs2",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.uiName = "CPUs (1&2/4)",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init,

105
CRT.c
View File

@ -5,6 +5,7 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "CRT.h"
#include "StringUtils.h"
@ -17,8 +18,14 @@ in the source distribution for its full text.
#include <string.h>
#include <locale.h>
#include <langinfo.h>
#if HAVE_SETUID_ENABLED
#include <unistd.h>
#include <sys/types.h>
#endif
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
#define ColorIndex(i,j) ((7-i)*8+j)
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
#define Black COLOR_BLACK
#define Red COLOR_RED
@ -29,6 +36,9 @@ in the source distribution for its full text.
#define Cyan COLOR_CYAN
#define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
@ -118,6 +128,7 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT
} ColorElements;
@ -125,7 +136,7 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) KEY_F(60) + (x - 'A')
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
}*/
@ -183,7 +194,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LED_COLOR] = ColorPair(Green,Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black),
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow,Black),
[PROCESS_MEGABYTES] = ColorPair(Cyan,Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan,Black),
@ -195,7 +206,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_THREAD] = ColorPair(Green,Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green,Black),
[BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_BOLD | ColorPair(Black,Black),
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
[SWAP] = ColorPair(Red,Black),
[GRAPH_1] = A_BOLD | ColorPair(Cyan,Black),
[GRAPH_2] = ColorPair(Cyan,Black),
@ -222,6 +233,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Magenta,Black),
[CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL,
@ -281,6 +293,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE,
[PANEL_EDIT] = A_BOLD,
},
[COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White),
@ -340,6 +353,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,White),
[CPU_STEAL] = ColorPair(Cyan,White),
[CPU_GUEST] = ColorPair(Cyan,White),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black),
@ -360,7 +374,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LED_COLOR] = ColorPair(Green,Black),
[TASKS_RUNNING] = ColorPair(Green,Black),
[PROCESS] = ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = ColorPair(White,Blue),
[PROCESS_MEGABYTES] = ColorPair(Blue,Black),
[PROCESS_BASENAME] = ColorPair(Green,Black),
@ -372,7 +386,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PROCESS_THREAD] = ColorPair(Blue,Black),
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue,Black),
[BAR_BORDER] = ColorPair(Blue,Black),
[BAR_SHADOW] = ColorPair(Black,Black),
[BAR_SHADOW] = ColorPairGrayBlack,
[SWAP] = ColorPair(Red,Black),
[GRAPH_1] = A_BOLD | ColorPair(Cyan,Black),
[GRAPH_2] = ColorPair(Cyan,Black),
@ -399,6 +413,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Black,Black),
[CPU_GUEST] = ColorPair(Black,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue),
@ -458,6 +473,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Black,Blue),
[CPU_STEAL] = ColorPair(White,Blue),
[CPU_GUEST] = ColorPair(White,Blue),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black),
@ -478,7 +494,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LED_COLOR] = ColorPair(Green,Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black),
[PROCESS] = ColorPair(Cyan,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPair(Black,Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = A_BOLD | ColorPair(Yellow,Black),
[PROCESS_MEGABYTES] = A_BOLD | ColorPair(Green,Black),
[PROCESS_BASENAME] = A_BOLD | ColorPair(Green,Black),
@ -517,12 +533,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Cyan),
},
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
};
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;
@ -541,6 +556,48 @@ static void CRT_handleSIGTERM(int sgn) {
exit(0);
}
#if HAVE_SETUID_ENABLED
static int CRT_euid = -1;
static int CRT_egid = -1;
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges() {
CRT_egid = getegid();
CRT_euid = geteuid();
if (setegid(getgid()) == -1) {
DIE("Fatal error: failed dropping group privileges.\n");
}
if (seteuid(getuid()) == -1) {
DIE("Fatal error: failed dropping user privileges.\n");
}
}
void CRT_restorePrivileges() {
if (CRT_egid == -1 || CRT_euid == -1) {
DIE("Fatal error: internal inconsistency.\n");
}
if (setegid(CRT_egid) == -1) {
DIE("Fatal error: failed restoring group privileges.\n");
}
if (seteuid(CRT_euid) == -1) {
DIE("Fatal error: failed restoring user privileges.\n");
}
}
#else
/* Turn setuid operations into NOPs */
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
// TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme) {
@ -555,7 +612,7 @@ void CRT_init(int delay, int colorScheme) {
for (int i = 0; i < LAST_COLORELEMENT; i++) {
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPair(Black,Black)) ? ColorPair(White,Black) : color;
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White,Black) : color;
}
halfdelay(CRT_delay);
@ -575,7 +632,7 @@ void CRT_init(int delay, int colorScheme) {
CRT_scrollHAmount = 20;
else
CRT_scrollHAmount = 5;
if (String_eq(CRT_termType, "xterm") || String_eq(CRT_termType, "xterm-color") || String_eq(CRT_termType, "vt220")) {
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) {
define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME);
@ -599,6 +656,7 @@ void CRT_init(int delay, int colorScheme) {
signal(11, CRT_handleSIGSEGV);
#endif
signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
use_default_colors();
if (!has_colors())
CRT_colorScheme = 1;
@ -661,14 +719,23 @@ void CRT_enableDelay() {
void CRT_setColors(int colorScheme) {
CRT_colorScheme = colorScheme;
if (colorScheme == COLORSCHEME_BLACKNIGHT) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
init_pair((7-i)*8+j, i, j);
} else {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
init_pair((7-i)*8+j, i, (j==0?-1:j));
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (ColorIndex(i,j) != ColorPairGrayBlack) {
int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? (j==0 ? -1 : j)
: j;
init_pair(ColorIndex(i,j), i, bg);
}
}
}
int grayBlackFg = COLORS > 8 ? 8 : 0;
int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? -1
: 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
CRT_colors = CRT_colorSchemes[colorScheme];
}

34
CRT.h
View File

@ -9,7 +9,12 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
#if HAVE_SETUID_ENABLED
#endif
#define ColorIndex(i,j) ((7-i)*8+j)
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
#define Black COLOR_BLACK
#define Red COLOR_RED
@ -20,6 +25,9 @@ in the source distribution for its full text.
#define Cyan COLOR_CYAN
#define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
@ -108,6 +116,7 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT
} ColorElements;
@ -115,7 +124,7 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) KEY_F(60) + (x - 'A')
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
@ -136,8 +145,6 @@ int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
@ -150,6 +157,25 @@ extern int CRT_colorScheme;
void *backtraceArray[128];
#if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges();
void CRT_restorePrivileges();
#else
/* Turn setuid operations into NOPs */
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
// TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme);

View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "DisplayOptionsPanel.h"
#include "ColumnsPanel.h"
#include "ScreensPanel.h"
#include "ColorsPanel.h"
#include "AvailableColumnsPanel.h"
@ -34,7 +34,7 @@ typedef struct CategoriesPanel_ {
}*/
static const char* CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static void CategoriesPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -64,9 +64,11 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1);
}
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Panel* screens = (Panel*) ScreensPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}
@ -82,9 +84,9 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
result = HANDLED;
break;
case KEY_UP:
case KEY_CTRLP:
case KEY_CTRL('P'):
case KEY_DOWN:
case KEY_CTRLN:
case KEY_CTRL('N'):
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_HOME:
@ -118,7 +120,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel_makeColorsPage(this);
break;
case 3:
CategoriesPanel_makeColumnsPage(this);
CategoriesPanel_makeScreensPage(this);
break;
}
}
@ -147,6 +149,6 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0));
Panel_add(super, (Object*) ListItem_new("Screens", 0));
return this;
}

View File

@ -1,3 +1,60 @@
What's new in version 2.1.1
* Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report)
What's new in version 2.1.0
* Linux: Delay accounting metrics
(thanks to André Carvalho)
* DragonFlyBSD support
(thanks to Diederik de Groot)
* Support for real-time signals
(thanks to Kang-Che Sung)
* 'c' key now works with threads as well
* Session column renamed from SESN to SID
(thanks to Kamyar Rasta)
* Improved UI for meter style selection
(thanks to Kang-Che Sung)
* Improved code for constructing process tree
(thanks to wangqr)
* Compile-time option to disable setuid
* Error checking of various standard library operations
* Replacement of sprintf with snprintf
(thanks to Tomasz Kramkowski)
* Linux: performance improvements in battery meter
* Linux: update process TTY device
* Linux: add support for sorting TASK_IDLE
(thanks to Vladimir Panteleev)
* Linux: add upper-bound to running process counter
(thanks to Lucas Correia Villa Real)
* BUGFIX: avoid crash when battery is removed
(thanks to Jan Chren)
* BUGFIX: macOS: fix infinite loop in tree view
(thanks to Wataru Ashihara)
What's new in version 2.0.2
* Mac OS X: stop trying when task_for_pid fails for a process,
stops spamming logs with errors.
* Add Ctrl+A and Ctrl+E to go to beginning and end of line
* FreeBSD: fixes for CPU calculation
(thanks to Tim Creech, Andy Pilate)
* Usability: auto-follow process after a search.
* Use Linux backend on GNU Hurd
* Improvement for reproducible builds.
* BUGFIX: Fix behavior of Alt-key combinations
(thanks to Kang-Che Sung)
* Various code tweaks and cleanups
(thanks to Kang-Che Sung)
What's new in version 2.0.1

View File

@ -19,7 +19,7 @@ int ClockMeter_attributes[] = {
CLOCK
};
static void ClockMeter_setValues(Meter* this, char* buffer, int size) {
static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
time_t t = time(NULL);
struct tm result;
struct tm *lt = localtime_r(&t, &result);
@ -32,8 +32,9 @@ MeterClass ClockMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = ClockMeter_setValues,
.updateValues = ClockMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 1440, /* 24*60 */
.attributes = ClockMeter_attributes,
.name = "Clock",

View File

@ -34,9 +34,9 @@ typedef struct ColorsPanel_ {
}*/
static const char* ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* const ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* ColorSchemeNames[] = {
static const char* const ColorSchemeNames[] = {
"Default",
"Monochromatic",
"Black on White",
@ -78,6 +78,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
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);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);

View File

@ -22,14 +22,15 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
}*/
static const char* ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -65,8 +66,8 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(7):
case '[':
case '-':
@ -81,8 +82,8 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(8):
case ']':
case '+':
@ -123,22 +124,31 @@ PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler
};
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss) {
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->fields;
Panel_prune(super);
ProcessField* fields = ss->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
this->ss = ss;
}
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->ss = ss;
this->changed = changed;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ColumnsPanel_fill(this, ss);
return this;
}
@ -154,14 +164,14 @@ int ColumnsPanel_fieldNameToIndex(const char* name) {
void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
this->settings->flags = 0;
*(this->changed) = true;
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size+1));
this->ss->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
this->settings->fields[i] = key;
this->settings->flags |= Process_fields[key].flags;
this->ss->fields[i] = key;
this->ss->flags |= key < 1000 ? Process_fields[key].flags : 0;
}
this->settings->fields[size] = 0;
this->ss->fields[size] = 0;
}

View File

@ -14,15 +14,18 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
extern PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss);
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed);
int ColumnsPanel_fieldNameToIndex(const char* name);

View File

@ -28,7 +28,7 @@ typedef struct DisplayOptionsPanel_ {
}*/
static const char* DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static void DisplayOptionsPanel_delete(Object* object) {
Panel* super = (Panel*) object;
@ -83,7 +83,6 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));

View File

@ -48,10 +48,9 @@ void EnvScreen_scan(InfoScreen* this) {
Panel_prune(panel);
uid_t euid = geteuid();
seteuid(getuid());
char *env = Platform_getProcessEnv(this->process->pid);
seteuid(euid);
CRT_dropPrivileges();
char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges();
if (env) {
for (char *p = env; *p; p = strrchr(p, 0)+1)
InfoScreen_addLine(this, p);

View File

@ -28,21 +28,21 @@ typedef struct FunctionBar_ {
}*/
static const char* FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
static const char* FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL};
static const char* const FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL};
static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)};
static const char* FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};
static int FunctionBar_EnterEscEvents[] = {13, 27};
static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};
static const int FunctionBar_EnterEscEvents[] = {13, 27};
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) {
const char* functions[] = {enter, esc, NULL};
return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents);
}
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events) {
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events) {
FunctionBar* this = xCalloc(1, sizeof(FunctionBar));
this->functions = xCalloc(16, sizeof(char*));
if (!functions) {
@ -96,11 +96,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
void FunctionBar_draw(const FunctionBar* this, char* buffer) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
int FunctionBar_draw(const FunctionBar* this, char* buffer) {
return FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
}
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int cursorX = 0;
attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS);
int x = 0;
@ -115,12 +116,10 @@ void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
if (buffer) {
attrset(attr);
mvaddstr(LINES-1, x, buffer);
CRT_cursorX = x + strlen(buffer);
curs_set(1);
} else {
curs_set(0);
cursorX = x + strlen(buffer);
}
attrset(CRT_colors[RESET_COLOR]);
return cursorX;
}
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -24,15 +24,15 @@ typedef struct FunctionBar_ {
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events);
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_draw(const FunctionBar* this, char* buffer);
int FunctionBar_draw(const FunctionBar* this, char* buffer);
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

View File

@ -37,7 +37,7 @@ typedef struct Header_ {
#endif
#ifndef Header_forEachColumn
#define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++)
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {
@ -62,7 +62,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) {
@ -75,7 +75,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
String_freeArray(colSettings->names);
free(colSettings->modes);
@ -91,9 +91,9 @@ void Header_writeBackToSettings(const Header* this) {
Meter* meter = (Meter*) Vector_get(vec, i);
char* name = xCalloc(64, sizeof(char));
if (meter->param) {
snprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param);
xSnprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param);
} else {
snprintf(name, 63, "%s", As_Meter(meter)->name);
xSnprintf(name, 63, "%s", As_Meter(meter)->name);
}
colSettings->names[i] = name;
colSettings->modes[i] = meter->mode;
@ -155,7 +155,7 @@ char* Header_readMeterName(Header* this, int i, int column) {
strncpy(name, Meter_name(meter), nameLen);
name[nameLen] = '\0';
if (meter->param)
snprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
return name;
}

View File

@ -28,7 +28,7 @@ typedef struct Header_ {
#endif
#ifndef Header_forEachColumn
#define Header_forEachColumn(this_, i_) for (int i_=0; i_ < this->nrColumns; i_++)
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);

View File

@ -19,7 +19,7 @@ int HostnameMeter_attributes[] = {
HOSTNAME
};
static void HostnameMeter_setValues(Meter* this, char* buffer, int size) {
static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this;
gethostname(buffer, size-1);
}
@ -29,8 +29,9 @@ MeterClass HostnameMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = HostnameMeter_setValues,
.updateValues = HostnameMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0,
.attributes = HostnameMeter_attributes,
.name = "Hostname",

View File

@ -12,8 +12,8 @@ without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
Briefly, the shell command `./configure && make && make install'
should configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented

View File

@ -38,8 +38,10 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
@ -51,8 +53,8 @@ static void IncMode_reset(IncMode* mode) {
mode->buffer[0] = 0;
}
static const char* searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* searchKeys[] = {"F3", "Esc", " "};
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 inline void IncMode_initSearch(IncMode* search) {
@ -61,8 +63,8 @@ static inline void IncMode_initSearch(IncMode* search) {
search->isFilter = false;
}
static const char* filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* filterKeys[] = {"Enter", "Esc", " "};
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* const filterKeys[] = {"Enter", "Esc", " "};
static int filterEvents[] = {13, 27, ERR};
static inline void IncMode_initFilter(IncMode* filter) {
@ -114,20 +116,33 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
}
}
static void search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
static bool search(IncSet* this, 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)) {
if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) {
Panel_setSelected(panel, i);
found = true;
break;
}
}
if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]);
return found;
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
panel->cursorOn = true;
this->panel = panel;
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
}
static void IncSet_deactivate(IncSet* this, Panel* panel) {
this->active = NULL;
Panel_setDefaultBar(panel);
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar, NULL);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
@ -151,7 +166,8 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
}
}
doSearch = false;
} else if (ch < 255 && isprint((char)ch) && (mode->index < INCMODE_MAX)) {
} else if (ch < 255 && isprint((char)ch)) {
if (mode->index < INCMODE_MAX) {
mode->buffer[mode->index] = ch;
mode->index++;
mode->buffer[mode->index] = 0;
@ -159,7 +175,9 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
filterChanged = true;
if (mode->index == 1) this->filtering = true;
}
} else if ((ch == KEY_BACKSPACE || ch == 127) && (mode->index > 0)) {
}
} else if ((ch == KEY_BACKSPACE || ch == 127)) {
if (mode->index > 0) {
mode->index--;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
@ -169,6 +187,9 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode);
}
}
} else {
doSearch = false;
}
} else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES-panel->y-1);
} else {
@ -181,13 +202,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} else {
IncMode_reset(mode);
}
this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
IncSet_deactivate(this, panel);
doSearch = false;
}
if (doSearch) {
search(mode, panel, getPanelValue);
this->found = search(this, panel, getPanelValue);
}
if (filterChanged && lines) {
updateWeakPanel(this, panel, lines);
@ -202,15 +221,11 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
void IncSet_drawBar(IncSet* this, int attr) {
if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer);
int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else {
FunctionBar_draw(this->defaultBar, NULL);
}

View File

@ -33,8 +33,10 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
} IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
@ -44,13 +46,13 @@ IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(IncSet* this);
void IncSet_drawBar(IncSet* this, int attr);
int IncSet_synthesizeEvent(IncSet* this, int x);

View File

@ -50,9 +50,9 @@ struct InfoScreen_ {
};
}*/
static const char* InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
static const char* InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
@ -83,8 +83,9 @@ void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
wmove(stdscr, 0, 0);
vw_printw(stdscr, fmt, ap);
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
va_end(ap);
}
@ -114,10 +115,7 @@ void InfoScreen_run(InfoScreen* this) {
Panel_draw(panel, true);
if (this->inc->active)
move(LINES-1, CRT_cursorX);
ESCDELAY = 25;
int ch = getch();
int ch = Panel_getCh(panel);
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {

View File

@ -27,19 +27,19 @@ typedef struct ListItem_ {
}*/
static void ListItem_delete(Object* cast) {
void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast;
free(this->value);
free(this);
}
static void ListItem_display(Object* cast, RichString* out) {
void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast;
assert (this != NULL);
/*
int len = strlen(this->value)+1;
char buffer[len+1];
snprintf(buffer, len, "%s", this->value);
xSnprintf(buffer, len, "%s", this->value);
*/
if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR],
@ -59,11 +59,15 @@ ObjectClass ListItem_class = {
.compare = ListItem_compare
};
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
void ListItem_init(ListItem* this, const char* value, int key) {
this->value = xStrdup(value);
this->key = key;
this->moving = false;
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
return this;
}

View File

@ -19,8 +19,14 @@ typedef struct ListItem_ {
} ListItem;
void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
extern ObjectClass ListItem_class;
void ListItem_init(ListItem* this, const char* value, int key);
ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text);

View File

@ -20,35 +20,35 @@ int LoadAverageMeter_attributes[] = {
int LoadMeter_attributes[] = { LOAD };
static void LoadAverageMeter_setValues(Meter* this, char* buffer, int size) {
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
snprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
}
static void LoadAverageMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
sprintf(buffer, "%.2f ", this->values[0]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
sprintf(buffer, "%.2f ", this->values[1]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
sprintf(buffer, "%.2f ", this->values[2]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
}
static void LoadMeter_setValues(Meter* this, char* buffer, int size) {
static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
double five, fifteen;
Platform_getLoadAverage(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) {
this->total = this->values[0];
}
snprintf(buffer, size, "%.2f", this->values[0]);
xSnprintf(buffer, size, "%.2f", this->values[0]);
}
static void LoadMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast;
char buffer[20];
sprintf(buffer, "%.2f ", ((Meter*)this)->values[0]);
xSnprintf(buffer, sizeof(buffer), "%.2f ", ((Meter*)this)->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer);
}
@ -58,7 +58,7 @@ MeterClass LoadAverageMeter_class = {
.delete = Meter_delete,
.display = LoadAverageMeter_display,
},
.setValues = LoadAverageMeter_setValues,
.updateValues = LoadAverageMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 3,
.total = 100.0,
@ -75,8 +75,9 @@ MeterClass LoadMeter_class = {
.delete = Meter_delete,
.display = LoadMeter_display,
},
.setValues = LoadMeter_setValues,
.updateValues = LoadMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = LoadMeter_attributes,
.name = "Load",

View File

@ -31,7 +31,7 @@ typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t);
}*/
static const char* MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
@ -67,15 +67,17 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
if (field == ss->sortKey) {
ScreenSettings_invertSortOrder(ss);
ss->treeView = false;
} else {
reaction |= Action_setSortKey(settings, field);
}
@ -87,7 +89,10 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
this->state->pl->incFilter = IncSet_filter(this->inc);
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
}
if (this->inc->found) {
reaction |= Action_follow(this->state);
reaction |= HTOP_KEEP_FOLLOWING;
}
result = HANDLED;
} else if (ch == 27) {
return HANDLED;
@ -102,25 +107,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} else {
reaction |= HTOP_KEEP_FOLLOWING;
}
switch (ch) {
case KEY_LEFT:
case KEY_CTRLB:
if (super->scrollH > 0) {
super->scrollH -= CRT_scrollHAmount;
super->needsRedraw = true;
}
return HANDLED;
case KEY_RIGHT:
case KEY_CTRLF:
super->scrollH += CRT_scrollHAmount;
super->needsRedraw = true;
return HANDLED;
}
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
MainPanel_updateTreeFunctions(this, settings->ss->treeView);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
@ -159,7 +150,7 @@ const char* MainPanel_getValue(MainPanel* this, int i) {
return "";
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, int arg, bool* wasAnyTagged) {
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, size_t arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this;
bool ok = true;
bool anyTagged = false;

View File

@ -34,7 +34,7 @@ int MainPanel_selectedPid(MainPanel* this);
const char* MainPanel_getValue(MainPanel* this, int i);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, int arg, bool* wasAnyTagged);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, size_t arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class;

View File

@ -21,7 +21,7 @@ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c ScreensPanel.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c
@ -31,7 +31,7 @@ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h ScreensPanel.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h
@ -39,10 +39,12 @@ EnvScreen.h InfoScreen.h XAlloc.h
if HTOP_LINUX
htop_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PerfCounter.c
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h \
linux/PerfCounter.h
endif
if HTOP_FREEBSD
@ -53,6 +55,15 @@ myhtopplatheaders = freebsd/Platform.h freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h
endif
if HTOP_DRAGONFLYBSD
htop_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = dragonflybsd/Platform.h dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDCRT.h dragonflybsd/Battery.h
endif
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
@ -94,6 +105,9 @@ profile:
debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"

View File

@ -24,7 +24,7 @@ int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE
};
static void MemoryMeter_setValues(Meter* this, char* buffer, int size) {
static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
int written;
Platform_setMemoryValues(this);
@ -60,7 +60,7 @@ MeterClass MemoryMeter_class = {
.delete = Meter_delete,
.display = MemoryMeter_display,
},
.setValues = MemoryMeter_setValues,
.updateValues = MemoryMeter_updateValues,
.defaultMode = BAR_METERMODE,
.maxItems = 3,
.total = 100.0,

61
Meter.c
View File

@ -37,7 +37,7 @@ typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_SetValues)(Meter*, char*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ {
@ -46,7 +46,7 @@ typedef struct MeterClass_ {
const Meter_Done done;
const Meter_UpdateMode updateMode;
const Meter_Draw draw;
const Meter_SetValues setValues;
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
@ -66,7 +66,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_)
#define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)
@ -132,12 +133,8 @@ Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) {
this->h = 1;
this->param = param;
this->pl = pl;
char maxItems = type->maxItems;
if (maxItems == 0) {
maxItems = 1;
}
type->curItems = maxItems;
this->values = xCalloc(maxItems, sizeof(double));
type->curItems = type->maxItems;
this->values = xCalloc(type->maxItems, sizeof(double));
this->total = type->total;
this->caption = xStrdup(type->caption);
if (Meter_initFn(this))
@ -155,7 +152,7 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
if (value / 1024 < powi)
break;
if (prefix[1] == 0)
if (prefix[1] == '\0')
break;
powi *= 1024;
@ -184,7 +181,6 @@ void Meter_delete(Object* cast) {
if (Meter_doneFn(this)) {
Meter_done(this);
}
if (this->drawData)
free(this->drawData);
free(this->caption);
free(this->values);
@ -216,7 +212,6 @@ void Meter_setMode(Meter* this, int modeIndex) {
Meter_updateMode(this, modeIndex);
} else {
assert(modeIndex >= 1);
if (this->drawData)
free(this->drawData);
this->drawData = NULL;
@ -230,16 +225,16 @@ void Meter_setMode(Meter* this, int modeIndex) {
ListItem* Meter_toListItem(Meter* this, bool moving) {
char mode[21];
if (this->mode)
snprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
xSnprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
else
mode[0] = '\0';
char number[11];
if (this->param > 0)
snprintf(number, 10, " %d", this->param);
xSnprintf(number, 10, " %d", this->param);
else
number[0] = '\0';
char buffer[51];
snprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
xSnprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
ListItem* li = ListItem_new(buffer, 0);
li->moving = moving;
return li;
@ -249,7 +244,7 @@ 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_setValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
(void) w;
attrset(CRT_colors[METER_TEXT]);
@ -265,11 +260,11 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- BarMeterMode ---------- */
static char BarMeterMode_characters[] = "|#*@$%&.";
static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
w -= 2;
attrset(CRT_colors[METER_TEXT]);
@ -291,11 +286,8 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char bar[w + 1];
int blockSizes[10];
for (int i = 0; i < w; i++)
bar[i] = ' ';
const size_t barOffset = w - MIN((int)strlen(buffer), w);
snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
// First draw in the bar[] buffer...
int offset = 0;
@ -344,7 +336,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
#ifdef HAVE_LIBNCURSESW
#define PIXPERROW_UTF8 4
static const char* GraphMeterMode_dotsUtf8[] = {
static const char* const GraphMeterMode_dotsUtf8[] = {
/*00*/" ", /*01*/"", /*02*/"", /*03*/"", /*04*/ "",
/*10*/"", /*11*/"", /*12*/"", /*13*/"", /*14*/ "",
/*20*/"", /*21*/"", /*22*/"", /*23*/"", /*24*/ "",
@ -355,13 +347,13 @@ static const char* GraphMeterMode_dotsUtf8[] = {
#endif
#define PIXPERROW_ASCII 2
static const char* GraphMeterMode_dotsAscii[] = {
static const char* const GraphMeterMode_dotsAscii[] = {
/*00*/" ", /*01*/".", /*02*/":",
/*10*/".", /*11*/".", /*12*/":",
/*20*/":", /*21*/":", /*22*/":"
};
static const char** GraphMeterMode_dots;
static const char* const* GraphMeterMode_dots;
static int GraphMeterMode_pixPerRow;
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
@ -397,7 +389,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
data->values[i] = data->values[i+1];
char buffer[nValues];
Meter_setValues(this, buffer, nValues - 1);
Meter_updateValues(this, buffer, nValues - 1);
double value = 0.0;
int items = Meter_getItems(this);
@ -414,8 +406,8 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
}
for (; i < nValues; i+=2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP(data->values[i] * pix, 1, pix);
int v2 = CLAMP(data->values[i+1] * pix, 1, pix);
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);
int colorIdx = GRAPH_1;
for (int line = 0; line < GRAPH_HEIGHT; line++) {
@ -432,7 +424,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- LEDMeterMode ---------- */
static const char* LEDMeterMode_digitsAscii[] = {
static const char* const LEDMeterMode_digitsAscii[] = {
" __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ ",
"| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|",
"|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"
@ -440,7 +432,7 @@ static const char* LEDMeterMode_digitsAscii[] = {
#ifdef HAVE_LIBNCURSESW
static const char* LEDMeterMode_digitsUtf8[] = {
static const char* const LEDMeterMode_digitsUtf8[] = {
"┌──┐","","╶──┐","╶──┐","╷ ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐",
"│ │","","┌──┘"," ──┤","└──┤","└──┐","├──┐","","├──┤","└──┤",
"└──┘","","└──╴","╶──┘","","╶──┘","└──┘","","└──┘"," ──┘"
@ -448,7 +440,7 @@ static const char* LEDMeterMode_digitsUtf8[] = {
#endif
static const char** LEDMeterMode_digits;
static const char* const* LEDMeterMode_digits;
static void LEDMeterMode_drawDigit(int x, int y, int n) {
for (int i = 0; i < 3; i++)
@ -466,7 +458,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
@ -529,7 +521,7 @@ MeterMode* Meter_modes[] = {
/* Blank meter */
static void BlankMeter_setValues(Meter* this, char* buffer, int size) {
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) buffer; (void) size;
}
@ -548,8 +540,9 @@ MeterClass BlankMeter_class = {
.delete = Meter_delete,
.display = BlankMeter_display,
},
.setValues = BlankMeter_setValues,
.updateValues = BlankMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0,
.attributes = BlankMeter_attributes,
.name = "Blank",

View File

@ -24,7 +24,7 @@ typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_SetValues)(Meter*, char*, int);
typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ {
@ -33,7 +33,7 @@ typedef struct MeterClass_ {
const Meter_Done done;
const Meter_UpdateMode updateMode;
const Meter_Draw draw;
const Meter_SetValues setValues;
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
const int* attributes;
@ -53,7 +53,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_)
#define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)

View File

@ -31,13 +31,19 @@ struct MetersPanel_ {
}*/
static const char* MetersFunctions[] = {"Type ", "Move ", "Delete", "Done ", NULL};
static const char* MetersKeys[] = {"Space", "Enter", "Del", "Esc"};
static int MetersEvents[] = {' ', 13, KEY_DC, 27};
// 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* MetersMovingFunctions[] = {"Up ", "Down ", "Left ", "Right ", "Confirm", "Delete", "Done ", NULL};
static const char* MetersMovingKeys[] = {"Up", "Dn", "Lt", "Rt", "Enter", "Del", "Esc"};
static int MetersMovingEvents[] = {KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, 13, KEY_DC, 27};
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// 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 FunctionBar* Meters_movingBar = NULL;
static void MetersPanel_delete(Object* object) {
@ -61,6 +67,7 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
super->currentBar = Meters_movingBar;
}
FunctionBar_draw(this->super.currentBar, NULL);
}
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
@ -99,7 +106,6 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (!Vector_size(this->meters))
break;
MetersPanel_setMoving(this, !(this->moving));
FunctionBar_draw(this->super.currentBar, NULL);
result = HANDLED;
break;
}
@ -122,8 +128,8 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(7):
case '[':
case '-':
@ -138,8 +144,8 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (!this->moving) {
break;
}
/* else fallthrough */
}
/* else fallthrough */
case KEY_F(8):
case ']':
case '+':

View File

@ -27,6 +27,12 @@ struct MetersPanel_ {
};
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
void MetersPanel_setMoving(MetersPanel* this, bool moving);

View File

@ -77,7 +77,7 @@ void OpenFilesScreen_draw(InfoScreen* this) {
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char command[1025];
snprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid);
xSnprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid);
FILE* fd = popen(command, "r");
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
@ -86,17 +86,12 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
pdata->error = 127;
return pdata;
}
while (!feof(fd)) {
int cmd = fgetc(fd);
if (cmd == EOF)
break;
char* entry = xMalloc(1024);
if (!fgets(entry, 1024, fd)) {
free(entry);
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
char* newline = strrchr(entry, '\n');
*newline = '\0';
unsigned char cmd = line[0];
if (cmd == 'f') {
OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) {
@ -107,8 +102,8 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
fdata = nextFile;
item = &(fdata->data);
}
assert(cmd >= 0 && cmd <= 0xff);
item->data[cmd] = entry;
item->data[cmd] = xStrdup(line + 1);
free(line);
}
pdata->error = pclose(fd);
return pdata;
@ -132,9 +127,11 @@ void OpenFilesScreen_scan(InfoScreen* this) {
} else {
OpenFiles_FileData* fdata = pdata->files;
while (fdata) {
char entry[1024];
char** data = fdata->data.data;
sprintf(entry, "%5s %4s %10s %10s %10s %s",
int lenN = data['n'] ? strlen(data['n']) : 0;
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
char* entry = xMalloc(sizeEntry);
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "",
data['t'] ? data['t'] : "",
data['D'] ? data['D'] : "",

57
Panel.c
View File

@ -57,14 +57,17 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -82,10 +85,12 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define KEY_CTRLN 0016 /* control-n key */
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
PanelClass Panel_class = {
.super = {
@ -114,6 +119,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->y = y;
this->w = w;
this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0;
@ -327,6 +334,7 @@ void Panel_draw(Panel* this, bool focus) {
if (selected) {
attrset(selectionColor);
RichString_setAttr(&item, selectionColor);
this->selectedLen = itemLen;
}
mvhline(y + line, x, ' ', this->w);
if (amt > 0)
@ -352,6 +360,7 @@ void Panel_draw(Panel* this, bool focus) {
RichString_begin(new);
Object_display(newObj, &new);
int newLen = RichString_sizeVal(new);
this->selectedLen = newLen;
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w);
if (scrollH < oldLen)
RichString_printoffnVal(old, y+this->oldSelected - first, x,
@ -367,7 +376,6 @@ void Panel_draw(Panel* this, bool focus) {
RichString_end(old);
}
this->oldSelected = this->selected;
move(0, 0);
}
bool Panel_onKey(Panel* this, int key) {
@ -376,11 +384,11 @@ bool Panel_onKey(Panel* this, int key) {
int size = Vector_size(this->items);
switch (key) {
case KEY_DOWN:
case KEY_CTRLN:
case KEY_CTRL('N'):
this->selected++;
break;
case KEY_UP:
case KEY_CTRLP:
case KEY_CTRL('P'):
this->selected--;
break;
#ifdef KEY_C_DOWN
@ -394,25 +402,26 @@ bool Panel_onKey(Panel* this, int key) {
break;
#endif
case KEY_LEFT:
case KEY_CTRLB:
case KEY_CTRL('B'):
if (this->scrollH > 0) {
this->scrollH -= CRT_scrollHAmount;
this->scrollH -= MAX(CRT_scrollHAmount, 0);
this->needsRedraw = true;
}
break;
case KEY_RIGHT:
case KEY_CTRLF:
case KEY_CTRL('F'):
this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true;
break;
case KEY_PPAGE:
this->selected -= (this->h - 1);
this->scrollV -= (this->h - 1);
this->scrollV = MAX(0, this->scrollV - this->h + 1);
this->needsRedraw = true;
break;
case KEY_NPAGE:
this->selected += (this->h - 1);
this->scrollV += (this->h - 1);
this->scrollV = MAX(0, MIN(Vector_size(this->items) - this->h,
this->scrollV + this->h - 1));
this->needsRedraw = true;
break;
case KEY_WHEELUP:
@ -436,6 +445,16 @@ bool Panel_onKey(Panel* this, int key) {
case KEY_END:
this->selected = size - 1;
break;
case KEY_CTRL('A'):
case '^':
this->scrollH = 0;
this->needsRedraw = true;
break;
case KEY_CTRL('E'):
case '$':
this->scrollH = MAX(this->selectedLen - this->w, 0);
this->needsRedraw = true;
break;
default:
return false;
}
@ -458,7 +477,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
if (ch < 255 && isalnum(ch)) {
if (ch > 0 && ch < 255 && isalnum(ch)) {
int len = strlen(buffer);
if (len < 99) {
buffer[len] = ch;
@ -488,3 +507,15 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
}
return IGNORED;
}
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
set_escdelay(25);
return getch();
}

13
Panel.h
View File

@ -46,14 +46,17 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
int oldSelected;
int selectedLen;
void* eventHandlerState;
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -70,10 +73,9 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define KEY_CTRLN 0016 /* control-n key */
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern PanelClass Panel_class;
@ -125,4 +127,7 @@ bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
#endif

131
Process.c
View File

@ -18,6 +18,7 @@ in the source distribution for its full text.
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
@ -44,10 +45,12 @@ in the source distribution for its full text.
#include "Object.h"
#include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001
typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2,
STATE = 3,
@ -148,7 +151,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -170,6 +173,10 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
}*/
static int Process_getuid = -1;
@ -193,10 +200,10 @@ void Process_setupColumnWidths() {
assert(digits < 20);
for (int i = 0; Process_pidColumns[i].label; i++) {
assert(i < 20);
sprintf(Process_titleBuffer[i], "%*s ", digits, Process_pidColumns[i].label);
xSnprintf(Process_titleBuffer[i], 20, "%*s ", digits, Process_pidColumns[i].label);
Process_fields[Process_pidColumns[i].id].title = Process_titleBuffer[i];
}
sprintf(Process_pidFormat, "%%%dd ", digits);
xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits);
}
void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
@ -260,13 +267,16 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
processShadowColor = CRT_colors[PROCESS];
}
if (number > 10000000000) {
snprintf(buffer, 13, "%11lld ", number / 1000);
if ((long long) number == -1LL) {
int len = snprintf(buffer, 13, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number > 10000000000) {
xSnprintf(buffer, 13, "%11lld ", number / 1000);
RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4);
} else {
snprintf(buffer, 13, "%11llu ", number);
xSnprintf(buffer, 13, "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3);
@ -283,15 +293,15 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
int hundredths = totalHundredths - (totalSeconds * 100);
char buffer[11];
if (hours >= 100) {
snprintf(buffer, 10, "%7lluh ", hours);
xSnprintf(buffer, 10, "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
} else {
if (hours) {
snprintf(buffer, 10, "%2lluh", hours);
xSnprintf(buffer, 10, "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
snprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
xSnprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
} else {
snprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths);
xSnprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths);
}
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
}
@ -353,6 +363,21 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
}
}
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
if (val >= 0) {
if (val < 100) {
xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 1000) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)val);
} else {
xSnprintf(buffer, n, "%4d ", (unsigned int)val);
}
} else {
*attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
@ -361,30 +386,15 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes;
switch (field) {
case PERCENT_CPU: {
if (this->percent_cpu > 999.9) {
snprintf(buffer, n, "%4d ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
snprintf(buffer, n, "%3d. ", (unsigned int)this->percent_cpu);
} else {
snprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
snprintf(buffer, n, "100. ");
} else {
snprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
if (!this->settings->treeView || this->indent == 0) {
ScreenSettings* ss = this->settings->ss;
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
@ -394,7 +404,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
int indent = (this->indent < 0 ? -this->indent : this->indent);
for (int i = 0; i < 32; i++)
if (indent & (1 << i))
if (indent & (1U << i))
maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) {
int written;
@ -405,8 +415,8 @@ void Process_writeField(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];
snprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : 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);
Process_writeCommand(this, attr, baseattr, str);
return;
@ -417,28 +427,28 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
case M_RESIDENT: Process_humanNumber(str, this->m_resident * PAGE_SIZE_KB, coloring); return;
case M_SIZE: Process_humanNumber(str, this->m_size * PAGE_SIZE_KB, coloring); return;
case NICE: {
snprintf(buffer, n, "%3ld ", this->nice);
xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
: this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
: attr;
break;
}
case NLWP: snprintf(buffer, n, "%4ld ", this->nlwp); break;
case PGRP: snprintf(buffer, n, Process_pidFormat, this->pgrp); break;
case PID: snprintf(buffer, n, Process_pidFormat, this->pid); break;
case PPID: snprintf(buffer, n, Process_pidFormat, this->ppid); 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 PRIORITY: {
if(this->priority == -100)
snprintf(buffer, n, " RT ");
if(this->priority <= -100)
xSnprintf(buffer, n, " RT ");
else
snprintf(buffer, n, "%3ld ", this->priority);
xSnprintf(buffer, n, "%3ld ", this->priority);
break;
}
case PROCESSOR: snprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
case SESSION: snprintf(buffer, n, Process_pidFormat, this->session); break;
case STARTTIME: snprintf(buffer, n, "%s", this->starttime_show); 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 STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE: {
snprintf(buffer, n, "%c ", this->state);
xSnprintf(buffer, n, "%c ", this->state);
switch(this->state) {
case 'R':
attr = CRT_colors[PROCESS_R_STATE];
@ -449,18 +459,18 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
}
break;
}
case ST_UID: snprintf(buffer, n, "%4d ", this->st_uid); break;
case ST_UID: xSnprintf(buffer, n, "%4d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return;
case TGID: snprintf(buffer, n, Process_pidFormat, this->tgid); break;
case TPGID: snprintf(buffer, n, Process_pidFormat, this->tpgid); break;
case TTY_NR: snprintf(buffer, n, "%5u ", this->tty_nr); break;
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, 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 != (int) this->st_uid)
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
snprintf(buffer, n, "%-9s ", this->user);
xSnprintf(buffer, n, "%-9s ", this->user);
} else {
snprintf(buffer, n, "%-9d ", this->st_uid);
xSnprintf(buffer, n, "%-9d ", this->st_uid);
}
if (buffer[9] != '\0') {
buffer[9] = ' ';
@ -469,14 +479,14 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
break;
}
default:
snprintf(buffer, n, "- ");
xSnprintf(buffer, n, "- ");
}
RichString_append(str, attr, buffer);
}
void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
RichString_prune(out);
for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]);
@ -517,11 +527,10 @@ void Process_toggleTag(Process* this) {
}
bool Process_setPriority(Process* this, int priority) {
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
int old_prio = getpriority(PRIO_PROCESS, this->pid);
int err = setpriority(PRIO_PROCESS, this->pid, priority);
seteuid(euid);
CRT_restorePrivileges();
if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) {
this->nice = priority;
}
@ -533,10 +542,9 @@ bool Process_changePriorityBy(Process* this, size_t delta) {
}
void Process_sendSignal(Process* this, size_t sgn) {
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
kill(this->pid, (int) sgn);
seteuid(euid);
CRT_restorePrivileges();
}
long Process_pidCompare(const void* v1, const void* v2) {
@ -548,14 +556,15 @@ long Process_pidCompare(const void* v1, const void* v2) {
long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
ScreenSettings* ss = settings->ss;
if (ss->direction == 1) {
p1 = (Process*)v1;
p2 = (Process*)v2;
} else {
p2 = (Process*)v1;
p1 = (Process*)v2;
}
switch (settings->sortKey) {
switch (ss->sortKey) {
case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM:
@ -593,7 +602,7 @@ long Process_compare(const void* v1, const void* v2) {
return (p1->starttime_ctime - p2->starttime_ctime);
}
case STATE:
return (p1->state - p2->state);
return (Process_sortState(p1->state) - Process_sortState(p2->state));
case ST_UID:
return (p1->st_uid - p2->st_uid);
case TIME:

View File

@ -24,10 +24,12 @@ in the source distribution for its full text.
#include "Object.h"
#include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001
typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
PID = 1,
COMM = 2,
STATE = 3,
@ -128,7 +130,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -150,6 +152,10 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
#define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K)
@ -171,6 +177,8 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out);

View File

@ -124,14 +124,15 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
unsigned int key = fields[i];
const char* field = Process_fields[key].title;
if (!field) field = "- ";
if (!this->settings->treeView && this->settings->sortKey == fields[i])
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
else
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
int color = (!this->settings->ss->treeView && this->settings->ss->sortKey == key)
? CRT_colors[PANEL_SELECTION_FOCUS]
: CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, color, field);
}
}
@ -173,7 +174,7 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* process = (Process*) (Vector_get(this->processes, i));
if (process->show && (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid))) {
if (process->show && Process_isChildOf(process, pid)) {
process = (Process*) (Vector_take(this->processes, i));
Vector_add(children, process);
}
@ -200,36 +201,64 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
}
void ProcessList_sort(ProcessList* this) {
if (!this->settings->treeView) {
if (!this->settings->ss->treeView) {
Vector_insertionSort(this->processes);
} else {
// Save settings
int direction = this->settings->direction;
int sortKey = this->settings->sortKey;
int direction = this->settings->ss->direction;
int sortKey = this->settings->ss->sortKey;
// Sort by PID
this->settings->sortKey = PID;
this->settings->direction = 1;
this->settings->ss->sortKey = PID;
this->settings->ss->direction = 1;
Vector_quickSort(this->processes);
// Restore settings
this->settings->sortKey = sortKey;
this->settings->direction = direction;
// Take PID 1 as root and add to the new listing
this->settings->ss->sortKey = sortKey;
this->settings->ss->direction = direction;
int vsize = Vector_size(this->processes);
Process* init = (Process*) (Vector_take(this->processes, 0));
if (!init) return;
// This assertion crashes on hardened kernels.
// I wonder how well tree view works on those systems.
// assert(init->pid == 1);
init->indent = 0;
Vector_add(this->processes2, init);
// Recursively empty list
ProcessList_buildTree(this, init->pid, 0, 0, direction, true);
// Add leftovers
while (Vector_size(this->processes)) {
Process* p = (Process*) (Vector_take(this->processes, 0));
p->indent = 0;
Vector_add(this->processes2, p);
ProcessList_buildTree(this, p->pid, 0, 0, direction, p->showChildren);
// Find all processes whose parent is not visible
int size;
while ((size = Vector_size(this->processes))) {
int i;
for (i = 0; i < size; i++) {
Process* process = (Process*)(Vector_get(this->processes, i));
// Immediately consume not shown processes
if (!process->show) {
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
break;
}
pid_t ppid = process->tgid == process->pid ? process->ppid : process->tgid;
// Bisect the process vector to find parent
int l = 0, r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
// root.
if (process->pid == ppid)
r = 0;
while (l < r) {
int c = (l + r) / 2;
pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid;
if (ppid == pid) {
break;
} else if (ppid < pid) {
r = c;
} else {
l = c + 1;
}
}
// If parent not found, then construct the tree with this root
if (l >= r) {
process = (Process*)(Vector_take(this->processes, i));
process->indent = 0;
Vector_add(this->processes2, process);
ProcessList_buildTree(this, process->pid, 0, 0, direction, process->showChildren);
break;
}
}
// There should be no loop in the process tree
assert(i < size);
}
assert(Vector_size(this->processes2) == vsize); (void)vsize;
assert(Vector_size(this->processes) == 0);
@ -243,7 +272,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessField ProcessList_keyAt(ProcessList* this, int at) {
int x = 0;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title;

2
README
View File

@ -40,7 +40,7 @@ Compilation instructions
This program is distributed as a standard autotools-based package.
See the [INSTALL](/INSTALL) file for detailed instructions.
When compiling from a release tarball, run:
When compiling from a [release tarball](https://hisham.hm/htop/releases/), run:
./configure && make

View File

@ -71,30 +71,46 @@ inline int ScreenManager_size(ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
ScreenManager_insert(this, item, size, Vector_size(this->panels));
}
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
if (this->orientation == HORIZONTAL) {
int lastX = 0;
if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
if (idx > 0) {
Panel* last = (Panel*) Vector_get(this->panels, idx - 1);
lastX = last->x + last->w + 1;
}
int height = LINES - this->y1 + this->y2;
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
if (size <= 0) {
size = COLS-this->x1+this->x2-lastX;
}
Panel_resize(item, size, height);
Panel_move(item, lastX, this->y1);
if (idx < this->panelCount) {
for (int i = idx + 1; i <= this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x + size, p->y);
}
}
}
// TODO: VERTICAL
Vector_add(this->panels, item);
Vector_insert(this->panels, idx, item);
item->needsRedraw = true;
this->panelCount++;
}
Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--;
if (idx < this->panelCount) {
for (int i = idx; i < this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x - w, p->y);
}
}
return panel;
}
@ -131,7 +147,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->treeView) {
if (*sortTimeout == 0 || this->settings->ss->treeView) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
@ -157,7 +173,8 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
}
}
static Panel* setCurrentPanel(Panel* panel) {
static Panel* setCurrentPanel(ScreenManager* this, int focus) {
Panel* panel = (Panel*) Vector_get(this->panels, focus);
FunctionBar_draw(panel->currentBar, NULL);
return panel;
}
@ -166,7 +183,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false;
int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
Panel* panelFocus = setCurrentPanel(this, focus);
double oldTime = 0.0;
@ -189,8 +206,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
}
int prevCh = ch;
ESCDELAY = 25;
ch = getch();
ch = Panel_getCh(panelFocus);
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) {
@ -212,7 +228,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(panel);
panelFocus = setCurrentPanel(this, i);
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@ -279,25 +295,31 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
continue;
}
case KEY_LEFT:
case KEY_CTRLB:
case KEY_CTRL('B'):
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
break;
tryLeft:
if (focus > 0)
focus--;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus > 0)
goto tryLeft;
break;
case KEY_RIGHT:
case KEY_CTRLF:
case KEY_CTRL('F'):
case 9:
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange)
break;
tryRight:
if (focus < this->panelCount - 1)
focus++;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
goto tryRight;
break;
@ -307,6 +329,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
quit = true;
continue;
default:
defaultHandler:
sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch);
break;

View File

@ -43,6 +43,8 @@ extern int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);
Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);

330
ScreensPanel.c Normal file
View File

@ -0,0 +1,330 @@
/*
htop - ScreensPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ScreensPanel.h"
#include "Platform.h"
#include "StringUtils.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
/*{
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
}*/
ObjectClass ScreenListItem_class = {
.extends = Class(ListItem),
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) {
ScreenListItem* this = AllocThis(ScreenListItem);
ListItem_init((ListItem*)this, value, 0);
this->ss = ss;
return this;
}
static const char* const ScreensFunctions[] = {" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ScreensPanel_delete(Object* object) {
Panel* super = (Panel*) object;
ScreensPanel* this = (ScreensPanel*) object;
Panel_done(super);
free(this);
}
static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
//ListItem* item = (ListItem*)Panel_getSelected(super);
if (ch >= 32 && ch < 127 && ch != 61 && ch != 22) {
if (this->cursor < SCREEN_NAME_LEN - 1) {
this->buffer[this->cursor] = ch;
this->cursor++;
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
} else {
switch(ch) {
case 127:
case KEY_BACKSPACE:
{
if (this->cursor > 0) {
this->cursor--;
this->buffer[this->cursor] = '\0';
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
break;
}
case 0x0a:
case 0x0d:
case KEY_ENTER:
{
ListItem* item = (ListItem*) Panel_getSelected(super);
free(this->saved);
item->value = xStrdup(this->buffer);
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
ScreensPanel_update(super);
break;
}
case 27: // Esc
{
ListItem* item = (ListItem*) Panel_getSelected(super);
item->value = this->saved;
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
break;
}
}
}
return HANDLED;
}
static void startRenaming(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
ListItem* item = (ListItem*) Panel_getSelected(super);
this->renaming = true;
super->cursorOn = true;
char* name = item->value;
this->saved = name;
strncpy(this->buffer, name, SCREEN_NAME_LEN);
this->buffer[SCREEN_NAME_LEN] = '\0';
this->cursor = strlen(this->buffer);
item->value = this->buffer;
Panel_setSelectionColor(super, CRT_colors[PANEL_EDIT]);
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
static void rebuildSettingsArray(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
int n = Panel_size(super);
free(this->settings->screens);
this->settings->screens = xMalloc(sizeof(ScreenSettings*) * (n + 1));
this->settings->screens[n] = NULL;
for (int i = 0; i < n; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
this->settings->screens[i] = item->ss;
}
}
static void addNewScreen(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
char* name = "New";
ScreenSettings* ss = Settings_newScreen(this->settings, name, "PID Command");
ScreenListItem* item = ScreenListItem_new(name, ss);
int idx = Panel_getSelectedIndex(super);
Panel_insert(super, idx + 1, (Object*) item);
Panel_setSelected(super, idx + 1);
}
static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
int selected = Panel_getSelectedIndex(super);
ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super);
bool shouldRebuildArray = false;
HandlerResult result = IGNORED;
switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
result = HANDLED;
break;
}
case EVENT_SET_SELECTED:
result = HANDLED;
break;
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_HOME:
case KEY_END: {
Panel_onKey(super, ch);
break;
}
case KEY_F(2):
case KEY_CTRL('R'):
{
startRenaming(super);
result = HANDLED;
break;
}
case KEY_F(5):
case KEY_CTRL('N'):
{
addNewScreen(super);
startRenaming(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_UP:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(7):
case '[':
case '-':
{
Panel_moveSelectedUp(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_DOWN:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(8):
case ']':
case '+':
{
Panel_moveSelectedDown(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_F(9):
//case KEY_DC:
{
if (Panel_size(super) > 1) {
Panel_remove(super, selected);
}
shouldRebuildArray = true;
result = HANDLED;
break;
}
default:
{
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
break;
}
}
ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super);
if (oldFocus != newFocus) {
ColumnsPanel_fill(this->columns, newFocus->ss);
result = HANDLED;
}
if (shouldRebuildArray) {
rebuildSettingsArray(super);
}
if (result == HANDLED)
ScreensPanel_update(super);
return result;
}
static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
if (this->renaming) {
return ScreensPanel_eventHandlerRenaming(super, ch);
} else {
return ScreensPanel_eventHandlerNormal(super, ch);
}
}
PanelClass ScreensPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ScreensPanel_delete
},
.eventHandler = ScreensPanel_eventHandler
};
ScreensPanel* ScreensPanel_new(Settings* settings) {
ScreensPanel* this = AllocThis(ScreensPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ScreensFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->columns = ColumnsPanel_new(settings->screens[0], &(settings->changed));
this->moving = false;
this->renaming = false;
super->cursorOn = false;
this->cursor = 0;
Panel_setHeader(super, "Screens");
for (unsigned int i = 0; i < settings->nScreens; i++) {
ScreenSettings* ss = settings->screens[i];
char* name = ss->name;
Panel_add(super, (Object*) ScreenListItem_new(name, ss));
}
return this;
}
void ScreensPanel_update(Panel* super) {
ScreensPanel* this = (ScreensPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->screens = xRealloc(this->settings->screens, sizeof(char*) * (size+1));
for (int i = 0; i < size; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
ScreenSettings* ss = item->ss;
free(ss->name);
this->settings->screens[i] = ss;
ss->name = xStrdup(((ListItem*) item)->value);
}
this->settings->screens[size] = NULL;
}

51
ScreensPanel.h Normal file
View File

@ -0,0 +1,51 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ScreensPanel
#define HEADER_ScreensPanel
/*
htop - ScreensPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
extern ObjectClass ScreenListItem_class;
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss);
extern PanelClass ScreensPanel_class;
ScreensPanel* ScreensPanel_new(Settings* settings);
void ScreensPanel_update(Panel* super);
#endif

View File

@ -29,23 +29,32 @@ typedef struct {
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -68,25 +77,82 @@ typedef struct Settings_ {
}*/
static void writeList(FILE* fd, char** list, int len) {
const char* sep = "";
for (int i = 0; i < len; i++) {
fprintf(fd, "%s%s", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
/*
static char** readQuotedList(char* line) {
int n = 0;
char** list = xCalloc(sizeof(char*), 1);
int start = 0;
for (;;) {
while (line[start] && line[start] == ' ') {
start++;
}
if (line[start] != '"') {
break;
}
start++;
int close = start;
while (line[close] && line[close] != '"') {
close++;
}
int len = close - start;
char* item = xMalloc(len + 1);
strncpy(item, line + start, len);
item[len] = '\0';
list[n] = item;
n++;
list = xRealloc(list, sizeof(char*) * (n + 1));
start = close + 1;
}
list[n] = NULL;
return list;
}
static void writeQuotedList(FILE* fd, char** list) {
const char* sep = "";
for (int i = 0; list[i]; i++) {
fprintf(fd, "%s\"%s\"", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
*/
void Settings_delete(Settings* this) {
free(this->filename);
free(this->fields);
for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->columns[i].names);
free(this->columns[i].modes);
for (unsigned int i = 0; i < (sizeof(this->meterColumns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->meterColumns[i].names);
free(this->meterColumns[i].modes);
}
if (this->screens) {
for (unsigned int i = 0; this->screens[i]; i++) {
free(this->screens[i]->name);
free(this->screens[i]->fields);
}
free(this->screens);
}
free(this);
}
static void Settings_readMeters(Settings* this, char* line, int column) {
static void Settings_readMeters(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
this->columns[column].names = ids;
this->meterColumns[side].names = ids;
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
static void Settings_readMeterModes(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
@ -95,13 +161,13 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
for (int i = 0; ids[i]; i++) {
len++;
}
this->columns[column].len = len;
this->meterColumns[side].len = len;
int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]);
}
String_freeArray(ids);
this->columns[column].modes = modes;
this->meterColumns[side].modes = modes;
}
static void Settings_defaultMeters(Settings* this) {
@ -110,37 +176,59 @@ static void Settings_defaultMeters(Settings* this) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
this->meterColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->meterColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->meterColumns[i].len = sizes[i];
}
int r = 0;
if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs2");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs2");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else {
this->columns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("AllCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
}
this->columns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->meterColumns[0].names[1] = xStrdup("Memory");
this->meterColumns[0].modes[1] = BAR_METERMODE;
this->meterColumns[0].names[2] = xStrdup("Swap");
this->meterColumns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Tasks");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("LoadAverage");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Uptime");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
}
static const char* toFieldName(int i) {
return Process_fields[i].name;
}
static int toFieldIndex(const char* str) {
if (isdigit(str[0])) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(str) + 1;
if (id < Platform_numberOfFields && toFieldName(id)) {
return id;
}
} else {
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
const char* pName = toFieldName(p);
if (pName && strcmp(pName, str) == 0) {
return p;
}
}
}
return -1;
}
static void readFields(ProcessField* fields, int* flags, const char* line) {
@ -151,47 +239,71 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
fields[j] = id;
*flags |= Process_fields[id].flags;
int idx = toFieldIndex(ids[i]);
if (idx != -1) {
fields[j] = idx;
*flags |= Process_fields[idx].flags;
j++;
}
}
fields[j] = (ProcessField) NULL;
fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids);
}
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line) {
ScreenSettings* ss = xCalloc(sizeof(ScreenSettings), 1);
ss->name = xStrdup(name);
ss->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
ss->flags = 0;
ss->direction = 1;
ss->treeView = 0;
readFields(ss->fields, &(ss->flags), line);
this->screens[this->nScreens] = ss;
this->nScreens++;
this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
this->screens[this->nScreens] = NULL;
return ss;
}
static void Settings_defaultScreens(Settings* this) {
Settings_newScreen(this, "Default", "PID USER PRIORITY NICE M_SIZE M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command");
this->screens[0]->sortKey = toFieldIndex("PERCENT_CPU");
Settings_newScreen(this, "I/O", "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command");
this->screens[1]->sortKey = toFieldIndex("IO_RATE");
Settings_newScreen(this, "Perf Counters", "PID USER PERCENT_CPU PROCESSOR MCYCLE MINSTR IPC PERCENT_MISS PERCENT_BMISS Command");
this->screens[2]->sortKey = toFieldIndex("MCYCLE");
Settings_newScreen(this, "L1 Data Cache", "PID USER PERCENT_CPU L1DREADS L1DRMISSES L1DWRITES L1DWMISSES Command");
this->screens[3]->sortKey = toFieldIndex("L1DREADS");
}
static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd;
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
fd = fopen(fileName, "r");
seteuid(euid);
CRT_restorePrivileges();
if (!fd)
return false;
const int maxLine = 2048;
char buffer[maxLine];
bool readMeters = false;
while (fgets(buffer, maxLine, fd)) {
ProcessField* legacyFields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
int legacyFlags;
bool legacyFieldsRead = false;
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
int nOptions;
char** option = String_split(buffer, '=', &nOptions);
char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) {
String_freeArray(option);
continue;
}
if (String_eq(option[0], "fields")) {
readFields(this->fields, &(this->flags), option[1]);
} 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], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
readFields(legacyFields, &legacyFlags, option[1]);
legacyFieldsRead = true;
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
@ -240,55 +352,78 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1);
readMeters = true;
} else if (strncmp(option[0], "screen:", 7) == 0) {
Settings_newScreen(this, option[0] + 7, option[1]);
} else if (String_eq(option[0], ".tree_view")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->treeView = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_direction")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->direction = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_key")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->sortKey = toFieldIndex(option[1]);
}
}
String_freeArray(option);
}
fclose(fd);
if (this->nScreens == 0) {
Settings_defaultScreens(this);
if (legacyFieldsRead) {
free(this->screens[0]->fields);
this->screens[0]->fields = legacyFields;
this->screens[0]->flags = legacyFlags;
}
}
if (!readMeters) {
Settings_defaultMeters(this);
}
return true;
}
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
static void writeFields(FILE* fd, ProcessField* fields, bool byName) {
const char* sep = "";
for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%d ", (int) fields[i]-1);
if (byName) {
fprintf(fd, "%s%s", sep, toFieldName(fields[i]));
} else {
// This " - 1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
}
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeters(Settings* this, FILE* fd, int column) {
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s ", this->columns[column].names[i]);
}
fprintf(fd, "\n");
static void writeMeters(Settings* this, FILE* fd, int side) {
writeList(fd, this->meterColumns[side].names, this->meterColumns[side].len);
}
static void writeMeterModes(Settings* this, FILE* fd, int column) {
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%d ", this->columns[column].modes[i]);
static void writeMeterModes(Settings* this, FILE* fd, int side) {
const char* sep = "";
for (int i = 0; i < this->meterColumns[side].len; i++) {
fprintf(fd, "%s%d", sep, this->meterColumns[side].modes[i]);
sep = " ";
}
fprintf(fd, "\n");
}
bool Settings_write(Settings* this) {
FILE* fd;
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
fd = fopen(this->filename, "w");
seteuid(euid);
CRT_restorePrivileges();
if (fd == NULL) {
return false;
}
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields");
// 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, "fields="); writeFields(fd, this->screens[0]->fields, false);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
@ -298,7 +433,6 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
@ -310,6 +444,23 @@ bool Settings_write(Settings* this) {
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
// Legacy compatibility with older versions of htop
fprintf(fd, "tree_view=%d\n", (int) this->screens[0]->treeView);
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->screens[0]->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->screens[0]->direction);
if (this->screens && this->screens[0]) {
for (unsigned int i = 0; i < this->nScreens; i++) {
ScreenSettings* ss = this->screens[i];
fprintf(fd, "screen:%s=", ss->name);
writeFields(fd, ss->fields, true);
fprintf(fd, ".tree_view=%d\n", (int) ss->treeView);
fprintf(fd, ".sort_key=%s\n", toFieldName(ss->sortKey));
fprintf(fd, ".sort_direction=%d\n", (int) ss->direction);
}
}
fclose(fd);
return true;
}
@ -318,14 +469,11 @@ Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
@ -335,15 +483,8 @@ Settings* Settings_new(int cpuCount) {
this->showProgramPath = true;
this->highlightThreads = true;
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
this->screens = xCalloc(sizeof(ScreenSettings*), 1);
this->nScreens = 0;
char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC");
@ -365,8 +506,8 @@ Settings* Settings_new(int cpuCount) {
htopDir = String_cat(home, "/.config/htop");
}
legacyDotfile = String_cat(home, "/.htoprc");
uid_t euid = geteuid();
seteuid(getuid());
CRT_dropPrivileges();
(void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700);
free(htopDir);
@ -379,7 +520,7 @@ Settings* Settings_new(int cpuCount) {
free(legacyDotfile);
legacyDotfile = NULL;
}
seteuid(euid);
CRT_restorePrivileges();
}
this->colorScheme = 0;
this->changed = false;
@ -399,17 +540,22 @@ Settings* Settings_new(int cpuCount) {
free(systemSettings);
if (!ok) {
Settings_defaultMeters(this);
Settings_defaultScreens(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
}
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
free(legacyDotfile);
return this;
}
void Settings_invertSortOrder(Settings* this) {
void ScreenSettings_invertSortOrder(ScreenSettings* this) {
if (this->direction == 1)
this->direction = -1;
else

View File

@ -9,7 +9,6 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define DEFAULT_DELAY 15
#include "Process.h"
#include <stdbool.h>
@ -20,23 +19,32 @@ typedef struct {
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -58,12 +66,18 @@ typedef struct Settings_ {
#endif
/*
*/
void Settings_delete(Settings* this);
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line);
bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount);
void Settings_invertSortOrder(Settings* this);
void ScreenSettings_invertSortOrder(ScreenSettings* this);
#endif

View File

@ -31,13 +31,27 @@ Panel* SignalsPanel_new() {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Send ", "Cancel "));
const int defaultSignal = SIGTERM;
int defaultPosition = 15;
for(unsigned int i = 0; i < Platform_numberOfSignals; i++) {
unsigned int i;
for (i = 0; i < Platform_numberOfSignals; i++) {
Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number));
// signal 15 is not always the 15th signal in the table
if (Platform_signals[i].number == defaultSignal) {
defaultPosition = i;
}
}
#if (defined(SIGRTMIN) && defined(SIGRTMAX))
if (SIGRTMAX - SIGRTMIN <= 100) {
static char buf[15];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
xSnprintf(buf, 15, "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}
Panel_set(this, i, (Object*) ListItem_new(buf, sig));
}
}
#endif
Panel_setHeader(this, "Send signal:");
Panel_setSelected(this, defaultPosition);
return this;

View File

@ -13,13 +13,19 @@ in the source distribution for its full text.
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
/*{
#define String_startsWith(s, match) (strstr((s), (match)) == (s))
#include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
}*/
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
@ -69,13 +75,7 @@ char** String_split(const char* s, char sep, int* n) {
ctr++;
if (ctr == blocks) {
blocks += rate;
char** newOut = (char**) xRealloc(out, sizeof(char*) * blocks);
if (newOut) {
out = newOut;
} else {
blocks -= rate;
break;
}
out = (char**) xRealloc(out, sizeof(char*) * blocks);
}
s += size + 1;
}
@ -86,16 +86,16 @@ char** String_split(const char* s, char sep, int* n) {
out[ctr] = token;
ctr++;
}
char** newOut = xRealloc(out, sizeof(char*) * (ctr + 1));
if (newOut) {
out = newOut;
}
out = xRealloc(out, sizeof(char*) * (ctr + 1));
out[ctr] = NULL;
*n = ctr;
return out;
}
void String_freeArray(char** s) {
if (!s) {
return;
}
for (int i = 0; s[i] != NULL; i++) {
free(s[i]);
}
@ -128,3 +128,29 @@ char* String_getToken(const char* line, const unsigned short int numMatch) {
match[foundCount] = '\0';
return((char*)xStrdup(match));
}
char* String_readLine(FILE* fd) {
const int step = 1024;
int bufSize = step;
char* buffer = xMalloc(step + 1);
char* at = buffer;
for (;;) {
char* ok = fgets(at, step + 1, fd);
if (!ok) {
free(buffer);
return NULL;
}
char* newLine = strrchr(at, '\n');
if (newLine) {
*newLine = '\0';
return buffer;
} else {
if (feof(fd)) {
return buffer;
}
}
bufSize += step;
buffer = xRealloc(buffer, bufSize + 1);
at = buffer + bufSize - step;
}
}

View File

@ -9,9 +9,16 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define String_startsWith(s, match) (strstr((s), (match)) == (s))
#include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
char* String_cat(const char* s1, const char* s2);
char* String_trim(const char* in);
@ -24,4 +31,6 @@ void String_freeArray(char** s);
char* String_getToken(const char* line, const unsigned short int numMatch);
char* String_readLine(FILE* fd);
#endif

View File

@ -24,7 +24,7 @@ int SwapMeter_attributes[] = {
SWAP
};
static void SwapMeter_setValues(Meter* this, char* buffer, int size) {
static void SwapMeter_updateValues(Meter* this, char* buffer, int size) {
int written;
Platform_setSwapValues(this);
@ -54,8 +54,9 @@ MeterClass SwapMeter_class = {
.delete = Meter_delete,
.display = SwapMeter_display,
},
.setValues = SwapMeter_setValues,
.updateValues = SwapMeter_updateValues,
.defaultMode = BAR_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = SwapMeter_attributes,
.name = "Swap",

View File

@ -18,19 +18,19 @@ int TasksMeter_attributes[] = {
CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING
};
static void TasksMeter_setValues(Meter* this, char* buffer, int len) {
static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {
ProcessList* pl = this->pl;
this->values[0] = pl->kernelThreads;
this->values[1] = pl->userlandThreads;
this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads;
this->values[3] = pl->runningTasks;
this->values[3] = MIN(pl->runningTasks, pl->cpuCount);
if (pl->totalTasks > this->total) {
this->total = pl->totalTasks;
}
if (this->pl->settings->hideKernelThreads) {
this->values[0] = 0;
}
snprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total);
xSnprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total);
}
static void TasksMeter_display(Object* cast, RichString* out) {
@ -40,7 +40,7 @@ static void TasksMeter_display(Object* cast, RichString* out) {
int processes = (int) this->values[2];
sprintf(buffer, "%d", processes);
xSnprintf(buffer, sizeof(buffer), "%d", processes);
RichString_write(out, CRT_colors[METER_VALUE], buffer);
int threadValueColor = CRT_colors[METER_VALUE];
int threadCaptionColor = CRT_colors[METER_TEXT];
@ -50,18 +50,18 @@ static void TasksMeter_display(Object* cast, RichString* out) {
}
if (!settings->hideUserlandThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", ");
sprintf(buffer, "%d", (int)this->values[1]);
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " thr");
}
if (!settings->hideKernelThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", ");
sprintf(buffer, "%d", (int)this->values[0]);
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " kthr");
}
RichString_append(out, CRT_colors[METER_TEXT], "; ");
sprintf(buffer, "%d", (int)this->values[3]);
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");
}
@ -72,10 +72,10 @@ MeterClass TasksMeter_class = {
.delete = Meter_delete,
.display = TasksMeter_display,
},
.setValues = TasksMeter_setValues,
.updateValues = TasksMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.total = 100.0,
.maxItems = 4,
.total = 100.0,
.attributes = TasksMeter_attributes,
.name = "Tasks",
.uiName = "Task counter",

View File

@ -22,6 +22,7 @@ in the source distribution for its full text.
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
@ -42,9 +43,9 @@ typedef struct TraceScreen_ {
}*/
static const char* TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL};
static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL};
static const char* TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
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};
@ -85,21 +86,21 @@ void TraceScreen_draw(InfoScreen* this) {
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
bool TraceScreen_forkTracer(TraceScreen* this) {
char buffer[1001];
int err = pipe(this->fdpair);
if (err == -1) return false;
int error = pipe(this->fdpair);
if (error == -1) return false;
this->child = fork();
if (this->child == -1) return false;
if (this->child == 0) {
seteuid(getuid());
CRT_dropPrivileges();
dup2(this->fdpair[1], STDERR_FILENO);
int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) {
sprintf(buffer, "%d", this->super.process->pid);
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-p", buffer, NULL);
}
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";

View File

@ -17,10 +17,10 @@ int UptimeMeter_attributes[] = {
UPTIME
};
static void UptimeMeter_setValues(Meter* this, char* buffer, int len) {
static void UptimeMeter_updateValues(Meter* this, char* buffer, int len) {
int totalseconds = Platform_getUptime();
if (totalseconds == -1) {
snprintf(buffer, len, "(unknown)");
xSnprintf(buffer, len, "(unknown)");
return;
}
int seconds = totalseconds % 60;
@ -31,17 +31,17 @@ static void UptimeMeter_setValues(Meter* this, char* buffer, int len) {
if (days > this->total) {
this->total = days;
}
char daysbuf[15];
char daysbuf[32];
if (days > 100) {
sprintf(daysbuf, "%d days(!), ", days);
xSnprintf(daysbuf, sizeof(daysbuf), "%d days(!), ", days);
} else if (days > 1) {
sprintf(daysbuf, "%d days, ", days);
xSnprintf(daysbuf, sizeof(daysbuf), "%d days, ", days);
} else if (days == 1) {
sprintf(daysbuf, "1 day, ");
xSnprintf(daysbuf, sizeof(daysbuf), "1 day, ");
} else {
daysbuf[0] = '\0';
}
snprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds);
xSnprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds);
}
MeterClass UptimeMeter_class = {
@ -49,8 +49,9 @@ MeterClass UptimeMeter_class = {
.extends = Class(Meter),
.delete = Meter_delete
},
.setValues = UptimeMeter_setValues,
.updateValues = UptimeMeter_updateValues,
.defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0,
.attributes = UptimeMeter_attributes,
.name = "Uptime",

View File

@ -284,11 +284,19 @@ inline Object* Vector_get(Vector* this, int idx) {
#endif
#ifdef DEBUG
inline int Vector_size(Vector* this) {
assert(Vector_isConsistent(this));
return this->items;
}
#else
#define Vector_size(v_) ((v_)->items)
#endif
/*
static void Vector_merge(Vector* this, Vector* v2) {

View File

@ -70,8 +70,16 @@ extern Object* Vector_get(Vector* this, int idx);
#endif
#ifdef DEBUG
extern int Vector_size(Vector* this);
#else
#define Vector_size(v_) ((v_)->items)
#endif
/*
*/

View File

@ -5,11 +5,12 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <err.h>
#include <stdlib.h>
#include <string.h>
/*{
#include <err.h>
#include <assert.h>
#include <stdlib.h>
}*/
@ -29,7 +30,7 @@ void* xMalloc(size_t size) {
void* xCalloc(size_t nmemb, size_t size) {
void* data = calloc(nmemb, size);
if (!data) {
if (!data && nmemb > 0 && size > 0) {
fail();
}
return data;
@ -43,9 +44,27 @@ void* xRealloc(void* ptr, size_t size) {
return data;
}
char* xStrdup(const char* str) {
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
#undef xStrdup
#undef xStrdup_
#ifdef NDEBUG
# define xStrdup_ xStrdup
#else
# define xStrdup(str_) (assert(str_), xStrdup_(str_))
#endif
#ifndef __has_attribute // Clang's macro
# define __has_attribute(x) 0
#endif
#if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later
char* xStrdup_(const char* str) {
char* data = strdup(str);
if (!data && str) {
if (!data) {
fail();
}
return data;

View File

@ -7,6 +7,8 @@
#define _GNU_SOURCE
#endif
#include <err.h>
#include <assert.h>
#include <stdlib.h>
void* xMalloc(size_t size);
@ -15,6 +17,24 @@ void* xCalloc(size_t nmemb, size_t size);
void* xRealloc(void* ptr, size_t size);
char* xStrdup(const char* str);
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
#undef xStrdup
#undef xStrdup_
#ifdef NDEBUG
# define xStrdup_ xStrdup
#else
# define xStrdup(str_) (assert(str_), xStrdup_(str_))
#endif
#ifndef __has_attribute // Clang's macro
# define __has_attribute(x) 0
#endif
#if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later
char* xStrdup_(const char* str);
#endif

View File

@ -2,41 +2,45 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
AC_INIT([htop],[2.0.1],[hisham@gobolinux.org])
AC_INIT([htop],[3.0.0beta2],[hisham@gobolinux.org])
year=$(date +%Y)
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
# The following two lines are required by hwloc scripts
AC_USE_SYSTEM_EXTENSIONS
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Required by hwloc scripts
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([1.11])
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Checks for programs.
# ----------------------------------------------------------------------
AC_PROG_CC
AM_PROG_CC_C_O
AC_DISABLE_SHARED
AC_ENABLE_STATIC
AC_PROG_LIBTOOL
# Required by hwloc scripts
AC_USE_SYSTEM_EXTENSIONS
# Checks for platform.
# ----------------------------------------------------------------------
case "$target" in
*linux*)
case "$target_os" in
linux*|gnu*)
my_htop_platform=linux
;;
*freebsd*)
freebsd*|kfreebsd*)
my_htop_platform=freebsd
;;
*openbsd*)
openbsd*)
my_htop_platform=openbsd
;;
*darwin*)
dragonfly*)
my_htop_platform=dragonflybsd
;;
darwin*)
my_htop_platform=darwin
;;
*)
@ -74,15 +78,16 @@ AC_CHECK_FUNCS([memmove strncasecmp strstr strdup])
save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99"
AC_MSG_CHECKING([whether gcc -std=c99 option works])
AC_TRY_COMPILE(AC_INCLUDES_DEFAULT, [char *a; a = strdup("foo"); int i = 0; i++; // C99],
AC_MSG_RESULT([yes]),
AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.]))
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[AC_INCLUDES_DEFAULT], [[char *a; a = strdup("foo"); int i = 0; i++; // C99]])],
[AC_MSG_RESULT([yes])],
[AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.])])
CFLAGS="$save_cflags"
save_cflags="${CFLAGS}"
CFLAGS="$CFLAGS -Wextra"
AC_MSG_CHECKING([if compiler supports -Wextra])
AC_TRY_COMPILE([], [], [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],[
wextra_flag=-Wextra
AC_MSG_RESULT([yes])
],[
@ -96,14 +101,14 @@ AC_SUBST(wextra_flag)
# ----------------------------------------------------------------------
PROCDIR=/proc
AC_ARG_ENABLE(proc, [AC_HELP_STRING([--enable-proc], [use Linux-compatible proc filesystem])], enable_proc="yes", enable_proc="no")
AC_ARG_ENABLE(proc, [AS_HELP_STRING([--enable-proc], [use Linux-compatible proc filesystem])], enable_proc="yes", enable_proc="no")
if test "x$enable_proc" = xyes; then
# An enabled proc assumes we're emulating Linux.
my_htop_platform=linux
AC_DEFINE(HAVE_PROC, 1, [Define if using a Linux-compatible proc filesystem.])
fi
AC_ARG_WITH(proc, [AC_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])],
AC_ARG_WITH(proc, [AS_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])],
if test -n "$withval"; then
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem])
@ -118,28 +123,28 @@ if test "x$cross_compiling" = xno; then
fi
fi
AC_ARG_ENABLE(openvz, [AC_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no")
AC_ARG_ENABLE(openvz, [AS_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no")
if test "x$enable_openvz" = xyes; then
AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.])
fi
AC_ARG_ENABLE(cgroup, [AC_HELP_STRING([--enable-cgroup], [enable cgroups support])], ,enable_cgroup="no")
AC_ARG_ENABLE(cgroup, [AS_HELP_STRING([--enable-cgroup], [enable cgroups support])], ,enable_cgroup="no")
if test "x$enable_cgroup" = xyes; then
AC_DEFINE(HAVE_CGROUP, 1, [Define if cgroup support enabled.])
fi
AC_ARG_ENABLE(vserver, [AC_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no")
AC_ARG_ENABLE(vserver, [AS_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no")
if test "x$enable_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
fi
AC_ARG_ENABLE(ancient_vserver, [AC_HELP_STRING([--enable-ancient-vserver], [enable ancient VServer support (implies --enable-vserver)])], ,enable_ancient_vserver="no")
AC_ARG_ENABLE(ancient_vserver, [AS_HELP_STRING([--enable-ancient-vserver], [enable ancient VServer support (implies --enable-vserver)])], ,enable_ancient_vserver="no")
if test "x$enable_ancient_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.])
fi
AC_ARG_ENABLE(taskstats, [AC_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="yes")
AC_ARG_ENABLE(taskstats, [AS_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="yes")
if test "x$enable_taskstats" = xyes; then
AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.])
fi
@ -147,7 +152,12 @@ fi
# HTOP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART)
m4_define([HTOP_CHECK_SCRIPT],
[
if test ! -z "m4_toupper($HTOP_[$1]_CONFIG_SCRIPT)"; then
# to be used to set the path to ncurses*-config when cross-compiling
htop_config_script=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null)
else
htop_config_script=$([$4] --libs 2> /dev/null)
fi
htop_script_success=no
htop_save_LDFLAGS="$LDFLAGS"
if test ! "x$htop_config_script" = x; then
@ -157,7 +167,7 @@ m4_define([HTOP_CHECK_SCRIPT],
LIBS="$htop_config_script $LIBS "
htop_script_success=yes
], [])
LDFLAGS="$save_LDFLAGS"
LDFLAGS="$htop_save_LDFLAGS"
fi
if test "x$htop_script_success" = xno; then
[$5]
@ -173,9 +183,10 @@ m4_define([HTOP_CHECK_LIB],
], [$4])
])
AC_ARG_ENABLE(unicode, [AC_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes")
AC_ARG_ENABLE(unicode, [AS_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes")
if test "x$enable_unicode" = xyes; then
HTOP_CHECK_SCRIPT([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw5-config",
HTOP_CHECK_SCRIPT([ncurses], [addnwstr], [HAVE_LIBNCURSESW], "ncurses5-config",
HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW],
@ -183,7 +194,7 @@ if test "x$enable_unicode" = xyes; then
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.])
))))))
)))))))
AC_CHECK_HEADERS([ncursesw/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
@ -211,7 +222,7 @@ if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi
AC_ARG_ENABLE(linux_affinity, [AC_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
AC_MSG_CHECKING([for usable sched_setaffinity])
AC_RUN_IFELSE([
@ -232,13 +243,77 @@ 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
AC_ARG_ENABLE(hwloc, [AC_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity])],, enable_hwloc="no")
AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity])],, enable_hwloc="no")
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
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
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi
AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes")
if test "x$enable_perfcounters" = "xyes" -a "$my_htop_platform" = "linux"
then
AC_DEFINE(HAVE_PERFCOUNTERS, 1, [Define if hardware performance counter support should be enabled.])
AC_CHECK_HEADERS([linux/perf_counter.h], [have_perf_counter=yes],
[have_perf_counter=no])
AC_CHECK_HEADERS([linux/perf_event.h], [have_perf_event=yes],
[have_perf_event=no])
if test "x${have_perf_counter}" = xno -a "x${have_perf_event}" = xno; then
os=`uname -s -r`
AC_MSG_FAILURE([
------------------------------------------------------------
Could not locate linux/perf_count.h or linux/perf_event.h.
Are performance counters supported on this machine?
Linux 2.6.31+ is required.
uname reports: ${os}
------------------------------------------------------------])
fi
# Check for hardware architecture
no_target=yes
AC_MSG_CHECKING([hardware])
hw=`uname -m`
case $hw in
x86_64 | i386 | i686 ) :
AC_MSG_RESULT([x86])
AC_DEFINE([TARGET_X86], [1], [Define to 1 if the target is x86.])
no_target=no
;;
unknown ) :
AC_MSG_RESULT([unknown])
AC_MSG_WARN([Could not detect architecture])
;;
* ) :
AC_MSG_RESULT([$hw])
;;
esac
if test x$no_target = xyes; then
AC_DEFINE([NOTARGET], [1], [Define to 1 when no specific target is supported.])
fi
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes
then
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"])
CFLAGS+=" $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
LIBS+=" $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
fi
# Bail out on errors.
# ----------------------------------------------------------------------
if test ! -z "$missing_libraries"; then
@ -254,9 +329,24 @@ AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright messa
# ----------------------------------------------------------------------
AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux])
AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1])
AC_OUTPUT
if test "$my_htop_platform" = "unsupported"
then
echo ""
echo "****************************************************************"
echo "WARNING! This platform is not currently supported by htop."
echo ""
echo "The code will build, but it will produce a dummy version of htop"
echo "which shows no processes, using the files from the unsupported/"
echo "directory. This is meant to be a skeleton, to be used as a"
echo "starting point if you are porting htop to a new platform."
echo "****************************************************************"
echo ""
fi

View File

@ -26,6 +26,7 @@ typedef struct DarwinProcess_ {
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;
}*/
@ -47,6 +48,7 @@ DarwinProcess* DarwinProcess_new(Settings* settings) {
this->utime = 0;
this->stime = 0;
this->taskAccess = true;
return this;
}
@ -301,6 +303,10 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
Process* proc = (Process*) dp;
kern_return_t ret;
if (!dp->taskAccess) {
return;
}
if (proc->state == 'Z') {
return;
}
@ -308,6 +314,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
task_t port;
ret = task_for_pid(mach_task_self(), proc->pid, &port);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
@ -315,6 +322,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
@ -322,6 +330,7 @@ void DarwinProcess_scanThreads(DarwinProcess *dp) {
mach_msg_type_number_t thread_count;
ret = task_threads(port, &thread_list, &thread_count);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
mach_port_deallocate(mach_task_self(), port);
return;
}

View File

@ -19,6 +19,7 @@ typedef struct DarwinProcess_ {
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;

View File

@ -171,7 +171,8 @@ void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
DarwinProcess_scanThreads(proc);
// Disabled due to bug in macOS High Sierra
// DarwinProcess_scanThreads(proc);
super->totalTasks += 1;

View File

@ -25,6 +25,11 @@ in the source distribution for its full text.
#include "CPUMeter.h"
#include "BatteryMeter.h"
#include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
}*/
#ifndef CLAMP
@ -33,7 +38,7 @@ in the source distribution for its full text.
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
SignalItem Platform_signals[] = {
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
@ -69,7 +74,7 @@ SignalItem Platform_signals[] = {
{ .name = "31 SIGUSR2", .number = 31 },
};
unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
@ -78,7 +83,7 @@ ProcessFieldData Process_fields[] = {
[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 = " SESN ", .description = "Process's session 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, },
@ -97,7 +102,7 @@ ProcessFieldData Process_fields[] = {
[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, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
MeterClass* Platform_meterTypes[] = {
@ -166,7 +171,7 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
@ -280,13 +285,11 @@ char* Platform_getProcessEnv(pid_t pid) {
size_t size = endp - p;
env = xMalloc(size+2);
if (env) {
memcpy(env, p, size);
env[size] = 0;
env[size+1] = 0;
}
}
}
free(buf);
}
}

View File

@ -16,15 +16,19 @@ in the source distribution for its full text.
#include "BatteryMeter.h"
#include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ProcessField Platform_defaultFields[];
extern SignalItem Platform_signals[];
extern const SignalItem Platform_signals[];
extern unsigned int Platform_numberOfSignals;
extern const unsigned int Platform_numberOfSignals;
extern ProcessFieldData Process_fields[];

26
dragonflybsd/Battery.c Normal file
View File

@ -0,0 +1,26 @@
/*
htop - dragonflybsd/Battery.c
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "BatteryMeter.h"
#include <sys/sysctl.h>
void Battery_getData(double* level, ACPresence* isOnAC) {
int life;
size_t life_len = sizeof(life);
if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
*level = -1;
else
*level = life;
int acline;
size_t acline_len = sizeof(acline);
if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
*isOnAC = AC_ERROR;
else
*isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
}

15
dragonflybsd/Battery.h Normal file
View File

@ -0,0 +1,15 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Battery
#define HEADER_Battery
/*
htop - dragonflybsd/Battery.h
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

View File

@ -0,0 +1,35 @@
/*
htop - dragonflybsd/DragonFlyBSDCRT.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "CRT.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
void CRT_handleSIGSEGV(int sgn) {
(void) sgn;
CRT_done();
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n");
#ifdef HAVE_EXECINFO_H
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
fprintf(stderr, "\n Please include in your report the following backtrace: \n");
backtrace_symbols_fd(backtraceArray, size, 2);
fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
#else
fprintf(stderr, "\nPlease contact your DragonFlyBSD package maintainer!\n\n");
#endif
abort();
}

View File

@ -0,0 +1,19 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DragonFlyBSDCRT
#define HEADER_DragonFlyBSDCRT
/*
htop - dragonflybsd/DragonFlyBSDCRT.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_EXECINFO_H
#endif
void CRT_handleSIGSEGV(int sgn);
#endif

View File

@ -0,0 +1,166 @@
/*
htop - dragonflybsd/DragonFlyBSDProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Process.h"
#include "ProcessList.h"
#include "DragonFlyBSDProcess.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
/*{
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;
int jid;
char* jname;
} DragonFlyBSDProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
//#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#define Process_isUserlandThread(_process) (_process->nlwp > 1)
#endif
}*/
ProcessClass DragonFlyBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = DragonFlyBSDProcess_compare
},
.writeField = (Process_WriteField) DragonFlyBSDProcess_writeField,
};
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 (<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, },
[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_SIZE] = { .name = "M_SIZE", .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, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, },
[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 },
};
DragonFlyBSDProcess* DragonFlyBSDProcess_new(Settings* settings) {
DragonFlyBSDProcess* this = xCalloc(1, sizeof(DragonFlyBSDProcess));
Object_setClass(this, Class(DragonFlyBSDProcess));
Process_init(&this->super, settings);
return this;
}
void Process_delete(Object* cast) {
DragonFlyBSDProcess* this = (DragonFlyBSDProcess*) cast;
Process_done((Process*)cast);
free(this->jname);
free(this);
}
void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField field) {
DragonFlyBSDProcess* fp = (DragonFlyBSDProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) 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 JAIL:{
xSnprintf(buffer, n, "%-11s ", fp->jname); break;
if (buffer[11] != '\0') {
buffer[11] = ' ';
buffer[12] = '\0';
}
break;
}
default:
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
}
long DragonFlyBSDProcess_compare(const void* v1, const void* v2) {
DragonFlyBSDProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
p1 = (DragonFlyBSDProcess*)v1;
p2 = (DragonFlyBSDProcess*)v2;
} else {
p2 = (DragonFlyBSDProcess*)v1;
p1 = (DragonFlyBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
// add Platform-specific fields here
case JID:
return (p1->jid - p2->jid);
case JAIL:
return strcmp(p1->jname ? p1->jname : "", p2->jname ? p2->jname : "");
default:
return Process_compare(v1, v2);
}
}
bool Process_isThread(Process* this) {
DragonFlyBSDProcess* fp = (DragonFlyBSDProcess*) this;
if (fp->kernel == 1 )
return 1;
else
return (Process_isUserlandThread(this));
}

View File

@ -0,0 +1,56 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DragonFlyBSDProcess
#define HEADER_DragonFlyBSDProcess
/*
htop - dragonflybsd/DragonFlyBSDProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, 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;
int jid;
char* jname;
} DragonFlyBSDProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
//#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#define Process_isUserlandThread(_process) (_process->nlwp > 1)
#endif
extern ProcessClass DragonFlyBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
DragonFlyBSDProcess* DragonFlyBSDProcess_new(Settings* settings);
void Process_delete(Object* cast);
void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField field);
long DragonFlyBSDProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -0,0 +1,578 @@
/*
htop - DragonFlyBSDProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
#include "DragonFlyBSDProcessList.h"
#include "DragonFlyBSDProcess.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/param.h>
/*{
#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
char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
} CPUData;
typedef struct DragonFlyBSDProcessList_ {
ProcessList super;
kvm_t* kd;
unsigned long long int memWire;
unsigned long long int memActive;
unsigned long long int memInactive;
unsigned long long int memFree;
CPUData* cpus;
unsigned long *cp_time_o;
unsigned long *cp_time_n;
unsigned long *cp_times_o;
unsigned long *cp_times_n;
Hashtable *jails;
} DragonFlyBSDProcessList;
}*/
#define _UNUSED_ __attribute__((unused))
static int MIB_hw_physmem[2];
static int MIB_vm_stats_vm_v_page_count[4];
static int pageSize;
static int pageSizeKb;
static int MIB_vm_stats_vm_v_wire_count[4];
static int MIB_vm_stats_vm_v_active_count[4];
static int MIB_vm_stats_vm_v_cache_count[4];
static int MIB_vm_stats_vm_v_inactive_count[4];
static int MIB_vm_stats_vm_v_free_count[4];
static int MIB_vfs_bufspace[2];
static int MIB_kern_cp_time[2];
static int MIB_kern_cp_times[2];
static int kernelFScale;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
size_t len;
char errbuf[_POSIX2_LINE_MAX];
DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList));
ProcessList* pl = (ProcessList*) dfpl;
ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, pidWhiteList, userId);
// physical memory in system: hw.physmem
// physical page size: hw.pagesize
// usable pagesize : vm.stats.vm.v_page_size
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 = PAGE_SIZE;
pageSizeKb = PAGE_SIZE_KB;
} else {
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
len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len);
len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len);
int cpus = 1;
len = sizeof(cpus);
if (sysctlbyname("hw.ncpu", &cpus, &len, NULL, 0) != 0) {
cpus = 1;
}
size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len);
dfpl->cp_time_o = xCalloc(cpus, sizeof_cp_time_array);
dfpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array);
len = sizeof_cp_time_array;
// fetch initial single (or average) CPU clicks from kernel
sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_o, &len, NULL, 0);
// on smp box, fetch rest of initial CPU's clicks
if (cpus > 1) {
len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len);
dfpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array);
dfpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array);
len = cpus * sizeof_cp_time_array;
sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_o, &len, NULL, 0);
}
pl->cpuCount = MAX(cpus, 1);
if (cpus == 1 ) {
dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData));
} else {
// on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
dfpl->cpus = xRealloc(dfpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
len = sizeof(kernelFScale);
if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) {
//sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed
kernelFScale = 2048;
}
dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
if (dfpl->kd == NULL) {
errx(1, "kvm_open: %s", errbuf);
}
return pl;
}
void ProcessList_delete(ProcessList* this) {
const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this;
if (dfpl->kd) kvm_close(dfpl->kd);
if (dfpl->jails) {
Hashtable_delete(dfpl->jails);
}
free(dfpl->cp_time_o);
free(dfpl->cp_time_n);
free(dfpl->cp_times_o);
free(dfpl->cp_times_n);
free(dfpl->cpus);
ProcessList_done(this);
free(this);
}
static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) {
const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl;
int cpus = pl->cpuCount; // actual CPU count
int maxcpu = cpus; // max iteration (in case we have average + smp)
int cp_times_offset;
assert(cpus > 0);
size_t sizeof_cp_time_array;
unsigned long *cp_time_n; // old clicks state
unsigned long *cp_time_o; // current clicks state
unsigned long cp_time_d[CPUSTATES];
double cp_time_p[CPUSTATES];
// get averages or single CPU clicks
sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_n, &sizeof_cp_time_array, NULL, 0);
// get rest of CPUs
if (cpus > 1) {
// on smp systems DragonFlyBSD kernel concats all CPU states into one long array in
// kern.cp_times sysctl OID
// we store averages in dfpl->cpus[0], and actual cores after that
maxcpu = cpus + 1;
sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
}
for (int i = 0; i < maxcpu; i++) {
if (cpus == 1) {
// single CPU box
cp_time_n = dfpl->cp_time_n;
cp_time_o = dfpl->cp_time_o;
} else {
if (i == 0 ) {
// average
cp_time_n = dfpl->cp_time_n;
cp_time_o = dfpl->cp_time_o;
} else {
// specific smp cores
cp_times_offset = i - 1;
cp_time_n = dfpl->cp_times_n + (cp_times_offset * CPUSTATES);
cp_time_o = dfpl->cp_times_o + (cp_times_offset * CPUSTATES);
}
}
// diff old vs new
unsigned long long total_o = 0;
unsigned long long total_n = 0;
unsigned long long total_d = 0;
for (int s = 0; s < CPUSTATES; s++) {
cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
total_o += cp_time_o[s];
total_n += cp_time_n[s];
}
// totals
total_d = total_n - total_o;
if (total_d < 1 ) total_d = 1;
// save current state as old and calc percentages
for (int s = 0; s < CPUSTATES; ++s) {
cp_time_o[s] = cp_time_n[s];
cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
}
CPUData* cpuData = &(dfpl->cpus[i]);
cpuData->userPercent = cp_time_p[CP_USER];
cpuData->nicePercent = cp_time_p[CP_NICE];
cpuData->systemPercent = cp_time_p[CP_SYS];
cpuData->irqPercent = cp_time_p[CP_INTR];
cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR];
// this one is not really used, but we store it anyway
cpuData->idlePercent = cp_time_p[CP_IDLE];
}
}
static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl;
// @etosan:
// memory counter relationships seem to be these:
// total = active + wired + inactive + cache + free
// htop_used (unavail to anybody) = active + wired
// htop_cache (for cache meter) = buffers + cache
// user_free (avail to procs) = buffers + inactive + cache + free
size_t len = sizeof(pl->totalMem);
//disabled for now, as it is always smaller than phycal amount of memory...
//...to avoid "where is my memory?" questions
//sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0);
//pl->totalMem *= pageSizeKb;
sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0);
pl->totalMem /= 1024;
sysctl(MIB_vm_stats_vm_v_active_count, 4, &(dfpl->memActive), &len, NULL, 0);
dfpl->memActive *= pageSizeKb;
sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(dfpl->memWire), &len, NULL, 0);
dfpl->memWire *= pageSizeKb;
sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0);
pl->buffersMem /= 1024;
sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0);
pl->cachedMem *= pageSizeKb;
pl->usedMem = dfpl->memActive + dfpl->memWire;
//currently unused, same as with arc, custom meter perhaps
//sysctl(MIB_vm_stats_vm_v_inactive_count, 4, &(dfpl->memInactive), &len, NULL, 0);
//sysctl(MIB_vm_stats_vm_v_free_count, 4, &(dfpl->memFree), &len, NULL, 0);
//pl->freeMem = dfpl->memInactive + dfpl->memFree;
//pl->freeMem *= pageSizeKb;
struct kvm_swap swap[16];
int nswap = kvm_getswapinfo(dfpl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0);
pl->totalSwap = 0;
pl->usedSwap = 0;
for (int i = 0; i < nswap; i++) {
pl->totalSwap += swap[i].ksw_total;
pl->usedSwap += swap[i].ksw_used;
}
pl->totalSwap *= pageSizeKb;
pl->usedSwap *= pageSizeKb;
pl->sharedMem = 0; // currently unused
}
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
char** argv = kvm_getargv(kd, kproc, 0);
if (!argv) {
return xStrdup(kproc->kp_comm);
}
int len = 0;
for (int i = 0; argv[i]; i++) {
len += strlen(argv[i]) + 1;
}
char* comm = xMalloc(len);
char* at = comm;
*basenameEnd = 0;
for (int i = 0; argv[i]; i++) {
at = stpcpy(at, argv[i]);
if (!*basenameEnd) {
*basenameEnd = at - comm;
}
*at = ' ';
at++;
}
at--;
*at = '\0';
return comm;
}
static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* dfpl) {
size_t len;
char *jls; /* Jail list */
char *curpos;
char *nextpos;
if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) {
fprintf(stderr, "initial sysctlbyname / jail.list failed\n");
exit(3);
}
retry:
if (len == 0)
return;
jls = xMalloc(len);
if (jls == NULL) {
fprintf(stderr, "xMalloc failed\n");
exit(4);
}
if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) {
if (errno == ENOMEM) {
free(jls);
goto retry;
}
fprintf(stderr, "sysctlbyname / jail.list failed\n");
exit(5);
}
if (dfpl->jails) {
Hashtable_delete(dfpl->jails);
}
dfpl->jails = Hashtable_new(20, true);
curpos = jls;
while (curpos) {
int jailid;
char *str_hostname;
nextpos = strchr(curpos, '\n');
if (nextpos)
*nextpos++ = 0;
jailid = atoi(strtok(curpos, " "));
str_hostname = strtok(NULL, " ");
char *jname = (char *) (Hashtable_get(dfpl->jails, jailid));
if (jname == NULL) {
jname = xStrdup(str_hostname);
Hashtable_put(dfpl->jails, jailid, jname);
}
curpos = nextpos;
}
free(jls);
}
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
char* hostname;
char* jname;
if (jailid != 0 && dfpl->jails && (hostname = (char *)Hashtable_get(dfpl->jails, jailid))) {
jname = xStrdup(hostname);
} else {
jname = xStrdup("-");
}
return jname;
}
void ProcessList_goThroughEntries(ProcessList* this) {
DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this;
Settings* settings = this->settings;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
DragonFlyBSDProcessList_scanMemoryInfo(this);
DragonFlyBSDProcessList_scanCPUTime(this);
DragonFlyBSDProcessList_scanJails(dfpl);
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);
for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false;
bool _UNUSED_ isIdleProcess = false;
// note: dragonflybsd kernel processes all have the same pid, so we misuse the kernel thread address to give them a unique identifier
Process* proc = ProcessList_getProcess(this, kproc->kp_ktaddr ? (pid_t)kproc->kp_ktaddr : kproc->kp_pid, &preExisting, (Process_New) DragonFlyBSDProcess_new);
DragonFlyBSDProcess* dfp = (DragonFlyBSDProcess*) proc;
proc->show = ! ((hideKernelThreads && Process_isKernelThread(dfp)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
if (!preExisting) {
dfp->jid = kproc->kp_jailid;
if (kproc->kp_ktaddr && kproc->kp_flags & P_SYSTEM) {
// dfb kernel threads all have the same pid, so we misuse the kernel thread address to give them a unique identifier
proc->pid = (pid_t)kproc->kp_ktaddr;
dfp->kernel = 1;
} else {
proc->pid = kproc->kp_pid; // process ID
dfp->kernel = 0;
}
proc->ppid = kproc->kp_ppid; // parent process id
proc->tpgid = kproc->kp_tpgid; // tty process group id
//proc->tgid = kproc->kp_lwp.kl_tid; // thread group id
proc->tgid = kproc->kp_pid; // thread group id
proc->pgrp = kproc->kp_pgid; // process group id
proc->session = kproc->kp_sid;
proc->tty_nr = kproc->kp_tdev; // control terminal device number
proc->st_uid = kproc->kp_uid; // user ID
proc->processor = kproc->kp_lwp.kl_origcpu;
proc->starttime_ctime = kproc->kp_start.tv_sec;
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
ProcessList_add((ProcessList*)this, proc);
proc->comm = DragonFlyBSDProcessList_readProcessName(dfpl->kd, kproc, &proc->basenameOffset);
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
} else {
proc->processor = kproc->kp_lwp.kl_cpuid;
if(dfp->jid != kproc->kp_jailid) { // process can enter jail anytime
dfp->jid = kproc->kp_jailid;
free(dfp->jname);
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
}
if (proc->ppid != kproc->kp_ppid) { // if there are reapers in the system, process can get reparented anytime
proc->ppid = kproc->kp_ppid;
}
if(proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs)
proc->st_uid = kproc->kp_uid;
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
}
if (settings->updateProcessNames) {
free(proc->comm);
proc->comm = DragonFlyBSDProcessList_readProcessName(dfpl->kd, kproc, &proc->basenameOffset);
}
}
proc->m_size = kproc->kp_vm_map_size / 1024 / pageSizeKb;
proc->m_resident = kproc->kp_vm_rssize;
proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
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 * PAGE_SIZE_KB) / (double)(this->totalMem);
if (proc->percent_cpu > 0.1) {
// system idle process should own all CPU time left regardless of CPU count
if ( strcmp("idle", kproc->kp_comm) == 0 ) {
isIdleProcess = true;
}
}
if (kproc->kp_lwp.kl_pid != -1)
proc->priority = kproc->kp_lwp.kl_prio;
else
proc->priority = -kproc->kp_lwp.kl_tdprio;
switch(kproc->kp_lwp.kl_rtprio.type) {
case RTP_PRIO_REALTIME:
proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX + kproc->kp_lwp.kl_rtprio.prio;
break;
case RTP_PRIO_IDLE:
proc->nice = PRIO_MAX + 1 + kproc->kp_lwp.kl_rtprio.prio;
break;
case RTP_PRIO_THREAD:
proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX - kproc->kp_lwp.kl_rtprio.prio;
break;
default:
proc->nice = kproc->kp_nice;
break;
}
// would be nice if we could store multiple states in proc->state (as enum) and have writeField render them
switch (kproc->kp_stat) {
case SIDL: proc->state = 'I'; isIdleProcess = true; break;
case SACTIVE:
switch (kproc->kp_lwp.kl_stat) {
case LSSLEEP:
if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptable wait short/long
if (kproc->kp_lwp.kl_slptime >= MAXSLP) {
proc->state = 'I';
isIdleProcess = true;
} else {
proc->state = 'S';
}
else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptable lwkt wait
proc->state = 'S';
else if (kproc->kp_paddr) // uninterruptable wait
proc->state = 'D';
else // uninterruptable lwkt wait
proc->state = 'B';
break;
case LSRUN:
if (kproc->kp_lwp.kl_stat == LSRUN) {
if (!(kproc->kp_lwp.kl_tdflags & (TDF_RUNNING | TDF_RUNQ)))
proc->state = 'Q';
else
proc->state = 'R';
}
break;
case LSSTOP:
proc->state = 'T';
break;
default:
proc->state = 'A';
break;
}
break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
case SCORE: proc->state = 'C'; break;
default: proc->state = '?';
}
if (kproc->kp_flags & P_SWAPPEDOUT) {
proc->state = 'W';
}
if (kproc->kp_flags & P_TRACED) {
proc->state = 'T';
}
if (kproc->kp_flags & P_JAILED) {
proc->state = 'J';
}
if (Process_isKernelThread(dfp)) {
this->kernelThreads++;
}
this->totalTasks++;
if (proc->state == 'R')
this->runningTasks++;
proc->updated = true;
}
}

View File

@ -0,0 +1,73 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DragonFlyBSDProcessList
#define HEADER_DragonFlyBSDProcessList
/*
htop - DragonFlyBSDProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
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
char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
} CPUData;
typedef struct DragonFlyBSDProcessList_ {
ProcessList super;
kvm_t* kd;
unsigned long long int memWire;
unsigned long long int memActive;
unsigned long long int memInactive;
unsigned long long int memFree;
CPUData* cpus;
unsigned long *cp_time_o;
unsigned long *cp_time_n;
unsigned long *cp_times_o;
unsigned long *cp_times_n;
Hashtable *jails;
} DragonFlyBSDProcessList;
#define _UNUSED_ __attribute__((unused))
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
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* this);
#endif

210
dragonflybsd/Platform.c Normal file
View File

@ -0,0 +1,210 @@
/*
htop - dragonflybsd/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "DragonFlyBSDProcess.h"
#include "DragonFlyBSDProcessList.h"
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <vm/vm_param.h>
#include <time.h>
#include <math.h>
/*{
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
{ .name = " 3 SIGQUIT", .number = 3 },
{ .name = " 4 SIGILL", .number = 4 },
{ .name = " 5 SIGTRAP", .number = 5 },
{ .name = " 6 SIGABRT", .number = 6 },
{ .name = " 7 SIGEMT", .number = 7 },
{ .name = " 8 SIGFPE", .number = 8 },
{ .name = " 9 SIGKILL", .number = 9 },
{ .name = "10 SIGBUS", .number = 10 },
{ .name = "11 SIGSEGV", .number = 11 },
{ .name = "12 SIGSYS", .number = 12 },
{ .name = "13 SIGPIPE", .number = 13 },
{ .name = "14 SIGALRM", .number = 14 },
{ .name = "15 SIGTERM", .number = 15 },
{ .name = "16 SIGURG", .number = 16 },
{ .name = "17 SIGSTOP", .number = 17 },
{ .name = "18 SIGTSTP", .number = 18 },
{ .name = "19 SIGCONT", .number = 19 },
{ .name = "20 SIGCHLD", .number = 20 },
{ .name = "21 SIGTTIN", .number = 21 },
{ .name = "22 SIGTTOU", .number = 22 },
{ .name = "23 SIGIO", .number = 23 },
{ .name = "24 SIGXCPU", .number = 24 },
{ .name = "25 SIGXFSZ", .number = 25 },
{ .name = "26 SIGVTALRM", .number = 26 },
{ .name = "27 SIGPROF", .number = 27 },
{ .name = "28 SIGWINCH", .number = 28 },
{ .name = "29 SIGINFO", .number = 29 },
{ .name = "30 SIGUSR1", .number = 30 },
{ .name = "31 SIGUSR2", .number = 31 },
{ .name = "32 SIGTHR", .number = 32 },
{ .name = "33 SIGLIBRT", .number = 33 },
};
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
}
MeterClass* Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
&UptimeMeter_class,
&BatteryMeter_class,
&HostnameMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
int Platform_getUptime() {
struct timeval bootTime, currTime;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
size_t size = sizeof(bootTime);
int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
if (err) {
return -1;
}
gettimeofday(&currTime, NULL);
return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
struct loadavg loadAverage;
int mib[2] = { CTL_VM, VM_LOADAVG };
size_t size = sizeof(loadAverage);
int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
if (err) {
*one = 0;
*five = 0;
*fifteen = 0;
return;
}
*one = (double) loadAverage.ldavg[0] / loadAverage.fscale;
*five = (double) loadAverage.ldavg[1] / loadAverage.fscale;
*fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale;
}
int Platform_getMaxPid() {
int maxPid;
size_t size = sizeof(maxPid);
int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0);
if (err) {
return 999999;
}
return maxPid;
}
double Platform_setCPUValues(Meter* this, int cpu) {
DragonFlyBSDProcessList* fpl = (DragonFlyBSDProcessList*) this->pl;
int cpus = this->pl->cpuCount;
CPUData* cpuData;
if (cpus == 1) {
// single CPU box has everything in fpl->cpus[0]
cpuData = &(fpl->cpus[0]);
} else {
cpuData = &(fpl->cpus[cpu]);
}
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}
percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
// TODO
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
this->values[1] = pl->buffersMem;
this->values[2] = pl->cachedMem;
}
void Platform_setSwapValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
void Platform_setTasksValues(Meter* this) {
// TODO
(void)this; // prevent unused warning
}
char* Platform_getProcessEnv(pid_t pid) {
// TODO
(void)pid; // prevent unused warning
return NULL;
}

52
dragonflybsd/Platform.h Normal file
View File

@ -0,0 +1,52 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Platform
#define HEADER_Platform
/*
htop - dragonflybsd/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys);
extern MeterClass* Platform_meterTypes[];
int Platform_getUptime();
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid();
double Platform_setCPUValues(Meter* this, int cpu);
void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
void Platform_setTasksValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
#endif

View File

@ -61,7 +61,7 @@ ProcessFieldData Process_fields[] = {
[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 = " SESN ", .description = "Process's session 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, },
@ -92,7 +92,7 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
@ -117,9 +117,9 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
int n = sizeof(buffer) - 1;
switch ((int) field) {
// add FreeBSD-specific fields here
case JID: snprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JAIL:{
snprintf(buffer, n, "%-11s ", fp->jname); break;
xSnprintf(buffer, n, "%-11s ", fp->jname); break;
if (buffer[11] != '\0') {
buffer[11] = ' ';
buffer[12] = '\0';

View File

@ -151,10 +151,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
fpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array);
len = sizeof_cp_time_array;
// fetch intial single (or average) CPU clicks from kernel
// fetch initial single (or average) CPU clicks from kernel
sysctl(MIB_kern_cp_time, 2, fpl->cp_time_o, &len, NULL, 0);
// on smp box, fetch rest of intial CPU's clicks
// on smp box, fetch rest of initial CPU's clicks
if (cpus > 1) {
len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len);
fpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array);
@ -175,7 +175,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
len = sizeof(kernelFScale);
if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) {
//sane default for kernel provded CPU precentage scaling, at least on x86 machines, in case this sysctl call failed
//sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed
kernelFScale = 2048;
}
@ -398,7 +398,7 @@ char* FreeBSDProcessList_readJailName(struct kinfo_proc* kproc) {
jid = jail_get(jiov, 6, 0);
if (jid < 0) {
if (!jail_errmsg[0])
snprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno));
xSnprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno));
return NULL;
} else if (jid == kproc->ki_jid) {
jname = xStrdup(jnamebuf);
@ -427,7 +427,7 @@ void ProcessList_goThroughEntries(ProcessList* this) {
int cpus = this->cpuCount;
int count = 0;
struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_ALL, 0, &count);
struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count);
for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i];
@ -459,17 +459,17 @@ void ProcessList_goThroughEntries(ProcessList* this) {
fp->jname = FreeBSDProcessList_readJailName(kproc);
} else {
if(fp->jid != kproc->ki_jid) {
// proces can enter jail anytime
// process can enter jail anytime
fp->jid = kproc->ki_jid;
free(fp->jname);
fp->jname = FreeBSDProcessList_readJailName(kproc);
}
if (proc->ppid != kproc->ki_ppid) {
// if there are reapers in the system, proces can get reparented anytime
// if there are reapers in the system, process can get reparented anytime
proc->ppid = kproc->ki_ppid;
}
if(proc->st_uid != kproc->ki_uid) {
// some proceses change users (eg. to lower privs)
// some processes change users (eg. to lower privs)
proc->st_uid = kproc->ki_uid;
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
}
@ -495,10 +495,6 @@ void ProcessList_goThroughEntries(ProcessList* this) {
isIdleProcess = true;
}
}
if (isIdleProcess == false && proc->percent_cpu >= 99.8) {
// don't break formatting
proc->percent_cpu = 99.8;
}
proc->priority = kproc->ki_pri.pri_level - PZERO;

View File

@ -43,7 +43,7 @@ ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_R
int Platform_numberOfFields = LAST_PROCESSFIELD;
SignalItem Platform_signals[] = {
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
@ -80,7 +80,7 @@ SignalItem Platform_signals[] = {
{ .name = "33 SIGLIBRT", .number = 33 },
};
unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
@ -163,15 +163,15 @@ double Platform_setCPUValues(Meter* this, int cpu) {
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent * cpus;
v[CPU_METER_NORMAL] = cpuData->userPercent * cpus;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent * cpus;
v[CPU_METER_IRQ] = cpuData->irqPercent * cpus;
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent * cpus;
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}

View File

@ -24,9 +24,9 @@ extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern SignalItem Platform_signals[];
extern const SignalItem Platform_signals[];
extern unsigned int Platform_numberOfSignals;
extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys);

View File

@ -10,7 +10,7 @@ Htop is a free (GPL) ncurses-based process viewer for Linux.
.LP
It is similar to top, but allows you to scroll vertically and horizontally,
so you can see all the processes running on the system, along with their full
command lines, as well as viewing them as a process tree, selecting mutiple
command lines, as well as viewing them as a process tree, selecting multiple
processes and acting on them all at once.
.LP
Tasks related to processes (killing, renicing) can be done without
@ -48,8 +48,34 @@ Output version information and exit
The following commands are supported while in htop:
.LP
.TP 5
.B Arrows, PgUP, PgDn, Home, End
Scroll the process list.
.B Up, Alt-k
Select (highlight) the previous process in the process list. Scroll the list
if necessary.
.TP
.B Down, Alt-j
Select (highlight) the next process in the process list. Scroll the list if
necessary.
.TP
.B Left, Alt-h
Scroll the process list left.
.TP
.B Right, Alt-l
Scroll the process list right.
.TP
.B PgUp, PgDn
Scroll the process list up or down one window.
.TP
.B Home
Scroll to the top of the process list and select the first process.
.TP
.B End
Scroll to the bottom of the process list and select the last process.
.TP
.B Ctrl-A, ^
Scroll left to the beginning of the process entry (i.e. beginning of line).
.TP
.B Ctrl-E, $
Scroll right to the end of the process entry (i.e. end of line).
.TP
.B Space
Tag or untag a process. Commands that can operate on multiple processes,
@ -191,7 +217,7 @@ The parent process ID.
.B PGRP
The process's group ID.
.TP
.B SESSION (SESN)
.B SESSION (SID)
The process's session ID.
.TP
.B TTY_NR (TTY)
@ -344,6 +370,15 @@ The I/O scheduling class followed by the priority if the class supports it:
\fBB\fR for Best-effort
\fBid\fR for Idle
.TP
.B PERCENT_CPU_DELAY (CPUD%)
The percentage of time spent waiting for a CPU (while runnable). Requires CAP_NET_ADMIN.
.TP
.B PERCENT_IO_DELAY (IOD%)
The percentage of time spent waiting for the completion of synchronous block I/O. Requires CAP_NET_ADMIN.
.TP
.B PERCENT_SWAP_DELAY (SWAPD%)
The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN.
.TP
.B All other flags
Currently unsupported (always displays '-').

8
htop.c
View File

@ -203,12 +203,12 @@ int main(int argc, char** argv) {
MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->treeView);
MainPanel_updateTreeFunctions(panel, settings->screens[0]->treeView);
if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey;
settings->treeView = false;
settings->direction = 1;
settings->screens[0]->sortKey = flags.sortKey;
settings->screens[0]->treeView = false;
settings->screens[0]->direction = 1;
}
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));

View File

@ -28,6 +28,7 @@ Comment[sr@latin]=Prikaz sistemskih procesa
Comment[sv]=Visa systemprocesser
Comment[tr]=Sistem Süreçlerini Göster
Comment[uk]=Перегляд системних процесів
Comment[zh_CN]=显示系统进程
Comment[zh_TW]=顯示系統行程
Terminal=true
Exec=htop
@ -59,4 +60,6 @@ GenericName[sr@latin]=Prikazivač procesa
GenericName[sv]=Processvisning
GenericName[tr]=Süreç Görüntüleyici
GenericName[uk]=Перегляд процесів
GenericName[zh_CN]=进程查看器
GenericName[zh_TW]=行程檢視器
Keywords=system;process;task

View File

@ -41,11 +41,9 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
unsigned int nBatteries = 0;
memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
struct dirent result;
struct dirent* dirEntry;
while (nBatteries < MAX_BATTERIES) {
int err = readdir_r(batteryDir, &result, &dirEntry);
if (err || !dirEntry)
struct dirent* dirEntry = readdir(batteryDir);
if (!dirEntry)
break;
char* entryName = dirEntry->d_name;
if (strncmp(entryName, "BAT", 3))
@ -58,24 +56,28 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
unsigned long int total = 0;
for (unsigned int i = 0; i < nBatteries; i++) {
char infoPath[30];
snprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
FILE* file = fopen(infoPath, "r");
if (!file) {
break;
}
char line[50] = "";
char* line = NULL;
for (unsigned short int i = 0; i < lineNum; i++) {
char* ok = fgets(line, sizeof line, file);
if (!ok) break;
free(line);
line = String_readLine(file);
if (!line) break;
}
fclose(file);
if (!line) break;
char *foundNumStr = String_getToken(line, wordNum);
const unsigned long int foundNum = atoi(foundNumStr);
free(foundNumStr);
free(line);
total += foundNum;
}
@ -95,11 +97,9 @@ static ACPresence procAcpiCheck() {
return AC_ERROR;
}
struct dirent result;
struct dirent* dirEntry;
for (;;) {
int err = readdir_r((DIR *) dir, &result, &dirEntry);
if (err || !dirEntry)
struct dirent* dirEntry = readdir((DIR *) dir);
if (!dirEntry)
break;
char* entryName = (char *) dirEntry->d_name;
@ -108,7 +108,7 @@ static ACPresence procAcpiCheck() {
continue;
char statePath[50];
snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
xSnprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
FILE* file = fopen(statePath, "r");
if (!file) {
@ -116,14 +116,13 @@ static ACPresence procAcpiCheck() {
continue;
}
char line[100];
char* ok = fgets(line, sizeof line, file);
if (!ok) continue;
line[sizeof(line) - 1] = '\0';
char* line = String_readLine(file);
if (!line) continue;
fclose(file);
const char *isOnline = String_getToken(line, 2);
free(line);
if (strcmp(isOnline, "on-line") == 0) {
isOn = AC_PRESENT;
@ -178,25 +177,6 @@ static inline ssize_t xread(int fd, void *buf, size_t count) {
}
}
/**
* Returns a pointer to the suffix of `str` if its beginning matches `prefix`.
* Returns NULL if the prefix does not match.
* Examples:
* match("hello world", "hello "); -> "world"
* match("hello world", "goodbye "); -> NULL
*/
static inline const char* match(const char* str, const char* prefix) {
for (;;) {
if (*prefix == '\0') {
return str;
}
if (*prefix != *str) {
return NULL;
}
prefix++; str++;
}
}
static void Battery_getSysData(double* level, ACPresence* isOnAC) {
*level = 0;
@ -209,18 +189,16 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
unsigned long int totalFull = 0;
unsigned long int totalRemain = 0;
struct dirent result;
struct dirent* dirEntry;
for (;;) {
int err = readdir_r((DIR *) dir, &result, &dirEntry);
if (err || !dirEntry)
struct dirent* dirEntry = readdir((DIR *) dir);
if (!dirEntry)
break;
char* entryName = (char *) dirEntry->d_name;
const char filePath[50];
if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') {
snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
int fd = open(filePath, O_RDONLY);
if (fd == -1) {
closedir(dir);
@ -239,6 +217,8 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
bool full = false;
bool now = false;
while ((line = strsep(&buf, "\n")) != NULL) {
#define match(str,prefix) \
(String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL)
const char* ps = match(line, "POWER_SUPPLY_");
if (!ps) {
continue;
@ -265,12 +245,13 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) {
continue;
}
}
#undef match
} else if (entryName[0] == 'A') {
if (*isOnAC != AC_ERROR) {
continue;
}
snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
int fd = open(filePath, O_RDONLY);
if (fd == -1) {
closedir(dir);
@ -325,6 +306,9 @@ void Battery_getData(double* level, ACPresence* isOnAC) {
*level = -1;
*isOnAC = AC_ERROR;
}
if (*level > 100.0) {
*level = 100.0;
}
Battery_cacheLevel = *level;
Battery_cacheIsOnAC = *isOnAC;
Battery_cacheTime = now;

View File

@ -29,13 +29,6 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat
// READ FROM /sys
// ----------------------------------------
/**
* Returns a pointer to the suffix of `str` if its beginning matches `prefix`.
* Returns NULL if the prefix does not match.
* Examples:
* match("hello world", "hello "); -> "world"
* match("hello world", "goodbye "); -> NULL
*/
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

View File

@ -35,7 +35,7 @@ typedef int IOPriority;
#define IOPriority_error 0xffffffff
#define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7)
}*/

View File

@ -36,7 +36,7 @@ typedef int IOPriority;
#define IOPriority_error 0xffffffff
#define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 0)
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7)

View File

@ -19,7 +19,7 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) {
Panel_setHeader(this, "IO Priority:");
Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None));
if (currPrio == IOPriority_None) Panel_setSelected(this, 0);
struct { int klass; const char* name; } classes[] = {
static const struct { int klass; const char* name; } classes[] = {
{ .klass = IOPRIO_CLASS_RT, .name = "Realtime" },
{ .klass = IOPRIO_CLASS_BE, .name = "Best-effort" },
{ .klass = 0, .name = NULL }
@ -27,7 +27,7 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) {
for (int c = 0; classes[c].name; c++) {
for (int i = 0; i < 8; i++) {
char name[50];
snprintf(name, sizeof(name)-1, "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : ""));
xSnprintf(name, sizeof(name)-1, "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : ""));
IOPriority ioprio = IOPriority_tuple(classes[c].klass, i);
Panel_add(this, (Object*) ListItem_new(name, ioprio));
if (currPrio == ioprio) Panel_setSelected(this, Panel_size(this) - 1);

View File

@ -18,11 +18,24 @@ in the source distribution for its full text.
/*{
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -81,7 +94,23 @@ typedef enum LinuxProcessFields {
#endif
OOM = 114,
IO_PRIORITY = 115,
LAST_PROCESSFIELD = 116,
#ifdef HAVE_DELAYACCT
PERCENT_CPU_DELAY = 116,
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
@ -124,6 +153,35 @@ typedef struct LinuxProcess_ {
char* cgroup;
#endif
unsigned int oom;
char* ttyDevice;
#ifdef HAVE_DELAYACCT
unsigned long long int delay_read_time;
unsigned long long cpu_delay_total;
unsigned long long blkio_delay_total;
unsigned long long swapin_delay_total;
float cpu_delay_percent;
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
@ -140,11 +198,11 @@ 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, },
[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 = " SESN ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .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, },
[FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
@ -214,6 +272,22 @@ ProcessFieldData Process_fields[] = {
#endif
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPD% ", .description = "CPU delay %", .flags = 0, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWD% ", .description = "Swapin delay %", .flags = 0, },
#endif
#ifdef HAVE_PERFCOUNTERS
[IPC] = { .name = "IPC", .title = " IPC ", .description = "Executed instructions per cycle", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE | PROCESS_FLAG_LINUX_HPC_INSN, },
[MCYCLE] = { .name = "MCYCLE", .title = " Mcycle ", .description = "Cycles (millions)", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE, },
[MINSTR] = { .name = "MINSTR", .title = " Minstr ", .description = "Instructions (millions)", .flags = PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_MISS] = { .name = "PERCENT_MISS", .title = "MIS% ", .description = "Cache misses per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_MISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_BMISS] = { .name = "PERCENT_BMISS", .title = "BrM% ", .description = "Branch misprediction per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_BMISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[L1DREADS] = { .name = "L1DREADS", .title = " L1Dread ", .description = "L1 data cache: reads (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DR, },
[L1DRMISSES] = { .name = "L1DRMISSES", .title = " R miss ", .description = "L1 data cache: reads misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DRM, },
[L1DWRITES] = { .name = "L1DWRITES", .title = " L1Dwrite ", .description = "L1D data cache: writes (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DW, },
[L1DWMISSES] = { .name = "L1DWMISSES", .title = " W miss ", .description = "L1D data cache: write misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DWM, },
#endif
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
@ -226,7 +300,7 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = SESSION, .label = "SID" },
{ .id = OOM, .label = "OOM" },
{ .id = 0, .label = NULL },
};
@ -254,6 +328,11 @@ void Process_delete(Object* cast) {
#ifdef HAVE_CGROUP
free(this->cgroup);
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter_delete(this->cycleCounter);
PerfCounter_delete(this->insnCounter);
#endif
free(this->ttyDevice);
free(this);
}
@ -285,6 +364,45 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) {
return (LinuxProcess_updateIOPriority(this) == ioprio);
}
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
static char* perfFmt[] = {
"%6.2f ",
NULL,
NULL,
NULL,
NULL,
NULL,
"%6.1f ",
"%7.1f ",
"%8.2f ",
"%9.1f ",
};
static char* perfNA[] = {
" N/A ",
NULL,
NULL,
NULL,
NULL,
NULL,
" N/A ",
" N/A ",
" N/A ",
" N/A ",
};
static inline void LinuxProcess_printPerfCounter(float val, int len, char* buffer, int n, int* attr) {
if (val != -1) {
xSnprintf(buffer, n, perfFmt[len], val);
} else {
xSnprintf(buffer, n, perfNA[len]);
*attr = CRT_colors[PROCESS_SHADOW];
}
}
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
LinuxProcess* lp = (LinuxProcess*) this;
bool coloring = this->settings->highlightMegabytes;
@ -292,6 +410,15 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int)field) {
case TTY_NR: {
if (lp->ttyDevice) {
xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
} else {
attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "? ");
}
break;
}
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 * PAGE_SIZE_KB, coloring); return;
@ -321,34 +448,50 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
}
#endif
#ifdef HAVE_OPENVZ
case CTID: snprintf(buffer, n, "%7u ", lp->ctid); break;
case VPID: snprintf(buffer, n, Process_pidFormat, lp->vpid); break;
case CTID: xSnprintf(buffer, n, "%7u ", lp->ctid); break;
case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break;
#endif
#ifdef HAVE_VSERVER
case VXID: snprintf(buffer, n, "%5u ", lp->vxid); break;
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
#endif
#ifdef HAVE_CGROUP
case CGROUP: snprintf(buffer, n, "%-10s ", lp->cgroup); break;
case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup); break;
#endif
case OOM: snprintf(buffer, n, Process_pidFormat, lp->oom); break;
case OOM: xSnprintf(buffer, n, Process_pidFormat, lp->oom); break;
case IO_PRIORITY: {
int klass = IOPriority_class(lp->ioPriority);
if (klass == IOPRIO_CLASS_NONE) {
// see note [1] above
snprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5);
xSnprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5);
} else if (klass == IOPRIO_CLASS_BE) {
snprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority));
xSnprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority));
} else if (klass == IOPRIO_CLASS_RT) {
attr = CRT_colors[PROCESS_HIGH_PRIORITY];
snprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority));
} else if (lp->ioPriority == IOPriority_Idle) {
xSnprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority));
} else if (klass == IOPRIO_CLASS_IDLE) {
attr = CRT_colors[PROCESS_LOW_PRIORITY];
snprintf(buffer, n, "id ");
xSnprintf(buffer, n, "id ");
} else {
snprintf(buffer, n, "?? ");
xSnprintf(buffer, n, "?? ");
}
break;
}
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break;
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break;
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break;
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: Process_printPercentage(lp->pMiss, buffer, n, &attr); break;
case PERCENT_BMISS: Process_printPercentage(lp->pBMiss, buffer, n, &attr); break;
case IPC: LinuxProcess_printPerfCounter(lp->ipc, 0, buffer, n, &attr); break;
case MCYCLE: LinuxProcess_printPerfCounter(lp->mcycle, 8, buffer, n, &attr); break;
case MINSTR: LinuxProcess_printPerfCounter(lp->minstr, 8, buffer, n, &attr); break;
case L1DREADS: LinuxProcess_printPerfCounter(lp->l1dr, 9, buffer, n, &attr); break;
case L1DRMISSES: LinuxProcess_printPerfCounter(lp->l1drm, 9, buffer, n, &attr); break;
case L1DWRITES: LinuxProcess_printPerfCounter(lp->l1dw, 9, buffer, n, &attr); break;
case L1DWMISSES: LinuxProcess_printPerfCounter(lp->l1dwm, 9, buffer, n, &attr); break;
#endif
default:
Process_writeField((Process*)this, str, field);
return;
@ -356,10 +499,12 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
RichString_append(str, attr, buffer);
}
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2) {
LinuxProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
if (settings->ss->direction == 1) {
p1 = (LinuxProcess*)v1;
p2 = (LinuxProcess*)v2;
} else {
@ -367,7 +512,7 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
p1 = (LinuxProcess*)v2;
}
long long diff;
switch ((int)settings->sortKey) {
switch ((int)settings->ss->sortKey) {
case M_DRS:
return (p2->m_drs - p1->m_drs);
case M_DT:
@ -410,6 +555,22 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
#endif
case OOM:
return (p2->oom - p1->oom);
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: COMPARE_FIELD(cpu_delay_percent);
case PERCENT_IO_DELAY: COMPARE_FIELD(blkio_delay_percent);
case PERCENT_SWAP_DELAY: COMPARE_FIELD(swapin_delay_percent);
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: COMPARE_FIELD(pMiss);
case PERCENT_BMISS: COMPARE_FIELD(pBMiss);
case IPC: COMPARE_FIELD(ipc);
case MCYCLE: COMPARE_FIELD(mcycle);
case MINSTR: COMPARE_FIELD(minstr);
case L1DREADS: COMPARE_FIELD(l1dr);
case L1DRMISSES: COMPARE_FIELD(l1drm);
case L1DWRITES: COMPARE_FIELD(l1dw);
case L1DWMISSES: COMPARE_FIELD(l1dwm);
#endif
case IO_PRIORITY:
return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2);
default:
@ -422,4 +583,3 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
bool Process_isThread(Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this));
}

View File

@ -10,11 +10,24 @@ in the source distribution for its full text.
*/
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -73,7 +86,23 @@ typedef enum LinuxProcessFields {
#endif
OOM = 114,
IO_PRIORITY = 115,
LAST_PROCESSFIELD = 116,
#ifdef HAVE_DELAYACCT
PERCENT_CPU_DELAY = 116,
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
@ -116,6 +145,35 @@ typedef struct LinuxProcess_ {
char* cgroup;
#endif
unsigned int oom;
char* ttyDevice;
#ifdef HAVE_DELAYACCT
unsigned long long int delay_read_time;
unsigned long long cpu_delay_total;
unsigned long long blkio_delay_total;
unsigned long long swapin_delay_total;
float cpu_delay_percent;
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
@ -151,11 +209,16 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio);
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -27,6 +27,16 @@ in the source distribution for its full text.
#include <sys/types.h>
#include <fcntl.h>
#ifdef HAVE_DELAYACCT
#include <netlink/attr.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <linux/taskstats.h>
#endif
/*{
#include "ProcessList.h"
@ -59,11 +69,23 @@ typedef struct CPUData_ {
unsigned long long int guestPeriod;
} CPUData;
typedef struct TtyDriver_ {
char* path;
unsigned int major;
unsigned int minorFrom;
unsigned int minorTo;
} TtyDriver;
typedef struct LinuxProcessList_ {
ProcessList super;
CPUData* cpus;
TtyDriver* ttyDrivers;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
int netlink_family;
#endif
} LinuxProcessList;
#ifndef PROCDIR
@ -78,6 +100,10 @@ typedef struct LinuxProcessList_ {
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif
#ifndef PROCTTYDRIVERSFILE
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
#endif
#ifndef PROC_LINE_LENGTH
#define PROC_LINE_LENGTH 512
#endif
@ -88,11 +114,124 @@ typedef struct LinuxProcessList_ {
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
static ssize_t xread(int fd, void *buf, size_t count) {
// Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
size_t alreadyRead = 0;
for(;;) {
ssize_t res = read(fd, buf, count);
if (res == -1 && errno == EINTR) continue;
if (res > 0) {
buf = ((char*)buf)+res;
count -= res;
alreadyRead += res;
}
if (res == -1) return -1;
if (count == 0 || res == 0) return alreadyRead;
}
}
static int sortTtyDrivers(const void* va, const void* vb) {
TtyDriver* a = (TtyDriver*) va;
TtyDriver* b = (TtyDriver*) vb;
return (a->major == b->major) ? (a->minorFrom - b->minorFrom) : (a->major - b->major);
}
static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
TtyDriver* ttyDrivers;
int fd = open(PROCTTYDRIVERSFILE, O_RDONLY);
if (fd == -1)
return;
char* buf = NULL;
int bufSize = MAX_READ;
int bufLen = 0;
for(;;) {
buf = realloc(buf, bufSize);
int size = xread(fd, buf + bufLen, MAX_READ);
if (size <= 0) {
buf[bufLen] = '\0';
close(fd);
break;
}
bufLen += size;
bufSize += MAX_READ;
}
if (bufLen == 0) {
free(buf);
return;
}
int numDrivers = 0;
int allocd = 10;
ttyDrivers = malloc(sizeof(TtyDriver) * allocd);
char* at = buf;
while (*at != '\0') {
at = strchr(at, ' '); // skip first token
while (*at == ' ') at++; // skip spaces
char* token = at; // mark beginning of path
at = strchr(at, ' '); // find end of path
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].path = strdup(token); // save
while (*at == ' ') at++; // skip spaces
token = at; // mark beginning of major
at = strchr(at, ' '); // find end of major
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].major = atoi(token); // save
while (*at == ' ') at++; // skip spaces
token = at; // mark beginning of minorFrom
while (*at >= '0' && *at <= '9') at++; //find end of minorFrom
if (*at == '-') { // if has range
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].minorFrom = atoi(token); // save
token = at; // mark beginning of minorTo
at = strchr(at, ' '); // find end of minorTo
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].minorTo = atoi(token); // save
} else { // no range
*at = '\0'; at++; // clear and skip
ttyDrivers[numDrivers].minorFrom = atoi(token); // save
ttyDrivers[numDrivers].minorTo = atoi(token); // save
}
at = strchr(at, '\n'); // go to end of line
at++; // skip
numDrivers++;
if (numDrivers == allocd) {
allocd += 10;
ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * allocd);
}
}
free(buf);
numDrivers++;
ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
ttyDrivers[numDrivers - 1].path = NULL;
qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers);
this->ttyDrivers = ttyDrivers;
}
#ifdef HAVE_DELAYACCT
static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
this->netlink_socket = nl_socket_alloc();
if (this->netlink_socket == NULL) {
return;
}
if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
return;
}
this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
}
#endif
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
ProcessList_init(pl, Class(LinuxProcess), usersTable, pidWhiteList, userId);
LinuxProcessList_initTtyDrivers(this);
#ifdef HAVE_DELAYACCT
LinuxProcessList_initNetlinkSocket(this);
#endif
// Update CPU count:
FILE* file = fopen(PROCSTATFILE, "r");
if (file == NULL) {
@ -122,25 +261,21 @@ void ProcessList_delete(ProcessList* pl) {
LinuxProcessList* this = (LinuxProcessList*) pl;
ProcessList_done(pl);
free(this->cpus);
if (this->ttyDrivers) {
for(int i = 0; this->ttyDrivers[i].path; i++) {
free(this->ttyDrivers[i].path);
}
free(this->ttyDrivers);
}
#ifdef HAVE_DELAYACCT
if (this->netlink_socket) {
nl_close(this->netlink_socket);
nl_socket_free(this->netlink_socket);
}
#endif
free(this);
}
static ssize_t xread(int fd, void *buf, size_t count) {
// Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
size_t alreadyRead = 0;
for(;;) {
ssize_t res = read(fd, buf, count);
if (res == -1 && errno == EINTR) continue;
if (res > 0) {
buf = ((char*)buf)+res;
count -= res;
alreadyRead += res;
}
if (res == -1) return -1;
if (count == 0 || res == 0) return alreadyRead;
}
}
static double jiffy = 0.0;
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
@ -152,7 +287,7 @@ static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command, int* commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1)
return false;
@ -230,7 +365,7 @@ static bool LinuxProcessList_statProcessDir(Process* process, const char* dirnam
char filename[MAX_NAME+1];
filename[MAX_NAME] = '\0';
snprintf(filename, MAX_NAME, "%s/%s", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s", dirname, name);
struct stat sstat;
int statok = stat(filename, &sstat);
if (statok == -1)
@ -252,11 +387,20 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
char filename[MAX_NAME+1];
filename[MAX_NAME] = '\0';
snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1) {
process->io_rate_read_bps = -1;
process->io_rate_write_bps = -1;
process->io_rchar = -1LL;
process->io_wchar = -1LL;
process->io_syscr = -1LL;
process->io_syscw = -1LL;
process->io_read_bytes = -1LL;
process->io_write_bytes = -1LL;
process->io_cancelled_write_bytes = -1LL;
process->io_rate_read_time = -1LL;
process->io_rate_write_time = -1LL;
return;
}
@ -312,7 +456,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1)
return false;
@ -342,7 +486,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* d
return;
}
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
return;
@ -365,7 +509,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* d
static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
FILE* file = fopen(filename, "r");
if (!file) {
process->cgroup = xStrdup("");
@ -400,7 +544,7 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* d
static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
return;
@ -431,10 +575,11 @@ static void LinuxProcessList_readVServerData(LinuxProcess* process, const char*
static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
if (!file) {
return;
}
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, PROC_LINE_LENGTH, file)) {
unsigned int oom;
@ -446,6 +591,146 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirn
fclose(file);
}
#ifdef HAVE_DELAYACCT
static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
struct nlmsghdr *nlhdr;
struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
struct nlattr *nlattr;
struct taskstats *stats;
int rem;
unsigned long long int timeDelta;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;
nlhdr = nlmsg_hdr(nlmsg);
if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
return NL_SKIP;
}
if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
stats = nla_data(nla_next(nla_data(nlattr), &rem));
assert(lp->super.pid == stats->ac_pid);
timeDelta = (stats->ac_etime*1000 - lp->delay_read_time);
#define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x;
#define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100);
lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total);
lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total);
lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC
#undef BOUNDS
lp->swapin_delay_total = stats->swapin_delay_total;
lp->blkio_delay_total = stats->blkio_delay_total;
lp->cpu_delay_total = stats->cpu_delay_total;
lp->delay_read_time = stats->ac_etime*1000;
}
return NL_OK;
}
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
struct nl_msg *msg;
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
return;
}
if (! (msg = nlmsg_alloc())) {
return;
}
if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
nlmsg_free(msg);
}
if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) {
nlmsg_free(msg);
}
if (nl_send_sync(this->netlink_socket, msg) < 0) {
process->swapin_delay_percent = -1;
process->blkio_delay_percent = -1;
process->cpu_delay_percent = -1;
return;
}
if (nl_recvmsgs_default(this->netlink_socket) < 0) {
return;
}
}
#endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
static void LinuxProcessList_readPerfCounters(LinuxProcess* lp, uint64_t flags) {
READ_COUNTER(c, lp->cycleCounter, PROCESS_FLAG_LINUX_HPC_CYCLE, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
READ_COUNTER(i, lp->insnCounter, PROCESS_FLAG_LINUX_HPC_INSN, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
READ_COUNTER(m, lp->missCounter, PROCESS_FLAG_LINUX_HPC_MISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES);
READ_COUNTER(b, lp->brCounter, PROCESS_FLAG_LINUX_HPC_BMISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES);
READ_COUNTER(r, lp->l1drCounter, PROCESS_FLAG_LINUX_HPC_L1DR, PERF_TYPE_HW_CACHE, L1DR);
READ_COUNTER(R, lp->l1drmCounter, PROCESS_FLAG_LINUX_HPC_L1DRM, PERF_TYPE_HW_CACHE, L1DRM);
READ_COUNTER(w, lp->l1dwCounter, PROCESS_FLAG_LINUX_HPC_L1DW, PERF_TYPE_HW_CACHE, L1DW);
READ_COUNTER(W, lp->l1dwmCounter, PROCESS_FLAG_LINUX_HPC_L1DWM, PERF_TYPE_HW_CACHE, L1DWM);
SET_IF(cOk, lp->mcycle, (double)cDelta / 1000000);
SET_IF(iOk, lp->minstr, (double)iDelta / 1000000);
SET_IFNZ(cOk && iOk, cDelta, lp->ipc, (double)iDelta / cDelta);
SET_IFNZ(mOk && iOk, iDelta, lp->pMiss, 100 * ((double)mDelta / iDelta));
SET_IFNZ(bOk && iOk, iDelta, lp->pBMiss, 100 * ((double)bDelta / iDelta));
SET_IF(rOk, lp->l1dr, (double)rDelta / 1000);
SET_IF(ROk, lp->l1drm, (double)RDelta / 1000);
SET_IF(wOk, lp->l1dw, (double)wDelta / 1000);
SET_IF(WOk, lp->l1dwm, (double)WDelta / 1000);
}
#endif
static void setCommand(Process* process, const char* command, int len) {
if (process->comm && process->commLen >= len) {
strncpy(process->comm, command, len + 1);
@ -461,7 +746,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
return true;
char filename[MAX_NAME+1];
snprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1)
return false;
@ -470,30 +755,78 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
int amtRead = xread(fd, command, sizeof(command) - 1);
close(fd);
int tokenEnd = 0;
if (amtRead > 0) {
for (int i = 0; i < amtRead; i++)
int lastChar = 0;
if (amtRead <= 0) {
return false;
}
for (int i = 0; i < amtRead; i++) {
if (command[i] == '\0' || command[i] == '\n') {
if (tokenEnd == 0) {
tokenEnd = i;
}
command[i] = ' ';
} else {
lastChar = i;
}
}
if (tokenEnd == 0) {
tokenEnd = amtRead;
}
command[amtRead] = '\0';
command[lastChar + 1] = '\0';
process->basenameOffset = tokenEnd;
setCommand(process, command, amtRead);
setCommand(process, command, lastChar);
return true;
}
static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned int tty_nr) {
unsigned int maj = major(tty_nr);
unsigned int min = minor(tty_nr);
int i = -1;
for (;;) {
i++;
if ((!ttyDrivers[i].path) || maj < ttyDrivers[i].major) {
break;
}
if (maj > ttyDrivers[i].major) {
continue;
}
if (min < ttyDrivers[i].minorFrom) {
break;
}
if (min > ttyDrivers[i].minorTo) {
continue;
}
unsigned int idx = min - ttyDrivers[i].minorFrom;
struct stat sstat;
char* fullPath;
for(;;) {
asprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx);
int err = stat(fullPath, &sstat);
if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
free(fullPath);
asprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx);
err = stat(fullPath, &sstat);
if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
free(fullPath);
if (idx == min) break;
idx = min;
}
int err = stat(ttyDrivers[i].path, &sstat);
if (err == 0 && tty_nr == sstat.st_rdev) return strdup(ttyDrivers[i].path);
}
char* out;
asprintf(&out, "/dev/%u:%u", maj, min);
return out;
}
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
ProcessList* pl = (ProcessList*) this;
DIR* dir;
struct dirent* entry;
Settings* settings = pl->settings;
ScreenSettings* ss = settings->ss;
time_t curTime = tv.tv_sec;
#ifdef HAVE_TASKSTATS
@ -535,11 +868,11 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
LinuxProcess* lp = (LinuxProcess*) proc;
char subdirname[MAX_NAME+1];
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
xSnprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
#ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO)
if (ss->flags & PROCESS_FLAG_IO)
LinuxProcessList_readIoFile(lp, dirname, name, now);
#endif
@ -551,9 +884,14 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
char command[MAX_NAME+1];
unsigned long long int lasttimes = (lp->utime + lp->stime);
int commLen = 0;
unsigned int tty_nr = proc->tty_nr;
if (! LinuxProcessList_readStatFile(proc, dirname, name, command, &commLen))
goto errorReadingProcess;
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO)
LinuxProcess_updateIOPriority(lp);
float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
@ -568,13 +906,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) {
LinuxProcessList_readOpenVZData(lp, dirname, name);
}
#endif
#ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) {
LinuxProcessList_readVServerData(lp, dirname, name);
}
#endif
@ -592,14 +930,23 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
}
}
#ifdef HAVE_DELAYACCT
LinuxProcessList_readDelayAcctData(this, lp);
#endif
#ifdef HAVE_CGROUP
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
if (ss->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcessList_readCGroupFile(lp, dirname, name);
#endif
if (settings->flags & PROCESS_FLAG_LINUX_OOM)
if (ss->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcessList_readOomData(lp, dirname, name);
#ifdef HAVE_PERFCOUNTERS
if (ss->flags & PROCESS_FLAG_LINUX_HPC)
LinuxProcessList_readPerfCounters(lp, ss->flags);
#endif
if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
setCommand(proc, command, commLen);

View File

@ -9,6 +9,9 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_DELAYACCT
#endif
#include "ProcessList.h"
@ -40,11 +43,23 @@ typedef struct CPUData_ {
unsigned long long int guestPeriod;
} CPUData;
typedef struct TtyDriver_ {
char* path;
unsigned int major;
unsigned int minorFrom;
unsigned int minorTo;
} TtyDriver;
typedef struct LinuxProcessList_ {
ProcessList super;
CPUData* cpus;
TtyDriver* ttyDrivers;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
int netlink_family;
#endif
} LinuxProcessList;
#ifndef PROCDIR
@ -59,6 +74,10 @@ typedef struct LinuxProcessList_ {
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif
#ifndef PROCTTYDRIVERSFILE
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
#endif
#ifndef PROC_LINE_LENGTH
#define PROC_LINE_LENGTH 512
#endif
@ -68,6 +87,10 @@ typedef struct LinuxProcessList_ {
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#ifdef HAVE_DELAYACCT
#endif
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
@ -89,6 +112,58 @@ void ProcessList_delete(ProcessList* pl);
#endif
#ifdef HAVE_DELAYACCT
#endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#endif
void ProcessList_goThroughEntries(ProcessList* super);
#endif

135
linux/PerfCounter.c Normal file
View File

@ -0,0 +1,135 @@
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include "PerfCounter.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "XAlloc.h"
/*{
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
}*/
int PerfCounter_openFds = 0;
static int PerfCounter_fdLimit = -1;
static void PerfCounter_initFdLimit() {
char name[100] = { 0 }; /* needs to fit the name /proc/xxxx/limits */
snprintf(name, sizeof(name) - 1, "/proc/%d/limits", getpid());
FILE* f = fopen(name, "r");
if (f) {
char line[100];
while (fgets(line, 100, f)) {
int n = sscanf(line, "Max open files %d", &PerfCounter_fdLimit);
if (n) {
break;
}
}
fclose(f);
}
PerfCounter_fdLimit -= 20; /* keep some slack */
if (PerfCounter_fdLimit == 0) { /* something went wrong */
PerfCounter_fdLimit = 200; /* reasonable default? */
}
}
static long perf_event_open(struct STRUCT_NAME *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
int ret = syscall(SYSCALL_NUM, hw_event, pid, cpu, group_fd, flags);
#if defined(__x86_64__) || defined(__i386__)
if (ret < 0 && ret > -4096) {
errno = -ret;
ret = -1;
}
#endif
return ret;
}
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config) {
if (PerfCounter_fdLimit == -1) {
PerfCounter_initFdLimit();
}
PerfCounter* this = xCalloc(sizeof(PerfCounter), 1);
this->pid = pid;
this->events.disabled = 0;
this->events.pinned = 1;
this->events.exclude_hv = 1;
this->events.exclude_kernel = 1;
this->events.type = type;
this->events.config = config;
if (PerfCounter_openFds < PerfCounter_fdLimit) {
this->fd = perf_event_open(&this->events, pid, -1, -1, 0);
} else {
this->fd = -1;
}
if (this->fd != -1) {
PerfCounter_openFds++;
}
return this;
}
void PerfCounter_delete(PerfCounter* this) {
if (!this) {
return;
}
if (this->fd != -1) {
PerfCounter_openFds--;
}
close(this->fd);
free(this);
}
bool PerfCounter_read(PerfCounter* this) {
if (this->fd == -1) {
return false;
}
uint64_t value;
int r = read(this->fd, &value, sizeof(value));
if (r != sizeof(value)) {
close(this->fd);
this->fd = perf_event_open(&this->events, this->pid, -1, -1, 0);
return false;
}
this->prevValue = this->value;
this->value = value;
return true;
}

54
linux/PerfCounter.h Normal file
View File

@ -0,0 +1,54 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_PerfCounter
#define HEADER_PerfCounter
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
extern int PerfCounter_openFds;
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config);
void PerfCounter_delete(PerfCounter* this);
bool PerfCounter_read(PerfCounter* this);
#endif

View File

@ -22,6 +22,7 @@ in the source distribution for its full text.
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "LinuxProcess.h"
#include "CRT.h"
#include <math.h>
#include <assert.h>
@ -47,7 +48,7 @@ ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_R
int Platform_numberOfFields = LAST_PROCESSFIELD;
SignalItem Platform_signals[] = {
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
@ -84,7 +85,7 @@ SignalItem Platform_signals[] = {
{ .name = "31 SIGSYS", .number = 31 },
};
unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
static Htop_Reaction Platform_actionSetIOPriority(State* st) {
Panel* panel = st->panel;
@ -215,7 +216,7 @@ void Platform_setSwapValues(Meter* this) {
char* Platform_getProcessEnv(pid_t pid) {
char procname[32+1];
snprintf(procname, 32, "/proc/%d/environ", pid);
xSnprintf(procname, 32, "/proc/%d/environ", pid);
FILE* fd = fopen(procname, "r");
char *env = NULL;
if (fd) {

View File

@ -23,9 +23,9 @@ extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern SignalItem Platform_signals[];
extern const SignalItem Platform_signals[];
extern unsigned int Platform_numberOfSignals;
extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys);

View File

@ -99,7 +99,7 @@ int Platform_numberOfFields = LAST_PROCESSFIELD;
/*
* See /usr/include/sys/signal.h
*/
SignalItem Platform_signals[] = {
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
@ -136,7 +136,7 @@ SignalItem Platform_signals[] = {
{ .name = "32 SIGTHR", .number = 32 },
};
unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
@ -265,9 +265,6 @@ void Platform_setSwapValues(Meter* this) {
}
swdev = xCalloc(nswap, sizeof(*swdev));
if (swdev == NULL) {
return;
}
rnswap = swapctl(SWAP_STATS, swdev, nswap);
if (rnswap == -1) {

View File

@ -39,9 +39,9 @@ extern int Platform_numberOfFields;
/*
* See /usr/include/sys/signal.h
*/
extern SignalItem Platform_signals[];
extern const SignalItem Platform_signals[];
extern unsigned int Platform_numberOfSignals;
extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys);

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