84 Commits

Author SHA1 Message Date
5c6d7cca3e Bump version to 3.0.0beta4 2018-04-06 12:43:24 -03:00
6fe06fb7e5 Portability: make list of default screens per-platform 2018-04-06 12:41:36 -03:00
657836a2ae Minor style fixes. 2018-04-06 12:40:51 -03:00
63e1417b8c Update some field accesses to new structures 2018-04-06 12:34:57 -03:00
9b4bdfcb3e Add -t command-line flag for tree view 2018-04-06 11:14:09 -03:00
7a71f9cf32 macOS: fix the switched version test (#772) 2018-04-06 11:14:09 -03:00
c91b0938d5 Solaris: update proc state letters to reflect Solaris usage 2018-04-06 11:14:09 -03:00
4f934c977d Solaris: bump copyright in Platform.{c,h} 2018-04-06 11:14:09 -03:00
d1696b5d5d Solaris: fix a memory leak caused by calling ProcessList_getProcess twice for each LWP 2018-04-06 11:14:09 -03:00
beb47cbb12 Solaris: Implement process environment listing 2018-04-06 11:14:09 -03:00
677cac9fab Solaris: add placeholder message about environment listing 2018-04-06 11:14:09 -03:00
6790e004cd Solaris: showing a dash for the top-level process is no longer necessary 2018-04-06 11:14:09 -03:00
8677162836 Solaris: add warning about proc_walk_f callback function 2018-04-06 11:14:09 -03:00
475798e8ab Solaris: condense separate process vs lwp handling down to a single workflow 2018-04-06 11:14:09 -03:00
7689c5c3b7 Solaris: get completely out of the file handling business using libproc 2018-04-06 11:14:09 -03:00
c2ab3ac422 Solaris: Condense conditional blocks for new vs old LWPs and procs 2018-04-06 11:14:09 -03:00
61bb649e0a Solaris: remove unneeded accumulators for process and thread counting 2018-04-06 11:14:09 -03:00
b8ec1c0337 Solaris: Assorted post-LWP code cleanup 2018-04-06 11:14:09 -03:00
499af738e7 Solaris: Implement kernel thread counting 2018-04-06 11:14:09 -03:00
416ef48a62 Solaris: If a process has a running LWP, then the process is by definition running 2018-04-06 11:14:09 -03:00
b9e0da9200 Collapse current subtree pressing Backspace 2018-04-06 11:14:09 -03:00
509303323f Solaris: Implement LWP enumeration (#768)
Squashed the following commits:

* Solaris: Get LWP enumeration working
* Solaris: Make showing and hiding of kernel threads behave
* Solaris: remove usage of lwpstatus that is no longer needed
* Solaris: no discrete access to parent proc structure needed
* Solaris: Restore runtime MaxPid detection after LWP changes
* Solaris: Workaround virtual PID signal issue by shadowing kill() with a macro
* Solaris: Fix unintention double-shifting of virtual PID for LWP enumeration
* Solaris: Add LWPID to default display since LWP enumeration is also default
* Solaris: use PAGE_SIZE_KB from Process.h instead of custom definition
* Solaris: stop LWP enumeration at 1023 LWPs per proc since that is all we can handle in the virtual PID
2018-04-06 11:14:09 -03:00
421a31d6f1 OpenBSD: read Battery data
Signed-off-by: Hisham Muhammad <hisham@gobolinux.org>
2018-04-06 11:14:09 -03:00
d4c4c02b97 macOS: keep scanning thread for versions before High Sierra (#728)
Keep scanning threads for versions before High Sierra 13.0.0 and after 13.3.0.
2018-04-06 11:14:09 -03:00
1ef20d1f14 Solaris: Fix virt and resident memory sizes. Was using KiB, needed pages. 2018-04-06 11:14:09 -03:00
1ab5afa1fe New makefile targets to rebuild and clean htop headers.
`make htop-headers` will regenerate all '.h' headers in htop source for
all platforms.
`make clean-htop-headers` will delete all generated htop headers.

Because of the introduction of these two targets, I slightly changed
the style of platform-specific portions of makefile rules.
Please comment if you accept such a style, or need me to revert to old
style.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-04-06 11:14:04 -03:00
00d1fb019a Linux: change how kernel threads are detected
Use the same method that ps and top use to determine if a
process is a kernel thread on Linux: check if cmdline is empty.

Thanks to @wangqr's investigation reported here:
https://github.com/hishamhm/htop/issues/761#issuecomment-375306069

Fixes #761.
2018-04-06 11:13:30 -03:00
dd175b6881 Fix overflow for signals >= 100.
Thanks to @gzip4 for tracking this down.

Closes #764.
2018-04-06 11:13:30 -03:00
7ad9701a12 strace: increase string length 2018-04-06 11:13:30 -03:00
881fe9631a Solaris: code indentation fix 2018-04-06 11:13:30 -03:00
0de77c7075 Solaris: enough changes made to justify a copyright bump to 2018 2018-04-06 11:13:30 -03:00
0f71db9d82 Solaris: fix memory allocation for usernames (some empty usernames in 32-bit builds) 2018-04-06 11:13:30 -03:00
da4010783e Solaris: fix malloc() / free() issues with zone name handling 2018-04-06 11:13:30 -03:00
3f7622a302 Solaris: Link against libmalloc to fix various crashes 2018-04-06 11:13:30 -03:00
df5d81a881 Solaris: Import backtrace-on-abort from Linux, with minor modification for Solaris 2018-04-06 11:13:30 -03:00
6c2b3b546d Use fork/exec instead of popen to run lsof (#757)
Fixes #675
2018-04-06 11:13:30 -03:00
f48f5a10ca Use AM_CFLAGS and AM_LDFLAGS in Makefile.am (#760)
This reduces generated Makefile.in size by 74%.
(217319 bytes -> 56326 bytes)

Automake considers that <prog>_CFLAGS and <prog>_LDFLAGS are
program-specific build rules, and when such are specified, Automake
will generate additional code just to avoid the "generic" and
package-wide AM_CFLAGS or AM_LDFLAGS. (Especially for <prog>_CFLAGS,
Automake will rename generated object files to become "prog-foo.o" and
such, and it's _a lot_ of code to achieve this in Makefile.)

There's no reason for htop to rename intermediate object files. It's
better to make things simpler.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-04-06 11:13:30 -03:00
1aa23925c9 Import Solaris support (#741)
This commit adds support for Solaris, squashed from PR #741:

Summary of additions:

* Initial setup of Solaris platform directory
* Add Solaris platform into autoconf template
* Uptime and load averages
* Add dependency on libkstat
* Basic process listing
* Zone name display
* CPU detection
* Per-process memory and CPU usage parsed correctly
* Uses sysconf to discover number of CPUs, instead of more complex libkstat code
* Simple memory display working
* Reduce repetitive calls to the PAGE_SIZE macro when reading memory info
* Add Project, Contract, Task, and Pool into process properties
* Use system major()/minor() implementations and remove extraneous definition of mkdev()
* Get the STARTTIME column working properly, using the Linux implementation as a guide
2018-04-06 11:13:30 -03:00
2e1f56d934 Changed type of some integer variables to avoid overflows 2018-03-16 12:01:24 -03:00
6ee99566cd Bump version to 3.0.0beta3 2018-02-26 20:13:09 -03:00
dc6bb069f0 Update generated header 2018-02-26 20:13:09 -03:00
0169577019 Fix inttypes.h header 2018-02-26 20:13:09 -03:00
0e38be9ee7 Darwin: expose LAST_PROCESSFIELD like the other platforms 2018-02-26 20:13:09 -03:00
8e6c1e1bac Add more default screens 2018-02-26 20:13:09 -03:00
709619800f Only compute counters is process is shown 2018-02-26 20:13:09 -03:00
a72439c9b7 Implemented various performance counters 2018-02-26 20:13:09 -03:00
61e94c1b5b Add IPC performance counter for Linux 2018-02-26 20:13:09 -03:00
b9f5892593 Add perf counter object 2018-02-26 20:13:09 -03:00
267d03b6d8 configure.ac: add --enable-perfcounters 2018-02-26 20:13:09 -03:00
3b819daf82 Set default sort keys in default screens 2018-02-26 20:13:09 -03:00
d9f8cdf0a6 Add make symbols target 2018-02-26 20:13:09 -03:00
59982a188c Store .sort_key as a string 2018-02-26 20:13:09 -03:00
0800424fe6 Match iotop's screen configuration 2018-02-26 20:13:09 -03:00
b4a8f048d1 Use screen's flags when reading process data 2018-02-26 20:13:09 -03:00
2df1f61d77 Screens: Fix "New Screen" option 2018-02-26 20:13:09 -03:00
e6c98b6e8e htoprc: store screen 0's setup for improved compatibility 2018-02-26 20:13:09 -03:00
b815e4c7a3 Add support for multiple screens, switchable using Tab 2018-02-26 20:13:09 -03:00
4791050cea Begin add supporting for multiple screens 2018-02-26 20:13:09 -03:00
1edcfad874 Move responsibility for cursor placement to Panels 2018-02-26 20:13:09 -03:00
d4ea7cd65c Fix bashisms (#749)
The configure script relied on bash-specific extensions to shell syntax
and behavior, causing build failures on systems with other /bin/sh
implementations. This commit replaces those with equivalent constructs
that should work in all POSIX shells.
2018-02-26 20:07:52 -03:00
9ca1c993ac Add Contributing Guide! 2018-02-26 11:45:53 -03:00
ccd156f8ba Updates to generated header files 2018-02-26 11:44:46 -03:00
858af2505f Protect against overflows in RichString_setAttrn 2018-02-26 11:05:12 -03:00
655c7293d2 Update ChangeLog 2018-02-26 10:54:01 -03:00
bc5d46982f use CFLAGS from ncurses*-config, if present (#745)
Fixes #695.
2018-02-26 10:19:01 -03:00
c01f40eb3e Fix build failure ('major' undefined) in glibc 2.28. (#746)
glibc 2.28 no longer defines 'major' and 'minor' in <sys/types.h> and
requires us to include <sys/sysmacros.h>. (glibc 2.25 starts
deprecating the macros in <sys/types.h>.) Now do include the latter if
found on the system.

At the moment, let's also utilize AC_HEADER_MAJOR in configure script.
However as Autoconf 2.69 has not yet updated the AC_HEADER_MAJOR macro
to reflect the glibc change [1], so add a workaround code.

Fixes #663. Supersedes pull request #729.

Reference:
[1] https://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commit;h=e17a30e987d7ee695fb4294a82d987ec3dc9b974

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-02-26 10:15:05 -03:00
eed18dd107 Remove unused function from unsupported/ 2018-02-18 21:18:53 -03:00
f914617508 Make settings file finding sequence more straightforward
Avoid unnecessary access() call and make code read more linearly.
2018-02-18 20:42:17 -03:00
03b2581745 Only consider a read successful when the file seems valid
Require at least the `fields` entry to be present,
so we can have a decent guess that it was indeed a settings file.
2018-02-18 20:35:23 -03:00
8c653212c0 Replace size_t with int/void* union
I was occasionally passing negative values to size_t.
Plus, this better reflects the intent of the variant argument.

Reported by Coverity:
https://scan8.coverity.com/reports.htm#v13253/p10402/fileInstanceId=22093891&defectInstanceId=7543346&mergedDefectId=174179&fileStart=251&fileEnd=500
2018-02-18 10:38:49 -03:00
b064d501ae linux/Battery.c: make sure fd is always closed
Detected by Coverity:
https://scan8.coverity.com/reports.htm#v13252/p10402/fileInstanceId=22093957&defectInstanceId=7543348&mergedDefectId=174180
2018-02-18 10:21:22 -03:00
ff78a1bfce Fix out-of-bounds read
Detected by Coverity:
https://scan8.coverity.com/reports.htm#v13252/p10402/fileInstanceId=22093847&defectInstanceId=7543344&mergedDefectId=174181
2018-02-18 10:17:56 -03:00
f4f35da7e0 Fix indentation 2018-02-18 10:17:29 -03:00
76366be3f1 Update ChangeLog 2018-02-17 21:11:54 -02:00
6dda8d2586 linux/LinuxProcessList.c: Fix indentation. 2018-02-17 20:52:46 -02:00
5fca258f33 call clear() function when SIGWINCH is received. (#660) 2018-02-17 16:25:57 -02:00
70ed51a303 linux/LinuxProcessList: fix reading of number of read syscalls of process
The "if" tests if the character at index "5" is 'r', as a first quick
check. However at index "5" will always be a colon ":". This patch fixes
the off-by-one error. htop now shows proper values in the RD_SYSC
column.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
2018-02-17 16:14:34 -02:00
df9922a67e Fix preservation of LDFLAGS value during configure script
Fixes #738.
2018-02-17 14:50:55 -02:00
fcdff59f4c Update ChangeLog 2018-02-17 14:50:42 -02:00
b544c22c72 Fix issue with small terminals.
Fixes #733.
2018-02-13 06:41:44 -02:00
f37a050d3d Optimize Vector_size on non-debug builds 2018-02-05 11:01:35 +01:00
03f17688ad Handle unexpected values for character passed to isalnum
It seems that certain negative integer values can crash isalnum().
Let's protect against those.

Fixes #711.
2018-02-05 10:59:20 +01:00
a32d7528f6 Check for pkgconfig's presence before using it.
Fixes #710.
2018-02-05 10:22:16 +01:00
ac2dff2881 Fix color behavior on some terminals.
Fixes #635.
2018-02-05 07:20:27 +01:00
79 changed files with 3302 additions and 506 deletions

View File

@ -115,7 +115,7 @@ static void Action_runSetup(Settings* settings, const Header* header, ProcessLis
static bool changePriority(MainPanel* panel, int delta) { static bool changePriority(MainPanel* panel, int delta) {
bool anyTagged; bool anyTagged;
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged); bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged);
if (!ok) if (!ok)
beep(); beep();
return anyTagged; return anyTagged;
@ -155,10 +155,26 @@ static bool expandCollapse(Panel* panel) {
return true; return true;
} }
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
if (q->pid == ppid) {
q->showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey; ScreenSettings* ss = settings->ss;
settings->direction = 1; ss->sortKey = sortKey;
settings->treeView = false; ss->direction = 1;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
} }
@ -166,11 +182,12 @@ static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel ")); Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by"); Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields; ScreenSettings* ss = st->settings->ss;
ProcessField* fields = ss->fields;
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name); char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey) if (fields[i] == ss->sortKey)
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
} }
@ -185,6 +202,7 @@ static Htop_Reaction sortBy(State* st) {
// ---------------------------------------- // ----------------------------------------
static Htop_Reaction actionResize(State* st) { static Htop_Reaction actionResize(State* st) {
clear();
Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1); Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1);
return HTOP_REDRAW_BAR; return HTOP_REDRAW_BAR;
} }
@ -218,8 +236,9 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
} }
static Htop_Reaction actionToggleTreeView(State* st) { static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView; ScreenSettings* ss = st->settings->ss;
if (st->settings->treeView) st->settings->direction = 1; ss->treeView = !ss->treeView;
if (ss->treeView) ss->direction = 1;
ProcessList_expandTree(st->pl); ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
} }
@ -247,7 +266,7 @@ static Htop_Reaction actionLowerPriority(State* st) {
} }
static Htop_Reaction actionInvertSortOrder(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings); ScreenSettings_invertSortOrder(st->settings->ss);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS; return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
} }
@ -260,14 +279,32 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
return changed ? HTOP_RECALCULATE : HTOP_OK; return changed ? HTOP_RECALCULATE : HTOP_OK;
} }
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->ss->treeView) {
return HTOP_OK;
}
bool changed = collapseIntoParent(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) { static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
} }
static Htop_Reaction actionQuit() { static Htop_Reaction actionQuit() {
return HTOP_QUIT; return HTOP_QUIT;
} }
static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
static Htop_Reaction actionSetAffinity(State* st) { static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1) if (st->pl->cpuCount == 1)
return HTOP_OK; return HTOP_OK;
@ -284,7 +321,7 @@ static Htop_Reaction actionSetAffinity(State* st) {
void* set = Action_pickFromVector(st, affinityPanel, 15); void* set = Action_pickFromVector(st, affinityPanel, 15);
if (set) { if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl); Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (size_t) affinity, NULL); bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity }, NULL);
if (!ok) beep(); if (!ok) beep();
Affinity_delete(affinity); Affinity_delete(affinity);
} }
@ -301,7 +338,7 @@ static Htop_Reaction actionKill(State* st) {
Panel_setHeader(st->panel, "Sending..."); Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true); Panel_draw(st->panel, true);
refresh(); refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL); MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL);
napms(500); napms(500);
} }
} }
@ -556,6 +593,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['+'] = actionExpandOrCollapse; keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse; keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse; keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser; keys['u'] = actionFilterByUser;
keys['F'] = Action_follow; keys['F'] = Action_follow;
keys['S'] = actionSetup; keys['S'] = actionSetup;
@ -571,5 +609,6 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll; keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren; keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen; keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
} }

56
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,56 @@
Contributing Guide
==================
Hello, and thank you so much for taking your time to contribute in any way to
htop! There are many ways to contribute, and I'll try to list them below. The
support from the free software community has been amazing over the years and
it is the number one thing that keeps me going, maintaining and improving
something that started as a tiny pet project back in 2004 and that nowadays is
a piece of software used all over the world, in both reality [and
fiction!](http://hisham.hm/htop/index.php?page=sightings). Cheers!
-- Hisham Muhammad
Bug Reports
-----------
Bug reports should be posted in the [Github issue
tracker](http://github.com/hishamhm/htop/issues). (I reply to them all, but I
usually do it in batches! :) ) Bug reports are extremely important since it's
impossible for me to test htop in every possible system, distribution and
scenario. Your feedback is what keeps the tool stable and always improving!
Thank you!
Pull Requests
-------------
Code contributions are most welcome! Just [fork the
repo](http://github.com/hishamhm/htop) and send a [pull
request](https://github.com/hishamhm/htop/pulls). Help is especially
appreciated for support of platforms other than Linux. If proposing new
features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately I
can't accept every new feature proposed, as I need to keep the tool slim and
maintainable. Great ideas backed by a PR are always carefully considered for
inclusion, though! Also, PRs containing bug fixes and portability tweaks are a
no-brainer, please send those in!
Feature Requests
----------------
Back when htop was hosted in SourceForge, there used to be separate Bug
Tracker and Feature Request pages. These go all lumped together under "Issues"
in Github, which is a bit confusing. For this reason, I close Feature Requests
and file them with the [`feature
request`](https://github.com/hishamhm/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label, where they remain accessible, but not mixed with actual bug reports.
This doesn't mean I'm dismissing or ignoring feature requests right away! It's
just an organizational issue (with Github, really!).
Donations
---------
If you like htop, feel free to [buy the author a
beer](http://hisham.hm/htop/index.php?page=donate). :-)

41
CRT.c
View File

@ -37,6 +37,7 @@ in the source distribution for its full text.
#define White COLOR_WHITE #define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta) #define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20) #define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21) #define KEY_WHEELDOWN KEY_F(21)
@ -127,6 +128,7 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
@ -231,6 +233,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Magenta,Black), [CPU_SOFTIRQ] = ColorPair(Magenta,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_MONOCHROME] = { [COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL, [RESET_COLOR] = A_NORMAL,
@ -290,6 +293,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = A_BOLD, [CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE, [CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE, [CPU_GUEST] = A_REVERSE,
[PANEL_EDIT] = A_BOLD,
}, },
[COLORSCHEME_BLACKONWHITE] = { [COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White), [RESET_COLOR] = ColorPair(Black,White),
@ -349,6 +353,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,White), [CPU_SOFTIRQ] = ColorPair(Blue,White),
[CPU_STEAL] = ColorPair(Cyan,White), [CPU_STEAL] = ColorPair(Cyan,White),
[CPU_GUEST] = ColorPair(Cyan,White), [CPU_GUEST] = ColorPair(Cyan,White),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_LIGHTTERMINAL] = { [COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black), [RESET_COLOR] = ColorPair(Black,Black),
@ -408,6 +413,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Black,Black), [CPU_STEAL] = ColorPair(Black,Black),
[CPU_GUEST] = ColorPair(Black,Black), [CPU_GUEST] = ColorPair(Black,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_MIDNIGHT] = { [COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue), [RESET_COLOR] = ColorPair(White,Blue),
@ -467,6 +473,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Black,Blue), [CPU_SOFTIRQ] = ColorPair(Black,Blue),
[CPU_STEAL] = ColorPair(White,Blue), [CPU_STEAL] = ColorPair(White,Blue),
[CPU_GUEST] = ColorPair(White,Blue), [CPU_GUEST] = ColorPair(White,Blue),
[PANEL_EDIT] = ColorPair(White,Blue),
}, },
[COLORSCHEME_BLACKNIGHT] = { [COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black), [RESET_COLOR] = ColorPair(Cyan,Black),
@ -526,12 +533,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black), [CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Cyan,Black), [CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black), [CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Cyan),
}, },
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
}; };
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5; int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10; int CRT_scrollWheelVAmount = 10;
@ -713,22 +719,23 @@ void CRT_enableDelay() {
void CRT_setColors(int colorScheme) { void CRT_setColors(int colorScheme) {
CRT_colorScheme = colorScheme; CRT_colorScheme = colorScheme;
if (colorScheme == COLORSCHEME_BLACKNIGHT) {
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
if (ColorIndex(i,j) != ColorIndex(Magenta,Magenta)) { if (ColorIndex(i,j) != ColorPairGrayBlack) {
init_pair(ColorIndex(i,j), i, j); int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
} ? (j==0 ? -1 : j)
: j;
init_pair(ColorIndex(i,j), i, bg);
} }
init_pair(ColorIndex(Magenta,Magenta), 8, 0); }
} else {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
if (ColorIndex(i,j) != ColorIndex(Magenta,Magenta)) {
init_pair(ColorIndex(i,j), i, (j==0?-1:j));
}
}
init_pair(ColorIndex(Magenta,Magenta), 8, -1);
} }
int grayBlackFg = COLORS > 8 ? 8 : 0;
int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? -1
: 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
CRT_colors = CRT_colorSchemes[colorScheme]; CRT_colors = CRT_colorSchemes[colorScheme];
} }

4
CRT.h
View File

@ -26,6 +26,7 @@ in the source distribution for its full text.
#define White COLOR_WHITE #define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta) #define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20) #define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21) #define KEY_WHEELDOWN KEY_F(21)
@ -115,6 +116,7 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ, CPU_SOFTIRQ,
CPU_STEAL, CPU_STEAL,
CPU_GUEST, CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT LAST_COLORELEMENT
} ColorElements; } ColorElements;
@ -143,8 +145,6 @@ int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT]; extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount; extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount; extern int CRT_scrollWheelVAmount;

View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include "AvailableMetersPanel.h" #include "AvailableMetersPanel.h"
#include "MetersPanel.h" #include "MetersPanel.h"
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
#include "ColumnsPanel.h" #include "ScreensPanel.h"
#include "ColorsPanel.h" #include "ColorsPanel.h"
#include "AvailableColumnsPanel.h" #include "AvailableColumnsPanel.h"
@ -64,9 +64,11 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1); ScreenManager_add(this->scr, colors, -1);
} }
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) { static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings); Panel* screens = (Panel*) ScreensPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns); Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20); ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1); ScreenManager_add(this->scr, availableColumns, -1);
} }
@ -118,7 +120,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel_makeColorsPage(this); CategoriesPanel_makeColorsPage(this);
break; break;
case 3: case 3:
CategoriesPanel_makeColumnsPage(this); CategoriesPanel_makeScreensPage(this);
break; break;
} }
} }
@ -147,6 +149,6 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
Panel_add(super, (Object*) ListItem_new("Meters", 0)); Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0)); Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0)); Panel_add(super, (Object*) ListItem_new("Screens", 0));
return this; return this;
} }

View File

@ -1,3 +1,21 @@
What's new in version 2.1.1
* Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report)
* Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein)
* Fix build failure in Glibc 2.28
(thanks to Kang-Che Sung)
* BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report)
What's new in version 2.1.0 What's new in version 2.1.0

View File

@ -78,6 +78,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
this->settings->changed = true; this->settings->changed = true;
const Header* header = this->scr->header; const Header* header = this->scr->header;
CRT_setColors(mark); CRT_setColors(mark);
clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0); Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header); Header_draw(header);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]); RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);

View File

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

View File

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

View File

@ -83,7 +83,6 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Display options"); Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));

View File

@ -96,11 +96,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
} }
} }
void FunctionBar_draw(const FunctionBar* this, char* buffer) { int FunctionBar_draw(const FunctionBar* this, char* buffer) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]); return FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
} }
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) { int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int cursorX = 0;
attrset(CRT_colors[FUNCTION_BAR]); attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS); mvhline(LINES-1, 0, ' ', COLS);
int x = 0; int x = 0;
@ -115,12 +116,10 @@ void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
if (buffer) { if (buffer) {
attrset(attr); attrset(attr);
mvaddstr(LINES-1, x, buffer); mvaddstr(LINES-1, x, buffer);
CRT_cursorX = x + strlen(buffer); cursorX = x + strlen(buffer);
curs_set(1);
} else {
curs_set(0);
} }
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
return cursorX;
} }
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) { int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -30,9 +30,9 @@ void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_draw(const FunctionBar* this, char* buffer); int FunctionBar_draw(const FunctionBar* this, char* buffer);
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr); int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos); int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

View File

@ -62,7 +62,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) { void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col]; MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
for (int i = 0; i < colSettings->len; i++) { for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col); Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) { if (colSettings->modes[i] != 0) {
@ -75,7 +75,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) { void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) { Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col]; MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
String_freeArray(colSettings->names); String_freeArray(colSettings->names);
free(colSettings->modes); free(colSettings->modes);

View File

@ -38,6 +38,7 @@ typedef struct IncMode_ {
typedef struct IncSet_ { typedef struct IncSet_ {
IncMode modes[2]; IncMode modes[2];
IncMode* active; IncMode* active;
Panel* panel;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found; bool found;
@ -115,23 +116,35 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
} }
} }
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { static bool search(IncSet* this, 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++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) {
Panel_setSelected(panel, i); Panel_setSelected(panel, i);
found = true; found = true;
break; break;
} }
} }
if (found) IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]);
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
return found; return found;
} }
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
panel->cursorOn = true;
this->panel = panel;
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
}
static void IncSet_deactivate(IncSet* this, Panel* panel) {
this->active = NULL;
Panel_setDefaultBar(panel);
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar, NULL);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR) if (ch == ERR)
return true; return true;
@ -189,13 +202,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} else { } else {
IncMode_reset(mode); IncMode_reset(mode);
} }
this->active = NULL; IncSet_deactivate(this, panel);
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
this->found = search(mode, panel, getPanelValue); this->found = search(this, panel, getPanelValue);
} }
if (filterChanged && lines) { if (filterChanged && lines) {
updateWeakPanel(this, panel, lines); updateWeakPanel(this, panel, lines);
@ -210,15 +221,11 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return ""; return "";
} }
void IncSet_activate(IncSet* this, IncType type, Panel* panel) { void IncSet_drawBar(IncSet* this, int attr) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
if (this->active) { if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer); int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else { } else {
FunctionBar_draw(this->defaultBar, NULL); FunctionBar_draw(this->defaultBar, NULL);
} }

View File

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

View File

@ -85,7 +85,7 @@ void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true; this->display->needsRedraw = true;
Panel_draw(this->display, true); Panel_draw(this->display, true);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
va_end(ap); va_end(ap);
} }
@ -115,10 +115,7 @@ void InfoScreen_run(InfoScreen* this) {
Panel_draw(panel, true); Panel_draw(panel, true);
if (this->inc->active) int ch = Panel_getCh(panel);
move(LINES-1, CRT_cursorX);
set_escdelay(25);
int ch = getch();
if (ch == ERR) { if (ch == ERR) {
if (As_InfoScreen(this)->onErr) { if (As_InfoScreen(this)->onErr) {

View File

@ -27,13 +27,13 @@ typedef struct ListItem_ {
}*/ }*/
static void ListItem_delete(Object* cast) { void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast; ListItem* this = (ListItem*)cast;
free(this->value); free(this->value);
free(this); free(this);
} }
static void ListItem_display(Object* cast, RichString* out) { void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast; ListItem* const this = (ListItem*)cast;
assert (this != NULL); assert (this != NULL);
/* /*
@ -59,11 +59,15 @@ ObjectClass ListItem_class = {
.compare = ListItem_compare .compare = ListItem_compare
}; };
ListItem* ListItem_new(const char* value, int key) { void ListItem_init(ListItem* this, const char* value, int key) {
ListItem* this = AllocThis(ListItem);
this->value = xStrdup(value); this->value = xStrdup(value);
this->key = key; this->key = key;
this->moving = false; this->moving = false;
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
return this; return this;
} }

View File

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

View File

@ -25,7 +25,12 @@ typedef struct MainPanel_ {
pid_t pidSearch; pid_t pidSearch;
} MainPanel; } MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t); typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
@ -67,15 +72,17 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) { if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch); int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl; ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1; int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx); ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) { if (field == ss->sortKey) {
Settings_invertSortOrder(settings); ScreenSettings_invertSortOrder(ss);
settings->treeView = false; ss->treeView = false;
} else { } else {
reaction |= Action_setSortKey(settings, field); reaction |= Action_setSortKey(settings, field);
} }
@ -108,8 +115,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
if (reaction & HTOP_REDRAW_BAR) { if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView); MainPanel_updateTreeFunctions(this, settings->ss->treeView);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
} }
if (reaction & HTOP_UPDATE_PANELHDR) { if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super)); ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
@ -148,7 +155,7 @@ const char* MainPanel_getValue(MainPanel* this, int i) {
return ""; return "";
} }
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, size_t arg, bool* wasAnyTagged) { bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg 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

@ -21,7 +21,12 @@ typedef struct MainPanel_ {
pid_t pidSearch; pid_t pidSearch;
} MainPanel; } MainPanel;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, size_t); typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
@ -34,7 +39,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, size_t arg, bool* wasAnyTagged); bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class; extern PanelClass MainPanel_class;

View File

@ -12,8 +12,8 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
htop_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
htop_LDFLAGS = AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \ myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
@ -21,7 +21,7 @@ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \ SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c ScreensPanel.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \ TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \ HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c InfoScreen.c XAlloc.c
@ -31,69 +31,169 @@ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \ CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \ Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \ BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \ ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h ScreensPanel.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \ TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \ AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h EnvScreen.h InfoScreen.h XAlloc.h
if HTOP_LINUX all_platform_headers =
htop_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \ # Linux
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h # -----
linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h \
linux/PerfCounter.h
all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX
AM_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PerfCounter.c
myhtopplatheaders = $(linux_platform_headers)
endif endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \ myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c
myhtopplatheaders = freebsd/Platform.h freebsd/FreeBSDProcessList.h \ myhtopplatheaders = $(freebsd_platform_headers)
freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h
endif endif
# DragonFlyBSD
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
all_platform_headers += $(dragonflybsd_platform_headers)
if HTOP_DRAGONFLYBSD if HTOP_DRAGONFLYBSD
htop_LDFLAGS += -lkvm -lkinfo -lexecinfo AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \ myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = dragonflybsd/Platform.h dragonflybsd/DragonFlyBSDProcessList.h \ myhtopplatheaders = $(dragonflybsd_platform_headers)
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDCRT.h dragonflybsd/Battery.h
endif endif
# OpenBSD
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
all_platform_headers += $(openbsd_platform_headers)
if HTOP_OPENBSD if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
myhtopplatheaders = openbsd/Platform.h openbsd/OpenBSDProcessList.h \ myhtopplatheaders = $(openbsd_platform_headers)
openbsd/OpenBSDProcess.h openbsd/OpenBSDCRT.h openbsd/Battery.h
endif endif
# Darwin
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN if HTOP_DARWIN
htop_LDFLAGS += -framework IOKit -framework CoreFoundation AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \ myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c
myhtopplatheaders = darwin/Platform.h darwin/DarwinProcess.h \ myhtopplatheaders = $(darwin_platform_headers)
darwin/DarwinProcessList.h darwin/DarwinCRT.h darwin/Battery.h
endif endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h
all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c
myhtopplatheaders = $(solaris_platform_headers)
endif
# Unsupported
# -----------
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
all_platform_headers += $(unsupported_platform_headers)
if HTOP_UNSUPPORTED if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \ myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \ unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = unsupported/Platform.h \ myhtopplatheaders = $(unsupported_platform_headers)
unsupported/UnsupportedProcess.h unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h unsupported/Battery.h
endif endif
# ----
SUFFIXES = .h SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders) BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h
.PHONY: htop-headers clean-htop-headers
htop-headers: $(myhtopheaders) $(all_platform_headers)
clean-htop-headers:
-rm -f $(myhtopheaders) $(all_platform_headers)
target: target:
echo $(htop_SOURCES) echo $(htop_SOURCES)
@ -103,6 +203,9 @@ profile:
debug: debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
coverage: coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"

View File

@ -287,7 +287,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
int blockSizes[10]; int blockSizes[10];
xSnprintf(bar, w + 1, "%*s", w, buffer); xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
// First draw in the bar[] buffer... // First draw in the bar[] buffer...
int offset = 0; int offset = 0;
@ -359,7 +359,7 @@ static int GraphMeterMode_pixPerRow;
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
if (!this->drawData) this->drawData = xCalloc(1, sizeof(GraphData)); if (!this->drawData) this->drawData = xCalloc(1, sizeof(GraphData));
GraphData* data = (GraphData*) this->drawData; GraphData* data = (GraphData*) this->drawData;
const int nValues = METER_BUFFER_LEN; const int nValues = METER_BUFFER_LEN;
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
@ -404,7 +404,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
k = -i/2; k = -i/2;
i = 0; i = 0;
} }
for (; i < nValues; i+=2, k++) { for (; i < nValues - 1; i+=2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT; int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix); int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix); int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);

View File

@ -76,16 +76,35 @@ void OpenFilesScreen_draw(InfoScreen* this) {
} }
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char command[1025]; char buffer[1025];
xSnprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid); xSnprintf(buffer, 1024, "%d", pid);
FILE* fd = popen(command, "r");
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData)); OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL; OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data); OpenFiles_Data* item = &(pdata->data);
if (!fd) { int fdpair[2];
pdata->error = 127; if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata; return pdata;
} }
pid_t child = fork();
if (child == -1) {
pdata->error = 1;
return pdata;
}
if (child == 0) {
close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0)
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
FILE* fd = fdopen(fdpair[0], "r");
for (;;) { for (;;) {
char* line = String_readLine(fd); char* line = String_readLine(fd);
if (!line) { if (!line) {
@ -105,7 +124,15 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
item->data[cmd] = xStrdup(line + 1); item->data[cmd] = xStrdup(line + 1);
free(line); free(line);
} }
pdata->error = pclose(fd); int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus))
pdata->error = 1;
else
pdata->error = WEXITSTATUS(wstatus);
return pdata; return pdata;
} }
@ -130,7 +157,7 @@ void OpenFilesScreen_scan(InfoScreen* this) {
char** data = fdata->data.data; char** data = fdata->data.data;
int lenN = data['n'] ? strlen(data['n']) : 0; int lenN = data['n'] ? strlen(data['n']) : 0;
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/; int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
char* entry = xMalloc(sizeEntry); char entry[sizeEntry];
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s", xSnprintf(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'] : "",

24
Panel.c
View File

@ -57,6 +57,7 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
int cursorX, cursorY;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
@ -66,6 +67,7 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
@ -85,6 +87,11 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1) #define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
PanelClass Panel_class = { PanelClass Panel_class = {
.super = { .super = {
.extends = Class(Object), .extends = Class(Object),
@ -112,6 +119,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->y = y; this->y = y;
this->w = w; this->w = w;
this->h = h; this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL; this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE); this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0; this->scrollV = 0;
@ -367,7 +376,6 @@ void Panel_draw(Panel* this, bool focus) {
RichString_end(old); RichString_end(old);
} }
this->oldSelected = this->selected; this->oldSelected = this->selected;
move(0, 0);
} }
bool Panel_onKey(Panel* this, int key) { bool Panel_onKey(Panel* this, int key) {
@ -469,7 +477,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
this->eventHandlerState = xCalloc(100, sizeof(char)); this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState; char* buffer = this->eventHandlerState;
if (ch < 255 && isalnum(ch)) { if (ch > 0 && ch < 255 && isalnum(ch)) {
int len = strlen(buffer); int len = strlen(buffer);
if (len < 99) { if (len < 99) {
buffer[len] = ch; buffer[len] = ch;
@ -499,3 +507,15 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
} }
return IGNORED; return IGNORED;
} }
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
set_escdelay(25);
return getch();
}

View File

@ -46,6 +46,7 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
int x, y, w, h; int x, y, w, h;
int cursorX, cursorY;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
@ -55,6 +56,7 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
@ -73,6 +75,8 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1) #define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern PanelClass Panel_class; extern PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar); Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
@ -123,4 +127,7 @@ bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch); HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
#endif #endif

View File

@ -28,6 +28,12 @@ in the source distribution for its full text.
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#include <sys/sysmacros.h>
#endif
#ifdef __ANDROID__ #ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get #define SYS_ioprio_get __NR_ioprio_get
@ -45,6 +51,7 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include <sys/types.h> #include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
@ -150,7 +157,7 @@ typedef struct ProcessFieldData_ {
const char* name; const char* name;
const char* title; const char* title;
const char* description; const char* description;
int flags; uint64_t flags;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
@ -172,6 +179,8 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) #define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_)) #define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state)) #define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
@ -362,6 +371,21 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
} }
} }
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
if (val >= 0) {
if (val < 100) {
xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 1000) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)val);
} else {
xSnprintf(buffer, n, "%4d ", (unsigned int)val);
}
} else {
*attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) { void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
@ -370,30 +394,15 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes; bool coloring = this->settings->highlightMegabytes;
switch (field) { switch (field) {
case PERCENT_CPU: { case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
if (this->percent_cpu > 999.9) { case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
xSnprintf(buffer, n, "%4d ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)this->percent_cpu);
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
xSnprintf(buffer, n, "100. ");
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case COMM: { case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) { if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD]; attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
} }
if (!this->settings->treeView || this->indent == 0) { ScreenSettings* ss = this->settings->ss;
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str); Process_writeCommand(this, attr, baseattr, str);
return; return;
} else { } else {
@ -414,7 +423,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
buf += written; buf += written;
n -= written; n -= written;
} }
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE]; const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer); RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str); Process_writeCommand(this, attr, baseattr, str);
@ -485,7 +494,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
void Process_display(Object* cast, RichString* out) { void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast; Process* this = (Process*) cast;
ProcessField* fields = this->settings->fields; ProcessField* fields = this->settings->ss->fields;
RichString_prune(out); RichString_prune(out);
for (int i = 0; fields[i]; i++) for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]); As_Process(this)->writeField(this, out, fields[i]);
@ -536,11 +545,11 @@ bool Process_setPriority(Process* this, int priority) {
return (err == 0); return (err == 0);
} }
bool Process_changePriorityBy(Process* this, size_t delta) { bool Process_changePriorityBy(Process* this, int delta) {
return Process_setPriority(this, this->nice + delta); return Process_setPriority(this, this->nice + delta);
} }
void Process_sendSignal(Process* this, size_t sgn) { void Process_sendSignal(Process* this, int sgn) {
CRT_dropPrivileges(); CRT_dropPrivileges();
kill(this->pid, (int) sgn); kill(this->pid, (int) sgn);
CRT_restorePrivileges(); CRT_restorePrivileges();
@ -555,14 +564,15 @@ long Process_pidCompare(const void* v1, const void* v2) {
long Process_compare(const void* v1, const void* v2) { long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2; Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings; Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) { ScreenSettings* ss = settings->ss;
if (ss->direction == 1) {
p1 = (Process*)v1; p1 = (Process*)v1;
p2 = (Process*)v2; p2 = (Process*)v2;
} else { } else {
p2 = (Process*)v1; p2 = (Process*)v1;
p1 = (Process*)v2; p1 = (Process*)v2;
} }
switch (settings->sortKey) { switch (ss->sortKey) {
case PERCENT_CPU: case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1); return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM: case PERCENT_MEM:

View File

@ -9,6 +9,11 @@ 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.
*/ */
#ifdef MAJOR_IN_MKDEV
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#endif
#ifdef __ANDROID__ #ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get #define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set #define SYS_ioprio_set __NR_ioprio_set
@ -24,6 +29,7 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include <sys/types.h> #include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
@ -129,7 +135,7 @@ typedef struct ProcessFieldData_ {
const char* name; const char* name;
const char* title; const char* title;
const char* description; const char* description;
int flags; uint64_t flags;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
@ -151,6 +157,8 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass)) #define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_)) #define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state)) #define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
@ -176,6 +184,8 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring); void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
void Process_writeField(Process* this, RichString* str, ProcessField field); void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out); void Process_display(Object* cast, RichString* out);
@ -190,9 +200,9 @@ void Process_toggleTag(Process* this);
bool Process_setPriority(Process* this, int priority); bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, size_t delta); bool Process_changePriorityBy(Process* this, int delta);
void Process_sendSignal(Process* this, size_t sgn); void Process_sendSignal(Process* this, int sgn);
long Process_pidCompare(const void* v1, const void* v2); long Process_pidCompare(const void* v1, const void* v2);

View File

@ -124,14 +124,15 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) { void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header); RichString_prune(header);
ProcessField* fields = this->settings->fields; ProcessField* fields = this->settings->ss->fields;
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title; unsigned int key = fields[i];
const char* field = Process_fields[key].title;
if (!field) field = "- "; if (!field) field = "- ";
if (!this->settings->treeView && this->settings->sortKey == fields[i]) int color = (!this->settings->ss->treeView && this->settings->ss->sortKey == key)
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field); ? CRT_colors[PANEL_SELECTION_FOCUS]
else : CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field); RichString_append(header, color, field);
} }
} }
@ -200,19 +201,19 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
} }
void ProcessList_sort(ProcessList* this) { void ProcessList_sort(ProcessList* this) {
if (!this->settings->treeView) { if (!this->settings->ss->treeView) {
Vector_insertionSort(this->processes); Vector_insertionSort(this->processes);
} else { } else {
// Save settings // Save settings
int direction = this->settings->direction; int direction = this->settings->ss->direction;
int sortKey = this->settings->sortKey; int sortKey = this->settings->ss->sortKey;
// Sort by PID // Sort by PID
this->settings->sortKey = PID; this->settings->ss->sortKey = PID;
this->settings->direction = 1; this->settings->ss->direction = 1;
Vector_quickSort(this->processes); Vector_quickSort(this->processes);
// Restore settings // Restore settings
this->settings->sortKey = sortKey; this->settings->ss->sortKey = sortKey;
this->settings->direction = direction; this->settings->ss->direction = direction;
int vsize = Vector_size(this->processes); int vsize = Vector_size(this->processes);
// Find all processes whose parent is not visible // Find all processes whose parent is not visible
int size; int size;
@ -228,7 +229,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessList_buildTree(this, process->pid, 0, 0, direction, false); ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
break; break;
} }
pid_t ppid = process->tgid == process->pid ? process->ppid : process->tgid; pid_t ppid = Process_getParentPid(process);
// Bisect the process vector to find parent // Bisect the process vector to find parent
int l = 0, r = size; int l = 0, r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0) // If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
@ -271,7 +272,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessField ProcessList_keyAt(ProcessList* this, int at) { ProcessField ProcessList_keyAt(ProcessList* this, int at) {
int x = 0; int x = 0;
ProcessField* fields = this->settings->fields; ProcessField* fields = this->settings->ss->fields;
ProcessField field; ProcessField field;
for (int i = 0; (field = fields[i]); i++) { for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title; const char* title = Process_fields[field].title;

View File

@ -63,6 +63,10 @@ typedef struct RichString_ {
}*/ }*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
static void RichString_extendLen(RichString* this, int len) { static void RichString_extendLen(RichString* this, int len) {
@ -103,6 +107,7 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
cchar_t* ch = this->chptr + start; cchar_t* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) { for (int i = start; i <= finish; i++) {
ch->attr = attrs; ch->attr = attrs;
ch++; ch++;
@ -132,6 +137,7 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
chtype* ch = this->chptr + start; chtype* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) { for (int i = start; i <= finish; i++) {
*ch = (*ch & 0xff) | attrs; *ch = (*ch & 0xff) | attrs;
ch++; ch++;

View File

@ -59,6 +59,10 @@ typedef struct RichString_ {
} RichString; } RichString;
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0) #define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)

View File

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

View File

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

330
ScreensPanel.c Normal file
View File

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

51
ScreensPanel.h Normal file
View File

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

View File

@ -23,29 +23,44 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include <stdbool.h> #include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct { typedef struct {
int len; int len;
char** names; char** names;
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings columns[2]; MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount; int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
bool shadowOtherUsers; bool shadowOtherUsers;
@ -68,25 +83,82 @@ typedef struct Settings_ {
}*/ }*/
static void writeList(FILE* fd, char** list, int len) {
const char* sep = "";
for (int i = 0; i < len; i++) {
fprintf(fd, "%s%s", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
/*
static char** readQuotedList(char* line) {
int n = 0;
char** list = xCalloc(sizeof(char*), 1);
int start = 0;
for (;;) {
while (line[start] && line[start] == ' ') {
start++;
}
if (line[start] != '"') {
break;
}
start++;
int close = start;
while (line[close] && line[close] != '"') {
close++;
}
int len = close - start;
char* item = xMalloc(len + 1);
strncpy(item, line + start, len);
item[len] = '\0';
list[n] = item;
n++;
list = xRealloc(list, sizeof(char*) * (n + 1));
start = close + 1;
}
list[n] = NULL;
return list;
}
static void writeQuotedList(FILE* fd, char** list) {
const char* sep = "";
for (int i = 0; list[i]; i++) {
fprintf(fd, "%s\"%s\"", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
*/
void Settings_delete(Settings* this) { void Settings_delete(Settings* this) {
free(this->filename); free(this->filename);
free(this->fields); for (unsigned int i = 0; i < (sizeof(this->meterColumns)/sizeof(MeterColumnSettings)); i++) {
for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) { String_freeArray(this->meterColumns[i].names);
String_freeArray(this->columns[i].names); free(this->meterColumns[i].modes);
free(this->columns[i].modes); }
if (this->screens) {
for (unsigned int i = 0; this->screens[i]; i++) {
free(this->screens[i]->name);
free(this->screens[i]->fields);
}
free(this->screens);
} }
free(this); free(this);
} }
static void Settings_readMeters(Settings* this, char* line, int column) { static void Settings_readMeters(Settings* this, char* line, int side) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
this->columns[column].names = ids; this->meterColumns[side].names = ids;
} }
static void Settings_readMeterModes(Settings* this, char* line, int column) { static void Settings_readMeterModes(Settings* this, char* line, int side) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
@ -95,13 +167,13 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
for (int i = 0; ids[i]; i++) { for (int i = 0; ids[i]; i++) {
len++; len++;
} }
this->columns[column].len = len; this->meterColumns[side].len = len;
int* modes = xCalloc(len, sizeof(int)); int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]); modes[i] = atoi(ids[i]);
} }
String_freeArray(ids); String_freeArray(ids);
this->columns[column].modes = modes; this->meterColumns[side].modes = modes;
} }
static void Settings_defaultMeters(Settings* this) { static void Settings_defaultMeters(Settings* this) {
@ -110,37 +182,59 @@ static void Settings_defaultMeters(Settings* this) {
sizes[1]++; sizes[1]++;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); this->meterColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int)); this->meterColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i]; this->meterColumns[i].len = sizes[i];
} }
int r = 0; int r = 0;
if (this->cpuCount > 8) { if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2"); this->meterColumns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE; this->meterColumns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2"); this->meterColumns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE; this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) { } else if (this->cpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs"); this->meterColumns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE; this->meterColumns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs"); this->meterColumns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE; this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else { } else {
this->columns[0].names[0] = xStrdup("AllCPUs"); this->meterColumns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE; this->meterColumns[0].modes[0] = BAR_METERMODE;
} }
this->columns[0].names[1] = xStrdup("Memory"); this->meterColumns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE; this->meterColumns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap"); this->meterColumns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE; this->meterColumns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks"); this->meterColumns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE; this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage"); this->meterColumns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE; this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime"); this->meterColumns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE; this->meterColumns[1].modes[r++] = TEXT_METERMODE;
}
static const char* toFieldName(int i) {
return Process_fields[i].name;
}
static int toFieldIndex(const char* str) {
if (isdigit(str[0])) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(str) + 1;
if (id < Platform_numberOfFields && toFieldName(id)) {
return id;
}
} else {
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
const char* pName = toFieldName(p);
if (pName && strcmp(pName, str) == 0) {
return p;
}
}
}
return -1;
} }
static void readFields(ProcessField* fields, int* flags, const char* line) { static void readFields(ProcessField* fields, int* flags, const char* line) {
@ -151,11 +245,10 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
int i, j; int i, j;
*flags = 0; *flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) { for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format. int idx = toFieldIndex(ids[i]);
int id = atoi(ids[i]) + 1; if (idx != -1) {
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) { fields[j] = idx;
fields[j] = id; *flags |= Process_fields[idx].flags;
*flags |= Process_fields[id].flags;
j++; j++;
} }
} }
@ -163,6 +256,29 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
String_freeArray(ids); String_freeArray(ids);
} }
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line) {
ScreenSettings* ss = xCalloc(sizeof(ScreenSettings), 1);
ss->name = xStrdup(name);
ss->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
ss->flags = 0;
ss->direction = 1;
ss->treeView = 0;
readFields(ss->fields, &(ss->flags), line);
this->screens[this->nScreens] = ss;
this->nScreens++;
this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
this->screens[this->nScreens] = NULL;
return ss;
}
static void Settings_defaultScreens(Settings* this) {
for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) {
ScreenDefaults* defaults = &Platform_defaultScreens[i];
Settings_newScreen(this, defaults->name, defaults->columns);
this->screens[0]->sortKey = toFieldIndex(defaults->sortKey);
}
}
static bool Settings_read(Settings* this, const char* fileName) { static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd; FILE* fd;
@ -172,7 +288,11 @@ static bool Settings_read(Settings* this, const char* fileName) {
if (!fd) if (!fd)
return false; return false;
bool readMeters = false; bool didReadMeters = false;
bool didReadFields = false;
ProcessField* legacyFields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
int legacyFlags;
bool legacyFieldsRead = false;
for (;;) { for (;;) {
char* line = String_readLine(fd); char* line = String_readLine(fd);
if (!line) { if (!line) {
@ -186,14 +306,8 @@ static bool Settings_read(Settings* this, const char* fileName) {
continue; continue;
} }
if (String_eq(option[0], "fields")) { if (String_eq(option[0], "fields")) {
readFields(this->fields, &(this->flags), option[1]); readFields(legacyFields, &legacyFlags, option[1]);
} else if (String_eq(option[0], "sort_key")) { legacyFieldsRead = true;
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "hide_threads")) { } else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]); this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) { } else if (String_eq(option[0], "hide_kernel_threads")) {
@ -232,50 +346,70 @@ static bool Settings_read(Settings* this, const char* fileName) {
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0; if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
} else if (String_eq(option[0], "left_meters")) { } else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0); Settings_readMeters(this, option[1], 0);
readMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "right_meters")) { } else if (String_eq(option[0], "right_meters")) {
Settings_readMeters(this, option[1], 1); Settings_readMeters(this, option[1], 1);
readMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "left_meter_modes")) { } else if (String_eq(option[0], "left_meter_modes")) {
Settings_readMeterModes(this, option[1], 0); Settings_readMeterModes(this, option[1], 0);
readMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1); Settings_readMeterModes(this, option[1], 1);
readMeters = true; didReadMeters = true;
} else if (strncmp(option[0], "screen:", 7) == 0) {
Settings_newScreen(this, option[0] + 7, option[1]);
} else if (String_eq(option[0], ".tree_view")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->treeView = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_direction")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->direction = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_key")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->sortKey = toFieldIndex(option[1]);
}
} }
String_freeArray(option); String_freeArray(option);
} }
fclose(fd); fclose(fd);
if (!readMeters) { if (this->nScreens == 0) {
Settings_defaultScreens(this);
if (legacyFieldsRead) {
free(this->screens[0]->fields);
this->screens[0]->fields = legacyFields;
this->screens[0]->flags = legacyFlags;
}
}
if (!didReadMeters) {
Settings_defaultMeters(this); Settings_defaultMeters(this);
} }
return true; return didReadFields;
} }
static void writeFields(FILE* fd, ProcessField* fields, const char* name) { static void writeFields(FILE* fd, ProcessField* fields, bool byName) {
fprintf(fd, "%s=", name);
const char* sep = ""; const char* sep = "";
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format. if (byName) {
fprintf(fd, "%s%d", sep, (int) fields[i]-1); fprintf(fd, "%s%s", sep, toFieldName(fields[i]));
} else {
// This " - 1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
}
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
} }
static void writeMeters(Settings* this, FILE* fd, int column) { static void writeMeters(Settings* this, FILE* fd, int side) {
const char* sep = ""; writeList(fd, this->meterColumns[side].names, this->meterColumns[side].len);
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]);
sep = " ";
}
fprintf(fd, "\n");
} }
static void writeMeterModes(Settings* this, FILE* fd, int column) { static void writeMeterModes(Settings* this, FILE* fd, int side) {
const char* sep = ""; const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) { for (int i = 0; i < this->meterColumns[side].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]); fprintf(fd, "%s%d", sep, this->meterColumns[side].modes[i]);
sep = " "; sep = " ";
} }
fprintf(fd, "\n"); fprintf(fd, "\n");
@ -293,10 +427,7 @@ bool Settings_write(Settings* this) {
} }
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n"); fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n"); fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields"); fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, false);
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads); fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
@ -306,7 +437,6 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
@ -318,6 +448,23 @@ bool Settings_write(Settings* this) {
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
// Legacy compatibility with older versions of htop
fprintf(fd, "tree_view=%d\n", (int) this->screens[0]->treeView);
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->screens[0]->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->screens[0]->direction);
if (this->screens && this->screens[0]) {
for (unsigned int i = 0; i < this->nScreens; i++) {
ScreenSettings* ss = this->screens[i];
fprintf(fd, "screen:%s=", ss->name);
writeFields(fd, ss->fields, true);
fprintf(fd, ".tree_view=%d\n", (int) ss->treeView);
fprintf(fd, ".sort_key=%s\n", toFieldName(ss->sortKey));
fprintf(fd, ".sort_direction=%d\n", (int) ss->direction);
}
}
fclose(fd); fclose(fd);
return true; return true;
} }
@ -326,14 +473,11 @@ Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false; this->hideThreads = false;
this->shadowOtherUsers = false; this->shadowOtherUsers = false;
this->showThreadNames = false; this->showThreadNames = false;
this->hideKernelThreads = false; this->hideKernelThreads = false;
this->hideUserlandThreads = false; this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false; this->highlightBaseName = false;
this->highlightMegabytes = false; this->highlightMegabytes = false;
this->detailedCPUTime = false; this->detailedCPUTime = false;
@ -343,15 +487,8 @@ Settings* Settings_new(int cpuCount) {
this->showProgramPath = true; this->showProgramPath = true;
this->highlightThreads = true; this->highlightThreads = true;
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField)); this->screens = xCalloc(sizeof(ScreenSettings*), 1);
// TODO: turn 'fields' into a Vector, this->nScreens = 0;
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
char* legacyDotfile = NULL; char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC"); char* rcfile = getenv("HTOPRC");
@ -380,10 +517,8 @@ Settings* Settings_new(int cpuCount) {
free(htopDir); free(htopDir);
free(configDir); free(configDir);
struct stat st; struct stat st;
if (lstat(legacyDotfile, &st) != 0) { int err = lstat(legacyDotfile, &st);
st.st_mode = 0; if (err || S_ISLNK(st.st_mode)) {
}
if (access(legacyDotfile, R_OK) != 0 || S_ISLNK(st.st_mode)) {
free(legacyDotfile); free(legacyDotfile);
legacyDotfile = NULL; legacyDotfile = NULL;
} }
@ -392,32 +527,42 @@ Settings* Settings_new(int cpuCount) {
this->colorScheme = 0; this->colorScheme = 0;
this->changed = false; this->changed = false;
this->delay = DEFAULT_DELAY; this->delay = DEFAULT_DELAY;
bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->filename); bool ok = false;
if (ok) { if (legacyDotfile) {
if (legacyDotfile) { ok = Settings_read(this, legacyDotfile);
if (ok) {
// Transition to new location and delete old configuration file // Transition to new location and delete old configuration file
if (Settings_write(this)) if (Settings_write(this))
unlink(legacyDotfile); unlink(legacyDotfile);
} }
} else { free(legacyDotfile);
}
if (!ok) {
ok = Settings_read(this, this->filename);
}
if (!ok) {
this->changed = true; this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf? // TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc"); char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings); ok = Settings_read(this, systemSettings);
free(systemSettings); free(systemSettings);
if (!ok) {
Settings_defaultMeters(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
} }
free(legacyDotfile); if (!ok) {
Settings_defaultMeters(this);
Settings_defaultScreens(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
return this; return this;
} }
void Settings_invertSortOrder(Settings* this) { void ScreenSettings_invertSortOrder(ScreenSettings* this) {
if (this->direction == 1) if (this->direction == 1)
this->direction = -1; this->direction = -1;
else else

View File

@ -14,29 +14,44 @@ in the source distribution for its full text.
#include "Process.h" #include "Process.h"
#include <stdbool.h> #include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct { typedef struct {
int len; int len;
char** names; char** names;
int* modes; int* modes;
} MeterColumnSettings; } MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* filename; char* filename;
MeterColumnSettings columns[2]; MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount; int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero; bool countCPUsFromZero;
bool detailedCPUTime; bool detailedCPUTime;
bool treeView;
bool showProgramPath; bool showProgramPath;
bool hideThreads; bool hideThreads;
bool shadowOtherUsers; bool shadowOtherUsers;
@ -58,12 +73,18 @@ typedef struct Settings_ {
#endif #endif
/*
*/
void Settings_delete(Settings* this); void Settings_delete(Settings* this);
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line);
bool Settings_write(Settings* this); bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount); Settings* Settings_new(int cpuCount);
void Settings_invertSortOrder(Settings* this); void ScreenSettings_invertSortOrder(ScreenSettings* this);
#endif #endif

View File

@ -41,10 +41,10 @@ Panel* SignalsPanel_new() {
} }
#if (defined(SIGRTMIN) && defined(SIGRTMAX)) #if (defined(SIGRTMIN) && defined(SIGRTMAX))
if (SIGRTMAX - SIGRTMIN <= 100) { if (SIGRTMAX - SIGRTMIN <= 100) {
static char buf[15]; static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) { for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN; int n = sig - SIGRTMIN;
xSnprintf(buf, 15, "%2d SIGRTMIN%-+3d", sig, n); xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) { if (n == 0) {
buf[11] = '\0'; buf[11] = '\0';
} }

View File

@ -27,11 +27,11 @@ in the source distribution for its full text.
*/ */
char* String_cat(const char* s1, const char* s2) { char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1); size_t l1 = strlen(s1);
int l2 = strlen(s2); size_t l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1); char* out = xMalloc(l1 + l2 + 1);
strncpy(out, s1, l1); strncpy(out, s1, l1);
strncpy(out+l1, s2, l2+1); strncpy(out + l1, s2, l2 + 1);
return out; return out;
} }
@ -39,7 +39,7 @@ char* String_trim(const char* in) {
while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') { while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
in++; in++;
} }
int len = strlen(in); size_t len = strlen(in);
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) { while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--; len--;
} }
@ -80,7 +80,7 @@ char** String_split(const char* s, char sep, int* n) {
s += size + 1; s += size + 1;
} }
if (s[0] != '\0') { if (s[0] != '\0') {
int size = strlen(s); size_t size = strlen(s);
char* token = xMalloc(size + 1); char* token = xMalloc(size + 1);
strncpy(token, s, size + 1); strncpy(token, s, size + 1);
out[ctr] = token; out[ctr] = token;

View File

@ -86,7 +86,7 @@ void TraceScreen_draw(InfoScreen* this) {
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm); mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
} }
bool TraceScreen_forkTracer(TraceScreen* this) { bool TraceScreen_forkTracer(TraceScreen* this) {
@ -101,14 +101,15 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
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) {
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-p", buffer, NULL); execlp("strace", "strace", "-s", "512", "-p", buffer, NULL);
} }
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
ssize_t written = write(this->fdpair[1], message, strlen(message)); ssize_t written = write(this->fdpair[1], message, strlen(message));
(void) written; (void) written;
exit(1); exit(1);
} }
fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK); int ok = fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK);
if (ok == -1) return false;
this->strace = fdopen(this->fdpair[0], "r"); this->strace = fdopen(this->fdpair[0], "r");
this->fd_strace = fileno(this->strace); this->fd_strace = fileno(this->strace);
return true; return true;

View File

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

View File

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

View File

@ -2,7 +2,7 @@
# 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.1.0],[hisham@gobolinux.org]) AC_INIT([htop],[3.0.0beta4],[hisham@gobolinux.org])
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" 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") year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
@ -43,6 +43,9 @@ dragonfly*)
darwin*) darwin*)
my_htop_platform=darwin my_htop_platform=darwin
;; ;;
solaris*)
my_htop_platform=solaris
;;
*) *)
my_htop_platform=unsupported my_htop_platform=unsupported
;; ;;
@ -61,6 +64,16 @@ AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/param.h sys/time.h unistd.h],[
]) ])
AC_CHECK_HEADERS([execinfo.h],[:],[:]) AC_CHECK_HEADERS([execinfo.h],[:],[:])
AC_HEADER_MAJOR
dnl glibc 2.25 deprecates 'major' and 'minor' in <sys/types.h> and requires to
dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet
dnl been updated in Autoconf 2.69, so use a workaround:
m4_version_prereq([2.70], [],
[if test "x$ac_cv_header_sys_mkdev_h" = xno; then
AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1,
[Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])])
fi])
# Checks for typedefs, structures, and compiler characteristics. # Checks for typedefs, structures, and compiler characteristics.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
AC_HEADER_STDBOOL AC_HEADER_STDBOOL
@ -154,20 +167,26 @@ m4_define([HTOP_CHECK_SCRIPT],
[ [
if test ! -z "m4_toupper($HTOP_[$1]_CONFIG_SCRIPT)"; then if test ! -z "m4_toupper($HTOP_[$1]_CONFIG_SCRIPT)"; then
# to be used to set the path to ncurses*-config when cross-compiling # 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) htop_config_script_libs=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null)
htop_config_script_cflags=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --cflags 2> /dev/null)
else else
htop_config_script=$([$4] --libs 2> /dev/null) htop_config_script_libs=$([$4] --libs 2> /dev/null)
htop_config_script_cflags=$([$4] --cflags 2> /dev/null)
fi 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 htop_save_CFLAGS="$CFLAGS"
LDFLAGS="$htop_config_script $LDFLAGS" if test ! "x$htop_config_script_libs" = x; then
LDFLAGS="$htop_config_script_libs $LDFLAGS"
CFLAGS="$htop_config_script_cflags $CFLAGS"
AC_CHECK_LIB([$1], [$2], [ AC_CHECK_LIB([$1], [$2], [
AC_DEFINE([$3], 1, [The library is present.]) AC_DEFINE([$3], 1, [The library is present.])
LIBS="$htop_config_script $LIBS " LIBS="$htop_config_script_libs $LIBS "
htop_script_success=yes htop_script_success=yes
], []) ], [
LDFLAGS="$save_LDFLAGS" CFLAGS="$htop_save_CFLAGS"
])
LDFLAGS="$htop_save_LDFLAGS"
fi fi
if test "x$htop_script_success" = xno; then if test "x$htop_script_success" = xno; then
[$5] [$5]
@ -222,6 +241,12 @@ 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
if test "$my_htop_platform" = "solaris"; then
AC_CHECK_LIB([kstat], [kstat_open], [], [missing_libraries="$missing_libraries libkstat"])
AC_CHECK_LIB([proc], [Pgrab_error], [], [missing_libraries="$missing_libraries libproc"])
AC_CHECK_LIB([malloc], [free], [], [missing_libraries="$missing_libraries libmalloc"])
fi
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") AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then 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])
@ -256,13 +281,60 @@ then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi fi
AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes")
if test "x$enable_perfcounters" = "xyes" -a "$my_htop_platform" = "linux"
then
AC_DEFINE(HAVE_PERFCOUNTERS, 1, [Define if hardware performance counter support should be enabled.])
AC_CHECK_HEADERS([linux/perf_counter.h], [have_perf_counter=yes],
[have_perf_counter=no])
AC_CHECK_HEADERS([linux/perf_event.h], [have_perf_event=yes],
[have_perf_event=no])
if test "x${have_perf_counter}" = xno -a "x${have_perf_event}" = xno; then
os=`uname -s -r`
AC_MSG_FAILURE([
------------------------------------------------------------
Could not locate linux/perf_count.h or linux/perf_event.h.
Are performance counters supported on this machine?
Linux 2.6.31+ is required.
uname reports: ${os}
------------------------------------------------------------])
fi
# Check for hardware architecture
no_target=yes
AC_MSG_CHECKING([hardware])
hw=`uname -m`
case $hw in
x86_64 | i386 | i686 ) :
AC_MSG_RESULT([x86])
AC_DEFINE([TARGET_X86], [1], [Define to 1 if the target is x86.])
no_target=no
;;
unknown ) :
AC_MSG_RESULT([unknown])
AC_MSG_WARN([Could not detect architecture])
;;
* ) :
AC_MSG_RESULT([$hw])
;;
esac
if test x$no_target = xyes; then
AC_DEFINE([NOTARGET], [1], [Define to 1 when no specific target is supported.])
fi
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no") AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes if test "x$enable_delayacct" = xyes
then then
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"]) PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"]) PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"])
CFLAGS+=" $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
LIBS+=" $LIBNL3_LIBS $LIBNL3GENL_LIBS" LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.]) AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
fi fi
@ -285,6 +357,7 @@ AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd]) AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd]) AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin]) AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin])
AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported]) AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform) AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1]) AC_CONFIG_FILES([Makefile htop.1])

View File

@ -18,6 +18,8 @@ in the source distribution for its full text.
#include <sys/mman.h> #include <sys/mman.h>
#include <utmpx.h> #include <utmpx.h>
#include <err.h> #include <err.h>
#include <sys/sysctl.h>
#include <stdbool.h>
/*{ /*{
#include "ProcessList.h" #include "ProcessList.h"
@ -38,19 +40,51 @@ typedef struct DarwinProcessList_ {
}*/ }*/
typedef struct kern {
short int version[3];
} kern_;
static void getKernelVersion(struct kern *k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
version_[0] = version_[1] = version_[2] = -1;
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
}
memcpy(k->version, version_, sizeof(version_));
}
static int compareKernelVersion(short int major, short int minor, short int component) {
/*
compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
struct kern k;
getKernelVersion(&k);
if ( k.version[0] != major) return k.version[0] - major;
if ( k.version[1] != minor) return k.version[1] - minor;
if ( k.version[2] != component) return k.version[2] - component;
return 0;
}
void ProcessList_getHostInfo(host_basic_info_data_t *p) { void ProcessList_getHostInfo(host_basic_info_data_t *p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
if(0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n"); CRT_fatalError("Unable to retrieve host info\n");
} }
} }
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) { void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
if(NULL != p && NULL != *p) { if (NULL != p && NULL != *p) {
if(0 != munmap(*p, vm_page_size)) { if (0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n"); CRT_fatalError("Unable to free old CPU load information\n");
} }
} }
*p = NULL; *p = NULL;
@ -61,7 +95,7 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
unsigned cpu_count; unsigned cpu_count;
// TODO Improving the accuracy of the load counts woule help a lot. // TODO Improving the accuracy of the load counts woule help a lot.
if(0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) { if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
CRT_fatalError("Unable to retrieve CPU info\n"); CRT_fatalError("Unable to retrieve CPU info\n");
} }
@ -69,10 +103,10 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
} }
void ProcessList_getVMStats(vm_statistics_t p) { void ProcessList_getVMStats(vm_statistics_t p) {
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
CRT_fatalError("Unable to retrieve VM statistics\n"); CRT_fatalError("Unable to retrieve VM statistics\n");
} }
struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) { struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
@ -127,61 +161,65 @@ void ProcessList_delete(ProcessList* this) {
} }
void ProcessList_goThroughEntries(ProcessList* super) { void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcessList *dpl = (DarwinProcessList *)super; DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true; bool preExisting = true;
struct kinfo_proc *ps; struct kinfo_proc *ps;
size_t count; size_t count;
DarwinProcess *proc; DarwinProcess *proc;
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); /* Start processing time */ gettimeofday(&tv, NULL); /* Start processing time */
/* Update the global data (CPU times and VM stats) */ /* Update the global data (CPU times and VM stats) */
ProcessList_freeCPULoadInfo(&dpl->prev_load); ProcessList_freeCPULoadInfo(&dpl->prev_load);
dpl->prev_load = dpl->curr_load; dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load); ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats); ProcessList_getVMStats(&dpl->vm_stats);
/* Get the time difference */ /* Get the time difference */
dpl->global_diff = 0; dpl->global_diff = 0;
for(int i = 0; i < dpl->super.cpuCount; ++i) { for(int i = 0; i < dpl->super.cpuCount; ++i) {
for(size_t j = 0; j < CPU_STATE_MAX; ++j) { for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
} }
} }
/* Clear the thread counts */ /* Clear the thread counts */
super->kernelThreads = 0; super->kernelThreads = 0;
super->userlandThreads = 0; super->userlandThreads = 0;
super->totalTasks = 0; super->totalTasks = 0;
super->runningTasks = 0; super->runningTasks = 0;
/* We use kinfo_procs for initial data since : /* We use kinfo_procs for initial data since :
* *
* 1) They always succeed. * 1) They always succeed.
* 2) The contain the basic information. * 2) The contain the basic information.
* *
* We attempt to fill-in additional information with libproc. * We attempt to fill-in additional information with libproc.
*/ */
ps = ProcessList_getKInfoProcs(&count); ps = ProcessList_getKInfoProcs(&count);
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);
// Disabled due to bug in macOS High Sierra
// DarwinProcess_scanThreads(proc);
super->totalTasks += 1; // Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( compareKernelVersion(17, 0, 0) >= 0 && compareKernelVersion(17, 5, 0) < 0);
if(!preExisting) { if (isScanThreadSupported){
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid); DarwinProcess_scanThreads(proc);
}
ProcessList_add(super, &proc->super); super->totalTasks += 1;
}
}
free(ps); if (!preExisting) {
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
ProcessList_add(super, &proc->super);
}
}
free(ps);
} }

View File

@ -26,6 +26,7 @@ typedef struct DarwinProcessList_ {
} DarwinProcessList; } DarwinProcessList;
void ProcessList_getHostInfo(host_basic_info_data_t *p); void ProcessList_getHostInfo(host_basic_info_data_t *p);
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p); void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p);

View File

@ -25,13 +25,26 @@ in the source distribution for its full text.
#include "CPUMeter.h" #include "CPUMeter.h"
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "DarwinProcess.h" #include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
}*/ }*/
#ifndef CLAMP #ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },
@ -97,7 +110,7 @@ ProcessFieldData Process_fields[] = {
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
}; };
MeterClass* Platform_meterTypes[] = { MeterClass* Platform_meterTypes[] = {

View File

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

View File

@ -40,7 +40,15 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
int Platform_numberOfFields = LAST_PROCESSFIELD; int Platform_numberOfFields = LAST_PROCESSFIELD;

View File

@ -21,7 +21,9 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
extern ProcessField Platform_defaultFields[]; extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern int Platform_numberOfFields; extern int Platform_numberOfFields;

View File

@ -39,6 +39,16 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
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 };
int Platform_numberOfFields = LAST_PROCESSFIELD; int Platform_numberOfFields = LAST_PROCESSFIELD;

View File

@ -20,6 +20,10 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern ProcessField Platform_defaultFields[]; extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields; extern int Platform_numberOfFields;

19
htop.c
View File

@ -42,6 +42,7 @@ static void printHelpFlag() {
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-h --help Print this help screen\n" "-h --help Print this help screen\n"
"-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n" "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
"-t --tree Show the tree view by default\n"
"-u --user=USERNAME Show only processes of a given user\n" "-u --user=USERNAME Show only processes of a given user\n"
"-p --pid=PID,[,PID,PID...] Show only the given PIDs\n" "-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
"-v --version Print version info\n" "-v --version Print version info\n"
@ -61,6 +62,7 @@ typedef struct CommandLineSettings_ {
int sortKey; int sortKey;
int delay; int delay;
bool useColors; bool useColors;
bool treeView;
} CommandLineSettings; } CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) { static CommandLineSettings parseArguments(int argc, char** argv) {
@ -71,6 +73,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.sortKey = 0, .sortKey = 0,
.delay = -1, .delay = -1,
.useColors = true, .useColors = true,
.treeView = false,
}; };
static struct option long_opts[] = static struct option long_opts[] =
@ -82,6 +85,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"user", required_argument, 0, 'u'}, {"user", required_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'}, {"no-color", no_argument, 0, 'C'},
{"no-colour",no_argument, 0, 'C'}, {"no-colour",no_argument, 0, 'C'},
{"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'}, {"pid", required_argument, 0, 'p'},
{"io", no_argument, 0, 'i'}, {"io", no_argument, 0, 'i'},
{0,0,0,0} {0,0,0,0}
@ -89,7 +93,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
int opt, opti=0; int opt, opti=0;
/* Parse arguments */ /* Parse arguments */
while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:i", long_opts, &opti))) { while ((opt = getopt_long(argc, argv, "hvCst::d:u:p:i", long_opts, &opti))) {
if (opt == EOF) break; if (opt == EOF) break;
switch (opt) { switch (opt) {
case 'h': case 'h':
@ -127,6 +131,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
case 'C': case 'C':
flags.useColors = false; flags.useColors = false;
break; break;
case 't':
flags.treeView = true;
break;
case 'p': { case 'p': {
char* argCopy = xStrdup(optarg); char* argCopy = xStrdup(optarg);
char* saveptr; char* saveptr;
@ -197,18 +204,20 @@ int main(int argc, char** argv) {
settings->delay = flags.delay; settings->delay = flags.delay;
if (!flags.useColors) if (!flags.useColors)
settings->colorScheme = COLORSCHEME_MONOCHROME; settings->colorScheme = COLORSCHEME_MONOCHROME;
if (flags.treeView)
settings->screens[0]->treeView = true;
CRT_init(settings->delay, settings->colorScheme); CRT_init(settings->delay, settings->colorScheme);
MainPanel* panel = MainPanel_new(); MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel); ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->treeView); MainPanel_updateTreeFunctions(panel, settings->screens[0]->treeView);
if (flags.sortKey > 0) { if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey; settings->screens[0]->sortKey = flags.sortKey;
settings->treeView = false; settings->screens[0]->treeView = false;
settings->direction = 1; settings->screens[0]->direction = 1;
} }
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel)); ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));

View File

@ -110,16 +110,13 @@ static ACPresence procAcpiCheck() {
char statePath[50]; char statePath[50];
xSnprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName); xSnprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
FILE* file = fopen(statePath, "r"); FILE* file = fopen(statePath, "r");
if (!file) { if (!file) {
isOn = AC_ERROR; isOn = AC_ERROR;
continue; continue;
} }
char* line = String_readLine(file); char* line = String_readLine(file);
if (!line) continue;
fclose(file); fclose(file);
if (!line) continue;
const char *isOnline = String_getToken(line, 2); const char *isOnline = String_getToken(line, 2);
free(line); free(line);

View File

@ -18,11 +18,24 @@ in the source distribution for its full text.
/*{ /*{
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100 #include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_OOM 0x1000 #define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields { typedef enum UnsupportedProcessFields {
FLAGS = 9, FLAGS = 9,
@ -86,13 +99,25 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117, PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118, PERCENT_SWAP_DELAY = 118,
#endif #endif
LAST_PROCESSFIELD = 119, #ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField; } LinuxProcessField;
#include "IOPriority.h" #include "IOPriority.h"
typedef struct LinuxProcess_ { typedef struct LinuxProcess_ {
Process super; Process super;
bool isKernelThread;
IOPriority ioPriority; IOPriority ioPriority;
unsigned long int cminflt; unsigned long int cminflt;
unsigned long int cmajflt; unsigned long int cmajflt;
@ -139,10 +164,29 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent; float blkio_delay_percent;
float swapin_delay_percent; float swapin_delay_percent;
#endif #endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess; } LinuxProcess;
#ifndef Process_isKernelThread #ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0) #define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#endif #endif
#ifndef Process_isUserlandThread #ifndef Process_isUserlandThread
@ -230,9 +274,20 @@ ProcessFieldData Process_fields[] = {
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, }, [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPD% ", .description = "CPU delay %", .flags = 0, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, }, [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, }, [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWD% ", .description = "Swapin delay %", .flags = 0, },
#endif
#ifdef HAVE_PERFCOUNTERS
[IPC] = { .name = "IPC", .title = " IPC ", .description = "Executed instructions per cycle", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE | PROCESS_FLAG_LINUX_HPC_INSN, },
[MCYCLE] = { .name = "MCYCLE", .title = " Mcycle ", .description = "Cycles (millions)", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE, },
[MINSTR] = { .name = "MINSTR", .title = " Minstr ", .description = "Instructions (millions)", .flags = PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_MISS] = { .name = "PERCENT_MISS", .title = "MIS% ", .description = "Cache misses per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_MISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_BMISS] = { .name = "PERCENT_BMISS", .title = "BrM% ", .description = "Branch misprediction per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_BMISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[L1DREADS] = { .name = "L1DREADS", .title = " L1Dread ", .description = "L1 data cache: reads (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DR, },
[L1DRMISSES] = { .name = "L1DRMISSES", .title = " R miss ", .description = "L1 data cache: reads misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DRM, },
[L1DWRITES] = { .name = "L1DWRITES", .title = " L1Dwrite ", .description = "L1D data cache: writes (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DW, },
[L1DWMISSES] = { .name = "L1DWMISSES", .title = " W miss ", .description = "L1D data cache: write misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DWM, },
#endif #endif
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
}; };
@ -273,6 +328,10 @@ void Process_delete(Object* cast) {
Process_done((Process*)cast); Process_done((Process*)cast);
#ifdef HAVE_CGROUP #ifdef HAVE_CGROUP
free(this->cgroup); free(this->cgroup);
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter_delete(this->cycleCounter);
PerfCounter_delete(this->insnCounter);
#endif #endif
free(this->ttyDevice); free(this->ttyDevice);
free(this); free(this);
@ -306,14 +365,43 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) {
return (LinuxProcess_updateIOPriority(this) == ioprio); return (LinuxProcess_updateIOPriority(this) == ioprio);
} }
#ifdef HAVE_DELAYACCT #if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
if (delay_percent == -1LL) { static char* perfFmt[] = {
xSnprintf(buffer, n, " N/A "); "%6.2f ",
} else { NULL,
xSnprintf(buffer, n, "%4.1f ", delay_percent); NULL,
} NULL,
NULL,
NULL,
"%6.1f ",
"%7.1f ",
"%8.2f ",
"%9.1f ",
};
static char* perfNA[] = {
" N/A ",
NULL,
NULL,
NULL,
NULL,
NULL,
" N/A ",
" N/A ",
" N/A ",
" N/A ",
};
static inline void LinuxProcess_printPerfCounter(float val, int len, char* buffer, int n, int* attr) {
if (val != -1) {
xSnprintf(buffer, n, perfFmt[len], val);
} else {
xSnprintf(buffer, n, perfNA[len]);
*attr = CRT_colors[PROCESS_SHADOW];
}
} }
#endif #endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) { void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
@ -390,9 +478,20 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
break; break;
} }
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break; case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break;
case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break; case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break;
case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break; case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break;
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: Process_printPercentage(lp->pMiss, buffer, n, &attr); break;
case PERCENT_BMISS: Process_printPercentage(lp->pBMiss, buffer, n, &attr); break;
case IPC: LinuxProcess_printPerfCounter(lp->ipc, 0, buffer, n, &attr); break;
case MCYCLE: LinuxProcess_printPerfCounter(lp->mcycle, 8, buffer, n, &attr); break;
case MINSTR: LinuxProcess_printPerfCounter(lp->minstr, 8, buffer, n, &attr); break;
case L1DREADS: LinuxProcess_printPerfCounter(lp->l1dr, 9, buffer, n, &attr); break;
case L1DRMISSES: LinuxProcess_printPerfCounter(lp->l1drm, 9, buffer, n, &attr); break;
case L1DWRITES: LinuxProcess_printPerfCounter(lp->l1dw, 9, buffer, n, &attr); break;
case L1DWMISSES: LinuxProcess_printPerfCounter(lp->l1dwm, 9, buffer, n, &attr); break;
#endif #endif
default: default:
Process_writeField((Process*)this, str, field); Process_writeField((Process*)this, str, field);
@ -401,10 +500,12 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
RichString_append(str, attr, buffer); RichString_append(str, attr, buffer);
} }
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2) { long LinuxProcess_compare(const void* v1, const void* v2) {
LinuxProcess *p1, *p2; LinuxProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings; Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) { if (settings->ss->direction == 1) {
p1 = (LinuxProcess*)v1; p1 = (LinuxProcess*)v1;
p2 = (LinuxProcess*)v2; p2 = (LinuxProcess*)v2;
} else { } else {
@ -412,7 +513,7 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
p1 = (LinuxProcess*)v2; p1 = (LinuxProcess*)v2;
} }
long long diff; long long diff;
switch ((int)settings->sortKey) { switch ((int)settings->ss->sortKey) {
case M_DRS: case M_DRS:
return (p2->m_drs - p1->m_drs); return (p2->m_drs - p1->m_drs);
case M_DT: case M_DT:
@ -456,12 +557,20 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
case OOM: case OOM:
return (p2->oom - p1->oom); return (p2->oom - p1->oom);
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: case PERCENT_CPU_DELAY: COMPARE_FIELD(cpu_delay_percent);
return (p2->cpu_delay_percent > p1->cpu_delay_percent ? 1 : -1); case PERCENT_IO_DELAY: COMPARE_FIELD(blkio_delay_percent);
case PERCENT_IO_DELAY: case PERCENT_SWAP_DELAY: COMPARE_FIELD(swapin_delay_percent);
return (p2->blkio_delay_percent > p1->blkio_delay_percent ? 1 : -1); #endif
case PERCENT_SWAP_DELAY: #ifdef HAVE_PERFCOUNTERS
return (p2->swapin_delay_percent > p1->swapin_delay_percent ? 1 : -1); case PERCENT_MISS: COMPARE_FIELD(pMiss);
case PERCENT_BMISS: COMPARE_FIELD(pBMiss);
case IPC: COMPARE_FIELD(ipc);
case MCYCLE: COMPARE_FIELD(mcycle);
case MINSTR: COMPARE_FIELD(minstr);
case L1DREADS: COMPARE_FIELD(l1dr);
case L1DRMISSES: COMPARE_FIELD(l1drm);
case L1DWRITES: COMPARE_FIELD(l1dw);
case L1DWMISSES: COMPARE_FIELD(l1dwm);
#endif #endif
case IO_PRIORITY: case IO_PRIORITY:
return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2); return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2);
@ -475,4 +584,3 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
bool Process_isThread(Process* this) { bool Process_isThread(Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this)); return (Process_isUserlandThread(this) || Process_isKernelThread(this));
} }

View File

@ -10,11 +10,24 @@ in the source distribution for its full text.
*/ */
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100 #include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_OOM 0x1000 #define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields { typedef enum UnsupportedProcessFields {
FLAGS = 9, FLAGS = 9,
@ -78,13 +91,25 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117, PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118, PERCENT_SWAP_DELAY = 118,
#endif #endif
LAST_PROCESSFIELD = 119, #ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField; } LinuxProcessField;
#include "IOPriority.h" #include "IOPriority.h"
typedef struct LinuxProcess_ { typedef struct LinuxProcess_ {
Process super; Process super;
bool isKernelThread;
IOPriority ioPriority; IOPriority ioPriority;
unsigned long int cminflt; unsigned long int cminflt;
unsigned long int cmajflt; unsigned long int cmajflt;
@ -131,10 +156,29 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent; float blkio_delay_percent;
float swapin_delay_percent; float swapin_delay_percent;
#endif #endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess; } LinuxProcess;
#ifndef Process_isKernelThread #ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0) #define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#endif #endif
#ifndef Process_isUserlandThread #ifndef Process_isUserlandThread
@ -166,15 +210,16 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio); bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio);
#ifdef HAVE_DELAYACCT #if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n);
#endif #endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field); void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2); long LinuxProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this); bool Process_isThread(Process* this);
#endif #endif

View File

@ -26,6 +26,12 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <sys/types.h> #include <sys/types.h>
#include <fcntl.h> #include <fcntl.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#include <sys/sysmacros.h>
#endif
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
#include <netlink/attr.h> #include <netlink/attr.h>
@ -436,7 +442,7 @@ 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[4] == '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) {
process->io_syscw = strtoull(line+7, NULL, 10); process->io_syscw = strtoull(line+7, NULL, 10);
@ -595,36 +601,36 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirn
static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) { static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
struct nlmsghdr *nlhdr; struct nlmsghdr *nlhdr;
struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1]; struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
struct nlattr *nlattr; struct nlattr *nlattr;
struct taskstats *stats; struct taskstats *stats;
int rem; int rem;
unsigned long long int timeDelta; unsigned long long int timeDelta;
LinuxProcess* lp = (LinuxProcess*) linuxProcess; LinuxProcess* lp = (LinuxProcess*) linuxProcess;
nlhdr = nlmsg_hdr(nlmsg); nlhdr = nlmsg_hdr(nlmsg);
if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
return NL_SKIP; return NL_SKIP;
} }
if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
stats = nla_data(nla_next(nla_data(nlattr), &rem)); stats = nla_data(nla_next(nla_data(nlattr), &rem));
assert(lp->super.pid == stats->ac_pid); assert(lp->super.pid == stats->ac_pid);
timeDelta = (stats->ac_etime*1000 - lp->delay_read_time); timeDelta = (stats->ac_etime*1000 - lp->delay_read_time);
#define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x; #define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x;
#define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100); #define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100);
lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total); lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total);
lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total); lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total);
lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total); lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC #undef DELTAPERC
#undef BOUNDS #undef BOUNDS
lp->swapin_delay_total = stats->swapin_delay_total; lp->swapin_delay_total = stats->swapin_delay_total;
lp->blkio_delay_total = stats->blkio_delay_total; lp->blkio_delay_total = stats->blkio_delay_total;
lp->cpu_delay_total = stats->cpu_delay_total; lp->cpu_delay_total = stats->cpu_delay_total;
lp->delay_read_time = stats->ac_etime*1000; lp->delay_read_time = stats->ac_etime*1000;
} }
return NL_OK; return NL_OK;
} }
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) { static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
@ -647,9 +653,9 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
} }
if (nl_send_sync(this->netlink_socket, msg) < 0) { if (nl_send_sync(this->netlink_socket, msg) < 0) {
process->swapin_delay_percent = -1LL; process->swapin_delay_percent = -1;
process->blkio_delay_percent = -1LL; process->blkio_delay_percent = -1;
process->cpu_delay_percent = -1LL; process->cpu_delay_percent = -1;
return; return;
} }
@ -660,6 +666,77 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
#endif #endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
static void LinuxProcessList_readPerfCounters(LinuxProcess* lp, uint64_t flags) {
READ_COUNTER(c, lp->cycleCounter, PROCESS_FLAG_LINUX_HPC_CYCLE, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
READ_COUNTER(i, lp->insnCounter, PROCESS_FLAG_LINUX_HPC_INSN, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
READ_COUNTER(m, lp->missCounter, PROCESS_FLAG_LINUX_HPC_MISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES);
READ_COUNTER(b, lp->brCounter, PROCESS_FLAG_LINUX_HPC_BMISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES);
READ_COUNTER(r, lp->l1drCounter, PROCESS_FLAG_LINUX_HPC_L1DR, PERF_TYPE_HW_CACHE, L1DR);
READ_COUNTER(R, lp->l1drmCounter, PROCESS_FLAG_LINUX_HPC_L1DRM, PERF_TYPE_HW_CACHE, L1DRM);
READ_COUNTER(w, lp->l1dwCounter, PROCESS_FLAG_LINUX_HPC_L1DW, PERF_TYPE_HW_CACHE, L1DW);
READ_COUNTER(W, lp->l1dwmCounter, PROCESS_FLAG_LINUX_HPC_L1DWM, PERF_TYPE_HW_CACHE, L1DWM);
SET_IF(cOk, lp->mcycle, (double)cDelta / 1000000);
SET_IF(iOk, lp->minstr, (double)iDelta / 1000000);
SET_IFNZ(cOk && iOk, cDelta, lp->ipc, (double)iDelta / cDelta);
SET_IFNZ(mOk && iOk, iDelta, lp->pMiss, 100 * ((double)mDelta / iDelta));
SET_IFNZ(bOk && iOk, iDelta, lp->pBMiss, 100 * ((double)bDelta / iDelta));
SET_IF(rOk, lp->l1dr, (double)rDelta / 1000);
SET_IF(ROk, lp->l1drm, (double)RDelta / 1000);
SET_IF(wOk, lp->l1dw, (double)wDelta / 1000);
SET_IF(WOk, lp->l1dwm, (double)WDelta / 1000);
}
#endif
static void setCommand(Process* process, const char* command, int len) { 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);
@ -671,9 +748,6 @@ static void setCommand(Process* process, const char* command, int len) {
} }
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) { static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
if (Process_isKernelThread(process))
return true;
char filename[MAX_NAME+1]; char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name); xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
int fd = open(filename, O_RDONLY); int fd = open(filename, O_RDONLY);
@ -685,7 +759,10 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
close(fd); close(fd);
int tokenEnd = 0; int tokenEnd = 0;
int lastChar = 0; int lastChar = 0;
if (amtRead <= 0) { if (amtRead == 0) {
((LinuxProcess*)process)->isKernelThread = true;
return true;
} else if (amtRead < 0) {
return false; return false;
} }
for (int i = 0; i < amtRead; i++) { for (int i = 0; i < amtRead; i++) {
@ -755,6 +832,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
DIR* dir; DIR* dir;
struct dirent* entry; struct dirent* entry;
Settings* settings = pl->settings; Settings* settings = pl->settings;
ScreenSettings* ss = settings->ss;
time_t curTime = tv.tv_sec; time_t curTime = tv.tv_sec;
#ifdef HAVE_TASKSTATS #ifdef HAVE_TASKSTATS
@ -800,7 +878,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv); LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
#ifdef HAVE_TASKSTATS #ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO) if (ss->flags & PROCESS_FLAG_IO)
LinuxProcessList_readIoFile(lp, dirname, name, now); LinuxProcessList_readIoFile(lp, dirname, name, now);
#endif #endif
@ -819,7 +897,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
free(lp->ttyDevice); free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr); lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
} }
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO) if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO)
LinuxProcess_updateIOPriority(lp); LinuxProcess_updateIOPriority(lp);
float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0; float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0); proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
@ -834,13 +912,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid); proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) { if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) {
LinuxProcessList_readOpenVZData(lp, dirname, name); LinuxProcessList_readOpenVZData(lp, dirname, name);
} }
#endif #endif
#ifdef HAVE_VSERVER #ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) { if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) {
LinuxProcessList_readVServerData(lp, dirname, name); LinuxProcessList_readVServerData(lp, dirname, name);
} }
#endif #endif
@ -863,13 +941,18 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
#endif #endif
#ifdef HAVE_CGROUP #ifdef HAVE_CGROUP
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) if (ss->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcessList_readCGroupFile(lp, dirname, name); LinuxProcessList_readCGroupFile(lp, dirname, name);
#endif #endif
if (settings->flags & PROCESS_FLAG_LINUX_OOM) if (ss->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcessList_readOomData(lp, dirname, name); LinuxProcessList_readOomData(lp, dirname, name);
#ifdef HAVE_PERFCOUNTERS
if (ss->flags & PROCESS_FLAG_LINUX_HPC)
LinuxProcessList_readPerfCounters(lp, ss->flags);
#endif
if (proc->state == 'Z' && (proc->basenameOffset == 0)) { if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1; proc->basenameOffset = -1;
setCommand(proc, command, commLen); setCommand(proc, command, commLen);

View File

@ -9,6 +9,11 @@ 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.
*/ */
#ifdef MAJOR_IN_MKDEV
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#endif
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
#endif #endif
@ -116,6 +121,54 @@ void ProcessList_delete(ProcessList* pl);
#endif #endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#endif
void ProcessList_goThroughEntries(ProcessList* super); void ProcessList_goThroughEntries(ProcessList* super);
#endif #endif

135
linux/PerfCounter.c Normal file
View File

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

54
linux/PerfCounter.h Normal file
View File

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

View File

@ -22,6 +22,7 @@ in the source distribution for its full text.
#include "ClockMeter.h" #include "ClockMeter.h"
#include "HostnameMeter.h" #include "HostnameMeter.h"
#include "LinuxProcess.h" #include "LinuxProcess.h"
#include "CRT.h"
#include <math.h> #include <math.h>
#include <assert.h> #include <assert.h>
@ -35,16 +36,13 @@ in the source distribution for its full text.
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "LinuxProcess.h" #include "LinuxProcess.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
#include "Settings.h"
}*/ }*/
#ifndef CLAMP #ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
//static ProcessField defaultIoFields[] = { PID, IO_PRIORITY, USER, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD; int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
@ -84,6 +82,31 @@ const SignalItem Platform_signals[] = {
{ .name = "31 SIGSYS", .number = 31 }, { .name = "31 SIGSYS", .number = 31 },
}; };
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
{
.name = "I/O",
.columns = "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command",
.sortKey = "IO_RATE",
},
{
.name = "Perf Counters",
.columns = "PID USER PERCENT_CPU PROCESSOR MCYCLE MINSTR IPC PERCENT_MISS PERCENT_BMISS Command",
.sortKey = "MCYCLE",
},
{
.name = "L1 Data Cache",
.columns = "PID USER PERCENT_CPU L1DREADS L1DRMISSES L1DWRITES L1DWMISSES Command",
.sortKey = "LD1READS",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem); const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
static Htop_Reaction Platform_actionSetIOPriority(State* st) { static Htop_Reaction Platform_actionSetIOPriority(State* st) {
@ -96,7 +119,7 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) {
void* set = Action_pickFromVector(st, ioprioPanel, 21); void* set = Action_pickFromVector(st, ioprioPanel, 21);
if (set) { if (set) {
IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel); IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (size_t) ioprio, NULL); bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (Arg){ .i = ioprio }, NULL);
if (!ok) if (!ok)
beep(); beep();
} }

View File

@ -14,17 +14,20 @@ in the source distribution for its full text.
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "LinuxProcess.h" #include "LinuxProcess.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
#include "Settings.h"
#ifndef CLAMP #ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields; extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern const unsigned int Platform_numberOfSignals; extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys); void Platform_setBindings(Htop_Action* keys);

View File

@ -7,10 +7,65 @@ in the source distribution for its full text.
*/ */
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include <sys/sysctl.h>
#include <sys/sensors.h>
#include <errno.h>
void Battery_getData(double* level, ACPresence* isOnAC) { static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) {
// TODO for (int devn = 0;; devn++) {
*level = -1; mib[2] = devn;
*isOnAC = AC_ERROR; if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) {
if (errno == ENXIO)
continue;
if (errno == ENOENT)
return false;
}
if (strcmp(name, snsrdev->xname) == 0) {
return true;
}
}
} }
void Battery_getData(double* level, ACPresence* isOnAC) {
static int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
struct sensor s;
size_t slen = sizeof(struct sensor);
struct sensordev snsrdev;
size_t sdlen = sizeof(struct sensordev);
bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen);
*level = -1;
if (found) {
/* last full capacity */
mib[3] = 7;
mib[4] = 0;
double last_full_capacity = 0;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
last_full_capacity = s.value;
}
if (last_full_capacity > 0) {
/* remaining capacity */
mib[3] = 7;
mib[4] = 3;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
double charge = s.value;
*level = 100*(charge / last_full_capacity);
if (charge >= last_full_capacity) {
*level = 100;
}
}
}
}
found = findDevice("acpiac0", mib, &snsrdev, &sdlen);
*isOnAC = AC_ERROR;
if (found) {
mib[3] = 9;
mib[4] = 0;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
*isOnAC = s.value;
}
}
}

View File

@ -12,5 +12,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

@ -92,7 +92,15 @@ static int percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_
return (total_change); return (total_change);
} }
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
int Platform_numberOfFields = LAST_PROCESSFIELD; int Platform_numberOfFields = LAST_PROCESSFIELD;

View File

@ -32,7 +32,9 @@ extern ProcessFieldData Process_fields[];
* The routine assumes modulo arithmetic. This function is especially * The routine assumes modulo arithmetic. This function is especially
* useful on BSD machines for calculating cpu state percentages. * useful on BSD machines for calculating cpu state percentages.
*/ */
extern ProcessField Platform_defaultFields[]; extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern int Platform_numberOfFields; extern int Platform_numberOfFields;

View File

@ -12,6 +12,7 @@ ANY=1
COPY=2 COPY=2
SKIP=3 SKIP=3
SKIPONE=4 SKIPONE=4
COPYDEFINE=5
state = ANY state = ANY
static = 0 static = 0
@ -49,7 +50,11 @@ for line in file.readlines():
elif len(line) > 1: elif len(line) > 1:
static = 0 static = 0
equals = line.find(" = ") equals = line.find(" = ")
if line[-3:] == "= {": if line[:7] == "#define":
if line[-1:] == "\\":
state = COPYDEFINE
out.write( line + "\n")
elif line[-3:] == "= {":
out.write( "extern " + line[:-4] + ";\n" ) out.write( "extern " + line[:-4] + ";\n" )
state = SKIP state = SKIP
elif equals != -1: elif equals != -1:
@ -60,7 +65,7 @@ for line in file.readlines():
out.write( line[:-2].replace("inline", "extern") + ";\n" ) out.write( line[:-2].replace("inline", "extern") + ";\n" )
state = SKIP state = SKIP
else: else:
out.write( line + "\n") out.write( line + "\n" )
is_blank = False is_blank = False
elif line == "": elif line == "":
if not is_blank: if not is_blank:
@ -69,6 +74,11 @@ for line in file.readlines():
else: else:
out.write( line + "\n") out.write( line + "\n")
is_blank = False is_blank = False
elif state == COPYDEFINE:
is_blank = False
out.write( line + "\n")
if line[-1:] != "\\":
state = ANY
elif state == COPY: elif state == COPY:
is_blank = False is_blank = False
if line == "}*/": if line == "}*/":

8
solaris/Battery.c Normal file
View File

@ -0,0 +1,8 @@
#include "BatteryMeter.h"
void Battery_getData(double* level, ACPresence* isOnAC) {
*level = -1;
*isOnAC = AC_ERROR;
}

9
solaris/Battery.h Normal file
View File

@ -0,0 +1,9 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Battery
#define HEADER_Battery
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

265
solaris/Platform.c Normal file
View File

@ -0,0 +1,265 @@
/*
htop - solaris/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
#include "SolarisProcess.h"
#include "SolarisProcessList.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <utmpx.h>
#include <sys/loadavg.h>
#include <string.h>
#include <kstat.h>
#include <time.h>
#include <math.h>
#include <sys/var.h>
/*{
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
#include <signal.h>
#include <sys/mkdev.h>
#include <sys/proc.h>
#include <libproc.h>
#define kill(pid, signal) kill(pid / 1024, signal)
extern ProcessFieldData Process_fields[];
typedef struct var kvar_t;
typedef struct envAccum_ {
size_t capacity;
size_t size;
size_t bytes;
char *env;
} envAccum;
}*/
double plat_loadavg[3] = {0};
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
{ .name = " 3 SIGQUIT", .number = 3 },
{ .name = " 4 SIGILL", .number = 4 },
{ .name = " 5 SIGTRAP", .number = 5 },
{ .name = " 6 SIGABRT/IOT", .number = 6 },
{ .name = " 7 SIGEMT", .number = 7 },
{ .name = " 8 SIGFPE", .number = 8 },
{ .name = " 9 SIGKILL", .number = 9 },
{ .name = "10 SIGBUS", .number = 10 },
{ .name = "11 SIGSEGV", .number = 11 },
{ .name = "12 SIGSYS", .number = 12 },
{ .name = "13 SIGPIPE", .number = 13 },
{ .name = "14 SIGALRM", .number = 14 },
{ .name = "15 SIGTERM", .number = 15 },
{ .name = "16 SIGUSR1", .number = 16 },
{ .name = "17 SIGUSR2", .number = 17 },
{ .name = "18 SIGCHLD/CLD", .number = 18 },
{ .name = "19 SIGPWR", .number = 19 },
{ .name = "20 SIGWINCH", .number = 20 },
{ .name = "21 SIGURG", .number = 21 },
{ .name = "22 SIGPOLL/IO", .number = 22 },
{ .name = "23 SIGSTOP", .number = 23 },
{ .name = "24 SIGTSTP", .number = 24 },
{ .name = "25 SIGCONT", .number = 25 },
{ .name = "26 SIGTTIN", .number = 26 },
{ .name = "27 SIGTTOU", .number = 27 },
{ .name = "28 SIGVTALRM", .number = 28 },
{ .name = "29 SIGPROF", .number = 29 },
{ .name = "30 SIGXCPU", .number = 30 },
{ .name = "31 SIGXFSZ", .number = 31 },
{ .name = "32 SIGWAITING", .number = 32 },
{ .name = "33 SIGLWP", .number = 33 },
{ .name = "34 SIGFREEZE", .number = 34 },
{ .name = "35 SIGTHAW", .number = 35 },
{ .name = "36 SIGCANCEL", .number = 36 },
{ .name = "37 SIGLOST", .number = 37 },
{ .name = "38 SIGXRES", .number = 38 },
{ .name = "39 SIGJVM1", .number = 39 },
{ .name = "40 SIGJVM2", .number = 40 },
{ .name = "41 SIGINFO", .number = 41 },
};
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID LWPID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
MeterClass* Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
&BatteryMeter_class,
&HostnameMeter_class,
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
}
int Platform_numberOfFields = LAST_PROCESSFIELD;
extern char Process_pidFormat[20];
int Platform_getUptime() {
int boot_time = 0;
int curr_time = time(NULL);
struct utmpx * ent;
while (( ent = getutxent() )) {
if ( !strcmp("system boot", ent->ut_line )) {
boot_time = ent->ut_tv.tv_sec;
}
}
endutxent();
return (curr_time-boot_time);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
getloadavg( plat_loadavg, 3 );
*one = plat_loadavg[LOADAVG_1MIN];
*five = plat_loadavg[LOADAVG_5MIN];
*fifteen = plat_loadavg[LOADAVG_15MIN];
}
int Platform_getMaxPid() {
kstat_ctl_t *kc = NULL;
kstat_t *kshandle = NULL;
kvar_t *ksvar = NULL;
int vproc = 32778; // Reasonable Solaris default
kc = kstat_open();
if (kc != NULL) { kshandle = kstat_lookup(kc,"unix",0,"var"); }
if (kshandle != NULL) { kstat_read(kc,kshandle,NULL); }
ksvar = kshandle->ks_data;
if (ksvar->v_proc > 0 ) {
vproc = ksvar->v_proc;
}
if (kc != NULL) { kstat_close(kc); }
return vproc;
}
double Platform_setCPUValues(Meter* this, int cpu) {
SolarisProcessList* spl = (SolarisProcessList*) this->pl;
int cpus = this->pl->cpuCount;
CPUData* cpuData = NULL;
if (cpus == 1) {
// single CPU box has everything in spl->cpus[0]
cpuData = &(spl->cpus[0]);
} else {
cpuData = &(spl->cpus[cpu]);
}
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}
percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
this->values[1] = pl->buffersMem;
this->values[2] = pl->cachedMem;
}
void Platform_setSwapValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
static int Platform_buildenv(void *accum, struct ps_prochandle *Phandle, uintptr_t addr, const char *str) {
envAccum *accump = accum;
(void) Phandle;
(void) addr;
size_t thissz = strlen(str);
if ((thissz + 2) > (accump->capacity - accump->size))
accump->env = xRealloc(accump->env, accump->capacity *= 2);
if ((thissz + 2) > (accump->capacity - accump->size))
return 1;
strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size));
strncpy( accump->env + accump->size + thissz + 1, "\n", 1);
accump->size = accump->size + thissz + 1;
return 0;
}
char* Platform_getProcessEnv(pid_t pid) {
envAccum envBuilder;
pid_t realpid = pid / 1024;
int graberr;
struct ps_prochandle *Phandle;
if ((Phandle = Pgrab(realpid,PGRAB_RDONLY,&graberr)) == NULL)
return "Unable to read process environment.";
envBuilder.capacity = 4096;
envBuilder.size = 0;
envBuilder.env = xMalloc(envBuilder.capacity);
(void) Penv_iter(Phandle,Platform_buildenv,&envBuilder);
Prelease(Phandle, 0);
strncpy( envBuilder.env + envBuilder.size, "\0", 1);
return envBuilder.env;
}

67
solaris/Platform.h Normal file
View File

@ -0,0 +1,67 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Platform
#define HEADER_Platform
/*
htop - solaris/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
#include <signal.h>
#include <sys/mkdev.h>
#include <sys/proc.h>
#include <libproc.h>
#define kill(pid, signal) kill(pid / 1024, signal)
extern ProcessFieldData Process_fields[];
typedef struct var kvar_t;
typedef struct envAccum_ {
size_t capacity;
size_t size;
size_t bytes;
char *env;
} envAccum;
extern double plat_loadavg[3];
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern MeterClass* Platform_meterTypes[];
void Platform_setBindings(Htop_Action* keys);
extern int Platform_numberOfFields;
extern char Process_pidFormat[20];
int Platform_getUptime();
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid();
double Platform_setCPUValues(Meter* this, int cpu);
void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
#endif

32
solaris/SolarisCRT.c Normal file
View File

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

18
solaris/SolarisCRT.h Normal file
View File

@ -0,0 +1,18 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisCRT
#define HEADER_SolarisCRT
/*
htop - SolarisCRT.h
(C) 2014 Hisham H. Muhammad
(C) 2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_EXECINFO_H
#endif
void CRT_handleSIGSEGV(int sgn);
#endif

214
solaris/SolarisProcess.c Normal file
View File

@ -0,0 +1,214 @@
/*
htop - SolarisProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Process.h"
#include "ProcessList.h"
#include "SolarisProcess.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
/*{
#include "Settings.h"
#include <zone.h>
#include <sys/proc.h>
#include <libproc.h>
typedef enum SolarisProcessFields {
// Add platform-specific fields here, with ids >= 100
ZONEID = 100,
ZONE = 101,
PROJID = 102,
TASKID = 103,
POOLID = 104,
CONTID = 105,
LWPID = 106,
LAST_PROCESSFIELD = 107,
} SolarisProcessField;
typedef struct SolarisProcess_ {
Process super;
int kernel;
zoneid_t zoneid;
char* zname;
taskid_t taskid;
projid_t projid;
poolid_t poolid;
ctid_t contid;
bool is_lwp;
pid_t realpid;
pid_t realppid;
pid_t lwpid;
} SolarisProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
}*/
ProcessClass SolarisProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = SolarisProcess_compare
},
.writeField = (Process_WriteField) SolarisProcess_writeField,
};
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, O onproc, Z zombie, T stopped, W waiting)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[ZONEID] = { .name = "ZONEID", .title = " ZONEID ", .description = "Zone ID", .flags = 0, },
[ZONE] = { .name = "ZONE", .title = "ZONE ", .description = "Zone name", .flags = 0, },
[PROJID] = { .name = "PROJID", .title = " PRJID ", .description = "Project ID", .flags = 0, },
[TASKID] = { .name = "TASKID", .title = " TSKID ", .description = "Task ID", .flags = 0, },
[POOLID] = { .name = "POOLID", .title = " POLID ", .description = "Pool ID", .flags = 0, },
[CONTID] = { .name = "CONTID", .title = " CNTID ", .description = "Contract ID", .flags = 0, },
[LWPID] = { .name = "LWPID", .title = " LWPID ", .description = "LWP ID", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = ZONEID, .label = "ZONEID" },
{ .id = TASKID, .label = "TSKID" },
{ .id = PROJID, .label = "PRJID" },
{ .id = POOLID, .label = "POLID" },
{ .id = CONTID, .label = "CNTID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = LWPID, .label = "LWPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
SolarisProcess* SolarisProcess_new(Settings* settings) {
SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess));
Object_setClass(this, Class(SolarisProcess));
Process_init(&this->super, settings);
return this;
}
void Process_delete(Object* cast) {
SolarisProcess* sp = (SolarisProcess*) cast;
Process_done((Process*)cast);
free(sp->zname);
free(sp);
}
void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field) {
SolarisProcess* sp = (SolarisProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
// add Solaris-specific fields here
case ZONEID: xSnprintf(buffer, n, Process_pidFormat, sp->zoneid); break;
case PROJID: xSnprintf(buffer, n, Process_pidFormat, sp->projid); break;
case TASKID: xSnprintf(buffer, n, Process_pidFormat, sp->taskid); break;
case POOLID: xSnprintf(buffer, n, Process_pidFormat, sp->poolid); break;
case CONTID: xSnprintf(buffer, n, Process_pidFormat, sp->contid); break;
case ZONE:{
xSnprintf(buffer, n, "%-*s ", ZONENAME_MAX/4, sp->zname); break;
if (buffer[ZONENAME_MAX/4] != '\0') {
buffer[ZONENAME_MAX/4] = ' ';
buffer[(ZONENAME_MAX/4)+1] = '\0';
}
break;
}
case PID: xSnprintf(buffer, n, Process_pidFormat, sp->realpid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, sp->realppid); break;
case LWPID: xSnprintf(buffer, n, Process_pidFormat, sp->lwpid); break;
default:
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
}
long SolarisProcess_compare(const void* v1, const void* v2) {
SolarisProcess *p1, *p2;
Settings* settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
p1 = (SolarisProcess*)v1;
p2 = (SolarisProcess*)v2;
} else {
p2 = (SolarisProcess*)v1;
p1 = (SolarisProcess*)v2;
}
switch ((int) settings->sortKey) {
case ZONEID:
return (p1->zoneid - p2->zoneid);
case PROJID:
return (p1->projid - p2->projid);
case TASKID:
return (p1->taskid - p2->taskid);
case POOLID:
return (p1->poolid - p2->poolid);
case CONTID:
return (p1->contid - p2->contid);
case ZONE:
return strcmp(p1->zname ? p1->zname : "global", p2->zname ? p2->zname : "global");
case PID:
return (p1->realpid - p2->realpid);
case PPID:
return (p1->realppid - p2->realppid);
case LWPID:
return (p1->lwpid - p2->lwpid);
default:
return Process_compare(v1, v2);
}
}
bool Process_isThread(Process* this) {
SolarisProcess* fp = (SolarisProcess*) this;
if (fp->kernel == 1 ) {
return 1;
} else if (fp->is_lwp) {
return 1;
} else {
return 0;
}
}

72
solaris/SolarisProcess.h Normal file
View File

@ -0,0 +1,72 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisProcess
#define HEADER_SolarisProcess
/*
htop - SolarisProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Settings.h"
#include <zone.h>
#include <sys/proc.h>
#include <libproc.h>
typedef enum SolarisProcessFields {
// Add platform-specific fields here, with ids >= 100
ZONEID = 100,
ZONE = 101,
PROJID = 102,
TASKID = 103,
POOLID = 104,
CONTID = 105,
LWPID = 106,
LAST_PROCESSFIELD = 107,
} SolarisProcessField;
typedef struct SolarisProcess_ {
Process super;
int kernel;
zoneid_t zoneid;
char* zname;
taskid_t taskid;
projid_t projid;
poolid_t poolid;
ctid_t contid;
bool is_lwp;
pid_t realpid;
pid_t realppid;
pid_t lwpid;
} SolarisProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
extern ProcessClass SolarisProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
SolarisProcess* SolarisProcess_new(Settings* settings);
void Process_delete(Object* cast);
void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field);
long SolarisProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -0,0 +1,373 @@
/*
htop - SolarisProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
#include "SolarisProcess.h"
#include "SolarisProcessList.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/user.h>
#include <err.h>
#include <limits.h>
#include <string.h>
#include <procfs.h>
#include <errno.h>
#include <pwd.h>
#include <math.h>
#include <time.h>
#define MAXCMDLINE 255
/*{
#include <kstat.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/sysconf.h>
#include <sys/sysinfo.h>
#include <sys/swap.h>
#define ZONE_ERRMSGLEN 1024
char zone_errmsg[ZONE_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
uint64_t luser;
uint64_t lkrnl;
uint64_t lintr;
uint64_t lidle;
} CPUData;
typedef struct SolarisProcessList_ {
ProcessList super;
kstat_ctl_t* kd;
CPUData* cpus;
} SolarisProcessList;
}*/
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) {
char* zname;
if ( sproc->zoneid == 0 ) {
zname = xStrdup("global ");
} else if ( kd == NULL ) {
zname = xStrdup("unknown ");
} else {
kstat_t* ks = kstat_lookup( kd, "zones", sproc->zoneid, NULL );
zname = xStrdup(ks->ks_name);
}
return zname;
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList));
ProcessList* pl = (ProcessList*) spl;
ProcessList_init(pl, Class(SolarisProcess), usersTable, pidWhiteList, userId);
spl->kd = kstat_open();
pl->cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
if (pl->cpuCount == 1 ) {
spl->cpus = xRealloc(spl->cpus, sizeof(CPUData));
} else {
spl->cpus = xRealloc(spl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
return pl;
}
static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) {
const SolarisProcessList* spl = (SolarisProcessList*) pl;
int cpus = pl->cpuCount;
kstat_t *cpuinfo = NULL;
int kchain = 0;
kstat_named_t *idletime = NULL;
kstat_named_t *intrtime = NULL;
kstat_named_t *krnltime = NULL;
kstat_named_t *usertime = NULL;
double idlebuf = 0;
double intrbuf = 0;
double krnlbuf = 0;
double userbuf = 0;
uint64_t totaltime = 0;
int arrskip = 0;
assert(cpus > 0);
if (cpus > 1) {
// Store values for the stats loop one extra element up in the array
// to leave room for the average to be calculated afterwards
arrskip++;
}
// Calculate per-CPU statistics first
for (int i = 0; i < cpus; i++) {
if (spl->kd != NULL) { cpuinfo = kstat_lookup(spl->kd,"cpu",i,"sys"); }
if (cpuinfo != NULL) { kchain = kstat_read(spl->kd,cpuinfo,NULL); }
if (kchain != -1 ) {
idletime = kstat_data_lookup(cpuinfo,"cpu_nsec_idle");
intrtime = kstat_data_lookup(cpuinfo,"cpu_nsec_intr");
krnltime = kstat_data_lookup(cpuinfo,"cpu_nsec_kernel");
usertime = kstat_data_lookup(cpuinfo,"cpu_nsec_user");
}
assert( (idletime != NULL) && (intrtime != NULL)
&& (krnltime != NULL) && (usertime != NULL) );
CPUData* cpuData = &(spl->cpus[i+arrskip]);
totaltime = (idletime->value.ui64 - cpuData->lidle)
+ (intrtime->value.ui64 - cpuData->lintr)
+ (krnltime->value.ui64 - cpuData->lkrnl)
+ (usertime->value.ui64 - cpuData->luser);
// Calculate percentages of deltas since last reading
cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0;
cpuData->nicePercent = (double)0.0; // Not implemented on Solaris
cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0;
cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0;
cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent;
cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0;
// Store current values to use for the next round of deltas
cpuData->luser = usertime->value.ui64;
cpuData->lkrnl = krnltime->value.ui64;
cpuData->lintr = intrtime->value.ui64;
cpuData->lidle = idletime->value.ui64;
// Accumulate the current percentages into buffers for later average calculation
if (cpus > 1) {
userbuf += cpuData->userPercent;
krnlbuf += cpuData->systemPercent;
intrbuf += cpuData->irqPercent;
idlebuf += cpuData->idlePercent;
}
}
if (cpus > 1) {
CPUData* cpuData = &(spl->cpus[0]);
cpuData->userPercent = userbuf / cpus;
cpuData->nicePercent = (double)0.0; // Not implemented on Solaris
cpuData->systemPercent = krnlbuf / cpus;
cpuData->irqPercent = intrbuf / cpus;
cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent;
cpuData->idlePercent = idlebuf / cpus;
}
}
static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
kstat_t *meminfo = NULL;
int ksrphyserr = -1;
kstat_named_t *totalmem_pgs = NULL;
kstat_named_t *lockedmem_pgs = NULL;
kstat_named_t *pages = NULL;
struct swaptable *sl = NULL;
struct swapent *swapdev = NULL;
uint64_t totalswap = 0;
uint64_t totalfree = 0;
int nswap = 0;
char *spath = NULL;
char *spathbase = NULL;
// Part 1 - physical memory
if (spl->kd != NULL) { meminfo = kstat_lookup(spl->kd,"unix",0,"system_pages"); }
if (meminfo != NULL) { ksrphyserr = kstat_read(spl->kd,meminfo,NULL); }
if (ksrphyserr != -1) {
totalmem_pgs = kstat_data_lookup( meminfo, "physmem" );
lockedmem_pgs = kstat_data_lookup( meminfo, "pageslocked" );
pages = kstat_data_lookup( meminfo, "pagestotal" );
pl->totalMem = totalmem_pgs->value.ui64 * PAGE_SIZE_KB;
pl->usedMem = lockedmem_pgs->value.ui64 * PAGE_SIZE_KB;
// Not sure how to implement this on Solaris - suggestions welcome!
pl->cachedMem = 0;
// Not really "buffers" but the best Solaris analogue that I can find to
// "memory in use but not by programs or the kernel itself"
pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * PAGE_SIZE_KB;
} else {
// Fall back to basic sysconf if kstat isn't working
pl->totalMem = sysconf(_SC_PHYS_PAGES) * PAGE_SIZE;
pl->buffersMem = 0;
pl->cachedMem = 0;
pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * PAGE_SIZE);
}
// Part 2 - swap
nswap = swapctl(SC_GETNSWP, NULL);
if (nswap > 0) { sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); }
if (sl != NULL) { spathbase = xMalloc( nswap * MAXPATHLEN ); }
if (spathbase != NULL) {
spath = spathbase;
swapdev = sl->swt_ent;
for (int i = 0; i < nswap; i++, swapdev++) {
swapdev->ste_path = spath;
spath += MAXPATHLEN;
}
sl->swt_n = nswap;
}
nswap = swapctl(SC_LIST, sl);
if (nswap > 0) {
swapdev = sl->swt_ent;
for (int i = 0; i < nswap; i++, swapdev++) {
totalswap += swapdev->ste_pages;
totalfree += swapdev->ste_free;
}
}
free(spathbase);
free(sl);
pl->totalSwap = totalswap * PAGE_SIZE_KB;
pl->usedSwap = pl->totalSwap - (totalfree * PAGE_SIZE_KB);
}
void ProcessList_delete(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
ProcessList_done(pl);
free(spl->cpus);
if (spl->kd) kstat_close(spl->kd);
free(spl);
}
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
* system for more info.
*/
int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr) {
struct timeval tv;
struct tm date;
bool preExisting;
pid_t getpid;
// Setup process list
ProcessList *pl = (ProcessList*) listptr;
SolarisProcessList *spl = (SolarisProcessList*) listptr;
id_t lwpid_real = _lwpsinfo->pr_lwpid;
if (lwpid_real > 1023) return 0;
pid_t lwpid = (_psinfo->pr_pid * 1024) + lwpid_real;
bool onMasterLWP = (_lwpsinfo->pr_lwpid == _psinfo->pr_lwp.pr_lwpid);
if (onMasterLWP) {
getpid = _psinfo->pr_pid * 1024;
} else {
getpid = lwpid;
}
Process *proc = ProcessList_getProcess(pl, getpid, &preExisting, (Process_New) SolarisProcess_new);
SolarisProcess *sproc = (SolarisProcess*) proc;
gettimeofday(&tv, NULL);
// Common code pass 1
proc->show = false;
sproc->taskid = _psinfo->pr_taskid;
sproc->projid = _psinfo->pr_projid;
sproc->poolid = _psinfo->pr_poolid;
sproc->contid = _psinfo->pr_contract;
proc->priority = _lwpsinfo->pr_pri;
proc->nice = _lwpsinfo->pr_nice;
proc->processor = _lwpsinfo->pr_onpro;
proc->state = _lwpsinfo->pr_sname;
// NOTE: This 'percentage' is a 16-bit BINARY FRACTIONS where 1.0 = 0x8000
// Source: https://docs.oracle.com/cd/E19253-01/816-5174/proc-4/index.html
// (accessed on 18 November 2017)
proc->percent_mem = ((uint16_t)_psinfo->pr_pctmem/(double)32768)*(double)100.0;
proc->st_uid = _psinfo->pr_euid;
proc->pgrp = _psinfo->pr_pgid;
proc->nlwp = _psinfo->pr_nlwp;
proc->tty_nr = _psinfo->pr_ttydev;
proc->m_resident = _psinfo->pr_rssize/PAGE_SIZE_KB;
proc->m_size = _psinfo->pr_size/PAGE_SIZE_KB;
if (!preExisting) {
sproc->realpid = _psinfo->pr_pid;
sproc->lwpid = lwpid_real;
sproc->zoneid = _psinfo->pr_zoneid;
sproc->zname = SolarisProcessList_readZoneName(spl->kd,sproc);
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
proc->comm = xStrdup(_psinfo->pr_fname);
proc->commLen = strnlen(_psinfo->pr_fname,PRFNSZ);
}
// End common code pass 1
if (onMasterLWP) { // Are we on the representative LWP?
proc->ppid = (_psinfo->pr_ppid * 1024);
proc->tgid = (_psinfo->pr_ppid * 1024);
sproc->realppid = _psinfo->pr_ppid;
// See note above (in common section) about this BINARY FRACTION
proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu/(double)32768)*(double)100.0;
proc->time = _psinfo->pr_time.tv_sec;
if(!preExisting) { // Tasks done only for NEW processes
sproc->is_lwp = false;
proc->starttime_ctime = _psinfo->pr_start.tv_sec;
}
// Update proc and thread counts based on settings
if (sproc->kernel && !pl->settings->hideKernelThreads) {
pl->kernelThreads += proc->nlwp;
pl->totalTasks += proc->nlwp+1;
if (proc->state == 'O') pl->runningTasks++;
} else if (!sproc->kernel) {
if (proc->state == 'O') pl->runningTasks++;
if (pl->settings->hideUserlandThreads) {
pl->totalTasks++;
} else {
pl->userlandThreads += proc->nlwp;
pl->totalTasks += proc->nlwp+1;
}
}
proc->show = !(pl->settings->hideKernelThreads && sproc->kernel);
} else { // We are not in the master LWP, so jump to the LWP handling code
proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu/(double)32768)*(double)100.0;
proc->time = _lwpsinfo->pr_time.tv_sec;
if (!preExisting) { // Tasks done only for NEW LWPs
sproc->is_lwp = true;
proc->basenameOffset = -1;
proc->ppid = _psinfo->pr_pid * 1024;
proc->tgid = _psinfo->pr_pid * 1024;
sproc->realppid = _psinfo->pr_pid;
proc->starttime_ctime = _lwpsinfo->pr_start.tv_sec;
}
// Top-level process only gets this for the representative LWP
if (sproc->kernel && !pl->settings->hideKernelThreads) proc->show = true;
if (!sproc->kernel && !pl->settings->hideUserlandThreads) proc->show = true;
} // Top-level LWP or subordinate LWP
// Common code pass 2
if (!preExisting) {
if ((sproc->realppid <= 0) && !(sproc->realpid <= 1)) {
sproc->kernel = true;
} else {
sproc->kernel = false;
}
(void) localtime_r((time_t*) &proc->starttime_ctime, &date);
strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date);
ProcessList_add(pl, proc);
}
proc->updated = true;
// End common code pass 2
return 0;
}
void ProcessList_goThroughEntries(ProcessList* this) {
SolarisProcessList_scanCPUTime(this);
SolarisProcessList_scanMemoryInfo(this);
this->kernelThreads = 1;
proc_walk(&SolarisProcessList_walkproc, this, PR_WALK_LWP);
}

View File

@ -0,0 +1,64 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisProcessList
#define HEADER_SolarisProcessList
/*
htop - SolarisProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define MAXCMDLINE 255
#include <kstat.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/sysconf.h>
#include <sys/sysinfo.h>
#include <sys/swap.h>
#define ZONE_ERRMSGLEN 1024
char zone_errmsg[ZONE_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
uint64_t luser;
uint64_t lkrnl;
uint64_t lintr;
uint64_t lidle;
} CPUData;
typedef struct SolarisProcessList_ {
ProcessList super;
kstat_ctl_t* kd;
CPUData* cpus;
} SolarisProcessList;
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc);
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
* system for more info.
*/
int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr);
void ProcessList_goThroughEntries(ProcessList* this);
#endif

View File

@ -29,7 +29,15 @@ const SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem); const 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 }; ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID LWPID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
ProcessFieldData Process_fields[] = { ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },

View File

@ -19,7 +19,9 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals; extern const unsigned int Platform_numberOfSignals;
extern ProcessField Platform_defaultFields[]; extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern ProcessFieldData Process_fields[]; extern ProcessFieldData Process_fields[];

View File

@ -68,8 +68,3 @@ void ProcessList_goThroughEntries(ProcessList* super) {
proc->minflt = 20; proc->minflt = 20;
proc->majflt = 20; proc->majflt = 20;
} }
void UnsupportedProcessList_scan(ProcessList* this) {
(void) this;
// stub!
}

View File

@ -17,6 +17,4 @@ void ProcessList_delete(ProcessList* this);
void ProcessList_goThroughEntries(ProcessList* super); void ProcessList_goThroughEntries(ProcessList* super);
void UnsupportedProcessList_scan(ProcessList* this);
#endif #endif