136 Commits
2.0.0 ... 2.0.2

Author SHA1 Message Date
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
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
e2ccc7b240 Prepare for release 2.0.1. 2016-03-07 18:32:06 -03:00
ef1e62d1fa Merge branch 'juanfra684-openbsd-mem-used' 2016-03-07 15:03:50 -03:00
453105e77a Merge branch 'Sp1l-master' 2016-03-07 16:58:47 -03:00
db80f202f2 Avoid global, as done by @gaod in #387. 2016-03-07 16:58:02 -03:00
98e43816a5 Merge branch 'master' of https://github.com/Sp1l/htop into Sp1l-master 2016-03-07 16:54:38 -03:00
694addceb5 Merge pull request #436 from mmcco/tuneup
Misc. OpenBSD tuneup and improvement
2016-03-07 16:38:39 -03:00
4623394456 Merge pull request #435 from mmcco/freebsd
Improve error reporting on FreeBSD libkvm call
2016-03-07 16:35:59 -03:00
4ad7aa6432 Merge branch 'openbsd-mem-used' of https://github.com/juanfra684/htop into juanfra684-openbsd-mem-used 2016-03-07 15:03:18 -03:00
4b780a3499 A few more OpenBSD fixes
Namely:

 o use malloc where an xCalloc slipped in

 o safeguard against an empty arg list - I don't think it's possible,
   but it would be potentially exploitable

 o we need to initialize the arg string to an empty string because we no
 longer use strlcpy(3)

 o annotate a tricky use of strlcpy(3)'s truncation
2016-03-05 23:38:12 -05:00
b08cb7352e Misc. OpenBSD tuneup and improvement
Including:

 o set *basenameEnd even in error cases (FreeBSD probably needs this)

 o use kvm_openfiles(3) rather than kvm_open(3) so that we can report
   errors (as with FreeBSD)

 o sanify the process argument list creation by using strlcat(3)

 o drop the pageSizeKb variable and use the PAGE_SIZE_KB macro directly,
   as the page size can't change anyway

 o clean up a few macros, add MINIMUM() and MAXIMUM() (should be
   mirrored to FreeBSD)

 o fix some syntax

 o add some useful comments
2016-03-05 23:23:29 -05:00
b886ecc479 Improve error reporting on FreeBSD libkvm call
This involves switching from kvm_open(3) to kvm_openfiles(3). The only
difference is that the latter has saner error reporting (see the man
page for details). We can now fatally report the error rather than just
calling assert(3).
2016-03-05 22:59:39 -05:00
63c55854e5 Merge pull request #388 from mhinz/add-p-to-help
Add "p" to Help
2016-02-29 22:03:45 -03:00
4f1bd232d8 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-29 21:57:47 -03:00
84a69b1ea4 Release resource when using hwloc 2016-02-29 21:57:27 -03:00
e77811e99b any of these values may wrap 2016-02-29 21:57:03 -03:00
0a4a447d0d Add "p" to Help 2016-03-01 00:57:09 +01:00
cc8375f9ea FreeBSD: use KERN_PROC_PROC with kvm_getprocs to avoid erroneously combining LWPs 2016-02-28 22:41:50 -05:00
03af73bbc7 Merge pull request #427 from tcreech/master
Fix low CPU usage reporting in FreeBSD
2016-02-27 03:04:52 -03:00
0cf3cfa3af Fix low CPU usage reporting in FreeBSD 2016-02-26 23:23:27 -05:00
797bcd0961 Catch invalid IO values due to no permissions.
Display them properly. Not fully convinced of the "no perm" message...
2016-02-20 02:23:26 -02:00
baec4bdcb0 Try to retain last full name of a zombie process.
Once a process goes zombie on Linux, /proc/PID/cmdline
gets empty. So, when we detect it is a zombie we stop
reading this file.
For processes that were zombies before htop started,
there's no way to get the full name.
Closes #49.
2016-02-19 20:51:57 -02:00
a8a5e62760 Force -lgcov harder when running make coverage.
Shouldn't be needed, but I had to make this tweak to make this work again.
2016-02-19 13:40:21 -02:00
f5ddb974a1 Tweaks on the test suite, still problematic. 2016-02-19 13:40:00 -02:00
565101234a Remove old commented code. 2016-02-19 13:39:38 -02:00
d4741d5410 Fix error test — looks like nobody bumped into this one! 2016-02-19 13:39:16 -02:00
9668cd11ed Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-19 13:38:54 -02:00
c23d4f12d1 Fix behavior of ESC key, getting rid of the annoying delay.
Thank you @Explorer09 for the push!
Closes #417.
2016-02-19 13:38:02 -02:00
34431dc7fd Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-18 23:46:36 -02:00
42c4459375 Run through all command line arguments on Darwin.
Also fixes the basename offset for highlighting the basename.
Closes #379.
2016-02-18 23:45:17 -02:00
48254f92e4 Fix behavior of Panel on empty lists.
Closes #370.
2016-02-18 17:45:04 -02:00
5ee6715843 Scan threads for process state information.
Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
and       e86692e869/ProcessList.c
This should be a fix for #361.
2016-02-18 14:57:09 -02:00
c18309466d Pre-reset 'show' for process.
This simplifies the protocol between the platform-independent
and platform-specific parts. The platform-specific parts
were supposed to re-determine the value of process->show
on each iteration, and the Darwin subsystem wasn't doing that.
Instead of adding the code to the Darwin part, I lifted the
burden of the OS-specific of resetting process->show: now
they can choose to hide a process if they want to (e.g.
detecting kernel threads) but are not required to
(e.g. on Darwin where we're not listing threads separately (yet?)).
Fixes tree view collapsing/expanding on OSX. Closes #416.
2016-02-18 14:32:49 -02:00
fe0ad86e6d Fix tree organization on OSX.
Closes #393.
2016-02-18 14:14:45 -02:00
dc4f145629 Merge pull request #413 from koresar/master
(Regression) Typo in the word "prority" -> "priority"
2016-02-17 10:38:38 -02:00
d43e709364 Typo in the word "priority" 2016-02-17 13:44:07 +11:00
0627a5f0e9 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-16 14:52:08 -02:00
d10f4f615a Merge pull request #391 from gaod/master
Add MEM% for processes in process list on FreeBSD.
2016-02-16 14:52:02 -02:00
e0c364b9cc Fix reading of io_syscr and io_syscw.
Issue noticed by GCC6 -Wmisleading-indentation.
Thanks @JIghtuse and @Explorer09!
Closes #409.
2016-02-16 14:34:25 -02:00
fd216309d9 Merge pull request #380 from fingolfin/patch-1
Simplify autogen.sh
2016-02-14 21:17:19 -02:00
474d26cea8 Portable affinity using hwloc 2016-02-14 19:57:29 -02:00
471cd60635 Simplify autogen.sh
Make use of the autoreconf tool
2016-02-14 22:36:40 +01:00
f554f08fa9 Fix FreeBSD CPU% calculation 2016-02-14 22:21:11 +01:00
d753996b96 Fix memory percentage display on FreeBSD 2016-02-14 21:48:36 +01:00
b10278318b Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-14 17:47:29 +01:00
35657208d7 Disable the syscall on systems that don't have it.
Got a report in #397 that htop runs in NetBSD
masquerading as Linux and using a compatibility /proc
(like we used to in FreeBSD) and that it builds fine
apart from this syscall.
2016-02-14 12:05:35 -02:00
ae823c375a Make unsupported platform build again.
(Thanks @coypoop at #397 for the heads up!)
2016-02-14 11:45:47 -02:00
1b5025e6f5 Add support for cachedMem and fix usedMem on OpenBSD. 2016-02-14 13:04:18 +01:00
c66ac09a66 Add MEM% for processes in process list on FreeBSD. 2016-02-13 23:24:57 +08:00
0b70439316 Fix buffer reuse. 2016-02-13 02:18:28 -02:00
e9b32eb62f Fix memory accounting in Darwin.
htop currently expects m_size and m_resident in pages (Process.c).
According to the proc_info.h header, the values returned by libproc
are in bytes:
http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/proc_info.h
Eventually we should change the htop crossplatform API to expect memory
in bytes, but this is the smaller change that should fix it.
Closes #385.
2016-02-13 02:13:57 -02:00
3b9a4b1024 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-12 23:34:28 -02:00
0e3cf6975f Fix crash when emptying column. Closes #381. 2016-02-12 23:33:53 -02:00
a618f477b2 Merge pull request #387 from gaod/master
Suppress compiler warnings on FreeBSD
2016-02-12 22:47:29 -02:00
cc4267cc5c Suppress compiler warnings. 2016-02-13 02:09:50 +08:00
d0d14da8d9 Fix implicit define isnan() 2016-02-12 14:37:24 +01:00
1bedac1ca2 Merge pull request #363 from myfreeweb/freebsd-memory-fix
Fix process memory on FreeBSD
2016-02-11 17:30:41 -02:00
011bf30d22 Minor tweaks to the README 2016-02-11 17:20:50 -02:00
c56b1a8830 Merge pull request #365 from deric/master
Add travis badge, update README
2016-02-11 17:18:20 -02:00
01edfcf4d4 link to the official website, paypal donate button 2016-02-11 19:17:00 +01:00
d916f6e6f0 remove Debian instructions 2016-02-11 18:58:20 +01:00
e46488463d Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-11 15:24:49 -02:00
8fa9da47de don't mention installing to /usr 2016-02-11 17:32:06 +01:00
d408c74465 add travis badge, code formatting, updated compilation instructions 2016-02-11 14:59:45 +01:00
bb0333e45d fix process memory on FreeBSD 2016-02-11 15:52:39 +03:00
b6f927bffa Merge pull request #359 from myfreeweb/freebsd-battery
Add FreeBSD battery support
2016-02-11 08:46:42 -02:00
f1d1d6e0d6 add FreeBSD battery support 2016-02-11 13:42:37 +03:00
70 changed files with 1013 additions and 816 deletions

View File

@ -271,7 +271,7 @@ static Htop_Reaction actionQuit() {
static Htop_Reaction actionSetAffinity(State* st) { static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1) if (st->pl->cpuCount == 1)
return HTOP_OK; return HTOP_OK;
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY) #if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
Panel* panel = st->panel; Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel); Process* p = (Process*) Panel_getSelected(panel);
@ -328,7 +328,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; 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); st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]); Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]);
return HTOP_KEEP_FOLLOWING; return HTOP_KEEP_FOLLOWING;
@ -387,6 +387,7 @@ static struct { const char* key; const char* info; } helpLeft[] = {
{ .key = " F3 /: ", .info = "incremental name search" }, { .key = " F3 /: ", .info = "incremental name search" },
{ .key = " F4 \\: ",.info = "incremental name filtering" }, { .key = " F4 \\: ",.info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" }, { .key = " F5 t: ", .info = "tree view" },
{ .key = " p: ", .info = "toggle program path" },
{ .key = " u: ", .info = "show processes of a single user" }, { .key = " u: ", .info = "show processes of a single user" },
{ .key = " H: ", .info = "hide/show user process threads" }, { .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" }, { .key = " K: ", .info = "hide/show kernel threads" },
@ -405,11 +406,11 @@ static struct { const char* key; const char* info; } helpRight[] = {
{ .key = " F9 k: ", .info = "kill process/tagged processes" }, { .key = " F9 k: ", .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" }, { .key = " F7 ]: ", .info = "higher priority (root only)" },
{ .key = " F8 [: ", .info = "lower priority (+ nice)" }, { .key = " F8 [: ", .info = "lower priority (+ nice)" },
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY) #if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
{ .key = " a: ", .info = "set CPU affinity" }, { .key = " a: ", .info = "set CPU affinity" },
#endif #endif
{ .key = " e: ", .info = "show process environment" }, { .key = " e: ", .info = "show process environment" },
{ .key = " i: ", .info = "set IO prority" }, { .key = " i: ", .info = "set IO priority" },
{ .key = " l: ", .info = "list open files with lsof" }, { .key = " l: ", .info = "list open files with lsof" },
{ .key = " s: ", .info = "trace syscalls with strace" }, { .key = " s: ", .info = "trace syscalls with strace" },
{ .key = " ", .info = "" }, { .key = " ", .info = "" },
@ -479,8 +480,8 @@ static Htop_Reaction actionHelp(State* st) {
for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); } for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); }
for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); } for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
attrset(CRT_colors[PROCESS_THREAD]); attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(15, 32, "threads"); mvaddstr(16, 32, "threads");
mvaddstr(16, 26, "threads"); mvaddstr(17, 26, "threads");
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
attrset(CRT_colors[HELP_BOLD]); attrset(CRT_colors[HELP_BOLD]);
@ -556,7 +557,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['='] = actionExpandOrCollapse; keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse; keys['-'] = actionExpandOrCollapse;
keys['u'] = actionFilterByUser; keys['u'] = actionFilterByUser;
keys['F'] = actionFollow; keys['F'] = Action_follow;
keys['S'] = actionSetup; keys['S'] = actionSetup;
keys['C'] = actionSetup; keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup; keys[KEY_F(2)] = actionSetup;

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); void Action_setBindings(Htop_Action* keys);

View File

@ -10,8 +10,13 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
#include <hwloc/linux.h> #include <hwloc.h>
#elif HAVE_NATIVE_AFFINITY #if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#include <sched.h> #include <sched.h>
#endif #endif
@ -55,7 +60,7 @@ void Affinity_add(Affinity* this, int id) {
Affinity* Affinity_get(Process* proc, ProcessList* pl) { Affinity* Affinity_get(Process* proc, ProcessList* pl) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
bool ok = (hwloc_linux_get_tid_cpubind(pl->topology, proc->pid, cpuset) == 0); bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
Affinity* affinity = NULL; Affinity* affinity = NULL;
if (ok) { if (ok) {
affinity = Affinity_new(pl); affinity = Affinity_new(pl);
@ -76,15 +81,15 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
bool Affinity_set(Process* proc, Affinity* this) { bool Affinity_set(Process* proc, Affinity* this) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < affinity->used; i++) { for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, affinity->cpus[i]); hwloc_bitmap_set(cpuset, this->cpus[i]);
} }
bool ok = (hwloc_linux_set_tid_cpubind(this->pl->topology, proc->pid, cpuset) == 0); bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
hwloc_bitmap_free(cpuset); hwloc_bitmap_free(cpuset);
return ok; return ok;
} }
#elif HAVE_NATIVE_AFFINITY #elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl) { Affinity* Affinity_get(Process* proc, ProcessList* pl) {
cpu_set_t cpuset; cpu_set_t cpuset;

View File

@ -10,7 +10,12 @@ in the source distribution for its full text.
*/ */
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
#elif HAVE_NATIVE_AFFINITY #if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#endif #endif
#include "Process.h" #include "Process.h"
@ -36,7 +41,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this); bool Affinity_set(Process* proc, Affinity* this);
#elif HAVE_NATIVE_AFFINITY #elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl); Affinity* Affinity_get(Process* proc, ProcessList* pl);

View File

@ -112,20 +112,22 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Available meters"); 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++) { for (int i = 1; Platform_meterTypes[i]; i++) {
MeterClass* type = Platform_meterTypes[i]; MeterClass* type = Platform_meterTypes[i];
if (type != &CPUMeter_class) { assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName; const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16)); Panel_add(super, (Object*) ListItem_new(label, i << 16));
} }
} // Handle (&CPUMeter_class)
MeterClass* type = &CPUMeter_class; MeterClass* type = &CPUMeter_class;
int cpus = pl->cpuCount; int cpus = pl->cpuCount;
if (cpus > 1) { if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0)); Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) { for (int i = 1; i <= cpus; i++) {
char buffer[50]; char buffer[50];
sprintf(buffer, "%s %d", type->uiName, i); snprintf(buffer, 50, "%s %d", type->uiName, i);
Panel_add(super, (Object*) ListItem_new(buffer, i)); Panel_add(super, (Object*) ListItem_new(buffer, i));
} }
} else { } else {

View File

@ -32,7 +32,7 @@ int BatteryMeter_attributes[] = {
BATTERY BATTERY
}; };
static void BatteryMeter_setValues(Meter * this, char *buffer, int len) { static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
ACPresence isOnAC; ACPresence isOnAC;
double percent; double percent;
@ -73,8 +73,9 @@ MeterClass BatteryMeter_class = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete
}, },
.setValues = BatteryMeter_setValues, .updateValues = BatteryMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = BatteryMeter_attributes, .attributes = BatteryMeter_attributes,
.name = "Battery", .name = "Battery",

View File

@ -55,7 +55,7 @@ static void CPUMeter_init(Meter* this) {
Meter_setCaption(this, "Avg"); 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; int cpu = this->param;
if (cpu > this->pl->cpuCount) { if (cpu > this->pl->cpuCount) {
snprintf(buffer, size, "absent"); snprintf(buffer, size, "absent");
@ -215,7 +215,7 @@ MeterClass CPUMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.setValues = CPUMeter_setValues, .updateValues = CPUMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = CPU_METER_ITEMCOUNT, .maxItems = CPU_METER_ITEMCOUNT,
.total = 100.0, .total = 100.0,
@ -312,8 +312,8 @@ MeterClass LeftCPUs2Meter_class = {
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs2", .name = "LeftCPUs2",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.uiName = "CPUs (1&2/4)", .uiName = "CPUs (1&2/4)",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = DualColCPUsMeter_draw, .draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = AllCPUsMeter_init,

9
CRT.c
View File

@ -125,6 +125,8 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn); void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
}*/ }*/
const char *CRT_treeStrAscii[TREE_STR_COUNT] = { const char *CRT_treeStrAscii[TREE_STR_COUNT] = {
@ -587,11 +589,17 @@ void CRT_init(int delay, int colorScheme) {
define_key("\033[13~", KEY_F(3)); define_key("\033[13~", KEY_F(3));
define_key("\033[14~", KEY_F(4)); define_key("\033[14~", KEY_F(4));
define_key("\033[17;2~", KEY_F(18)); define_key("\033[17;2~", KEY_F(18));
char sequence[3] = "\033a";
for (char c = 'a'; c <= 'z'; c++) {
sequence[1] = c;
define_key(sequence, KEY_ALT('A' + (c - 'a')));
}
} }
#ifndef DEBUG #ifndef DEBUG
signal(11, CRT_handleSIGSEGV); signal(11, CRT_handleSIGSEGV);
#endif #endif
signal(SIGTERM, CRT_handleSIGTERM); signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
use_default_colors(); use_default_colors();
if (!has_colors()) if (!has_colors())
CRT_colorScheme = 1; CRT_colorScheme = 1;
@ -618,6 +626,7 @@ void CRT_init(int delay, int colorScheme) {
#else #else
mousemask(BUTTON1_RELEASED, NULL); mousemask(BUTTON1_RELEASED, NULL);
#endif #endif
} }
void CRT_done() { void CRT_done() {

2
CRT.h
View File

@ -115,6 +115,8 @@ void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn); void CRT_handleSIGSEGV(int sgn);
#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
extern const char *CRT_treeStrAscii[TREE_STR_COUNT]; extern const char *CRT_treeStrAscii[TREE_STR_COUNT];

View File

@ -82,9 +82,9 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
case KEY_UP: case KEY_UP:
case KEY_CTRLP: case KEY_CTRL('P'):
case KEY_DOWN: case KEY_DOWN:
case KEY_CTRLN: case KEY_CTRL('N'):
case KEY_NPAGE: case KEY_NPAGE:
case KEY_PPAGE: case KEY_PPAGE:
case KEY_HOME: case KEY_HOME:

View File

@ -1,4 +1,38 @@
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
* OpenBSD: Various fixes and improvements
(thanks to Michael McConville and Juan Francisco Cantero Hurtado)
* FreeBSD: fix CPU and memory readings
(thanks to Tim Creech, Hung-Yi Chen, Bernard Spil, Greg V)
* FreeBSD: add battery support
(thanks to Greg V)
* Linux: Retain last-obtained name of a zombie process
* Mac OS X: Improve portability for OS X versions
(thanks to Michael Klein)
* Mac OS X: Fix reading command-line arguments and basename
* Mac OS X: Fix process state information
* Mac OS X: Fix tree view collapsing/expanding
* Mac OS X: Fix tree organization
* Mac OS X: Fix memory accounting
* Fix crash when emptying a column of meters
* Make Esc key more responsive
What's new in version 2.0.0 What's new in version 2.0.0
* Platform abstraction layer * Platform abstraction layer

View File

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

View File

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

View File

@ -37,7 +37,7 @@ typedef struct Header_ {
#endif #endif
#ifndef Header_forEachColumn #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 #endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) { Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {

View File

@ -28,7 +28,7 @@ typedef struct Header_ {
#endif #endif
#ifndef Header_forEachColumn #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 #endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns); Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);

View File

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

231
INSTALL
View File

@ -1,13 +1,25 @@
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Installation Instructions
Foundation, Inc. *************************
This file is free documentation; the Free Software Foundation gives Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
unlimited permission to copy, distribute and modify it. Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation Basic Installation
================== ==================
These are generic installation instructions. 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
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses various system-dependent variables used during compilation. It uses
@ -20,9 +32,9 @@ debugging `configure').
It can also use an optional file (typically called `config.cache' It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale disabled by default to prevent problems with accidental use of stale
cache files.) cache files.
If you need to do unusual things to compile the package, please try If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail to figure out how `configure' could check whether to do them, and mail
@ -32,30 +44,37 @@ some point `config.cache' contains results you don't want to keep, you
may remove or edit it. may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need `configure' by a program called `autoconf'. You need `configure.ac' if
`configure.ac' if you want to change it or regenerate `configure' using you want to change it or regenerate `configure' using a newer version
a newer version of `autoconf'. of `autoconf'.
The simplest way to compile this package is: The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type 1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're `./configure' to configure the package for your system.
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some Running `configure' might take a while. While running, it prints
messages telling which features it is checking for. some messages telling which features it is checking for.
2. Type `make' to compile the package. 2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with 3. Optionally, type `make check' to run any self-tests that come with
the package. the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and 4. Type `make install' to install the programs and any data files and
documentation. documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. You can remove the program binaries and object files from the 5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is a different kind of computer), type `make distclean'. There is
@ -64,6 +83,16 @@ The simplest way to compile this package is:
all sorts of other programs in order to regenerate files that came all sorts of other programs in order to regenerate files that came
with the distribution. with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options Compilers and Options
===================== =====================
@ -75,7 +104,7 @@ for details on some of the pertinent environment variables.
by setting variables in the command line or in the environment. Here by setting variables in the command line or in the environment. Here
is an example: is an example:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details. *Note Defining Variables::, for more details.
@ -84,44 +113,89 @@ Compiling For Multiple Architectures
You can compile the package for more than one kind of computer at the You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that own directory. To do this, you can use GNU `make'. `cd' to the
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
If you have to use a `make' that does not support the `VPATH' With a non-GNU `make', it is safer to compile the package for one
variable, you have to compile the package for one architecture at a architecture at a time in the source code directory. After you have
time in the source code directory. After you have installed the installed the package for one architecture, use `make distclean' before
package for one architecture, use `make distclean' before reconfiguring reconfiguring for another architecture.
for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names Installation Names
================== ==================
By default, `make install' will install the package's files in By default, `make install' installs the package's commands under
`/usr/local/bin', `/usr/local/man', etc. You can specify an `/usr/local/bin', include files under `/usr/local/include', etc. You
installation prefix other than `/usr/local' by giving `configure' the can specify an installation prefix other than `/usr/local' by giving
option `--prefix=PATH'. `configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PATH as the prefix for installing programs and libraries. PREFIX as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix. Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package. `configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE They may also pay attention to `--with-PACKAGE' options, where PACKAGE
@ -134,6 +208,50 @@ find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations. `--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
HP-UX `make' updates targets which have the same time stamps as
their prerequisites, which makes it generally unusable when shipped
generated files such as `configure' are involved. Use GNU `make'
instead.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type Specifying the System Type
========================== ==========================
@ -149,14 +267,15 @@ type, such as `sun4', or a canonical name which has the form:
where SYSTEM can have one of these forms: where SYSTEM can have one of these forms:
OS KERNEL-OS OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't `config.sub' isn't included in this package, then this package doesn't
need to know the machine type. need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should If you are _building_ compiler tools for cross-compiling, you should
use the `--target=TYPE' option to select the type of system they will use the option `--target=TYPE' to select the type of system they will
produce code for. produce code for.
If you want to _use_ a cross compiler, that generates code for a If you want to _use_ a cross compiler, that generates code for a
@ -186,9 +305,15 @@ them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc ./configure CC=/usr/local2/bin/gcc
will cause the specified gcc to be used as the C compiler (unless it is causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script). overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf limitation. Until the limitation is lifted, you can use
this workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation `configure' Invocation
====================== ======================
@ -197,7 +322,14 @@ operates.
`--help' `--help'
`-h' `-h'
Print a summary of the options to `configure', and exit. Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version' `--version'
`-V' `-V'
@ -224,6 +356,15 @@ operates.
Look for the package's source code in directory DIR. Usually Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically. `configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run `configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details. `configure --help' for more details.

View File

@ -40,6 +40,7 @@ typedef struct IncSet_ {
IncMode* active; IncMode* active;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found;
} IncSet; } IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int); typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
@ -114,7 +115,7 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
} }
} }
static void search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel); int size = Panel_size(panel);
bool found = false; bool found = false;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@ -128,6 +129,7 @@ static void search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa
FunctionBar_draw(mode->bar, mode->buffer); FunctionBar_draw(mode->bar, mode->buffer);
else else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]); FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
return found;
} }
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
@ -151,7 +153,8 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} }
} }
doSearch = false; 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->buffer[mode->index] = ch;
mode->index++; mode->index++;
mode->buffer[mode->index] = 0; mode->buffer[mode->index] = 0;
@ -159,7 +162,9 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
filterChanged = true; filterChanged = true;
if (mode->index == 1) this->filtering = 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->index--;
mode->buffer[mode->index] = 0; mode->buffer[mode->index] = 0;
if (mode->isFilter) { if (mode->isFilter) {
@ -169,6 +174,9 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode); IncMode_reset(mode);
} }
} }
} else {
doSearch = false;
}
} else if (ch == KEY_RESIZE) { } else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES-panel->y-1); Panel_resize(panel, COLS, LINES-panel->y-1);
} else { } else {
@ -187,7 +195,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
search(mode, panel, getPanelValue); this->found = search(mode, panel, getPanelValue);
} }
if (filterChanged && lines) { if (filterChanged && lines) {
updateWeakPanel(this, panel, lines); updateWeakPanel(this, panel, lines);

View File

@ -35,6 +35,7 @@ typedef struct IncSet_ {
IncMode* active; IncMode* active;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found;
} IncSet; } IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int); typedef const char* (*IncMode_GetPanelValue)(Panel*, int);

View File

@ -83,6 +83,7 @@ void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
wmove(stdscr, 0, 0); wmove(stdscr, 0, 0);
vw_printw(stdscr, fmt, ap); vw_printw(stdscr, fmt, ap);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true); Panel_draw(this->display, true);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc);
va_end(ap); va_end(ap);
@ -116,6 +117,7 @@ void InfoScreen_run(InfoScreen* this) {
if (this->inc->active) if (this->inc->active)
move(LINES-1, CRT_cursorX); move(LINES-1, CRT_cursorX);
set_escdelay(25);
int ch = getch(); int ch = getch();
if (ch == ERR) { if (ch == ERR) {

View File

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

View File

@ -83,6 +83,9 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
} else if (ch != ERR && this->inc->active) { } else if (ch != ERR && this->inc->active) {
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL); bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
if (this->inc->found) {
reaction |= Action_follow(this->state);
}
if (filterChanged) { if (filterChanged) {
this->state->pl->incFilter = IncSet_filter(this->inc); this->state->pl->incFilter = IncSet_filter(this->inc);
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR; reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
@ -102,20 +105,6 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} else { } else {
reaction |= HTOP_KEEP_FOLLOWING; 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) { if (reaction & HTOP_REDRAW_BAR) {
@ -159,7 +148,7 @@ const char* MainPanel_getValue(MainPanel* this, int i) {
return ""; 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; Panel* super = (Panel*) this;
bool ok = true; bool ok = true;
bool anyTagged = false; bool anyTagged = false;

View File

@ -34,7 +34,7 @@ int MainPanel_selectedPid(MainPanel* this);
const char* MainPanel_getValue(MainPanel* this, int i); 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; extern PanelClass MainPanel_class;

View File

@ -95,7 +95,7 @@ debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
coverage: coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
.c.h: .c.h:
@srcdir@/scripts/MakeHeader.py $< @srcdir@/scripts/MakeHeader.py $<

View File

@ -24,7 +24,7 @@ int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE 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; int written;
Platform_setMemoryValues(this); Platform_setMemoryValues(this);
@ -60,7 +60,7 @@ MeterClass MemoryMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = MemoryMeter_display, .display = MemoryMeter_display,
}, },
.setValues = MemoryMeter_setValues, .updateValues = MemoryMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = 3, .maxItems = 3,
.total = 100.0, .total = 100.0,

35
Meter.c
View File

@ -37,7 +37,7 @@ typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); 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 void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { typedef struct MeterClass_ {
@ -46,7 +46,7 @@ typedef struct MeterClass_ {
const Meter_Done done; const Meter_Done done;
const Meter_UpdateMode updateMode; const Meter_UpdateMode updateMode;
const Meter_Draw draw; const Meter_Draw draw;
const Meter_SetValues setValues; const Meter_UpdateValues updateValues;
const int defaultMode; const int defaultMode;
const double total; const double total;
const int* attributes; const int* attributes;
@ -66,7 +66,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) #define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw #define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done #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_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems #define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_) #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->h = 1;
this->param = param; this->param = param;
this->pl = pl; this->pl = pl;
char maxItems = type->maxItems; type->curItems = type->maxItems;
if (maxItems == 0) { this->values = xCalloc(type->maxItems, sizeof(double));
maxItems = 1;
}
type->curItems = maxItems;
this->values = xCalloc(maxItems, sizeof(double));
this->total = type->total; this->total = type->total;
this->caption = xStrdup(type->caption); this->caption = xStrdup(type->caption);
if (Meter_initFn(this)) if (Meter_initFn(this))
@ -184,7 +181,6 @@ void Meter_delete(Object* cast) {
if (Meter_doneFn(this)) { if (Meter_doneFn(this)) {
Meter_done(this); Meter_done(this);
} }
if (this->drawData)
free(this->drawData); free(this->drawData);
free(this->caption); free(this->caption);
free(this->values); free(this->values);
@ -216,7 +212,6 @@ void Meter_setMode(Meter* this, int modeIndex) {
Meter_updateMode(this, modeIndex); Meter_updateMode(this, modeIndex);
} else { } else {
assert(modeIndex >= 1); assert(modeIndex >= 1);
if (this->drawData)
free(this->drawData); free(this->drawData);
this->drawData = NULL; this->drawData = NULL;
@ -249,7 +244,7 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) { static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
(void) w; (void) w;
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
@ -269,7 +264,7 @@ static char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) { static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
w -= 2; w -= 2;
attrset(CRT_colors[METER_TEXT]); 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]; char bar[w + 1];
int blockSizes[10]; int blockSizes[10];
for (int i = 0; i < w; i++)
bar[i] = ' ';
const size_t barOffset = w - MIN((int)strlen(buffer), w); snprintf(bar, w + 1, "%*s", w, buffer);
snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
// First draw in the bar[] buffer... // First draw in the bar[] buffer...
int offset = 0; int offset = 0;
@ -397,7 +389,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
data->values[i] = data->values[i+1]; data->values[i] = data->values[i+1];
char buffer[nValues]; char buffer[nValues];
Meter_setValues(this, buffer, nValues - 1); Meter_updateValues(this, buffer, nValues - 1);
double value = 0.0; double value = 0.0;
int items = Meter_getItems(this); int items = Meter_getItems(this);
@ -466,7 +458,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
LEDMeterMode_digits = LEDMeterMode_digitsAscii; LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, buffer, &out);
@ -529,7 +521,7 @@ MeterMode* Meter_modes[] = {
/* Blank meter */ /* 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; (void) this; (void) buffer; (void) size;
} }
@ -548,8 +540,9 @@ MeterClass BlankMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = BlankMeter_display, .display = BlankMeter_display,
}, },
.setValues = BlankMeter_setValues, .updateValues = BlankMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0, .total = 100.0,
.attributes = BlankMeter_attributes, .attributes = BlankMeter_attributes,
.name = "Blank", .name = "Blank",

View File

@ -24,7 +24,7 @@ typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); 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 void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { typedef struct MeterClass_ {
@ -33,7 +33,7 @@ typedef struct MeterClass_ {
const Meter_Done done; const Meter_Done done;
const Meter_UpdateMode updateMode; const Meter_UpdateMode updateMode;
const Meter_Draw draw; const Meter_Draw draw;
const Meter_SetValues setValues; const Meter_UpdateValues updateValues;
const int defaultMode; const int defaultMode;
const double total; const double total;
const int* attributes; const int* attributes;
@ -53,7 +53,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) #define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw #define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done #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_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems #define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_) #define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)

View File

@ -50,7 +50,10 @@ static void MetersPanel_delete(Object* object) {
void MetersPanel_setMoving(MetersPanel* this, bool moving) { void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
this->moving = moving; this->moving = moving;
((ListItem*)Panel_getSelected(super))->moving = moving; ListItem* selected = (ListItem*)Panel_getSelected(super);
if (selected) {
selected->moving = moving;
}
if (!moving) { if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setDefaultBar(super); Panel_setDefaultBar(super);
@ -58,6 +61,7 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]); Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
super->currentBar = Meters_movingBar; super->currentBar = Meters_movingBar;
} }
FunctionBar_draw(this->super.currentBar, NULL);
} }
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) { static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
@ -96,7 +100,6 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
if (!Vector_size(this->meters)) if (!Vector_size(this->meters))
break; break;
MetersPanel_setMoving(this, !(this->moving)); MetersPanel_setMoving(this, !(this->moving));
FunctionBar_draw(this->super.currentBar, NULL);
result = HANDLED; result = HANDLED;
break; break;
} }

View File

@ -86,17 +86,12 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
pdata->error = 127; pdata->error = 127;
return pdata; return pdata;
} }
while (!feof(fd)) { for (;;) {
int cmd = fgetc(fd); char* line = String_readLine(fd);
if (cmd == EOF) if (!line) {
break;
char* entry = xMalloc(1024);
if (!fgets(entry, 1024, fd)) {
free(entry);
break; break;
} }
char* newline = strrchr(entry, '\n'); unsigned char cmd = line[0];
*newline = '\0';
if (cmd == 'f') { if (cmd == 'f') {
OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData)); OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) { if (fdata == NULL) {
@ -108,7 +103,8 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
item = &(fdata->data); item = &(fdata->data);
} }
assert(cmd >= 0 && cmd <= 0xff); assert(cmd >= 0 && cmd <= 0xff);
item->data[cmd] = entry; item->data[cmd] = xStrdup(line + 1);
free(line);
} }
pdata->error = pclose(fd); pdata->error = pclose(fd);
return pdata; return pdata;
@ -132,9 +128,11 @@ void OpenFilesScreen_scan(InfoScreen* this) {
} else { } else {
OpenFiles_FileData* fdata = pdata->files; OpenFiles_FileData* fdata = pdata->files;
while (fdata) { while (fdata) {
char entry[1024];
char** data = fdata->data.data; 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);
snprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "", data['f'] ? data['f'] : "",
data['t'] ? data['t'] : "", data['t'] ? data['t'] : "",
data['D'] ? data['D'] : "", data['D'] ? data['D'] : "",

32
Panel.c
View File

@ -61,6 +61,7 @@ struct Panel_ {
Vector* items; Vector* items;
int selected; int selected;
int oldSelected; int oldSelected;
int selectedLen;
void* eventHandlerState; void* eventHandlerState;
int scrollV; int scrollV;
short scrollH; short scrollH;
@ -82,10 +83,7 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#define KEY_CTRLN 0016 /* control-n key */ #define KEY_CTRL(l) ((l)-'A'+1)
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
PanelClass Panel_class = { PanelClass Panel_class = {
.super = { .super = {
@ -327,6 +325,7 @@ void Panel_draw(Panel* this, bool focus) {
if (selected) { if (selected) {
attrset(selectionColor); attrset(selectionColor);
RichString_setAttr(&item, selectionColor); RichString_setAttr(&item, selectionColor);
this->selectedLen = itemLen;
} }
mvhline(y + line, x, ' ', this->w); mvhline(y + line, x, ' ', this->w);
if (amt > 0) if (amt > 0)
@ -352,6 +351,7 @@ void Panel_draw(Panel* this, bool focus) {
RichString_begin(new); RichString_begin(new);
Object_display(newObj, &new); Object_display(newObj, &new);
int newLen = RichString_sizeVal(new); int newLen = RichString_sizeVal(new);
this->selectedLen = newLen;
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w); mvhline(y+ this->oldSelected - first, x+0, ' ', this->w);
if (scrollH < oldLen) if (scrollH < oldLen)
RichString_printoffnVal(old, y+this->oldSelected - first, x, RichString_printoffnVal(old, y+this->oldSelected - first, x,
@ -376,11 +376,11 @@ bool Panel_onKey(Panel* this, int key) {
int size = Vector_size(this->items); int size = Vector_size(this->items);
switch (key) { switch (key) {
case KEY_DOWN: case KEY_DOWN:
case KEY_CTRLN: case KEY_CTRL('N'):
this->selected++; this->selected++;
break; break;
case KEY_UP: case KEY_UP:
case KEY_CTRLP: case KEY_CTRL('P'):
this->selected--; this->selected--;
break; break;
#ifdef KEY_C_DOWN #ifdef KEY_C_DOWN
@ -394,14 +394,14 @@ bool Panel_onKey(Panel* this, int key) {
break; break;
#endif #endif
case KEY_LEFT: case KEY_LEFT:
case KEY_CTRLB: case KEY_CTRL('B'):
if (this->scrollH > 0) { if (this->scrollH > 0) {
this->scrollH -= CRT_scrollHAmount; this->scrollH -= MAX(CRT_scrollHAmount, 0);
this->needsRedraw = true; this->needsRedraw = true;
} }
break; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRLF: case KEY_CTRL('F'):
this->scrollH += CRT_scrollHAmount; this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true; this->needsRedraw = true;
break; break;
@ -412,7 +412,7 @@ bool Panel_onKey(Panel* this, int key) {
break; break;
case KEY_NPAGE: case KEY_NPAGE:
this->selected += (this->h - 1); this->selected += (this->h - 1);
this->scrollV += (this->h - 1); this->scrollV = MIN(MAX(0, Vector_size(this->items) - this->h), this->selected - this->h);
this->needsRedraw = true; this->needsRedraw = true;
break; break;
case KEY_WHEELUP: case KEY_WHEELUP:
@ -436,12 +436,22 @@ bool Panel_onKey(Panel* this, int key) {
case KEY_END: case KEY_END:
this->selected = size - 1; this->selected = size - 1;
break; 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: default:
return false; return false;
} }
// ensure selection within bounds // ensure selection within bounds
if (this->selected < 0) { if (this->selected < 0 || size == 0) {
this->selected = 0; this->selected = 0;
this->needsRedraw = true; this->needsRedraw = true;
} else if (this->selected >= size) { } else if (this->selected >= size) {

View File

@ -50,6 +50,7 @@ struct Panel_ {
Vector* items; Vector* items;
int selected; int selected;
int oldSelected; int oldSelected;
int selectedLen;
void* eventHandlerState; void* eventHandlerState;
int scrollV; int scrollV;
short scrollH; short scrollH;
@ -70,10 +71,7 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#define KEY_CTRLN 0016 /* control-n key */ #define KEY_CTRL(l) ((l)-'A'+1)
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
extern PanelClass Panel_class; extern PanelClass Panel_class;

View File

@ -48,6 +48,7 @@ in the source distribution for its full text.
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
typedef enum ProcessFields { typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
PID = 1, PID = 1,
COMM = 2, COMM = 2,
STATE = 3, STATE = 3,
@ -193,7 +194,7 @@ void Process_setupColumnWidths() {
assert(digits < 20); assert(digits < 20);
for (int i = 0; Process_pidColumns[i].label; i++) { for (int i = 0; Process_pidColumns[i].label; i++) {
assert(i < 20); assert(i < 20);
sprintf(Process_titleBuffer[i], "%*s ", digits, Process_pidColumns[i].label); snprintf(Process_titleBuffer[i], 20, "%*s ", digits, Process_pidColumns[i].label);
Process_fields[Process_pidColumns[i].id].title = Process_titleBuffer[i]; Process_fields[Process_pidColumns[i].id].title = Process_titleBuffer[i];
} }
sprintf(Process_pidFormat, "%%%dd ", digits); sprintf(Process_pidFormat, "%%%dd ", digits);
@ -335,7 +336,10 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
largeNumberColor = CRT_colors[PROCESS]; largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS];
} }
if (rate < ONE_K) { if (rate == -1) {
int len = snprintf(buffer, n, " no perm ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate); int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(str, processColor, buffer, len); RichString_appendn(str, processColor, buffer, len);
} else if (rate < ONE_K * ONE_K) { } else if (rate < ONE_K * ONE_K) {
@ -391,7 +395,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
int indent = (this->indent < 0 ? -this->indent : this->indent); int indent = (this->indent < 0 ? -this->indent : this->indent);
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
if (indent & (1 << i)) if (indent & (1U << i))
maxIndent = i+1; maxIndent = i+1;
for (int i = 0; i < maxIndent - 1; i++) { for (int i = 0; i < maxIndent - 1; i++) {
int written; int written;
@ -515,10 +519,10 @@ void Process_toggleTag(Process* this) {
bool Process_setPriority(Process* this, int priority) { bool Process_setPriority(Process* this, int priority) {
uid_t euid = geteuid(); uid_t euid = geteuid();
seteuid(getuid()); (void) seteuid(getuid());
int old_prio = getpriority(PRIO_PROCESS, this->pid); int old_prio = getpriority(PRIO_PROCESS, this->pid);
int err = setpriority(PRIO_PROCESS, this->pid, priority); int err = setpriority(PRIO_PROCESS, this->pid, priority);
seteuid(euid); (void) seteuid(euid);
if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) { if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) {
this->nice = priority; this->nice = priority;
} }
@ -531,9 +535,9 @@ bool Process_changePriorityBy(Process* this, size_t delta) {
void Process_sendSignal(Process* this, size_t sgn) { void Process_sendSignal(Process* this, size_t sgn) {
uid_t euid = geteuid(); uid_t euid = geteuid();
seteuid(getuid()); (void) seteuid(getuid());
kill(this->pid, (int) sgn); kill(this->pid, (int) sgn);
seteuid(euid); (void) seteuid(euid);
} }
long Process_pidCompare(const void* v1, const void* v2) { long Process_pidCompare(const void* v1, const void* v2) {

View File

@ -28,6 +28,7 @@ in the source distribution for its full text.
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
typedef enum ProcessFields { typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
PID = 1, PID = 1,
COMM = 2, COMM = 2,
STATE = 3, STATE = 3,

View File

@ -22,6 +22,10 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include "Settings.h" #include "Settings.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
#ifndef MAX_NAME #ifndef MAX_NAME
#define MAX_NAME 128 #define MAX_NAME 128
#endif #endif
@ -104,6 +108,11 @@ ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable*
} }
void ProcessList_done(ProcessList* this) { void ProcessList_done(ProcessList* this) {
#ifdef HAVE_LIBHWLOC
if (this->topologyOk) {
hwloc_topology_destroy(this->topology);
}
#endif
Hashtable_delete(this->processTable); Hashtable_delete(this->processTable);
Vector_delete(this->processes); Vector_delete(this->processes);
Vector_delete(this->processes2); Vector_delete(this->processes2);
@ -307,6 +316,7 @@ void ProcessList_scan(ProcessList* this) {
for (int i = 0; i < Vector_size(this->processes); i++) { for (int i = 0; i < Vector_size(this->processes); i++) {
Process* p = (Process*) Vector_get(this->processes, i); Process* p = (Process*) Vector_get(this->processes, i);
p->updated = false; p->updated = false;
p->show = true;
} }
this->totalTasks = 0; this->totalTasks = 0;

View File

@ -16,6 +16,10 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include "Settings.h" #include "Settings.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
#ifndef MAX_NAME #ifndef MAX_NAME
#define MAX_NAME 128 #define MAX_NAME 128
#endif #endif

59
README
View File

@ -1,50 +1,61 @@
htop [![Build Status](https://travis-ci.org/hishamhm/htop.svg?branch=master)](https://travis-ci.org/hishamhm/htop)
[![PayPal donate](https://img.shields.io/badge/paypal-donate-green.svg)](http://hisham.hm/htop/index.php?page=donate)
[htop](http://hisham.hm/htop/)
==== ====
by Hisham Muhammad <hisham@gobolinux.org> by Hisham Muhammad <hisham@gobolinux.org> (2004 - 2016)
2004 - 2015
Introduction Introduction
------------ ------------
This is htop, an interactive process viewer. This is `htop`, an interactive process viewer.
It requires ncurses. It is developed primarily on Linux, It requires `ncurses`. It is developed primarily on Linux,
but we also have code for running under FreeBSD and Mac OS X but we also have code for running under FreeBSD and Mac OS X
(help and testing are wanted for these platforms!) (help and testing are wanted for these platforms!)
This software has evolved considerably over the years, This software has evolved considerably over the years,
and is reasonably complete, but there is always room for improvement. and is reasonably complete, but there is always room for improvement.
Comparison between 'htop' and classic 'top' Comparison between `htop` and classic `top`
------------------------------------------- -------------------------------------------
* In 'htop' you can scroll the list vertically and horizontally * In `htop` you can scroll the list vertically and horizontally
to see all processes and full command lines. to see all processes and full command lines.
* In 'top' you are subject to a delay for each unassigned * In `top` you are subject to a delay for each unassigned
key you press (especially annoying when multi-key escape key you press (especially annoying when multi-key escape
sequences are triggered by accident). sequences are triggered by accident).
* 'htop' starts faster ('top' seems to collect data for a while * `htop` starts faster (`top` seems to collect data for a while
before displaying anything). before displaying anything).
* In 'htop' you don't need to type the process number to * In `htop` you don't need to type the process number to
kill a process, in 'top' you do. kill a process, in `top` you do.
* In 'htop' you don't need to type the process number or * In `htop` you don't need to type the process number or
the priority value to renice a process, in 'top' you do. the priority value to renice a process, in `top` you do.
* In 'htop' you can kill multiple processes at once. * In `htop` you can kill multiple processes at once.
* 'top' is older, hence, more tested. * `top` is older, hence, more tested.
Compilation instructions Compilation instructions
------------------------ ------------------------
This program is distributed as a standard autotools-based package. This program is distributed as a standard autotools-based package.
See the INSTALL file for detailed instructions, but you are See the [INSTALL](/INSTALL) file for detailed instructions.
probably used to the common `./configure`/`make`/`make install` routine.
When fetching the code from the development repository, you need When compiling from a release tarball, run:
to run the `./autogen.sh` script, which in turn requires autotools
to be installed.
See the manual page (man htop) or the on-line help ('F1' or 'h' ./configure && make
inside htop) for a list of supported key commands.
if not all keys work check your curses configuration. For compiling sources downloaded from the Git repository, run:
./autogen.sh && ./configure && make
By default `make install` will install into `/usr/local`, for changing
the path use `./configure --prefix=/some/path`.
See the manual page (`man htop`) or the on-line help ('F1' or 'h'
inside `htop`) for a list of supported key commands.
If not all keys work check your curses configuration.
## License
GNU General Public License, version 2 (GPL-2.0)

View File

@ -189,6 +189,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} }
int prevCh = ch; int prevCh = ch;
set_escdelay(25);
ch = getch(); ch = getch();
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
@ -244,28 +245,11 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
redraw = false; redraw = false;
continue; continue;
} }
else if (ch == 27) { switch (ch) {
int ch2 = getch(); case KEY_ALT('H'): ch = KEY_LEFT; break;
if (ch2 != ERR) { case KEY_ALT('J'): ch = KEY_DOWN; break;
switch(ch2) case KEY_ALT('K'): ch = KEY_UP; break;
{ case KEY_ALT('L'): ch = KEY_RIGHT; break;
case 'h':
ch = KEY_LEFT;
break;
case 'j':
ch = KEY_DOWN;
break;
case 'k':
ch = KEY_UP;
break;
case 'l':
ch = KEY_RIGHT;
break;
default:
ungetch(ch2);
break;
}
}
} }
redraw = true; redraw = true;
if (Panel_eventHandlerFn(panelFocus)) { if (Panel_eventHandlerFn(panelFocus)) {
@ -295,7 +279,10 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
continue; continue;
} }
case KEY_LEFT: case KEY_LEFT:
case KEY_CTRLB: case KEY_CTRL('B'):
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange) if (!this->allowFocusChange)
break; break;
tryLeft: tryLeft:
@ -306,8 +293,11 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
goto tryLeft; goto tryLeft;
break; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRLF: case KEY_CTRL('F'):
case 9: case 9:
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange) if (!this->allowFocusChange)
break; break;
tryRight: tryRight:
@ -323,6 +313,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
quit = true; quit = true;
continue; continue;
default: default:
defaultHandler:
sortTimeout = resetSortTimeout; sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch); Panel_onKey(panelFocus, ch);
break; break;

View File

@ -159,7 +159,7 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
j++; j++;
} }
} }
fields[j] = (ProcessField) NULL; fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids); String_freeArray(ids);
} }
@ -167,18 +167,21 @@ static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd; FILE* fd;
uid_t euid = geteuid(); uid_t euid = geteuid();
seteuid(getuid()); (void) seteuid(getuid());
fd = fopen(fileName, "r"); fd = fopen(fileName, "r");
seteuid(euid); (void) seteuid(euid);
if (!fd) if (!fd)
return false; return false;
const int maxLine = 2048;
char buffer[maxLine];
bool readMeters = false; bool readMeters = false;
while (fgets(buffer, maxLine, fd)) { for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
int nOptions; int nOptions;
char** option = String_split(buffer, '=', &nOptions); char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) { if (nOptions < 2) {
String_freeArray(option); String_freeArray(option);
continue; continue;
@ -277,9 +280,9 @@ bool Settings_write(Settings* this) {
FILE* fd; FILE* fd;
uid_t euid = geteuid(); uid_t euid = geteuid();
seteuid(getuid()); (void) seteuid(getuid());
fd = fopen(this->filename, "w"); fd = fopen(this->filename, "w");
seteuid(euid); (void) seteuid(euid);
if (fd == NULL) { if (fd == NULL) {
return false; return false;
} }
@ -366,7 +369,7 @@ Settings* Settings_new(int cpuCount) {
} }
legacyDotfile = String_cat(home, "/.htoprc"); legacyDotfile = String_cat(home, "/.htoprc");
uid_t euid = geteuid(); uid_t euid = geteuid();
seteuid(getuid()); (void) seteuid(getuid());
(void) mkdir(configDir, 0700); (void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700); (void) mkdir(htopDir, 0700);
free(htopDir); free(htopDir);
@ -379,7 +382,7 @@ Settings* Settings_new(int cpuCount) {
free(legacyDotfile); free(legacyDotfile);
legacyDotfile = NULL; legacyDotfile = NULL;
} }
seteuid(euid); (void) seteuid(euid);
} }
this->colorScheme = 0; this->colorScheme = 0;
this->changed = false; this->changed = false;

View File

@ -13,9 +13,10 @@ in the source distribution for its full text.
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
/*{ /*{
#include <stdio.h>
#define String_startsWith(s, match) (strstr((s), (match)) == (s)) #define String_startsWith(s, match) (strstr((s), (match)) == (s))
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL) #define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
}*/ }*/
@ -69,13 +70,7 @@ char** String_split(const char* s, char sep, int* n) {
ctr++; ctr++;
if (ctr == blocks) { if (ctr == blocks) {
blocks += rate; blocks += rate;
char** newOut = (char**) xRealloc(out, sizeof(char*) * blocks); out = (char**) xRealloc(out, sizeof(char*) * blocks);
if (newOut) {
out = newOut;
} else {
blocks -= rate;
break;
}
} }
s += size + 1; s += size + 1;
} }
@ -86,10 +81,7 @@ char** String_split(const char* s, char sep, int* n) {
out[ctr] = token; out[ctr] = token;
ctr++; ctr++;
} }
char** newOut = xRealloc(out, sizeof(char*) * (ctr + 1)); out = xRealloc(out, sizeof(char*) * (ctr + 1));
if (newOut) {
out = newOut;
}
out[ctr] = NULL; out[ctr] = NULL;
*n = ctr; *n = ctr;
return out; return out;
@ -128,3 +120,29 @@ char* String_getToken(const char* line, const unsigned short int numMatch) {
match[foundCount] = '\0'; match[foundCount] = '\0';
return((char*)xStrdup(match)); 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,6 +9,8 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include <stdio.h>
#define String_startsWith(s, match) (strstr((s), (match)) == (s)) #define String_startsWith(s, match) (strstr((s), (match)) == (s))
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL) #define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
@ -24,4 +26,6 @@ void String_freeArray(char** s);
char* String_getToken(const char* line, const unsigned short int numMatch); char* String_getToken(const char* line, const unsigned short int numMatch);
char* String_readLine(FILE* fd);
#endif #endif

View File

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

View File

@ -18,7 +18,7 @@ int TasksMeter_attributes[] = {
CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING 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; ProcessList* pl = this->pl;
this->values[0] = pl->kernelThreads; this->values[0] = pl->kernelThreads;
this->values[1] = pl->userlandThreads; this->values[1] = pl->userlandThreads;
@ -72,10 +72,10 @@ MeterClass TasksMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = TasksMeter_display, .display = TasksMeter_display,
}, },
.setValues = TasksMeter_setValues, .updateValues = TasksMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.total = 100.0,
.maxItems = 4, .maxItems = 4,
.total = 100.0,
.attributes = TasksMeter_attributes, .attributes = TasksMeter_attributes,
.name = "Tasks", .name = "Tasks",
.uiName = "Task counter", .uiName = "Task counter",

View File

@ -95,7 +95,7 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
this->child = fork(); this->child = fork();
if (this->child == -1) return false; if (this->child == -1) return false;
if (this->child == 0) { if (this->child == 0) {
seteuid(getuid()); (void) seteuid(getuid());
dup2(this->fdpair[1], STDERR_FILENO); dup2(this->fdpair[1], STDERR_FILENO);
int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK); int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) { if (ok != -1) {

View File

@ -17,7 +17,7 @@ int UptimeMeter_attributes[] = {
UPTIME 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(); int totalseconds = Platform_getUptime();
if (totalseconds == -1) { if (totalseconds == -1) {
snprintf(buffer, len, "(unknown)"); snprintf(buffer, len, "(unknown)");
@ -49,8 +49,9 @@ MeterClass UptimeMeter_class = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete
}, },
.setValues = UptimeMeter_setValues, .updateValues = UptimeMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = UptimeMeter_attributes, .attributes = UptimeMeter_attributes,
.name = "Uptime", .name = "Uptime",

View File

@ -10,6 +10,7 @@
#include <string.h> #include <string.h>
/*{ /*{
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
}*/ }*/
@ -29,7 +30,7 @@ void* xMalloc(size_t size) {
void* xCalloc(size_t nmemb, size_t size) { void* xCalloc(size_t nmemb, size_t size) {
void* data = calloc(nmemb, size); void* data = calloc(nmemb, size);
if (!data) { if (!data && nmemb > 0 && size > 0) {
fail(); fail();
} }
return data; return data;
@ -43,9 +44,25 @@ void* xRealloc(void* ptr, size_t size) {
return data; return data;
} }
char* xStrdup(const char* str) { #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); char* data = strdup(str);
if (!data && str) { if (!data) {
fail(); fail();
} }
return data; return data;

View File

@ -7,6 +7,7 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
void* xMalloc(size_t size); void* xMalloc(size_t size);
@ -15,6 +16,22 @@ void* xCalloc(size_t nmemb, size_t size);
void* xRealloc(void* ptr, size_t size); void* xRealloc(void* ptr, size_t size);
char* xStrdup(const char* str); #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 #endif

View File

@ -1,15 +1,3 @@
#!/bin/sh #!/bin/sh
glibtoolize=$(which glibtoolize 2> /dev/null)
if [ ${#glibtoolize} -gt 0 ]
then libtoolize=glibtoolize
else libtoolize=libtoolize
fi
mkdir -p m4 mkdir -p m4
aclocal -I m4 autoreconf --install --force
autoconf
autoheader
$libtoolize --copy --force
automake --add-missing --copy

View File

@ -2,41 +2,45 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65) AC_PREREQ(2.65)
AC_INIT([htop],[2.0.0],[hisham@gobolinux.org]) LT_PREREQ([2.4.2])
AC_INIT([htop],[2.0.2],[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_CONFIG_SRCDIR([htop.c])
AC_USE_SYSTEM_EXTENSIONS AC_CONFIG_AUX_DIR([.])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Required by hwloc scripts
AC_CANONICAL_TARGET AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([1.11]) AM_INIT_AUTOMAKE([1.11])
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Checks for programs. # Checks for programs.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
AC_PROG_CC AC_PROG_CC
AM_PROG_CC_C_O AM_PROG_CC_C_O
AC_DISABLE_SHARED # Required by hwloc scripts
AC_ENABLE_STATIC AC_USE_SYSTEM_EXTENSIONS
AC_PROG_LIBTOOL
LT_INIT([disable-shared static])
# Checks for platform. # Checks for platform.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
case "$target" in case "$target_os" in
*linux*) linux*|gnu*)
my_htop_platform=linux my_htop_platform=linux
;; ;;
*freebsd*) freebsd*|kfreebsd*)
my_htop_platform=freebsd my_htop_platform=freebsd
;; ;;
*openbsd*) openbsd*)
my_htop_platform=openbsd my_htop_platform=openbsd
;; ;;
*darwin*) darwin*)
my_htop_platform=darwin my_htop_platform=darwin
;; ;;
*) *)
@ -74,15 +78,16 @@ AC_CHECK_FUNCS([memmove strncasecmp strstr strdup])
save_cflags="${CFLAGS}" save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99" CFLAGS="${CFLAGS} -std=c99"
AC_MSG_CHECKING([whether gcc -std=c99 option works]) 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_COMPILE_IFELSE([AC_LANG_PROGRAM(
AC_MSG_RESULT([yes]), [AC_INCLUDES_DEFAULT], [[char *a; a = strdup("foo"); int i = 0; i++; // C99]])],
AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.])) [AC_MSG_RESULT([yes])],
[AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.])])
CFLAGS="$save_cflags" CFLAGS="$save_cflags"
save_cflags="${CFLAGS}" save_cflags="${CFLAGS}"
CFLAGS="$CFLAGS -Wextra" CFLAGS="$CFLAGS -Wextra"
AC_MSG_CHECKING([if compiler supports -Wextra]) AC_MSG_CHECKING([if compiler supports -Wextra])
AC_TRY_COMPILE([], [], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],[
wextra_flag=-Wextra wextra_flag=-Wextra
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
],[ ],[
@ -96,14 +101,14 @@ AC_SUBST(wextra_flag)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
PROCDIR=/proc 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 if test "x$enable_proc" = xyes; then
# An enabled proc assumes we're emulating Linux. # An enabled proc assumes we're emulating Linux.
my_htop_platform=linux my_htop_platform=linux
AC_DEFINE(HAVE_PROC, 1, [Define if using a Linux-compatible proc filesystem.]) AC_DEFINE(HAVE_PROC, 1, [Define if using a Linux-compatible proc filesystem.])
fi 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 if test -n "$withval"; then
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem]) AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem])
@ -118,28 +123,28 @@ if test "x$cross_compiling" = xno; then
fi fi
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 if test "x$enable_openvz" = xyes; then
AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.]) AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.])
fi 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 if test "x$enable_cgroup" = xyes; then
AC_DEFINE(HAVE_CGROUP, 1, [Define if cgroup support enabled.]) AC_DEFINE(HAVE_CGROUP, 1, [Define if cgroup support enabled.])
fi 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 if test "x$enable_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.]) AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
fi 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 if test "x$enable_ancient_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.]) AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.]) AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.])
fi 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 if test "x$enable_taskstats" = xyes; then
AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.]) AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.])
fi fi
@ -147,7 +152,12 @@ fi
# HTOP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART) # HTOP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART)
m4_define([HTOP_CHECK_SCRIPT], 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) htop_config_script=$([$4] --libs 2> /dev/null)
fi
htop_script_success=no htop_script_success=no
htop_save_LDFLAGS="$LDFLAGS" htop_save_LDFLAGS="$LDFLAGS"
if test ! "x$htop_config_script" = x; then if test ! "x$htop_config_script" = x; then
@ -173,7 +183,7 @@ m4_define([HTOP_CHECK_LIB],
], [$4]) ], [$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 if test "x$enable_unicode" = xyes; then
HTOP_CHECK_SCRIPT([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config", HTOP_CHECK_SCRIPT([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw5-config", HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw5-config",
@ -211,8 +221,8 @@ if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"]) AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi fi
AC_ARG_ENABLE(native_affinity, [AC_HELP_STRING([--enable-native-affinity], [enable native sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_native_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_native_affinity" = xyes -a "x$cross_compiling" = xno; then if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
AC_MSG_CHECKING([for usable sched_setaffinity]) AC_MSG_CHECKING([for usable sched_setaffinity])
AC_RUN_IFELSE([ AC_RUN_IFELSE([
AC_LANG_PROGRAM([[ AC_LANG_PROGRAM([[
@ -225,17 +235,17 @@ if test "x$enable_native_affinity" = xyes -a "x$cross_compiling" = xno; then
if (errno == ENOSYS) return 1; if (errno == ENOSYS) return 1;
]])], ]])],
[AC_MSG_RESULT([yes])], [AC_MSG_RESULT([yes])],
[enable_native_affinity=no [enable_linux_affinity=no
AC_MSG_RESULT([no])]) AC_MSG_RESULT([no])])
fi fi
if test "x$enable_native_affinity" = xyes; then if test "x$enable_linux_affinity" = xyes; then
AC_DEFINE(HAVE_NATIVE_AFFINITY, 1, [Define if native sched_setaffinity and sched_getaffinity are to be used.]) AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.])
fi 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 if test "x$enable_hwloc" = xyes
then then
AC_CHECK_LIB([hwloc], [hwloc_linux_get_tid_cpubind], [], [missing_libraries="$missing_libraries libhwloc"]) AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"]) AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
fi fi

View File

@ -13,6 +13,8 @@ in the source distribution for its full text.
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <mach/mach.h>
/*{ /*{
#include "Settings.h" #include "Settings.h"
#include "DarwinProcessList.h" #include "DarwinProcessList.h"
@ -24,6 +26,7 @@ typedef struct DarwinProcess_ {
uint64_t utime; uint64_t utime;
uint64_t stime; uint64_t stime;
bool taskAccess;
} DarwinProcess; } DarwinProcess;
}*/ }*/
@ -45,6 +48,7 @@ DarwinProcess* DarwinProcess_new(Settings* settings) {
this->utime = 0; this->utime = 0;
this->stime = 0; this->stime = 0;
this->taskAccess = true;
return this; return this;
} }
@ -69,7 +73,7 @@ void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t no
strftime(proc->starttime_show, 7, ((proc->starttime_ctime > now - 86400) ? "%R " : "%b%d "), &date); strftime(proc->starttime_show, 7, ((proc->starttime_ctime > now - 86400) ? "%R " : "%b%d "), &date);
} }
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) { char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset) {
/* This function is from the old Mac version of htop. Originally from ps? */ /* This function is from the old Mac version of htop. Originally from ps? */
int mib[3], argmax, nargs, c = 0; int mib[3], argmax, nargs, c = 0;
size_t size; size_t size;
@ -167,13 +171,7 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
/* Save where the argv[0] string starts. */ /* Save where the argv[0] string starts. */
sp = cp; sp = cp;
/* *basenameOffset = 0;
* Iterate through the '\0'-terminated strings and convert '\0' to ' '
* until a string is found that has a '=' character in it (or there are
* no more strings in procargs). There is no way to deterministically
* know where the command arguments end and the environment strings
* start, which is why the '=' character is searched for as a heuristic.
*/
for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) { for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) {
if ( *cp == '\0' ) { if ( *cp == '\0' ) {
c++; c++;
@ -183,49 +181,11 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
} }
/* Note location of current '\0'. */ /* Note location of current '\0'. */
np = cp; np = cp;
if (*basenameOffset == 0) {
if ( !show_args ) { *basenameOffset = cp - sp;
/*
* Don't convert '\0' characters to ' '.
* However, we needed to know that the
* command name was terminated, which we
* now know.
*/
break;
} }
} }
} }
#if 0
/*
* If eflg is non-zero, continue converting '\0' characters to ' '
* characters until no more strings that look like environment settings
* follow.
*/
if ( ( eflg != 0 )
&& ( ( getuid( ) == 0 )
|| ( k->kp_eproc.e_pcred.p_ruid == getuid( ) ) ) ) {
for ( ; cp < &procargs[size]; cp++ ) {
if ( *cp == '\0' ) {
if ( np != NULL ) {
if ( &np[1] == cp ) {
/*
* Two '\0' characters in a row.
* This should normally only
* happen after all the strings
* have been seen, but in any
* case, stop parsing.
*/
break;
}
/* Convert previous '\0'. */
*np = ' ';
}
/* Note location of current '\0'. */
np = cp;
}
}
}
#endif
/* /*
* sp points to the beginning of the arguments/environment string, and * sp points to the beginning of the arguments/environment string, and
@ -235,6 +195,9 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ) {
/* Empty or unterminated string. */ /* Empty or unterminated string. */
goto ERROR_B; goto ERROR_B;
} }
if (*basenameOffset == 0) {
*basenameOffset = np - sp;
}
/* Make a copy of the string. */ /* Make a copy of the string. */
retval = xStrdup(sp); retval = xStrdup(sp);
@ -248,6 +211,7 @@ ERROR_B:
free( procargs ); free( procargs );
ERROR_A: ERROR_A:
retval = xStrdup(k->kp_proc.p_comm); retval = xStrdup(k->kp_proc.p_comm);
*basenameOffset = strlen(retval);
return retval; return retval;
} }
@ -275,35 +239,22 @@ void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t
proc->ppid = ps->kp_eproc.e_ppid; proc->ppid = ps->kp_eproc.e_ppid;
proc->pgrp = ps->kp_eproc.e_pgid; proc->pgrp = ps->kp_eproc.e_pgid;
proc->session = 0; /* TODO Get the session id */ proc->session = 0; /* TODO Get the session id */
proc->tgid = ps->kp_eproc.e_tpgid; proc->tpgid = ps->kp_eproc.e_tpgid;
proc->tgid = proc->pid;
proc->st_uid = ps->kp_eproc.e_ucred.cr_uid; proc->st_uid = ps->kp_eproc.e_ucred.cr_uid;
/* e_tdev = (major << 24) | (minor & 0xffffff) */ /* e_tdev = (major << 24) | (minor & 0xffffff) */
/* e_tdev == -1 for "no device" */ /* e_tdev == -1 for "no device" */
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */ proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
DarwinProcess_setStartTime(proc, ep, now); DarwinProcess_setStartTime(proc, ep, now);
proc->comm = DarwinProcess_getCmdLine(ps, &(proc->basenameOffset));
/* The command is from the old Mac htop */
char *slash;
proc->comm = DarwinProcess_getCmdLine(ps, false);
slash = strrchr(proc->comm, '/');
proc->basenameOffset = (NULL != slash) ? (slash - proc->comm) : 0;
} }
/* Mutable information */ /* Mutable information */
proc->nice = ep->p_nice; proc->nice = ep->p_nice;
proc->priority = ep->p_priority; proc->priority = ep->p_priority;
/* Set the state */ proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?';
switch(ep->p_stat) {
case SIDL: proc->state = 'I'; break;
case SRUN: proc->state = 'R'; break;
case SSLEEP: proc->state = 'S'; break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
default: proc->state = '?'; break;
}
/* Make sure the updated flag is set */ /* Make sure the updated flag is set */
proc->updated = true; proc->updated = true;
@ -327,8 +278,8 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000; proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
proc->super.nlwp = pti.pti_threadnum; proc->super.nlwp = pti.pti_threadnum;
proc->super.m_size = pti.pti_virtual_size / 1024; proc->super.m_size = pti.pti_virtual_size / 1024 / PAGE_SIZE_KB;
proc->super.m_resident = pti.pti_resident_size / 1024; proc->super.m_resident = pti.pti_resident_size / 1024 / PAGE_SIZE_KB;
proc->super.majflt = pti.pti_faults; proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
/ (double)dpl->host_info.max_mem; / (double)dpl->host_info.max_mem;
@ -342,3 +293,71 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList
dpl->super.runningTasks += pti.pti_numrunning; dpl->super.runningTasks += pti.pti_numrunning;
} }
} }
/*
* Scan threads for process state information.
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
void DarwinProcess_scanThreads(DarwinProcess *dp) {
Process* proc = (Process*) dp;
kern_return_t ret;
if (!dp->taskAccess) {
return;
}
if (proc->state == 'Z') {
return;
}
task_t port;
ret = task_for_pid(mach_task_self(), proc->pid, &port);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
task_info_data_t tinfo;
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;
}
thread_array_t thread_list;
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;
}
integer_t run_state = 999;
for (unsigned int i = 0; i < thread_count; i++) {
thread_info_data_t thinfo;
mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
ret = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);
if (ret == KERN_SUCCESS) {
thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo;
if (basic_info_th->run_state < run_state) {
run_state = basic_info_th->run_state;
}
mach_port_deallocate(mach_task_self(), thread_list[i]);
}
}
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
mach_port_deallocate(mach_task_self(), port);
char state = '?';
switch (run_state) {
case TH_STATE_RUNNING: state = 'R'; break;
case TH_STATE_STOPPED: state = 'S'; break;
case TH_STATE_WAITING: state = 'W'; break;
case TH_STATE_UNINTERRUPTIBLE: state = 'U'; break;
case TH_STATE_HALTED: state = 'H'; break;
}
proc->state = state;
}

View File

@ -19,6 +19,7 @@ typedef struct DarwinProcess_ {
uint64_t utime; uint64_t utime;
uint64_t stime; uint64_t stime;
bool taskAccess;
} DarwinProcess; } DarwinProcess;
@ -32,10 +33,17 @@ bool Process_isThread(Process* this);
void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now); void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now);
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int show_args ); char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset);
void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists); void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists);
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl); void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl);
/*
* Scan threads for process state information.
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
void DarwinProcess_scanThreads(DarwinProcess *dp);
#endif #endif

View File

@ -168,9 +168,11 @@ void ProcessList_goThroughEntries(ProcessList* super) {
for(size_t i = 0; i < count; ++i) { for(size_t i = 0; i < count; ++i) {
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new); proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, ps + i, tv.tv_sec, preExisting); DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl); DarwinProcess_setFromLibprocPidinfo(proc, dpl);
DarwinProcess_scanThreads(proc);
super->totalTasks += 1; super->totalTasks += 1;
if(!preExisting) { if(!preExisting) {

View File

@ -280,13 +280,11 @@ char* Platform_getProcessEnv(pid_t pid) {
size_t size = endp - p; size_t size = endp - p;
env = xMalloc(size+2); env = xMalloc(size+2);
if (env) {
memcpy(env, p, size); memcpy(env, p, size);
env[size] = 0; env[size] = 0;
env[size+1] = 0; env[size+1] = 0;
} }
} }
}
free(buf); free(buf);
} }
} }

View File

@ -6,10 +6,20 @@ in the source distribution for its full text.
*/ */
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include <sys/sysctl.h>
void Battery_getData(double* level, ACPresence* isOnAC) { void Battery_getData(double* level, ACPresence* isOnAC) {
// TODO int life;
size_t life_len = sizeof(life);
if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
*level = -1; *level = -1;
*isOnAC = AC_ERROR; 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;
}

View File

@ -11,5 +11,4 @@ in the source distribution for its full text.
void Battery_getData(double* level, ACPresence* isOnAC); void Battery_getData(double* level, ACPresence* isOnAC);
#endif #endif

View File

@ -115,7 +115,7 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1; int n = sizeof(buffer) - 1;
switch (field) { switch ((int) field) {
// add FreeBSD-specific fields here // add FreeBSD-specific fields here
case JID: snprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: snprintf(buffer, n, Process_pidFormat, fp->jid); break;
case JAIL:{ case JAIL:{
@ -143,7 +143,7 @@ long FreeBSDProcess_compare(const void* v1, const void* v2) {
p2 = (FreeBSDProcess*)v1; p2 = (FreeBSDProcess*)v1;
p1 = (FreeBSDProcess*)v2; p1 = (FreeBSDProcess*)v2;
} }
switch (settings->sortKey) { switch ((int) settings->sortKey) {
// add FreeBSD-specific fields here // add FreeBSD-specific fields here
case JID: case JID:
return (p1->jid - p2->jid); return (p1->jid - p2->jid);

View File

@ -14,7 +14,9 @@ in the source distribution for its full text.
#include <sys/types.h> #include <sys/types.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/user.h> #include <sys/user.h>
#include <err.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h>
#include <string.h> #include <string.h>
/*{ /*{
@ -84,14 +86,13 @@ static int MIB_kern_cp_time[2];
static int MIB_kern_cp_times[2]; static int MIB_kern_cp_times[2];
static int kernelFScale; static int kernelFScale;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
size_t len;
char errbuf[_POSIX2_LINE_MAX];
FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList)); FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList));
ProcessList* pl = (ProcessList*) fpl; ProcessList* pl = (ProcessList*) fpl;
ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidWhiteList, userId); ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidWhiteList, userId);
size_t len;
// physical memory in system: hw.physmem // physical memory in system: hw.physmem
// physical page size: hw.pagesize // physical page size: hw.pagesize
// usable pagesize : vm.stats.vm.v_page_size // usable pagesize : vm.stats.vm.v_page_size
@ -178,8 +179,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
kernelFScale = 2048; kernelFScale = 2048;
} }
fpl->kd = kvm_open(NULL, "/dev/null", NULL, 0, NULL); fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
assert(fpl->kd); if (fpl->kd == NULL) {
errx(1, "kvm_open: %s", errbuf);
}
return pl; return pl;
} }
@ -212,9 +215,6 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
unsigned long *cp_time_n; // old clicks state unsigned long *cp_time_n; // old clicks state
unsigned long *cp_time_o; // current clicks state unsigned long *cp_time_o; // current clicks state
unsigned long long total_o = 0;
unsigned long long total_n = 0;
unsigned long long total_d = 0;
unsigned long cp_time_d[CPUSTATES]; unsigned long cp_time_d[CPUSTATES];
double cp_time_p[CPUSTATES]; double cp_time_p[CPUSTATES];
@ -251,6 +251,9 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
} }
// diff old vs new // 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++) { for (int s = 0; s < CPUSTATES; s++) {
cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
total_o += cp_time_o[s]; total_o += cp_time_o[s];
@ -424,7 +427,7 @@ void ProcessList_goThroughEntries(ProcessList* this) {
int cpus = this->cpuCount; int cpus = this->cpuCount;
int count = 0; 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++) { for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i]; struct kinfo_proc* kproc = &kprocs[i];
@ -477,25 +480,21 @@ void ProcessList_goThroughEntries(ProcessList* this) {
} }
// from FreeBSD source /src/usr.bin/top/machine.c // from FreeBSD source /src/usr.bin/top/machine.c
proc->m_size = kproc->ki_size / 1024; proc->m_size = kproc->ki_size / 1024 / pageSizeKb;
proc->m_resident = kproc->ki_rssize * pageSizeKb; proc->m_resident = kproc->ki_rssize;
proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
proc->nlwp = kproc->ki_numthreads; proc->nlwp = kproc->ki_numthreads;
proc->time = (kproc->ki_runtime + 5000) / 10000; proc->time = (kproc->ki_runtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale); proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem);
if (proc->percent_cpu > 0.1) { if (proc->percent_cpu > 0.1) {
// system idle process should own all CPU time left regardless of CPU count // system idle process should own all CPU time left regardless of CPU count
if ( strcmp("idle", kproc->ki_comm) == 0 ) { if ( strcmp("idle", kproc->ki_comm) == 0 ) {
isIdleProcess = true; isIdleProcess = true;
} else {
if (cpus > 1)
proc->percent_cpu = proc->percent_cpu / (double) cpus;
} }
} }
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; proc->priority = kproc->ki_pri.pri_level - PZERO;

View File

@ -55,7 +55,6 @@ typedef struct FreeBSDProcessList_ {
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* this); void ProcessList_delete(ProcessList* this);

View File

@ -24,6 +24,7 @@ in the source distribution for its full text.
#include <sys/resource.h> #include <sys/resource.h>
#include <vm/vm_param.h> #include <vm/vm_param.h>
#include <time.h> #include <time.h>
#include <math.h>
/*{ /*{
#include "Action.h" #include "Action.h"

View File

@ -10,7 +10,7 @@ Htop is a free (GPL) ncurses-based process viewer for Linux.
.LP .LP
It is similar to top, but allows you to scroll vertically and horizontally, 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 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. processes and acting on them all at once.
.LP .LP
Tasks related to processes (killing, renicing) can be done without 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: The following commands are supported while in htop:
.LP .LP
.TP 5 .TP 5
.B Arrows, PgUP, PgDn, Home, End .B Up, Alt-k
Scroll the process list. Select (hightlight) the previous process in the process list. Scroll the list
if necessary.
.TP
.B Down, Alt-j
Select (hightlight) 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 .TP
.B Space .B Space
Tag or untag a process. Commands that can operate on multiple processes, Tag or untag a process. Commands that can operate on multiple processes,

196
htop.c
View File

@ -176,7 +176,8 @@ int main(int argc, char** argv) {
#ifdef HAVE_PROC #ifdef HAVE_PROC
if (access(PROCDIR, R_OK) != 0) { if (access(PROCDIR, R_OK) != 0) {
fprintf("Error: could not read procfs (compiled to look in %s).\n", PROCDIR); fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
exit(1);
} }
#endif #endif
@ -229,194 +230,6 @@ int main(int argc, char** argv) {
ScreenManager_run(scr, NULL, NULL); ScreenManager_run(scr, NULL, NULL);
/*
FunctionBar_draw(defaultBar, NULL);
int acc = 0;
bool follow = false;
struct timeval tv;
double oldTime = 0.0;
int ch = ERR;
int closeTimeout = 0;
bool drawPanel = true;
bool collapsed = false;
Htop_Action keys[KEY_MAX] = { NULL };
setBindings(keys);
Platform_setBindings(keys);
bool quit = false;
int sortTimeout = 0;
int resetSortTimeout = 5;
bool doRefresh = true;
bool forceRecalculate = false;
while (!quit) {
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
bool timeToRecalculate = (newTime - oldTime > settings->delay);
if (newTime < oldTime) timeToRecalculate = true; // clock was adjusted?
int following = follow ? MainPanel_selectedPid((MainPanel*)panel) : -1;
if (timeToRecalculate) {
Header_draw(header);
oldTime = newTime;
}
if (doRefresh) {
if (timeToRecalculate || forceRecalculate) {
ProcessList_scan(pl);
forceRecalculate = false;
}
if (sortTimeout == 0 || settings->treeView) {
ProcessList_sort(pl);
sortTimeout = 1;
}
ProcessList_rebuildPanel(pl, true, following, IncSet_filter(inc));
drawPanel = true;
}
doRefresh = true;
if (settings->treeView) {
Process* p = (Process*) Panel_getSelected(panel);
if (p) {
if (!p->showChildren && !collapsed) {
FunctionBar_setLabel(defaultBar, KEY_F(6), "Expand");
FunctionBar_draw(defaultBar, NULL);
} else if (p->showChildren && collapsed) {
FunctionBar_setLabel(defaultBar, KEY_F(6), "Collap");
FunctionBar_draw(defaultBar, NULL);
}
collapsed = !p->showChildren;
}
} else {
collapsed = false;
}
if (drawPanel) {
Panel_draw(panel, true);
}
int prev = ch;
if (inc->active)
move(LINES-1, CRT_cursorX);
ch = getch();
if (ch == ERR) {
if (!inc->active)
sortTimeout--;
if (prev == ch && !timeToRecalculate) {
closeTimeout++;
if (closeTimeout == 100) {
break;
}
} else
closeTimeout = 0;
drawPanel = false;
continue;
}
drawPanel = true;
Htop_Reaction reaction = HTOP_OK;
if (ch == KEY_MOUSE) {
MEVENT mevent;
int ok = getmouse(&mevent);
if (ok == OK) {
if (mevent.bstate & BUTTON1_CLICKED) {
if (mevent.y == panel->y) {
int x = panel->scrollH + mevent.x + 1;
ProcessField field = ProcessList_keyAt(pl, x);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
reaction |= HTOP_REDRAW_BAR;
} else {
reaction |= setSortKey(settings, field);
}
sortTimeout = 0;
ch = ERR;
} else if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
follow = true;
ch = ERR;
} if (mevent.y == LINES - 1) {
ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
}
} else if (mevent.bstate & BUTTON4_CLICKED) {
ch = KEY_UP;
#if NCURSES_MOUSE_VERSION > 1
} else if (mevent.bstate & BUTTON5_CLICKED) {
ch = KEY_DOWN;
#endif
}
}
}
if (inc->active) {
doRefresh = IncSet_handleKey(inc, ch, panel, getMainPanelValue, NULL);
if (!inc->active) {
follow = true;
}
continue;
}
if (ch < 255 && isdigit((char)ch)) {
if (Panel_size(panel) == 0) continue;
pid_t pid = ch-48 + acc;
for (int i = 0; i < ProcessList_size(pl); i++) {
Panel_setSelected(panel, i);
Process* p = (Process*) Panel_getSelected(panel);
if (p && p->pid == pid) {
break;
}
}
acc = pid * 10;
if (acc > 10000000)
acc = 0;
continue;
} else {
acc = 0;
}
if(ch != ERR && keys[ch]) {
reaction |= (keys[ch])(&state);
} else {
doRefresh = false;
sortTimeout = resetSortTimeout;
Panel_onKey(panel, ch);
}
// Reaction handlers:
if (reaction & HTOP_REDRAW_BAR) {
updateTreeFunctions(defaultBar, settings->treeView);
IncSet_drawBar(inc);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(pl, Panel_getHeader(panel));
}
if (reaction & HTOP_REFRESH) {
doRefresh = true;
sortTimeout = 0;
}
if (reaction & HTOP_RECALCULATE) {
forceRecalculate = true;
sortTimeout = 0;
}
if (reaction & HTOP_SAVE_SETTINGS) {
settings->changed = true;
}
if (reaction & HTOP_QUIT) {
quit = true;
}
follow = (reaction & HTOP_KEEP_FOLLOWING);
}
*/
attron(CRT_colors[RESET_COLOR]); attron(CRT_colors[RESET_COLOR]);
mvhline(LINES-1, 0, ' ', COLS); mvhline(LINES-1, 0, ' ', COLS);
attroff(CRT_colors[RESET_COLOR]); attroff(CRT_colors[RESET_COLOR]);
@ -428,11 +241,6 @@ int main(int argc, char** argv) {
Header_delete(header); Header_delete(header);
ProcessList_delete(pl); ProcessList_delete(pl);
/*
FunctionBar_delete((Object*)defaultBar);
Panel_delete((Object*)panel);
*/
ScreenManager_delete(scr); ScreenManager_delete(scr);
UsersTable_delete(ut); UsersTable_delete(ut);

View File

@ -60,3 +60,4 @@ GenericName[sv]=Processvisning
GenericName[tr]=Süreç Görüntüleyici GenericName[tr]=Süreç Görüntüleyici
GenericName[uk]=Перегляд процесів GenericName[uk]=Перегляд процесів
GenericName[zh_TW]=行程檢視器 GenericName[zh_TW]=行程檢視器
Keywords=system;process;task

View File

@ -65,10 +65,11 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
break; break;
} }
char line[50] = ""; char* line = NULL;
for (unsigned short int i = 0; i < lineNum; i++) { for (unsigned short int i = 0; i < lineNum; i++) {
char* ok = fgets(line, sizeof line, file); free(line);
if (!ok) break; line = String_readLine(file);
if (!line) break;
} }
fclose(file); fclose(file);
@ -76,6 +77,7 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short
char *foundNumStr = String_getToken(line, wordNum); char *foundNumStr = String_getToken(line, wordNum);
const unsigned long int foundNum = atoi(foundNumStr); const unsigned long int foundNum = atoi(foundNumStr);
free(foundNumStr); free(foundNumStr);
free(line);
total += foundNum; total += foundNum;
} }
@ -116,14 +118,13 @@ static ACPresence procAcpiCheck() {
continue; continue;
} }
char line[100]; char* line = String_readLine(file);
char* ok = fgets(line, sizeof line, file); if (!line) continue;
if (!ok) continue;
line[sizeof(line) - 1] = '\0';
fclose(file); fclose(file);
const char *isOnline = String_getToken(line, 2); const char *isOnline = String_getToken(line, 2);
free(line);
if (strcmp(isOnline, "on-line") == 0) { if (strcmp(isOnline, "on-line") == 0) {
isOn = AC_PRESENT; isOn = AC_PRESENT;

View File

@ -268,13 +268,20 @@ io_priority = (cpu_nice + 20) / 5. -- From ionice(1) man page
#define LinuxProcess_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->super.nice + 20) / 5) : p_->ioPriority) #define LinuxProcess_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->super.nice + 20) / 5) : p_->ioPriority)
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) { IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
IOPriority ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, this->super.pid); IOPriority ioprio = 0;
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
#ifdef SYS_ioprio_get
ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, this->super.pid);
#endif
this->ioPriority = ioprio; this->ioPriority = ioprio;
return ioprio; return ioprio;
} }
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) { bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) {
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
#ifdef SYS_ioprio_set
syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio); syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio);
#endif
return (LinuxProcess_updateIOPriority(this) == ioprio); return (LinuxProcess_updateIOPriority(this) == ioprio);
} }
@ -306,7 +313,12 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
case CNCLWB: Process_colorNumber(str, lp->io_cancelled_write_bytes, coloring); return; case CNCLWB: Process_colorNumber(str, lp->io_cancelled_write_bytes, coloring); return;
case IO_READ_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps, coloring); return; case IO_READ_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps, coloring); return;
case IO_WRITE_RATE: Process_outputRate(str, buffer, n, lp->io_rate_write_bps, coloring); return; case IO_WRITE_RATE: Process_outputRate(str, buffer, n, lp->io_rate_write_bps, coloring); return;
case IO_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps + lp->io_rate_write_bps, coloring); return; case IO_RATE: {
double totalRate = (lp->io_rate_read_bps != -1)
? (lp->io_rate_read_bps + lp->io_rate_write_bps)
: -1;
Process_outputRate(str, buffer, n, totalRate, coloring); return;
}
#endif #endif
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
case CTID: snprintf(buffer, n, "%7u ", lp->ctid); break; case CTID: snprintf(buffer, n, "%7u ", lp->ctid); break;

View File

@ -292,18 +292,19 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
} }
break; break;
case 's': case 's':
if (line[5] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) if (line[5] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
process->io_syscr = strtoull(line+7, NULL, 10); process->io_syscr = strtoull(line+7, NULL, 10);
else if (strncmp(line+1, "yscw: ", 6) == 0) } else if (strncmp(line+1, "yscw: ", 6) == 0) {
sscanf(line, "syscw: %32llu", &process->io_syscw);
process->io_syscw = strtoull(line+7, NULL, 10); process->io_syscw = strtoull(line+7, NULL, 10);
}
break; break;
case 'c': case 'c':
if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0) if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0) {
process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10); process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10);
} }
} }
} }
}
#endif #endif
@ -446,7 +447,7 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirn
} }
static void setCommand(Process* process, const char* command, int len) { static void setCommand(Process* process, const char* command, int len) {
if (process->comm && process->commLen <= len) { if (process->comm && process->commLen >= len) {
strncpy(process->comm, command, len + 1); strncpy(process->comm, command, len + 1);
} else { } else {
free(process->comm); free(process->comm);
@ -469,6 +470,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
int amtRead = xread(fd, command, sizeof(command) - 1); int amtRead = xread(fd, command, sizeof(command) - 1);
close(fd); close(fd);
int tokenEnd = 0; int tokenEnd = 0;
int lastChar = 0;
if (amtRead > 0) { if (amtRead > 0) {
for (int i = 0; i < amtRead; i++) for (int i = 0; i < amtRead; i++)
if (command[i] == '\0' || command[i] == '\n') { if (command[i] == '\0' || command[i] == '\n') {
@ -476,14 +478,16 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
tokenEnd = i; tokenEnd = i;
} }
command[i] = ' '; command[i] = ' ';
} else {
lastChar = i;
} }
} }
if (tokenEnd == 0) { if (tokenEnd == 0) {
tokenEnd = amtRead; tokenEnd = amtRead;
} }
command[amtRead] = '\0'; command[lastChar + 1] = '\0';
process->basenameOffset = tokenEnd; process->basenameOffset = tokenEnd;
setCommand(process, command, amtRead); setCommand(process, command, lastChar + 1);
return true; return true;
} }
@ -584,7 +588,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
ProcessList_add(pl, proc); ProcessList_add(pl, proc);
} else { } else {
if (settings->updateProcessNames) { if (settings->updateProcessNames && proc->state != 'Z') {
if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) { if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) {
goto errorReadingProcess; goto errorReadingProcess;
} }
@ -599,11 +603,11 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
if (settings->flags & PROCESS_FLAG_LINUX_OOM) if (settings->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcessList_readOomData(lp, dirname, name); LinuxProcessList_readOomData(lp, dirname, name);
if (proc->state == 'Z') { if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1; proc->basenameOffset = -1;
setCommand(proc, command, commLen); setCommand(proc, command, commLen);
} else if (Process_isThread(proc)) { } else if (Process_isThread(proc)) {
if (settings->showThreadNames || Process_isKernelThread(proc) || proc->state == 'Z') { if (settings->showThreadNames || Process_isKernelThread(proc) || (proc->state == 'Z' && proc->basenameOffset == 0)) {
proc->basenameOffset = -1; proc->basenameOffset = -1;
setCommand(proc, command, commLen); setCommand(proc, command, commLen);
} else if (settings->showThreadNames) { } else if (settings->showThreadNames) {
@ -720,31 +724,23 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
unsigned long long int virtalltime = guest + guestnice; unsigned long long int virtalltime = guest + guestnice;
unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
CPUData* cpuData = &(this->cpus[i]); CPUData* cpuData = &(this->cpus[i]);
assert (systemtime >= cpuData->systemTime);
assert (idletime >= cpuData->idleTime);
assert (totaltime >= cpuData->totalTime);
assert (systemalltime >= cpuData->systemAllTime);
assert (idlealltime >= cpuData->idleAllTime);
assert (ioWait >= cpuData->ioWaitTime);
assert (irq >= cpuData->irqTime);
assert (softIrq >= cpuData->softIrqTime);
assert (steal >= cpuData->stealTime);
assert (virtalltime >= cpuData->guestTime);
// Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t()
// used in /proc/stat rounds down numbers, it can lead to a case where the // used in /proc/stat rounds down numbers, it can lead to a case where the
// integer overflow. // integer overflow.
cpuData->userPeriod = (usertime > cpuData->userTime) ? usertime - cpuData->userTime : 0; #define WRAP_SUBTRACT(a,b) (a > b) ? a - b : 0
cpuData->nicePeriod = (nicetime > cpuData->niceTime) ? nicetime - cpuData->niceTime : 0; cpuData->userPeriod = WRAP_SUBTRACT(usertime, cpuData->userTime);
cpuData->systemPeriod = systemtime - cpuData->systemTime; cpuData->nicePeriod = WRAP_SUBTRACT(nicetime, cpuData->niceTime);
cpuData->systemAllPeriod = systemalltime - cpuData->systemAllTime; cpuData->systemPeriod = WRAP_SUBTRACT(systemtime, cpuData->systemTime);
cpuData->idleAllPeriod = idlealltime - cpuData->idleAllTime; cpuData->systemAllPeriod = WRAP_SUBTRACT(systemalltime, cpuData->systemAllTime);
cpuData->idlePeriod = idletime - cpuData->idleTime; cpuData->idleAllPeriod = WRAP_SUBTRACT(idlealltime, cpuData->idleAllTime);
cpuData->ioWaitPeriod = ioWait - cpuData->ioWaitTime; cpuData->idlePeriod = WRAP_SUBTRACT(idletime, cpuData->idleTime);
cpuData->irqPeriod = irq - cpuData->irqTime; cpuData->ioWaitPeriod = WRAP_SUBTRACT(ioWait, cpuData->ioWaitTime);
cpuData->softIrqPeriod = softIrq - cpuData->softIrqTime; cpuData->irqPeriod = WRAP_SUBTRACT(irq, cpuData->irqTime);
cpuData->stealPeriod = steal - cpuData->stealTime; cpuData->softIrqPeriod = WRAP_SUBTRACT(softIrq, cpuData->softIrqTime);
cpuData->guestPeriod = virtalltime - cpuData->guestTime; cpuData->stealPeriod = WRAP_SUBTRACT(steal, cpuData->stealTime);
cpuData->totalPeriod = totaltime - cpuData->totalTime; cpuData->guestPeriod = WRAP_SUBTRACT(virtalltime, cpuData->guestTime);
cpuData->totalPeriod = WRAP_SUBTRACT(totaltime, cpuData->totalTime);
#undef WRAP_SUBTRACT
cpuData->userTime = usertime; cpuData->userTime = usertime;
cpuData->niceTime = nicetime; cpuData->niceTime = nicetime;
cpuData->systemTime = systemtime; cpuData->systemTime = systemtime;

View File

@ -10,18 +10,20 @@ in the source distribution for its full text.
#include "OpenBSDProcessList.h" #include "OpenBSDProcessList.h"
#include "OpenBSDProcess.h" #include "OpenBSDProcess.h"
#include <unistd.h>
#include <stdlib.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <fcntl.h>
#include <sys/mount.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/proc.h> #include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <fcntl.h>
#include <string.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*{ /*{
@ -42,22 +44,37 @@ typedef struct OpenBSDProcessList_ {
}*/ }*/
#ifndef CLAMP /*
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) * avoid relying on or conflicting with MIN() and MAX() in sys/param.h
*/
#ifndef MINIMUM
#define MINIMUM(x, y) ((x) > (y) ? (y) : (x))
#endif
#ifndef MAXIMUM
#define MAXIMUM(x, y) ((x) > (y) ? (x) : (y))
#endif
#ifndef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : MAXIMUM(x, low))
#endif #endif
static int pageSizeKb;
static long fscale; static long fscale;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
int mib[] = { CTL_HW, HW_NCPU }; int mib[] = { CTL_HW, HW_NCPU };
int fmib[] = { CTL_KERN, KERN_FSCALE }; int fmib[] = { CTL_KERN, KERN_FSCALE };
int i, e; int i, e;
OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList)); OpenBSDProcessList* opl;
ProcessList* pl = (ProcessList*) opl; ProcessList* pl;
size_t size = sizeof(pl->cpuCount); size_t size;
char errbuf[_POSIX2_LINE_MAX];
opl = xCalloc(1, sizeof(OpenBSDProcessList));
pl = (ProcessList*) opl;
size = sizeof(pl->cpuCount);
ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidWhiteList, userId); ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidWhiteList, userId);
e = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0); e = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0);
if (e == -1 || pl->cpuCount < 1) { if (e == -1 || pl->cpuCount < 1) {
pl->cpuCount = 1; pl->cpuCount = 1;
@ -65,26 +82,29 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
opl->cpus = xRealloc(opl->cpus, pl->cpuCount * sizeof(CPUData)); opl->cpus = xRealloc(opl->cpus, pl->cpuCount * sizeof(CPUData));
size = sizeof(fscale); size = sizeof(fscale);
if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) {
err(1, "fscale sysctl call failed"); err(1, "fscale sysctl call failed");
}
for (i = 0; i < pl->cpuCount; i++) { for (i = 0; i < pl->cpuCount; i++) {
opl->cpus[i].totalTime = 1; opl->cpus[i].totalTime = 1;
opl->cpus[i].totalPeriod = 1; opl->cpus[i].totalPeriod = 1;
} }
pageSizeKb = PAGE_SIZE_KB; opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
if (opl->kd == NULL) {
// XXX: last arg should eventually be an errbuf errx(1, "kvm_open: %s", errbuf);
opl->kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL); }
assert(opl->kd);
return pl; return pl;
} }
void ProcessList_delete(ProcessList* this) { void ProcessList_delete(ProcessList* this) {
const OpenBSDProcessList* opl = (OpenBSDProcessList*) this; const OpenBSDProcessList* opl = (OpenBSDProcessList*) this;
if (opl->kd) kvm_close(opl->kd);
if (opl->kd) {
kvm_close(opl->kd);
}
free(opl->cpus); free(opl->cpus);
@ -95,15 +115,26 @@ void ProcessList_delete(ProcessList* this) {
static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP}; static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
struct uvmexp uvmexp; struct uvmexp uvmexp;
size_t size = sizeof(uvmexp); size_t size_uvmexp = sizeof(uvmexp);
if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) < 0) { if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) {
err(1, "uvmexp sysctl call failed"); err(1, "uvmexp sysctl call failed");
} }
//kb_pagesize = uvmexp.pagesize / 1024; pl->totalMem = uvmexp.npages * PAGE_SIZE_KB;
pl->usedMem = uvmexp.active * pageSizeKb;
pl->totalMem = uvmexp.npages * pageSizeKb; // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9)
static int bcache_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT};
struct bcachestats bcstats;
size_t size_bcstats = sizeof(bcstats);
if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) {
err(1, "cannot get vfs.bcachestat");
}
pl->cachedMem = bcstats.numbufpages * PAGE_SIZE_KB;
pl->freeMem = uvmexp.free * PAGE_SIZE_KB;
pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * PAGE_SIZE_KB;
/* /*
const OpenBSDProcessList* opl = (OpenBSDProcessList*) pl; const OpenBSDProcessList* opl = (OpenBSDProcessList*) pl;
@ -112,10 +143,10 @@ static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0); sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0);
pl->totalMem /= 1024; pl->totalMem /= 1024;
sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(pl->usedMem), &len, NULL, 0); sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(pl->usedMem), &len, NULL, 0);
pl->usedMem *= pageSizeKb; pl->usedMem *= PAGE_SIZE_KB;
pl->freeMem = pl->totalMem - pl->usedMem; pl->freeMem = pl->totalMem - pl->usedMem;
sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0); sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0);
pl->cachedMem *= pageSizeKb; pl->cachedMem *= PAGE_SIZE_KB;
struct kvm_swap swap[16]; struct kvm_swap swap[16];
int nswap = kvm_getswapinfo(opl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0); int nswap = kvm_getswapinfo(opl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0);
@ -125,8 +156,8 @@ static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
pl->totalSwap += swap[i].ksw_total; pl->totalSwap += swap[i].ksw_total;
pl->usedSwap += swap[i].ksw_used; pl->usedSwap += swap[i].ksw_used;
} }
pl->totalSwap *= pageSizeKb; pl->totalSwap *= PAGE_SIZE_KB;
pl->usedSwap *= pageSizeKb; pl->usedSwap *= PAGE_SIZE_KB;
pl->sharedMem = 0; // currently unused pl->sharedMem = 0; // currently unused
pl->buffersMem = 0; // not exposed to userspace pl->buffersMem = 0; // not exposed to userspace
@ -134,40 +165,40 @@ static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
} }
char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) { char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
char *s, *buf, **arg; char *s, **arg;
size_t cpsz, len = 0, n; size_t len = 0, n;
int i; int i;
/* /*
* We attempt to fall back to just the command name (argv[0]) if we * Like OpenBSD's top(1), we try to fall back to the command name
* fail to construct the full command at any point. * (argv[0]) if we fail to construct the full command.
*/ */
arg = kvm_getargv(kd, kproc, 500); arg = kvm_getargv(kd, kproc, 500);
if (arg == NULL) { if (arg == NULL || *arg == NULL) {
if ((s = xStrdup(kproc->p_comm)) == NULL) { *basenameEnd = strlen(kproc->p_comm);
err(1, NULL); return xStrdup(kproc->p_comm);
}
return s;
} }
for (i = 0; arg[i] != NULL; i++) { for (i = 0; arg[i] != NULL; i++) {
len += strlen(arg[i]) + 1; len += strlen(arg[i]) + 1; /* room for arg and trailing space or NUL */
} }
if ((buf = s = xMalloc(len)) == NULL) { /* don't use xMalloc here - we want to handle huge argv's gracefully */
if ((s = xStrdup(kproc->p_comm)) == NULL) { if ((s = malloc(len)) == NULL) {
err(1, NULL); *basenameEnd = strlen(kproc->p_comm);
} return xStrdup(kproc->p_comm);
return s;
} }
*s = '\0';
for (i = 0; arg[i] != NULL; i++) { for (i = 0; arg[i] != NULL; i++) {
n = strlcpy(buf, arg[i], (s + len) - buf); n = strlcat(s, arg[i], len);
buf += n;
if (i == 0) { if (i == 0) {
*basenameEnd = n; /* TODO: rename all basenameEnd to basenameLen, make size_t */
*basenameEnd = MINIMUM(n, len-1);
} }
*buf = ' '; /* the trailing space should get truncated anyway */
buf++; strlcat(s, " ", len);
} }
*(buf - 1) = '\0';
return s; return s;
} }

View File

@ -27,10 +27,20 @@ typedef struct OpenBSDProcessList_ {
} OpenBSDProcessList; } OpenBSDProcessList;
#ifndef CLAMP /*
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) * avoid relying on or conflicting with MIN() and MAX() in sys/param.h
*/
#ifndef MINIMUM
#define MINIMUM(x, y) ((x) > (y) ? (y) : (x))
#endif #endif
#ifndef MAXIMUM
#define MAXIMUM(x, y) ((x) > (y) ? (x) : (y))
#endif
#ifndef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : MAXIMUM(x, low))
#endif
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);

View File

@ -265,9 +265,6 @@ void Platform_setSwapValues(Meter* this) {
} }
swdev = xCalloc(nswap, sizeof(*swdev)); swdev = xCalloc(nswap, sizeof(*swdev));
if (swdev == NULL) {
return;
}
rnswap = swapctl(SWAP_STATS, swdev, nswap); rnswap = swapctl(SWAP_STATS, swdev, nswap);
if (rnswap == -1) { if (rnswap == -1) {

View File

@ -4,6 +4,8 @@ local VISUALDELAY = os.getenv("VISUALDELAY")
local visual = VISUALDELAY or false local visual = VISUALDELAY or false
local visual_delay = VISUALDELAY and (tonumber(VISUALDELAY)) or 0.1 local visual_delay = VISUALDELAY and (tonumber(VISUALDELAY)) or 0.1
local short_delay = 0.3
local long_delay = 1
local unistd = require("posix.unistd") local unistd = require("posix.unistd")
local time = require("posix.time") local time = require("posix.time")
@ -12,15 +14,16 @@ local rote = require("rote")
local rt = rote.RoteTerm(24, 80) local rt = rote.RoteTerm(24, 80)
--[[
local function os_execread(cmd) local function os_execread(cmd)
local fd = io.popen(cmd, "r") local fd = io.popen(cmd, "r")
local out = fd:read("*a") local out = fd:read("*a")
fd:close() fd:close()
return (out:gsub("\n$", "")) return (out:gsub("\n$", ""))
end end
local branch = os_execread("git branch | grep '*'"):sub(3) ]]
--local branch = os_execread("git branch | grep '*'"):sub(3)
print("Running in branch "..branch) --print("Running in branch "..branch)
os.execute("make coverage") os.execute("make coverage")
os.execute("rm -f *.gcda */*.gcda") os.execute("rm -f *.gcda */*.gcda")
@ -30,7 +33,7 @@ os.execute("killall htop")
os.execute("ps aux | grep '[s]leep 12345' | awk '{print $2}' | xargs kill 2> /dev/null") os.execute("ps aux | grep '[s]leep 12345' | awk '{print $2}' | xargs kill 2> /dev/null")
os.execute("cp ./default.htoprc ./test.htoprc") os.execute("cp ./default.htoprc ./test.htoprc")
rt:forkPty("LC_ALL=C HTOPRC=./test.htoprc ./htop") rt:forkPty("LC_ALL=C HTOPRC=./test.htoprc ./htop 2> htop-valgrind.txt")
local stdscr, term_win local stdscr, term_win
-- Curses initalization needed even when not in visual mode -- Curses initalization needed even when not in visual mode
@ -59,10 +62,6 @@ else
curses.endwin() curses.endwin()
end end
local function delay(t)
time.nanosleep({ tv_sec = math.floor(t), tv_nsec = (t - math.floor(t)) * 1000000000 })
end
local function show(key) local function show(key)
rt:update() rt:update()
if visual then if visual then
@ -115,7 +114,12 @@ end
local ESC = "\27\27" local ESC = "\27\27"
time.nanosleep({ tv_sec = 0, tv_nsec = 150000000 }) -- give some time for htop to initialize. function delay(t)
time.nanosleep({ tv_sec = math.floor(t), tv_nsec = (t - math.floor(t)) * 1000000000 })
end
delay(2) -- give some time for htop to initialize.
rt:update()
local y_panelhdr = (function() local y_panelhdr = (function()
for y = 1, 24 do for y = 1, 24 do
@ -125,7 +129,9 @@ local y_panelhdr = (function()
end end
end)() or 1 end)() or 1
local x_metercol2 = (branch == "wip") and 41 or 43 assert.not_equal(y_panelhdr, 1)
local x_metercol2 = 41
show() show()
@ -190,7 +196,7 @@ describe("htop test suite", function()
send("\\") send("\\")
send("x\127bux\127sted") -- test backspace send("x\127bux\127sted") -- test backspace
send("\n") send("\n")
delay(0.2) delay(short_delay)
rt:update() rt:update()
local pid = (" "..tostring(unistd.getpid())):sub(-5) local pid = (" "..tostring(unistd.getpid())):sub(-5)
local ourpid = check_string_at(1, y_panelhdr + 1, pid) local ourpid = check_string_at(1, y_panelhdr + 1, pid)
@ -198,10 +204,10 @@ describe("htop test suite", function()
send(ESC) send(ESC)
send(curses.KEY_F5) send(curses.KEY_F5)
send(curses.KEY_HOME) send(curses.KEY_HOME)
delay(0.15) delay(short_delay)
rt:update() rt:update()
local initpid = check_string_at(1, y_panelhdr + 1, " 1") local initpid = check_string_at(1, y_panelhdr + 1, " 1")
delay(0.15) delay(short_delay)
rt:update() rt:update()
send(curses.KEY_F5) send(curses.KEY_F5)
assert.equal(check(ourpid)) assert.equal(check(ourpid))
@ -213,7 +219,7 @@ describe("htop test suite", function()
send("/") send("/")
send("busted") send("busted")
local attr = rt:cellAttr(rt:rows() - 1, 30) local attr = rt:cellAttr(rt:rows() - 1, 30)
delay(0.3) delay(short_delay)
local line = find_selected_y() local line = find_selected_y()
local pid = (" "..tostring(unistd.getpid())):sub(-5) local pid = (" "..tostring(unistd.getpid())):sub(-5)
assert.equal(attr, attrs.black_on_cyan) assert.equal(attr, attrs.black_on_cyan)
@ -227,7 +233,7 @@ describe("htop test suite", function()
send(curses.KEY_F5) send(curses.KEY_F5)
send(curses.KEY_END) send(curses.KEY_END)
send("1") send("1")
delay(0.3) delay(short_delay)
local line = find_selected_y() local line = find_selected_y()
local initpid = check_string_at(1, line, " 1") local initpid = check_string_at(1, line, " 1")
send(curses.KEY_F5) send(curses.KEY_F5)
@ -238,16 +244,16 @@ describe("htop test suite", function()
running_it("horizontal scroll", function() running_it("horizontal scroll", function()
local h_scroll = 20 local h_scroll = 20
send(curses.KEY_F5) send(curses.KEY_F5)
delay(0.15) delay(short_delay)
local str1 = string_at(1+h_scroll, y_panelhdr+1, 5) local str1 = string_at(1+h_scroll, y_panelhdr+1, 5)
send(curses.KEY_RIGHT) send(curses.KEY_RIGHT)
delay(0.15) delay(short_delay)
local str2 = string_at(1, y_panelhdr+1, 5) local str2 = string_at(1, y_panelhdr+1, 5)
send(curses.KEY_LEFT) send(curses.KEY_LEFT)
delay(0.15) delay(short_delay)
local str3 = string_at(1+h_scroll, y_panelhdr+1, 5) local str3 = string_at(1+h_scroll, y_panelhdr+1, 5)
send(curses.KEY_LEFT) send(curses.KEY_LEFT)
delay(0.15) delay(short_delay)
local str4 = string_at(1+h_scroll, y_panelhdr+1, 5) local str4 = string_at(1+h_scroll, y_panelhdr+1, 5)
send(curses.KEY_F5) send(curses.KEY_F5)
assert.equal(str1, str2) assert.equal(str1, str2)
@ -262,7 +268,7 @@ describe("htop test suite", function()
local attr = rt:cellAttr(rt:rows() - 1, 30) local attr = rt:cellAttr(rt:rows() - 1, 30)
assert.equal(attr, attrs.black_on_cyan) assert.equal(attr, attrs.black_on_cyan)
send("\n") send("\n")
delay(0.3) delay(short_delay)
rt:update() rt:update()
local col = find_command_x() local col = find_command_x()
local procname = check_string_at(col, y_panelhdr + 1, "sleep 12345") local procname = check_string_at(col, y_panelhdr + 1, "sleep 12345")
@ -270,7 +276,7 @@ describe("htop test suite", function()
send("\n") send("\n")
send("\\") send("\\")
send(ESC) send(ESC)
delay(0.3) delay(short_delay)
assert.equal(check(procname)) assert.equal(check(procname))
assert.not_equal((os.execute("ps aux | grep -q '[s]leep 12345'")), true) assert.not_equal((os.execute("ps aux | grep -q '[s]leep 12345'")), true)
end) end)
@ -281,7 +287,7 @@ describe("htop test suite", function()
send("busted") send("busted")
send("\n") send("\n")
send("s") send("s")
delay(1) delay(long_delay)
send(ESC) send(ESC)
end) end)
@ -291,7 +297,7 @@ describe("htop test suite", function()
send("busted") send("busted")
send("\n") send("\n")
send("l") send("l")
delay(1) delay(long_delay)
send(ESC) send(ESC)
end) end)
@ -303,7 +309,7 @@ describe("htop test suite", function()
send("l") send("l")
send(curses.KEY_F4) send(curses.KEY_F4)
send("pipe") send("pipe")
delay(1) delay(long_delay)
local pipefd = check_string_at(1, 3, " 3") local pipefd = check_string_at(1, 3, " 3")
send(ESC) send(ESC)
assert.equal(check(pipefd)) assert.equal(check(pipefd))
@ -317,7 +323,7 @@ describe("htop test suite", function()
send("l") send("l")
send(curses.KEY_F3) send(curses.KEY_F3)
send("pipe") send("pipe")
delay(1) delay(long_delay)
local line = find_selected_y(3) local line = find_selected_y(3)
local pipefd = check_string_at(1, line, " 3") local pipefd = check_string_at(1, line, " 3")
send(ESC) send(ESC)
@ -341,18 +347,18 @@ describe("htop test suite", function()
send(curses.KEY_F5) send(curses.KEY_F5)
send("u") send("u")
send(curses.KEY_DOWN) send(curses.KEY_DOWN)
delay(0.3) delay(short_delay)
rt:update() rt:update()
local chosen = string_at(1, y_panelhdr + 2, 9) local chosen = string_at(1, y_panelhdr + 2, 9)
send("\n") send("\n")
send(curses.KEY_HOME) send(curses.KEY_HOME)
delay(0.3) delay(short_delay)
rt:update() rt:update()
local shown = string_at(7, y_panelhdr + 1, 9) local shown = string_at(7, y_panelhdr + 1, 9)
send("u") send("u")
send("\n") send("\n")
send(curses.KEY_HOME) send(curses.KEY_HOME)
delay(0.3) delay(short_delay)
rt:update() rt:update()
local inituser = string_at(7, y_panelhdr + 1, 9) local inituser = string_at(7, y_panelhdr + 1, 9)
send(curses.KEY_F5) send(curses.KEY_F5)
@ -364,7 +370,7 @@ describe("htop test suite", function()
send(curses.KEY_HOME) send(curses.KEY_HOME)
send("/") send("/")
send("xxxxxxxxxx") send("xxxxxxxxxx")
delay(0.3) delay(short_delay)
rt:update() rt:update()
local attr = rt:cellAttr(rt:rows() - 1, 30) local attr = rt:cellAttr(rt:rows() - 1, 30)
assert.equal(attr, attrs.red_on_cyan) assert.equal(attr, attrs.red_on_cyan)
@ -405,14 +411,14 @@ describe("htop test suite", function()
send(curses.KEY_DOWN, 2) send(curses.KEY_DOWN, 2)
send("\n") send("\n")
send(curses.KEY_F10) send(curses.KEY_F10)
delay(0.2) delay(short_delay)
local ppid = check_string_at(2, y_panelhdr, "PPID") local ppid = check_string_at(2, y_panelhdr, "PPID")
send("S") send("S")
send(curses.KEY_DOWN, 3) send(curses.KEY_DOWN, 3)
send(curses.KEY_RIGHT, 1) send(curses.KEY_RIGHT, 1)
send(curses.KEY_DC) send(curses.KEY_DC)
send(curses.KEY_F10) send(curses.KEY_F10)
delay(0.2) delay(short_delay)
local not_ppid = check_string_at(2, y_panelhdr, "PPID") local not_ppid = check_string_at(2, y_panelhdr, "PPID")
assert.equal(check(ppid)) assert.equal(check(ppid))
assert.not_equal(check(not_ppid)) assert.not_equal(check(not_ppid))
@ -431,7 +437,7 @@ describe("htop test suite", function()
local line = find_selected_y() local line = find_selected_y()
local before = check_string_at(22, line, " 0") local before = check_string_at(22, line, " 0")
send(curses.KEY_F8) send(curses.KEY_F8)
delay(0.3) delay(short_delay)
local after = check_string_at(22, line, " 1") local after = check_string_at(22, line, " 1")
assert.equal(check(before)) assert.equal(check(before))
assert.equal(check(after)) assert.equal(check(after))
@ -444,7 +450,7 @@ describe("htop test suite", function()
local line = find_selected_y() local line = find_selected_y()
local before = string_at(22, line, 2) local before = string_at(22, line, 2)
send(curses.KEY_F7) send(curses.KEY_F7)
delay(0.3) delay(short_delay)
local after = string_at(22, line, 2) local after = string_at(22, line, 2)
assert.equal(before, after) -- no permissions assert.equal(before, after) -- no permissions
end) end)
@ -454,10 +460,10 @@ describe("htop test suite", function()
send("P") send("P")
send("I") send("I")
send(curses.KEY_HOME) send(curses.KEY_HOME)
delay(0.3) delay(short_delay)
local zerocpu = check_string_at(cpu_col, y_panelhdr + 1, " 0.0") local zerocpu = check_string_at(cpu_col, y_panelhdr + 1, " 0.0")
send("I") send("I")
delay(0.3) delay(short_delay)
local nonzerocpu = check_string_at(cpu_col, y_panelhdr + 1, " 0.0") local nonzerocpu = check_string_at(cpu_col, y_panelhdr + 1, " 0.0")
assert.equal(check(zerocpu)) assert.equal(check(zerocpu))
assert.not_equal(check(nonzerocpu)) assert.not_equal(check(nonzerocpu))
@ -483,7 +489,6 @@ describe("htop test suite", function()
end) end)
running_it("moves meters around", function() running_it("moves meters around", function()
if branch == "wip" then
send("S") send("S")
send(curses.KEY_RIGHT) send(curses.KEY_RIGHT)
send(curses.KEY_UP) send(curses.KEY_UP)
@ -496,7 +501,6 @@ describe("htop test suite", function()
send(curses.KEY_LEFT) send(curses.KEY_LEFT)
send("\n") send("\n")
send(curses.KEY_F10) send(curses.KEY_F10)
end
end) end)
local meters = { local meters = {
@ -504,7 +508,7 @@ describe("htop test suite", function()
{ name = "load", down = 2, string = "Load" }, { name = "load", down = 2, string = "Load" },
{ name = "battery", down = 7, string = "Battery" }, { name = "battery", down = 7, string = "Battery" },
{ name = "hostname", down = 8, string = "Hostname" }, { name = "hostname", down = 8, string = "Hostname" },
{ name = "memory", down = 3, string = "Memory" }, { name = "memory", down = 3, string = "Mem" },
{ name = "CPU average", down = 16, string = "Avg" }, { name = "CPU average", down = 16, string = "Avg" },
} }
@ -513,19 +517,11 @@ describe("htop test suite", function()
send(curses.KEY_RIGHT, 3) send(curses.KEY_RIGHT, 3)
send(curses.KEY_DOWN, 9, "quick") send(curses.KEY_DOWN, 9, "quick")
for _ = 9, 14 do for _ = 9, 14 do
if branch == "wip" then
send("\n") send("\n")
send("\n") send("\n")
send(curses.KEY_DC) send(curses.KEY_DC)
send(curses.KEY_RIGHT) send(curses.KEY_RIGHT)
send(curses.KEY_DOWN) send(curses.KEY_DOWN)
else
send(curses.KEY_F6)
send(curses.KEY_LEFT)
send(curses.KEY_DC)
send(curses.KEY_RIGHT)
send(curses.KEY_DOWN, 4)
end
end end
end) end)
@ -534,22 +530,15 @@ describe("htop test suite", function()
send("S") send("S")
send(curses.KEY_RIGHT, 3) send(curses.KEY_RIGHT, 3)
send(curses.KEY_DOWN, item.down) send(curses.KEY_DOWN, item.down)
if branch == "wip" then
send("\n") send("\n")
send(curses.KEY_UP, 4) send(curses.KEY_UP, 4)
send("\n") send("\n")
else
send(curses.KEY_F6)
send(curses.KEY_LEFT)
send(curses.KEY_DOWN, 4)
send(curses.KEY_F7, 4)
end
send(curses.KEY_F4, 4) -- cycle through meter modes send(curses.KEY_F4, 4) -- cycle through meter modes
delay(0.15) delay(short_delay)
rt:update() rt:update()
local with = check_string_at(x_metercol2, 2, item.string) local with = check_string_at(x_metercol2, 2, item.string)
send(curses.KEY_DC) send(curses.KEY_DC)
delay(0.15) delay(short_delay)
local without = check_string_at(x_metercol2, 2, item.string) local without = check_string_at(x_metercol2, 2, item.string)
send(curses.KEY_F10) send(curses.KEY_F10)
assert.equal(check(with)) assert.equal(check(with))
@ -590,7 +579,7 @@ describe("htop test suite", function()
running_it("checks display option to "..item.name, function() running_it("checks display option to "..item.name, function()
for _ = 1, 2 do for _ = 1, 2 do
set_display_option(item.down) set_display_option(item.down)
delay(0.1) delay(short_delay)
end end
end) end)
end end
@ -609,7 +598,7 @@ describe("htop test suite", function()
send(curses.KEY_RIGHT) send(curses.KEY_RIGHT)
send(curses.KEY_F4, 4) -- cycle through CPU meter modes send(curses.KEY_F4, 4) -- cycle through CPU meter modes
send(curses.KEY_F10) send(curses.KEY_F10)
delay(0.1) delay(short_delay)
end end
end) end)
@ -636,7 +625,7 @@ describe("htop test suite", function()
for y = y_panelhdr + 2, 23 do for y = y_panelhdr + 2, 23 do
table.insert(taggedattrs, rt:cellAttr(y-1, 4)) table.insert(taggedattrs, rt:cellAttr(y-1, 4))
end end
delay(0.2) delay(short_delay)
send("U") send("U")
local untaggedattrs = {} local untaggedattrs = {}
rt:update() rt:update()
@ -687,6 +676,7 @@ describe("htop test suite", function()
send("q") send("q")
while not terminated() do while not terminated() do
unistd.sleep(1) unistd.sleep(1)
send("q")
end end
assert(terminated()) assert(terminated())
if visual then if visual then

View File

@ -19,9 +19,16 @@ in the source distribution for its full text.
/*{ /*{
#include "Action.h" #include "Action.h"
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "SignalsPanel.h"
#include "UnsupportedProcess.h" #include "UnsupportedProcess.h"
}*/ }*/
SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
};
unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ProcessFieldData Process_fields[] = { ProcessFieldData Process_fields[] = {
@ -79,7 +86,12 @@ void Platform_setBindings(Htop_Action* keys) {
} }
int Platform_numberOfFields = 100; int Platform_numberOfFields = 100;
char* Process_pidFormat = "%7u ";
extern char Process_pidFormat[20];
ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
int Platform_getUptime() { int Platform_getUptime() {
return 0; return 0;
@ -95,42 +107,26 @@ int Platform_getMaxPid() {
return 1; return 1;
} }
void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid();
if (maxPid == -1) return;
if (maxPid > 99999) {
Process_fields[PID].title = " PID ";
Process_fields[PPID].title = " PPID ";
Process_fields[TPGID].title = " TPGID ";
Process_fields[TGID].title = " TGID ";
Process_fields[PGRP].title = " PGRP ";
Process_fields[SESSION].title = " SESN ";
Process_pidFormat = "%7u ";
} else {
Process_fields[PID].title = " PID ";
Process_fields[PPID].title = " PPID ";
Process_fields[TPGID].title = "TPGID ";
Process_fields[TGID].title = " TGID ";
Process_fields[PGRP].title = " PGRP ";
Process_fields[SESSION].title = " SESN ";
Process_pidFormat = "%5u ";
}
}
double Platform_setCPUValues(Meter* this, int cpu) { double Platform_setCPUValues(Meter* this, int cpu) {
(void) this;
(void) cpu;
return 0.0; return 0.0;
} }
void Platform_setMemoryValues(Meter* this) { void Platform_setMemoryValues(Meter* this) {
(void) this;
} }
void Platform_setSwapValues(Meter* this) { void Platform_setSwapValues(Meter* this) {
(void) this;
} }
bool Process_isThread(Process* this) { bool Process_isThread(Process* this) {
(void) this;
return false; return false;
} }
char* Platform_getProcessEnv(pid_t pid) { char* Platform_getProcessEnv(pid_t pid) {
(void) pid;
return NULL; return NULL;
} }

View File

@ -12,8 +12,13 @@ in the source distribution for its full text.
#include "Action.h" #include "Action.h"
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "SignalsPanel.h"
#include "UnsupportedProcess.h" #include "UnsupportedProcess.h"
extern SignalItem Platform_signals[];
extern unsigned int Platform_numberOfSignals;
extern ProcessField Platform_defaultFields[]; extern ProcessField Platform_defaultFields[];
extern ProcessFieldData Process_fields[]; extern ProcessFieldData Process_fields[];
@ -23,7 +28,10 @@ extern MeterClass* Platform_meterTypes[];
void Platform_setBindings(Htop_Action* keys); void Platform_setBindings(Htop_Action* keys);
extern int Platform_numberOfFields; extern int Platform_numberOfFields;
extern char* Process_pidFormat;
extern char Process_pidFormat[20];
extern ProcessPidColumn Process_pidColumns[];
int Platform_getUptime(); int Platform_getUptime();
@ -31,8 +39,6 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid(); int Platform_getMaxPid();
void Process_setupColumnWidths();
double Platform_setCPUValues(Meter* this, int cpu); double Platform_setCPUValues(Meter* this, int cpu);
void Platform_setMemoryValues(Meter* this); void Platform_setMemoryValues(Meter* this);