diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f2a3c565 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +free +kill +pgrep +pkill +pmap +proc/.depend +proc/libproc-3.2.8.so +ps/ps +pwdx +skill +slabtop +snice +sysctl +tload +top +uptime +vmstat +w +watch diff --git a/README.top b/README.top index 3f225d55..b6c9142a 100644 --- a/README.top +++ b/README.top @@ -1,545 +1,328 @@ -Credit for this belongs to: -Jim / James C. Warner, - ----------------------------------- - -Ok, ok, I yield -- most of what follows has been removed from the manual page -and packaged separately as this README (hey, it was only TEMPORARY insanity). - -Of course, that means that now absolutely nobody will ever read it. - -This is probably a good thing... - - -## Table of Contents ---------------------------------------------------## - # the only darn thing that wasn't in the man page - CUSTOMIZING the Sources - # the following carry their original topic numbers - DIFFERENCES / New Features - Interface Etiquette - Expanded Configurable Display Support - Enhanced Field/Column Management - Customization Flexibility - NOTES and Rantings - The top Binary - Comparing Performance - Cost of Stuff - The top Sources - EXAMPLES of Windows - The 'A' Mode Command Toggle - STACKIN' & WHACKIN' Windows - ALL TOGETHER Now, Window(s) - - -## CUSTOMIZING the Sources ---------------------------------------------## - -Listed below are the conditionals available should you wish to recompile -this top. The author's favorite is: PRETEND4CPUS. - -That's the #define allowing you to simulate an SMP environment, and -(perhaps) impress your friends. It's currently set to display four -separate CPUs, but could easily be changed. - - Caution: do NOT use this provision in an effort to impress someone - who truly possesses such a machine! The fact that all 4 - CPUs show the same dynamic results will likely have the - opposite effect. - - -//#define ATEOJ_REPORT /* report a bunch of stuff, at end-of-job */ -//#define CASEUP_HEXES /* show any hex values in upper case */ -//#define CASEUP_SCALE /* show scaled time/num suffix upper case */ -//#define CASEUP_SUMMK /* show memory summary kilobytes with 'K' */ -//#define POSIX_CMDLIN /* use '[ ]' for kernel threads, not '( )' */ -//#define PRETEND2_5_X /* pretend we're linux 2.5.x (for IO-wait) */ -//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */ -//#define PRETENDNOCAP /* use a terminal without essential caps */ -//#define SORT_SUPRESS /* *attempt* to reduce qsort overhead */ -//#define STDOUT_IOLBF /* disable our own stdout _IOFBF override */ -//#define USE_LIB_STA3 /* use lib status (3 ch) vs. proc_t (1 ch) */ -//#define WARN_NOT_SMP /* restrict '1' & 'I' commands to true smp */ - - -## 6. DIFFERENCES / New Features ---------------------------------------## - The following summarizes differences between this top and your - former top. It was originally based on procps-2.0.7. However, - except for the separate/summary CPU toggle, all of these differ- - ences also apply through procps-2.0.10. - - 6a. Interface Etiquette - -*- Input and output are far more carefully implemented in - this top. You won't be subjected to 4 - 5 'Unknown command' - messages should you press the wrong key. - - -*- You need suffer a confirmation message only when the results - of a command are not obvious by their effects on the display. - - -*- The Help screen will no longer overflow, even when running - with a 24 row xterm (vt100). - - -*- The fields selection/ordering screens do not carelessly - destroy important information through unintended line wraps. - - -*- Should you narrow a xterm window to less than 80 columns - while this top is running, you will not be left with an - utterly worthless, embarrassing display. - - 6b. Expanded Configurable Display Support - -*- In an SMP environment, you can choose between a summary dis- - play or you may show each cpu separately. No longer must - this choice be irrevocably made at startup. - - -*- There are new fields and with this top, any field is - selectable for sorting. Plus, your sorted column can be - instantly reversed with just a single keystroke. - - -*- You may optionally apply 2 distinct types of highlighting to - running tasks and/or sorted columns. With this top, you'll - be able to instantly spot running tasks and always know the - current sort field. - - -*- While you could continue to use the more familiar (and - boring) monochrome display, you might want to try this top's - new color display. You can even create your own unique col- - ors used in summaries, messages, headings and tasks, each of - which can be made persistent until you choose to change them. - - -*- Up to four separate windows can be displayed simultaneously, - giving you four separate ways to sort and view the tasks cur- - rently cluttering up your system. You could have one view by - pids, another by cpu usage, yet another showing memory con- - sumption. You get the idea... - - -*- Each window comes with pre-configured (but user configurable) - fields and you can size each window individually. - - -*- Virtually every one of this top's options (summaries, fields, - colors, sorted column, etc.) is separately configurable for - each of those four windows. - - Heck, you can even change a window's name, if you don't care - for top's choices. Your changes will be reflected not only - when you're in what top calls alternate-display mode but also - on his special new 'Windows' help screen. - - -*- And, [ ** Drum-Roll + Ta-Da ** ] with just one keystroke you - can quickly switch between full-screen and multiple window - modes! Or, with a different keystroke, toggle a single win- - dow Off for now, then On again later!! - - 6c. Enhanced Field/Column Management - -*- Many Field/Column names have been changed to make them more - intuitive, more self-descriptive. And with this top you - won't be fooled with field choices that are "not yet imple- - mented". - - -*- Task memory statistics are more meaningful and more accurate. - - -*- You'll finally have complete display integrity regardless of - field selections, their order or screen width. And that - means the command column no longer need be kept as the right- - most field, lest your screen turn to when all the - following columns get misaligned. - - 6d. Customization Flexibility - -*- You have complete program naming freedom with no internal - ties to a specific personal configuration file. Symbolic - links could be used to establish different configuration - files reflecting the different personalities of your cus- - tomized "tops", under whatever aliases you've used. - - Thus, you could have an alias for running top in 'Batch - mode', another for when you work from the Linux console and - maybe a third used with X-Windows. All of that, yet still - just a single binary image! - - -*- All of your configuration choices can be preserved in a per- - sonal configuration file, including any changes made on a - per-window basis. Thus, once you personalize things they - remain personalized until you decide to change them again. - This top has been completely cured of: - i-cant-remember-so-please-do-that-all-over-again - ( and again, and again ... ) - - The bottom line is this: if you save your configuration - before quitting top, upon restart the display will appear - exactly as you left it. And that means you no longer have to - keep top running until-the-end-of-time (ok, a long time - anyway), lest your customizations go bye-bye. - - -## 7. NOTES and Rantings -----------------------------------------------## - 7a. The top Binary - To whom it may (should) concern: this top, even with its vastly - expanded capabilities, is only slightly larger than the old top. - Were it not for extensive help text and additional sort callbacks, - it would be smaller. - Throw source carelessly at objectives, it will - produce equally careless machine instructions. - example: (num_pages - an_address)/1024 == duh? - kicker: document result as broken, due to elf! - ---------------------------------------------- - I know you're out there, are you getting this? - - Now, as for all those new capabilities like colors and windows and - highlighting, you'd expect this top to be the "mother of all pigs" - compared to old top -- right? - - Yea, with this top expect following piglets: - . A smaller virtual image and resident footprint - . Slightly fewer major page faults - . A large reduction in minor page faults for SMP - . The same or better response time - . The same or even less CPU costs - - Ideally any comparison of the old and new top should be against - the same libproc format (32-bit or 64-bit tics) and run in a true - or simulated SMP environment (producing separate CPU stats). This - latter requirement will coax old top into handling his own - '/proc/stat' access -- something this top always does, but with - less cost. - - 7b. Comparing Performance - Even with equivalent libraries and '/proc/stat' access, it's dif- - ficult to accurately compare tops using their own displays. - Results for these cpu-intensive programs (who frequently exceed - their time-slice) generally show a wide disparity in %CPU. This - is due to differing call patterns, kernel preemptions and the tim- - ing of process snapshots. For slightly better results, start each - program with the following commands: - ./old-top -d 0.5 - nice -n-10 ./new-top -d 0.4 - - While actually putting this top at a performance disadvantage, the - higher scheduling priority and staggered timing will periodically - yield a somewhat truer picture. You could even reverse those - roles and get similar results. - - The most consistent performance results will be obtained 'off- - line', using your shell's time pipe or the time program itself. - And even in a single processor environment or without equivalent - libraries, total cpu costs (user time + system time) are similar. - - However, this top's cpu costs ARE influenced by the capabilities - you choose to exploit, even if they don't SEEM to be reflected in - such timings. So let's examine some... - - 7c. Cost of Stuff - Colors Cost -- Nada (almost). - Once the terminfo strings are built (at and during a user's - behest) they are SAVED with each window's stuff. And while - there will be extra tty escape sequences transmitted because of - colors, it makes no difference which 'char *' is actually used. - - Highlighting Cost -- Nada (maybe), or blame it on Rio. - On second thought, let's blame it on the user. - - For row highlighting, there is only the cost of those extra tty - escape sequences (same as for colors). For column highlight- - ing, there is a fairly significant cost associated with column - transition management combined with even more tty output. - These increased costs are incurred on every task display row. - - Sooo... hey USER -- do NOT highlight COLUMNS. You shouldn't - need a constant visual reminder of your chosen sort field. - However, if you forget which field top is sorting it can serve - as a quick visual reminder. - - Windows Cost -- Nada (if just 1 window). - If more than 1 window, almost certainly NOT Nada so blame it on - reality. Colors are not an issue, but those sort fields are. - - If we could trust the user to always select the same 'c' state, - 'S' state and sort field (hey, why ya got multiple windows then - user, huh?) AND if we can trust someone to recompile top with a - #define enabled, then we could achieve 'Nada'. - - Ok, not likely, so we're gonna' be doing multiple sorts. BUT, - it may not be as bad as it sounds. Those sorts involve point- - ers only. And, that's as good as it gets ! (right Mr. N?) - - 7d. The top Sources - top.h - Unlike his predecessor, this top has a proper header file. It - contains ONLY declarations, NOT definitions. And there are - several conditionals present to help with further customiza- - tions and experimentation. All are Off by default. - - top.c - Hopefully proves that source code needn't be a disorganized, - misaligned MESS. And, WHO says a source listing shouldn't - occasionally make you SMILE? Why, top.c even does a darn good - job of following the suggestions in a document hardly anybody - seems to observe. - - the Linus Torvalds CodingStyle guidelines ... - -*- -*- -*- on indentation + etc. -*- -*- -*- - well almost all, except for those stinkin'... - - I suppose even Linus Torvalds is entitled to err now and again. - How so you say? Tabs, me' bucko, stinkin' tabs! That, plus the - simplistic position regarding indentation espoused in that other- - wise excellent document. - - -*- Rant On, and on -*- - Let's compare two approaches to the tab/indentation issue with a - small code sample using tabs then spaces. This snippet happens to - be the key to top's use of dynamic colors on many static screens, - while also ensuring screen width isn't exceeded so as to avoid - line wraps. We'll view just the first 40 columns, assuming one - wishes to occasionally provide comments to the right of actual - code (you do, don't you?). - - Then YOU decide which approach makes the most SENSE! - - Stinkin' Tabs versus Spaces: the Linus way - Hey, where'd my +----+----1----+----2----+----3----+----4+ - many code lines | while (*sub_beg) { : - up-and-gone-to? | switch (*sub_end: - | case 0: : - Gosh, wonder if | \ Tabs Induced / : - Linus expects a | case 1: : - fellow to stick | + WASTE-Lands! + case 5: : - his comments on | : - the left side?! | + Not a Living + : - | : - Ever see source | + line-of-code + : - with not enough | : - whitespace; and | / To Be Found! \ : - this is better? | default:: - | : - Oh lookie here, \ } : - there's just a hint of REAL code! ----> if (0 >= room) b: - / } /* end: while 'subtrin: - +----------------------------------------+ - - Spaces versus Stinkin' Tabs: the other way - +----+----1----+----2----+----3----+----4+ - Wow, now this is | while (*sub_beg) { : - Visible hackin'! | switch (*sub_end) { : - | case 0: : - Hmmm, wonder how | *(sub_end + 1) = '\0'; : - many programmers | case 1: case 2: case 3: case: - read those lines | case 5: case 6: case 7: case: - from the LEFT to | cap = Curwin->captab[(int: - the RIGHT? This | *sub_end = '\0'; : - "innovation" may | PUTP("%s%.*s%s", cap, roo: - possibly benefit | room -= (sub_end - sub_be: - those particular | sub_beg = ++sub_end; : - kinds of people, | break; : - you agree? Duh! | default: : - | ++sub_end; : - AND, there might | } : - even be room for | if (0 >= room) break; : - unseen comments! | } /* end: while 'subtrings' */ : - +----------------------------------------+ - - Gosh, I just don't KNOW -- it's such a TOUGH choice... - - Oh you Stinkin' Tabs: correspondence, Who-Cares; documentation, - Oh-Alright; even scripts, Well-If-You-Must. But you have NO place - within the code-space of MY C-source listing! So be gone - already!! - - In Summation... - - If you want to use tabs to the right of the code, go-for-it. - But PLEASE, not ever in the C-source code-space, thank-you- - kindly. Just use three little ol' spaces (exactly 3, no-more, - no-less) where you WOULD have stuck a stinkin' tab. - - We'll get far more READABLE files, much less WAISTED precious - horizontal space, more consistent CURSORS and on, and ON, AND - ON! Plus, without those awful *the-devil's-own-handiwork*, the - aforementioned document need NEVER speak of their EVILS again. - - - Lastly, since SPACES (not stinkin' tabs) are SO beneficial, - maybe we should use just a few more of 'em. Some of those C- - thingies are VERY sensitive -- they don't like being TOUCHED - by any other syntax element! Which ones? Why these guys: - - braces, reserved words and binary operators - ( it's the TRUTH, they told me themselves ) - - It's so EASY to keep 'em HAPPY! And lo-and-behold, the combi- - nation of thingy turns out to be a darn effective bug - repellent, too. So much so, one can actually code while - TOTALLY NUDE yet still avoid them ol' bug-bytes (sic-sic)! - step - down_from - me_punctilious - soap-box_once_again - [1 +5 +5 +5 = huh?] - - -## 4c. EXAMPLES of Windows ---------------------------------------------## - - -*- The 'A' Mode Command Toggle -*- - Here's what you'll see when you first invoke the alternate-display - mode interactive command. - - This particular display was produce on a VT100 xterm, with only 24 - rows. All four task displays are visible, but they could not be sized - the same. Available lines are parceled out in the fairest way possi- - ble so the last two task displays have an extra line each. - - Notice the 'current' window name in the summary area -- it's been - emphasized because the associated task display is visible. Since - 1:Def has a task area, the full range of interactive commands would be - at your disposal. But remember, many of those commands will apply - only to window 1:Def. - - +--------------------------------------+ - 1:Def name is bold, |1:Def - 15:46:37 up 16:25, 9 users, : - thus all commands |Tasks: 76 total, 1 running, 75 sle: - will be available. |Cpu(s): 0.7% user, 1.3% system, : - |Mem: 126588k total, 123688k used,: - |Swap: 265032k total, 8232k used,: - |______________________________________: - Tough luck windows |1__PID_USER______PR__NI_%CPU____TIME+_: - #1 & 2 - you lost | 7343 jtwm 16 0 0.9 0:00.59: - one line each -- | 7339 jtwm 9 0 0.0 0:00.02: - guess you'll just |__7337_root_______9___0__0.0___0:01.30: - have to learn how |2__PID__PPID_Command____________TIME+_: - to live with it. | 997 952 kdeinit 17:59.59: - | 1115 952 kdeinit 2:16.47: - |__1803__1116_led_______________1:55.30: - |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: - The #3 & #4 windows | 4634 12.3 15620 0 15m 860 14m : - better not gloat | 7337 11.3 14396 92 13m 36 13m : - over 1 extra line. | 923 10.6 30524 16m 13m 1120 12m : - That user could yet |___991__7.2__9492__316_9176___12_9164_: - sock 'em with the |4_UID_USER_____GROUP____TTY________PID: - 'n' command and | 43 xfs xfs ? 806: - take those lines, | 0 ykde users pts/7 5561: - plus others, away! | 0 wgnome users pts/7 5560: - | 0 root root pts/7 5325: - +--------------------------------------+ - - So, what say we start applying some of those "full range of interac- - tive commands"? - - Onward + Downward... - - -*- STACKIN' & WHACKIN' Windows -*- - Whoa, hold on mate. Someone has already whacked these windows. See, - there are no task areas for windows 1:Def and 4:Usr. Well, we can at - least retrace their steps... - - Here's what was done, after issuing the 'A' command and entering - alternate-display mode. - 1) When #1 was the 'current' window, '-' was pressed, - toggling Off the associated task display - ( if 'l t m' had been applied to its summary, too ) - ( then there'll be only a msg line when 'current' ) - 2) Then the 'w' key was struck to cycle backward, - making 4:Usr the 'current' window - (could have used 'a a a', if one likes to type) - 3) Then step #1 was repeated, and bye-bye window #4 - 4) Finally, window #2 was made the 'current' window - ( Q. how many keystrokes were used? ) - ( A. minimum of 2: 'a a' or 'w w'. ) - - +--------------------------------------+ - No 'l','t','m','1' |2:Top - 15:48:35 up 16:27, 9 users, : - commands have been |Tasks: 75 total, 1 running, 74 sle: - issued here, |Cpu(s): 2.0% user, 0.7% system, : - but... |Mem: 126588k total, 123712k used,: - |Swap: 265032k total, 8232k used,: - |______________________________________: - #2's been changed; |2__PID__PPID_Command____________TIME+_: - user applied a 'c' | 997 952 kdeinit: konsol 18:00.70: - command (when it | 1115 952 kdeinit: konsol 2:16.47: - was current) - now | 1803 1116 led tiptop.HELP 1:55.30: - shows cmd lines vs. | 923 922 X :0 1:09.60: - program names; | 973 1 klaptopdaemon 0:59.63: - still seems to be | 981 952 /usr/bin/artsd 0:48.63: - sorted on TIME+ | 987 1 kdeinit: kdeskt 0:24.34: - though |___991_____1_kdeinit:_kicker___0:04.59: - |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: - This #3 guy appears | 4634 12.3 15620 0 15m 860 14m : - to still be running | 7337 11.3 14396 92 13m 36 13m : - with the supplied | 923 10.6 30544 16m 13m 1120 12m : - defaults, but no | 991 7.2 9492 316 9176 12 9164 : - telling what damage | 7329 7.0 9036 140 8896 36 8860 : - might have been | 1115 6.9 8956 160 8796 36 8760 : - done to it's | 987 6.4 8668 524 8144 20 8124 : - summary info stuff | 1131 6.4 8268 144 8124 36 8088 : - +--------------------------------------+ - - And that's what brought us to this current state. No, wait. Oh - lordy, will you look at that -- someone has changed the name of win- - dow #2 from 'Job' to 'Top'! - - How'd they do that? Well, they just issued the 'g' interactive com- - mand, of course. That command is available whenever alternate-display - mode is active and always impacts just the 'current' window. Gosh, - you can even issue the 'g' command when 'l' has toggled Off the very - summary area line containing the window name! - - Almost Done... - - -*- ALL TOGETHER Now, Window(s) -*- - Here, the window 1:Def task display has been toggled Off but it - remains the 'current' window. Since there is no task area, many com- - mands will be restricted. However, the commands ('l', 't', 'm', '1') - affecting the summary area, as well as some other global commands - ('k', 'Z', etc.), would still be active. - - Notice that the Mem and Swap lines are not shown. This means that the - loser (oops, user) has, in fact, issued the 'm' command! Now, if you - were to cycle the 'current' window with the 'a' or 'w' commands, the - task display would remain the same (except possibly growing/shrinking - slightly) but the summary area would change periodically. - - The comments to the left of the image provide additional insights into - how things came to be. Note especially the comments for window 4:Usr - -- the one with some empty rows... - - 1:Def no highlight, +--------------------------------------+ - thus disabled cmds: |1:Def - 15:50:32 up 16:29, 9 users, : - b,i,n,u,x,y, etc. |Tasks: 75 total, 2 running, 73 sle: - & m = lost Mem/Swap |Cpu(s): 10.6% user, 0.0% system, : - |______________________________________: - 2:Job was very busy: |2__PID__PPID_Command____________TIME+_: - 'n' cmd, w/ 7 tasks | 80 1 ( khubd ) 0:00.00: - 'c' cmd, cmd line | 6 0 ( kreclaimd ) 0:00.00: - 'O' cmd, sort cmd | 9 1 ( mdrecoveryd ) 0:00.00: - 'R' cmd, sort bkwd | 11358 1 /bin/bash/ /usr 0:00.00: - 'x' cmd, hi column | 1297 1 /sbin/mingetty 0:00.00: - (when 2 WAS current) | 683 1 xinetd -stayali 0:00.00: - |___836_____1_login_--_root_____0:00.00: - 3:Mem has altered |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: - some std defaults: | 4634 12.3 15620 0 15m 860 14m : - 'y' turned Off | 7337 11.3 14396 92 13m 36 13m : - 'x' turned On | 923 10.6 30544 16m 13m 1120 12m : - (when 3 WAS current) | 991 7.2 9492 316 9176 12 9164 : - |__7329__7.0__9036__140_8896___36_8860_: - Huh? 4:Usr has some |4_UID_USER_____GROUP____TTY________PID: - blank rows! ? ? ? ? | 0 jtwm root pts/2 5561: - Aha, the 'i' command | 0 root root ? 5560: - applied (when 4 WAS | : - current); could be | : - reversed with '=', | : - when 4 IS current! +--------------------------------------+ - - Ok now, how about that 'current' window 1:Def and its unseen tasks? - At any time, you can quickly retrieve lost tasks in a number of ways: - 1) Press '-', toggling just the 'current' window - 2) Press '_', toggling all visible/invisible windows - ( 1:Def is the only window currently not shown ) - ( afterward, it'll be the only window showing! ) - * 3) Press '+', forcing all task displays to become visible - 4) Press 'A' to return to full-screen mode, - with only 1:Def tasks shown and without a window name - - Now that should be enough ways of getting a task area visible again to - satisfy almost any user, don't ya think? - - Note: Use #3 above when you've messed up your screen beyond - redemption. The four task displays will reappear, nice and even. - They will also have retained any customizations you had previously - applied, except for the 'i' (idle tasks) and 'n' (max tasks) com- - mands. - - That's It ! Piece of Cake !! Enjoy them there windows !!! +This file summarizes changes to the top program and supporting documentation +introduced on March 31, 2011. + +Contents: + DOCUMENT Changes + INTERNAL Improvements + EXTERNAL Improvements + BUGS Previously Fixed and Preserved + BUGS Newly/Nearly Fixed + BUGS/WISH-LISTS That Should Go Bye-bye + BUGS FIXED You Didn't Know You Had + OTHER Changes, Hopefully They Won't Bite You + BENCHMARKS + + +DOCUMENT Changes ========================================================= + . The entire file was cleaned up, standardized and expanded to include: + - a new section "2. SUMMARY Display" added for symmetry with Fields + - nine new fields were added to section "3a. DESCRIPTIONS of Fields" + - a new section "3b. MANAGING Fields" replaced the obsolete section + "2b. SELECTING and ORDERING Columns" + - section "5c. SCROLLING a Window" was added for that new feature + + . I don't know when the explanations for CODE and DATA were changed to + show 'virtual' memory, but I think there's a reason their alternate + names contain the word 'resident'. Thus they were changed back to + say 'physical memory'. + + . And as I indicated in a previous email, the former string identifier + 'ME' was restored as were the 'h' key/command conventions (vs. ). + + Oops, the 'h' key/command conventions remain restored, but subsequent + testing revealed problems with the .ME string identifier. Thus, it was + changed to .WE (along with the companion .Me/.We id). + + . Also previously mentioned, the 'man2html' program translates top.1 to + HTML with near perfect fidelity. I take that to mean there should be + no problems with the top.1 source on most other platforms. + + To further improve translation to HTML, several .Bd and .Ed macros + were added to preserve literal (fixed width) spacing. + + +INTERNAL Improvements ==================================================== + . The old restriction of 26 fields has been lifted. With this new-top + 100+ fields are now possible. It currently supports up to 55, of + which 35 are in use. Adding a new field is almost too easy. + + . Task row construction has been considerably improved -- both from + a programming perspective and a performance perspective. + + . The column highlighting costs for sort field visibility were + virtually eliminated. + + An optional define (USE_X_COLHDR) can be enabled to completely + eliminate any costs associated with the 'x' command toggle. + + . The management of the HST_t structures, used for %cpu calculations, + was optimized with a hashing scheme. Thus the need for a qsort then + a binary search in each frame was completely eliminated. + + An optional define can restore the former qsort/bsearch approach but + with an internal inlined binary search function offering substantially + better performance than the old top. + + . This far more capable new-top executable is no larger than old top. + + . The above combine to produce substantially improved performance + whose details are documented below under BENCHMARKS. + + +EXTERNAL Improvements ==================================================== + . Field management has been completely redesigned. It's now embodied + on a single screen where display-ability, position and sort selection + can be handled in one place -- for all windows at one time! + + This function is dependent on cursor motion keys and should a device + not have the customary arrow keys, alternatives are provided and + documented under "Operation" near the beginning of the man page. + + . The following new fields have been added: + Group Id + Minor Page Faults + Number of Threads + Process Group Id + Real User Id + Saved User Id + Saved User Name + Session Id + Tty Process Group Id + + . Scrolling keys now allow one to move the view of any window vertically + or horizontally to reveal any desired task or column. Previously, only + some tasks were viewable even with reversible, selectable sort columns. + + Each of the four windows is capable of maintaining its own scrolled + coordinates and an optional toggle ('C') displays a message aiding + navigation within the available tasks and displayable fields. + + . User interactive line oriented input now provides for true line + editing supported by these new keys: + Left/Right arrow keys, Delete key, Backspace and + Home/End keys (likely limited to xterm, not terminal) + + . User filtering via the -u | -U interactive commands is now window + based which means that different windows could be used to filter + different users. + + . Signal handling has been normalized and is now consistent regardless + of the particular top screen a user may have been using. + + . The 'i' toggle now shows any task that has used *some* cpu since the + last screen update. It's no longer limited to just running tasks. + + . The summary area 'task states' line now reflects either 'Threads' + or 'Tasks' depending on the -H toggle. + + +BUGS Previously Fixed and Preserved ====================================== + ( but not necessarily literally) + . 228822, suspending top leaves xterm in slightly messed-up state + . 256376, segfaults, if the xterm is to small + . 320289, segv on sigwinch + . 351065, wrong highlight 1st column (escape characters displayed) + . 358724, accepts extra numeric args + . 378695, seg fault if "/proc" is not mounted + . 426782, UID field is too narrow + . 458986, should check xterm for EOF/EIO + . 459890, Irix mode should use %#4.1f when threads shown + + +BUGS Newly/Nearly Fixed ================================================== + . 225542, 'Unknown command' message blocks further commands + The message is now displayed using usleep for 1.25 seconds, instead + of the former full 2 seconds. And while it still blocks further + commands, the delay is much more tolerable. + + Can we consider this bug 'nearly' fixed? + + . 410292, interface error when using backspace + Full line editing was added but could be disabled via a #define. + And via that define, even under basic termios support, the backspace + problem was cured. + + . 567509, top idle command ('i') not working for threaded programs + Since the 'i' command now reflects tasks that have used *some* cpu, + and is no longer dependent on an 'R' state, I *believe/hope* this + bug has been swatted. + + +BUGS/WISH-LISTS That Should Go Bye-bye =================================== + . 340751, wish for hostname to benefit multiple top sessions + Craig's suggestion regarding symlinks is the perfect solution. + How dare Craig say that the solution was "not ideal" ! + + . 586497, wish for graceful degradation on small screen sizes + This objective could be accomplished by setting up 2 symlinks for + top, personalizing them for the 2 tiny phone displays, then writing + the respective configuration files. + + I shudder at the programming effort suggested by Paul. And when it + was done you'd find everybody else would have different criteria. + + +BUGS FIXED You Didn't Know You Had ======================================= + . Without amplifying the dirty details, the long standing occasionally + reported display corruption, and an unreported source of performance + degradation, has been eliminated. The cure is in the elimination of + the Pseudo_cols variable and the improved PUFF macro. + + . Line oriented input was not sensitive to screen width. Thus a user + could hold down any key and ultimately line wrap, overwriting the + columns header and the entire screen. New top prevents this. + + . User filtering (-u|-U) via a user ID (not name) now validates that + number. The old-top just made sure it was numeric, then blindly + displayed no matching users (i.e. an empty window). + + . The threads toggle ('H') is no longer window based but more properly + applies to all windows. The previous implementation produced the + following aberration if multiple windows were being shown: + . -H would be acknowledged and applied to all visible windows + . keying 'a' or 'w' would silently turn it off + . then keying -H would turn it back on, but the user expected off + + . If you hit ^Z on any help or fields screen to suspend old-top, after + issuing 'fg' you would then be left with a seemingly hung application + inviting ^C. In truth, one could recover with the space bar, but that + was far from intuitive. + + . The old-top consistently writes 1 extra byte for each task row or 1 + byte too few for columns headers, depending on your perspective. + The new top writes the same number of bytes for each. + + . By failing to clear to eol, old top left the display in a terrible + state after exiting a 'fields' screen when only a few columns were + being displayed. + + . The old-top used a zero value for the L_NONE library flag which could + cause repeated rebuilding of columns headers with each frame. In truth, + this was not likely to happen in real life since only two fields actually + used that flag. However, if it did happen, performance could be degraded + by 800%. + + +OTHER Changes, Hopefully They Won't Bite You ============================= + . The undocumented TOPRC environment variable is no longer supported. + Any similar need can be met through a symlink alias. + + . The use of environment variables to override terminal size is now + off by default but could be enabled through '#define TTYGETENVYES'. + + . The global 'bold enable' toggle is active by default and thus agrees + with the documentation. It's been wrong ever since Al's wholesale + 'cosmetic' changes in procps-3.2.2. + + . Task defaults now show bold (not reverse) and row highlighting. + This agrees with what was always stated in the documentation. + + . The 'H' toggle (thread mode) is not persistent. Persistence can be + achieved with a simple shell script employing the -H switch. + + . Then 'g' and 'G' commands were reversed to reflect their likely use. + + +BENCHMARKS =============================================================== + Tested as root with nice -10 and using only common fields + ( on a pretty old, slow laptop under Debian Lenny ) + but rcfiles specified identical sort fields and identical + settings for the 'B', 'b', 'x' and 'y' toggles (even though + the defaults are not necessarily identical). + + In every case new-top outperforms old-top, but I've shown % + improvements for only the most significant. Those cases mostly + involve colors with both row & column highlighting. I suggested + above that the highlighting cost was virtually eliminated in + new-top, and these tests bare that out. + + Note the much smaller differences for new-top between the 24x80 + window results and full screen (but don't mix apples_terminal + with oranges_xterm). This is a reflection of the simplification + of task row construction, also mentioned above. + + It's always been the case that any top in an xterm outperforms + that top under the terminal application, even when the xterm + provides additional rows and columns. It's true below with + Gnome and it was true nine years ago under KDE. + + ---------------------------------------------------------- + The following comparisons were run with: + 100 tasks & 160 threads + -d0 -n5000 + new-top old-top + xterm 24x80 + a 1 win, lflgs_none 11.2 secs 51.8 secs + 462.6% + 1 win, default 61.0 secs 66.8 secs + 1 win, colors w/ x+y 61.3 secs 83.0 secs + 135.4% + 1 win, thread mode 88.3 secs 94.2 secs + b 1 win, every field on 99.7 secs 106.0 secs + 1 win, cmdline 71.2 secs 76.6 secs + 4 wins, defaults 101.3 secs 107.2 secs + 4 wins, colors w/ x+y 101.5 secs 122.8 secs + 121.0% + + xterm, full screen (53x170) + a 1 win, lflgs_none 15.9 secs 54.2 secs + 340.9% + 1 win, default 70.0 secs 73.2 secs + 1 win, colors w/ x+y 69.4 secs 131.3 secs + 189.2% + 1 win, thread mode 97.6 secs 102.6 secs + c 1 win, every field on 122.1 secs 128.1 secs + 1 win, cmdline 80.8 secs 83.7 secs + 4 wins, defaults 111.4 secs 115.8 secs + 4 wins, colors w/ x+y 112.0 secs 172.9 secs + 154.4% + + terminal 24x80 + a 1 win, lflgs_none 8.9 secs 58.6 secs + 658.4% + 1 win, default 70.1 secs 80.3 secs + 1 win, colors w/ x+y 70.6 secs 157.3 secs + 222.8% + 1 win, thread mode 104.7 secs 120.5 secs + b 1 win, every field on 111.2 secs 134.5 secs + 1 win, cmdline 83.8 secs 94.5 secs + 4 wins, defaults 125.6 secs 146.7 secs + 4 wins, colors w/ x+y 125.6 secs 206.9 secs + 176.7% + + terminal, full screen (39x125) + a 1 win, lflgs_none 9.1 secs 60.6 secs + 665.9% + 1 win, default 74.3 secs 88.0 secs + 1 win, colors w/ x+y 73.9 secs 314.5 secs + 425.6% + 1 win, thread mode 113.0 secs 140.9 secs + b 1 win, every field on 117.7 secs 154.9 secs + 1 win, cmdline 87.4 secs 107.2 secs + 4 wins, defaults 139.1 secs 166.7 secs + 4 wins, colors w/ x+y 157.3 secs 423.2 secs + 269.0% + + ---------------------------------------------------------- + The following comarisons were run with: + 300 tasks & 360 threads + -d0 -n3000 + new-top old-top + xterm, full screen (53x170) + a 1 win, lflgs_none 14.3 secs 79.0 secs + 552.4% + 1 win, default 101.1 secs 104.5 secs + 1 win, colors w/ x+y 101.3 secs 140.0 secs + 138.2% + 1 win, thread mode 120.1 secs 123.1 secs + c 1 win, every field on 179.8 secs 185.6 secs + 1 win, cmdline 124.9 secs 132.8 secs + 4 wins, defaults 174.8 secs 179.2 secs + 4 wins, colors w/ x+y 175.0 secs 215.2 secs + 123.0% + + terminal, full screen (39x125) + a 1 win, lflgs_none 12.3 secs 98.5 secs + 800.8% + 1 win, default 117.4 secs 134.0 secs + 1 win, colors w/ x+y 111.6 secs 296.1 secs + 265.3% + 1 win, thread mode 141.3 secs 155.3 secs + b 1 win, every field on 197.7 secs 204.8 secs + 1 win, cmdline 143.9 secs 157.3 secs + 4 wins, defaults 204.0 secs 226.2 secs + 4 wins, colors w/ x+y 216.9 secs 434.5 secs + 200.3% + + . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + +notes: + a these results represent the library flags L_NONE zero value and + thus the hidden cost of rebuilding column headers w/ every frame + b while every common field was turned on, not all fields could be + displayed due to limited screen width + c only in a full screen xterm window could all common fields + actually be displayed diff --git a/proc/escape.c b/proc/escape.c index 0eb1418b..92ba4b19 100644 --- a/proc/escape.c +++ b/proc/escape.c @@ -50,13 +50,6 @@ static int escape_str_utf8(char *restrict dst, const char *restrict src, int buf my_cells++; my_bytes++; - } else if (len==1) { - /* non-multibyte */ - *(dst++) = isprint(*src) ? *src : '?'; - src++; - my_cells++; - my_bytes++; - } else if (!iswprint(wc)) { /* multibyte - no printable */ *(dst++) = '?'; @@ -98,7 +91,7 @@ static int escape_str_utf8(char *restrict dst, const char *restrict src, int buf } //fprintf(stdout, "cells: %d\n", my_cells); } - *(dst++) = '\0'; + *dst = '\0'; // fprintf(stderr, "maxcells: %d, my_cells; %d\n", *maxcells, my_cells); @@ -114,14 +107,14 @@ int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *m int my_cells = 0; int my_bytes = 0; const char codes[] = - "Z-------------------------------" - "********************************" - "********************************" - "*******************************-" - "--------------------------------" - "********************************" - "********************************" - "********************************"; + "Z..............................." + "||||||||||||||||||||||||||||||||" + "||||||||||||||||||||||||||||||||" + "|||||||||||||||||||||||||||||||." + "????????????????????????????????" + "????????????????????????????????" + "????????????????????????????????" + "????????????????????????????????"; #if (__GNU_LIBRARY__ >= 6) static int utf_init=0; @@ -131,9 +124,10 @@ int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *m char *enc = nl_langinfo(CODESET); utf_init = enc && strcasecmp(enc, "UTF-8")==0 ? 1 : -1; } - if (utf_init==1) + if (utf_init==1 && MB_CUR_MAX>1) { /* UTF8 locales */ return escape_str_utf8(dst, src, bufsize, maxcells); + } #endif if(bufsize > *maxcells+1) bufsize=*maxcells+1; // FIXME: assumes 8-bit locale @@ -143,12 +137,12 @@ int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *m break; c = (unsigned char) *(src++); if(!c) break; - if(codes[c]=='-') c='?'; + if(codes[c]!='|') c=codes[c]; my_cells++; my_bytes++; *(dst++) = c; } - *(dst++) = '\0'; + *dst = '\0'; *maxcells -= my_cells; return my_bytes; // bytes of text, excluding the NUL @@ -214,3 +208,16 @@ int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, outbuf[end] = '\0'; return end; // bytes, not including the NUL } + +///////////////////////////////////////////////// + +// copy an already 'escaped' string, +// using the traditional escape.h calling conventions +int escaped_copy(char *restrict dst, const char *restrict src, int bufsize, int *maxroom){ + int n; + if (bufsize > *maxroom+1) bufsize = *maxroom+1; + n = snprintf(dst, bufsize, "%s", src); + if (n >= bufsize) n = bufsize-1; + *maxroom -= n; + return n; +} diff --git a/proc/escape.h b/proc/escape.h index 172960f5..aa9f6d64 100644 --- a/proc/escape.h +++ b/proc/escape.h @@ -17,6 +17,7 @@ EXTERN_C_BEGIN extern int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t n, int *cells); extern int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells); extern int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags); +extern int escaped_copy(char *restrict dst, const char *restrict src, int bufsize, int *maxroom); EXTERN_C_END #endif diff --git a/proc/library.map b/proc/library.map index b438849e..032ada0c 100644 --- a/proc/library.map +++ b/proc/library.map @@ -6,7 +6,7 @@ global: __cyg_profile_func_enter; __cyg_profile_func_exit; main; readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command; - escape_str; escape_strlist; + escape_str; escape_strlist; escaped_copy; read_cmdline; openproc; closeproc; tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan; display_version; procps_version; linux_version_code; diff --git a/proc/readproc.c b/proc/readproc.c index 890e77a7..b96d0a45 100644 --- a/proc/readproc.c +++ b/proc/readproc.c @@ -12,6 +12,7 @@ #include "version.h" #include "readproc.h" #include "alloc.h" +#include "escape.h" #include "pwcache.h" #include "devname.h" #include "procps.h" @@ -365,6 +366,19 @@ LEAVE(0x220); } /////////////////////////////////////////////////////////////////////// +#ifdef OOMEM_ENABLE +static void oomscore2proc(const char* S, proc_t *restrict P) +{ + sscanf(S, "%d", &P->oom_score); +} + +static void oomadj2proc(const char* S, proc_t *restrict P) +{ + sscanf(S, "%d", &P->oom_adj); +} +#endif +/////////////////////////////////////////////////////////////////////// + // Reads /proc/*/stat files, being careful not to trip over processes with // names like ":-) 1 2 3 4 5 6". @@ -514,13 +528,15 @@ static char** file2strvec(const char* directory, const char* what) { return ret; } -// warning: interface may change -int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){ + // this is the former under utilized 'read_cmdline', which has been + // generalized in support of these new libproc flags: + // PROC_EDITCGRPCVT, PROC_EDITCMDLCVT +static int read_unvectored(char *restrict const dst, unsigned sz, unsigned pid, const char *what, char sep) { char name[32]; int fd; unsigned n = 0; - dst[0] = '\0'; - snprintf(name, sizeof name, "/proc/%u/cmdline", pid); + + snprintf(name, sizeof name, "/proc/%u/%s", pid, what); fd = open(name, O_RDONLY); if(fd==-1) return 0; for(;;){ @@ -530,23 +546,86 @@ int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){ break; } n += r; - if(n==sz) break; // filled the buffer + if(n==sz) { // filled the buffer + --n; // make room for '\0' + break; + } if(r==0) break; // EOF } close(fd); if(n){ - int i; - if(n==sz) n--; - dst[n] = '\0'; - i=n; - while(i--){ - int c = dst[i]; - if(c<' ' || c>'~') dst[i]=' '; - } + int i=n; + while(i--) + if(dst[i]=='\n' || dst[i]=='\0') dst[i]=sep; } + dst[n] = '\0'; return n; } +static char** vectorize_this_str (const char* src) { + #define pSZ (sizeof(char*)) + char *cpy, **vec; + int adj, tot; + + tot = strlen(src) + 1; // prep for our vectors + adj = (pSZ-1) - ((tot + pSZ-1) & (pSZ-1)); // calc alignment bytes + cpy = xcalloc(NULL, tot + adj + (2 * pSZ)); // get new larger buffer + snprintf(cpy, tot, "%s", src); // duplicate their string + vec = (char**)(cpy + tot + adj); // prep pointer to pointers + *vec = cpy; // point 1st vector to string + *(vec+1) = NULL; // null ptr 'list' delimit + return vec; // ==> free(*vec) to dealloc + #undef pSZ +} + + // This routine reads /proc/#/cgroup for a single task. + // It is similar to file2strvec except we filter and concatenate + // the data into a single string represented as a single vector. +static void fill_cgroup_cvt (proc_t *restrict p) { + #define vMAX ( sizeof(dbuf) - (int)(dst - dbuf) ) + char sbuf[1024], dbuf[1024]; + char *src, *dst, *grp, *eob; + int tot, x, whackable_int = sizeof(dbuf); + + *(dst = dbuf) = '\0'; // empty destination + tot = read_unvectored(sbuf, sizeof(sbuf), p->tid, "cgroup", '\0'); + for (src = sbuf, eob = sbuf + tot; src < eob; src += x) { + x = 1; // loop assist + if (!*src) continue; + x = strlen((grp = src)); + if ('/' == grp[x - 1]) continue; // skip empty root cgroups +#if 0 + grp += strspn(grp, "0123456789:"); // jump past group number +#endif + dst += snprintf(dst, vMAX, "%s", (dst > dbuf) ? "," : ""); + dst += escape_str(dst, grp, vMAX, &whackable_int); + } + p->cgroup = vectorize_this_str(dbuf[0] ? dbuf : "-"); + #undef vMAX +} + + // This routine reads /proc/#/cmdline for the designated task, "escapes" + // the result into a single string represented as a single vector and + // guarantees the caller a valid proc_t.cmdline pointer. +static void fill_cmdline_cvt (proc_t *restrict p) { + #define uFLG ( ESC_BRACKETS | ESC_DEFUNCT ) + char sbuf[2048], dbuf[2048]; + int whackable_int = sizeof(dbuf); + + if (read_unvectored(sbuf, sizeof(sbuf), p->tid, "cmdline", ' ')) + escape_str(dbuf, sbuf, sizeof(dbuf), &whackable_int); + else + escape_command(dbuf, p, sizeof(dbuf), &whackable_int, uFLG); + p->cmdline = vectorize_this_str(dbuf); + #undef uFLG +} + +// warning: interface may change +int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid) { + return read_unvectored(dst, sz, pid, "cmdline", ' '); +} + + /* These are some nice GNU C expression subscope "inline" functions. * The can be used with arbitrary types and evaluate their arguments * exactly once. @@ -628,21 +707,37 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons } } - if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */ - p->cmdline = file2strvec(path, "cmdline"); - else - p->cmdline = NULL; - - if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */ - p->environ = file2strvec(path, "environ"); + if (unlikely(flags & PROC_FILLENV)) /* read /proc/#/environ */ + p->environ = file2strvec(path, "environ"); else p->environ = NULL; - if(linux_version_code>=LINUX_VERSION(2,6,24) && (flags & PROC_FILLCGROUP)) - p->cgroup = file2strvec(path, "cgroup"); /* read /proc/#/cgroup */ - else - p->cgroup = NULL; - + if (flags & (PROC_FILLCOM|PROC_FILLARG)) { /* read /proc/#/cmdline */ + if (flags & PROC_EDITCMDLCVT) + fill_cmdline_cvt(p); + else + p->cmdline = file2strvec(path, "cmdline"); + } else + p->cmdline = NULL; + + if ((flags & PROC_FILLCGROUP) /* read /proc/#/cgroup, if possible */ + && linux_version_code >= LINUX_VERSION(2,6,24)) { + if (flags & PROC_EDITCGRPCVT) + fill_cgroup_cvt(p); + else + p->cgroup = file2strvec(path, "cgroup"); + } else + p->cgroup = NULL; + +#ifdef OOMEM_ENABLE + if (unlikely(flags & PROC_FILLOOM)) { + if (likely( file2str(path, "oom_score", sbuf, sizeof sbuf) != -1 )) + oomscore2proc(sbuf, p); + if (likely( file2str(path, "oom_adj", sbuf, sizeof sbuf) != -1 )) + oomadj2proc(sbuf, p); + } +#endif + return p; next_proc: return NULL; diff --git a/proc/readproc.h b/proc/readproc.h index 0b2c46a7..b8d97bb3 100644 --- a/proc/readproc.h +++ b/proc/readproc.h @@ -111,8 +111,9 @@ typedef struct proc_t { cmin_flt, // stat cumulative min_flt of process and child processes cmaj_flt; // stat cumulative maj_flt of process and child processes char - **environ, // (special) environment string vector (/proc/#/environ) - **cmdline; // (special) command line string vector (/proc/#/cmdline) + **environ, // (special) environment string vector (/proc/#/environ) + **cmdline, // (special) command line string vector (/proc/#/cmdline) + **cgroup; // (special) cgroup string vector (/proc/#/cgroup) char // Be compatible: Digital allows 16 and NT allows 14 ??? euser[P_G_SZ], // stat(),status effective user name @@ -140,7 +141,11 @@ typedef struct proc_t { tpgid, // stat terminal process group id exit_signal, // stat might not be SIGCHLD processor; // stat current (or most recent?) CPU - char **cgroup; // cgroup current cgroup, looks like a classic filepath +#ifdef OOMEM_ENABLE + int + oom_score, // oom_score (badness for OOM killer) + oom_adj; // oom_adj (adjustment to OOM score) +#endif } proc_t; // PROCTAB: data structure holding the persistent information readproc needs @@ -239,6 +244,7 @@ extern proc_t * get_proc_stats(pid_t pid, proc_t *p); #define PROC_FILLWCHAN 0x0080 // look up WCHAN name #define PROC_FILLARG 0x0100 // alloc and fill in `cmdline' #define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup` +#define PROC_FILLOOM 0x0400 // alloc and fill in oom_score, oom_adj #define PROC_LOOSE_TASKS 0x2000 // threat threads as if they were processes @@ -246,6 +252,9 @@ extern proc_t * get_proc_stats(pid_t pid, proc_t *p); #define PROC_PID 0x1000 // process id numbers ( 0 terminated) #define PROC_UID 0x4000 // user id numbers ( length needed ) +#define PROC_EDITCGRPCVT 0x10000 // edit `cgroup' as single vector +#define PROC_EDITCMDLCVT 0x20000 // edit `cmdline' as single vector + // it helps to give app code a few spare bits #define PROC_SPARE_1 0x01000000 #define PROC_SPARE_2 0x02000000 diff --git a/proc/sysinfo.c b/proc/sysinfo.c index 34f70ec6..952faf41 100644 --- a/proc/sysinfo.c +++ b/proc/sysinfo.c @@ -24,7 +24,9 @@ #include /* htons */ #endif +#ifndef OOMEM_ENABLE long smp_num_cpus; /* number of CPUs */ +#endif #define BAD_OPEN_MESSAGE \ "Error: /proc must be mounted\n" \ @@ -180,7 +182,11 @@ static void old_Hertz_hack(void){ setlocale(LC_NUMERIC, savelocale); jiffies = user_j + nice_j + sys_j + other_j; seconds = (up_1 + up_2) / 2; +#ifndef OOMEM_ENABLE h = (unsigned)( (double)jiffies/seconds/smp_num_cpus ); +#else + h = (unsigned)( (double)jiffies/seconds/smp_num_cpus() ); +#endif /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ switch(h){ case 9 ... 11 : Hertz = 10; break; /* S/390 (sometimes) */ @@ -246,10 +252,34 @@ static int check_for_privs(void){ return !!rc; } +#ifdef OOMEM_ENABLE +long smp_num_cpus(void) +{ + static long _smp_num_cpus=-1; /* number of CPUs */ + + if (_smp_num_cpus != -1) + return(_smp_num_cpus); + + // ought to count CPUs in /proc/stat instead of relying + // on glibc, which foolishly tries to parse /proc/cpuinfo + // + // SourceForge has an old Alpha running Linux 2.2.20 that + // appears to have a non-SMP kernel on a 2-way SMP box. + // _SC_NPROCESSORS_CONF returns 2, resulting in HZ=512 + // _SC_NPROCESSORS_ONLN returns 1, which should work OK + + _smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if(_smp_num_cpus<1) _smp_num_cpus=1; /* SPARC glibc is buggy */ + + return(_smp_num_cpus); +} +#endif + static void init_libproc(void) __attribute__((constructor)); static void init_libproc(void){ have_privs = check_for_privs(); init_Linux_version(); /* Must be called before we check code */ +#ifndef OOMEM_ENABLE // ought to count CPUs in /proc/stat instead of relying // on glibc, which foolishly tries to parse /proc/cpuinfo // @@ -259,7 +289,7 @@ static void init_libproc(void){ // _SC_NPROCESSORS_ONLN returns 1, which should work OK smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); if(smp_num_cpus<1) smp_num_cpus=1; /* SPARC glibc is buggy */ - +#endif if(linux_version_code > LINUX_VERSION(2, 4, 0)){ Hertz = find_elf_note(AT_CLKTCK); if(Hertz!=NOTE_NOT_FOUND) return; diff --git a/proc/sysinfo.h b/proc/sysinfo.h index 97ae818f..744f2805 100644 --- a/proc/sysinfo.h +++ b/proc/sysinfo.h @@ -7,7 +7,11 @@ EXTERN_C_BEGIN extern unsigned long long Hertz; /* clock tick frequency */ +#ifndef OOMEM_ENABLE extern long smp_num_cpus; /* number of CPUs */ +#else +extern long smp_num_cpus(void); /* number of CPUs */ +#endif extern int have_privs; /* boolean, true if setuid or similar */ #if 0 diff --git a/ps/display.c b/ps/display.c index 3d6bbded..035a8840 100644 --- a/ps/display.c +++ b/ps/display.c @@ -224,6 +224,7 @@ static unsigned task_format_needs; #define needs_for_format (proc_format_needs|task_format_needs) #define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM|PROC_FILLCGROUP) + /***** munge lists and determine openproc() flags */ static void lists_and_needs(void){ check_headers(); @@ -283,11 +284,12 @@ static void lists_and_needs(void){ } if(!unix_f_option){ proc_format_needs &= ~PROC_FILLCOM; + proc_format_needs |= PROC_EDITCMDLCVT; needs_for_sort &= ~PROC_FILLCOM; } // convert ARG to COM as a standard if(proc_format_needs & PROC_FILLARG){ - proc_format_needs |= PROC_FILLCOM; + proc_format_needs |= (PROC_FILLCOM | PROC_EDITCMDLCVT); proc_format_needs &= ~PROC_FILLARG; } if(bsd_e_option){ diff --git a/ps/output.c b/ps/output.c index 8fbc488e..6063c903 100644 --- a/ps/output.c +++ b/ps/output.c @@ -331,8 +331,11 @@ Modifications to the arguments are not shown. // FIXME: some of these may hit the guard page in forest mode -/* "command" is the same thing: long unless c */ -static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){ +/* + * "args", "cmd", "command" are all the same: long unless c + * "comm", "ucmd", "ucomm" are all the same: short unless -f + * ( determinations are made in display.c, we just deal with results ) */ +static int pr_argcom(char *restrict const outbuf, const proc_t *restrict const pp){ char *endp = outbuf; unsigned flags; int rightward=max_rightward; @@ -342,84 +345,30 @@ static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp) endp += fh; rightward -= fh; } - if(bsd_c_option) flags = ESC_DEFUNCT; - else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; - endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags); + if(pp->cmdline) + endp += escaped_copy(endp, *pp->cmdline, OUTBUF_SIZE, &rightward); + else + endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, ESC_DEFUNCT); - if(bsd_e_option && rightward>1){ - const char **env = (const char**)pp->environ; - if(env && *env){ - *endp++ = ' '; - rightward--; - endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward); - } + if(bsd_e_option && rightward>1) { + if(pp->environ && *pp->environ) + endp += escape_strlist(endp, pp->environ, OUTBUF_SIZE, &rightward); } //return endp - outbuf; return max_rightward-rightward; } static int pr_cgroup(char *restrict const outbuf,const proc_t *restrict const pp) { - char *endp = outbuf; int rightward = max_rightward; if(pp->cgroup) { - char **pcgroup = pp->cgroup; - - while(*pcgroup != NULL) { - //Skip root cgroups - if(!**pcgroup || (*pcgroup)[strlen(*pcgroup)-1] == '/') { - pcgroup++; - continue; - } - - //Skip initial cgroup number - char *ccgroup = strchr(*pcgroup, ':'); - if(ccgroup == NULL) - ccgroup = *pcgroup; - else - ccgroup++; - - if(endp != outbuf) - endp += escape_str(endp, ";", OUTBUF_SIZE, &rightward); - - endp += escape_str(endp, ccgroup, OUTBUF_SIZE, &rightward); - - pcgroup++; - } + escaped_copy(outbuf, *pp->cgroup, OUTBUF_SIZE, &rightward); + return max_rightward-rightward; } - - if(endp == outbuf) + else return pr_nop(outbuf,pp); - - return (int)(endp-outbuf); } -/* "ucomm" is the same thing: short unless -f */ -static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){ - char *endp = outbuf; - unsigned flags; - int rightward=max_rightward; - - if(forest_prefix){ - int fh = forest_helper(outbuf); - endp += fh; - rightward -= fh; - } - if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; - else flags = ESC_DEFUNCT; - endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags); - - if(bsd_e_option && rightward>1){ - const char **env = (const char**)pp->environ; - if(env && *env){ - *endp++ = ' '; - rightward--; - endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward); - } - } - //return endp - outbuf; - return max_rightward-rightward; -} /* Non-standard, from SunOS 5 */ static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){ char *endp = outbuf; @@ -1292,7 +1241,7 @@ static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const #define GRP PROC_FILLGRP /* gid_t -> group names */ #define WCH PROC_FILLWCHAN /* do WCHAN lookup */ -#define CGRP PROC_FILLCGROUP /* read cgroup */ +#define CGRP PROC_FILLCGROUP | PROC_EDITCGRPCVT /* read cgroup */ /* TODO * pull out annoying BSD aliases into another table (to macro table?) * add sorting functions here (to unify names) @@ -1320,7 +1269,7 @@ static const format_struct format_array[] = { {"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT}, {"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT}, {"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT}, -{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/ +{"args", "COMMAND", pr_argcom, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/ {"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */ {"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/ {"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT}, @@ -1332,11 +1281,11 @@ static const format_struct format_array[] = { {"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT}, {"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/ {"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT}, -{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/ +{"cmd", "CMD", pr_argcom, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/ {"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT}, {"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT}, -{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/ -{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/ +{"comm", "COMMAND", pr_argcom, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/ +{"command", "COMMAND", pr_argcom, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/ {"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT}, {"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/ {"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */ @@ -1516,8 +1465,8 @@ static const format_struct format_array[] = { {"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT}, {"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT}, {"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, -{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/ -{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/ +{"ucmd", "CMD", pr_argcom, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/ +{"ucomm", "COMMAND", pr_argcom, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/ {"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT}, {"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER}, {"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, diff --git a/top.1 b/top.1 index 1f998ccb..9d466e47 100644 --- a/top.1 +++ b/top.1 @@ -1,6 +1,9 @@ .ig -. manual page for NEW top -. Copyright (c) 2002, by: JC Warner & Associates, Ltd. +. manual page for NEW and IMPROVED linux top +. +. Copyright (c) 2002-2011, by: James C. Warner +. All rights reserved. 8921 Hilloway Road +. Eden Prairie, Minnesota 55347 USA . . Permission is granted to copy, distribute and/or modify this document . under the terms of the GNU Free Documentation License, Version 1.1 or @@ -18,42 +21,44 @@ . [ can be made printable by disabling the .ig directive! ] . .. -.\" Setup //////////////////////////////////////////////////////////////// +\# Setup //////////////////////////////////////////////////////////////// \# ** Comment out '.nr' or set to 0 to eliminate WIDTH fiddlin' ! .nr half_xtra 4 . .ll +(\n[half_xtra] + \n[half_xtra]) . -\# Our darn Bullet style ---------------------------- -.de Jbu -.IP "-" 3 -.. -\# - bullet continuation paragraph -.de Jp -.IP "" 3 -.. -\# New features/differences style ------------------- -.de New -.IP "-*-" 5 +\# ** we use single quote char (') alot, so change the no-break ctrl char +.c2 ` +. +\# Our own Bullet style(s) -------------------------- +.de jBu +.IP "o" 3 .. . \# Commonly used strings (for consistency) ---------- -\# - a real em-dash, darn-it -.ds EM \ \fB\-\-\ \fR -\# - these two are for chuckles, makes great grammar -.ds Me top -.ds NE \fBtop\fR -\# - other misc strings for consistent usage/emphasis +\# - our em-dashes +.ds Em \fR\ \--\ \fR +.ds EM \fB\ \--\ \fR +\# - our program name (makes great grammer) +.ds We top +.ds WE \fBtop\fR +\# - other misc strs for consistent usage .ds F \fIOff\fR .ds O \fIOn\fR . +.ds AK asterisk (\'*\') .ds AM alternate\-display mode -.ds AS asterisk ('*') +.ds AS auxiliary storage .ds CF configuration file +.ds CG \'current\' window/field group .ds CI interactive command .ds CO command\-line option -.ds CW \'current' window +.ds CT command toggle +.ds CW \'current\' window +.ds FG field group .ds FM full\-screen mode +.ds KA arrow key +.ds KS scrolling key .ds MP \fBphysical\fR memory .ds MS \fBshared\fR memory .ds MV \fBvirtual\fR memory @@ -63,30 +68,39 @@ .ds SA summary area .ds TA task area .ds TD task display +.ds TT \fBtasks\fR or\fB threads\fR .ds TW task window -\# - xref's that depend on commands or topic names +\# Reference to the various widths/sizes ------------ +\# - the max screen width limit +.ds WX 512 +\# - the header width w/ all fields +.ds WF approximately 200 +\# - max room for the command column +.ds WC 300+ +\# - pid monitoring limit +.ds WP 20 +\# Xref's that depend on/mention other stuff -------- +.ds Xa see .ds XC See the .ds Xc see the .ds XT See topic .ds Xt see topic . -.\" ////////////////////////////////////////////////////////////////////// +.\" Document ///////////////////////////////////////////////////////////// .\" ---------------------------------------------------------------------- -.TH TOP 1 "September 2002" "Linux" "Linux User's Manual" +.TH TOP 1 "April 2011" "Linux" "Linux User's Manual" .\" ---------------------------------------------------------------------- - .\" ---------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------- -top \- display Linux tasks - +\*(We \- display Linux processes .\" ---------------------------------------------------------------------- .SH SYNOPSIS .\" ---------------------------------------------------------------------- -\*(NE \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI -iterations\fR \-\fBp\fI pid\fR [,\fI pid\fR ...] +\*(WE \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI limit\fR +\-\fBu\fR|\fBU\fI user\fR | \-\fBp\fI pid\fR [,\fIpid\fR ...] The traditional switches '-' and whitespace are optional. @@ -94,20 +108,20 @@ The traditional switches '-' and whitespace are optional. .\" ---------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------- -The \*(NE program provides a dynamic real-time view of a running system. -It can display\fB system\fR summary information as well as a list of\fB -tasks\fR currently being managed by the Linux kernel. +The \*(WE program provides a dynamic real-time view of a running system. +It can display\fB system\fR summary information as well as a list of +\*(TT currently being managed by the Linux kernel. The types of system summary information shown and the types, order and -size of information displayed for tasks are all user configurable and -that configuration can be made persistent across restarts. +size of information displayed for processes are all user configurable +and that configuration can be made persistent across restarts. The program provides a limited interactive interface for process manipulation as well as a much more extensive interface for personal -configuration \*(EM encompassing every aspect of its operation. -And while \*(NE is referred to throughout this document, you are free +configuration \*(Em encompassing every aspect of its operation. +And while \*(WE is referred to throughout this document, you are free to name the program anything you wish. -That new name, possibly an alias, will then be reflected on \*(Me's display -and used when reading and writing a \*(CF. +That new name, possibly an alias, will then be reflected on \*(We's +display and used when reading and writing a \*(CF. .\" ---------------------------------------------------------------------- @@ -117,349 +131,303 @@ and used when reading and writing a \*(CF. .SS Documentation .\" ---------------------------------------------------------------------- The remaining Table of Contents + +.Bd -literal 1. COMMAND\-LINE Options - 2. FIELDS / Columns + 2. SUMMARY Display + a. UPTIME and LOAD Averages + b. TASK and CPU States + c. MEMORY Usage + 3. FIELDS / Columns Display a. DESCRIPTIONS of Fields - b. SELECTING and ORDERING Columns - 3. INTERACTIVE Commands + b. MANAGING Fields + 4. INTERACTIVE Commands a. GLOBAL Commands - b. SUMMARY Area Commands - c. TASK Area Commands + b. SUMMARY AREA Commands + c. TASK AREA Commands d. COLOR Mapping - 4. ALTERNATE\-DISPLAY Mode + 5. ALTERNATE\-DISPLAY Provisions a. WINDOWS Overview b. COMMANDS for Windows - 5. FILES + c. SCROLLING a Window + 6. FILES a. SYSTEM Configuration File b. PERSONAL Configuration File - 6. STUPID TRICKS Sampler + 7. STUPID TRICKS Sampler a. Kernel Magic b. Bouncing Windows c. The Big Bird Window - 7. BUGS, 8. HISTORY Former top, 9. AUTHOR, 10. SEE ALSO + 8. BUGS, 9. HISTORY Former top, 10. AUTHOR, 11. SEE Also +.Ed .\" ...................................................................... .SS Operation .\" ---------------------------------------------------------------------- -When operating \*(Me, the two most important keys are help ('h' or '?') and -quit ('q') key. +When operating \*(We, the two most important keys are the help ('h' or '?') +key and quit ('q') key. Alternatively, you could simply use the traditional interrupt key ('^C') when you're done. -When you start \*(Me for the first time, you'll be presented with the -traditional screen elements: 1) Summary Area; 2) Message/Prompt Line; -3) Columns Header; 4) Task Area. -There will, however, be some differences when compared to the former top. +Some of \*(We's screens or functions require the use of cursor motion +keys like the standard \*(KAs plus the Home, End, PgUp and PgDn keys. +If your terminal or emulator does not provide those keys, the following +keys are accepted for compatibility: +.Bd -literal -compact + \fI key equivalents \fR + Up alt +\fB \\ \fR or alt +\fB k \fR + Down alt +\fB / \fR or alt +\fB j \fR + Left alt +\fB < \fR or alt +\fB h \fR + Right alt +\fB > \fR or alt +\fB l \fR (lower case L) + PgUp alt +\fB Up \fR + PgDn alt +\fB Down \fR + Home alt +\fB Left \fR + End alt +\fB Right \fR +.Ed -.TP 3 -.B Highlighting -.I Summary_Area\fR: -There is no highlighting for load/uptime and only values are highlighted for -other elements. - -.I Task_Area\fR: -Tasks running (or ready to run) will be highlighted, and bold is only one way -of emphasizing such processes. - -.TP 3 -.B Content/Labels -.I Summary_Area\fR: -The program name is shown, perhaps a symlink or alias. -The Cpu(s) state label hints at other possibilities. -The memory stats use a lower case 'k'. - -.I Columns_Header\fR: -Will show a new field and some changed labels. -More new fields will be found as you customize your \*(Me. +When you start \*(We for the first time, you'll be presented with these +traditional screen elements: 1) Summary Area; 2) Fields/Columns Header; +3) Task Area. +These areas will be explored in the sections that follow. +There is also an Input/Message line between the Summary Area and Columns +Header which needs no further explanation. .PP -\*(NT the width of \*(Me's display will be limited to 512 positions. -Displaying all fields requires a minimum of 160 characters. -The remaining width could be used for the 'Command' column. +\*(NT the width of \*(We's display will be limited to \*(WX positions. +Displaying all fields requires \*(WF characters. +The remaining width is allocated to variable width columns currently being +displayed which can expand to show additional information. + +An example would be the COMMAND field which could show the program name +only or a complete command line reflecting path and arguments. +Other variable width columns are noted in topic 3a. DESCRIPTIONS of Fields. .\" ...................................................................... .SS Startup Defaults .\" ---------------------------------------------------------------------- The following startup defaults assume no \*(CF, thus no user customizations. -Even so, items shown with an \*(AS could be overridden through the +Even so, items shown with an \*(AK could be overridden through the command-line. +All are explained in detail in the sections that follow. - \fIGlobal_defaults\fR +.Bd -literal + \fIGlobal-defaults\fR 'A' - Alt display Off (full-screen) * 'd' - Delay time 3.0 seconds + * 'H' - Threads mode Off (summarize as tasks) 'I' - Irix mode On\ \ (no, 'solaris' smp) - * 'p' - PID monitoring Off + * 'p' - PID monitoring Off (show all processes) * 's' - Secure mode Off (unsecured) - 'B' - Bold enable Off - \fISummary_Area_defaults\fR + 'B' - Bold enable On\ \ (yes, bold globally) + \fISummary-Area-defaults\fR 'l' - Load Avg/Uptime On\ \ (thus program name) 't' - Task/Cpu states On\ \ (1+1 lines, see '1') 'm' - Mem/Swap usage On\ \ (2 lines worth) '1' - Single Cpu On\ \ (thus 1 line if smp) - \fITask_Area_defaults\fR + \fITask-Area-defaults\fR 'b' - Bold hilite On\ \ (not 'reverse') * 'c' - Command line Off (name, not cmdline) - * 'H' - Threads Off\ (show all threads) * 'i' - Idle tasks On\ \ (show all tasks) 'R' - Reverse sort On\ \ (pids high-to-low) * 'S' - Cumulative time Off (no, dead children) - 'x' - Column hilite Off\ (no, sort field) + * 'u' - User filter Off (show euid only) + * 'U' - User filter Off (show any uid) + 'x' - Column hilite Off (no, sort field) 'y' - Row hilite On\ \ (yes, running tasks) - 'z' - color/mono Off\ (no, colors) + 'z' - color/mono Off (no, colors) +.Ed .\" ---------------------------------------------------------------------- .SH 1. COMMAND-LINE Options .\" ---------------------------------------------------------------------- -The command-line syntax for \*(Me consists of: +The command-line syntax for \*(We consists of: - \-\fBhv\fR\ |\ -\fBbcHisS\fR\ \-\fBd\fI\ delay\fR\ \-\fBn\fI\ iterations\ -\fR\ \-\fBp\fI\ pid\fR\ [,\fIpid\fR...] + \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI limit\fR +\-\fBu\fR|\fBU\fI user\fR | \-\fBp\fI pid\fR [,\fIpid\fR ...] The typically mandatory switches ('-') and even whitespace are completely optional. .TP 5 -\-\fBb\fR :\fB Batch mode\fR operation -Starts \*(Me in 'Batch mode', which could be useful for sending output -from \*(Me to other programs or to a file. -In this mode, \*(Me will not accept input and runs until the iterations +\-\fBh\fR | \-\fBv\fR :\fI Help/Version \fR +Show library version and the usage prompt, then quit. + +.TP 5 +\-\fBb\fR :\fI Batch-mode\fR operation \fR +Starts \*(We in 'Batch mode', which could be useful for sending output +from \*(We to other programs or to a file. +In this mode, \*(We will not accept input and runs until the iterations limit you've set with the '-n' \*(CO or until killed. .TP 5 -\-\fBc\fR :\fB Command line/Program name\fR toggle -Starts \*(Me with the last remembered 'c' state reversed. -Thus, if \*(Me was displaying command lines, now that field will show program +\-\fBc\fR :\fI Command-line/Program-name\fR toggle \fR +Starts \*(We with the last remembered 'c' state reversed. +Thus, if \*(We was displaying command lines, now that field will show program names, and visa versa. \*(XC 'c' \*(CI for additional information. .TP 5 -\-\fBd\fR :\fB Delay time\fR interval as:\ \ \fB-d ss.tt\fR (\fIseconds\fR.\fItenths\fR) +\-\fBd\fR :\fI Delay-time\fR interval as:\ \ \fB-d ss.tt\fR (\fIsecs\fR.\fItenths\fR) \fR Specifies the delay between screen updates, and overrides the corresponding value in one's personal \*(CF or the startup default. Later this can be changed with the 'd' or 's' \*(CIs. Fractional seconds are honored, but a negative number is not allowed. -In all cases, however, such changes are prohibited if \*(Me is running +In all cases, however, such changes are prohibited if \*(We is running in 'Secure mode', except for root (unless the 's' \*(CO was used). -For additional information on 'Secure mode' \*(Xt 5a. SYSTEM Configuration File. - +For additional information on 'Secure mode' \*(Xt 6a. SYSTEM Configuration File. .TP 5 -\-\fBh\fR :\fB Help\fR -Show library version and the usage prompt, then quit. +\-\fBH\fR :\fI Threads-mode\fR operation \fR +Instructs \*(We to display individual threads. +Without this \*(CO a summation of all threads in each process is shown. +Later this can be changed with the 'H' \*(CI. .TP 5 -\-\fBH\fR :\fB Threads\fR toggle -Starts \*(Me with the last remembered 'H' state reversed. -When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process. +\-\fBi\fR :\fI Idle-process\fR toggle \fR +Starts \*(We with the last remembered 'i' state reversed. +When this toggle is \*F, tasks that have not used any \*(PU since the +last update will not be displayed. +For additional information regarding this toggle +\*(Xt 4c. TASK AREA Commands, SIZE. .TP 5 -\-\fBi\fR :\fB Idle Processes\fR toggle -Starts \*(Me with the last remembered 'i' state reversed. -When this toggle is \*F, tasks that are idled or zombied will not be displayed. - -.TP 5 -\-\fBn\fR :\fB Number of iterations\fR limit as:\fB\ \ -n number\fR -Specifies the maximum number of iterations, or frames, \*(Me should +\-\fBn\fR :\fI Number-of-iterations\fR limit as:\fB\ \ -n number \fR +Specifies the maximum number of iterations, or frames, \*(We should produce before ending. .TP 5 -\-\fBu\fR :\fB Monitor by user\fR as:\fB\ \ -u somebody -Monitor only processes with an effective UID or user name -matching that given. - -.TP 5 -\-\fBU\fR :\fB Monitor by user\fR as:\fB\ \ -U somebody -Monitor only processes with a UID or user name matching that given. -This matches real, effective, saved, and filesystem UIDs. - -.TP 5 -\-\fBp\fR :\fB Monitor PIDs\fR as:\fB\ \ -pN1 -pN2 ...\fR\ \ or\fB\ \ -pN1, N2 [,...] +\-\fBp\fR :\fI Monitor-PIDs\fR mode as:\fB\ \ -pN1 -pN2 ...\fR\ \ or\fB\ \ -pN1,N2,N3 ... \fR Monitor only processes with specified process IDs. -This option can be given up to 20 times, or you can provide a comma delimited -list with up to 20 pids. +This option can be given up to \*(WP times, or you can provide a comma delimited +list with up to \*(WP pids. Co-mingling both approaches is permitted. -This is a \*(CO only. -And should you wish to return to normal operation, it is not necessary -to quit and and restart \*(Me \*(EM just issue the '=' \*(CI. +This is a \*(CO only and should you wish to return to normal operation, +it is not necessary to quit and and restart \*(We \*(Em just issue any +of these \*(CIs: '=', 'u' or 'U'. + +The 'p', 'u' and 'U' \*(COs are mutually exclusive. .TP 5 -\-\fBs\fR :\fB Secure mode\fR operation -Starts \*(Me with secure mode forced, even for root. +\-\fBs\fR :\fI Secure-mode\fR operation \fR +Starts \*(We with secure mode forced, even for root. This mode is far better controlled through the system \*(CF -(\*(Xt 5. FILES). +(\*(Xt 6. FILES). .TP 5 -\-\fBS\fR :\fB Cumulative time mode\fR toggle -Starts \*(Me with the last remembered 'S' state reversed. -When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +\-\fBS\fR :\fI Cumulative-time\fR toggle \fR +Starts \*(We with the last remembered 'S' state reversed. +When 'Cumulative time' mode is \*O, each process is listed with the \*(Pu time that it and its dead children have used. \*(XC 'S' \*(CI for additional information regarding this mode. .TP 5 -\-\fBv\fR :\fB Version\fR -Show library version and the usage prompt, then quit. +\-\fBu\fR | \-\fBU\fR :\fI User-filter-mode\fR as:\ \ -u | -U\fB number\fR or\fB name \fR +Display only processes with a user id or user name matching that given. +The '-u' option matches on \fB effective\fR user whereas the '-U' option +matches on\fB any\fR user (real, effective, saved, or filesystem). + +The 'p', 'u' and 'U' \*(COs are mutually exclusive. .\" ---------------------------------------------------------------------- -.SH 2. FIELDS / Columns +.SH 2. SUMMARY Display +.\" ---------------------------------------------------------------------- +Each of the following three areas are individually controlled through +one or more \*(CIs. +\*(XT 4b. SUMMARY AREA Commands for additional information regarding +these provisions. + +.\" ...................................................................... +.SS 2a. UPTIME and LOAD Averages +.\" ---------------------------------------------------------------------- +This portion consists of a single line containing: +.Bd -literal -compact + \fBprogram\fR or\fB window\fR name, depending on display mode + current time and length of time since last boot + total number of users + system load avg over the last 1, 5 and 15 minutes +.Ed + +.\" ...................................................................... +.SS 2b. TASK and CPU States +.\" ---------------------------------------------------------------------- +This portion consists of a minimum of two lines. +In an SMP environment, additional lines can reflect individual \*(PU +state percentages. + +Line 1 shows total\fB tasks\fR or\fB threads\fR, depending on the state +of the Threads-mode toggle. +That total is further classified as: +.Bd -literal -compact + running; sleeping; stopped; zombie +.Ed + +Line 2 shows \*(PU state percentages based on the interval since the +last refresh. Where two labels are shown below, those for more recent +kernel versions are shown first. +.Bd -literal -compact + \fBus\fR,\fB user\fR : time running un-niced user processes + \fBsy\fR,\fB system\fR : time running kernel processes + \fBni\fR,\fB nice\fR : time running niced user processes + \fBwa\fR,\fB IO-wait\fR : time waiting for I/O completion + \fBhi\fR : time spent servicing hardware interrupts + \fBsi\fR : time spent servicing software interrupts + \fBst\fR : time stolen from this vm by the hypervisor +.Ed + +.\" ...................................................................... +.SS 2c. MEMORY Usage +.\" ---------------------------------------------------------------------- +This portion consists of two lines which may express values in kilobytes (Kb), +megabytes (Mb) or gigabytes (Gb) depending on the amount of currently +installed \*(MP. + +Line 1 reflects \*(MP, classified as: + total, used, free, buffers + +Line 2 reflects \*(MV, classified as: + total, used, free, cached + +.\" ---------------------------------------------------------------------- +.SH 3. FIELDS / Columns .\" ---------------------------------------------------------------------- .\" ...................................................................... -.SS 2a. DESCRIPTIONS of Fields +.SS 3a. DESCRIPTIONS of Fields .\" ---------------------------------------------------------------------- -Listed below are \*(Me's available fields. -They are always associated with the letter shown, regardless of the position -you may have established for them with the 'o' (Order fields) \*(CI. +Listed below are \*(We's available process fields (columns). +They are shown in alphabetical order. +You may customize their position and whether or not they are displayable +with the 'f' or 'F' (Fields Management) \*(CIs. Any field is selectable as the sort field, and you control whether they are sorted high-to-low or low-to-high. -For additional information on sort provisions \*(Xt 3c. TASK Area Commands. +For additional information on sort provisions +\*(Xt 4c. TASK AREA Commands, SORTING. -.TP 3 -a:\fB PID\fR \*(EM Process Id\fR -The task's unique process ID, which periodically wraps, -though never restarting at zero. +.TP 4 + 1.\fB CGROUPS \*(Em Code Size (kb) \fR +Control groups provide for allocating resources (cpu, memory, network +bandwidth, etc.) among installation-defined groups of processes. +They enable fine-grained control over allocating, denying, prioritizing, +managing and monitoring those resources. -.TP 3 -b:\fB PPID\fR \*(EM Parent Process Pid\fR -The process ID of a task's parent. +Many different hierarchies of cgroups can exist simultaneously on a system +and each hierarchy is attached to one or more subsystems. +A subsystem represents a single resource. -.TP 3 -c:\fB RUSER\fR \*(EM Real User Name\fR -The real user name of the task's owner. +\*(NT The 'CGROUPS' field/column, unlike most columns, is not fixed-width. +When displayed, it plus any other variable width columns will be allocated +all remaining screen width (up to the maximum \*(WX characters). -.TP 3 -d:\fB UID\fR \*(EM User Id\fR -The effective user ID of the task's owner. - -.TP 3 -e:\fB USER\fR \*(EM User Name\fR -The effective user name of the task's owner. - -.TP 3 -f:\fB GROUP\fR \*(EM Group Name\fR -The effective group name of the task's owner. - -.TP 3 -g:\fB TTY\fR \*(EM Controlling Tty -The name of the controlling terminal. -This is usually the device (serial port, pty, etc.) from which the -process was started, and which it uses for input or output. -However, a task need not be associated with a terminal, in which case -you'll see '?' displayed. - -.TP 3 -h:\fB PR\fR \*(EM Priority -The priority of the task. - -.TP 3 -i:\fB NI\fR \*(EM Nice value -The nice value of the task. -A negative nice value means higher priority, whereas a positive nice value -means lower priority. -Zero in this field simply means priority will not be adjusted in determining a -task's dispatchability. - -.TP 3 -j:\fB P\fR \*(EM Last used \*(PU (SMP) -A number representing the last used processor. -In a true SMP environment this will likely change frequently since the kernel -intentionally uses weak affinity. -Also, the very act of running \*(Me may break this weak affinity and cause more -processes to change \*(PUs more often (because of the extra demand for -\*(Pu time). - -.TP 3 -k:\fB %CPU\fR \*(EM \*(PU usage -The task's share of the elapsed \*(PU time since the last screen update, -expressed as a percentage of total \*(PU time. -In a true SMP environment, if 'Irix mode' is \*F, \*(Me will operate -in 'Solaris mode' where a task's \*(Pu usage will be divided by the total -number of \*(PUs. -You toggle 'Irix/Solaris' modes with the 'I' \*(CI. - -.TP 3 -l:\fB TIME\fR \*(EM \*(PU Time -Total \*(PU time the task has used since it started. -When 'Cumulative mode' is \*O, each process is listed with the \*(Pu -time that it and its dead children has used. -You toggle 'Cumulative mode' with 'S', which is a \*(CO and an \*(CI. -\*(XC 'S' \*(CI for additional information regarding this mode. - -.TP 3 -m:\fB TIME+\fR \*(EM \*(PU Time, hundredths -The same as 'TIME', but reflecting more granularity through hundredths of -a second. - -.TP 3 -n:\fB %MEM\fR \*(EM Memory usage (RES) -A task's currently used share of available \*(MP. - -.TP 3 -o:\fB VIRT\fR \*(EM Virtual Image (kb) -The total amount of \*(MV used by the task. -It includes all code, data and shared libraries plus pages that have been -swapped out and pages that have been mapped but not used. - -.TP 3 -p:\fB SWAP\fR \*(EM Swapped size (kb) -Memory that is not resident but is present in a task. This is memory that -has been swapped out but could include additional non-resident memory. -This column is calculated by subtracting \*(MP from \*(MV. - -.TP 3 -q:\fB RES\fR \*(EM Resident size (kb) -The non-swapped \*(MP a task has used. - -.TP 3 -r:\fB CODE\fR \*(EM Code size (kb) -The amount of \*(MV devoted to executable code, also known as +.TP 4 + 2.\fB CODE \*(Em Code Size (kb) \fR +The amount of \*(MP devoted to executable code, also known as the 'text resident set' size or TRS. -.TP 3 -s:\fB DATA\fR \*(EM Data+Stack size (kb) -The amount of \*(MV devoted to other than executable code, also known as -the 'data resident set' size or DRS. - -.TP 3 -t:\fB SHR\fR \*(EM Shared Mem size (kb) -The amount of \*(MS used by a task. -It simply reflects memory that could be potentially shared with -other processes. - -.TP 3 -u:\fB nFLT\fR \*(EM Page Fault count -The number of\fB major\fR page faults that have occurred for a task. -A page fault occurs when a process attempts to read from or write to a virtual -page that is not currently present in its address space. -A major page fault is when backing storage access (such as a disk) is involved -in making that page available. - -.TP 3 -v:\fB nDRT\fR \*(EM Dirty Pages count -The number of pages that have been modified since they were last -written to disk. -Dirty pages must be written to disk before the corresponding physical memory -location can be used for some other virtual page. - -.TP 3 -w:\fB S\fR \*(EM Process Status -The status of the task which can be one of: - '\fBD\fR' = uninterruptible sleep - '\fBR\fR' = running - '\fBS\fR' = sleeping - '\fBT\fR' = traced or stopped - '\fBZ\fR' = zombie - -Tasks shown as running should be more properly thought of as 'ready to run' -\*(EM their task_struct is simply represented on the Linux run-queue. -Even without a true SMP machine, you may see numerous tasks in this state -depending on \*(Me's delay interval and nice value. - -.TP 3 -x:\fB Command\fR \*(EM Command\fB line\fR or Program\fB name\fR +.TP 4 + 3.\fB COMMAND \*(Em Command\fB Name\fR or Command\fB Line \fR Display the command line used to start a task or the name of the associated program. You toggle between command\fI line\fR and\fI name\fR with 'c', which is both @@ -467,376 +435,565 @@ a \*(CO and an \*(CI. When you've chosen to display command lines, processes without a command line (like kernel threads) will be shown with only the program name in -parentheses, as in this example: - \fR( mdrecoveryd ) +brackets, as in this example: + \fR[ mdrecoveryd ] Either form of display is subject to potential truncation if it's too long to fit in this field's current width. That width depends upon other fields selected, their order and the current screen width. -\*(NT The 'Command' field/column is unique, in that it is not fixed-width. -When displayed, this column will be allocated all remaining screen width (up -to the maximum 512 characters) to provide for the potential growth of program -names into command lines. +\*(NT The 'COMMAND' field/column, unlike most columns, is not fixed-width. +When displayed, it plus any other variable width columns will be allocated +all remaining screen width (up to the maximum \*(WX characters). -.TP 3 -y:\fB WCHAN\fR \*(EM Sleeping in Function -Depending on the availability of the kernel link map ('System.map'), this field -will show the name or the address of the kernel function in which the task is -currently sleeping. -Running tasks will display a dash ('-') in this column. +.TP 4 + 4.\fB %CPU \*(Em \*(PU Usage \fR +The task's share of the elapsed \*(PU time since the last screen update, +expressed as a percentage of total \*(PU time. +In a true SMP environment, if 'Irix mode' is \*F, \*(We will operate +in 'Solaris mode' where a task's \*(Pu usage will be divided by the total +number of \*(PUs. +You toggle 'Irix/Solaris' modes with the 'I' \*(CI. -\*(NT By displaying this field, \*(Me's own working set will be increased by -over 700Kb. -Your only means of reducing that overhead will be to stop and restart \*(Me. +.TP 4 + 5.\fB DATA \*(Em Data + Stack Size (kb) \fR +The amount of \*(MP devoted to other than executable code, also known as +the 'data resident set' size or DRS. -.TP 3 -z:\fB Flags\fR \*(EM Task Flags +.TP 4 + 6.\fB Flags \*(Em Task Flags \fR This column represents the task's current scheduling flags which are expressed in hexadecimal notation and with zeros suppressed. These flags are officially documented in . -Less formal documentation can also be found on the 'Fields select' -and 'Order fields' screens. + +.TP 4 + 7.\fB GID \*(Em Group Id \fR +The\fI effective\fR group ID. + +.TP 4 + 8.\fB GROUP \*(Em Group Name \fR +The\fI effective\fR group name. + +.TP 4 + 9.\fB %MEM \*(Em Memory Usage (RES) \fR +A task's currently used share of available \*(MP. + +.TP 4 + 10.\fB NI \*(Em Nice Value \fR +The nice value of the task. +A negative nice value means higher priority, whereas a positive nice value +means lower priority. +Zero in this field simply means priority will not be adjusted in determining +a task's dispatch-ability. + +.TP 4 +11.\fB nDRT \*(Em Dirty Pages Count \fR +The number of pages that have been modified since they were last +written to \*(AS. +Dirty pages must be written to \*(AS before the corresponding physical +memory location can be used for some other virtual page. + +.TP 4 +12.\fB nMaj \*(Em Major Page Fault Count \fR +The number of\fB major\fR page faults that have occurred for a task. +A page fault occurs when a process attempts to read from or write to a +virtual page that is not currently present in its address space. +A major page fault is when \*(AS access is involved in making that +page available. + +.TP 4 +13.\fB nMin \*(Em Minor Page Fault count \fR +The number of\fB minor\fR page faults that have occurred for a task. +A page fault occurs when a process attempts to read from or write to a +virtual page that is not currently present in its address space. +A minor page fault does not involve \*(AS access in making that +page available. + +.TP 4 +14.\fB nTH \*(Em Number of Threads \fR +The number of threads associated with a process. + +.TP 4 +15.\fB P \*(Em Last used \*(PU (SMP) \fR +A number representing the last used processor. +In a true SMP environment this will likely change frequently since the kernel +intentionally uses weak affinity. +Also, the very act of running \*(We may break this weak affinity and cause more +processes to change \*(PUs more often (because of the extra demand for +\*(Pu time). + +.TP 4 +16.\fB PGRP \*(Em Process Group Id \fR +Every process is member of a unique process group which is used for +distribution of signals and by terminals to arbitrate requests for their +input and output. +When a process is created (forked), it becomes a member of the process +group of its parent. +By convention, this value equals the process ID (\*(Xa PID) of the first +member of a process group, called the process group leader. + +.TP 4 +17.\fB PID \*(Em Process Id \fR +The task's unique process ID, which periodically wraps, though never +restarting at zero. + +This value may also be used as: a process group ID (\*(Xa PGRP); +a session ID for the session leader (\*(Xa SID); +and a TTY process group ID for the process group leader (\*(Xa TPGID). + +.TP 4 +18.\fB PPID \*(Em Parent Process pid \fR +The process ID of a task's parent. + +.TP 4 +19.\fB PR \*(Em Priority \fR +The scheduling priority of the task. +If you see 'rt' in this field, it means the task is running under +'real time' scheduling priority. + +Under linux, real time priority is somewhat misleading since traditionally +the operating itself was not preemptable. +And while the 2.6 kernel can be made mostly preemptable, it is not always so. + +.TP 4 +20.\fB RES \*(Em Resident Memory Size (kb) \fR +The non-swapped \*(MP a task has used. + +.TP 4 +21.\fB RUID \*(Em Real User Id \fR +The\fI real\fR user ID. + +.TP 4 +22.\fB RUSER \*(Em Real User Name \fR +The\fI real\fR user name. + +.TP 4 +23.\fB S \*(Em Process Status \fR +The status of the task which can be one of: + '\fBD\fR' = uninterruptible sleep + '\fBR\fR' = running + '\fBS\fR' = sleeping + '\fBT\fR' = traced or stopped + '\fBZ\fR' = zombie + +Tasks shown as running should be more properly thought of as 'ready to run' +\*(Em their task_struct is simply represented on the Linux run-queue. +Even without a true SMP machine, you may see numerous tasks in this state +depending on \*(We's delay interval and nice value. + +.TP 4 +24.\fB SHR \*(Em Shared Memory Size (kb) \fR +The amount of \*(MS available to a task, not all of which is +typically resident. +It simply reflects memory that could be potentially shared with +other processes. + +.TP 4 +25.\fB SID \*(Em Session Id \fR +A session is a collection of process groups (\*(Xa PGRP), +usually established by the login shell. +A newly forked process joins the session of its creator. +By convention, this value equals the process ID (\*(Xa PID) of the first +member of the session, called the session leader, which is usually the +login shell. + +.TP 4 +26.\fB SUID \*(Em Saved User Id \fR +The\fI saved\fR user ID. + +.TP 4 +27.\fB SUSER \*(Em Saved User Name \fR +The\fI saved\fR user name. + +.TP 4 +28.\fB SWAP \*(Em Swapped Size (kb) \fR +The non-resident portion of a task's address space. + +SWAP = VIRT - RES. + +.TP 4 +29.\fB TIME \*(Em \*(PU Time \fR +Total \*(PU time the task has used since it started. +When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +time that it and its dead children have used. +You toggle 'Cumulative mode' with 'S', which is both a \*(CO and an \*(CI. +\*(XC 'S' \*(CI for additional information regarding this mode. + +.TP 4 +30.\fB TIME+ \*(Em \*(PU Time, hundredths \fR +The same as 'TIME', but reflecting more granularity through hundredths +of a second. + +.TP 4 +31.\fB TPGID \*(Em Tty Process Group Id \fR +The process group ID of the foreground process for the connected tty, +or -1 if a process is not connected to a terminal. +By convention, this value equals the process ID (\*(Xa PID) of the +the process group leader (\*(Xa PGRP). + +.TP 4 +32.\fB TTY \*(Em Controlling Tty \fR +The name of the controlling terminal. +This is usually the device (serial port, pty, etc.) from which the +process was started, and which it uses for input or output. +However, a task need not be associated with a terminal, in which case +you'll see '?' displayed. + +.TP 4 +33.\fB UID \*(Em User Id \fR +The\fI effective\fR user ID of the task's owner. + +.TP 4 +34.\fB USER \*(Em User Name \fR +The\fI effective\fR user name of the task's owner. + +.TP 4 +35.\fB VIRT \*(Em Virtual Memory Size (kb) \fR +The total amount of \*(MV used by the task. +It includes all code, data and shared libraries plus pages that have been +swapped out and pages that have been mapped but not used. + +VIRT = SWAP + RES. + +.TP 4 +36.\fB WCHAN \*(Em Sleeping in Function \fR +Depending on the availability of the kernel link map ('System.map'), this +field will show the name or the address of the kernel function in which the +task is currently sleeping. +Running tasks will display a dash ('-') in this column. + +By displaying this field, \*(We's own working set could be increased by over +700Kb, depending on the kernel version. +Should that occur, your only means of reducing that overhead will be to stop +and restart \*(We. + +\*(NT The 'WCHAN' field/column, unlike most columns, is not fixed-width. +When displayed, it plus any other variable width columns will be allocated +all remaining screen width (up to the maximum \*(WX characters). .\" ...................................................................... -.SS 2b. SELECTING and ORDERING Columns +.SS 3b. MANAGING Fields .\" ---------------------------------------------------------------------- -After pressing the \*(CIs 'f' (Fields select) or 'o' (Order fields) you will -be shown a screen containing the current \fBfields string\fR followed by names -and descriptions for all fields. +After pressing the \*(CI 'f' or 'F' (Fields Management) you will be presented +with a screen showing: 1) the \*(CW name; 2) the designated sort field; +3) all fields in their current order along with descriptions. +Entries marked with an asterisk are the currently displayed fields, +screen width permitting. -Here is a sample\fB fields string\fR from one of \*(Me's four windows/field -groups and an explanation of the conventions used: -.Jbu -Sample fields string: - \fIANOPQRSTUVXbcdefgjlmyzWHIK\fR -.Jbu -The order of displayed fields corresponds to the order of the letters -in that string. -.Jbu -If the letter is\fI upper case\fR the corresponding field itself will -then be shown as part of the \*(TD (screen width permitting). -This will also be indicated by a leading \*(AS, as in this excerpt: - \fR... - \fB* K: %CPU = CPU usage - \fR l: TIME = CPU Time - \fR m: TIME+ = CPU Time, hundredths - \fB* N: %MEM = Memory usage (RES) - \fB* O: VIRT = Virtual Image (kb) - \fR... +.RS +4 +.jBu +As the on screen instructions indicate, you navigate among the fields with +the\fB Up\fR and\fB Down\fR \*(KAs. +The PgUp, PgDn, Home and End keys can also be used to quickly reach the +first or last available field. -.TP -.B Fields select\fR screen \*(EM the 'f' \*(CI -You\fI toggle\fR the\fB display\fR of a field by simply pressing the -corresponding letter. +.jBu +The\fB Right\fR \*(KA selects a field for repositioning and +the\fB Left\fR \*(KA or the <\fBEnter\fR> key commits that field's +placement. -.TP -.B Order fields\fR screen \*(EM the 'o' \*(CI -You\fI move\fR a field to the\fB left\fR by pressing the corresponding\fB -upper case\fR letter and to the\fB right\fR with the\fB lower case\fR -letter. +.jBu +The '\fBd\fR' key or the <\fBSpace\fR> bar toggles a field's display +status, and thus the presence or absence of the asterisk. -.\" ...................................................................... -.SS 2c. CPU States -.\" ---------------------------------------------------------------------- -The CPU states are shown in the Summary Area. They are always shown as a -percentage and are for the time between now and the last refresh. +.jBu +The '\fBs\fR' key designates a field as the sort field. +\*(XT 4c. TASK AREA Commands, SORTING for additional information regarding +your selection of a sort field. -.TP 3 -\fB us\fR \*(EM User CPU time -The time the CPU has spent running users' processes that are not -niced. +.jBu +The '\fBa\fR' and '\fBw\fR' keys can be used to cycle through all available +windows and the '\fBq\fR' or <\fBEsc\fR> keys exit Fields Management. +.RS -4 -.TP 3 -\fB sy\fR \*(EM System CPU time -The time the CPU has spent running the kernel and its processes. +.PP +The Fields Management screen can also be used to change the \*(CG in +either \*(FM or \*(AM. +Whatever was targeted when 'q' or was pressed will be made current +as you return to the \*(We display. +\*(XT 5. ALTERNATE\-DISPLAY Provisions and the 'g' \*(CI for insight +into \*(CWs and \*(FGs. -.TP 3 -\fB ni\fR \*(EM Nice CPU time -The time the CPU has spent running users' proccess that have been -niced. +.PP +\*(NT Any window that has been scrolled\fI horizontally\fR will be reset if any +field changes are made via the Fields Management screen. +Any\fI vertical\fR scrolled position, however, will not be affected. +\*(XT 5c. SCROLLING a Window for additional information regarding vertical +and horizontal scrolling. -.TP 3 -\fB wa\fR \*(EM iowait -Amount of time the CPU has been waiting for I/O to complete. - -.TP 3 -\fB hi\fR \*(EM Hardware IRQ -The amount of time the CPU has been servicing hardware interrupts. - -.TP 3 -\fB si\fR \*(EM Software Interrupts -The amount of time the CPU has been servicing software interrupts. - -.TP 3 -\fB st\fR \*(EM Steal Time -The amount of CPU 'stolen' from this virtual machine by the hypervisor -for other tasks (such as running another virtual machine). .\" ---------------------------------------------------------------------- -.SH 3. INTERACTIVE Commands +.SH 4. INTERACTIVE Commands .\" ---------------------------------------------------------------------- Listed below is a brief index of commands within categories. -Some commands appear more than once \*(EM their meaning or scope may vary +Some commands appear more than once \*(Em their meaning or scope may vary depending on the context in which they are issued. - 3a.\fI GLOBAL_Commands\fR - ?, =, A, B, d, G, h, I, k, q, r, s, W, Z - 3b.\fI SUMMARY_Area_Commands\fR - l, m, t, 1 - 3c.\fI TASK_Area_Commands\fR +.Bd -literal + 4a.\fI Global-Commands \fR + ?, =, A, B, d, g, h, H, I, k, q, r, s, W, Z + 4b.\fI Summary-Area-Commands \fR + l, t, 1, m + 4c.\fI Task-Area-Commands \fR Appearance: b, x, y, z - Content: c, f, H, o, S, u + Content: c, f, F, S, u, U Size: #, i, n - Sorting: <, >, F, O, R - 3d.\fI COLOR_Mapping\fR + Sorting: <, >, f, F, R + 4d.\fI Color-Mapping \fR , a, B, b, H, M, q, S, T, w, z, 0 - 7 - 4b.\fI COMMANDS_for_Windows\fR - -, _, =, +, A, a, G, g, w + 5b.\fI Commands-for-Windows \fR + -, _, =, +, A, a, g, G, w + 5c.\fI Scrolling-a-Window \fR + C, Up, Dn, Left, Right, PgUp, PgDn, Home, End +.Ed .\" ...................................................................... -.SS 3a. GLOBAL Commands +.SS 4a. GLOBAL Commands +.\" ---------------------------------------------------------------------- The global \*(CIs are\fB always\fR available\fR in both \*(FM and \*(AM. However, some of these \*(CIs are\fB not available\fR when running in 'Secure mode'. -If you wish to know in advance whether or not your \*(Me has been secured, -simply ask for help and view the system summary on the second line. +If you wish to know in advance whether or not your \*(We has been +secured, simply ask for help and view the system summary on the second +line. .TP 7 -\ \ <\fBEnter\fR> or <\fBSpace\fR> :\fIRefresh_Display\fR +\ \ \<\fBEnter\fR> or <\fBSpace\fR> :\fIRefresh-Display \fR These commands do nothing, they are simply ignored. -However, they will awaken \*(Me and following receipt of any input +However, they will awaken \*(We and following receipt of any input the entire display will be repainted. -Use either of these keys if you have a large delay interval and wish to -see current status, +Use either of these keys if you have a large delay interval and wish +to see current status, .TP 7 -\ \ <\fB?\fR> or <\fBh\fR> :\fIHelp\fR +\ \ \'\fB?\fR\' | \'\fBh\fR\' :\fIHelp \fR There are two help levels available. The first will provide a reminder of all the basic \*(CIs. -If \*(Me is\fI secured\fR, that screen will be abbreviated. +If \*(We is\fI secured\fR, that screen will be abbreviated. -Typing 'h' or '?' on that help screen will take you to help for those \*(CIs -applicable to \*(AM. +Typing 'h' or '?' on that help screen will take you to help for +those \*(CIs applicable to \*(AM. .TP 7 -\ \ <\fB=\fR> :\fIExit_Task_Limits\fR +\ \ \'\fB=\fR\' :\fIExit-Task-Limits \fR Removes restrictions on which tasks are shown. -This command will reverse any 'i' (idle tasks) and 'n' (max tasks) commands -that might be active. -It also provides for an 'exit' from PID monitoring. -See the '-p' \*(CO for a discussion of PID monitoring. +This command will reverse any 'i' (idle tasks) and 'n' (max tasks) +commands that might be active. +It also provides for an 'exit' from pid monitoring\fI and\fR user filtering. +See the '-p' \*(CO for a discussion of PID monitoring and the 'U' or 'u' +\*(CIs regarding user filtering. -When operating in \*(AM this command has a slightly broader meaning. +Additionally, any window that has been scrolled will be reset with +this command. +\*(XT 5c. SCROLLING a Window for additional information regarding +vertical and horizontal scrolling. + +When operating in \*(AM this command has a broader meaning. .TP 7 -\ \ <\fBA\fR> :\fIAlternate_Display_Mode_toggle\fR +\ \ \'\fBA\fR\' :\fIAlternate-Display-Mode\fR toggle \fR This command will switch between \*(FM and \*(AM. -\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight -into \*(CWs and field groups. +\*(XT 5. ALTERNATE\-DISPLAY Provisions and the 'g' \*(CI for insight +into \*(CWs and \*(FGs. .TP 7 -\ \ <\fBB\fR> :\fIBold_Disable/Enable_toggle\fR +\ \ \'\fBB\fR\' :\fIBold-Disable/Enable\fR toggle \fR This command will influence use of the 'bold' terminfo capability and alters\fB both\fR the \*(SA and \*(TA for the \*(CW. While it is intended primarily for use with dumb terminals, it can be applied anytime. -\*(NT When this toggle is \*O and \*(Me is operating in monochrome mode, +\*(NT When this toggle is \*O and \*(We is operating in monochrome mode, the\fB entire display\fR will appear as normal text. Thus, unless the 'x' and/or 'y' toggles are using reverse for emphasis, there will be no visual confirmation that they are even on. .TP 7 -*\ <\fBd\fR> or <\fBs\fR> :\fIChange_Delay_Time_interval\fR +*\ \'\fBd\fR\' | \'\fBs\fR\' :\fIChange-Delay-Time-interval \fR You will be prompted to enter the delay time, in seconds, between display updates. Fractional seconds are honored, but a negative number is not allowed. Entering 0 causes (nearly) continuous updates, with an unsatisfactory -display as the system and tty driver try to keep up with \*(Me's demands. +display as the system and tty driver try to keep up with \*(We's demands. The delay value is inversely proportional to system loading, so set it with care. -If at any time you wish to know the current delay time, simply ask for help -and view the system summary on the second line. +If at any time you wish to know the current delay time, simply ask for +help and view the system summary on the second line. .TP 7 -\ \ <\fBG\fR> :\fIChoose_Another_Window/Field_Group\fR +\ \ \'\fBg\fR\' :\fIChoose-Another-Window/Field-Group \fR You will be prompted to enter a number between 1 and 4 designating the -window/field group which should be made the \*(CW. +\*(FG which should be made the \*(CW. You will soon grow comfortable with these 4 windows, especially after experimenting with \*(AM. .TP 7 -\ \ <\fBI\fR> :\fIIrix/Solaris_Mode_toggle\fR +\ \ \'\fBH\fR\' :\fIThreads-mode\fR toggle \fR +When this toggle is \*O, individual threads will be displayed for all +processes in all visible \*(TWs. +Otherwise, \*(We displays a summation of all threads in each process. + +.TP 7 +\ \ \'\fBI\fR\' :\fIIrix/Solaris-Mode\fR toggle \fR When operating in 'Solaris mode' ('I' toggled \*F), a task's \*(Pu usage will be divided by the total number of \*(PUs. -After issuing this command, you'll be informed of the new state of this toggle. +After issuing this command, you'll be told the new state of this toggle. .TP 7 -\ \ <\fBu\fR> :\fIselect a user\fR -You will be prompted for a UID or username. Only processes -belonging to the selected user will be displayed. This option -matches on the effective UID. - -.TP 7 -\ \ <\fBU\fR> :\fIselect a user\fR -You will be prompted for a UID or username. Only processes -belonging to the selected user will be displayed. This option -matches on the real, effective, saved, and filesystem UID. - -.TP 7 -*\ <\fBk\fR> :\fIKill_a_task\fR +*\ \'\fBk\fR\' :\fIKill-a-task \fR You will be prompted for a PID and then the signal to send. The default signal, as reflected in the prompt, is SIGTERM. However, you can send any signal, via number or name. If you wish to abort the kill process, do one of the following depending on your progress: - 1) at the pid prompt, just press - 2) at the signal prompt, type 0 + 1) at the pid prompt, just press + 2) at the signal prompt, type 0 .TP 7 -\ \ <\fBq\fR> :\fIQuit\fR +\ \ \'\fBq\fR\' :\fIQuit \fR .TP 7 -*\ <\fBr\fR> :\fIRenice_a_Task\fR +*\ \'\fBr\fR\' :\fIRenice-a-Task \fR You will be prompted for a PID and then the value to nice it to. Entering a positive value will cause a process to lose priority. Conversely, a negative value will cause a process to be viewed more favorably by the kernel. .TP 7 -\ \ <\fBW\fR> :\fIWrite_the_Configuration_File\fR +\ \ \'\fBW\fR\' :\fIWrite-the-Configuration-File \fR This will save all of your options and toggles plus the current display mode and delay time. -By issuing this command just before quitting \*(Me, you will be able restart -later in exactly that same state. +By issuing this command just before quitting \*(We, you will be able +restart later in exactly that same state. .TP 7 -\ \ <\fBZ\fR> :\fIChange_Color_Mapping +\ \ \'\fBZ\fR\' :\fIChange-Color-Mapping This key will take you to a separate screen where you can change the colors for the \*(CW, or for all windows. -For details regarding this \*(CI \*(Xt 3d. COLOR Mapping. +For details regarding this \*(CI \*(Xt 4d. COLOR Mapping. .IP "*" 3 -The commands shown with an \*(AS are not available in 'Secure mode', +The commands shown with an \*(AK are not available in 'Secure mode', nor will they be shown on the level-1 help screen. .\" ...................................................................... -.SS 3b. SUMMARY Area Commands +.SS 4b. SUMMARY AREA Commands +.\" ---------------------------------------------------------------------- The \*(SA \*(CIs are\fB always available\fR in both \*(FM and \*(AM. -They affect the beginning lines of your display and will determine the position -of messages and prompts. +They affect the beginning lines of your display and will determine the +position of messages and prompts. -These commands always impact just the \*(CW/field group. -\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight -into \*(CWs and field groups. +These commands always impact just the \*(CG. +\*(XT 5. ALTERNATE\-DISPLAY Provisions and the 'g' \*(CI for insight into +\*(CWs and \*(FGs. .TP 7 -\ \ <\fBl\fR> :\fIToggle_Load_Average/Uptime\fR \*(EM On/Off -This is also the line containing the program name (possibly an alias) when -operating in \*(FM or the \*(CW name when operating in \*(AM. +\ \ \'\fBl\fR\' :\fILoad-Average/Uptime\fR toggle \fR +This is also the line containing the program name (possibly an alias) +when operating in \*(FM or the \*(CW name when operating in \*(AM. .TP 7 -\ \ <\fBm\fR> :\fIToggle_Memory/Swap_Usage\fR \*(EM On/Off -This command affects two \*(SA lines. - -.TP 7 -\ \ <\fBt\fR> :\fIToggle_Task/Cpu_States\fR \*(EM On/Off +\ \ \'\fBt\fR\' :\fITask/Cpu-States\fR toggle \fR This command affects from 2 to many \*(SA lines, depending on the state -of the '1' toggle and whether or not \*(Me is running under true SMP. +of the '1' toggle and whether or not \*(We is running under true SMP. + +This portion of the \*(SA is also influenced by the 'H' \*(CI toggle, +as reflected in the total label which shows either 'Tasks' or 'Threads'. .TP 7 -\ \ <\fB1\fR> :\fIToggle_Single/Separate_Cpu_States\fR \*(EM On/Off +\ \ \'\fB1\fR\' :\fISingle/Separate-Cpu-States\fR toggle \fR This command affects how the 't' command's Cpu States portion is shown. -Although this toggle exists primarily to serve massively-parallel SMP machines, -it is not restricted to solely SMP environments. +Although this toggle exists primarily to serve massively-parallel SMP +machines, it is not restricted to solely SMP environments. -When you see 'Cpu(s):' in the \*(SA, the '1' toggle is \*O and all +When you see '%Cpu(s):' in the \*(SA, the '1' toggle is \*O and all \*(Pu information is gathered in a single line. -Otherwise, each \*(Pu is displayed separately as: 'Cpu0, Cpu1, ...' +Otherwise, each \*(Pu is displayed separately as: '%Cpu0, %Cpu1, ...' +up to available screen height. + +.TP 7 +\ \ \'\fBm\fR\' :\fIMemory/Swap-Usage\fR toggle \fR +This command affects the two \*(SA lines dealing with physical +and virtual memory. .PP -\*(NT If the entire \*(SA has been toggled \*F for any window, you would be left -with just the\fB message line\fR. +\*(NT If the entire \*(SA has been toggled \*F for any window, you would +be left with just the\fB message line\fR. In that way, you will have maximized available task rows but (temporarily) sacrificed the program name in \*(FM or the \*(CW name when in \*(AM. .\" ...................................................................... -.SS 3c. TASK Area Commands +.SS 4c. TASK AREA Commands +.\" ---------------------------------------------------------------------- The \*(TA \*(CIs are\fB always\fR available in \*(FM. The \*(TA \*(CIs are\fB never available\fR in \*(AM\fI if\fR the \*(CW's -\*(TD has been toggled \*F (\*(Xt 4. ALTERNATE\-DISPLAY Mode). +\*(TD has been toggled \*F (\*(Xt 5. ALTERNATE\-DISPLAY Provisions). .PP -.\" ......................... +.\" .................................................. .B APPEARANCE\fR of \*(TW .br .in +2 The following commands will also be influenced by the state of the -global 'B' (bold disable) toggle. +global 'B' (bold enable) toggle. .in .TP 7 -\ \ <\fBb\fR> :\fIBold/Reverse_toggle\fR +\ \ \'\fBb\fR\' :\fIBold/Reverse\fR toggle \fR This command will impact how the 'x' and 'y' toggles are displayed. Further, it will only be available when at least one of those toggles is \*O. .TP 7 -\ \ <\fBx\fR> :\fIColumn_Highlight_toggle\fR +\ \ \'\fBx\fR\' :\fIColumn-Highlight\fR toggle \fR Changes highlighting for the current sort field. -You probably don't need a constant visual reminder of the sort field and -\*(Me hopes that you always run with 'column highlight' \*F, due to the cost -in path-length. - If you forget which field is being sorted this command can serve as a quick -visual reminder. +visual reminder, providing the sort field is being displayed. +The sort field might\fI not\fR be visible because: + 1) there is insufficient\fI Screen Width \fR + 2) the 'f' \*(CI turned it \*F .TP 7 -\ \ <\fBy\fR> :\fIRow_Highlight_toggle\fR +\ \ \'\fBy\fR\' :\fIRow-Highlight\fR toggle \fR Changes highlighting for "running" tasks. -For additional insight into this task state, \*(Xt 2a. DESCRIPTIONS of Fields, -Process Status. +For additional insight into this task state, +\*(Xt 3a. DESCRIPTIONS of Fields, the 'S' field (Process Status). Use of this provision provides important insight into your system's health. The only costs will be a few additional tty escape sequences. .TP 7 -\ \ <\fBz\fR> :\fIColor/Monochrome_toggle\fR +\ \ \'\fBz\fR\' :\fIColor/Monochrome\fR toggle \fR Switches the \*(CW between your last used color scheme and the older form of black-on-white or white-on-black. -This command will alter\fB both\fR the \*(SA and \*(TA but does not affect the -state of the 'x', 'y' or 'b' toggles. +This command will alter\fB both\fR the \*(SA and \*(TA but does not affect +the state of the 'x', 'y' or 'b' toggles. .PP -.\" ......................... +.\" .................................................. .B CONTENT\fR of \*(TW .PD 0 .TP 7 -\ \ <\fBc\fR> :\fICommand_Line/Program_Name_toggle\fR -This command will be honored whether or not the 'Command' column +\ \ \'\fBc\fR\' :\fICommand-Line/Program-Name\fR toggle \fR +This command will be honored whether or not the 'COMMAND' column is currently visible. Later, should that field come into view, the change you applied will be seen. .TP 7 -\ \ <\fBf\fR> and <\fBo\fR> :\fIFields_select\fR or \fIOrder_fields\fR -These keys display separate screens where you can change which -fields are displayed and their order. +\ \ \'\fBf\fR\' | \'\fBF\fR\' :\fIFields-Management \fR +These keys display a separate screen where you can change which fields are +displayed, their order and also designate the sort field. For additional information on these \*(CIs -\*(Xt 2b. SELECTING and ORDERING Columns. +\*(Xt 3b. MANAGING Fields. .TP 7 -\ \ <\fBS\fR> :\fICumulative_Time_Mode_toggle\fR -When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process. - -.TP 7 -\ \ \'\fBS\fR\' :\fICumulative_Time_Mode_toggle\fR +\ \ \'\fBS\fR\' :\fICumulative-Time-Mode\fR toggle \fR When 'Cumulative mode' is \*O, each process is listed with the \*(Pu time that it and its dead children have used. @@ -852,28 +1009,36 @@ If you wish to know in advance whether or not 'Cumulative mode' is in effect, simply ask for help and view the window summary on the second line. .TP 7 -\ \ <\fBu\fR> :\fIShow_Specific_User_Only\fR -You will be prompted to enter the name of the user to display. -Thereafter, in that \*(TW only matching User ID's will be shown, or possibly -no tasks will be shown. +\ \ \'\fBu\fR' | '\fBU\fR' :\fIShow-Specific-User-Only \fR +You will be prompted for the\fB uid\fR or\fB name\fR of the user to display. +The '-u' option matches on \fB effective\fR user whereas the '-U' option +matches on\fB any\fR user (real, effective, saved, or filesystem). -Later, if you wish to monitor all tasks again, re-issue this command but -just press at the prompt, without providing a name. +Thereafter, in that \*(TW only matching users will be shown, or possibly +no processes will be shown. +Different \*(TWs can can be used to filter different users. + +Later, if you wish to monitor all tasks again in the \*(CW, re-issue this +command but just press at the prompt. .PP -.\" ......................... +.\" .................................................. .B SIZE\fR of \*(TW .PD 0 .TP 7 -\ \ <\fBi\fR> :\fIIdle_Processes_toggle\fR +\ \ \'\fBi\fR\' :\fIIdle-Process\fR toggle \fR Displays all tasks or just active tasks. -When this toggle is \*F, idled or zombied processes will not be displayed. +When this toggle is \*F, tasks that have not used any \*(PU since the +last update will not be displayed. +However, due to the granularity of the %CPU and TIME+ fields, +some processes may still be displayed that\fI appear\fR to have +used\fI no\fR \*(PU. If this command is applied to the last \*(TD when in \*(AM, then it will not affect the window's size, as all prior \*(TDs will have already been painted. .TP 7 -\ \ <\fBn\fR> or <\fB#\fR> :\fISet_Maximum_Tasks\fR +\ \ \'\fBn\fR\' | \'\fB#\fR\' :\fISet-Maximum-Tasks \fR You will be prompted to enter the number of tasks to display. The lessor of your number and available screen rows will be used. @@ -886,38 +1051,40 @@ already been painted. simply decrease the size of the \*(TD(s) above it. .PP -.\" ......................... +.\" .................................................. .B SORTING\fR of \*(TW .br .in +2 -For compatibility, this \*(Me supports most of the former \*(Me sort keys. -Since this is primarily a service to former \*(Me users, these commands do +For compatibility, this \*(We supports most of the former \*(We sort keys. +Since this is primarily a service to former \*(We users, these commands do not appear on any help screen. - command sorted field supported - A start time (non-display) No +.Bd -literal -compact + \fI command sorted-field supported \fR + A start time (non-display) \fB No \fR M %MEM Yes N PID Yes P %CPU Yes T TIME+ Yes +.Ed -Before using any of the following sort provisions, \*(Me suggests that you +Before using any of the following sort provisions, \*(We suggests that you temporarily turn on column highlighting using the 'x' \*(CI. That will help ensure that the actual sort environment matches your intent. -The following \*(CIs will\fB only\fR be honored when the -current sort field is\fB visible\fR. +The following \*(CIs will\fB only\fR be honored when the current sort field +is\fB visible\fR. The sort field might\fI not\fR be visible because: - 1) there is insufficient\fI Screen Width\fR + 1) there is insufficient\fI Screen Width \fR 2) the 'f' \*(CI turned it \*F .in .TP 7 -\ \ <\fB<\fR> :\fIMove_Sort_Field_Left\fR +\ \ \'\fB<\fR\' :\fIMove-Sort-Field-Left \fR Moves the sort column to the left unless the current sort field is the first field being displayed. .TP 7 -\ \ <\fB>\fR> :\fIMove_Sort_Field_Right\fR +\ \ \'\fB>\fR\' :\fIMove-Sort-Field-Right \fR Moves the sort column to the right unless the current sort field is the last field being displayed. @@ -928,20 +1095,14 @@ the current sort field is visible. .in .TP 7 -\ \ <\fBF\fR> or <\fBO\fR> :\fISelect_Sort_Field\fR +\ \ \'\fBf\fR\' | \'\fBF\fR\' :\fIFields-Management \fR These keys display a separate screen where you can change which field -is used as the sort column. - -If a field is selected which was not previously being displayed, it will -be forced \*O when you return to the \*(Me display. -However, depending upon your screen width and the order of your fields, -this sort field may not be displayable. - -This \*(CI can be a convenient way to simply verify the current sort field, -when running \*(Me with column highlighting turned \*F. +is used as the sort column, among other functions. +This can be a convenient way to simply verify the current sort field, +when running \*(We with column highlighting turned \*F. .TP 7 -\ \ <\fBR\fR> :\fIReverse/Normal_Sort_Field_toggle\fR +\ \ \'\fBR\fR\' :\fIReverse/Normal-Sort-Field\fR toggle \fR Using this \*(CI you can alternate between high-to-low and low-to-high sorts. .PP @@ -951,55 +1112,59 @@ Thus, the TTY and WCHAN fields will violate strict ASCII collating sequence. .in .\" ...................................................................... -.SS 3d. COLOR Mapping +.SS 4d. COLOR Mapping +.\" ---------------------------------------------------------------------- When you issue the 'Z' \*(CI, you will be presented with a separate screen. That screen can be used to change the colors in just the \*(CW or -in all four windows before returning to the \*(Me display. +in all four windows before returning to the \*(We display. .P -.B Available \*(CIs - \fB4\fR upper case letters to select a\fB target\fR - \fB8\fR numbers to select a\fB color\fR - normal toggles available\fR +The following \*(CIs are available. +.Bd -literal -compact + \fB4\fR upper case letters to select a\fB target \fR + \fB8\fR numbers to select a\fB color \fR + normal toggles available \fR 'B' :bold disable/enable 'b' :running tasks "bold"/reverse 'z' :color/mono - other commands available\fR + other commands available \fR 'a'/'w' :apply, then go to next/prior :apply and exit 'q' :abandon current changes and exit +.Ed If your use 'a' or 'w' to cycle the targeted window, you will have applied the color scheme that was displayed when you left that window. You can, of course, easily return to any window and reapply different colors or turn colors \*F completely with the 'z' toggle. -The Color Mapping screen can also be used to change the \*(CW/field group -in either \*(FM or \*(AM. +The Color Mapping screen can also be used to change the \*(CG in +either \*(FM or \*(AM. Whatever was targeted when 'q' or was pressed will be made current -as you return to the \*(Me display. +as you return to the \*(We display. .\" ---------------------------------------------------------------------- -.SH 4. ALTERNATE\-DISPLAY Mode +.SH 5. ALTERNATE\-DISPLAY Provisions .\" ---------------------------------------------------------------------- .\" ...................................................................... -.SS 4a. WINDOWS Overview +.SS 5a. WINDOWS Overview +.\" ---------------------------------------------------------------------- .TP .B Field Groups/Windows\fR: .br In \*(FM there is a single window represented by the entire screen. That single window can still be changed to display 1 of 4 different\fB field -groups\fR (\*(Xc 'G' \*(CI, repeated below). -Each of the 4 field groups has a unique separately configurable\fB \*(SA\fR +groups\fR (\*(Xc 'g' \*(CI, repeated below). +Each of the 4 \*(FGs has a unique separately configurable\fB \*(SA \fR and its own configurable\fB \*(TA\fR. -In \*(AM, those 4 underlying field groups can now be made visible +In \*(AM, those 4 underlying \*(FGs can now be made visible simultaneously, or can be turned \*F individually at your command. The \*(SA will always exist, even if it's only the message line. At any given time only\fI one\fR \*(SA can be displayed. -However, depending on your commands, there could be from\fI zero\fR +However, depending on your commands, there could be from\fI zero \fR to\fI four\fR separate \*(TDs currently showing on the screen. .TP @@ -1016,14 +1181,15 @@ With the loss of the window name (the 'l' toggled line), you'll not easily know what window is the \*(CW. .\" ...................................................................... -.SS 4b. COMMANDS for Windows +.SS 5b. COMMANDS for Windows +.\" ---------------------------------------------------------------------- .TP 7 -\ \ <\fB-\fR> and <\fB_\fR> :\fIShow/Hide_Window(s)_toggles\fR +\ \ \'\fB-\fR\' | \'\fB_\fR\' :\fIShow/Hide-Window(s)\fR toggles \fR The '-' key turns the \*(CW's \*(TD \*O and \*F. When \*O, that \*(TA will show a minimum of the columns header you've -established with the 'f' and 'o' commands. -It will also reflect any other \*(TA options/toggles you've applied yielding -zero or more tasks. +established with the 'f' \*(CI. +It will also reflect any other \*(TA options/toggles you've applied +yielding zero or more tasks. The '_' key does the same for all \*(TDs. In other words, it switches between the currently visible \*(TD(s) and any @@ -1032,18 +1198,22 @@ If all 4 \*(TDs are currently visible, this \*(CI will leave the \*(SA as the only display element. .TP 7 -*\ <\fB=\fR> and <\fB+\fR> :\fIEqualize_(re-balance)_Window(s)\fR +*\ \'\fB=\fR\' | \'\fB+\fR\' :\fIEqualize-(re-balance)-Window(s) \fR The '=' key forces the \*(CW's \*(TD to be visible. -It also reverses any 'i' (idle tasks) and 'n' (max tasks) commands that might -be active. +It also reverses any 'i' (idle tasks), 'n' (max tasks) and 'u'/'U' +(user filter) commands that might be active. +Also, if the window had been scrolled, it will be reset with this command. +\*(XT 5c. SCROLLING a Window for additional information regarding vertical +and horizontal scrolling. The '+' key does the same for all windows. The four \*(TDs will reappear, evenly balanced. They will also have retained any customizations you had previously applied, -except for the 'i' (idle tasks) and 'n' (max tasks) commands. +except for the 'i' (idle tasks), 'n' (max tasks), 'u'/'U' (user filter) +and scrolling \*(CIs. .TP 7 -*\ <\fBA\fR> :\fIAlternate_Display_Mode_toggle\fR +*\ \'\fBA\fR\' :\fIAlternate-Display-Mode\fR toggle \fR This command will switch between \*(FM and \*(AM. The first time you issue this command, all four \*(TDs will be shown. @@ -1051,7 +1221,7 @@ Thereafter when you switch modes, you will see only the \*(TD(s) you've chosen to make visible. .TP 7 -*\ <\fBa\fR> and <\fBw\fR> :\fINext_Window_Forward/Backward\fR +*\ \'\fBa\fR\' | \'\fBw\fR\' :\fINext-Window-Forward/Backward \fR This will change the \*(CW, which in turn changes the window to which commands are directed. These keys act in a circular fashion so you can reach any desired \*(CW @@ -1062,80 +1232,168 @@ whenever the \*(CW name loses its emphasis/color, that's a reminder the \*(TD is \*F and many commands will be restricted. .TP 7 -*\ <\fBG\fR> :\fIChoose_Another_Window/Field_Group\fR +*\ \'\fBg\fR\' :\fIChoose-Another-Window/Field-Group \fR You will be prompted to enter a number between 1 and 4 designating the -window/field group which should be made the \*(CW. +\*(FG which should be made the \*(CW. In \*(FM, this command is necessary to alter the \*(CW. In \*(AM, it is simply a less convenient alternative to the 'a' and 'w' commands. .TP 7 -\ \ <\fBg\fR> :\fIChange_Window/Field_Group_Name\fR +\ \ \'\fBG\fR\' :\fIChange-Window/Field-Group-Name \fR You will be prompted for a new name to be applied to the \*(CW. It does not require that the window name be visible (the 'l' toggle to be \*O). .IP "*" 3 -The \*(CIs shown with an \*(AS have use beyond \*(AM. - '=', 'A', 'G' are always available - 'a', 'w' act the same when color mapping +The \*(CIs shown with an \*(AK have use beyond \*(AM. +.Bd -literal -compact + \'=', 'A', 'g' are always available + \'a', 'w' act the same with color mapping + \ and fields management +.Ed + +.\" ...................................................................... +.SS 5c. SCROLLING a Window +.\" ---------------------------------------------------------------------- +Typically a \*(TW is a partial view into a systems's total tasks/threads +which shows only some of the available fields/columns. +With these \*(KSs, you can move that view vertically or horizontally to +reveal any desired task or column. + +.TP 4 +\fBUp\fR,\fBPgUp\fR :\fIScroll-Tasks \fR +Move the view up toward the first task row, until the first task is +displayed at the top of the \*(CW. +The \fIUp\fR \*(KA moves a single line while \fIPgUp\fR scrolls the +entire window. + +.TP 4 +\fBDown\fR,\fBPgDn\fR :\fIScroll-Tasks \fR +Move the view down toward the last task row, until the last task is +the only task displayed at the top of the \*(CW. +The \fIDown\fR \*(KA moves a single line while \fIPgDn\fR scrolls the +entire window. + +.TP 4 +\fBLeft\fR,\fBRight\fR :\fIScroll-Columns \fR +Move the view of displayable fields horizontally one column at a time. + +\*(NT As a reminder, some fields/columns are not fixed-width but +allocated all remaining screen width when visible. +When scrolling right or left, that feature may produce some +unexpected results initially. + +.TP 4 +\fBHome\fR :\fIJump-to-Home-Position \fR +Reposition the display to the un-scrolled coordinates. + +.TP 4 +\fBEnd\fR :\fIJump-to-End-Position \fR +Reposition the display so that the rightmost column reflects the last +displayable field and the bottom task row represents the last task. + +\*(NT From this position it is still possible to scroll\fI down\fR +and\fI right\fR using the \*(KAs. +This is true until a single column and a single task is left as the only +display element. + +.TP 4 +'\fBC\fR' :\fIShow-scroll-coordinates\fR toggle \fR +Toggle an informational message which is displayed whenever the message +line is not otherwise being used. + +.Bd -literal + "\fBscroll coordinates: y = n/n (tasks), x = n/n (fields)\fR" +.Ed + +The coordinates shown as \fBn\fR/\fBn\fR are relative to the upper left +corner of the \*(CW. + +.RS +4 +.TP 4 +\fBy = n/n (tasks) \fR +The first \fBn\fR represents the topmost visible task and is controlled +by \*(KSs. +The second \fBn\fR is updated automatically to reflect total tasks. + +.TP 4 +\fBx = n/n (fields) \fR +The first \fBn\fR represents the leftmost displayed column and is +controlled by \*(KSs. +The second \fBn\fR is the total number of displayable fields and is +established with the '\fBf\fR' \*(CI. +.RS -4 + +.PP +The above \*(CIs are\fB always\fR available in \*(FM but\fB never\fR +available in \*(AM if the \*(CW's \*(TD has been toggled \*F. .\" ---------------------------------------------------------------------- -.SH 5. FILES +.SH 6. FILES .\" ---------------------------------------------------------------------- .\" ...................................................................... -.SS 5a. SYSTEM Configuration File +.SS 6a. SYSTEM Configuration File +.\" ---------------------------------------------------------------------- The presence of this file will influence which version of the 'help' screen is shown to an ordinary user. More importantly, it will limit what ordinary users are allowed -to do when \*(Me is running. +to do when \*(We is running. They will not be able to issue the following commands. - k Kill a task - r Renice a task - d or s Change delay/sleep interval +.Bd -literal -compact + k Kill a task + r Renice a task + d or s Change delay/sleep interval +.Ed -The system \*(CF is\fB not\fR created by \*(Me. -Rather, you create this file manually and place it in the \fI/etc\fR +The system \*(CF is\fB not\fR created by \*(We. +Rather, you create this file manually and place it in the \fI/etc \fR directory. Its name must be 'toprc' and must have no leading '.' (period). It must have only two lines. Here is an example of the contents of\fI /etc/toprc\fR: - s # line 1: 'secure' mode switch - 5.0 # line 2: 'delay'\ \ interval in seconds +.Bd -literal -compact + s # line 1: 'secure' mode switch + 5.0 # line 2: 'delay'\ \ interval in seconds +.Ed .\" ...................................................................... -.SS 5b. PERSONAL Configuration File +.SS 6b. PERSONAL Configuration File +.\" ---------------------------------------------------------------------- This file is written as '$HOME/.your-name-4-top' + 'rc'. Use the 'W' \*(CI to create it or update it. Here is the general layout: - global # line 1: the program name/alias notation - " # line 2: id,altscr,irixps,delay,curwin - per ea # line a: winname,fieldscur - window # line b: winflags,sortindx,maxtasks - " # line c: summclr,msgsclr,headclr,taskclr +.Bd -literal -compact + global # line 1: the program name/alias notation + " # line 2: id,altscr,irixps,delay,curwin + per ea # line a: winname,fieldscur + window # line b: winflags,sortindx,maxtasks + " # line c: summclr,msgsclr,headclr,taskclr +.Ed -If the $HOME variable is not present, \*(Me will try to write the +If the $HOME variable is not present, \*(We will try to write the personal \*(CF to the current directory, subject to permissions. .\" ---------------------------------------------------------------------- -.SH 6. STUPID TRICKS Sampler +.SH 7. STUPID TRICKS Sampler .\" ---------------------------------------------------------------------- -Many of these 'tricks' work best when you give \*(Me a scheduling boost. +Many of these 'tricks' work best when you give \*(We a scheduling boost. So plan on starting him with a nice value of -10, assuming you've got the authority. .\" ...................................................................... -.SS 6a. Kernel Magic +.SS 7a. Kernel Magic +.\" ---------------------------------------------------------------------- .\" sorry, just can't help it -- don't ya love the sound of this? -For these stupid tricks, \*(Me needs \*(FM. +For these stupid tricks, \*(We needs \*(FM. .\" ( apparently AM static was a potential concern ) -.New +.jBu The user interface, through prompts and help, intentionally implies that the delay interval is limited to tenths of a second. However, you're free to set any desired delay. @@ -1144,81 +1402,88 @@ seconds or less. For this experiment, under x-windows open an xterm and maximize it. Then do the following: +.Bd -literal -compact . provide a scheduling boost and tiny delay via: nice -n -10 top -d.09 - . keep sorted column highlighting \*F to minimize - path length + . keep sorted column highlighting \*F so as to + minimize path length . turn \*O reverse row highlighting for emphasis . try various sort columns (TIME/MEM work well), and normal or reverse sorts to bring the most active processes into view +.Ed What you'll see is a very busy Linux doing what he's always done for you, but there was no program available to illustrate this. -.New -Under an xterm using 'white-on-black' colors, try setting \*(Me's task color -to black and be sure that task highlighting is set to bold, not reverse. +.jBu +Under an xterm using 'white-on-black' colors, on \*(We's Color Mapping screen +set the task color to black and be sure that task highlighting is set to bold, +not reverse. Then set the delay interval to around .3 seconds. After bringing the most active processes into view, what you'll see are the ghostly images of just the currently running tasks. -.New +.jBu Delete the existing rcfile, or create a new symlink. -Start this new version then type 'T' (a secret key, \*(Xt 3c. TASK Area -Commands, Sorting) followed by 'W' and 'q'. +Start this new version then type 'T' (a secret key, +\*(Xt 4c. Task Area Commands, SORTING) followed by 'W' and 'q'. Finally, restart the program with -d0 (zero delay). -Your display will be refreshed at three times the rate of the former \*(Me, +Your display will be refreshed at three times the rate of the former \*(We, a 300% speed advantage. -As \*(Me climbs the TIME ladder, be as patient as you can while speculating -on whether or not \*(Me will ever reach the \*(Me. +As \*(We climbs the TIME ladder, be as patient as you can while speculating +on whether or not \*(We will ever reach the \*(We. .\" ...................................................................... -.SS 6b. Bouncing Windows -For these stupid tricks, \*(Me needs \*(AM. +.SS 7b. Bouncing Windows +.\" ---------------------------------------------------------------------- +For these stupid tricks, \*(We needs \*(AM. -.New +.jBu With 3 or 4 \*(TDs visible, pick any window other than the last -and turn idle processes \*F. +and turn idle processes \*F using the 'i' \*(CT. Depending on where you applied 'i', sometimes several \*(TDs are bouncing and -sometimes it's like an accordion, as \*(Me tries his best to allocate space. +sometimes it's like an accordion, as \*(We tries his best to allocate space. -.New -Set each window's summary lines differently: one with no memory; another with -no states; maybe one with nothing at all, just the message line. -Then hold down 'a' or 'w' and watch a variation on bouncing windows \*(EM +.jBu +Set each window's summary lines differently: one with no memory ('m'); another +with no states ('t'); maybe one with nothing at all, just the message line. +Then hold down 'a' or 'w' and watch a variation on bouncing windows \*(Em hopping windows. -.New -Display all 4 windows and for each, in turn, set idle processes to \*F. +.jBu +Display all 4 windows and for each, in turn, set idle processes to \*F using +the 'i' \*(CT. You've just entered the "extreme bounce" zone. .\" ...................................................................... -.SS 6c. The Big Bird Window +.SS 7c. The Big Bird Window +.\" ---------------------------------------------------------------------- This stupid trick also requires \*(AM. -.New +.jBu Display all 4 windows and make sure that 1:Def is the \*(CW. -Then, keep increasing window size until the all the other \*(TDs -are "pushed out of the nest". +Then, keep increasing window size with the 'n' \*(CI until all the other +\*(TDs are "pushed out of the nest". -When they've all been displaced, toggle between all visible/invisible windows. +When they've all been displaced, toggle between all visible/invisible windows +using the '_' \*(CT. Then ponder this: .br - is \*(Me fibbing or telling honestly your imposed truth? + is \*(We fibbing or telling honestly your imposed truth? +.PP + +.\" ---------------------------------------------------------------------- +.SH 8. BUGS +.\" ---------------------------------------------------------------------- +To report bugs, follow the instructions at: + http://www.debian.org/Bugs/Reporting .\" ---------------------------------------------------------------------- -.SH 7. BUGS -.\" ---------------------------------------------------------------------- -Send bug reports to: - Albert D\. Cahalan, - - -.\" ---------------------------------------------------------------------- -.SH 8. HISTORY Former top +.SH 9. HISTORY Former top .\" ---------------------------------------------------------------------- The original top was written by Roger Binns, based on Branko Lankester's ps program. @@ -1233,27 +1498,31 @@ Plus many other individuals contributed over the years. .\" ---------------------------------------------------------------------- -.SH 9. AUTHOR +.SH 10. AUTHOR .\" ---------------------------------------------------------------------- This entirely new and enhanced replacement was written by: - Jim / James C. Warner, + Jim Warner, .ig ( as a means to learn Linux, can you believe it? ) ( & he accidentally learned a little groff, too! ) + ( then after ten years he did it all over again! ) .. With invaluable help from: - Albert D\. Cahalan, - Craig Small, + Craig Small, +.\" Craig Small. +.\" Craig Small, +.\" Craig Small, + Albert Cahalan, .ig .rj 2 -.B -*-\fR few though they are, some yet believe\fB -*-\fR -.B -*-\~\~\~\~\~\~\~\fRin-the-\fBart\fR-of-programming\~\~\~\~\~\~\~\fB-*-\fR +.B -*-\fR few though they are, some yet believe\fB -*- \fR +.B -*-\~\~\~\~\~\~\~\fRin-the-\fBart\fR-of-programming\~\~\~\~\~\~\~\fB-*- \fR .. -.\" ---------------------------------------------------------------------- -.SH 10. SEE ALSO + \" ---------------------------------------------------------------------- +.SH 11. SEE Also .\" ---------------------------------------------------------------------- .BR free (1), .BR ps (1), @@ -1270,7 +1539,7 @@ With invaluable help from: \-*- .PD .in -3 -Copyright (c) 2002 \*(EM JC Warner & Associates, Ltd. +Copyright (c) 2002,2011 \*(Em James C. Warner Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or @@ -1288,8 +1557,8 @@ A copy of the license is included in the section entitled \(dqGNU Free Documentation License\(dq. .. . -.\" end: active doc |||||||||||||||||||||||||||||||||||||||||||||||||| -.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +.\" end: active doc |||||||||||||||||||||||||||||||||||||||||||||||||||||| +.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| .ig .\" ---------------------------------------------------------------------- @@ -1647,5 +1916,5 @@ to permit their use in free software. .IP "" .PP .. -.\" end: gfdl license |||||||||||||||||||||||||||||||||||||||||||||||| -.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +.\" end: gfdl license |||||||||||||||||||||||||||||||||||||||||||||||||||| +.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/top.c b/top.c index 1f581d1c..06841cbe 100644 --- a/top.c +++ b/top.c @@ -1,9 +1,8 @@ /* top.c - Source file: show Linux processes */ /* - * Copyright (c) 2002, by: James C. Warner + * Copyright (c) 2002-2011, by: James C. Warner * All rights reserved. 8921 Hilloway Road * Eden Prairie, Minnesota 55347 USA - * * * This file may be used subject to the terms and conditions of the * GNU Library General Public License Version 2, or any later version @@ -12,91 +11,81 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. - * - * For their contributions to this program, the author wishes to thank: - * Albert D. Cahalan, + */ +/* For contributions to this program, the author wishes to thank: * Craig Small, - * - * Changes by Albert Cahalan, 2002-2004. + * Albert D. Cahalan, */ #include #include #include #include -#include + #include #include #include #include +#include #include #include #include #include #include - -// Foul POS defines all sorts of stuff... -#include -#undef tab - +#include // foul sob, defines all sorts of stuff... +#undef tab +#undef TTY #include #include #include #include #include "proc/devname.h" -#include "proc/wchan.h" #include "proc/procps.h" #include "proc/readproc.h" -#include "proc/escape.h" #include "proc/sig.h" #include "proc/sysinfo.h" #include "proc/version.h" +#include "proc/wchan.h" #include "proc/whattime.h" #include "top.h" /*###### Miscellaneous global stuff ####################################*/ - /* The original and new terminal attributes */ -static struct termios Savedtty, - Rawtty; + /* The original and new terminal definitions + (only set when not in 'Batch' mode) */ +static struct termios Tty_original, // our inherited terminal definition +#ifdef TERMIOS_ONLY + Tty_tweaked, // for interactive 'line' input +#endif + Tty_raw; // for unsolicited input static int Ttychanged = 0; /* Program name used in error messages and local 'rc' file name */ static char *Myname; - /* Name of user config file (dynamically constructed) and our - 'Current' rcfile contents, initialized with defaults but may be - overridden with the local rcfile (old or new-style) values */ + /* The 'local' config file support */ static char Rc_name [OURPATHSZ]; static RCF_t Rc = DEF_RCFILE; - /* The run-time acquired page size */ + /* The run-time acquired page stuff */ static unsigned Page_size; -static unsigned page_to_kb_shift; +static unsigned Pg2K_shft = 0; - /* SMP Irix/Solaris mode */ -static int Cpu_tot; -static double pcpu_max_value; // usually 99.9, for %CPU display - /* assume no IO-wait stats, overridden if linux 2.5.41 */ -static const char *States_fmts = STATES_line2x4; + /* SMP, Irix/Solaris mode, Linux 2.5.xx support */ + /* (assume no IO-wait stats, overridden if linux 2.5.41) */ +static int Cpu_tot; +static float Cpu_pmax = 99.9; // for %CPU display (may later change!) +static const char *Cpu_States_fmts = STATES_line2x4; /* Specific process id monitoring support */ static pid_t Monpids [MONPIDMAX] = { 0 }; static int Monpidsidx = 0; /* A postponed error message */ -static char Msg_delayed [SMLBUFSIZ]; +static char Msg_delayed [MEDBUFSIZ]; static int Msg_awaiting = 0; -// This is the select() timeout. Clear it in sig handlers to avoid a race. -// (signal happens just as we are about to select() and thus does not -// break us out of the select(), causing us to delay until timeout) -static volatile struct timeval tv; -#define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0); - - /* Configurable Display support ##################################*/ - /* Current screen dimensions. note: the number of processes displayed is tracked on a per window basis (see the WIN_t). Max_lines is the total number of @@ -104,10 +93,6 @@ static volatile struct timeval tv; /* Current terminal screen size. */ static int Screen_cols, Screen_rows, Max_lines; -// set to 1 if writing to the last column would be troublesome -// (we don't distinguish the lowermost row from the other rows) -static int avoid_last_column; - /* This is really the number of lines needed to display the summary information (0 - nn), but is used as the relative row where we stick the cursor between frames. */ @@ -118,230 +103,267 @@ static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise PSDBopen = 0, // set to '1' if psdb opened (now postponed) Batch = 0, // batch mode, collect no input, dumb output Loops = -1, // number of iterations, -1 loops forever - Secure_mode = 0; // set if some functionality restricted + Secure_mode = 0, // set if some functionality restricted + Thread_mode = 0; // set w/ 'H' - show threads via readtask() - /* Some cap's stuff to reduce runtime calls -- - to accomodate 'Batch' mode, they begin life as empty strings */ -static char Cap_clr_eol [CAPBUFSIZ], - Cap_clr_eos [CAPBUFSIZ], - Cap_clr_scr [CAPBUFSIZ], - Cap_rmam [CAPBUFSIZ], - Cap_smam [CAPBUFSIZ], - Cap_curs_norm [CAPBUFSIZ], - Cap_curs_huge [CAPBUFSIZ], - Cap_home [CAPBUFSIZ], - Cap_norm [CAPBUFSIZ], - Cap_reverse [CAPBUFSIZ], - Caps_off [CAPBUFSIZ]; + /* Unchangeable cap's stuff built just once (if at all) and + thus NOT saved in a WIN_t's RCW_t. To accomodate 'Batch' + mode, they begin life as empty strings so the overlying + logic need not change ! */ +static char Cap_clr_eol [CAPBUFSIZ] = "", // global and/or static vars + Cap_nl_clreos [CAPBUFSIZ] = "", // are initialized to zeros! + Cap_clr_scr [CAPBUFSIZ] = "", // the assignments used here + Cap_curs_norm [CAPBUFSIZ] = "", // cost nothing but DO serve + Cap_curs_huge [CAPBUFSIZ] = "", // to remind people of those + Cap_curs_hide [CAPBUFSIZ] = "", // batch requirements! + Cap_home [CAPBUFSIZ] = "", + Cap_norm [CAPBUFSIZ] = "", + Cap_reverse [CAPBUFSIZ] = "", + Caps_off [CAPBUFSIZ] = "", + Caps_endline [CAPBUFSIZ] = ""; +#ifndef RMAN_IGNORED +static char Cap_rmam [CAPBUFSIZ] = "", + Cap_smam [CAPBUFSIZ] = ""; + /* set to 1 if writing to the last column would be troublesome + (we don't distinguish the lowermost row from the other rows) */ +static int Cap_avoid_eol = 0; +#endif static int Cap_can_goto = 0; /* Some optimization stuff, to reduce output demands... - The Pseudo_ guys are managed by wins_resize and frame_make. They + The Pseudo_ guys are managed by adj_geometry and frame_make. They are exploited in a macro and represent 90% of our optimization. The Stdout_buf is transparent to our code and regardless of whose buffer is used, stdout is flushed at frame end or if interactive. */ -static char *Pseudo_scrn; -static int Pseudo_row, Pseudo_cols, Pseudo_size; -#ifndef STDOUT_IOLBF +static char *Pseudo_screen; +static int Pseudo_row; +static size_t Pseudo_size; +#ifndef OFF_STDIOLBF // less than stdout's normal buffer but with luck mostly '\n' anyway static char Stdout_buf[2048]; #endif - - /* ////////////////////////////////////////////////////////////// */ - /* Special Section: multiple windows/field groups ---------------*/ - - /* The pointers to our four WIN_t's, and which of those is considered - the 'current' window (ie. which window is associated with any summ - info displayed and to which window commands are directed) */ -static WIN_t Winstk [GROUPSMAX], - *Curwin; + /* Our four WIN_t's, and which of those is considered the 'current' + window (ie. which window is associated with any summ info displayed + and to which window commands are directed) */ +static WIN_t Winstk [GROUPSMAX]; +static WIN_t *Curwin; /* Frame oriented stuff that can't remain local to any 1 function and/or that would be too cumbersome managed as parms, and/or that are simply more efficiently handled as globals - (first 2 persist beyond a single frame, changed infrequently) */ -static int Frames_libflags; // PROC_FILLxxx flags (0 = need new) -//atic int Frames_maxcmdln; // the largest from the 4 windows -static unsigned Frame_maxtask; // last known number of active tasks - // ie. current 'size' of proc table -static unsigned Frame_running, // state categories for this frame - Frame_sleepin, - Frame_stopped, - Frame_zombied; -static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu' -static int Frame_srtflg, // the subject window's sort direction - Frame_ctimes, // the subject window's ctimes flag - Frame_cmdlin; // the subject window's cmdlin flag - /* ////////////////////////////////////////////////////////////// */ + [ 'Frames_...' (plural) stuff persists beyond 1 frame ] + [ or are used in response to async signals received ! ] */ + // Frames_endpgm set by sig_endpgm(), no need to unset +static volatile int Frames_endpgm; // time to quit this shit + // Frames_paused set by sig_paused(), unset by pause_pgm() +static volatile int Frames_paused; // become a paused background job + // Frames_resize set by do_key() & sig_resize(), unset by calibrate_fields() +static volatile int Frames_resize; // time to rebuild all column headers +static int Frames_libflags; // PROC_FILLxxx flags +static int Frame_maxtask; // last known number of active tasks + // ie. current 'size' of proc table +static float Frame_etscale; // so we can '*' vs. '/' WHEN 'pcpu' +static unsigned Frame_running, // state categories for this frame + Frame_sleepin, + Frame_stopped, + Frame_zombied; +static int Frame_srtflg, // the subject window's sort direction + Frame_ctimes, // the subject window's ctimes flag + Frame_cmdlin; // the subject window's cmdlin flag + /* Support for 'history' processing so we can calculate %cpu */ +static int HHist_siz; // max number of HST_t structs +static HST_t *PHist_sav, // alternating 'old/new' HST_t anchors + *PHist_new; +#ifndef OFF_HST_HASH +#define HHASH_SIZ 1024 +static int HHash_one [HHASH_SIZ], // actual hash tables ( hereafter known + HHash_two [HHASH_SIZ], // as PHash_sav/PHash_new ) + HHash_nul [HHASH_SIZ]; // 'empty' hash table image +static int *PHash_sav = HHash_one, // alternating 'old/new' hash tables + *PHash_new = HHash_two; +#endif /*###### Sort callbacks ################################################*/ /* - * These happen to be coded in the same order as the enum 'pflag' - * values. Note that 2 of these routines serve double duty -- - * 2 columns each. + * These happen to be coded in the enum identifier alphabetic order, + * not the order of the enum 'pflgs' value. Also note that a callback + * routine may serve more than one column. */ -SCB_NUMx(P_PID, XXXID) -SCB_NUMx(P_PPD, ppid) -SCB_STRx(P_URR, ruser) -SCB_NUMx(P_UID, euid) -SCB_STRx(P_URE, euser) -SCB_STRx(P_GRP, egroup) -SCB_NUMx(P_TTY, tty) -SCB_NUMx(P_PRI, priority) -SCB_NUMx(P_NCE, nice) -SCB_NUMx(P_CPN, processor) -SCB_NUM1(P_CPU, pcpu) - // also serves P_TM2 ! -static int sort_P_TME (const proc_t **P, const proc_t **Q) -{ +SCB_STRV(CGR, 1, cgroup, cgroup[0]) +SCB_STRV(CMD, Frame_cmdlin, cmdline, cmd) +SCB_NUM1(COD, trs) +SCB_NUMx(CPN, processor) +SCB_NUM1(CPU, pcpu) +SCB_NUM1(DAT, drs) +SCB_NUM1(DRT, dt) +SCB_NUM1(FLG, flags) +SCB_NUM1(FL1, maj_flt) +SCB_NUM1(FL2, min_flt) +SCB_NUMx(GID, egid) +SCB_STRS(GRP, egroup) +SCB_NUMx(NCE, nice) +#ifdef OOMEM_ENABLE +SCB_NUM1(OOA, oom_adj) +SCB_NUM1(OOM, oom_score) +#endif +SCB_NUMx(PGD, pgrp) +SCB_NUMx(PID, tid) +SCB_NUMx(PPD, ppid) +SCB_NUMx(PRI, priority) +SCB_NUM1(RES, resident) // also serves MEM ! +SCB_NUM1(SHR, share) +SCB_NUM1(SID, session) +SCB_NUMx(STA, state) +SCB_NUM2(SWP, size, resident) +SCB_NUMx(THD, nlwp) + // also serves TM2 ! +static int SCB_NAME(TME) (const proc_t **P, const proc_t **Q) { if (Frame_ctimes) { - if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) - < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + if (((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) + < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime)) return SORT_lt; - if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) - > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + if (((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) + > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime)) return SORT_gt; } else { - if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime)) + if (((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime)) return SORT_lt; - if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime)) + if (((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime)) return SORT_gt; } return SORT_eq; } +SCB_NUM1(TPG, tpgid) +SCB_NUMx(TTY, tty) +SCB_NUMx(UED, euid) +SCB_STRS(UEN, euser) +SCB_NUMx(URD, ruid) +SCB_STRS(URN, ruser) +SCB_NUMx(USD, suid) +SCB_STRS(USN, suser) +SCB_NUM1(VRT, size) +SCB_NUM1(WCH, wchan) -SCB_NUM1(P_VRT, size) -SCB_NUM2(P_SWP, size, resident) -SCB_NUM1(P_RES, resident) // also serves P_MEM ! -SCB_NUM1(P_COD, trs) -SCB_NUM1(P_DAT, drs) -SCB_NUM1(P_SHR, share) -SCB_NUM1(P_FLT, maj_flt) -SCB_NUM1(P_DRT, dt) -SCB_NUMx(P_STA, state) - -static int sort_P_CMD (const proc_t **P, const proc_t **Q) -{ - /* if a process doesn't have a cmdline, we'll consider it a kernel thread - -- since displayed tasks are given special treatment, we must too */ - if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) { - if (!(*Q)->cmdline) return Frame_srtflg * -1; - if (!(*P)->cmdline) return Frame_srtflg; - return Frame_srtflg * - strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln); - } - // this part also handles the compare if both are kernel threads - return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd); -} - -SCB_NUM1(P_WCH, wchan) -SCB_NUM1(P_FLG, flags) - - /* ///////////////////////////////// special sort for prochlp() ! */ -static int sort_HST_t (const HST_t *P, const HST_t *Q) -{ +#ifdef OFF_HST_HASH + /* special sort for prochlp() ! -------------------------- */ +static int sort_HST_t (const HST_t *P, const HST_t *Q) { return P->pid - Q->pid; } - - +#endif + /*###### Tiny useful routine(s) ########################################*/ /* - * This routine isolates ALL user INPUT and ensures that we - * wont be mixing I/O from stdio and low-level read() requests */ -static int chin (int ech, char *buf, unsigned cnt) -{ - int rc; - - fflush(stdout); - if (!ech) - rc = read(STDIN_FILENO, buf, cnt); - else { - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); - rc = read(STDIN_FILENO, buf, cnt); - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); - } - // may be the beginning of a lengthy escape sequence - tcflush(STDIN_FILENO, TCIFLUSH); - return rc; // note: we do NOT produce a vaid 'string' -} - - -// This routine simply formats whatever the caller wants and -// returns a pointer to the resulting 'const char' string... + * This routine simply formats whatever the caller wants and + * returns a pointer to the resulting 'const char' string... */ static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2))); -static const char *fmtmk (const char *fmts, ...) -{ +static const char *fmtmk (const char *fmts, ...) { static char buf[BIGBUFSIZ]; // with help stuff, our buffer - va_list va; // requirements exceed 1k + va_list va; // requirements now exceed 1k va_start(va, fmts); vsnprintf(buf, sizeof(buf), fmts, va); va_end(va); return (const char *)buf; -} +} // end: fmtmk -// This guy is just our way of avoiding the overhead of the standard -// strcat function (should the caller choose to participate) -static inline char *scat (char *restrict dst, const char *restrict src) -{ + /* + * This guy is just our way of avoiding the overhead of the standard + * strcat function (should the caller choose to participate) */ +static inline char *scat (char *dst, const char *src) { while (*dst) dst++; while ((*(dst++) = *(src++))); return --dst; -} +} // end: scat -// Trim the rc file lines and any 'open_psdb_message' result which arrives -// with an inappropriate newline (thanks to 'sysmap_mmap') -static char *strim_0 (char *str) -{ - static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape + /* + * Trim the rc file lines and any 'open_psdb_message' which arrives + * with an inappropriate newline (thanks to 'sysmap_mmap') */ +static char *strim (char *str) { + static const char ws[] = "\b\f\n\r\t\v\x1b\x9b"; // 0x1b + 0x9b are escape char *p; - if ((p = strpbrk(str, ws))) *p = 0; + if ((p = strpbrk(str, ws))) *p = '\0'; return str; -} +} // end: strim -// This guy just facilitates Batch and protects against dumb ttys -// -- we'd 'inline' him but he's only called twice per frame, -// yet used in many other locations. -static const char *tg2 (int x, int y) -{ + /* + * This guy just facilitates Batch and protects against dumb ttys + * -- we'd 'inline' him but he's only called twice per frame, + * yet used in many other locations. */ +static const char *tg2 (int x, int y) { + // it's entirely possible we're trying for an invalid row... return Cap_can_goto ? tgoto(cursor_address, x, y) : ""; -} - - +} // end: tg2 + /*###### Exit/Interrput routines #######################################*/ -// The usual program end -- called only by functions in this section. -static void bye_bye (FILE *fp, int eno, const char *str) NORETURN; -static void bye_bye (FILE *fp, int eno, const char *str) -{ - if (!Batch) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); - putp(tg2(0, Screen_rows)); - putp(Cap_curs_norm); - putp(Cap_smam); - putp("\n"); + /* + * The real program end */ +static void bye_bye (const char *str) NORETURN; +static void bye_bye (const char *str) { + if (Ttychanged) { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_original); + putp(tg2(0, Screen_rows)); + putp(Cap_curs_norm); +#ifndef RMAN_IGNORED + putp(Cap_smam); +#endif + putp("\n"); + } fflush(stdout); -//#define ATEOJ_REPORT -#ifdef ATEOJ_REPORT - - fprintf(fp, +#ifdef ATEOJ_RPTSTD +{ proc_t *p; + if (!str) { fprintf(stderr, + "\n%s's Summary report:" + "\n\tProgram" + "\n\t Linux version = %u.%u.%u, %s" + "\n\t Hertz = %u (%u bytes, %u-bit time)" + "\n\t Page_size = %d, Cpu_tot = %d" + "\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page), HHist_siz = %u" + "\n\t sizeof(proc_t) = %u, sizeof(proc_t.cmd) = %u, sizeof(proc_t *) = %u" + "\n\t Frames_libflags = %08lX" + "\n\t SCREENMAX = %u, ROWMINSIZ = %u, ROWMAXSIZ = %u" "\n\tTerminal: %s" "\n\t device = %s, ncurses = v%s" "\n\t max_colors = %d, max_pairs = %d" "\n\t Cap_can_goto = %s" "\n\t Screen_cols = %d, Screen_rows = %d" - "\n\t Max_lines = %d, most recent Pseudo_size = %d" + "\n\t Max_lines = %d, most recent Pseudo_size = %u" +#ifndef OFF_STDIOLBF + "\n\t Stdout_buf = %u, BUFSIZ = %u" +#endif + "\n\tWindows and Curwin->" + "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d" + "\n\t winname = %s, grpname = %s" +#ifdef CASEUP_HEXES + "\n\t winflags = %08X, maxpflgs = %d" +#else + "\n\t winflags = %08x, maxpflgs = %d" +#endif + "\n\t fieldscur = %s, sortindx = %d" + "\n\t maxtasks = %d, varcolsz = %d, winlines = %d" + "\n\t strlen(columnhdr) = %d" "\n" + , __func__ + , LINUX_VERSION_MAJOR(linux_version_code) + , LINUX_VERSION_MINOR(linux_version_code) + , LINUX_VERSION_PATCH(linux_version_code) + , procps_version + , (unsigned)Hertz, (unsigned)sizeof(Hertz), (unsigned)sizeof(Hertz) * 8 + , Page_size, Cpu_tot + , (unsigned) sizeof(CPU_t), (unsigned)sizeof(HST_t), Page_size / (unsigned)sizeof(HST_t), HHist_siz + , (unsigned)sizeof(*p), (unsigned)sizeof(p->cmd), (unsigned)sizeof(p) + , (long)Frames_libflags + , (unsigned)SCREENMAX, (unsigned)ROWMINSIZ, (unsigned)ROWMAXSIZ #ifdef PRETENDNOCAP , "dumb" #else @@ -351,151 +373,176 @@ static void bye_bye (FILE *fp, int eno, const char *str) , max_colors, max_pairs , Cap_can_goto ? "yes" : "No!" , Screen_cols, Screen_rows - , Max_lines, Pseudo_size - ); - - fprintf(fp, -#ifndef STDOUT_IOLBF - "\n\t Stdout_buf = %d, BUFSIZ = %u" + , Max_lines, (unsigned)Pseudo_size +#ifndef OFF_STDIOLBF + , (unsigned)sizeof(Stdout_buf), (unsigned)BUFSIZ #endif - "\n\tWindows and Curwin->" - "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d" - "\n\t rc.winname = %s, grpname = %s" - "\n\t rc.winflags = %08x, maxpflgs = %d" - "\n\t rc.fieldscur = %s" - "\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d" - "\n\t rc.sortindx = %d" - "\n" -#ifndef STDOUT_IOLBF - , sizeof(Stdout_buf), (unsigned)BUFSIZ -#endif - , sizeof(WIN_t), GROUPSMAX + , (unsigned)sizeof(WIN_t), GROUPSMAX , Curwin->rc.winname, Curwin->grpname , Curwin->rc.winflags, Curwin->maxpflgs - , Curwin->rc.fieldscur - , Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln - , Curwin->rc.sortindx + , Curwin->rc.fieldscur, Curwin->rc.sortindx + , Curwin->rc.maxtasks, Curwin->varcolsz, Curwin->winlines + , (int)strlen(Curwin->columnhdr) ); - - fprintf(fp, - "\n\tProgram" - "\n\t Linux version = %u.%u.%u, %s" - "\n\t Hertz = %u (%u bytes, %u-bit time)" - "\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u" - "\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)" - "\n" - , LINUX_VERSION_MAJOR(linux_version_code) - , LINUX_VERSION_MINOR(linux_version_code) - , LINUX_VERSION_PATCH(linux_version_code) - , procps_version - , (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8 - , Page_size, Cpu_tot, sizeof(proc_t) - , sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t) - ); - - -#endif - - if (str) fputs(str, fp); - exit(eno); + } } +#endif // end: ATEOJ_RPTSTD +#ifndef OFF_HST_HASH +#ifdef ATEOJ_RPTHSH + if (!str) { + int i, j, pop, total_occupied, maxdepth, maxdepth_sav, numdepth + , cross_foot, sz = HHASH_SIZ * (unsigned)sizeof(int); + int depths[HHASH_SIZ]; - /* - * Normal end of execution. - * catches: - * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */ -// FIXME: can't do this shit in a signal handler -static void end_pgm (int sig) NORETURN; -static void end_pgm (int sig) -{ - if(sig) - sig |= 0x80; // for a proper process exit code - bye_bye(stdout, sig, NULL); -} + for (i = 0, total_occupied = 0, maxdepth = 0; i < HHASH_SIZ; i++) { + int V = PHash_new[i]; + j = 0; + if (-1 < V) { + ++total_occupied; + while (-1 < V) { + V = PHist_new[V].lnk; + if (-1 < V) j++; + } + } + depths[i] = j; + if (maxdepth < j) maxdepth = j; + } + maxdepth_sav = maxdepth; + fprintf(stderr, + "\n%s's Supplementary HASH report:" + "\n\tTwo Tables providing for %d entries each + 1 extra for 'empty' image" + "\n\t%dk (%d bytes) per table, %d total bytes (including 'empty' image)" + "\n\tResults from latest hash (PHash_new + PHist_new)..." + "\n" + "\n\tTotal hashed = %d" + "\n\tLevel-0 hash entries = %d (%d%% occupied)" + "\n\tMax Depth = %d" + "\n\n" + , __func__ + , HHASH_SIZ, sz / 1024, sz, sz * 3 + , Frame_maxtask + , total_occupied, (total_occupied * 100) / HHASH_SIZ + , maxdepth + 1); - /* - * Standard error handler to normalize the look of all err o/p */ -static void std_err (const char *str) NORETURN; -static void std_err (const char *str) -{ - static char buf[SMLBUFSIZ]; + if (total_occupied) { + for (pop = total_occupied, cross_foot = 0; maxdepth; maxdepth--) { + for (i = 0, numdepth = 0; i < HHASH_SIZ; i++) + if (depths[i] == maxdepth) ++numdepth; + fprintf(stderr, + "\t %5d (%3d%%) hash table entries at depth %d\n" + , numdepth, (numdepth * 100) / total_occupied, maxdepth + 1); + pop -= numdepth; + cross_foot += numdepth; + if (0 == pop && cross_foot == total_occupied) break; + } + if (pop) { + fprintf(stderr, "\t %5d (%3d%%) unchained hash table entries\n" + , pop, (pop * 100) / total_occupied); + cross_foot += pop; + } + fprintf(stderr, + "\t -----\n" + "\t %5d total entries occupied\n", cross_foot); - fflush(stdout); - /* we'll use our own buffer so callers can still use fmtmk() and, yes the - leading tab is not the standard convention, but the standard is wrong - -- OUR msg won't get lost in screen clutter, like so many others! */ - snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str); - if (!Ttychanged) { - fprintf(stderr, "%s\n", buf); + if (maxdepth_sav) { + fprintf(stderr, "PIDs at max depth: "); + for (i = 0; i < HHASH_SIZ; i++) + if (depths[i] == maxdepth_sav) { + j = PHash_new[i]; + fprintf(stderr, "\n\tpos %4d: %05d", i, PHist_new[j].pid); + while (-1 < j) { + j = PHist_new[j].lnk; + if (-1 < j) fprintf(stderr, ", %05d", PHist_new[j].pid); + } + } + fprintf(stderr, "\n"); + } + } + } +#endif // end: ATEOJ_RPTHSH +#endif // end: OFF_HST_HASH + + if (str) { + fputs(str, stderr); exit(1); } - /* not to worry, he'll change our exit code to 1 due to 'buf' */ - bye_bye(stderr, 1, buf); -} + exit(0); +} // end: bye_bye /* - * Standard out handler */ -static void std_out (const char *str) NORETURN; -static void std_out (const char *str) -{ - static char buf[SMLBUFSIZ]; + * Standard error handler to normalize the look of all err output */ +static void error_exit (const char *str) NORETURN; +static void error_exit (const char *str) { + static char buf[MEDBUFSIZ]; - fflush(stdout); /* we'll use our own buffer so callers can still use fmtmk() and, yes the leading tab is not the standard convention, but the standard is wrong -- OUR msg won't get lost in screen clutter, like so many others! */ snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str); - if (!Ttychanged) { - fprintf(stdout, "%s\n", buf); - exit(0); - } - bye_bye(stdout, 0, buf); -} + bye_bye(buf); +} // end: error_exit /* - * Suspend ourself. - * catches: - * SIGTSTP, SIGTTIN and SIGTTOU */ -// FIXME: can't do this shit in a signal handler! -static void suspend (int dont_care_sig) -{ - (void)dont_care_sig; - /* reset terminal */ - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + * Called in response to Frames_paused (tku: sig_paused) */ +static void pause_pgm (void) { + Frames_paused = 0; + // reset terminal (maybe) + if (Ttychanged) tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_original); putp(tg2(0, Screen_rows)); putp(Cap_curs_norm); +#ifndef RMAN_IGNORED putp(Cap_smam); - putp("\n"); +#endif fflush(stdout); raise(SIGSTOP); - /* later, after SIGCONT... */ - ZAP_TIMEOUT - if (!Batch) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); - putp(Cap_clr_scr); + // later, after SIGCONT... + if (Ttychanged) tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_raw); +#ifndef RMAN_IGNORED putp(Cap_rmam); -} +#endif +} // end: pause_pgm -/*###### Misc Color/Display support ####################################*/ - - /* macro to test if a basic (non-color) capability is valid - thanks: Floyd Davidson */ -#define tIF(s) s ? s : "" -#define CAPCOPY(dst,src) src && strcpy(dst,src) - /* - * Make the appropriate caps/color strings and set some - * lengths which are used to distinguish twix the displayed - * columns and an actual printed row! + * Catches: + * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */ +static void sig_endpgm (int dont_care_sig) { + (void)dont_care_sig; + Frames_endpgm = 1; +} // end: sig_endpgm + + + /* + * Catches: + * SIGTSTP, SIGTTIN and SIGTTOU */ +static void sig_paused (int dont_care_sig) { + (void)dont_care_sig; + Frames_paused = 1; +} // end: sig_paused + + + /* + * Catches: + * SIGCONT and SIGWINCH */ +static void sig_resize (int dont_care_sig) { + (void)dont_care_sig; + Frames_resize = 1; +} // end: sig_resize + +/*###### Misc Color/Display support ####################################*/ + + /* + * Make the appropriate caps/color strings for a window/field group. * note: we avoid the use of background color so as to maximize * compatibility with the user's xterm settings */ -static void capsmk (WIN_t *q) -{ +static void capsmk (WIN_t *q) { + /* macro to test if a basic (non-color) capability is valid + thanks: Floyd Davidson */ + #define tIF(s) s ? s : "" static int capsdone = 0; // we must NOT disturb our 'empty' terminfo strings! @@ -503,38 +550,41 @@ static void capsmk (WIN_t *q) // these are the unchangeable puppies, so we only do 'em once if (!capsdone) { - CAPCOPY(Cap_clr_eol, clr_eol); - CAPCOPY(Cap_clr_eos, clr_eos); - CAPCOPY(Cap_clr_scr, clear_screen); - - if (!eat_newline_glitch) { // we like the eat_newline_glitch - CAPCOPY(Cap_rmam, exit_am_mode); - CAPCOPY(Cap_smam, enter_am_mode); - if (!*Cap_rmam || !*Cap_smam) { // need both + STRLCPY(Cap_clr_eol, tIF(clr_eol)) + STRLCPY(Cap_clr_scr, tIF(clear_screen)) + // due to leading newline, only 1 function may use this (and carefully) + snprintf(Cap_nl_clreos, sizeof(Cap_nl_clreos), "\n%s", tIF(clr_eos)); + STRLCPY(Cap_curs_huge, tIF(cursor_visible)) + STRLCPY(Cap_curs_norm, tIF(cursor_normal)) + STRLCPY(Cap_curs_hide, tIF(cursor_invisible)) + STRLCPY(Cap_home, tIF(cursor_home)) + STRLCPY(Cap_norm, tIF(exit_attribute_mode)) + STRLCPY(Cap_reverse, tIF(enter_reverse_mode)) +#ifndef RMAN_IGNORED + if (!eat_newline_glitch) { + STRLCPY(Cap_rmam, tIF(exit_am_mode)) + STRLCPY(Cap_smam, tIF(enter_am_mode)) + if (!*Cap_rmam || !*Cap_smam) { *Cap_rmam = '\0'; *Cap_smam = '\0'; - if (auto_right_margin) { - avoid_last_column = 1; - } + if (auto_right_margin) + Cap_avoid_eol = 1; } + putp(Cap_rmam); } - - CAPCOPY(Cap_curs_huge, cursor_visible); - CAPCOPY(Cap_curs_norm, cursor_normal); - CAPCOPY(Cap_home, cursor_home); - CAPCOPY(Cap_norm, exit_attribute_mode); - CAPCOPY(Cap_reverse, enter_reverse_mode); - +#endif snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair)); + snprintf(Caps_endline, sizeof(Caps_endline), "%s%s", Caps_off, Cap_clr_eol); if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1; capsdone = 1; } + /* the key to NO run-time costs for configurable colors -- we spend a little time with the user now setting up our terminfo strings, and the job's done until he/she/it has a change-of-heart */ - strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode)); + STRLCPY(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode)) if (CHKw(q, Show_COLORS) && max_colors > 0) { - strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr)); + STRLCPY(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr)) snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s" , tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse); snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s" @@ -545,70 +595,83 @@ static void capsmk (WIN_t *q) , Caps_off, tparm(set_a_foreground, q->rc.taskclr)); } else { q->capclr_sum[0] = '\0'; - strcpy(q->capclr_msg, Cap_reverse); - strcpy(q->capclr_pmt, q->cap_bold); - strcpy(q->capclr_hdr, Cap_reverse); - strcpy(q->capclr_rownorm, Cap_norm); + STRLCPY(q->capclr_msg, Cap_reverse) + STRLCPY(q->capclr_pmt, q->cap_bold) + STRLCPY(q->capclr_hdr, Cap_reverse) + STRLCPY(q->capclr_rownorm, Cap_norm) } + // composite(s), so we do 'em outside and after the if snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s" , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse); - q->len_rownorm = strlen(q->capclr_rownorm); - q->len_rowhigh = strlen(q->capclr_rowhigh); - -#undef tIF -} + #undef tIF +} // end: capsmk -// Show an error, but not right now. -// Due to the postponed opening of ksym, using open_psdb_message, -// if P_WCH had been selected and the program is restarted, the -// message would otherwise be displayed prematurely. + /* + * Show an error, but not right now. + * Due to the postponed opening of ksym, using open_psdb_message, + * if P_WCH had been selected and the program is restarted, the + * message would otherwise be displayed prematurely. */ static void msg_save (const char *fmts, ...) __attribute__((format(printf,1,2))); -static void msg_save (const char *fmts, ...) -{ - char tmp[SMLBUFSIZ]; +static void msg_save (const char *fmts, ...) { + char tmp[MEDBUFSIZ]; va_list va; va_start(va, fmts); vsnprintf(tmp, sizeof(tmp), fmts, va); va_end(va); - /* we'll add some extra attention grabbers to whatever this is */ - snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim_0(tmp)); + // we'll add some extra attention grabbers to whatever this is + snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim(tmp)); Msg_awaiting = 1; -} +} // end: msg_save /* - * Show an error message (caller may include a '\a' for sound) */ -static void show_msg (const char *str) -{ - PUTT("%s%s %s %s%s", - tg2(0, Msg_row), - Curwin->capclr_msg, - str, - Caps_off, - Cap_clr_eol - ); + * Show an error message (caller may include '\a' for sound) */ +static void show_msg (const char *str) { + PUTT("%s%s %.*s %s%s" + , tg2(0, Msg_row) + , Curwin->capclr_msg + , Screen_cols - 2 + , str + , Caps_off + , Cap_clr_eol); fflush(stdout); - sleep(MSG_SLEEP); + usleep(MSG_USLEEP); Msg_awaiting = 0; -} +} // end: show_msg /* - * Show an input prompt + larger cursor */ -static void show_pmt (const char *str) -{ - PUTT("%s%s%s: %s%s", - tg2(0, Msg_row), - Curwin->capclr_pmt, - str, - Cap_curs_huge, - Caps_off - ); + * Show an input prompt + larger cursor (if possible) */ +static int show_pmt (const char *str) { + PUTT("%s%s%.*s: %s%s%s" + , tg2(0, Msg_row) + , Curwin->capclr_pmt + , Screen_cols - 2 + , str + , Cap_curs_huge + , Caps_off + , Cap_clr_eol); fflush(stdout); -} + // +2 for the ': ' characters we added (if anybody cares)... + return (int)strlen(str) + 2; +} // end: show_pmt + + + /* + * Show a special coordinate message, in support of scrolling */ +static inline void show_scroll (void) { + char tmp[SMLBUFSIZ]; + + snprintf(tmp, sizeof(tmp) + , "scroll coordinates: y = %d/%d (tasks), x = %d/%d (fields)" + , Curwin->begtask + 1, Frame_maxtask + , Curwin->begpflg + 1, Curwin->totpflgs); + PUTT("%s%s %.*s%s", tg2(0, Msg_row), Caps_off, Screen_cols - 2, tmp, Cap_clr_eol); + putp(tg2(0, Msg_row)); +} // end: show_scroll /* @@ -636,117 +699,321 @@ static void show_pmt (const char *str) * terminfo string truncation, such non-display stuff should * be placed at the beginning of a "short" line. * (and as for tabs, gimme 1 more color then no worries, mate) */ -static void show_special (int interact, const char *glob) -{ /* note: the following is for documentation only, +static void show_special (int interact, const char *glob) { + /* note: the following is for documentation only, the real captab is now found in a group's WIN_t ! +------------------------------------------------------+ | char *captab[] = { : Cap's/Delim's | - | Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 | - | Sum_color, = \03 | - | Msg_color, Pmt_color, = \04, \05 | - | Hdr_color, = \06 | - | Row_color_high, = \07 | - | Row_color_norm }; = \10 [octal!] | + | Cap_norm, Cap_norm, = \00, \01, | + | cap_bold, capclr_sum, = \02, \03, | + | capclr_msg, capclr_pmt, = \04, \05, | + | capclr_hdr, = \06, | + | capclr_rowhigh, = \07, | + | capclr_rownorm }; = \10 [octal!] | +------------------------------------------------------+ */ - char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ]; + /* ( pssst, after adding the termcap transitions, row may ) + ( exceed 300+ bytes, even in an 80x24 terminal window! ) */ + char tmp[SMLBUFSIZ], lin[MEDBUFSIZ], row[LRGBUFSIZ]; char *rp, *cap, *lin_end, *sub_beg, *sub_end; int room; - /* handle multiple lines passed in a bunch */ + // handle multiple lines passed in a bunch while ((lin_end = strchr(glob, '\n'))) { - - /* create a local copy we can extend and otherwise abuse */ - size_t amt = lin_end - glob; - if(amt > sizeof lin - 1) - amt = sizeof lin - 1; // shit happens - memcpy(lin, glob, amt); - /* zero terminate this part and prepare to parse substrings */ - lin[amt] = '\0'; + // create a local copy we can extend and otherwise abuse + memcpy(lin, glob, (unsigned)(lin_end - glob)); + // zero terminate this part and prepare to parse substrings + lin[lin_end - glob] = '\0'; room = Screen_cols; sub_beg = sub_end = lin; *(rp = row) = '\0'; while (*sub_beg) { switch (*sub_end) { - case 0: /* no end delim, captab makes normal */ - *(sub_end + 1) = '\0'; /* extend str end, then fall through */ + case 0: // no end delim, captab makes normal + *(sub_end + 1) = '\0'; // extend str end, then fall through case 1 ... 8: cap = Curwin->captab[(int)*sub_end]; *sub_end = '\0'; snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off); - amt = strlen(tmp); - if(rp - row + amt + 1 > sizeof row) - goto overflow; // shit happens rp = scat(rp, tmp); room -= (sub_end - sub_beg); sub_beg = ++sub_end; break; - default: /* nothin' special, just text */ + default: // nothin' special, just text ++sub_end; } - if (unlikely(0 >= room)) break; /* skip substrings that won't fit */ + if (0 >= room) break; // skip substrings that won't fit } -overflow: + if (interact) PUTT("%s%s\n", row, Cap_clr_eol); - else PUFF("%s%s\n", row, Cap_clr_eol); - glob = ++lin_end; /* point to next line (maybe) */ - } /* end: while 'lines' */ + else PUFF("%s%s\n", row, Caps_endline); + glob = ++lin_end; // point to next line (maybe) + + } // end: while 'lines' /* If there's anything left in the glob (by virtue of no trailing '\n'), it probably means caller wants to retain cursor position on this final line. That, in turn, means we're interactive and so we'll just do our 'fit-to-screen' thingy... */ if (*glob) PUTT("%.*s", Screen_cols, glob); -} +} // end: show_special + +/*###### Low Level Memory/Keyboard support #############################*/ + + /* + * Handle our own memory stuff without the risk of leaving the + * user's terminal in an ugly state should things go sour. */ + +static void *alloc_c (size_t num) MALLOC; +static void *alloc_c (size_t num) { + void *pv; + + if (!num) ++num; + if (!(pv = calloc(1, num))) + error_exit("failed memory allocate"); + return pv; +} // end: alloc_c -/*###### Small Utility routines ########################################*/ +static void *alloc_r (void *ptr, size_t num) MALLOC; +static void *alloc_r (void *ptr, size_t num) { + void *pv; -// Get a string from the user -static char *ask4str (const char *prompt) -{ - static char buf[GETBUFSIZ]; + if (!num) ++num; + if (!(pv = realloc(ptr, num))) + error_exit("failed memory re-allocate"); + return pv; +} // end: alloc_r + + + /* + * This routine isolates ALL user INPUT and ensures that we + * wont be mixing I/O from stdio and low-level read() requests */ +static int chin (int ech, char *buf, unsigned cnt) { + fd_set fs; + int rc = -1; + + fflush(stdout); +#ifdef TERMIOS_ONLY + if (ech) { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_tweaked); + rc = read(STDIN_FILENO, buf, cnt); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Tty_raw); + } else { + FD_ZERO(&fs); + FD_SET(STDIN_FILENO, &fs); + if (0 < select(STDIN_FILENO + 1, &fs, NULL, NULL, NULL)) + rc = read(STDIN_FILENO, buf, cnt); + } +#else + (void)ech; + FD_ZERO(&fs); + FD_SET(STDIN_FILENO, &fs); + if (0 < select(STDIN_FILENO + 1, &fs, NULL, NULL, NULL)) + rc = read(STDIN_FILENO, buf, cnt); +#endif + + // zero means EOF, might happen if we erroneously get detached from terminal + if (0 == rc) bye_bye(NULL); + + // it may have been the beginning of a lengthy escape sequence + tcflush(STDIN_FILENO, TCIFLUSH); + + // note: we do NOT produce a vaid 'string' + return rc; +} // end: chin + + + /* + * Support for single keystroke input AND escaped cursor motion keys + * note: we support more keys than we currently need, in case + * we attract new consumers in the future */ +static int keyin (int init) { + static char buf12[CAPBUFSIZ], buf13[CAPBUFSIZ] + , buf14[CAPBUFSIZ], buf15[CAPBUFSIZ]; + static struct { + const char *str; + int key; + } tinfo_tab[] = { + { "\n", kbd_ENTER }, { NULL, kbd_UP }, { NULL, kbd_DOWN }, + { NULL, kbd_RIGHT }, { NULL, kbd_LEFT }, { NULL, kbd_PGUP }, + { NULL, kbd_PGDN }, { NULL, kbd_END }, { NULL, kbd_HOME }, + { NULL, kbd_BKSP }, { NULL, kbd_INS }, { NULL, kbd_DEL }, + // next 4 destined to be meta + arrow keys... + { buf12, kbd_PGUP }, { buf13, kbd_PGDN }, + { buf14, kbd_END }, { buf15, kbd_HOME }, + // remainder are alternatives for above, just in case + // ( the k,j,l,h entries are meta key + vim cursor motion keys ) + { "\033\\",kbd_UP }, { "\033/", kbd_DOWN }, { "\033>", kbd_RIGHT }, + { "\033<", kbd_LEFT }, { "\033k", kbd_UP }, { "\033j", kbd_DOWN }, + { "\033l", kbd_RIGHT }, { "\033h", kbd_LEFT } }; + char buf[SMLBUFSIZ], *pb; + int i; + + if (init) { + #define tOk(s) s ? s : "" + tinfo_tab[1].str = tOk(key_up); + tinfo_tab[2].str = tOk(key_down); + tinfo_tab[3].str = tOk(key_right); + tinfo_tab[4].str = tOk(key_left); + tinfo_tab[5].str = tOk(key_ppage); + tinfo_tab[6].str = tOk(key_npage); + tinfo_tab[7].str = tOk(key_end); + tinfo_tab[8].str = tOk(key_home); + tinfo_tab[9].str = tOk(key_backspace); + tinfo_tab[10].str = tOk(key_ic); + tinfo_tab[11].str = tOk(key_dc); + STRLCPY(buf12, fmtmk("\033%s", tOk(key_up))); + STRLCPY(buf13, fmtmk("\033%s", tOk(key_down))); + STRLCPY(buf14, fmtmk("\033%s", tOk(key_right))); + STRLCPY(buf15, fmtmk("\033%s", tOk(key_left))); + // next is critical so returned results match bound terminfo keys + putp(tOk(keypad_xmit)); + return 0; + #undef tOk + } + + memset(buf, '\0', sizeof(buf)); + if (1 > chin(0, buf, sizeof(buf)-1)) return 0; + + /* some emulators implement 'key repeat' too well and we get duplicate + key sequences -- so we'll focus on the last escaped sequence, while + also allowing use of the meta key... */ + if (!(pb = strrchr(buf, '\033'))) pb = buf; + else if (pb > buf && '\033' == *(pb - 1)) --pb; + + for (i = 0; i < MAXTBL(tinfo_tab); i++) + if (!strcmp(tinfo_tab[i].str, pb)) + return tinfo_tab[i].key; + + // no match, so we'll return single keystrokes only + if (buf[1]) return 0; + return buf[0]; +} // end: keyin + + +#ifdef TERMIOS_ONLY + /* + * Get line oriented interactive input from the user, + * using native tty support */ +static char *linein (const char *prompt) { + static char buf[MEDBUFSIZ]; show_pmt(prompt); memset(buf, '\0', sizeof(buf)); - chin(1, buf, sizeof(buf) - 1); + chin(1, buf, sizeof(buf)-1); putp(Cap_curs_norm); - return strim_0(buf); -} + // note: we DO produce a vaid 'string' + return strim(buf); +} // end: linein -// Get a float from the user -static float get_float (const char *prompt) -{ +#else + /* + * Get line oriented interactive input from the user, + * going way beyond native tty support ! + * Unlike native tty input support, this function provides: + * . true line editing, not just destructive backspace + * . an input limit that's sensitive to current screen dimensions + * . immediate signal response without the need to wait for '\n' */ +static char *linein (const char *prompt) { + // thank goodness memmove allows the two strings to overlap + #define sqzSTR { memmove(&buf[pos], &buf[pos+1], bufMAX-pos); \ + buf[sizeof(buf)-1] = '\0'; } + #define expSTR if (len+1 < bufMAX-1 && len+beg+1 < Screen_cols-1) { \ + memmove(&buf[pos+1], &buf[pos], bufMAX-pos); buf[pos] = ' '; } + #define logCOL (pos+1) + #define phyCOL (beg+pos+1) + #define bufMAX ((int)sizeof(buf)-2) // -1 for '\0' string delimeter + static char buf[MEDBUFSIZ+1]; // +1 for '\0' string delimeter + int beg, pos, len; + int i, key; + + pos = i = 0; + beg = show_pmt(prompt); + memset(buf, '\0', sizeof(buf)); + do { + len = strlen(buf); + switch (key = keyin(0)) { + case kbd_ESC: + buf[0] = '\0'; // fall through ! + case kbd_ENTER: + break; + case kbd_DEL: + case kbd_DOWN: + sqzSTR + break; + case kbd_BKSP : + if (0 < pos) { --pos; sqzSTR } + break; + case kbd_INS: + case kbd_UP: + expSTR + break; + case kbd_LEFT: + if (0 < pos) --pos; + break; + case kbd_RIGHT: + if (pos < len) ++pos; + break; + case kbd_HOME: + pos = 0; + break; + case kbd_END: + pos = len; + break; + default: // what we REALLY wanted (maybe) + if (isprint(key) && logCOL+1 < bufMAX && phyCOL+1 < Screen_cols) + buf[pos++] = key; + break; + } + putp(fmtmk("%s%s%s", tg2(beg, Msg_row), Cap_clr_eol, buf)); + putp(tg2(beg+pos, Msg_row)); + } while (key && kbd_ENTER != key && kbd_ESC != key); + + return buf; + #undef sqzSTR + #undef expSTR + #undef logCOL + #undef phyCOL + #undef bufMAX +} // end: linein +#endif + +/*###### Small Utility routines ########################################*/ + + /* + * Get a float from the user */ +static float get_float (const char *prompt) { char *line; float f; - if (!(*(line = ask4str(prompt)))) return -1; + if (!(*(line = linein(prompt)))) return -1.0; // note: we're not allowing negative floats - if (strcspn(line, ",.1234567890")) { - show_msg("\aNot valid"); - return -1; + if (strcspn(line, "+,.0123456789")) { + show_msg("Not valid"); + return -1.0; } sscanf(line, "%f", &f); return f; -} +} // end: get_float -// Get an integer from the user -static int get_int (const char *prompt) -{ + /* + * Get an integer from the user, returning INT_MIN for error */ +static int get_int (const char *prompt) { char *line; int n; - if (!(*(line = ask4str(prompt)))) return -1; + if (!(*(line = linein(prompt)))) return INT_MIN; // note: we've got to allow negative ints (renice) - if (strcspn(line, "-1234567890")) { + if (strcspn(line, "-+0123456789")) { show_msg("\aNot valid"); - return -1; + return INT_MIN; } sscanf(line, "%d", &n); return n; -} +} // end: get_int /* @@ -758,159 +1025,638 @@ static int get_int (const char *prompt) * SK_Mb (2) it's megabytes * SK_Gb (3) it's gigabytes * SK_Tb (4) it's terabytes */ -static const char *scale_num (unsigned long num, const int width, const unsigned type) -{ - /* kilobytes, megabytes, gigabytes, terabytes, duh! */ +static const char *scale_num (unsigned long num, const int width, const int type) { + // kilobytes, megabytes, gigabytes, terabytes, duh! static double scale[] = { 1024.0, 1024.0*1024, 1024.0*1024*1024, 1024.0*1024*1024*1024, 0 }; - /* kilo, mega, giga, tera, none */ -#ifdef CASEUP_SCALE + // kilo, mega, giga, tera, none +#ifdef CASEUP_SUFIX static char nextup[] = { 'K', 'M', 'G', 'T', 0 }; #else static char nextup[] = { 'k', 'm', 'g', 't', 0 }; #endif - static char buf[TNYBUFSIZ]; + static char buf[SMLBUFSIZ]; double *dp; char *up; - /* try an unscaled version first... */ + // try an unscaled version first... if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf; - /* now try successively higher types until it fits */ - for (up = nextup + type, dp = scale; *dp; ++dp, ++up) { - /* the most accurate version */ + // now try successively higher types until it fits + for (up = nextup + type, dp = scale; 0 < *dp; ++dp, ++up) { + // the most accurate version if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up)) return buf; - /* the integer version */ - if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up)) + // the integer version + if (width >= snprintf(buf, sizeof(buf), "%lu%c", (unsigned long)(num / *dp), *up)) return buf; } - /* well shoot, this outta' fit... */ + // well shoot, this outta' fit... return "?"; -} +} // end: scale_num /* * Do some scaling stuff. * format 'tics' to fit 'width'. */ -static const char *scale_tics (TIC_t tics, const int width) -{ -#ifdef CASEUP_SCALE -#define HH "%uH" -#define DD "%uD" -#define WW "%uW" +static const char *scale_tics (TIC_t tics, const int width) { +#ifdef CASEUP_SUFIX + #define HH "%uH" + #define DD "%uD" + #define WW "%uW" #else -#define HH "%uh" -#define DD "%ud" -#define WW "%uw" + #define HH "%uh" + #define DD "%ud" + #define WW "%uw" #endif - static char buf[TNYBUFSIZ]; + static char buf[SMLBUFSIZ]; unsigned long nt; // narrow time, for speed on 32-bit unsigned cc; // centiseconds unsigned nn; // multi-purpose whatever - nt = (tics * 100ull) / Hertz; + nt = (tics * 100ull) / Hertz; // up to 68 weeks of cpu time cc = nt % 100; // centiseconds past second nt /= 100; // total seconds nn = nt % 60; // seconds past the minute nt /= 60; // total minutes if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc)) return buf; - if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn)) + if (width >= snprintf(buf, sizeof(buf), "%lu:%02u", nt, nn)) return buf; nn = nt % 60; // minutes past the hour nt /= 60; // total hours - if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn)) + if (width >= snprintf(buf, sizeof(buf), "%lu,%02u", nt, nn)) return buf; nn = nt; // now also hours - if (width >= snprintf(buf, sizeof buf, HH, nn)) + if (width >= snprintf(buf, sizeof(buf), HH, nn)) return buf; nn /= 24; // now days - if (width >= snprintf(buf, sizeof buf, DD, nn)) + if (width >= snprintf(buf, sizeof(buf), DD, nn)) return buf; nn /= 7; // now weeks - if (width >= snprintf(buf, sizeof buf, WW, nn)) + if (width >= snprintf(buf, sizeof(buf), WW, nn)) return buf; // well shoot, this outta' fit... return "?"; + #undef HH + #undef DD + #undef WW +} // end: scale_tics -#undef HH -#undef DD -#undef WW -} - -#include - -static int selection_type; -static uid_t selection_uid; - -// FIXME: this is "temporary" code we hope -static int good_uid(const proc_t *restrict const pp){ - switch(selection_type){ - case 'p': - return 1; - case 0: - return 1; - case 'U': - if (pp->ruid == selection_uid) return 1; - if (pp->suid == selection_uid) return 1; - if (pp->fuid == selection_uid) return 1; - // FALLTHROUGH - case 'u': - if (pp->euid == selection_uid) return 1; - // FALLTHROUGH - default: - ; // don't know what it is; find bugs fast - } - return 0; -} - -// swiped from ps, and ought to be in libproc -static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){ - struct passwd *passwd_data; - char *endp; - unsigned long num; - static const char uidrange[] = "User ID out of range."; - static const char uidexist[] = "User name does not exist."; - num = strtoul(str, &endp, 0); - if(*endp != '\0'){ /* hmmm, try as login name */ - passwd_data = getpwnam(str); - if(!passwd_data) return uidexist; - num = passwd_data->pw_uid; - } - if(num > 0xfffffffeUL) return uidrange; - *ret = num; - return 0; -} - - -/*###### Library Alternatives ##########################################*/ /* - * Handle our own memory stuff without the risk of leaving the - * user's terminal in an ugly state should things go sour. */ + * Validate the passed string as a user name or number, + * and/or update the window's 'u/U' selection stuff. */ +static const char *user_certify (WIN_t *q, const char *str, char typ) { + struct passwd *pwd; + char *endp; + uid_t num; -static void *alloc_c (unsigned numb) MALLOC; -static void *alloc_c (unsigned numb) -{ - void * p; + q->usrseltyp = 0; + Monpidsidx = 0; + if (*str) { + num = (uid_t)strtoul(str, &endp, 0); + if ('\0' == *endp) + pwd = getpwuid(num); + else + pwd = getpwnam(str); + if (!pwd) return "Invalid user"; + q->usrseluid = pwd->pw_uid; + q->usrseltyp = typ; + } + return NULL; +} // end: user_certify - if (!numb) ++numb; - if (!(p = calloc(1, numb))) - std_err("failed memory allocate"); - return p; + + /* + * Determine if this proc_t matches the 'u/U' selection criteria + * for a given window -- it's called from only one place, and + * likely inlined even without the directive */ +static inline int user_matched (WIN_t *q, const proc_t *p) { + switch(q->usrseltyp) { + case 0: // uid selection inactive + return 1; + case 'U': // match any uid + if (p->ruid == q->usrseluid) return 1; + if (p->suid == q->usrseluid) return 1; + if (p->fuid == q->usrseluid) return 1; + // fall through... + case 'u': // match effective uid + if (p->euid == q->usrseluid) return 1; + // fall through... + default: // no match, don't display + ; + } + return 0; +} // end: user_matched + +/*###### Fields Management support #####################################*/ + + /* These are the Fieldstab.lflg values used here and in calibrate_fields. + (own identifiers as documentation and protection against changes) */ +#define L_stat PROC_FILLSTAT +#define L_statm PROC_FILLMEM +#define L_status PROC_FILLSTATUS +#define L_CGROUP PROC_EDITCGRPCVT | PROC_FILLCGROUP +#define L_CMDLINE PROC_EDITCMDLCVT | PROC_FILLARG +#define L_EUSER PROC_FILLUSR +#define L_OUSER PROC_FILLSTATUS | PROC_FILLUSR +#define L_EGROUP PROC_FILLSTATUS | PROC_FILLGRP + // make 'none' non-zero (used to be important to Frames_libflags) +#define L_NONE PROC_SPARE_1 + // from either 'stat' or 'status' (preferred), via bits not otherwise used +#define L_EITHER PROC_SPARE_2 + // for calibrate_fields and summary_show 1st pass +#define L_DEFAULT PROC_FILLSTAT + + /* These are our gosh darn 'Fields' ! + They MUST be kept in sync with pflags !! + note: for integer data, the length modifiers found in .fmts may + NOT reflect the true field type found in proc_t -- this plus + a cast when/if displayed provides minimal width protection. */ +static FLD_t Fieldstab[] = { + // a temporary macro, soon to be undef'd... + #define SF(f) (QFP_t)SCB_NAME(f) + +/* .head + .fmts anomolies: + entries shown with NULL are either valued at runtime (see zap_fieldstab) + or, in the case of .fmts, represent variable width fields + .lflg anomolies: + P_UED, L_NONE - natural outgrowth of 'stat()' in readproc (euid) + P_CPU, L_stat - never filled by libproc, but requires times (pcpu) + P_CMD, L_stat - may yet require L_CMDLINE in calibrate_fields (cmd/cmdline) + L_EITHER - must L_status, else L_stat == 64-bit math (__udivdi3) on 32-bit ! + .head .fmts .width .scale .sort .lflg .desc + ------------ -------- ------ ------ -------- -------- ---------------------- */ + { NULL, NULL, -1, -1, SF(PID), L_NONE, "Process Id" }, + { NULL, NULL, -1, -1, SF(PPD), L_EITHER, "Parent Process pid" }, + { " UID ", "%5d ", -1, -1, SF(UED), L_NONE, "Effective User Id" }, + { "USER ", "%-8.8s ", -1, -1, SF(UEN), L_EUSER, "Effective User Name" }, + { " RUID ", "%5d ", -1, -1, SF(URD), L_status, "Real User Id" }, + { "RUSER ", "%-8.8s ", -1, -1, SF(URN), L_OUSER, "Real User Name" }, + { " SUID ", "%5d ", -1, -1, SF(USD), L_status, "Saved User Id" }, + { "SUSER ", "%-8.8s ", -1, -1, SF(USN), L_OUSER, "Saved User Name" }, + { " GID ", "%5d ", -1, -1, SF(GID), L_NONE, "Group Id" }, + { "GROUP ", "%-8.8s ", -1, -1, SF(GRP), L_EGROUP, "Group Name" }, + { " PGRP ", "%5d ", -1, -1, SF(PGD), L_stat, "Process Group Id" }, + { "TTY ", "%-8.8s ", 8, -1, SF(TTY), L_stat, "Controlling Tty" }, + { "TPGID ", "%5d ", -1, -1, SF(TPG), L_stat, "Tty Process Grp Id" }, + { " SID ", "%5d ", -1, -1, SF(SID), L_stat, "Session Id" }, + { " PR ", "%3d ", -1, -1, SF(PRI), L_stat, "Priority" }, + { " NI ", "%3d ", -1, -1, SF(NCE), L_stat, "Nice Value" }, + { "nTH ", "%3d ", -1, -1, SF(THD), L_EITHER, "Number of Threads" }, + { NULL, NULL, -1, -1, SF(CPN), L_stat, "Last Used Cpu (SMP)" }, + { "%CPU ", NULL, -1, -1, SF(CPU), L_stat, "CPU Usage" }, + { " TIME ", "%6.6s ", 6, -1, SF(TME), L_stat, "CPU Time" }, + { " TIME+ ", "%9.9s ", 9, -1, SF(TME), L_stat, "CPU Time, hundredths" }, + { "%MEM ", "%#4.1f ", -1, -1, SF(RES), L_statm, "Memory Usage (RES)" }, + { " VIRT ", "%5.5s ", 5, SK_Kb, SF(VRT), L_statm, "Virtual Image (kb)" }, + { "SWAP ", "%4.4s ", 4, SK_Kb, SF(SWP), L_statm, "Swapped Size (kb)" }, + { " RES ", "%4.4s ", 4, SK_Kb, SF(RES), L_statm, "Resident Size (kb)" }, + { "CODE ", "%4.4s ", 4, SK_Kb, SF(COD), L_statm, "Code Size (kb)" }, + { "DATA ", "%4.4s ", 4, SK_Kb, SF(DAT), L_statm, "Data+Stack Size (kb)" }, + { " SHR ", "%4.4s ", 4, SK_Kb, SF(SHR), L_statm, "Shared Mem Size (kb)" }, + { "nMaj ", "%4.4s ", 4, SK_no, SF(FL1), L_stat, "Major Page Faults" }, + { "nMin ", "%4.4s ", 4, SK_no, SF(FL2), L_stat, "Minor Page Faults" }, + { "nDRT ", "%4.4s ", 4, SK_no, SF(DRT), L_statm, "Dirty Pages Count" }, + { "S ", "%c ", -1, -1, SF(STA), L_EITHER, "Process Status" }, + // next 2 entries are special: '.head' is variable width (see calibrate_fields) + { "COMMAND ", NULL, -1, -1, SF(CMD), L_EITHER, "Command Name/Line" }, + { "WCHAN ", NULL, -1, -1, SF(WCH), L_stat, "Sleeping in Function" }, + // next entry's special: the 0's will be replaced with '.'! +#ifdef CASEUP_HEXES + { "Flags ", "%08lX ", -1, -1, SF(FLG), L_stat, "Task Flags " }, +#else + { "Flags ", "%08lx ", -1, -1, SF(FLG), L_stat, "Task Flags " }, +#endif + // next entry's like P_CMD, and '.head' must be the same length -- they share varcolsz + { "CGROUPS ", NULL, -1, -1, SF(CGR), L_CGROUP, "Control Groups" } +#ifdef OOMEM_ENABLE +#define L_oom PROC_FILLOOM + ,{ "Adj ", "%3d ", -1, -1, SF(OOA), L_oom, "oom_adjustment (2^X)" } + ,{ " Badness ", "%8d ", -1, -1, SF(OOM), L_oom, "oom_score (badness)" } +#undef L_oom +#endif + #undef SF +}; + + + /* + * A calibrate_fields() *Helper* function to refresh the + * cached screen geometry and related variables */ +static void adj_geometry (void) { + static size_t pseudo_max = 0; + struct winsize wz; + + Screen_cols = columns; // + Screen_rows = lines; // + + if (-1 != ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) + && 0 < wz.ws_col && 0 < wz.ws_row) { + Screen_cols = wz.ws_col; + Screen_rows = wz.ws_row; + } + +#ifdef TTYGETENVYES +{ char *env_columns = getenv("COLUMNS"); // Unix98 environment vars + char *env_lines = getenv("LINES"); + + if (env_columns && *env_columns && env_lines && *env_lines) { + long t, tc = 0, tr = 0; + char *endptr; + + t = strtol(env_columns, &endptr, 0); + if (!*endptr && (t > 0) && (t <= 0x7fffffffL)) tc = t; + t = strtol(env_lines, &endptr, 0); + if (!*endptr && (t > 0) && (t <= 0x7fffffffL)) tr = t; + // only allow environment vars override if both make sense... + if (0 < tc && 0 < tr) { + Screen_cols = (int)tc; + Screen_rows = (int)tr; + } + } } +#endif -static void *alloc_r (void *q, unsigned numb) MALLOC; -static void *alloc_r (void *q, unsigned numb) -{ - void *p; +#ifndef RMAN_IGNORED + // be crudely tolerant of crude tty emulators + if (Cap_avoid_eol) Screen_cols--; +#endif - if (!numb) ++numb; - if (!(p = realloc(q, numb))) - std_err("failed memory allocate"); - return p; -} + // we might disappoint some folks (but they'll deserve it) + if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX; +#ifdef RESIZE_LIMIT + if (SCRCOLMIN > Screen_cols || SCRROWMIN > Screen_rows) error_exit("too tiny tty"); +#endif + /* keep our support for output optimization in sync with current reality + note: when we're in Batch mode, we don't really need a Pseudo_screen + and when not Batch, our buffer will contain 1 extra 'line' since + Msg_row is never represented -- but it's nice to have some space + between us and the great-beyond... */ + if (Batch) { + Screen_rows = MAXINT; + Pseudo_size = (sizeof(*Pseudo_screen) * ROWMAXSIZ); + } else + Pseudo_size = (sizeof(*Pseudo_screen) * ROWMAXSIZ) * Screen_rows; + + // we'll only grow our Pseudo_screen, never shrink it + if (pseudo_max < Pseudo_size) { + pseudo_max = Pseudo_size; + Pseudo_screen = alloc_r(Pseudo_screen, pseudo_max); + } + PSU_CLREOS(0); +} // end: adj_geometry + + + /* + * After ensuring the intergrity of our cached screen dimensions, + * via adj_geometry(), for each visible window: + * 1) Set the number of fields/columns to display + * 2) Create the field columns heading + * 3) Set maximum cmdline length, if command lines are in use + * In the process, the required PROC_FILLxxx flags will be rebuilt! */ +static void calibrate_fields (void) { + sigset_t newss, oldss; + FLG_t f; + char *s; + const char *h; + WIN_t *w = Curwin; +#ifdef EQUCOLHDRYES + int x, hdrmax = 0; +#endif + int i, needpsdb = 0; + int varcolcnt; + + // block SIGWINCH signals while we do our thing... + sigemptyset(&newss); + sigaddset(&newss, SIGWINCH); + if (-1 == sigprocmask(SIG_BLOCK, &newss, &oldss)) + error_exit(fmtmk("failed sigprocmask, SIG_BLOCK: %s", strerror(errno))); + + adj_geometry(); + Frames_libflags = 0; + + do { + if (VIZISw(w)) { + w->hdrcaplen = 0; // really only used with USE_X_COLHDR + // build window's pflgsall array, establish upper bounds for maxpflgs + for (i = 0, w->totpflgs = 0; i < P_MAXPFLGS; i++) { + if (FLDviz(w, i)) { + f = FLDget(w, i); +#ifdef USE_X_COLHDR + w->pflgsall[w->totpflgs++] = f; +#else + if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) { + w->pflgsall[w->totpflgs++] = X_XON; + w->pflgsall[w->totpflgs++] = f; + w->pflgsall[w->totpflgs++] = X_XOF; + } else + w->pflgsall[w->totpflgs++] = f; +#endif + } + } + + /* build a preliminary columns header not to exceed screen width + while accounting for a possible leading window number */ + w->varcolsz = varcolcnt = 0; + *(s = w->columnhdr) = '\0'; + if (Rc.mode_altscr) s = scat(s, " "); + for (i = 0; i + w->begpflg < w->totpflgs; i++) { + f = w->pflgsall[i + w->begpflg]; + w->procflgs[i] = f; +#ifndef USE_X_COLHDR + if (P_MAXPFLGS < f) continue; +#endif + h = Fieldstab[f].head; + // oops, won't fit -- we're outta here... + if (Screen_cols <= ((int)(s - w->columnhdr) + (int)strlen(h))) break; + if (!Fieldstab[f].fmts) { ++varcolcnt; w->varcolsz += strlen(h) - 1; } + s = scat(s, h); + } + + /* establish the final maxpflgs and prepare to grow the variable column + heading(s) via varcolsz - it may be a fib if their pflags weren't + encountered, but that's ok because they won't be displayed anyway */ + w->maxpflgs = i; + w->varcolsz += Screen_cols - strlen(w->columnhdr); + if (varcolcnt) w->varcolsz = w->varcolsz / varcolcnt; + + /* establish the field where all remaining fields would still + fit within screen width, including a leading window number */ + *(s = w->columnhdr) = '\0'; + if (Rc.mode_altscr) s = scat(s, " "); + for (i = w->totpflgs - 1; -1 < i; i--) { + f = w->pflgsall[i]; +#ifndef USE_X_COLHDR + if (P_MAXPFLGS < f) continue; +#endif + h = Fieldstab[f].head; + if (Screen_cols <= ((int)(s - w->columnhdr) + (int)strlen(h))) break; + s = scat(s, h); + w->endpflg = i; + } + + /* finally, we can build the true run-time columns header, format any + variable column heading(s), if they're really being displayed, and + rebuild the all-important PROC_FILLxxx flags that will be used + until/if we're we're called again */ + memset((s = w->columnhdr), 0, sizeof(w->columnhdr)); + if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum)); + for (i = 0; i < w->maxpflgs; i++) { + f = w->procflgs[i]; +#ifdef USE_X_COLHDR + if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) { + s = scat(s, fmtmk("%s%s", Caps_off, w->capclr_msg)); + w->hdrcaplen += strlen(Caps_off) + strlen(w->capclr_msg); + } +#else + if (P_MAXPFLGS < f) continue; +#endif + h = Fieldstab[f].head; + if (P_WCH == f) needpsdb = 1; + if (P_CMD == f && CHKw(w, Show_CMDLIN)) Frames_libflags |= L_CMDLINE; + if (Fieldstab[f].fmts) s = scat(s, h); + else s = scat(s, fmtmk(VARCOL_fmts, w->varcolsz, w->varcolsz, h)); + Frames_libflags |= Fieldstab[w->procflgs[i]].lflg; +#ifdef USE_X_COLHDR + if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) { + s = scat(s, fmtmk("%s%s", Caps_off, w->capclr_hdr)); + w->hdrcaplen += strlen(Caps_off) + strlen(w->capclr_hdr); + } +#endif + } +#ifdef EQUCOLHDRYES + // prepare to even out column header lengths... + if (hdrmax + w->hdrcaplen < (x = strlen(w->columnhdr))) hdrmax = x - w->hdrcaplen; +#endif + // we must also accommodate an out of view sort field... + f = w->rc.sortindx; + Frames_libflags |= Fieldstab[f].lflg; + if (P_CMD == f && CHKw(w, Show_CMDLIN)) Frames_libflags |= L_CMDLINE; + } // end: VIZISw(w) + if (Rc.mode_altscr) w = w->next; + } while (w != Curwin); + +#ifdef EQUCOLHDRYES + /* now we can finally even out column header lengths + (we're assuming entire columnhdr was memset to '\0') */ + if (Rc.mode_altscr && SCREENMAX > Screen_cols) + for (i = 0; i < GROUPSMAX; i++) { + w = &Winstk[i]; + if (CHKw(w, Show_TASKON)) + if (hdrmax + w->hdrcaplen > (x = strlen(w->columnhdr))) + memset(&w->columnhdr[x], ' ', hdrmax + w->hdrcaplen - x); + } +#endif + + // do we need the kernel symbol table (and is it already open?) + if (needpsdb) { + if (-1 == No_ksyms) { + No_ksyms = 0; + if (open_psdb_message(NULL, msg_save)) + No_ksyms = 1; + else + PSDBopen = 1; + } + } + + // finalize/touchup the libproc PROC_FILLxxx flags for current config... + if ((Frames_libflags & L_EITHER) && !(Frames_libflags & L_stat)) + Frames_libflags |= L_status; + if (!Frames_libflags) Frames_libflags = L_DEFAULT; + if (Monpidsidx) Frames_libflags |= PROC_PID; + + Frames_resize = 0; + if (-1 == sigprocmask(SIG_SETMASK, &oldss, NULL)) + error_exit(fmtmk("failed sigprocmask, SIG_SETMASK: %s", strerror(errno))); +} // end: calibrate_fields + + + /* + * Display each field represented in the current window's fieldscur + * array along with its description. Mark with bold and a leading + * asterisk those fields associated with the "on" or "active" state. + * + * Special highlighting will be accorded the "focus" field with such + * highlighting potentially extended to include the description. + * + * After all fields have been displayed, some extra explanatory + * text may also be output */ +static void display_fields (int focus, int extend, const char *xtra) { + #define yRSVD 4 + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + const char *p; +#ifdef FIELD_CURSOR + int savcol = 0, savrow = 0; // make gcc happy (with initialization) +#endif + int i,cmax, rmax; + + fflush(stdout); + cmax = Screen_cols / 2; + rmax = Screen_rows - yRSVD; + + for (i = 0; i < P_MAXPFLGS; ++i) { + FLG_t f = FLDget(w, i); + int b = FLDviz(w, i); + + // advance past leading spaces, if any + for (p = Fieldstab[f].head; ' ' == *p; ++p) ; + PUTT("%s%c %s%s%-10.10s%s%s%s = %-20.20s%s" + , tg2((i / rmax) * cmax, (i % rmax) + yRSVD) + , b ? '*' : ' ' + , b ? w->cap_bold : Cap_norm + , i == focus ? w->capclr_msg : "" + , p + , Cap_norm + , b ? w->cap_bold : "" + , (i == focus && extend) ? w->capclr_msg : "" + , Fieldstab[f].desc + , Cap_norm); +#ifdef FIELD_CURSOR + if (i == focus) { savcol = (i / rmax) * cmax; savrow = (i % rmax) + yRSVD; } +#endif + } + if (xtra) { + putp(w->capclr_rownorm); + while ((p = strchr(xtra, '\n'))) { + ++i; + PUTT("%s%.*s" + , tg2((i / rmax) * cmax, (i % rmax) + yRSVD) + , (int)(p - xtra) + , xtra); + xtra = ++p; + } + } + +#ifdef FIELD_CURSOR + putp(tg2(savcol+1, savrow)); +#endif + putp(Caps_off); + #undef yRSVD +} // end: display_fields + + + /* + * Manage all fields aspects (order/toggle/sort), for all windows. */ +static void fields_utility (void) { + #define unSCRL w->begpflg = 0; + #define swapEM { char c; unSCRL; c = w->rc.fieldscur[i]; \ + w->rc.fieldscur[i] = *p; *p = c; p = &w->rc.fieldscur[i]; } + #define spewFI { char *t; f = w->rc.sortindx; t = strchr(w->rc.fieldscur, f + FLD_OFFSET); \ + if (!t) t = strchr(w->rc.fieldscur, (f + FLD_OFFSET) | 0x80); \ + i = (t) ? (int)(t - w->rc.fieldscur) : 0; } + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + const char *h = NULL; + char *p = NULL; + int i, key; + FLG_t f; + + putp(Cap_clr_scr); +#ifdef FIELD_CURSOR + putp(Cap_curs_huge); +#else + putp(Cap_curs_hide); +#endif + spewFI + + do { + // advance past leading spaces, if any + if (!h) for (h = Fieldstab[f].head; ' ' == *h; ++h) ; +#ifdef FIELD_CURSOR + show_special(1, fmtmk(FIELDS_heading, Cap_home, w->grpname, h)); + display_fields(i, (p != NULL), FIELDS_notes); +#else + display_fields(i, (p != NULL), FIELDS_notes); + show_special(1, fmtmk(FIELDS_heading, Cap_home, w->grpname, h)); +#endif + switch (key = keyin(0)) { + case kbd_UP: + if (i > 0) { --i; if (p) swapEM } + break; + case kbd_DOWN: + if (i + 1 < P_MAXPFLGS) { ++i; if (p) swapEM } + break; + case kbd_LEFT: + case kbd_ENTER: + p = NULL; + break; + case kbd_RIGHT: + p = &w->rc.fieldscur[i]; + break; + case kbd_HOME: + case kbd_PGUP: + if (!p) i = 0; + break; + case kbd_END: + case kbd_PGDN: + if (!p) i = P_MAXPFLGS - 1; + break; + case kbd_SPACE: + case 'd': + if (!p) { FLDtog(w, i); unSCRL } + break; + case 's': + if (!p) { w->rc.sortindx = f = FLDget(w, i); h = NULL; unSCRL } + break; + case 'a': + case 'w': + Curwin = w = ('a' == key) ? w->next : w->prev; + spewFI + p = NULL; + h = NULL; + break; + default: // keep gcc happy + break; + } + } while (key && 'q' != key && kbd_ESC != key); + putp(Cap_curs_norm); + #undef unSCRL + #undef swapEM + #undef spewFI +} // end: fields_utility + + + /* + * This routine exists just to consolidate all the messin' around + * with Fieldstab '.head' and '.fmts' members -- until we devise + * a more elegant soultion. */ +static void zap_fieldstab (void) { + static char fmts_pid[8]; + static char fmts_cpu[8]; + unsigned digits; + char buf[8]; + + Fieldstab[P_PID].head = " PID "; + Fieldstab[P_PID].fmts = "%5d "; + Fieldstab[P_PPD].head = " PPID "; + Fieldstab[P_PPD].fmts = "%5d "; + if (5 < (digits = get_pid_digits())) { + if (10 < digits) error_exit("failed pid size test"); + snprintf(fmts_pid, sizeof(fmts_pid), "%%%uu ", digits); + Fieldstab[P_PID].head = " PID " + 10 - digits; + Fieldstab[P_PID].fmts = fmts_pid; + Fieldstab[P_PPD].head = " PPID " + 10 - digits; + Fieldstab[P_PPD].fmts = fmts_pid; + } + + Fieldstab[P_CPN].head = "P "; + Fieldstab[P_CPN].fmts = "%1d "; + if (1 < (digits = (unsigned)snprintf(buf, sizeof(buf), "%u", (unsigned)Cpu_tot))) { + if (5 < digits) error_exit("failed num cpus test"); + snprintf(fmts_cpu, sizeof(fmts_cpu), "%%%ud ", digits); + Fieldstab[P_CPN].head = " P " + 5 - digits; + Fieldstab[P_CPN].fmts = fmts_cpu; + } + + Cpu_pmax = 99.0; + Fieldstab[P_CPU].fmts = "%#4.1f "; + if (Rc.mode_irixps && Cpu_tot > 1 && !Thread_mode) { + Cpu_pmax = 9999.0; + Fieldstab[P_CPU].fmts = "%4.0f "; + } +} // end: zap_fieldstab + +/*###### Library Interface #############################################*/ /* * This guy's modeled on libproc's 'five_cpu_numbers' function except @@ -918,19 +1664,27 @@ static void *alloc_r (void *q, unsigned numb) * as follows: * cpus[0] thru cpus[n] == tics for each separate cpu * cpus[Cpu_tot] == tics from the 1st /proc/stat line */ -static CPU_t *cpus_refresh (CPU_t *cpus) -{ +static CPU_t *cpus_refresh (CPU_t *cpus) { + static const char err_read[] = "failed /proc/stat read"; static FILE *fp = NULL; + static int smp_sav = -1; + char buf[MEDBUFSIZ]; // enough for /proc/stat CPU line (not the intr line) int i; - int num; - // enough for a /proc/stat CPU line (not the intr line) - char buf[SMLBUFSIZ]; + + /*** hotplug_acclimated ***/ + if (smp_sav != SMP_NUM_CPUS) { + Cpu_tot = smp_sav = SMP_NUM_CPUS; + zap_fieldstab(); + calibrate_fields(); + if (fp) { fclose(fp); fp = NULL; } + if (cpus) { free(cpus); cpus = NULL; } + } /* by opening this file once, we'll avoid the hit on minor page faults (sorry Linux, but you'll have to close it for us) */ if (!fp) { if (!(fp = fopen("/proc/stat", "r"))) - std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno))); + error_exit(fmtmk("failed /proc/stat open: %s", strerror(errno))); /* note: we allocate one more CPU_t than Cpu_tot so that the last slot can hold tics representing the /proc/stat cpu summary (the first line read) -- that slot supports our View_CPUSUM toggle */ @@ -940,46 +1694,81 @@ static CPU_t *cpus_refresh (CPU_t *cpus) fflush(fp); // first value the last slot with the cpu summary line - if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); - cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number - cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number - cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number - num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", - &cpus[Cpu_tot].u, - &cpus[Cpu_tot].n, - &cpus[Cpu_tot].s, - &cpus[Cpu_tot].i, - &cpus[Cpu_tot].w, - &cpus[Cpu_tot].x, - &cpus[Cpu_tot].y, - &cpus[Cpu_tot].z - ); - if (num < 4) - std_err("failed /proc/stat read"); - + cpus[Cpu_tot].x = cpus[Cpu_tot].y = cpus[Cpu_tot].z = 0; + // FIXME: can't tell above by kernel version number + if (!fgets(buf, sizeof(buf), fp)) error_exit(err_read); + if (4 > sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu" + , &cpus[Cpu_tot].u, &cpus[Cpu_tot].n, &cpus[Cpu_tot].s, &cpus[Cpu_tot].i + , &cpus[Cpu_tot].w, &cpus[Cpu_tot].x, &cpus[Cpu_tot].y, &cpus[Cpu_tot].z)) + error_exit(err_read); // and just in case we're 2.2.xx compiled without SMP support... - if (Cpu_tot == 1) { - cpus[1].id = 0; + if (1 == Cpu_tot) memcpy(cpus, &cpus[1], sizeof(CPU_t)); - } - // now value each separate cpu's tics - for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) { - if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); - cpus[i].x = 0; // FIXME: can't tell by kernel version number - cpus[i].y = 0; // FIXME: can't tell by kernel version number - cpus[i].z = 0; // FIXME: can't tell by kernel version number - num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", - &cpus[i].id, - &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z - ); - if (num < 4) - std_err("failed /proc/stat read"); + for (i = 0; i < Cpu_tot; i++) { +#ifdef PRETEND4CPUS + rewind(fp); + fgets(buf, sizeof(buf), fp); +#endif + if (!fgets(buf, sizeof(buf), fp)) error_exit(err_read); + cpus[i].x = cpus[i].y = cpus[i].z = 0; + // FIXME: can't tell above by kernel version number + if (4 > sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &cpus[i].id + , &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i + , &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z)) { + error_exit(err_read); + } +#ifdef PRETEND4CPUS + cpus[i].id = i; +#endif } return cpus; -} +} // end: cpus_refresh +#ifdef OFF_HST_HASH + /* + * Binary Search for HST_t's put/get support */ + +static inline HST_t *hstbsrch (HST_t *hst, int max, int pid) { + int mid, min = 0; + + while (min <= max) { + mid = (min + max) / 2; + if (pid < hst[mid].pid) max = mid - 1; + else if (pid > hst[mid].pid) min = mid + 1; + else return &hst[mid]; + } + return NULL; +} // end: hstbsrch + +#else + /* + * Hashing functions for HST_t's put/get support + * (not your normal 'chaining', those damn HST_t's might move!) */ + +#define _HASH_(K) (K & (HHASH_SIZ - 1)) + +static inline HST_t *hstget (int pid) { + int V = PHash_sav[_HASH_(pid)]; + + while (-1 < V) { + if (PHist_sav[V].pid == pid) return &PHist_sav[V]; + V = PHist_sav[V].lnk; } + return NULL; +} // end: hstget + + +static inline void hstput (unsigned idx) { + int V = _HASH_(PHist_new[idx].pid); + + PHist_new[idx].lnk = PHash_new[V]; + PHash_new[V] = idx; +} // end: hstput + +#undef _HASH_ +#endif + /* * Refresh procs *Helper* function to eliminate yet one more need * to loop through our darn proc_t table. He's responsible for: @@ -987,20 +1776,17 @@ static CPU_t *cpus_refresh (CPU_t *cpus) * 2) counting the number of tasks in each state (run, sleep, etc) * 3) maintaining the HST_t's and priming the proc_t pcpu field * 4) establishing the total number tasks for this frame */ -static void prochlp (proc_t *this) -{ - static HST_t *hist_sav = NULL; - static HST_t *hist_new = NULL; - static unsigned hist_siz = 0; // number of structs - static unsigned maxt_sav; // prior frame's max tasks +static void prochlp (proc_t *this) { + static unsigned maxt_sav = 0; // prior frame's max tasks TIC_t tics; + HST_t *h; - if (unlikely(!this)) { + if (!this) { static struct timeval oldtimev; struct timeval timev; struct timezone timez; - HST_t *hist_tmp; float et; + void *v; gettimeofday(&timev, &timez); et = (timev.tv_sec - oldtimev.tv_sec) @@ -1009,16 +1795,23 @@ static void prochlp (proc_t *this) oldtimev.tv_usec = timev.tv_usec; // if in Solaris mode, adjust our scaling for all cpus - Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot)); + Frame_etscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot)); maxt_sav = Frame_maxtask; Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0; - // reuse memory each time around - hist_tmp = hist_sav; - hist_sav = hist_new; - hist_new = hist_tmp; - // prep for our binary search by sorting the last frame's HST_t's - qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t); + // prep for saving this frame's HST_t's (and reuse mem each time around) + v = PHist_sav; + PHist_sav = PHist_new; + PHist_new = v; +#ifdef OFF_HST_HASH + // prep for binary search by sorting the last frame's HST_t's + qsort(PHist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t); +#else + v = PHash_sav; + PHash_sav = PHash_new; + PHash_new = v; + memcpy(PHash_new, HHash_nul, sizeof(HHash_nul)); +#endif return; } @@ -1036,145 +1829,126 @@ static void prochlp (proc_t *this) case 'Z': Frame_zombied++; break; + default: // keep gcc happy + break; } - if (unlikely(Frame_maxtask+1 >= hist_siz)) { - hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25% - hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz); - hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz); + if (Frame_maxtask+1 >= HHist_siz) { + HHist_siz = HHist_siz * 5 / 4 + 100; + PHist_sav = alloc_r(PHist_sav, sizeof(HST_t) * HHist_siz); + PHist_new = alloc_r(PHist_new, sizeof(HST_t) * HHist_siz); } + /* calculate time in this process; the sum of user time (utime) and system time (stime) -- but PLEASE dont waste time and effort on calcs and saves that go unused, like the old top! */ - hist_new[Frame_maxtask].pid = this->tid; - hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime); - -#if 0 -{ int i; - int lo = 0; - int hi = maxt_sav - 1; + PHist_new[Frame_maxtask].pid = this->tid; + PHist_new[Frame_maxtask].tics = tics = (this->utime + this->stime); +#ifdef OFF_HST_HASH // find matching entry from previous frame and make ticks elapsed - while (lo <= hi) { - i = (lo + hi) / 2; - if (this->tid < hist_sav[i].pid) - hi = i - 1; - else if (likely(this->tid > hist_sav[i].pid)) - lo = i + 1; - else { - tics -= hist_sav[i].tics; - break; - } - } -} + if ((h = hstbsrch(PHist_sav, maxt_sav - 1, this->tid))) tics -= h->tics; #else -{ - HST_t tmp; - const HST_t *ptr; - tmp.pid = this->tid; - ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t); - if(ptr) tics -= ptr->tics; -} + // hash & save for the next frame + hstput(Frame_maxtask); + // find matching entry from previous frame and make ticks elapsed + if ((h = hstget(this->tid))) tics -= h->tics; #endif - // we're just saving elapsed tics, to be converted into %cpu if - // this task wins it's displayable screen row lottery... */ + /* we're just saving elapsed tics, to be converted into %cpu if + this task wins it's displayable screen row lottery... */ this->pcpu = tics; -// if (Frames_maxcmdln) { } + // shout this to the world with the final call (or us the next time in) Frame_maxtask++; -} +} // end: prochlp /* * This guy's modeled on libproc's 'readproctab' function except * we reuse and extend any prior proc_t's. He's been customized * for our specific needs and to avoid the use of */ -static proc_t **procs_refresh (proc_t **table, int flags) -{ -#define PTRsz sizeof(proc_t *) -#define ENTsz sizeof(proc_t) - static unsigned savmax = 0; // first time, Bypass: (i) - proc_t *ptsk = (proc_t *)-1; // first time, Force: (ii) - unsigned curmax = 0; // every time (jeeze) +static proc_t **procs_refresh (proc_t **ppt) { + #define PTRsz sizeof(proc_t *) + #define ENTsz sizeof(proc_t) + static int threadshown = 0; // thread hack optimization + static unsigned savmax = 0; // first time, Bypass: (i) + proc_t *ptask = (proc_t *)-1; // first time, Force: (ii) + unsigned curmax = 0; // every time (jeeze) PROCTAB* PT; - static int show_threads_was_enabled = 0; // optimization + proc_t *pthrd; // for thread hack + unsigned i; // ditto - prochlp(NULL); // prep for a new frame - if (Monpidsidx) - PT = openproc(flags, Monpids); - else - PT = openproc(flags); + prochlp(NULL); // prep for a new frame + if (NULL == (PT = openproc(Frames_libflags, Monpids))) + error_exit(fmtmk("failed openproc: %s", strerror(errno))); // i) Allocated Chunks: *Existing* table; refresh + reuse - if (!(CHKw(Curwin, Show_THREADS))) { + if (!Thread_mode) { while (curmax < savmax) { - if (table[curmax]->cmdline) { - unsigned idx; - // Skip if Show_THREADS was never enabled - if (show_threads_was_enabled) { - for (idx = curmax + 1; idx < savmax; idx++) { - if (table[idx]->cmdline == table[curmax]->cmdline) - table[idx]->cmdline = NULL; + if (ppt[curmax]->cmdline || ppt[curmax]->cgroup) { + if (threadshown) { // skip if never used (see note below) + for (i = curmax + 1; i < savmax; i++) { + if (ppt[i]->cmdline == ppt[curmax]->cmdline) ppt[i]->cmdline = NULL; + if (ppt[i]->cgroup == ppt[curmax]->cgroup) ppt[i]->cgroup = NULL; } } - free(*table[curmax]->cmdline); - table[curmax]->cmdline = NULL; + if (ppt[curmax]->cmdline) free(*ppt[curmax]->cmdline); + if (ppt[curmax]->cgroup) free(*ppt[curmax]->cgroup); + ppt[curmax]->cmdline = ppt[curmax]->cgroup = NULL; } - if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break; - prochlp(ptsk); // tally & complete this proc_t + if (!(ptask = readproc(PT, ppt[curmax]))) break; + prochlp(ptask); // tally & complete this proc_t ++curmax; } } - else { // show each thread in a process separately + else { // acquire as separate threads while (curmax < savmax) { - proc_t *ttsk; - if (unlikely(!(ptsk = readproc(PT, NULL)))) break; - show_threads_was_enabled = 1; + if (!(ptask = readproc(PT, NULL))) break; while (curmax < savmax) { - unsigned idx; - if (table[curmax]->cmdline) { - // threads share the same cmdline storage. 'table' is - // qsort()ed, so must look through the rest of the table. - for (idx = curmax + 1; idx < savmax; idx++) { - if (table[idx]->cmdline == table[curmax]->cmdline) - table[idx]->cmdline = NULL; - } - free(*table[curmax]->cmdline); // only free once - table[curmax]->cmdline = NULL; + if (ppt[curmax]->cmdline || ppt[curmax]->cgroup) { + /* note: threads share some of the same storage, so we must look + through the rest of our table for duplicate ref's... */ + for (i = curmax + 1; i < savmax; i++) { + if (ppt[i]->cmdline == ppt[curmax]->cmdline) ppt[i]->cmdline = NULL; + if (ppt[i]->cgroup == ppt[curmax]->cgroup) ppt[i]->cgroup = NULL; + } /* ...but free only once ! */ + if (ppt[curmax]->cmdline) free(*ppt[curmax]->cmdline); + if (ppt[curmax]->cgroup) free(*ppt[curmax]->cgroup); + ppt[curmax]->cmdline = ppt[curmax]->cgroup = NULL; } - if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break; - prochlp(ttsk); + if (!(pthrd = readtask(PT, ptask, ppt[curmax]))) break; + threadshown = 1; + prochlp(pthrd); // tally & complete this thread ++curmax; } - free(ptsk); // readproc() proc_t not used + free(ptask); // readproc's proc_t not needed } } // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill - if (!(CHKw(Curwin, Show_THREADS))) { - while (ptsk) { - // realloc as we go, keeping 'table' ahead of 'currmax++' - table = alloc_r(table, (curmax + 1) * PTRsz); + if (!Thread_mode) { + while (ptask) { + // realloc as we go, keeping 'ppt' ahead of 'currmax++' + ppt = alloc_r(ppt, (curmax + 1) * PTRsz); // here, readproc will allocate the underlying proc_t stg - if (likely(ptsk = readproc(PT, NULL))) { - prochlp(ptsk); // tally & complete this proc_t - table[curmax++] = ptsk; + if ((ptask = readproc(PT, NULL))) { + prochlp(ptask); // tally & complete this proc_t + ppt[curmax++] = ptask; } } } - else { // show each thread in a process separately - while (ptsk) { - proc_t *ttsk; - if (likely(ptsk = readproc(PT, NULL))) { - show_threads_was_enabled = 1; - while (1) { - table = alloc_r(table, (curmax + 1) * PTRsz); - if (!(ttsk = readtask(PT, ptsk, NULL))) break; - prochlp(ttsk); - table[curmax++] = ttsk; + else { // acquire as separate threads + while (ptask) { + if ((ptask = readproc(PT, NULL))) { + for (;;) { + ppt = alloc_r(ppt, (curmax + 1) * PTRsz); + if (!(pthrd = readtask(PT, ptask, NULL))) break; + threadshown = 1; + prochlp(pthrd); // tally & complete this thread + ppt[curmax++] = pthrd; } - free(ptsk); // readproc() proc_t not used + free(ptask); // readproc's proc_t not needed } } } @@ -1182,637 +1956,184 @@ static proc_t **procs_refresh (proc_t **table, int flags) // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists if (curmax >= savmax) { - table = alloc_r(table, (curmax + 1) * PTRsz); + ppt = alloc_r(ppt, (curmax + 1) * PTRsz); // here, we must allocate the underlying proc_t stg ourselves - table[curmax] = alloc_c(ENTsz); + ppt[curmax] = alloc_c(ENTsz); savmax = curmax + 1; } // this frame's end, but not necessarily end of allocated space - table[curmax]->tid = -1; - return table; - -#undef PTRsz -#undef ENTsz -} - -/*###### Field Table/RCfile compatability support ######################*/ - -// from either 'stat' or 'status' (preferred), via bits not otherwise used -#define L_EITHER PROC_SPARE_1 -// These are the Fieldstab.lflg values used here and in reframewins. -// (own identifiers as documentation and protection against changes) -#define L_stat PROC_FILLSTAT -#define L_statm PROC_FILLMEM -#define L_status PROC_FILLSTATUS -#define L_CMDLINE L_EITHER | PROC_FILLARG -#define L_EUSER PROC_FILLUSR -#define L_RUSER L_status | PROC_FILLUSR -#define L_GROUP L_status | PROC_FILLGRP -#define L_NONE 0 -// for reframewins and summary_show 1st pass -#define L_DEFAULT PROC_FILLSTAT - -// a temporary macro, soon to be undef'd... -#define SF(f) (QFP_t)sort_P_ ## f - - /* These are our gosh darn 'Fields' ! - They MUST be kept in sync with pflags !! - note: for integer data, the length modifiers found in .fmts may - NOT reflect the true field type found in proc_t -- this plus - a cast when/if displayed provides minimal width protection. */ -static FLD_t Fieldstab[] = { -/* .lflg anomolies: - P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid) - P_CPU, L_stat - never filled by libproc, but requires times (pcpu) - P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline) - L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit ! - keys head fmts width scale sort desc lflg - ------ ----------- ------- ------ ----- ----- ---------------------- -------- */ - { "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE }, - { "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER }, - { "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER }, - { "DdCc", " UID", " %4u", -1, -1, SF(UID), "User Id", L_NONE }, - { "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER }, - { "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP }, - { "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat }, - { "HhHh", " PR", " %3d", -1, -1, SF(PRI), "Priority", L_stat }, - { "IiIi", " NI", " %3d", -1, -1, SF(NCE), "Nice value", L_stat }, - { "JjYy", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat }, - { "KkEe", " %CPU", " %#4.1f", -1, -1, SF(CPU), "CPU usage", L_stat }, - { "LlWw", " TIME", " %6.6s", 6, -1, SF(TME), "CPU Time", L_stat }, - { "MmRr", " TIME+ ", " %9.9s", 9, -1, SF(TME), "CPU Time, hundredths", L_stat }, - { "NnFf", " %MEM", " %#4.1f", -1, -1, SF(RES), "Memory usage (RES)", L_statm }, - { "OoMm", " VIRT", " %5.5s", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", L_statm }, - { "PpOo", " SWAP", " %4.4s", 4, SK_Kb, SF(SWP), "Swapped size (kb)", L_statm }, - { "QqTt", " RES", " %4.4s", 4, SK_Kb, SF(RES), "Resident size (kb)", L_statm }, - { "RrKk", " CODE", " %4.4s", 4, SK_Kb, SF(COD), "Code size (kb)", L_statm }, - { "SsLl", " DATA", " %4.4s", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm }, - { "TtPp", " SHR", " %4.4s", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm }, - { "UuJj", " nFLT", " %4.4s", 4, SK_no, SF(FLT), "Page Fault count", L_stat }, - { "VvSs", " nDRT", " %4.4s", 4, SK_no, SF(DRT), "Dirty Pages count", L_statm }, - { "WwVv", " S", " %c", -1, -1, SF(STA), "Process Status", L_EITHER }, - // next entry's special: '.head' will be formatted using table entry's own - // '.fmts' plus runtime supplied conversion args! - { "XxXx", " COMMAND", " %-*.*s", -1, -1, SF(CMD), "Command name/line", L_EITHER }, - { "YyUu", " WCHAN ", " %-9.9s", -1, -1, SF(WCH), "Sleeping in Function", L_stat }, - // next entry's special: the 0's will be replaced with '.'! - { "ZzZz", " Flags ", " %08lx", -1, -1, SF(FLG), "Task Flags ", L_stat }, -#if 0 - { "..Qq", " A", " %4.4s", 4, SK_no, SF(PID), "Accessed Page count", L_stat }, - { "..Nn", " TRS", " %4.4s", 4, SK_Kb, SF(PID), "Code in memory (kb)", L_stat }, - { "..Rr", " WP", " %4.4s", 4, SK_no, SF(PID), "Unwritable Pages", L_stat }, - { "Jj[{", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat }, - { "..\\|"," Bad", " %2u", -1, -1, SF(CPN), "-- must ignore | --", 0 }, - { "..]}", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 }, - { "..^~", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 }, -#endif -}; -#undef SF - - - /* All right, those-that-follow -- Listen Up! - * For the above table keys and the following present/future rc file - * compatibility support, you have Mr. Albert D. Cahalan to thank. - * He must have been in a 'Christmas spirit'. Were it left to me, - * this top would never have gotten that close to the former top's - * crufty rcfile. Not only is it illogical, it's odoriferous ! - */ - - // used as 'to' and/or 'from' args in the ft_xxx utilities... -#define FT_NEW_fmt 0 -#define FT_OLD_fmt 2 - - -#if 0 - // convert, or 0 for failure -static int ft_cvt_char (const int fr, const int to, int c) { - int j = -1; - - while (++j < MAXTBL(Fieldstab)) { - if (c == Fieldstab[j].keys[fr]) return Fieldstab[j].keys[to]; - if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1]; - } - return 0; -} -#endif - - - // convert -static inline int ft_get_char (const int fr, int i) { - int c; - if (i < 0) return 0; - if (i >= MAXTBL(Fieldstab)) return 0; - c = Fieldstab[i].keys[fr]; - if (c == '.') c = 0; // '.' marks a bad entry - return c; -} - - -#if 0 - // convert, or -1 for failure -static int ft_get_idx (const int fr, int c) { - int j = -1; - - while (++j < MAXTBL(Fieldstab)) { - if (c == Fieldstab[j].keys[fr]) return j; - if (c == Fieldstab[j].keys[fr+1]) return j; - } - return -1; -} -#endif - - - // convert, or NULL for failure -static const FLD_t *ft_get_ptr (const int fr, int c) { - int j = -1; - - while (++j < MAXTBL(Fieldstab)) { - if (c == Fieldstab[j].keys[fr]) return Fieldstab+j; - if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j; - } - return NULL; -} - - -#if 0 - // convert, or NULL for failure -static const FLD_t *ft_idx_to_ptr (const int i) { - if (i < 0) return NULL; - if (i >= MAXTBL(Fieldstab)) return NULL; - return Fieldstab + i; -} - - - // convert, or -1 for failure -static int ft_ptr_to_idx (const FLD_t *p) { - int i; - if (p < Fieldstab) return -1; - i = p - Fieldstab; - if (i >= MAXTBL(Fieldstab)) return -1; - return i; -} -#endif - - -#if 0 -static void rc_bugless (const RCF_t *const rc) { - const RCW_t *w; - int i = 0; - - fprintf(stderr,"\n%d %d %f %d\n", - rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index - ); - while(i < 4) { - w = &rc->win[i++]; - fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n", - w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks, - w->summclr, w->msgsclr, w->headclr, w->taskclr - ); - } -} -#endif - - -// '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window. -// line 1: an eyecatcher, with a shameless advertisement -// line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin. -// For each of the 4 windows: -// line a: contains winname, fieldscur -// line b: contains winflags, sortindx, maxtasks -// line c: contains summclr, msgsclr, headclr, taskclr -// line d: if present, would crash procps-3.1.1 -static int rc_read_new (const char *const buf, RCF_t *rc) { - int i; - int cnt; - const char *cp; - - cp = strstr(buf, "\n\n" RCF_EYECATCHER); - if (!cp) return -1; - cp = strchr(cp + 2, '\n'); - if (!cp++) return -2; - - cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n", - &rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index - ); - if (cnt != 4) return -3; - cp = strchr(cp, '\n'); - if (!cp++) return -4; - - for (i = 0; i < GROUPSMAX; i++) { - RCW_t *ptr = &rc->win[i]; - cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur); - if (cnt != 2) return 5+100*i; // OK to have less than 4 windows - if (WINNAMSIZ <= strlen(ptr->winname)) return -6; - if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7; - cp = strchr(cp, '\n'); - if (!cp++) return -(8+100*i); - - cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n", - &ptr->winflags, &ptr->sortindx, &ptr->maxtasks - ); - if (cnt != 3) return -(9+100*i); - cp = strchr(cp, '\n'); - if (!cp++) return -(10+100*i); - - cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n", - &ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr - ); - if (cnt != 4) return -(11+100*i); - cp = strchr(cp, '\n'); - if (!cp++) return -(12+100*i); - while (*cp == '\t') { // skip unknown per-window settings - cp = strchr(cp, '\n'); - if (!cp++) return -(13+100*i); - } - } - return 13; -} - - - -static int rc_read_old (const char *const buf, RCF_t *rc) { - const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......"; - const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr"; - unsigned u; - const char *cp; - unsigned c_show = 0; - int badchar = 0; // allow a limited number of duplicates and junk - - char scoreboard[256]; - memset(scoreboard, '\0', sizeof scoreboard); - - cp = buf+2; // skip the "\n\n" we stuck at the beginning - u = 0; - for (;;) { - const char *tmp; - int c = *cp++; - if (u+1 >= sizeof rc->win[0].fieldscur) return -1; - if (c == '\0') return -2; - if (c == '\n') break; - if (c & ~0x7f) return -3; - if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden - if (scoreboard[c|0xe0u]) badchar++; // duplicates not allowed - scoreboard[c|0xe0u]++; - tmp = strchr(old,c); - if (!tmp) continue; - c = *((tmp-old)+std); - if (c == '.') continue; - if (scoreboard[c&0x1fu]) badchar++; // duplicates not allowed - scoreboard[c&0x1fu]++; - rc->win[0].fieldscur[u++] = c; - } - rc->win[0].fieldscur[u++] = '\0'; - if (u < 21) return -6; // catch junk, not good files (had 23 chars in one) - if (u > 33) return -7; // catch junk, not good files (had 29 chars in one) -// fprintf(stderr, "badchar: %d\n", badchar); sleep(2); - if (badchar > 8) return -8; // too much junk - if (!c_show) return -9; // nothing was shown - - // rest of file is optional, but better look right if it exists - if (!*cp) return 12; - if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid? - rc->delay_time = *cp - '0'; - - memset(scoreboard, '\0', sizeof(scoreboard)); - for (;;) { - int c = *++cp & 0xffu; // protect scoreboard[] from negative char - if (!c) return -14; // not OK to hit EOL w/o '\n' - if (c == '\n') break; - switch (c) { - case ' ': - case '.': - case '0' ... '9': - return -15; // not supposed to have digits here - -// case 's': // mostly for global rcfile -// rc->mode_secure = 1; -// break; - case 'S': - rc->win[0].winflags |= Show_CTIMES; - break; - case 'c': - rc->win[0].winflags |= Show_CMDLIN; - break; - case 'i': - rc->win[0].winflags &= ~Show_IDLEPS; - break; - case 'H': - rc->win[0].winflags |= Show_THREADS; - break; - case 'm': - rc->win[0].winflags &= ~View_MEMORY; - break; - case 'l': - rc->win[0].winflags &= ~View_LOADAV; - break; - case 't': - rc->win[0].winflags &= ~View_STATES; - break; - case 'I': - rc->mode_irixps = 0; - break; - - case 'M': - c = 0; // for scoreboard - rc->win[0].sortindx = P_MEM; - break; - case 'P': - c = 0; // for scoreboard - rc->win[0].sortindx = P_CPU; - break; - case 'A': // supposed to be start_time - c = 0; // for scoreboard - rc->win[0].sortindx = P_PID; - break; - case 'T': - c = 0; // for scoreboard - rc->win[0].sortindx = P_TM2; - break; - case 'N': - c = 0; // for scoreboard - rc->win[0].sortindx = P_PID; - break; - - default: - // just ignore it, except for the scoreboard of course - break; - } - if (scoreboard[c]) return -16; // duplicates not allowed - scoreboard[c] = 1; - } - return 17; -} - - -static void rc_write_new (FILE *fp) { - int i; - - fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n", - Myname - ); - fprintf(fp, RCF_DEPRECATED - "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n", - Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk) - ); - for (i = 0; i < GROUPSMAX; i++) { - char buf[40]; - char *cp = Winstk[i].rc.fieldscur; - int j = 0; - - while (j < 36) { - int c = *cp++ & 0xff; - switch (c) { - case '.': - case 1 ... ' ': - case 0x7f ... 0xff: - continue; // throw away junk (some of it) - default: - buf[j++] = c; // gets the '\0' too - } - if (!c) break; - } - fprintf(fp, "%s\tfieldscur=%s\n", - Winstk[i].rc.winname, buf - ); - fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n", - Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks - ); - fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n", - Winstk[i].rc.summclr, Winstk[i].rc.msgsclr, - Winstk[i].rc.headclr, Winstk[i].rc.taskclr - ); - } -} - - -static const char *rc_write_whatever (void) { - FILE *fp = fopen(Rc_name, "w"); - - if (!fp) return strerror(errno); - rc_write_new(fp); - fclose(fp); - return NULL; -} - - + ppt[curmax]->tid = -1; + return ppt; + #undef PTRsz + #undef ENTsz +} // end: procs_refresh + /*###### Startup routines ##############################################*/ -// No mater what *they* say, we handle the really really BIG and -// IMPORTANT stuff upon which all those lessor functions depend! -static void before (char *me) -{ + /* + * No mater what *they* say, we handle the really really BIG and + * IMPORTANT stuff upon which all those lessor functions depend! */ +static void before (char *me) { int i; - /* setup our program name -- big! */ + // setup our program name -- big! Myname = strrchr(me, '/'); if (Myname) ++Myname; else Myname = me; - /* establish cpu particulars -- even bigger! */ - Cpu_tot = smp_num_cpus; + // establish cpu particulars -- even bigger! +#ifdef PRETEND4CPUS + SMP_NUM_CPUS = Cpu_tot = 4; +#else + Cpu_tot = SMP_NUM_CPUS; +#endif if (linux_version_code > LINUX_VERSION(2, 5, 41)) - States_fmts = STATES_line2x5; - if (linux_version_code >= LINUX_VERSION(2, 6, 0)) // grrr... only some 2.6.0-testX :-( - States_fmts = STATES_line2x6; + Cpu_States_fmts = STATES_line2x5; + if (linux_version_code >= LINUX_VERSION(2, 6, 0)) + Cpu_States_fmts = STATES_line2x6; if (linux_version_code >= LINUX_VERSION(2, 6, 11)) - States_fmts = STATES_line2x7; + Cpu_States_fmts = STATES_line2x7; - /* get virtual page size -- nearing huge! */ + // get virtual page stuff -- nearing huge! Page_size = getpagesize(); i = Page_size; - while(i>1024){ - i >>= 1; - page_to_kb_shift++; + while(i > 1024) { i >>= 1; Pg2K_shft++; } + +#ifndef OFF_HST_HASH + // prep for HST_t's put/get hashing optimizations + for (i = 0; i < HHASH_SIZ; i++) HHash_nul[i] = -1; + memcpy(HHash_one, HHash_nul, sizeof(HHash_nul)); + memcpy(HHash_two, HHash_nul, sizeof(HHash_nul)); +#endif + + // lastly, fill in the missing Fieldstab .head and .fmts members + zap_fieldstab(); +} // end: before + + + /* + * Build the local RC file name then try to read both of 'em. + * 'SYS_RCFILESPEC' contains two lines consisting of the secure + * mode switch and an update interval. It's presence limits what + * ordinary users are allowed to do. + * 'Rc_name' contains multiple lines - 2 global + 3 per window. + * line 1: an eyecatcher and creating program/alias name + * line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin. + * If running in secure mode via the /etc/rcfile, + * the 'delay time' will be ignored except for root. + * For each of the 4 windows: + * line a: contains w->winname, fieldscur + * line b: contains w->winflags, sortindx, maxtasks + * line c: contains w->summclr, msgsclr, headclr, taskclr */ +static void configs_read (void) { +#ifndef RCFILE_NOERR + static const char err_rcid[] = "incompatible rcfile, you should delete '%s'"; + static const char err_flds[] = "window entry #%d corrupt, please delete '%s'"; +#else + RCF_t rcdef = DEF_RCFILE; +#endif + float tmp_delay = DEF_DELAY; + char fbuf[LRGBUFSIZ]; + FILE *fp; + int i, x; + char id; + + if (!getenv("HOME")) snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); + else snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname); + + fp = fopen(SYS_RCFILESPEC, "r"); + if (fp) { + fbuf[0] = '\0'; + fgets(fbuf, sizeof(fbuf), fp); // sys rc file, line 1 + if (strchr(fbuf, 's')) Secure_mode = 1; + fbuf[0] = '\0'; + fgets(fbuf, sizeof(fbuf), fp); // sys rc file, line 2 + sscanf(fbuf, "%f", &Rc.delay_time); } - pcpu_max_value = 99.9; + fp = fopen(Rc_name, "r"); + if (fp) { + fbuf[0] = '\0'; + fgets(fbuf, sizeof(fbuf), fp); // ignore eyecatcher + if (5 != (fscanf(fp, "Id:%c, " + "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n" + , &id, &Rc.mode_altscr, &Rc.mode_irixps, &tmp_delay, &i)) + || RCF_VERSION_ID != id) +#ifndef RCFILE_NOERR + error_exit(fmtmk(err_rcid, Rc_name)); +#else + goto just_default_em; +#endif + // you saw that, right? (fscanf stickin' it to 'i') + Curwin = &Winstk[i]; - Fieldstab[P_CPN].head = " P"; - Fieldstab[P_CPN].fmts = " %1u"; - if(smp_num_cpus>9){ - Fieldstab[P_CPN].head = " P"; - Fieldstab[P_CPN].fmts = " %2u"; - } - if(smp_num_cpus>99){ - Fieldstab[P_CPN].head = " P"; - Fieldstab[P_CPN].fmts = " %3u"; - } - if(smp_num_cpus>999){ - Fieldstab[P_CPN].head = " P"; - Fieldstab[P_CPN].fmts = " %4u"; - } - - { - static char pid_fmt[6]; - unsigned pid_digits = get_pid_digits(); - if(pid_digits<4) pid_digits=4; - snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits); - Fieldstab[P_PID].fmts = pid_fmt; - Fieldstab[P_PID].head = " PID" + 10 - pid_digits; - Fieldstab[P_PPD].fmts = pid_fmt; - Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits; - } -} - - -// Config file read *helper* function. -// Anything missing won't show as a choice in the field editor, -// so make sure there is exactly one of each letter. -// -// Due to Rik blindly accepting damem's broken patches, procps-2.0.1x -// has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too. -static void confighlp (char *fields) { - unsigned upper[PFLAGSSIZ]; - unsigned lower[PFLAGSSIZ]; - char c; - char *cp; - - memset(upper, '\0', sizeof upper); - memset(lower, '\0', sizeof lower); - - cp = fields; - for (;;) { - c = *cp++; - if (!c) break; - if(isupper(c)) upper[c&0x1f]++; - else lower[c&0x1f]++; - } - - c = 'a'; - while (c <= 'z') { - if (upper[c&0x1f] && lower[c&0x1f]) { - lower[c&0x1f] = 0; // got both, so wipe out unseen column - for (;;) { - cp = strchr(fields, c); - if (cp) memmove(cp, cp+1, strlen(cp)); - else break; + for (i = 0 ; i < GROUPSMAX; i++) { + // note: "fieldscur=%__s" on next line should equal PFLAGSSIZ ! + fscanf(fp, "%3s\tfieldscur=%64s\n" + , Winstk[i].rc.winname, Winstk[i].rc.fieldscur); +#if PFLAGSSIZ > 64 + // too bad fscanf is not as flexible with his format string as snprintf + # error "Hey, fix the above fscanf 'PFLAGSSIZ' dependency !" +#endif + if (strlen(Winstk[i].rc.fieldscur) != sizeof(DEF_FIELDS) - 1) +#ifndef RCFILE_NOERR + error_exit(fmtmk(err_flds, i+1, Rc_name)); +#else + goto just_default_em; +#endif + for (x = 0; x < P_MAXPFLGS; ++x) { + int f = FLDget(&Winstk[i], x); + if (P_MAXPFLGS <= f) +#ifndef RCFILE_NOERR + error_exit(fmtmk(err_flds, i+1, Rc_name)); +#else + goto just_default_em; +#endif } + fscanf(fp, "\twinflags=%d, sortindx=%u, maxtasks=%d\n" + , &Winstk[i].rc.winflags, &Winstk[i].rc.sortindx, &Winstk[i].rc.maxtasks); + fscanf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n" + , &Winstk[i].rc.summclr, &Winstk[i].rc.msgsclr + , &Winstk[i].rc.headclr, &Winstk[i].rc.taskclr); } - while (lower[c&0x1f] > 1) { // got too many a..z - lower[c&0x1f]--; - cp = strchr(fields, c); - memmove(cp, cp+1, strlen(cp)); - } - while (upper[c&0x1f] > 1) { // got too many A..Z - upper[c&0x1f]--; - cp = strchr(fields, toupper(c)); - memmove(cp, cp+1, strlen(cp)); - } - if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing - lower[c&0x1f]++; - memmove(fields+1, fields, strlen(fields)+1); - fields[0] = c; - } - c++; - } -} - - -// First attempt to read the /etc/rcfile which contains two lines -// consisting of the secure mode switch and an update interval. -// It's presence limits what ordinary users are allowed to do. -// (it's actually an old-style config file) -// -// Then build the local rcfile name and try to read a crufty old-top -// rcfile (whew, odoriferous), which may contain an embedded new-style -// rcfile. Whether embedded or standalone, new-style rcfile values -// will always override that crufty stuff! -// note: If running in secure mode via the /etc/rcfile, -// Delay_time will be ignored except for root. -static void configs_read (void) -{ - const RCF_t def_rcf = DEF_RCFILE; - char fbuf[MEDBUFSIZ]; - int i, fd; - RCF_t rcf; - float delay = Rc.delay_time; - - // read part of an old-style config in /etc/toprc - fd = open(SYS_RCFILESPEC, O_RDONLY); - if (fd > 0) { - ssize_t num; - num = read(fd, fbuf, sizeof(fbuf) - 1); - if (num > 0) { - const char *sec = strchr(fbuf, 's'); - const char *eol = strchr(fbuf, '\n'); - if (eol) { - const char *two = eol + 1; // line two - if (sec < eol) Secure_mode = !!sec; - eol = strchr(two, '\n'); - if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two); - } - } - close(fd); - } - - if (getenv("TOPRC")) { // should switch on Myname before documenting this? - // not the most optimal here... - snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC")); - } else { - snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew... - if (getenv("HOME")) - snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname); - } - - rcf = def_rcf; - fd = open(Rc_name, O_RDONLY); - if (fd > 0) { - ssize_t num; - num = read(fd, fbuf+2, sizeof(fbuf) -3); - if (num > 0) { - fbuf[0] = '\n'; - fbuf[1] = '\n'; - fbuf[num+2] = '\0'; -//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf)); -//sleep(2); - if (rc_read_new(fbuf, &rcf) < 0) { - rcf = def_rcf; // on failure, maybe mangled - if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf; - } - delay = rcf.delay_time; - } - close(fd); - } - - // update Rc defaults, establish a Curwin and fix up the window stack - Rc.mode_altscr = rcf.mode_altscr; - Rc.mode_irixps = rcf.mode_irixps; - if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0; - Curwin = &Winstk[rcf.win_index]; - for (i = 0; i < GROUPSMAX; i++) { - memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]); - confighlp(Winstk[i].rc.fieldscur); - } - - if(Rc.mode_irixps && smp_num_cpus>1){ - // good for 100 CPUs per process - pcpu_max_value = 9999.0; - Fieldstab[P_CPU].fmts = " %4.0f"; + fclose(fp); } // lastly, establish the true runtime secure mode and delay time if (!getuid()) Secure_mode = 0; - if (!Secure_mode) Rc.delay_time = delay; -} + if (!Secure_mode) Rc.delay_time = tmp_delay; + return; + +#ifdef RCFILE_NOERR +just_default_em: + fclose(fp); + Rc = rcdef; + for (i = 0 ; i < GROUPSMAX; i++) + Winstk[i].rc = Rc.win[i]; +#endif +} // end: configs_read -// Parse command line arguments. -// Note: it's assumed that the rc file(s) have already been read -// and our job is to see if any of those options are to be -// overridden -- we'll force some on and negate others in our -// best effort to honor the loser's (oops, user's) wishes... -static void parse_args (char **args) -{ + /* + * Parse command line arguments. + * Note: it's assumed that the rc file(s) have already been read + * and our job is to see if any of those options are to be + * overridden -- we'll force some on and negate others in our + * best effort to honor the loser's (oops, user's) wishes... */ +static void parse_args (char **args) { /* differences between us and the former top: -C (separate CPU states for SMP) is left to an rcfile + -u (user monitoring) added to compliment interactive 'u' -p (pid monitoring) allows a comma delimited list -q (zero delay) eliminated as redundant, incomplete and inappropriate use: "nice -n-10 top -d0" to achieve what was only claimed - -c,i,S act as toggles (not 'on' switches) for enhanced user flexibility + . most switches act as toggles (not 'on' sw) for more user flexibility . no deprecated/illegal use of 'breakargv:' with goto . bunched args are actually handled properly and none are ignored . we tolerate NO whitespace and NO switches -- maybe too tolerant? */ - static const char usage[] = - " -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]"; + static const char usage_str[] = + " -hv | -bcHisS -d delay -n iterations -u|U user | -p pid [,pid ...]"; + static const char sel_error[] = "conflicting process selections (U/p/u)"; + static const char numbs_str[] = "+,-.0123456789"; float tmp_delay = MAXFLOAT; char *p; @@ -1820,10 +2141,16 @@ static void parse_args (char **args) const char *cp = *(args++); while (*cp) { - switch (*cp) { + char ch; + switch ((ch = *cp)) { case '\0': - case '-': break; + case '-': + if (cp[1]) ++cp; + else if (*args) cp = *args++; + if (strspn(cp, numbs_str)) + error_exit(fmtmk("inappropriate '%s'\nusage:\t%s%s", cp, Myname, usage_str)); + continue; case 'b': Batch = 1; break; @@ -1833,17 +2160,18 @@ static void parse_args (char **args) case 'd': if (cp[1]) ++cp; else if (*args) cp = *args++; - else std_err("-d requires argument"); + else error_exit("-d requires argument"); /* a negative delay will be dealt with shortly... */ - if (sscanf(cp, "%f", &tmp_delay) != 1) - std_err(fmtmk("bad delay '%s'", cp)); + if (1 != sscanf(cp, "%f", &tmp_delay)) + error_exit(fmtmk("bad delay '%s'", cp)); break; case 'H': - TOGw(Curwin, Show_THREADS); + Thread_mode = 1; break; case 'h': case 'v': case 'V': - std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage)); + fprintf(stdout, "\t%s\nusage:\t%s%s\n", procps_version, Myname, usage_str); + bye_bye(NULL); case 'i': TOGw(Curwin, Show_IDLEPS); Curwin->rc.maxtasks = 0; @@ -1851,21 +2179,21 @@ static void parse_args (char **args) case 'n': if (cp[1]) cp++; else if (*args) cp = *args++; - else std_err("-n requires argument"); - if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1) - std_err(fmtmk("bad iterations arg '%s'", cp)); + else error_exit("-n requires argument"); + if (1 != sscanf(cp, "%d", &Loops) || 1 > Loops) + error_exit(fmtmk("bad iterations arg '%s'", cp)); break; case 'p': + if (Curwin->usrseltyp) error_exit(sel_error); do { - if (selection_type && selection_type != 'p') std_err("conflicting process selection"); - selection_type = 'p'; if (cp[1]) cp++; else if (*args) cp = *args++; - else std_err("-p argument missing"); + else error_exit("-p argument missing"); if (Monpidsidx >= MONPIDMAX) - std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX)); - if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0) - std_err(fmtmk("bad pid '%s'", cp)); + error_exit(fmtmk("pid limit (%d) exceeded", MONPIDMAX)); + if (1 != sscanf(cp, "%d", &Monpids[Monpidsidx]) + || 0 > Monpids[Monpidsidx]) + error_exit(fmtmk("bad pid '%s'", cp)); if (!Monpids[Monpidsidx]) Monpids[Monpidsidx] = getpid(); Monpidsidx++; @@ -1881,365 +2209,157 @@ static void parse_args (char **args) TOGw(Curwin, Show_CTIMES); break; case 'u': - do { - const char *errmsg; - if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection"); - if (cp[1]) cp++; - else if (*args) cp = *args++; - else std_err("-u missing name"); - errmsg = parse_uid(cp, &selection_uid); - if (errmsg) std_err(errmsg); - selection_type = 'u'; - cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk - } while(0); - break; case 'U': - do { - const char *errmsg; - if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection"); - if (cp[1]) cp++; - else if (*args) cp = *args++; - else std_err("-u missing name"); - errmsg = parse_uid(cp, &selection_uid); - if (errmsg) std_err(errmsg); - selection_type = 'U'; - cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk - } while(0); + { const char *errmsg; + if (Monpidsidx || Curwin->usrseltyp) error_exit(sel_error); + if (cp[1]) cp++; + else if (*args) cp = *args++; + else error_exit(fmtmk("-%c missing name", ch)); + if ((errmsg = user_certify(Curwin, cp, ch))) error_exit(errmsg); + cp += strlen(cp); + } break; default : - std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s" - , *cp, Myname, usage)); + error_exit(fmtmk("unknown option '%c'\nusage:\t%s%s", *cp, Myname, usage_str)); - } /* end: switch (*cp) */ + } // end: switch (*cp) - /* advance cp and jump over any numerical args used above */ - if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1; - } /* end: while (*cp) */ - } /* end: while (*args) */ + // advance cp and jump over any numerical args used above + if (*cp) cp += strspn(&cp[1], numbs_str) + 1; - /* fixup delay time, maybe... */ - if (MAXFLOAT != tmp_delay) { - if (Secure_mode || tmp_delay < 0) - msg_save("Delay time Not changed"); - else - Rc.delay_time = tmp_delay; + } // end: while (*cp) + } // end: while (*args) + + // fixup delay time, maybe... + if (MAXFLOAT > tmp_delay) { + if (Secure_mode) + error_exit("-d disallowed in \"secure\" mode"); + if (0 > tmp_delay) + error_exit("-d requires positive argument"); + Rc.delay_time = tmp_delay; } -} +} // end: parse_args /* * Set up the terminal attributes */ -static void whack_terminal (void) -{ - struct termios newtty; +static void whack_terminal (void) { + static char dummy[] = "dumb"; + struct termios tmptty; + // the curses part... if (Batch) { - setupterm("dumb", STDOUT_FILENO, NULL); + setupterm(dummy, STDOUT_FILENO, NULL); return; } +#ifdef PRETENDNOCAP + setupterm(dummy, STDOUT_FILENO, NULL); +#else setupterm(NULL, STDOUT_FILENO, NULL); - if (tcgetattr(STDIN_FILENO, &Savedtty) == -1) - std_err("failed tty get"); - newtty = Savedtty; - newtty.c_lflag &= ~(ICANON | ECHO); - newtty.c_oflag &= ~(TAB3); - newtty.c_cc[VMIN] = 1; - newtty.c_cc[VTIME] = 0; - +#endif + // our part... + if (-1 == tcgetattr(STDIN_FILENO, &Tty_original)) + error_exit("failed tty get"); + // ok, haven't really changed anything but we do have our snapshot Ttychanged = 1; - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) { - putp(Cap_clr_scr); - std_err(fmtmk("failed tty set: %s", strerror(errno))); - } - tcgetattr(STDIN_FILENO, &Rawtty); -#ifndef STDOUT_IOLBF + + // first, a consistent canonical mode for interactive line input + tmptty = Tty_original; + tmptty.c_lflag |= (ECHO | ECHOCTL | ECHOE | ICANON | ISIG); + tmptty.c_lflag &= ~NOFLSH; + tmptty.c_oflag &= ~TAB3; + tmptty.c_iflag |= BRKINT; + tmptty.c_iflag &= ~IGNBRK; + if (key_backspace && 1 == strlen(key_backspace)) + tmptty.c_cc[VERASE] = *key_backspace; +#ifdef TERMIOS_ONLY + if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmptty)) + error_exit(fmtmk("failed Tty_tweaked set: %s", strerror(errno))); + tcgetattr(STDIN_FILENO, &Tty_tweaked); +#endif + // lastly, a nearly raw mode for unsolicited single keystrokes + tmptty.c_lflag &= ~(ECHO | ECHOCTL | ECHOE | ICANON); + tmptty.c_cc[VMIN] = 1; + tmptty.c_cc[VTIME] = 0; + if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmptty)) + error_exit(fmtmk("failed Tty_raw set: %s", strerror(errno))); + tcgetattr(STDIN_FILENO, &Tty_raw); + +#ifndef OFF_STDIOLBF // thanks anyway stdio, but we'll manage buffering at the frame level... setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf)); #endif - putp(Cap_clr_scr); - fflush(stdout); -} - - -/*###### Field Selection/Ordering routines #############################*/ - - -// Display each field represented in the Fields Table along with its -// description and mark (with a leading asterisk) fields associated -// with upper case letter(s) in the passed 'fields string'. -// -// After all fields have been displayed, some extra explanatory -// text may also be output -static void display_fields (const char *fields, const char *xtra) -{ -#define yRSVD 3 - const char *p; - int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD; - - /* we're relying on callers to first clear the screen and thus avoid screen - flicker if they're too lazy to handle their own asterisk (*) logic */ - putp(Curwin->cap_bold); - for (i = 0; fields[i]; ++i) { - const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]); - int b = isupper(fields[i]); - - if (!f) continue; // hey, should be std_err! - for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces - ; - PUTT("%s%s%c %c: %-10s = %s", - tg2((i / rmax) * cmax, (i % rmax) + yRSVD), - b ? Curwin->cap_bold : Cap_norm, - b ? '*' : ' ', - fields[i], - p, - f->desc - ); - } - if (xtra) { - putp(Curwin->capclr_rownorm); - while ((p = strchr(xtra, '\n'))) { - ++i; - PUTT("%s%.*s", - tg2((i / rmax) * cmax, (i % rmax) + yRSVD), - (int)(p - xtra), - xtra - ); - xtra = ++p; - } - } - putp(Caps_off); - -#undef yRSVD -} - - -// Change order of displayed fields. -static void fields_reorder (void) -{ - static const char prompt[] = - "Upper case letter moves field left, lower case right"; - char c, *p; - int i; - - putp(Cap_clr_scr); - putp(Cap_curs_huge); - for (;;) { - display_fields(Curwin->rc.fieldscur, FIELDS_xtra); - show_special(1, fmtmk(FIELDS_current - , Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt)); - chin(0, &c, 1); - if (!ft_get_ptr(FT_NEW_fmt, c)) break; - i = toupper(c) - 'A'; - if (((p = strchr(Curwin->rc.fieldscur, i + 'A'))) - || ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) { - if (isupper(c)) p--; - if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) { - c = p[0]; - p[0] = p[1]; - p[1] = c; - } - } - } - putp(Cap_curs_norm); -} - -// Select sort field. -static void fields_sort (void) -{ - static const char prompt[] = - "Select sort field via field letter, type any other key to return"; - char phoney[PFLAGSSIZ]; - char c, *p; - int i, x; - - strcpy(phoney, NUL_FIELDS); - x = i = Curwin->rc.sortindx; - putp(Cap_clr_scr); - putp(Cap_curs_huge); - for (;;) { - p = phoney + i; - *p = toupper(*p); - display_fields(phoney, SORT_xtra); - show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt)); - chin(0, &c, 1); - if (!ft_get_ptr(FT_NEW_fmt, c)) break; - i = toupper(c) - 'A'; - *p = tolower(*p); - x = i; - } - if ((p = strchr(Curwin->rc.fieldscur, x + 'a'))) - *p = x + 'A'; - Curwin->rc.sortindx = x; - putp(Cap_curs_norm); - putp(Cap_clr_scr); -} - - -// Toggle displayed fields. -static void fields_toggle (void) -{ - static const char prompt[] = - "Toggle fields via field letter, type any other key to return"; - char c, *p; - int i; - - putp(Cap_clr_scr); - putp(Cap_curs_huge); - for (;;) { - display_fields(Curwin->rc.fieldscur, FIELDS_xtra); - show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt)); - chin(0, &c, 1); - if (!ft_get_ptr(FT_NEW_fmt, c)) break; - i = toupper(c) - 'A'; - if ((p = strchr(Curwin->rc.fieldscur, i + 'A'))) - *p = i + 'a'; - else if ((p = strchr(Curwin->rc.fieldscur, i + 'a'))) - *p = i + 'A'; - } - putp(Cap_curs_norm); - putp(Cap_clr_scr); -} + // and don't forget to ask keyin to initialize his tinfo_tab + keyin(1); +} // end: whack_terminal + /*###### Windows/Field Groups support #################################*/ -// For each of the four windows: -// 1) Set the number of fields/columns to display -// 2) Create the field columns heading -// 3) Set maximum cmdline length, if command lines are in use -// In the process, the required PROC_FILLxxx flags will be rebuilt! -static void reframewins (void) -{ - WIN_t *w; - char *s; - const char *h; - int i, needpsdb = 0; - -// Frames_libflags = 0; // should be called only when it's zero -// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible - w = Curwin; - do { - if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) { - // build window's procflags array and establish a tentative maxpflgs - for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) { - if (isupper(w->rc.fieldscur[i])) - w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A'; - } - - /* build a preliminary columns header not to exceed screen width - while accounting for a possible leading window number */ - *(s = w->columnhdr) = '\0'; - if (Rc.mode_altscr) s = scat(s, " "); - for (i = 0; i < w->maxpflgs; i++) { - h = Fieldstab[w->procflags[i]].head; - // oops, won't fit -- we're outta here... - if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break; - s = scat(s, h); - } - - // establish the final maxpflgs and prepare to grow the command column - // heading via maxcmdln - it may be a fib if P_CMD wasn't encountered, - // but that's ok because it won't be displayed anyway - w->maxpflgs = i; - w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head)); - - // finally, we can build the true run-time columns header, format the - // command column heading, if P_CMD is really being displayed, and - // rebuild the all-important PROC_FILLxxx flags that will be used - // until/if we're we're called again - *(s = w->columnhdr) = '\0'; -// if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum)); - for (i = 0; i < w->maxpflgs; i++) { - int advance = (i==0) && !Rc.mode_altscr; - h = Fieldstab[w->procflags[i]].head; - if (P_WCH == w->procflags[i]) needpsdb = 1; - if (P_CMD == w->procflags[i]) { - s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/ )); - if (CHKw(w, Show_CMDLIN)) { - Frames_libflags |= L_CMDLINE; -// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln; - } - } else - s = scat(s, h+advance); - Frames_libflags |= Fieldstab[w->procflags[i]].lflg; - } - if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0'; - } - if (Rc.mode_altscr) w = w->next; - } while (w != Curwin); - - // do we need the kernel symbol table (and is it already open?) - if (needpsdb) { - if (No_ksyms == -1) { - No_ksyms = 0; - if (open_psdb_message(NULL, msg_save)) - No_ksyms = 1; - else - PSDBopen = 1; - } - } - - if (selection_type=='U') Frames_libflags |= L_status; - - if (Frames_libflags & L_EITHER) { - Frames_libflags &= ~L_EITHER; - if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status; - } - if (!Frames_libflags) Frames_libflags = L_DEFAULT; - if (selection_type=='p') Frames_libflags |= PROC_PID; -} + /* + * Value a window's name and make the associated group name. */ +static void win_names (WIN_t *q, const char *name) { + /* note: sprintf/snprintf results are "undefined" when src==dst, + according to C99 & POSIX.1-2001 (thanks adc) */ + if (q->rc.winname != name) + snprintf(q->rc.winname, sizeof(q->rc.winname), "%s", name); + snprintf(q->grpname, sizeof(q->grpname), "%d:%s", q->winnum, name); +} // end: win_names -// Value a window's name and make the associated group name. -static void win_names (WIN_t *q, const char *name) -{ - // Note that src==dst is illegal in sprintf. - // Failure: amd64, glibc 2.9-20081201, gcc 4.3.4 - if(q->rc.winname != name) - sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name); - sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name); -} - - -// Display a window/field group (ie. make it "current"). -static void win_select (char ch) -{ + /* + * Display a window/field group (ie. make it "current"). */ +static WIN_t *win_select (char ch) { static const char prompt[] = "Choose field group (1 - 4)"; + WIN_t *w = Curwin; // avoid gcc bloat with a local copy /* if there's no ch, it means we're supporting the external interface, so we must try to get our own darn ch by begging the user... */ if (!ch) { show_pmt(prompt); - chin(0, (char *)&ch, 1); + if (1 > chin(0, (char *)&ch, 1)) return Curwin; } switch (ch) { - case 'a': /* we don't carry 'a' / 'w' in our */ - Curwin = Curwin->next; /* pmt - they're here for a good */ - break; /* friend of ours -- wins_colors. */ - case 'w': /* (however those letters work via */ - Curwin = Curwin->prev; /* the pmt too but gee, end-loser */ - break; /* should just press the darn key) */ - case '1': case '2': - case '3': case '4': - Curwin = &Winstk[ch - '1']; + case 'a': // we don't carry 'a' / 'w' in our + w = w->next; // pmt - they're here for a good + break; // friend of ours -- wins_colors. + case 'w': // (however those letters work via + w = w->prev; // the pmt too but gee, end-loser + break; // should just press the darn key) + case '1' ... '4': + w = &Winstk[ch - '1']; + break; + default: // keep gcc happy break; } -} + return Curwin = w; +} // end: win_select -// Just warn the user when a command can't be honored. -static int win_warn (void) -{ - show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname)); - // we gotta' return false 'cause we're somewhat well known within - // macro society, by way of that sassy little tertiary operator... + /* + * Just warn the user when a command can't be honored. */ +static int win_warn (int what) { + switch (what) { + case Warn_ALT: + show_msg("\aCommand disabled, 'A' mode required"); + break; + case Warn_VIZ: + show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'" + , Curwin->grpname)); + break; + default: // keep gcc happy + break; + } + /* we gotta' return false 'cause we're somewhat well known within + macro society, by way of that sassy little tertiary operator... */ return 0; -} +} // end: win_warn -// Change colors *Helper* function to save/restore settings; -// ensure colors will show; and rebuild the terminfo strings. -static void winsclrhlp (WIN_t *q, int save) -{ + /* + * Change colors *Helper* function to save/restore settings; + * ensure colors will show; and rebuild the terminfo strings. */ +static void winsclrhlp (WIN_t *q, int save) { static int flgssav, summsav, msgssav, headsav, tasksav; if (save) { @@ -2251,61 +2371,54 @@ static void winsclrhlp (WIN_t *q, int save) q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav; } capsmk(q); -} +} // end: winsclrhlp -// Change colors used in display -static void wins_colors (void) -{ -#define kbdABORT 'q' -#define kbdAPPLY '\n' - int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr; + /* + * Change colors used in display */ +static void wins_colors (void) { + #define kbdABORT 'q' + #define kbdAPPLY kbd_ENTER + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + int clr = w->rc.taskclr, *pclr = &w->rc.taskclr; char ch, tgt = 'T'; if (0 >= max_colors) { show_msg("\aNo colors to map!"); return; } - winsclrhlp(Curwin, 1); + winsclrhlp(w, 1); putp(Cap_clr_scr); putp(Cap_curs_huge); do { putp(Cap_home); - /* this string is well above ISO C89's minimum requirements! */ - show_special( - 1, - fmtmk( - COLOR_help, - procps_version, - Curwin->grpname, - CHKw(Curwin, View_NOBOLD) ? "On" : "Off", - CHKw(Curwin, Show_COLORS) ? "On" : "Off", - CHKw(Curwin, Show_HIBOLD) ? "On" : "Off", - tgt, - clr, - Curwin->grpname - ) - ); - chin(0, &ch, 1); + // this string is well above ISO C89's minimum requirements! + show_special(1, fmtmk(COLOR_help + , procps_version, w->grpname + , CHKw(w, View_NOBOLD) ? "On" : "Off" + , CHKw(w, Show_COLORS) ? "On" : "Off" + , CHKw(w, Show_HIBOLD) ? "On" : "Off" + , tgt, clr, w->grpname)); + if (1 > chin(0, &ch, 1)) break; switch (ch) { case 'S': - pclr = &Curwin->rc.summclr; + pclr = &w->rc.summclr; clr = *pclr; tgt = ch; break; case 'M': - pclr = &Curwin->rc.msgsclr; + pclr = &w->rc.msgsclr; clr = *pclr; tgt = ch; break; case 'H': - pclr = &Curwin->rc.headclr; + pclr = &w->rc.headclr; clr = *pclr; tgt = ch; break; case 'T': - pclr = &Curwin->rc.taskclr; + pclr = &w->rc.taskclr; clr = *pclr; tgt = ch; break; @@ -2314,134 +2427,70 @@ static void wins_colors (void) *pclr = clr; break; case 'B': - TOGw(Curwin, View_NOBOLD); + TOGw(w, View_NOBOLD); break; case 'b': - TOGw(Curwin, Show_HIBOLD); + TOGw(w, Show_HIBOLD); break; case 'z': - TOGw(Curwin, Show_COLORS); + TOGw(w, Show_COLORS); break; case 'a': case 'w': - win_select(ch); - winsclrhlp(Curwin, 1); - clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr; + winsclrhlp((w = win_select(ch)), 1); + clr = w->rc.taskclr, pclr = &w->rc.taskclr; tgt = 'T'; break; + default: // keep gcc happy + break; } - capsmk(Curwin); + capsmk(w); } while (kbdAPPLY != ch && kbdABORT != ch); - if (kbdABORT == ch) - winsclrhlp(Curwin, 0); + if (kbdABORT == ch) winsclrhlp(w, 0); putp(Cap_curs_norm); - -#undef kbdABORT -#undef kbdAPPLY -} + #undef kbdABORT + #undef kbdAPPLY +} // end: wins_colors -// Manipulate flag(s) for all our windows. -static void wins_reflag (int what, int flg) -{ - WIN_t *w; + /* + * Manipulate flag(s) for all our windows. */ +static void wins_reflag (int what, int flg) { + WIN_t *w = Curwin; // avoid gcc bloat with a local copy - w = Curwin; do { switch (what) { case Flags_TOG: TOGw(w, flg); break; - case Flags_SET: /* Ummmm, i can't find anybody */ - SETw(w, flg); /* who uses Flags_set ... */ + case Flags_SET: // Ummmm, i can't find anybody + SETw(w, flg); // who uses Flags_set ... break; case Flags_OFF: OFFw(w, flg); break; + default: // keep gcc happy + break; } - // a flag with special significance -- user wants to rebalance - // display so we gotta' 'off' one number then force on two flags... - if (EQUWINS_cwo == flg) { - w->rc.maxtasks = 0; - SETw(w, Show_IDLEPS | VISIBLE_tsk); + /* a flag with special significance -- user wants to rebalance + display so we gotta' off some stuff then force on two flags... */ + if (EQUWINS_xxx == flg) { + w->rc.maxtasks = w->usrseltyp = w->begpflg = w->begtask = 0; + Monpidsidx = 0; + SETw(w, Show_IDLEPS | Show_TASKON); } w = w->next; } while (w != Curwin); -} +} // end: wins_reflag -// using a flag to avoid other code seeing inconsistant state -static volatile int need_resize; -static void wins_resize_sighandler (int dont_care_sig) -{ - (void)dont_care_sig; - need_resize = 1; - ZAP_TIMEOUT -} - - -// Set the screen dimensions and arrange for the real workhorse. -// (also) catches: -// SIGWINCH and SIGCONT -static void wins_resize (void) -{ - struct winsize wz; - char *env_columns; // Unix98 environment variable COLUMNS - char *env_lines; // Unix98 environment variable LINES - - Screen_cols = columns; // - Screen_rows = lines; // - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) { - Screen_cols = wz.ws_col; - Screen_rows = wz.ws_row; - } - - if (Batch) Screen_rows = MAXINT; - - env_columns = getenv("COLUMNS"); - if(env_columns && *env_columns){ - long t; - char *endptr; - t = strtol(env_columns, &endptr, 0); - if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t; - } - env_lines = getenv("LINES"); - if(env_lines && *env_lines){ - long t; - char *endptr; - t = strtol(env_lines, &endptr, 0); - if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t; - } - - // be crudely tolerant of crude tty emulators - if (avoid_last_column) Screen_cols--; - - // we might disappoint some folks (but they'll deserve it) - if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX; - - // keep our support for output optimization in sync with current reality - // note: when we're in Batch mode, we don't really need a Pseudo_scrn and - // when not Batch, our buffer will contain 1 extra 'line' since - // Msg_row is never represented -- but it's nice to have some space - // between us and the great-beyond... - Pseudo_cols = Screen_cols + CLRBUFSIZ + 1; - if (Batch) Pseudo_size = ROWBUFSIZ + 1; - else Pseudo_size = Pseudo_cols * Screen_rows; - Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size); - - // force rebuild of column headers AND libproc/readproc requirements - Frames_libflags = 0; -} - - -// Set up the raw/incomplete field group windows -- -// they'll be finished off after startup completes. -// [ and very likely that will override most/all of our efforts ] -// [ --- life-is-NOT-fair --- ] -static void windows_stage1 (void) -{ + /* + * Set up the raw/incomplete field group windows -- + * they'll be finished off after startup completes. + * [ and very likely that will override most/all of our efforts ] + * [ --- life-is-NOT-fair --- ] */ +static void wins_stage_1 (void) { WIN_t *w; int i; @@ -2462,84 +2511,106 @@ static void windows_stage1 (void) w->prev = w - 1; ++w; } - /* fixup the circular chains... */ - Winstk[3].next = &Winstk[0]; - Winstk[0].prev = &Winstk[3]; + + // fixup the circular chains... + Winstk[GROUPSMAX - 1].next = &Winstk[0]; + Winstk[0].prev = &Winstk[GROUPSMAX - 1]; Curwin = Winstk; -} +} // end: wins_stage_1 -// This guy just completes the field group windows after the -// rcfiles have been read and command line arguments parsed -static void windows_stage2 (void) -{ + /* + * This guy just completes the field group windows after the + * rcfiles have been read and command line arguments parsed */ +static void wins_stage_2 (void) { int i; for (i = 0; i < GROUPSMAX; i++) { win_names(&Winstk[i], Winstk[i].rc.winname); capsmk(&Winstk[i]); } - // rely on this next guy to force a call (eventually) to reframewins - wins_resize(); -} + // this guy will build each window's columnhdr + calibrate_fields(); +} // end: wins_stage_2 + +/*###### Interactive Input support (do_key helpers) ####################*/ + + /* + * These routines exist just to keep the do_key() function + * a reasonably modest size. */ + +static void file_writerc (void) { + FILE *fp; + int i; + + if (!(fp = fopen(Rc_name, "w"))) { + show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, strerror(errno))); + return; + } + fprintf(fp, "%s's " RCF_EYECATCHER, Myname); + fprintf(fp, "Id:%c, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%d\n" + , RCF_VERSION_ID + , Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (int)(Curwin - Winstk)); + + for (i = 0 ; i < GROUPSMAX; i++) { + fprintf(fp, "%s\tfieldscur=%s\n" + , Winstk[i].rc.winname, Winstk[i].rc.fieldscur); + fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n" + , Winstk[i].rc.winflags, Winstk[i].rc.sortindx + , Winstk[i].rc.maxtasks); + fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n" + , Winstk[i].rc.summclr, Winstk[i].rc.msgsclr + , Winstk[i].rc.headclr, Winstk[i].rc.taskclr); + } + fclose(fp); + show_msg(fmtmk("Wrote configuration to '%s'", Rc_name)); +} // end: file_writerc -/*###### Main Screen routines ##########################################*/ +static void help_view (void) { + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + char ch; -// Process keyboard input during the main loop -static void do_key (unsigned c) -{ - // standardized 'secure mode' errors + putp(Cap_clr_scr); + putp(Cap_curs_huge); + + // this string is well above ISO C89's minimum requirements! + show_special(1, fmtmk(KEYS_help + , procps_version + , w->grpname + , CHKw(w, Show_CTIMES) ? "On" : "Off" + , Rc.delay_time + , Secure_mode ? "On" : "Off" + , Secure_mode ? "" : KEYS_help_unsecured)); + + if (0 < chin(0, &ch, 1) + && ('?' == ch || 'h' == ch || 'H' == ch)) { + do { + putp(Cap_clr_scr); + show_special(1, fmtmk(WINDOWS_help + , w->grpname + , Winstk[0].rc.winname, Winstk[1].rc.winname + , Winstk[2].rc.winname, Winstk[3].rc.winname)); + if (1 > chin(0, &ch, 1)) break; + w = win_select(ch); + } while (kbd_ENTER != ch); + } + + putp(Cap_curs_norm); +} // end: help_view + + +static void keys_global (int ch) { + // standardized error message(s) static const char err_secure[] = "\aUnavailable in secure mode"; - static const char err_num_cpus[] = "\aSorry, terminal is not big enough"; -#ifdef WARN_NOT_SMP - // standardized 'smp' errors - static const char err_smp[] = "\aSorry, only 1 cpu detected"; -#endif - - switch (c) { - case '1': - if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) { - show_msg(err_num_cpus); - break; - } -#ifdef WARN_NOT_SMP - if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM); - else show_msg(err_smp); -#else - TOGw(Curwin, View_CPUSUM); -#endif - break; - - case 'a': - if (Rc.mode_altscr) Curwin = Curwin->next; - break; - - case 'A': - Rc.mode_altscr = !Rc.mode_altscr; - wins_resize(); - break; - - case 'b': - if (VIZCHKc) { - if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS)) - show_msg("\aNothing to highlight!"); - else { - TOGw(Curwin, Show_HIBOLD); - capsmk(Curwin); - } - } - break; + static const char err_notsmp[] = "\aOnly 1 cpu detected"; + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + switch (ch) { case 'B': - TOGw(Curwin, View_NOBOLD); - capsmk(Curwin); + TOGw(w, View_NOBOLD); + capsmk(w); break; - - case 'c': - VIZTOGc(Show_CMDLIN); - break; - case 'd': case 's': if (Secure_mode) @@ -2547,352 +2618,395 @@ static void do_key (unsigned c) else { float tmp = get_float(fmtmk("Change delay from %.1f to", Rc.delay_time)); - if (tmp > -1) Rc.delay_time = tmp; + if (-1 < tmp) Rc.delay_time = tmp; } break; - - case 'f': - if (VIZCHKc) fields_toggle(); - break; - case 'F': - case 'O': - if (VIZCHKc) fields_sort(); + case 'f': + fields_utility(); break; - case 'g': - if (Rc.mode_altscr) { - char tmp[GETBUFSIZ]; - strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname))); - if (tmp[0]) win_names(Curwin, tmp); - } - break; - - case 'G': win_select(0); break; - case 'H': - if (VIZCHKc) { - TOGw(Curwin, Show_THREADS); - show_msg(fmtmk("Show threads %s" - , CHKw(Curwin, Show_THREADS) ? "On" : "Off")); - } + Thread_mode = !Thread_mode; + show_msg(fmtmk("Show threads %s", Thread_mode ? "On" : "Off")); + zap_fieldstab(); break; - - case 'h': - case '?': - { char ch; - putp(Cap_clr_scr); - putp(Cap_curs_huge); - /* this string is well above ISO C89's minimum requirements! */ - show_special( - 1, - fmtmk( - KEYS_help, - procps_version, - Curwin->grpname, - CHKw(Curwin, Show_CTIMES) ? "On" : "Off", - Rc.delay_time, - Secure_mode ? "On" : "Off", - Secure_mode ? "" : KEYS_help_unsecured - ) - ); - chin(0, &ch, 1); - if ('?' == ch || 'h' == ch) { - do { - putp(Cap_clr_scr); - show_special(1, fmtmk(WINDOWS_help - , Curwin->grpname - , Winstk[0].rc.winname - , Winstk[1].rc.winname - , Winstk[2].rc.winname - , Winstk[3].rc.winname)); - chin(0, &ch, 1); - win_select(ch); - } while ('\n' != ch); - } - putp(Cap_curs_norm); - } - break; - - case 'i': - VIZTOGc(Show_IDLEPS); - break; - case 'I': -#ifdef WARN_NOT_SMP if (Cpu_tot > 1) { Rc.mode_irixps = !Rc.mode_irixps; show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off")); + zap_fieldstab(); } else - show_msg(err_smp); -#else - Rc.mode_irixps = !Rc.mode_irixps; - show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off")); -#endif - if(Rc.mode_irixps && smp_num_cpus>1){ - // good for 100 CPUs per process - pcpu_max_value = 9999.0; - Fieldstab[P_CPU].fmts = " %4.0f"; - } else { - pcpu_max_value = 99.9; - Fieldstab[P_CPU].fmts = " %#4.1f"; - } + show_msg(err_notsmp); break; - case 'k': if (Secure_mode) { show_msg(err_secure); } else { - int sig, pid = get_int("PID to kill"); - if (pid > 0) { + int sig, pid; + if (-1 < (pid = get_int("pid to signal/kill"))) { sig = signal_name_to_number( - ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL))); - if (sig == -1) sig = DEF_SIGNAL; - if (sig && kill(pid, sig)) - show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno))); + linein(fmtmk("Send pid %d signal [%d/sigterm]", pid, SIGTERM))); + if (-1 == sig) sig = SIGTERM; + if (0 < sig && kill(pid, sig)) + show_msg(fmtmk("\aFailed signal pid '%d' with '%d': %s" + , pid, sig, strerror(errno))); } } break; - - case 'l': - TOGw(Curwin, View_LOADAV); - break; - - case 'm': - TOGw(Curwin, View_MEMORY); - break; - - case 'n': - case '#': - if (VIZCHKc) { - int num = - get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks)); - if (num > -1) Curwin->rc.maxtasks = num; - } - break; - - case 'o': - if (VIZCHKc) fields_reorder(); - break; - - case 'q': - end_pgm(0); - case 'r': if (Secure_mode) show_msg(err_secure); else { - int val, pid = get_int("PID to renice"); - if (pid > 0) { - val = get_int(fmtmk("Renice PID %d to value", pid)); + int val, pid; + if (-1 < (pid = get_int("PID to renice")) + && INT_MIN < (val = get_int(fmtmk("Renice PID %d to value", pid)))) if (setpriority(PRIO_PROCESS, (unsigned)pid, val)) - show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno))); - } + show_msg(fmtmk("\aFailed renice of PID %d to %d: %s" + , pid, val, strerror(errno))); } break; - - case 'R': - VIZTOGc(Qsrt_NORMAL); - break; - - case 'S': - if (VIZCHKc) { - TOGw(Curwin, Show_CTIMES); - show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off")); - } - break; - - case 't': - TOGw(Curwin, View_STATES); - break; - -// case 'u': -// if (VIZCHKc) -// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)")); -// break; - - case 'u': -// if (!VIZCHKc) break; - do { - const char *errmsg; - const char *answer; - answer = ask4str("Which user (blank for all)"); - // FIXME: do this better: - if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') { - selection_type = 0; - selection_uid = -1; - break; - } - errmsg = parse_uid(answer, &selection_uid); - if (errmsg) { - show_msg(errmsg); - // Change settings here? I guess not. - break; - } - selection_type = 'u'; - } while(0); - break; - - case 'U': -// if (!VIZCHKc) break; - do { - const char *errmsg; - const char *answer; - answer = ask4str("Which user (blank for all)"); - // FIXME: do this better: - if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') { - selection_type = 0; - selection_uid = -1; - break; - } - errmsg = parse_uid(answer, &selection_uid); - if (errmsg) { - show_msg(errmsg); - // Change settings here? I guess not. - break; - } - selection_type = 'U'; - } while(0); - break; - - case 'w': - if (Rc.mode_altscr) Curwin = Curwin->prev; - break; - - case 'W': - { const char *err = rc_write_whatever(); - if (err) - show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err)); - else - show_msg(fmtmk("Wrote configuration to '%s'", Rc_name)); - } - break; - - case 'x': - if (VIZCHKc) { - TOGw(Curwin, Show_HICOLS); - capsmk(Curwin); - } - break; - - case 'y': - if (VIZCHKc) { - TOGw(Curwin, Show_HIROWS); - capsmk(Curwin); - } - break; - - case 'z': - if (VIZCHKc) { - TOGw(Curwin, Show_COLORS); - capsmk(Curwin); - } - break; - case 'Z': wins_colors(); break; - - case '-': - if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk); + default: // keep gcc happy break; + } +} // end: keys_global - case '_': - if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk); + +static void keys_summary (int ch) { + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + + switch (ch) { + case '1': + TOGw(w, View_CPUSUM); break; - - case '=': - Curwin->rc.maxtasks = 0; - SETw(Curwin, Show_IDLEPS | VISIBLE_tsk); - Monpidsidx = 0; - selection_type = '\0'; + case 'C': + VIZTOGw(w, View_SCROLL); break; - - case '+': - if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo); + case 'l': + TOGw(w, View_LOADAV); break; + case 'm': + TOGw(w, View_MEMORY); + break; + case 't': + TOGw(w, View_STATES); + break; + default: // keep gcc happy + break; + } +} // end: keys_summary + +static void keys_task (int ch) { + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + + switch (ch) { + case '#': + case 'n': + if (VIZCHKw(w)) { + int num = get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", w->rc.maxtasks)); + if (INT_MIN < num) w->rc.maxtasks = num; + } + break; case '<': - if (VIZCHKc) { - FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1; - while (*p != Curwin->rc.sortindx) --p; - if (--p >= Curwin->procflags) - Curwin->rc.sortindx = *p; + if (VIZCHKw(w)) { + FLG_t *p = w->procflgs + w->maxpflgs - 1; + while (p > w->procflgs && *p != w->rc.sortindx) --p; + if (*p == w->rc.sortindx) { + --p; +#ifndef USE_X_COLHDR + if (P_MAXPFLGS < *p) --p; +#endif + if (p >= w->procflgs) w->rc.sortindx = *p; + } } break; - case '>': - if (VIZCHKc) { - FLG_t *p = Curwin->procflags; - while (*p != Curwin->rc.sortindx) ++p; - if (++p < Curwin->procflags + Curwin->maxpflgs) - Curwin->rc.sortindx = *p; + if (VIZCHKw(w)) { + FLG_t *p = w->procflgs + w->maxpflgs - 1; + while (p > w->procflgs && *p != w->rc.sortindx) --p; + if (*p == w->rc.sortindx) { + ++p; +#ifndef USE_X_COLHDR + if (P_MAXPFLGS < *p) ++p; +#endif + if (p < w->procflgs + w->maxpflgs) w->rc.sortindx = *p; + } } break; + case 'b': + if (VIZCHKw(w)) { +#ifdef USE_X_COLHDR + if (!CHKw(w, Show_HIROWS)) +#else + if (!CHKw(w, Show_HICOLS | Show_HIROWS)) +#endif + show_msg("\aNothing to highlight!"); + else { + TOGw(w, Show_HIBOLD); + capsmk(w); + } + } + break; + case 'c': + VIZTOGw(w, Show_CMDLIN); + break; + case 'i': + VIZTOGw(w, Show_IDLEPS); + break; + case 'R': + VIZTOGw(w, Qsrt_NORMAL); + break; + case 'S': + if (VIZCHKw(w)) { + TOGw(w, Show_CTIMES); + show_msg(fmtmk("Cumulative time %s", CHKw(w, Show_CTIMES) ? "On" : "Off")); + } + break; + case 'U': + case 'u': + if (VIZCHKw(w)) { + const char *errmsg; + if ((errmsg = user_certify(w, linein("Which user (blank for all)"), ch))) + show_msg(errmsg); + } + break; + case 'x': + if (VIZCHKw(w)) { + TOGw(w, Show_HICOLS); + capsmk(w); + } + break; + case 'y': + if (VIZCHKw(w)) { + TOGw(w, Show_HIROWS); + capsmk(w); + } + break; + case 'z': + if (VIZCHKw(w)) { + TOGw(w, Show_COLORS); + capsmk(w); + } + break; + default: // keep gcc happy + break; + } +} // end: keys_task - case 'M': // these keys represent old-top compatability - case 'N': // -- grouped here so that if users could ever - case 'P': // be weaned, we would just whack this part... + +static void keys_window (int ch) { + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + + switch (ch) { + case '+': + if (ALTCHKw) wins_reflag(Flags_OFF, EQUWINS_xxx); + break; + case '-': + if (ALTCHKw) TOGw(w, Show_TASKON); + break; + case '=': + SETw(w, Show_IDLEPS | Show_TASKON); + w->rc.maxtasks = w->usrseltyp = w->begpflg = w->begtask = 0; + Monpidsidx = 0; + break; + case '_': + if (ALTCHKw) wins_reflag(Flags_TOG, Show_TASKON); + break; + case 'A': + Rc.mode_altscr = !Rc.mode_altscr; + break; + case 'a': + case 'w': + if (ALTCHKw) win_select(ch); + break; + case 'G': + if (ALTCHKw) { + char tmp[SMLBUFSIZ]; + STRLCPY(tmp, linein(fmtmk("Rename window '%s' to (1-3 chars)", w->rc.winname))) + if (tmp[0]) win_names(w, tmp); + } + break; + case kbd_UP: + if (VIZCHKw(w)) if (0 < w->begtask) w->begtask -= 1; + break; + case kbd_DOWN: + if (VIZCHKw(w)) if (w->begtask < Frame_maxtask - 1) w->begtask += 1; + break; +#ifdef USE_X_COLHDR + case kbd_LEFT: + if (VIZCHKw(w)) if (0 < w->begpflg) w->begpflg -= 1; + break; + case kbd_RIGHT: + if (VIZCHKw(w)) if (w->begpflg + 1 < w->totpflgs) w->begpflg += 1; + break; +#else + case kbd_LEFT: + if (VIZCHKw(w)) if (0 < w->begpflg) { + w->begpflg -= 1; + if (P_MAXPFLGS < w->pflgsall[w->begpflg]) w->begpflg -= 2; + } + break; + case kbd_RIGHT: + if (VIZCHKw(w)) if (w->begpflg + 1 < w->totpflgs) { + if (P_MAXPFLGS < w->pflgsall[w->begpflg]) + w->begpflg += (w->begpflg + 3 < w->totpflgs) ? 3 : 0; + else w->begpflg += 1; + } + break; +#endif + case kbd_PGUP: + if (VIZCHKw(w)) if (0 < w->begtask) { + w->begtask -= (w->winlines - 1); + if (0 > w->begtask) w->begtask = 0; + } + break; + case kbd_PGDN: + if (VIZCHKw(w)) if (w->begtask < Frame_maxtask - 1) { + w->begtask += (w->winlines - 1); + if (w->begtask > Frame_maxtask - 1) w->begtask = Frame_maxtask - 1; + if (0 > w->begtask) w->begtask = 0; + } + break; + case kbd_HOME: + if (VIZCHKw(w)) w->begtask = w->begpflg = 0; + break; + case kbd_END: + if (VIZCHKw(w)) { + w->begtask = (Frame_maxtask - w->winlines) + 1; + if (0 > w->begtask) w->begtask = 0; + w->begpflg = w->endpflg; + } + break; + default: // keep gcc happy + break; + } +} // end: keys_window + + +static void keys_xtra (int ch) { +// const char *xmsg; + WIN_t *w = Curwin; // avoid gcc bloat with a local copy + + /* these keys represent old-top compatability -- + they're grouped here so that if users could ever be weaned, + we would just whack do_key's key_tab entry and this function... */ + switch (ch) { + case 'M': + w->rc.sortindx = P_MEM; +// xmsg = "Memory"; + break; + case 'N': + w->rc.sortindx = P_PID; +// xmsg = "Numerical"; + break; + case 'P': + w->rc.sortindx = P_CPU; +// xmsg = "CPU"; + break; case 'T': - { static struct { - const char *xmsg; - const unsigned xkey; - const FLG_t sort; - } xtab[] = { - { "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, }, - { "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, }; - int i; - for (i = 0; i < MAXTBL(xtab); ++i) - if (c == xtab[i].xkey) { - Curwin->rc.sortindx = xtab[i].sort; -// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg)); + w->rc.sortindx = P_TM2; +// xmsg = "Time"; + break; + default: // keep gcc happy + break; + } +// some have objected to this message, so we'll just keep silent... +// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg)); +} // end: keys_xtra + +/*###### Main Screen routines ##########################################*/ + + /* + * Process keyboard input during the main loop */ +static void do_key (int ch) { + static struct { + void (*func)(int ch); + char keys[SMLBUFSIZ]; + } key_tab[] = { + { keys_global, + { 'B', 'd', 'F', 'f', 'g', 'H', 'I', 'k', 'r', 's', 'Z' } }, + { keys_summary, + { '1', 'C', 'l', 'm', 't' } }, + { keys_task, + { '#', '<', '>', 'b', 'c', 'i', 'n', 'R', 'S' + , 'U', 'u', 'x', 'y', 'z' } }, + { keys_window, + { '+', '-', '=', '_', 'A', 'a', 'G', 'w' + , kbd_UP, kbd_DOWN, kbd_LEFT, kbd_RIGHT, kbd_PGUP, kbd_PGDN + , kbd_HOME, kbd_END } }, + { keys_xtra, + { 'M', 'N', 'P', 'T' } } + }; + int i; + + switch (ch) { + case 0: // ignored (always) + case kbd_ESC: // ignored (sometimes) + return; + case 'q': // no return from this guy + bye_bye(NULL); + case 'W': // no need for rebuilds + file_writerc(); + return; + case '?': // might need rebuilds, + case 'h': // if curwin is changed + help_view(); + break; + case kbd_ENTER: // these two will have the effect of waking us + case kbd_SPACE: // from 'select()' then refreshing the display + break; + default: // and now, the real work... + for (i = 0; i < MAXTBL(key_tab); ++i) + if (strchr(key_tab[i].keys, ch)) { + key_tab[i].func(ch); break; } - } - break; - case '\n': // just ignore these, they'll have the effect - case ' ': // of refreshing display after waking us up ! - break; + if (!(i < MAXTBL(key_tab))) { + show_msg("\aUnknown command - try 'h' for help"); + return; + } + }; - default: - show_msg("\aUnknown command - try 'h' for help"); - } - // The following assignment will force a rebuild of all column headers and - // the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are - // some keys that COULD require new column headers and/or libproc flags: - // 'A' - likely - // 'c' - likely when !Mode_altscr, maybe when Mode_altscr - // 'F' - maybe, if new field forced on - // 'f' - likely - // 'G' - likely - // 'O' - maybe, if new field forced on - // 'o' - maybe, if new field brought into view - // 'Z' - likely, if 'Curwin' changed when !Mode_altscr - // '-' - likely (restricted to Mode_altscr) - // '_' - likely (restricted to Mode_altscr) - // '=' - maybe, but only when Mode_altscr - // '+' - likely (restricted to Mode_altscr) - // ( At this point we have a human being involved and so have all the time ) - // ( in the world. We can afford a few extra cpu cycles every now & then! ) - Frames_libflags = 0; -} + /* The following assignment will force a rebuild of all column headers and + the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are + some keys that COULD require new column headers and/or libproc flags: + 'A' - likely + 'c' - likely when !Mode_altscr, maybe when Mode_altscr + 'F' - likely + 'f' - likely + 'G' - likely + 'Z' - likely, if 'Curwin' changed when !Mode_altscr + '-' - likely (restricted to Mode_altscr) + '_' - likely (restricted to Mode_altscr) + '=' - maybe, but only when Mode_altscr + '+' - likely (restricted to Mode_altscr) + PLUS, likely for FOUR of the EIGHT cursor motion keys (scrolled) + ( At this point we have a human being involved and so have all the time ) + ( in the world. We can afford a few extra cpu cycles every now & then! ) + */ + Frames_resize = 1; +} // end: do_key -// State display *Helper* function to calc and display the state -// percentages for a single cpu. In this way, we can support -// the following environments without the usual code bloat. -// 1) single cpu machines -// 2) modest smp boxes with room for each cpu's percentages -// 3) massive smp guys leaving little or no room for process -// display and thus requiring the cpu summary toggle -static void summaryhlp (CPU_t *cpu, const char *pfx) -{ - // we'll trim to zero if we get negative time ticks, - // which has happened with some SMP kernels (pre-2.4?) -#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz) + /* + * State display *Helper* function to calc and display the state + * percentages for a single cpu. In this way, we can support + * the following environments without the usual code bloat. + * 1) single cpu machines + * 2) modest smp boxes with room for each cpu's percentages + * 3) massive smp guys leaving little or no room for process + * display and thus requiring the cpu summary toggle */ +static void summaryhlp (CPU_t *cpu, const char *pfx) { + /* we'll trim to zero if we get negative time ticks, + which has happened with some SMP kernels (pre-2.4?) */ + #define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz) SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz; float scale; @@ -2900,32 +3014,22 @@ static void summaryhlp (CPU_t *cpu, const char *pfx) s_frme = cpu->s - cpu->s_sav; n_frme = cpu->n - cpu->n_sav; i_frme = TRIMz(cpu->i - cpu->i_sav); + if ((u_frme == 0) && (i_frme == 0)) i_frme = 100; w_frme = cpu->w - cpu->w_sav; x_frme = cpu->x - cpu->x_sav; y_frme = cpu->y - cpu->y_sav; z_frme = cpu->z - cpu->z_sav; tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme; - if (tot_frme < 1) tot_frme = 1; + if (1 > tot_frme) tot_frme = 1; scale = 100.0 / (float)tot_frme; - // display some kinda' cpu state percentages - // (who or what is explained by the passed prefix) - show_special( - 0, - fmtmk( - States_fmts, - pfx, - (float)u_frme * scale, - (float)s_frme * scale, - (float)n_frme * scale, - (float)i_frme * scale, - (float)w_frme * scale, - (float)x_frme * scale, - (float)y_frme * scale, - (float)z_frme * scale - ) - ); - Msg_row += 1; + /* display some kinda' cpu state percentages + (who or what is explained by the passed prefix) */ + show_special(0, fmtmk(Cpu_States_fmts, pfx + , (float)u_frme * scale, (float)s_frme * scale + , (float)n_frme * scale, (float)i_frme * scale + , (float)w_frme * scale, (float)x_frme * scale + , (float)y_frme * scale, (float)z_frme * scale)); // remember for next time around cpu->u_sav = cpu->u; @@ -2936,331 +3040,346 @@ static void summaryhlp (CPU_t *cpu, const char *pfx) cpu->x_sav = cpu->x; cpu->y_sav = cpu->y; cpu->z_sav = cpu->z; - -#undef TRIMz -} + #undef TRIMz +} // end: summaryhlp -// Begin a new frame by: -// 1) Refreshing the all important proc table -// 2) Displaying uptime and load average (maybe) -// 3) Displaying task/cpu states (maybe) -// 4) Displaying memory & swap usage (maybe) -// and then, returning a pointer to the pointers to the proc_t's! -static proc_t **summary_show (void) -{ + /* + * Begin a new frame by: + * 1) Refreshing the all important proc table + * 2) Displaying uptime and load average (maybe) + * 3) Displaying task/cpu states (maybe) + * 4) Displaying memory & swap usage (maybe) + * and then, returning a pointer to the pointers to the proc_t's! */ +static proc_t **summary_show (void) { + #define isROOM(f,n) (CHKw(w, f) && Msg_row + (n) < Screen_rows - 1) + #define anyFLG 0xffffff static proc_t **p_table = NULL; - static CPU_t *smpcpu = NULL; + static CPU_t *smpcpu = NULL; + WIN_t *w = Curwin; // avoid gcc bloat with a local copy // whoa first time, gotta' prime the pump... if (!p_table) { - p_table = procs_refresh(NULL, Frames_libflags); + struct timeval tv = { 0, 300000 }; + p_table = procs_refresh(NULL); putp(Cap_clr_scr); - putp(Cap_rmam); -#ifndef PROF - // sleep for half a second - tv.tv_sec = 0; - tv.tv_usec = 500000; - select(0, NULL, NULL, NULL, &tv); // ought to loop until done -#endif - } else { + select(0, NULL, NULL, NULL, &tv); // sleep for a bit... + } else putp(Batch ? "\n\n" : Cap_home); - } - p_table = procs_refresh(p_table, Frames_libflags); + + p_table = procs_refresh(p_table); // Display Uptime and Loadavg - if (CHKw(Curwin, View_LOADAV)) { - if (!Rc.mode_altscr) { + if (isROOM(View_LOADAV, 1)) { + if (!Rc.mode_altscr) show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime())); - } else { - show_special( - 0, - fmtmk( - CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line, - Curwin->grpname, - sprint_uptime() - ) - ); - } + else + show_special(0, fmtmk(CHKw(w, Show_TASKON)? LOADAV_line_alt : LOADAV_line + , w->grpname, sprint_uptime())); Msg_row += 1; } // Display Task and Cpu(s) States - if (CHKw(Curwin, View_STATES)) { - show_special( - 0, - fmtmk( - STATES_line1, - Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied - ) - ); + if (isROOM(View_STATES, 2)) { + show_special(0, fmtmk(STATES_line1 + , Thread_mode ? "Threads" : "Tasks" + , Frame_maxtask, Frame_running, Frame_sleepin + , Frame_stopped, Frame_zombied)); Msg_row += 1; smpcpu = cpus_refresh(smpcpu); - if (CHKw(Curwin, View_CPUSUM)) { + if (CHKw(w, View_CPUSUM)) { // display just the 1st /proc/stat line summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):"); + Msg_row += 1; } else { int i; - char tmp[SMLBUFSIZ]; - // display each cpu's states separately + char tmp[MEDBUFSIZ]; + // display each cpu's states separately, screen height permitting... for (i = 0; i < Cpu_tot; i++) { snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id); summaryhlp(&smpcpu[i], tmp); + Msg_row += 1; + if (!isROOM(anyFLG, 1)) break; } } } // Display Memory and Swap stats meminfo(); - if (CHKw(Curwin, View_MEMORY)) { - show_special(0, fmtmk(MEMORY_line1 - , kb_main_total, kb_main_used, kb_main_free, kb_main_buffers)); - show_special(0, fmtmk(MEMORY_line2 - , kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached)); + if (isROOM(View_MEMORY, 2)) { + #define mkM(x) (unsigned long)(kb_main_ ## x >> shift) + #define mkS(x) (unsigned long)(kb_swap_ ## x >> shift) + const char *which = "Kb"; + int shift = 0; + + /*** hotplug_acclimated ***/ + if (kb_main_total > 9999999) { which = "Mb"; shift = 10; } + if (kb_main_total > 9999999999) { which = "Gb"; shift = 20; } + + show_special(0, fmtmk(MEMORY_twolines + , which, mkM(total), mkM(used), mkM(free), mkM(buffers) + , which, mkS(total), mkS(used), mkS(free), mkM(cached))); Msg_row += 2; + #undef mkM + #undef mkS } - SETw(Curwin, NEWFRAM_cwo); return p_table; -} + #undef isROOM + #undef anyFLG +} // end: summary_show -#define PAGES_TO_KB(n) (unsigned long)( (n) << page_to_kb_shift ) + /* + * Display information for a single task row. */ +static void task_show (const WIN_t *q, const proc_t *p) { + #define makeCOL(va...) snprintf(cbuf, sizeof(cbuf), f, ## va) + #define makeVAR(v) { f = VARCOL_fmts; makeCOL(q->varcolsz, q->varcolsz, v); } + #define pages2K(n) (unsigned long)( (n) << Pg2K_shft ) + char rbuf[ROWMINSIZ], *rp; + int j, x; -// the following macro is our means to 'inline' emitting a column -- next to -// procs_refresh, that's the most frequent and costly part of top's job ! -#define MKCOL(va...) do { \ - if(likely(!( CHKw(q, Show_HICOLS) && q->rc.sortindx==i ))) { \ - snprintf(cbuf, sizeof(cbuf), f, ## va); \ - } else { \ - snprintf(_z, sizeof(_z), f, ## va); \ - snprintf(cbuf, sizeof(cbuf), "%s%s%s", \ - q->capclr_rowhigh, \ - _z+advance, \ - !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \ - ); \ - advance=0; \ - pad += q->len_rowhigh; \ - if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \ - } \ -} while (0) - -// Display information for a single task row. -static void task_show (const WIN_t *q, const proc_t *p) -{ - char rbuf[ROWBUFSIZ]; - char *rp = rbuf; - int j, x, pad; - - *rp = '\0'; - - pad = Rc.mode_altscr; -// if (pad) rp = scat(rp, " "); + // we must begin a row with a possible window number in mind... + *(rp = rbuf) = '\0'; + if (Rc.mode_altscr) rp = scat(rp, " "); for (x = 0; x < q->maxpflgs; x++) { - char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ]; - FLG_t i = q->procflags[x]; // support for our field/column + char cbuf[SCREENMAX]; + FLG_t i = q->procflgs[x]; // support for our field/column const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt - unsigned s = Fieldstab[i].scale; // string must be altered ! - unsigned w = Fieldstab[i].width; - - int advance = (x==0) && !Rc.mode_altscr; + int s = Fieldstab[i].scale; // string must be altered ! + int w = Fieldstab[i].width; switch (i) { +#ifndef USE_X_COLHDR + // these 2 aren't real procflgs, they're used in column highlighting! + case X_XON: + case X_XOF: + /* treat running tasks specially - entire row may get highlighted + so we needn't turn it on and we MUST NOT turn it off */ + if (!('R' == p->state && CHKw(q, Show_HIROWS))) + rp = scat(rp, X_XON == i ? q->capclr_rowhigh : q->capclr_rownorm); + continue; +#endif + case P_CGR: + // our kernel may not support cgroups + makeVAR(p->cgroup ? *p->cgroup : "n/a"); + break; case P_CMD: - { char tmp[ROWBUFSIZ]; - unsigned flags; - int maxcmd = q->maxcmdln; - if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; - else flags = ESC_DEFUNCT; - escape_command(tmp, p, sizeof tmp, &maxcmd, flags); - MKCOL(q->maxcmdln, q->maxcmdln, tmp); - } + if (CHKw(q, Show_CMDLIN)) makeVAR(*p->cmdline) + else makeVAR(p->cmd); break; case P_COD: - MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s)); + makeCOL(scale_num(pages2K(p->trs), w, s)); break; case P_CPN: - MKCOL((unsigned)p->processor); + makeCOL(p->processor); break; case P_CPU: - { float u = (float)p->pcpu * Frame_tscale; - if (u > pcpu_max_value) u = pcpu_max_value; - MKCOL(u); + { float u = (float)p->pcpu * Frame_etscale; + if (u > Cpu_pmax) u = Cpu_pmax; + makeCOL(u); } break; case P_DAT: - MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s)); + makeCOL(scale_num(pages2K(p->drs), w, s)); break; case P_DRT: - MKCOL(scale_num((unsigned)p->dt, w, s)); + makeCOL(scale_num((unsigned long)p->dt, w, s)); break; case P_FLG: - { char tmp[TNYBUFSIZ]; + { char tmp[SMLBUFSIZ]; snprintf(tmp, sizeof(tmp), f, (long)p->flags); for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.'; f = tmp; - MKCOL(""); + makeCOL(""); } break; - case P_FLT: - MKCOL(scale_num(p->maj_flt, w, s)); + case P_FL1: + makeCOL(scale_num(p->maj_flt, w, s)); + break; + case P_FL2: + makeCOL(scale_num(p->min_flt, w, s)); + break; + case P_GID: + makeCOL(p->egid); break; case P_GRP: - MKCOL(p->egroup); + makeCOL(p->egroup); break; case P_MEM: - MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total); + makeCOL((float)pages2K(p->resident) * 100 / kb_main_total); break; case P_NCE: - MKCOL((int)p->nice); + makeCOL((int)p->nice); + break; +#ifdef OOMEM_ENABLE + case P_OOA: + makeCOL((int)p->oom_adj); + break; + case P_OOM: + makeCOL((long)p->oom_score); + break; +#endif + case P_PGD: + makeCOL(p->pgrp); break; case P_PID: - MKCOL((unsigned)p->XXXID); + makeCOL(p->tid); break; case P_PPD: - MKCOL((unsigned)p->ppid); + makeCOL(p->ppid); break; case P_PRI: - if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) { - f = " RT"; - MKCOL(""); + if (-99 > p->priority || 999 < p->priority) { + f = " rt "; + makeCOL(""); } else - MKCOL((int)p->priority); + makeCOL((int)p->priority); break; case P_RES: - MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s)); + makeCOL(scale_num(pages2K(p->resident), w, s)); break; case P_SHR: - MKCOL(scale_num(PAGES_TO_KB(p->share), w, s)); + makeCOL(scale_num(pages2K(p->share), w, s)); + break; + case P_SID: + makeCOL(p->session); break; case P_STA: - MKCOL(p->state); + makeCOL(p->state); break; case P_SWP: - MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s)); + makeCOL(scale_num(pages2K(p->size - p->resident), w, s)); + break; + case P_THD: + makeCOL(p->nlwp); break; case P_TME: case P_TM2: { TIC_t t = p->utime + p->stime; - if (CHKw(q, Show_CTIMES)) - t += (p->cutime + p->cstime); - MKCOL(scale_tics(t, w)); + if (CHKw(q, Show_CTIMES)) t += (p->cutime + p->cstime); + makeCOL(scale_tics(t, w)); } break; + case P_TPG: + makeCOL(p->tpgid); + break; case P_TTY: - { char tmp[TNYBUFSIZ]; - dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV); - MKCOL(tmp); + { char tmp[SMLBUFSIZ]; + dev_to_tty(tmp, w, p->tty, p->tid, ABBREV_DEV); + makeCOL(tmp); } break; - case P_UID: - MKCOL((unsigned)p->euid); + case P_UED: + makeCOL(p->euid); break; - case P_URE: - MKCOL(p->euser); + case P_UEN: + makeCOL(p->euser); break; - case P_URR: - MKCOL(p->ruser); + case P_URD: + makeCOL(p->ruid); + break; + case P_URN: + makeCOL(p->ruser); + break; + case P_USD: + makeCOL(p->suid); + break; + case P_USN: + makeCOL(p->suser); break; case P_VRT: - MKCOL(scale_num(PAGES_TO_KB(p->size), w, s)); + makeCOL(scale_num(pages2K(p->size), w, s)); break; case P_WCH: if (No_ksyms) { - f = " %08lx "; - MKCOL((long)p->wchan); - } else { - MKCOL(lookup_wchan(p->wchan, p->XXXID)); - } +#ifdef CASEUP_HEXES + makeVAR(fmtmk("%08" KLF "X", p->wchan)) +#else + makeVAR(fmtmk("%08" KLF "x", p->wchan)) +#endif + } else + makeVAR(lookup_wchan(p->wchan, p->tid)) + break; + default: // keep gcc happy break; - } /* end: switch 'procflag' */ + } // end: switch 'procflag' - rp = scat(rp, cbuf+advance); - } /* end: for 'maxpflgs' */ + rp = scat(rp, cbuf); + } // end: for 'maxpflgs' - PUFF( - "\n%s%.*s%s%s", - (CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm, - Screen_cols + pad, - rbuf, - Caps_off, - "" /*Cap_clr_eol*/ - ); - -#undef MKCOL -} + PUFF("\n%s%s%s", (CHKw(q, Show_HIROWS) && 'R' == p->state) + ? q->capclr_rowhigh : q->capclr_rownorm + , rbuf + , Caps_endline); + #undef makeCOL + #undef makeVAR + #undef pages2K +} // end: task_show -// Squeeze as many tasks as we can into a single window, -// after sorting the passed proc table. -static void window_show (proc_t **ppt, WIN_t *q, int *lscr) -{ -#ifdef SORT_SUPRESS - // the 1 flag that DOES and 2 flags that MAY impact our proc table qsort -#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES ) - static FLG_t sav_indx = 0; - static int sav_flgs = -1; -#endif + /* + * Squeeze as many tasks as we can into a single window, + * after sorting the passed proc table. */ +static int window_show (proc_t **ppt, WIN_t *q, int wmax) { + /* the isBUSY macro determines if a task is 'active' -- + it returns true if some cpu was used since the last sample. + ( actual 'running' tasks will be a subset of those selected ) */ + #define isBUSY(x) (0 < x->pcpu) + #define winMIN(a,b) ((a < b) ? a : b) int i, lwin; // Display Column Headings -- and distract 'em while we sort (maybe) - PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol); + PUFF("\n%s%s%s", q->capclr_hdr, q->columnhdr, Caps_endline); -#ifdef SORT_SUPRESS - if (CHKw(Curwin, NEWFRAM_cwo) - || sav_indx != q->rc.sortindx - || sav_flgs != (q->rc.winflags & srtMASK)) { - sav_indx = q->rc.sortindx; - sav_flgs = (q->rc.winflags & srtMASK); -#endif - if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed! - else Frame_srtflg = -1; - Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe - Frame_cmdlin = CHKw(q, Show_CMDLIN); - qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort); -#ifdef SORT_SUPRESS - } -#endif - // account for column headings - (*lscr)++; - lwin = 1; - i = 0; + if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed! + else Frame_srtflg = -1; + Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe + Frame_cmdlin = CHKw(q, Show_CMDLIN); + qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort); - while ( ppt[i]->tid != -1 && *lscr < Max_lines && (!q->winlines || (lwin <= q->winlines)) ) { - if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state)) - && good_uid(ppt[i]) ) { - // Display a process Row - task_show(q, ppt[i]); - (*lscr)++; + i = q->begtask; + if (i >= Frame_maxtask) i = q->begtask = Frame_maxtask - 1; + lwin = 1; // 1 for the ol' column header + wmax = winMIN(wmax, q->winlines+1); // ditto for winlines, too + + /* the least likely scenario is also the most costly, so we'll try to avoid + checking some stuff with each iteration and check it just once... */ + if (CHKw(q, Show_IDLEPS) && !q->usrseltyp) + while (-1 != ppt[i]->tid && lwin < wmax) { + task_show(q, ppt[i++]); ++lwin; } - ++i; - } - // for this frame that window's toast, cleanup for next time - q->winlines = 0; - OFFw(Curwin, FLGSOFF_cwo); - -#ifdef SORT_SUPRESS -#undef srtMASK -#endif -} - + else + while (-1 != ppt[i]->tid && lwin < wmax) { + if ((CHKw(q, Show_IDLEPS) || isBUSY(ppt[i])) + && user_matched(q, ppt[i])) { + task_show(q, ppt[i]); + ++lwin; + } + ++i; + } + return lwin; + #undef winMIN + #undef isBUSY +} // end: window_show + /*###### Entry point plus two ##########################################*/ -// This guy's just a *Helper* function who apportions the -// remaining amount of screen real estate under multiple windows -static void framehlp (int wix, int max) -{ + /* + * This guy's just a *Helper* function who apportions the + * remaining amount of screen real estate under multiple windows */ +static void framehlp (int wix, int max) { int i, rsvd, size, wins; // calc remaining number of visible windows + total 'user' lines for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) { - if (CHKw(&Winstk[i], VISIBLE_tsk)) { + if (CHKw(&Winstk[i], Show_TASKON)) { rsvd += Winstk[i].rc.maxtasks; ++wins; if (max <= rsvd) break; @@ -3272,146 +3391,135 @@ static void framehlp (int wix, int max) if (0 <= size) size = max; size = (max - wins) / wins; - // for remaining windows, set WIN_t winlines to either the user's - // maxtask (1st choice) or our 'foxized' size calculation - // (foxized adj. - 'fair and balanced') - for (i = wix ; i < GROUPSMAX; i++) { - if (CHKw(&Winstk[i], VISIBLE_tsk)) { - Winstk[i].winlines = - Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size; - } - } -} + /* for subject window, set WIN_t winlines to either the user's + maxtask (1st choice) or our 'foxized' size calculation + (foxized adj. - 'fair and balanced') */ + Winstk[wix].winlines = + Winstk[wix].rc.maxtasks ? Winstk[wix].rc.maxtasks : size; +} // end: framehlp -// Initiate the Frame Display Update cycle at someone's whim! -// This routine doesn't do much, mostly he just calls others. -// -// (Whoa, wait a minute, we DO caretake those row guys, plus) -// (we CALCULATE that IMPORTANT Max_lines thingy so that the) -// (*subordinate* functions invoked know WHEN the user's had) -// (ENOUGH already. And at Frame End, it SHOULD be apparent) -// (WE am d'MAN -- clearing UNUSED screen LINES and ensuring) -// (the CURSOR is STUCK in just the RIGHT place, know what I) -// (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say) -// (THAT about THIS function again, Ok? Good that's better.) -static void frame_make (void) -{ + /* + * Initiate the Frame Display Update cycle at someone's whim! + * This routine doesn't do much, mostly he just calls others. + * + * (Whoa, wait a minute, we DO caretake those row guys, plus) + * (we CALCULATE that IMPORTANT Max_lines thingy so that the) + * (*subordinate* functions invoked know WHEN the user's had) + * (ENOUGH already. And at Frame End, it SHOULD be apparent) + * (WE am d'MAN -- clearing UNUSED screen LINES and ensuring) + * (the CURSOR is STUCK in just the RIGHT place, know what I) + * (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say) + * (THAT about THIS function again, Ok? Good that's better.) + * + * (ps. we ARE the UNEQUALED justification KING of COMMENTS!) + * (No, I don't mean significance/relevance, only alignment.) + */ +static void frame_make (void) { + WIN_t *w = Curwin; // avoid gcc bloat with a local copy proc_t **ppt; int i, scrlins; - // note: all libproc flags are managed by - // reframewins(), who also builds each window's column headers - if (!Frames_libflags) { - reframewins(); - memset(Pseudo_scrn, '\0', Pseudo_size); - } + // deal with potential signals since the last time around... + if (Frames_endpgm) bye_bye(NULL); + if (Frames_paused) pause_pgm(); + if (Frames_resize) calibrate_fields(); + + putp(Cap_curs_hide); Pseudo_row = Msg_row = scrlins = 0; ppt = summary_show(); Max_lines = (Screen_rows - Msg_row) - 1; - if (CHKw(Curwin, EQUWINS_cwo)) - wins_reflag(Flags_OFF, EQUWINS_cwo); - - // sure hope each window's columns header begins with a newline... - putp(tg2(0, Msg_row)); - if (!Rc.mode_altscr) { // only 1 window to show so, piece o' cake - Curwin->winlines = Curwin->rc.maxtasks; - window_show(ppt, Curwin, &scrlins); + w->winlines = w->rc.maxtasks ? w->rc.maxtasks : Max_lines; + scrlins = window_show(ppt, w, Max_lines); } else { // maybe NO window is visible but assume, pieces o' cakes for (i = 0 ; i < GROUPSMAX; i++) { - if (CHKw(&Winstk[i], VISIBLE_tsk)) { + if (CHKw(&Winstk[i], Show_TASKON)) { framehlp(i, Max_lines - scrlins); - window_show(ppt, &Winstk[i], &scrlins); + scrlins += window_show(ppt, &Winstk[i], Max_lines - scrlins); } if (Max_lines <= scrlins) break; } } - // clear to end-of-screen (critical if last window is 'idleps off'), - // then put the cursor in-its-place, and rid us of any prior frame's msg - // (main loop must iterate such that we're always called before sleep) - PUTT( - "%s%s%s%s", - scrlins < Max_lines ? "\n" : "", - scrlins < Max_lines ? Cap_clr_eos : "", - tg2(0, Msg_row), - Cap_clr_eol - ); + + /* clear to end-of-screen (critical if last window is 'idleps off'), + then put the cursor in-its-place, and rid us of any prior frame's msg + (main loop must iterate such that we're always called before sleep) */ + if (scrlins < Max_lines) { + putp(Cap_nl_clreos); + PSU_CLREOS(Pseudo_row); + } + if (VIZISw(w) && CHKw(w, View_SCROLL)) show_scroll(); + else PUTT("%s%s", tg2(0, Msg_row), Cap_clr_eol); + putp(Cap_curs_norm); fflush(stdout); -} + + /* we'll deem any terminal not supporting tgoto as dumb and disable + the normal non-interactive output optimization... */ + if (!Cap_can_goto) PSU_CLREOS(0); +} // end: frame_make -int main (int dont_care_argc, char *argv[]) -{ + /* + * duh... */ +int main (int dont_care_argc, char **argv) { + struct sigaction sa; (void)dont_care_argc; before(*argv); // +-------------+ - windows_stage1(); // top (sic) slice + wins_stage_1(); // top (sic) slice configs_read(); // > spread etc, < parse_args(&argv[1]); // > lean stuff, < whack_terminal(); // > onions etc. < - windows_stage2(); // as bottom slice + wins_stage_2(); // as bottom slice // +-------------+ - signal(SIGALRM, end_pgm); - signal(SIGHUP, end_pgm); - signal(SIGINT, end_pgm); - signal(SIGPIPE, end_pgm); - signal(SIGQUIT, end_pgm); - signal(SIGTERM, end_pgm); - signal(SIGTSTP, suspend); - signal(SIGTTIN, suspend); - signal(SIGTTOU, suspend); - signal(SIGCONT, wins_resize_sighandler); - signal(SIGWINCH, wins_resize_sighandler); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = sig_endpgm; + sigaction(SIGALRM, &sa, NULL); + if (!Batch) sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = sig_paused; + sigaction(SIGTSTP, &sa, NULL); + sigaction(SIGTTIN, &sa, NULL); + sigaction(SIGTTOU, &sa, NULL); + sa.sa_handler = sig_resize; + sigaction(SIGCONT, &sa, NULL); + sigaction(SIGWINCH, &sa, NULL); for (;;) { - if (need_resize){ - need_resize = 0; - wins_resize(); - } + struct timeval tv; + frame_make(); if (Msg_awaiting) show_msg(Msg_delayed); - if (Loops > 0) --Loops; - if (!Loops) end_pgm(0); + if (0 < Loops) --Loops; + if (!Loops) bye_bye(NULL); tv.tv_sec = Rc.delay_time; tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000; - if (Batch) { - select(0, NULL, NULL, NULL, &tv); // ought to loop until done - } else { - long file_flags; - int rc; - char c; + if (Batch) + select(0, NULL, NULL, NULL, &tv); + else { fd_set fs; + FD_ZERO(&fs); FD_SET(STDIN_FILENO, &fs); - file_flags = fcntl(STDIN_FILENO, F_GETFL); - if(file_flags==-1) file_flags=0; - fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags); - - // check 1st, in case tv zeroed (by sig handler) before it got set - rc = chin(0, &c, 1); - if (rc <= 0) { - // EOF is pretty much a "can't happen" except for a kernel bug. - // We should quickly die via SIGHUP, and thus not spin here. - // if (rc == 0) end_pgm(0); /* EOF from terminal */ - fcntl(STDIN_FILENO, F_SETFL, file_flags); - select(1, &fs, NULL, NULL, &tv); - fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags); - } - if (chin(0, &c, 1) > 0) { - fcntl(STDIN_FILENO, F_SETFL, file_flags); - do_key((unsigned)c); - } else { - fcntl(STDIN_FILENO, F_SETFL, file_flags); - } + if (0 < select(STDIN_FILENO + 1, &fs, NULL, NULL, &tv)) + do_key(keyin(0)); + /* note: above select might have been interrupted by some signal + in which case the return code would have been -1 and an + integer (volatile) switch set. that in turn will cause + frame_make() to deal with it as its first responsiblity + */ } } - return 0; -} +} // end: main diff --git a/top.h b/top.h index b4ac0794..804eec40 100644 --- a/top.h +++ b/top.h @@ -1,222 +1,266 @@ -// top.h - Header file: show Linux processes -// -// Copyright (c) 2002, by: James C. Warner -// All rights reserved. 8921 Hilloway Road -// Eden Prairie, Minnesota 55347 USA -// -// -// This file may be used subject to the terms and conditions of the -// GNU Library General Public License Version 2, or any later version -// at your option, as published by the Free Software Foundation. -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Library General Public License for more details. -// -// For their contributions to this program, the author wishes to thank: -// Albert D. Cahalan, -// Craig Small, -// -// Changes by Albert Cahalan, 2002, 2004. - +/* top.h - Header file: show Linux processes */ +/* + * Copyright (c) 2002-2011, by: James C. Warner + * All rights reserved. 8921 Hilloway Road + * Eden Prairie, Minnesota 55347 USA + * + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +/* For contributions to this program, the author wishes to thank: + * Craig Small, + * Albert D. Cahalan, + */ #ifndef _Itop #define _Itop -// Defines intended to be experimented with ------------------------ -//#define CASEUP_HEXES // show any hex values in upper case -//#define CASEUP_SCALE // show scaled time/num suffix upper case -//#define CASEUP_SUMMK // show memory summary kilobytes with 'K' -//#define SORT_SUPRESS // *attempt* to reduce qsort overhead -//#define WARN_NOT_SMP // restrict '1' & 'I' commands to true smp + /* Development/Debugging defines ----------------------------------- */ +//#define ATEOJ_RPTHSH /* report on hash specifics, at end-of-job */ +//#define ATEOJ_RPTSTD /* report on misc stuff, at end-of-job */ +//#define CASEUP_HEXES /* show any hex values in upper case */ +//#define CASEUP_SUFIX /* show time/mem/cnts suffix in upper case */ +//#define EQUCOLHDRYES /* yes, do equalize column header lengths */ +//#define FIELD_CURSOR /* cursor follows selection w/ fields mgmt */ +//#define OFF_HST_HASH /* use BOTH qsort+bsrch vs. hashing scheme */ +//#define OFF_STDIOLBF /* disable our own stdout _IOFBF override */ +//#define OOMEM_ENABLE /* enable the SuSE out-of-memory additions * +//#define PRETEND2_5_X /* pretend we're linux 2.5.x (for IO-wait) */ +//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */ +//#define PRETENDNOCAP /* use a terminal without essential caps */ +//#define RCFILE_NOERR /* rcfile errs silently default, vs. fatal */ +//#define RESIZE_LIMIT /* enable limits on lower sigwinch bounds */ +//#define RMAN_IGNORED /* don't consider auto right margin glitch */ +//#define STRCMPNOCASE /* use strcasecmp vs. strcmp when sorting */ +//#define TERMIOS_ONLY /* just limp along with native input only */ +//#define TTYGETENVYES /* environ vars can override tty col/row */ +//#define USE_X_COLHDR /* emphasize header vs. whole col, for 'x' */ + + +/*###### Notes, etc. ###################################################*/ + + /* The following convention is used to identify those areas where + adaptations for hotplugging are to be found ... + *** hotplug_acclimated *** + ( hopefully libproc will also be supportive of our efforts ) */ + + /* And there are still some of these lurking here and there... + FIXME - blah, blah... */ + + /* For introducing inaugural cgroup support, thanks to: + Jan Gorig - April, 2011 */ -// Development/Debugging defines ----------------------------------- -//#define ATEOJ_REPORT // report a bunch of stuff, at end-of-job -//#define PRETEND2_5_X // pretend we're linux 2.5.x (for IO-wait) -//#define PRETENDNOCAP // use a terminal without essential caps -//#define STDOUT_IOLBF // disable our own stdout _IOFBF override #ifdef PRETEND2_5_X #define linux_version_code LINUX_VERSION(2,5,43) #endif +#ifdef STRCMPNOCASE +#define STRSORTCMP strcasecmp +#else +#define STRSORTCMP strcmp +#endif + +#ifdef OOMEM_ENABLE + /* FIXME: perhaps making this a function in the suse version of + sysinfo.c was a prelude to hotpluggable updates -- unfortunately, + the return value is invariant as currently implemented! */ +#define SMP_NUM_CPUS smp_num_cpus() +#else +#define SMP_NUM_CPUS smp_num_cpus +#endif + + /*###### Some Miscellaneous constants ##################################*/ -// The default delay twix updates + /* The default delay twix updates */ #define DEF_DELAY 3.0 -// The length of time a 'message' is displayed -#define MSG_SLEEP 2 + /* Length of time a 'message' is displayed (in microseconds) */ +#define MSG_USLEEP (useconds_t)1250000 -// The default value for the 'k' (kill) request -#define DEF_SIGNAL SIGTERM - -// Specific process id monitoring support (command line only) + /* Specific process id monitoring support (command line only) */ #define MONPIDMAX 20 -// Power-of-two sizes lead to trouble; the largest power of -// two factor should be the cache line size. It'll mean the -// array indexing math gets slower, but cache aliasing is -// avoided. -#define CACHE_TWEAK_FACTOR 64 - -// Miscellaneous buffer sizes with liberal values -- mostly -// just to pinpoint source code usage/dependancies -#define SCREENMAX ( 512 + CACHE_TWEAK_FACTOR) -// the above might seem pretty stingy, until you consider that with every -// one of top's fields displayed we're talking a 160 byte column header -- -// so that will provide for all fields plus a 350+ byte command line -#define WINNAMSIZ 4 -#define CAPTABMAX 9 -#define PFLAGSSIZ 32 + /* Miscellaneous buffers with liberal values and some other defines + -- mostly just to pinpoint source code usage/dependancies */ +#ifdef RESIZE_LIMIT +#define SCRCOLMIN 16 +#define SCRROWMIN 8 +#endif +#define SCREENMAX 512 + /* the above might seem pretty stingy, until you consider that with every + one of top's fields displayed it's less than 200 bytes of column header + -- so SCREENMAX provides for all fields plus a 300+ byte command line */ #define CAPBUFSIZ 32 #define CLRBUFSIZ 64 -#define GETBUFSIZ 32 -#define TNYBUFSIZ 32 -#define SMLBUFSIZ ( 256 + CACHE_TWEAK_FACTOR) -#define OURPATHSZ (1024 + CACHE_TWEAK_FACTOR) -#define MEDBUFSIZ (1024 + CACHE_TWEAK_FACTOR) -#define BIGBUFSIZ (2048 + CACHE_TWEAK_FACTOR) -#define USRNAMSIZ GETBUFSIZ -#define ROWBUFSIZ SCREENMAX + CLRBUFSIZ +#define PFLAGSSIZ 64 +#define SMLBUFSIZ 128 +#define MEDBUFSIZ 256 +#define LRGBUFSIZ 512 +#define OURPATHSZ 1024 +#define BIGBUFSIZ 2048 + /* in addition to the actual display data, our row might have to accomodate + many termcap/color transitions - these definitions ensure we have room */ +#define ROWMINSIZ ( SCREENMAX + 4 * (CAPBUFSIZ + CLRBUFSIZ) ) +#define ROWMAXSIZ ( SCREENMAX + 16 * (CAPBUFSIZ + CLRBUFSIZ) ) + + // support for keyboard stuff (cursor motion keystrokes, mostly) +#define kbd_ENTER '\n' +#define kbd_ESC '\033' +#define kbd_SPACE ' ' +#define kbd_UP '\x81' +#define kbd_DOWN '\x82' +#define kbd_RIGHT '\x83' +#define kbd_LEFT '\x84' +#define kbd_PGUP '\x85' +#define kbd_PGDN '\x86' +#define kbd_END '\x87' +#define kbd_HOME '\x88' +#define kbd_BKSP '\x89' +#define kbd_INS '\x8a' +#define kbd_DEL '\x8b' -/*###### Some Miscellaneous Macro definitions ##########################*/ +/* ##### Enum's and Typedef's ############################################ */ -// Yield table size as 'int' -#define MAXTBL(t) (int)(sizeof(t) / sizeof(t[0])) + /* Flags for each possible field (and then some) -- + these MUST be kept in sync with the FLD_t Fieldstab[] array !! */ +enum pflag { + P_PID = 0, P_PPD, + P_UED, P_UEN, P_URD, P_URN, P_USD, P_USN, + P_GID, P_GRP, P_PGD, P_TTY, P_TPG, P_SID, + P_PRI, P_NCE, P_THD, + P_CPN, P_CPU, P_TME, P_TM2, + P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR, + P_FL1, P_FL2, P_DRT, + P_STA, P_CMD, P_WCH, P_FLG, P_CGR, +#ifdef OOMEM_ENABLE + P_OOA, P_OOM, +#endif +#ifdef USE_X_COLHDR + // not really pflags, used with tbl indexing + P_MAXPFLGS +#else + // not really pflags, used with tbl indexing & col highlighting + P_MAXPFLGS, X_XON, X_XOF +#endif +}; -// Used as return arguments in *some* of the sort callbacks -#define SORT_lt ( Frame_srtflg > 0 ? 1 : -1 ) -#define SORT_gt ( Frame_srtflg > 0 ? -1 : 1 ) -#define SORT_eq 0 + /* The scaling 'type' used with scale_num() -- this is how + the passed number is interpreted should scaling be necessary */ +enum scale_num { + SK_no, SK_Kb, SK_Mb, SK_Gb, SK_Tb +}; -// Used to create *most* of the sort callback functions -#define SCB_NUM1(f,n) \ - static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ - if ( (*P)->n < (*Q)->n ) return SORT_lt; \ - if (likely( (*P)->n > (*Q)->n )) return SORT_gt; \ - return SORT_eq; } -#define SCB_NUM2(f,n1,n2) \ - static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ - if ( ((*P)->n1 - (*P)->n2) < ((*Q)->n1 - (*Q)->n2) ) return SORT_lt; \ - if (likely( ((*P)->n1 - (*P)->n2) > ((*Q)->n1 - (*Q)->n2) )) return SORT_gt; \ - return SORT_eq; } -#define SCB_NUMx(f,n) \ - static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ - return Frame_srtflg * ( (*Q)->n - (*P)->n ); } -#define SCB_STRx(f,s) \ - static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ - return Frame_srtflg * strcmp((*Q)->s, (*P)->s); } - -// The following two macros are used to 'inline' those portions of the -// display process requiring formatting, while protecting against any -// potential embedded 'millesecond delay' escape sequences. - -// PUTT - Put to Tty (used in many places) -// - for temporary, possibly interactive, 'replacement' output -// - may contain ANY valid terminfo escape sequences -// - need NOT represent an entire screen row -#define PUTT(fmt,arg...) do { \ - char _str[ROWBUFSIZ]; \ - snprintf(_str, sizeof(_str), fmt, ## arg); \ - putp(_str); \ - } while (0) - -// PUFF - Put for Frame (used in only 3 places) -// - for more permanent frame-oriented 'update' output -// - may NOT contain cursor motion terminfo escapes -// - assumed to represent a complete screen ROW -// - subject to optimization, thus MAY be discarded - -// The evil version (53892 byte stripped top, oddly enough) -#define _PUFF(fmt,arg...) \ -do { \ - char _str[ROWBUFSIZ]; \ - int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ - putp ( Batch ? _str : \ - ({ \ - char *restrict const _pse = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ - memcmp(_pse, _str, _len) ? memcpy(_pse, _str, _len) : "\n"; \ - }) \ - ); \ -} while (0) - -// The good version (53876 byte stripped top) -#define PUFF(fmt,arg...) \ -do { \ - char _str[ROWBUFSIZ]; \ - char *_ptr; \ - int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ - if (Batch) _ptr = _str; \ - else { \ - _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ - if (memcmp(_ptr, _str, _len)) { \ - memcpy(_ptr, _str, _len); \ - } else { \ - _ptr = "\n"; \ - } \ - } \ - putp(_ptr); \ -} while (0) - - -/*------ Special Macros (debug and/or informative) ---------------------*/ - -// Orderly end, with any sort of message - see fmtmk -#define debug_END(s) { \ - static void std_err (const char *); \ - fputs(Cap_clr_scr, stdout); \ - std_err(s); \ - } - -// A poor man's breakpoint, if he's too lazy to learn gdb -#define its_YOUR_fault { *((char *)0) = '!'; } - - -/*###### Some Typedef's and Enum's #####################################*/ - -// This typedef just ensures consistent 'process flags' handling + /* This typedef just ensures consistent 'process flags' handling */ typedef unsigned FLG_t; -// These typedefs attempt to ensure consistent 'ticks' handling + /* These typedefs attempt to ensure consistent 'ticks' handling */ typedef unsigned long long TIC_t; typedef long long SIC_t; -// Sort support, callback funtion signature + /* Sort support, callback function signature */ typedef int (*QFP_t)(const void *, const void *); -// This structure consolidates the information that's used -// in a variety of display roles. + /* This structure consolidates the information that's used + in a variety of display roles. */ typedef struct FLD_t { - const char keys [4]; // order: New-on New-off Old-on Old-off - // misaligned on 64-bit, but part of a table -- oh well const char *head; // name for col heads + toggle/reorder fields - const char *fmts; // sprintf format string for field display + const char *fmts; // snprintf format string for field display const int width; // field width, if applicable const int scale; // scale_num type, if applicable const QFP_t sort; // sort function - const char *desc; // description for toggle/reorder fields const int lflg; // PROC_FILLxxx flag(s) needed by this field + const char *desc; // description for fields management } FLD_t; -// This structure stores one piece of critical 'history' -// information from one frame to the next -- we don't calc -// and save data that goes unused +#ifdef OFF_HST_HASH + /* This structure supports 'history' processing and ultimately records + one piece of critical information from one frame to the next -- + we don't calc and save data that goes unused like the old top. */ typedef struct HST_t { - TIC_t tics; - int pid; + TIC_t tics; // last frame's tics count + int pid; // record 'key' } HST_t; +#else + /* This structure supports 'history' processing and ultimately records + one piece of critical information from one frame to the next -- + we don't calc and save data that goes unused like the old top nor + do we incure the overhead of sorting to support a binary search + (or worse, a friggin' for loop) when retrieval is necessary! */ +typedef struct HST_t { + TIC_t tics; // last frame's tics count + int pid; // record 'key' + int lnk; // next on hash chain +} HST_t; +#endif -// This structure stores a frame's cpu tics used in history -// calculations. It exists primarily for SMP support but serves -// all environments. + /* This structure stores a frame's cpu tics used in history + calculations. It exists primarily for SMP support but serves + all environments. */ typedef struct CPU_t { + /* other kernels: u == user/us, n == nice/ni, s == system/sy, i == idle/id + 2.5.41 kernel: w == IO-wait/wa (io wait time) + 2.6.0 kernel: x == hi (hardware irq time), y == si (software irq time) + 2.6.11 kernel: z == st (virtual steal time) */ TIC_t u, n, s, i, w, x, y, z; // as represented in /proc/stat TIC_t u_sav, s_sav, n_sav, i_sav, w_sav, x_sav, y_sav, z_sav; // in the order of our display unsigned id; // the CPU ID number } CPU_t; -// These 2 types support rcfile compatibility + + /* /////////////////////////////////////////////////////////////// */ + /* Special Section: multiple windows/field groups --------------- */ + /* ( kind of a header within a header: constants, types & macros ) */ + +#define CAPTABMAX 9 /* max entries in each win's caps table */ +#define GROUPSMAX 4 /* the max number of simultaneous windows */ +#define WINNAMSIZ 4 /* size of RCW_t winname buf (incl '\0') */ +#define GRPNAMSIZ WINNAMSIZ+2 /* window's name + number as in: '#:...' */ + + /* The Persistent 'Mode' flags! + These are preserved in the rc file, as a single integer and the + letter shown is the corresponding 'command' toggle */ + // 'View_' flags affect the summary (minimum), taken from 'Curwin' +#define View_CPUSUM 0x008000 // '1' - show combined cpu stats (vs. each) +#define View_LOADAV 0x004000 // 'l' - display load avg and uptime summary +#define View_STATES 0x002000 // 't' - display task/cpu(s) states summary +#define View_MEMORY 0x001000 // 'm' - display memory summary +#define View_NOBOLD 0x000008 // 'B' - disable 'bold' attribute globally +#define View_SCROLL 0x080000 // 'C' - enable coordinates msg w/ scrolling + // 'Show_' & 'Qsrt_' flags are for task display in a visible window +#define Show_COLORS 0x000800 // 'z' - show in color (vs. mono) +#define Show_HIBOLD 0x000400 // 'b' - rows and/or cols bold (vs. reverse) +#define Show_HICOLS 0x000200 // 'x' - show sort column emphasized +#define Show_HIROWS 0x000100 // 'y' - show running tasks highlighted +#define Show_CMDLIN 0x000080 // 'c' - show cmdline vs. name +#define Show_CTIMES 0x000040 // 'S' - show times as cumulative +#define Show_IDLEPS 0x000020 // 'i' - show idle processes (all tasks) +#define Show_TASKON 0x000010 // '-' - tasks showable when Mode_altscr +#define Qsrt_NORMAL 0x000004 // 'R' - reversed column sort (high to low) + // these flag(s) have no command as such - they're for internal use +#define EQUWINS_xxx 0x000001 // rebalance all wins & tasks (off 'i'/ 'n') + + // Default flags if there's no rcfile to provide user customizations +#define DEF_WINFLGS ( View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY \ + | Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Show_TASKON | Qsrt_NORMAL ) + + /* These are used to direct wins_reflag */ +enum reflag_enum { + Flags_TOG, Flags_SET, Flags_OFF +}; + + /* These are used to direct win_warn */ +enum warn_enum { + Warn_ALT, Warn_VIZ +}; + + /* This type helps support both a window AND the rcfile */ typedef struct RCW_t { // the 'window' portion of an rcfile FLG_t sortindx; // sort field, represented as a procflag int winflags, // 'view', 'show' and 'sort' mode flags @@ -229,137 +273,182 @@ typedef struct RCW_t { // the 'window' portion of an rcfile fieldscur [PFLAGSSIZ]; // fields displayed and ordered } RCW_t; -typedef struct RCF_t { // the complete rcfile (new style) + /* This represents the complete rcfile */ +typedef struct RCF_t { int mode_altscr; // 'A' - Alt display mode (multi task windows) int mode_irixps; // 'I' - Irix vs. Solaris mode (SMP-only) - float delay_time; // 'd' or 's' - How long to sleep twixt updates + float delay_time; // 'd'/'s' - How long to sleep twixt updates int win_index; // Curwin, as index - RCW_t win [4]; // a 'WIN_t.rc' for each of the 4 windows + RCW_t win [GROUPSMAX]; // a 'WIN_t.rc' for each window } RCF_t; -// The scaling 'type' used with scale_num() -- this is how -// the passed number is interpreted should scaling be necessary -enum scale_num { - SK_no, SK_Kb, SK_Mb, SK_Gb, SK_Tb -}; - -// Flags for each possible field -enum pflag { - P_PID, P_PPD, P_URR, P_UID, P_URE, P_GRP, P_TTY, - P_PRI, P_NCE, - P_CPN, P_CPU, P_TME, P_TM2, - P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR, - P_FLT, P_DRT, - P_STA, P_CMD, P_WCH, P_FLG -}; - - -/////////////////////////////////////////////////////////////////////////// -// Special Section: multiple windows/field groups ------------- -// (kind of a header within a header: constants, macros & types) - -#define GROUPSMAX 4 // the max number of simultaneous windows -#define GRPNAMSIZ WINNAMSIZ+2 // window's name + number as in: '#:...' - -#define Flags_TOG 1 // these are used to direct wins_reflag -#define Flags_SET 2 -#define Flags_OFF 3 - -// The Persistent 'Mode' flags! -// These are preserved in the rc file, as a single integer and the -// letter shown is the corresponding 'command' toggle - -// 'View_' flags affect the summary (minimum), taken from 'Curwin' -#define View_CPUSUM 0x8000 // '1' - show combined cpu stats (vs. each) -#define View_LOADAV 0x4000 // 'l' - display load avg and uptime summary -#define View_STATES 0x2000 // 't' - display task/cpu(s) states summary -#define View_MEMORY 0x1000 // 'm' - display memory summary -#define View_NOBOLD 0x0001 // 'B' - disable 'bold' attribute globally - -// 'Show_' & 'Qsrt_' flags are for task display in a visible window -#define Show_THREADS 0x10000 // 'H' - show threads in each task -#define Show_COLORS 0x0800 // 'z' - show in color (vs. mono) -#define Show_HIBOLD 0x0400 // 'b' - rows and/or cols bold (vs. reverse) -#define Show_HICOLS 0x0200 // 'x' - show sort column highlighted -#define Show_HIROWS 0x0100 // 'y' - show running tasks highlighted -#define Show_CMDLIN 0x0080 // 'c' - show cmdline vs. name -#define Show_CTIMES 0x0040 // 'S' - show times as cumulative -#define Show_IDLEPS 0x0020 // 'i' - show idle processes (all tasks) -#define Qsrt_NORMAL 0x0010 // 'R' - reversed column sort (high to low) - -// these flag(s) have no command as such - they're for internal use -#define VISIBLE_tsk 0x0008 // tasks are showable when in 'Mode_altscr' -#define NEWFRAM_cwo 0x0004 // new frame (if anyone cares) - in Curwin -#define EQUWINS_cwo 0x0002 // rebalance tasks next frame (off 'i'/ 'n') - // ...set in Curwin, but impacts all windows - -// Current-window-only flags -- always turned off at end-of-window! -#define FLGSOFF_cwo EQUWINS_cwo | NEWFRAM_cwo - -// Default flags if there's no rcfile to provide user customizations -#define DEF_WINFLGS ( \ - View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY | View_NOBOLD | \ - Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Qsrt_NORMAL | \ - VISIBLE_tsk \ -) + /* This structure stores configurable information for each window. + By expending a little effort in its creation and user requested + maintainence, the only real additional per frame cost of having + windows is an extra sort -- but that's just on pointers! */ +typedef struct WIN_t { + FLG_t pflgsall [PFLAGSSIZ], // all 'active/on' fieldscur, as enum + procflgs [PFLAGSSIZ]; // fieldscur subset, as enum + RCW_t rc; // stuff that gets saved in the rcfile + int winnum, // a window's number (array pos + 1) + winlines, // current task window's rows (volatile) + maxpflgs, // number of displayed procflgs ("on" in fieldscur) + totpflgs, // total of displayable procflgs in pflgsall array + begpflg, // scrolled beginning pos into pflgsall array + endpflg, // scrolled ending pos into pflgsall array + begtask, // scrolled beginning pos into Frame_maxtask + varcolsz, // max length of variable width column(s) + usrseluid, // validated uid for 'u/U' user selection + usrseltyp, // the basis for matching above uid + hdrcaplen; // column header xtra caps len, if any + char capclr_sum [CLRBUFSIZ], // terminfo strings built from + capclr_msg [CLRBUFSIZ], // RCW_t colors (& rebuilt too), + capclr_pmt [CLRBUFSIZ], // but NO recurring costs ! + capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs + capclr_rowhigh [CLRBUFSIZ], // are only used when this + capclr_rownorm [CLRBUFSIZ], // window is the 'Curwin'! + cap_bold [CAPBUFSIZ], // support for View_NOBOLD toggle + grpname [GRPNAMSIZ], // window number:name, printable +#ifdef USE_X_COLHDR + columnhdr [ROWMINSIZ], // column headings for procflgs +#else + columnhdr [SCREENMAX], // column headings for procflgs +#endif + *captab [CAPTABMAX]; // captab needed by show_special() + struct WIN_t *next, // next window in window stack + *prev; // prior window in window stack +} WIN_t; // Used to test/manipulate the window flags -#define CHKw(q,f) (int)((q)->rc.winflags & (f)) -#define TOGw(q,f) (q)->rc.winflags ^= (f) -#define SETw(q,f) (q)->rc.winflags |= (f) -#define OFFw(q,f) (q)->rc.winflags &= ~(f) -#define VIZCHKc (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \ - ? 1 : win_warn() -#define VIZTOGc(f) (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \ - ? TOGw(Curwin, f) : win_warn() +#define CHKw(q,f) (int)((q)->rc.winflags & (f)) +#define TOGw(q,f) (q)->rc.winflags ^= (f) +#define SETw(q,f) (q)->rc.winflags |= (f) +#define OFFw(q,f) (q)->rc.winflags &= ~(f) +#define ALTCHKw (Rc.mode_altscr ? 1 : win_warn(Warn_ALT)) +#define VIZISw(q) (!Rc.mode_altscr || CHKw(q,Show_TASKON)) +#define VIZCHKw(q) (VIZISw(q)) ? 1 : win_warn(Warn_VIZ) +#define VIZTOGw(q,f) (VIZISw(q)) ? TOGw(q,(f)) : win_warn(Warn_VIZ) -// This structure stores configurable information for each window. -// By expending a little effort in its creation and user requested -// maintainence, the only real additional per frame cost of having -// windows is an extra sort -- but that's just on ptrs! -typedef struct WIN_t { - struct WIN_t *next, // next window in window stack - *prev; // prior window in window stack - char *captab [CAPTABMAX]; // captab needed by show_special - int winnum, // window's num (array pos + 1) - winlines; // task window's rows (volatile) - FLG_t procflags [PFLAGSSIZ]; // fieldscur subset, as enum - int maxpflgs, // number of procflags (upcase fieldscur) - maxcmdln; // max length of a process' command line - int len_rownorm, // lengths of the corresponding terminfo - len_rowhigh; // strings to avoid repeated strlen calls - RCW_t rc; // stuff that gets saved in the rcfile - char capclr_sum [CLRBUFSIZ], // terminfo strings built from - capclr_msg [CLRBUFSIZ], // above clrs (& rebuilt too), - capclr_pmt [CLRBUFSIZ], // but NO recurring costs !!! - capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs - capclr_rowhigh [CLRBUFSIZ], // are only used when this - capclr_rownorm [CLRBUFSIZ]; // window is the 'Curwin'! - char cap_bold [CAPBUFSIZ]; // support for View_NOBOLD toggle - char grpname [GRPNAMSIZ], // window number:name, printable - columnhdr [SCREENMAX], // column headings for procflags - colusrnam [USRNAMSIZ]; // if selected by the 'u' command -} WIN_t; + // Used to test/manipulte fieldscur values +#define FLDget(q,i) ((FLG_t)((q)->rc.fieldscur[i] & 0x7f) - FLD_OFFSET) +#define FLDtog(q,i) ((q)->rc.fieldscur[i] ^= 0x80) +#define FLDviz(q,i) ((q)->rc.fieldscur[i] & 0x80) + + /* Special Section: end ------------------------------------------ */ + /* /////////////////////////////////////////////////////////////// */ + + +/*###### Some Miscellaneous Macro definitions ##########################*/ + + /* Yield table size as 'int' */ +#define MAXTBL(t) (int)(sizeof(t) / sizeof(t[0])) + + /* A null-terminating strncpy, assuming strlcpy is not available. + ( and assuming callers don't need the string length returned ) */ +#define STRLCPY(dst,src) { strncpy(dst, src, sizeof(dst)); dst[sizeof(dst) - 1] = '\0'; } + + /* Used to clear all or part of our Pseudo_screen */ +#define PSU_CLREOS(y) memset(&Pseudo_screen[ROWMAXSIZ*y], '\0', Pseudo_size-(ROWMAXSIZ*y)) + + /* Used as return arguments in *some* of the sort callbacks */ +#define SORT_lt ( Frame_srtflg > 0 ? 1 : -1 ) +#define SORT_gt ( Frame_srtflg > 0 ? -1 : 1 ) +#define SORT_eq 0 + + /* Used to create *most* of the sort callback functions + note: some of the callbacks are NOT your father's callbacks, they're + highly optimized to save them ol' precious cycles! */ +#define SCB_NAME(f) sort_P_ ## f +#define SCB_NUM1(f,n) \ + static int SCB_NAME(f) (const proc_t **P, const proc_t **Q) { \ + if ( (*P)->n < (*Q)->n ) return SORT_lt; \ + if ( (*P)->n > (*Q)->n ) return SORT_gt; \ + return SORT_eq; } +#define SCB_NUM2(f,n1,n2) \ + static int SCB_NAME(f) (const proc_t **P, const proc_t **Q) { \ + if ( ((*P)->n1 - (*P)->n2) < ((*Q)->n1 - (*Q)->n2) ) return SORT_lt; \ + if ( ((*P)->n1 - (*P)->n2) > ((*Q)->n1 - (*Q)->n2) ) return SORT_gt; \ + return SORT_eq; } +#define SCB_NUMx(f,n) \ + static int SCB_NAME(f) (const proc_t **P, const proc_t **Q) { \ + return Frame_srtflg * ( (*Q)->n - (*P)->n ); } +#define SCB_STRS(f,s) \ + static int SCB_NAME(f) (const proc_t **P, const proc_t **Q) { \ + return Frame_srtflg * STRSORTCMP((*Q)->s, (*P)->s); } +#define SCB_STRV(f,b,v,s) \ + static int SCB_NAME(f) (const proc_t **P, const proc_t **Q) { \ + if (b) { \ + if (!(*P)->v || !(*Q)->v) return SORT_eq; \ + return Frame_srtflg * STRSORTCMP((*Q)->v[0], (*P)->v[0]); } \ + return Frame_srtflg * STRSORTCMP((*Q)->s, (*P)->s); } + +/* + * The following two macros are used to 'inline' those portions of the + * display process requiring formatting, while protecting against any + * potential embedded 'millesecond delay' escape sequences. + */ + /** PUTT - Put to Tty (used in many places) + . for temporary, possibly interactive, 'replacement' output + . may contain ANY valid terminfo escape sequences + . need NOT represent an entire screen row */ +#define PUTT(fmt,arg...) do { \ + char _str[ROWMAXSIZ]; \ + snprintf(_str, sizeof(_str), fmt, ## arg); \ + putp(_str); \ + } while (0) + + /** PUFF - Put for Frame (used in only 3 places) + . for more permanent frame-oriented 'update' output + . may NOT contain cursor motion terminfo escapes + . assumed to represent a complete screen ROW + . subject to optimization, thus MAY be discarded */ +#define PUFF(fmt,arg...) do { \ + char _str[ROWMAXSIZ]; \ + snprintf(_str, sizeof(_str), fmt, ## arg); \ + if (Batch) putp(_str); \ + else { \ + char *_ptr = &Pseudo_screen[Pseudo_row * ROWMAXSIZ]; \ + if (Pseudo_row + 1 < Screen_rows) ++Pseudo_row; \ + if (!strcmp(_ptr, _str)) putp("\n"); \ + else { \ + strcpy(_ptr, _str); \ + putp(_ptr); } } \ + } while (0) + + /* Orderly end, with any sort of message - see fmtmk */ +#define debug_END(s) { \ + static void error_exit (const char *); \ + fputs(Cap_clr_scr, stdout); \ + error_exit(s); \ + } + + /* A poor man's breakpoint, if he's too lazy to learn gdb */ +#define its_YOUR_fault { *((char *)0) = '!'; } /*###### Display Support *Data* ########################################*/ -// Configuration files support + /* Configuration files support */ #define SYS_RCFILESPEC "/etc/toprc" -#define RCF_EYECATCHER "RCfile for " -#define RCF_DEPRECATED "Id:a, " +#define RCF_EYECATCHER "Config File (Linux processes with windows)\n" +#define RCF_VERSION_ID 'f' -// The default fields displayed and their order, -#define DEF_FIELDS "AEHIOQTWKNMbcdfgjplrsuvyzX" -// Pre-configured field groupss -#define JOB_FIELDS "ABcefgjlrstuvyzMKNHIWOPQDX" -#define MEM_FIELDS "ANOPQRSTUVbcdefgjlmyzWHIKX" -#define USR_FIELDS "ABDECGfhijlopqrstuvyzMKNWX" -// Used by fields_sort, placed here for peace-of-mind -#define NUL_FIELDS "abcdefghijklmnopqrstuvwxyz" + /* The default fields displayed and their order, if nothing is + specified by the loser, oops user. + note: any *contiguous* ascii sequence can serve as fieldscur + characters as long as the initial value is coordinated + with that specified for FLD_OFFSET + ( we're providing for up to 55 fields initially, ) + ( with values chosen to avoid the need to escape ) */ +#define FLD_OFFSET '%' + // seq_fields "%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[" +#define DEF_FIELDS "¥¨³´»½ÀÄ·º¹Å&')*+,-./012568<>?ABCFGHIJKLMNOPQRSTUVWXYZ[" + /* Pre-configured windows/field groups */ +#define JOB_FIELDS "¥¦¹·º³´Ä»¼½§Å()*+,-./012568>?@ABCFGHIJKLMNOPQRSTUVWXYZ[" +#define MEM_FIELDS "¥º»¼½¾¿ÀÁÃij´·Å&'()*+,-./0125689BFGHIJKLMNOPQRSTUVWXYZ[" +#define USR_FIELDS "¥¦§¨ª°¹·ºÄÅ)+,-./1234568;<=>?@ABCFGHIJKLMNOPQRSTUVWXYZ[" - -// The default values for the local config file + /* The default values for the local config file */ #define DEF_RCFILE { \ 0, 1, DEF_DELAY, 0, { \ { P_CPU, DEF_WINFLGS, 0, \ @@ -371,40 +460,39 @@ typedef struct WIN_t { { P_MEM, DEF_WINFLGS, 0, \ COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLUE, COLOR_MAGENTA, \ "Mem", MEM_FIELDS }, \ - { P_URE, DEF_WINFLGS, 0, \ + { P_UEN, DEF_WINFLGS, 0, \ COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW, \ "Usr", USR_FIELDS } \ } } + /* The format string used with variable width columns -- + see 'calibrate_fields' for supporting logic. */ +#define VARCOL_fmts "%-*.*s " -// Summary Lines specially formatted string(s) -- -// see 'show_special' for syntax details + other cautions. + /* Summary Lines specially formatted string(s) -- + see 'show_special' for syntax details + other cautions. */ #define LOADAV_line "%s -%s\n" #define LOADAV_line_alt "%s\06 -%s\n" -#define STATES_line1 "Tasks:\03" \ +#define STATES_line1 "%s:\03" \ " %3u \02total,\03 %3u \02running,\03 %3u \02sleeping,\03 %3u \02stopped,\03 %3u \02zombie\03\n" -#define STATES_line2x4 "%s\03" \ - " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle\03\n" -#define STATES_line2x5 "%s\03" \ - " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle,\03 %#5.1f%% \02IO-wait\03\n" -#define STATES_line2x6 "%s\03" \ - " %#4.1f%% \02us,\03 %#4.1f%% \02sy,\03 %#4.1f%% \02ni,\03 %#4.1f%% \02id,\03 %#4.1f%% \02wa,\03 %#4.1f%% \02hi,\03 %#4.1f%% \02si\03\n" -#define STATES_line2x7 "%s\03" \ - "%#5.1f%%\02us,\03%#5.1f%%\02sy,\03%#5.1f%%\02ni,\03%#5.1f%%\02id,\03%#5.1f%%\02wa,\03%#5.1f%%\02hi,\03%#5.1f%%\02si,\03%#5.1f%%\02st\03\n" -#ifdef CASEUP_SUMMK -#define MEMORY_line1 "Mem: \03" \ - " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02buffers\03\n" -#define MEMORY_line2 "Swap:\03" \ - " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02cached\03\n" -#else -#define MEMORY_line1 "Mem: \03" \ - " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02buffers\03\n" -#define MEMORY_line2 "Swap:\03" \ - " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02cached\03\n" -#endif +#define STATES_line2x4 "%%%s\03" \ + " %#5.1f \02user,\03 %#5.1f \02system,\03 %#5.1f \02nice,\03 %#5.1f \02idle\03\n" + /* These are the STATES_line evolutions + lnx 2.5.x, procps-3.0.5 : IO-wait = i/o wait time + lnx 2.6.x, procps-3.1.12 : IO-wait now wa, hi = hard irq, si = soft irq + lnx 2.7.x, procps-3.2.7 : st = steal time */ +#define STATES_line2x5 "%%%s\03" \ + " %#5.1f \02user,\03 %#5.1f \02system,\03 %#5.1f \02nice,\03 %#5.1f \02idle,\03 %#5.1f \02IO-wait\03\n" +#define STATES_line2x6 "%%%s\03" \ + " %#5.1f \02us,\03 %#5.1f \02sy,\03 %#5.1f \02ni,\03 %#5.1f \02id,\03 %#5.1f \02wa,\03 %#5.1f \02hi,\03 %#5.1f \02si\03\n" +#define STATES_line2x7 "%%%s\03" \ + "%#5.1f \02us,\03%#5.1f \02sy,\03%#5.1f \02ni,\03%#5.1f \02id,\03%#5.1f \02wa,\03%#5.1f \02hi,\03%#5.1f \02si,\03%#5.1f \02st\03\n" +#define MEMORY_twolines \ + "%s Mem: \03 %8lu \02total,\03 %8lu \02used,\03 %8lu \02free,\03 %8lu \02buffers\03\n" \ + "%s Swap:\03 %8lu \02total,\03 %8lu \02used,\03 %8lu \02free,\03 %8lu \02cached\03\n" -// Keyboard Help specially formatted string(s) -- -// see 'show_special' for syntax details + other cautions. + /* Keyboard Help specially formatted string(s) -- + see 'show_special' for syntax details + other cautions. */ #define KEYS_help \ "Help for Interactive Commands\02 - %s\n" \ "Window \01%s\06: \01Cumulative mode \03%s\02. \01System\06: \01Delay \03%.1f secs\02; \01Secure mode \03%s\02.\n" \ @@ -412,16 +500,16 @@ typedef struct WIN_t { " Z\05,\01B\05 Global: '\01Z\02' change color mappings; '\01B\02' disable/enable bold\n" \ " l,t,m Toggle Summaries: '\01l\02' load avg; '\01t\02' task/cpu stats; '\01m\02' mem info\n" \ " 1,I Toggle SMP view: '\0011\02' single/separate states; '\01I\02' Irix/Solaris mode\n" \ + " f,F Manage Fields: add/remove; change order; select sort field\n" \ "\n" \ - " f,o . Fields/Columns: '\01f\02' add or remove; '\01o\02' change display order\n" \ - " F or O . Select sort field\n" \ " <,> . Move sort field: '\01<\02' next col left; '\01>\02' next col right\n" \ " R,H . Toggle: '\01R\02' normal/reverse sort; '\01H\02' show threads\n" \ " c,i,S . Toggle: '\01c\02' cmd name/line; '\01i\02' idle tasks; '\01S\02' cumulative time\n" \ " x\05,\01y\05 . Toggle highlights: '\01x\02' sort field; '\01y\02' running tasks\n" \ " z\05,\01b\05 . Toggle: '\01z\02' color/mono; '\01b\02' bold/reverse (only if 'x' or 'y')\n" \ - " u . Show specific user only\n" \ + " u,U . Show: '\01u\02' effective user; '\01U\02' real, saved, file or effective user\n" \ " n or # . Set maximum tasks displayed\n" \ + " C,... . Toggle scroll coordinates msg for: \01up\02,\01down\02,\01left\02,right\02,\01home\02,\01end\02\n" \ "\n" \ "%s" \ " W Write configuration file\n" \ @@ -431,25 +519,38 @@ typedef struct WIN_t { "any other key to continue " \ "" -// This guy goes into the help text (maybe) + /* This guy goes into the help text (maybe) */ #define KEYS_help_unsecured \ " k,r Manipulate tasks: '\01k\02' kill; '\01r\02' renice\n" \ " d or s Set update interval\n" \ "" -// Fields Reorder/Toggle specially formatted string(s) -- -// see 'show_special' for syntax details + other cautions -// note: the leading newline below serves really dumb terminals; -// if there's no 'cursor_home', the screen will be a mess -// but this part will still be functional. -#define FIELDS_current \ - "\n%sCurrent Fields\02: \01 %s \04 for window \01%s\06\n%s " \ + /* Fields Management specially formatted string(s) -- + see 'show_special' for syntax details + other cautions */ +#define FIELDS_heading \ + "%sFields Management\02 for window \01%s\06, whose current sort field is \01%s\02\n" \ + " Navigate with Up/Dn, Right selects for move then or Left commits,\n" \ + " 'd' or toggles display, 's' sets sort. Use 'q' or to end! " \ "" -// Some extra explanatory text which accompanies the Fields display. -// note: the newlines cannot actually be used, they just serve as -// substring delimiters for the 'display_fields' routine. -#define FIELDS_xtra \ +#ifdef OOMEM_ENABLE + /* w/ 2 extra lines, no room for additional text on 24x80 terminal */ +#define FIELDS_notes NULL +#else + /* Extra explanatory text which could accompany the Fields display. + note: the newlines cannot actually be used, they just serve as + substring delimiters for the 'display_fields' routine. */ +#define FIELDS_notes \ + "Note(s):\n" \ + " Use the 'a' & 'w' keys to cycle\n" \ + " through all available windows.\n" \ + "" +/* no room in FIELDS_notes, sacrificed w/ 35 fields displayed .... */ +/* " If a sort field is not displayed,\n" */ +/* " the '<' & '>' keys are inactive.\n" */ +#endif + +#define FIELDS_flags \ "Flags field:\n" \ " 0x00000001 PF_ALIGNWARN\n" \ " 0x00000002 PF_STARTING\n" \ @@ -465,51 +566,24 @@ typedef struct WIN_t { " 0x001D0000 special states (2.5)\n" \ " 0x00100000 PF_USEDFPU (thru 2.4)\n" \ "" -/* no room, sacrificed this one: 'Killed for out-of-memory' */ -/* " 0x00001000 PF_MEMDIE (2.5)\n" ....................... */ +/* no room in FIELDS_flags, sacrificed w/ 26 fields displayed .... */ +/* ( PF_MEMDIE == 'Killed for out-of-memory' ) */ +/* " 0x00001000 PF_MEMDIE (2.5)\n" */ -// Sort Select specially formatted string(s) -- -// see 'show_special' for syntax details + other cautions -// note: the leading newline below serves really dumb terminals; -// if there's no 'cursor_home', the screen will be a mess -// but this part will still be functional. -#define SORT_fields \ - "\n%sCurrent Sort Field\02: \01 %c \04 for window \01%s\06\n%s " \ - "" - -// Some extra explanatory text which accompanies the Sort display. -// note: the newlines cannot actually be used, they just serve as -// substring delimiters for the 'display_fields' routine. -#define SORT_xtra \ - "Note1:\n" \ - " If a selected sort field can't be\n" \ - " shown due to screen width or your\n" \ - " field order, the '<' and '>' keys\n" \ - " will be unavailable until a field\n" \ - " within viewable range is chosen.\n" \ - "\n" \ - "Note2:\n" \ - " Field sorting uses internal values,\n" \ - " not those in column display. Thus,\n" \ - " the TTY & WCHAN fields will violate\n" \ - " strict ASCII collating sequence.\n" \ - " (shame on you if WCHAN is chosen)\n" \ - "" - -// Colors Help specially formatted string(s) -- -// see 'show_special' for syntax details + other cautions. + /* Colors Help specially formatted string(s) -- + see 'show_special' for syntax details + other cautions. */ #define COLOR_help \ "Help for color mapping\02 - %s\n" \ "current window: \01%s\06\n" \ "\n" \ " color - 04:25:44 up 8 days, 50 min, 7 users, load average:\n" \ " Tasks:\03 64 \02total,\03 2 \03running,\03 62 \02sleeping,\03 0 \02stopped,\03\n" \ - " Cpu(s):\03 76.5%% \02user,\03 11.2%% \02system,\03 0.0%% \02nice,\03 12.3%% \02idle\03\n" \ + " %%Cpu(s):\03 76.5 \02user,\03 11.2 \02system,\03 0.0 \02nice,\03 12.3 \02idle\03\n" \ " \01 Nasty Message! \04 -or- \01Input Prompt\05\n" \ - " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \ - " 17284 \10pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bash \10\n" \ - " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R < color -b \07\n" \ - " 11005 \10? \07 9 0 0.0 0:02.50 2852 1008 S amor -ses \10\n" \ + " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP S COMMAND \06\n" \ + " 17284 \10pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bash \10\n" \ + " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R color -b -z\07\n" \ + " 11005 \10? \07 9 0 0.0 0:02.50 2852 1008 S amor -sessi\10\n" \ " available toggles: \01B\02 =disable bold globally (\01%s\02),\n" \ " \01z\02 =color/mono (\01%s\02), \01b\02 =tasks \"bold\"/reverse (\01%s\02)\n" \ "\n" \ @@ -525,8 +599,8 @@ typedef struct WIN_t { " press 'a' or 'w' to commit & change another, to commit and end " \ "" -// Windows/Field Group Help specially formatted string(s) -- -// see 'show_special' for syntax details + other cautions. + /* Windows/Field Group Help specially formatted string(s) -- + see 'show_special' for syntax details + other cautions. */ #define WINDOWS_help \ "Help for Windows / Field Groups\02 - \"Current Window\" = \01 %s \06\n" \ "\n" \ @@ -536,13 +610,13 @@ typedef struct WIN_t { " . with \01NO\02 task display, some commands will be \01disabled\02 ('i','R','n','c', etc)\n" \ " until a \01different window\02 has been activated, making it the 'current' window\n" \ ". You \01change\02 the 'current' window by: \01 1\02) cycling forward/backward;\01 2\02) choosing\n" \ - " a specific field group; or\01 3\02) exiting the color mapping screen\n" \ + " a specific field group; or\01 3\02) exiting the color mapping or fields screens\n" \ ". Commands \01available anytime -------------\02\n" \ " A . Alternate display mode toggle, show \01Single\02 / \01Multiple\02 windows\n" \ - " G . Choose another field group and make it 'current', or change now\n" \ + " g . Choose another field group and make it 'current', or change now\n" \ " by selecting a number from: \01 1\02 =%s;\01 2\02 =%s;\01 3\02 =%s; or\01 4\02 =%s\n" \ ". Commands \01requiring\02 '\01A\02' mode\01 -------------\02\n" \ - " g . Change the \01Name\05 of the 'current' window/field group\n" \ + " G . Change the \01Name\05 of the 'current' window/field group\n" \ " \01*\04 a , w . Cycle through all four windows: '\01a\05' Forward; '\01w\05' Backward\n" \ " \01*\04 - , _ . Show/Hide: '\01-\05' \01Current\02 window; '\01_\05' all \01Visible\02/\01Invisible\02\n" \ " The screen will be divided evenly between task displays. But you can make\n" \ @@ -551,17 +625,108 @@ typedef struct WIN_t { " (this also forces the \01current\02 or \01every\02 window to become visible)\n" \ "\n" \ "In '\01A\02' mode, '\01*\04' keys are your \01essential\02 commands. Please try the '\01a\02' and '\01w\02'\n" \ - "commands plus the 'G' sub-commands NOW. Press to make 'Current' " \ + "commands plus the 'g' sub-commands NOW. Press to make 'Current' " \ "" /*###### For Piece of mind #############################################*/ /* just sanity check(s)... */ -#if USRNAMSIZ < GETBUFSIZ -# error "Jeeze, USRNAMSIZ Must NOT be less than GETBUFSIZ !" +#if defined(ATEOJ_RPTHSH) && defined(OFF_HST_HASH) +# error 'ATEOJ_RPTHSH' conflicts with 'OFF_HST_HASH' +#endif +#if defined(PRETEND4CPUS) && defined (OOMEM_ENABLE) +# error 'PRETEND4CPUS' conflicts with 'OOMEM_ENABLE' #endif +/*###### Some Prototypes (ha!) #########################################*/ + + /* These 'prototypes' are here solely for documentation purposes */ +/*------ Sort callbacks ------------------------------------------------*/ +/* for each possible field, in the form of: */ +/*atic int sort_P_XXX (const proc_t **P, const proc_t **Q); */ +/*------ Tiny useful routine(s) ----------------------------------------*/ +//atic const char *fmtmk (const char *fmts, ...); +//atic inline char *scat (char *dst, const char *src); +//atic char *strim (char *str); +//atic const char *tg2 (int x, int y); +/*------ Exit/Interrput routines ---------------------------------------*/ +//atic void bye_bye (const char *str); +//atic void error_exit (const char *str); +//atic void pause_pgm (void); +//atic void sig_endpgm (int dont_care_sig); +//atic void sig_paused (int dont_care_sig); +//atic void sig_resize (int dont_care_sig); +/*------ Misc Color/Display support ------------------------------------*/ +//atic void capsmk (WIN_t *q); +//atic void msg_save (const char *fmts, ...); +//atic void show_msg (const char *str); +//atic int show_pmt (const char *str); +//atic inline void show_scroll (void); +//atic void show_special (int interact, const char *glob); +/*------ Low Level Memory/Keyboard support -----------------------------*/ +//atic void *alloc_c (size_t num); +//atic void *alloc_r (void *ptr, size_t num); +//atic int chin (int ech, char *buf, unsigned cnt); +//atic int keyin (int init); +//atic char *linein (const char *prompt); +/*------ Small Utility routines ----------------------------------------*/ +//atic float get_float (const char *prompt); +//atic int get_int (const char *prompt); +//atic const char *scale_num (unsigned long num, const int width, const int type); +//atic const char *scale_tics (TIC_t tics, const int width); +//atic const char *user_certify (WIN_t *q, const char *str, char typ); +//atic inline int user_matched (WIN_t *q, const proc_t *p); +/*------ Fields Management support -------------------------------------*/ +/*atic FLD_t Fieldstab[] = { ... } */ +//atic void adj_geometry (void); +//atic void calibrate_fields (void); +//atic void display_fields (int focus, int extend, const char *xtra); +//atic void fields_utility (void); +//atic void zap_fieldstab (void); +/*------ Library Interface ---------------------------------------------*/ +//atic CPU_t *cpus_refresh (CPU_t *cpus); +#ifdef OFF_HST_HASH +//atic inline HST_t *hstbsrch (HST_t *hst, int max, int pid); +#else +//atic inline HST_t *hstget (int pid); +//atic inline void hstput (unsigned idx); +#endif +//atic void prochlp (proc_t *p); +//atic proc_t **procs_refresh (proc_t **ppt); +/*------ Startup routines ----------------------------------------------*/ +//atic void before (char *me); +//atic void configs_read (void); +//atic void parse_args (char **args); +//atic void whack_terminal (void); +/*------ Windows/Field Groups support ----------------------------------*/ +//atic void win_names (WIN_t *q, const char *name); +//atic WIN_t *win_select (char ch); +//atic int win_warn (int what); +//atic void winsclrhlp (WIN_t *q, int save); +//atic void wins_colors (void); +//atic void wins_reflag (int what, int flg); +//atic void wins_stage_1 (void); +//atic void wins_stage_2 (void); +/*------ Interactive Input support (do_key helpers) --------------------*/ +//atic void file_writerc (void); +//atic void help_view (void); +//atic void keys_global (int ch); +//atic void keys_summary (int ch); +//atic void keys_task (int ch); +//atic void keys_window (int ch); +//atic void keys_xtra (int ch); +/*------ Main Screen routines ------------------------------------------*/ +//atic void do_key (int ch); +//atic void summaryhlp (CPU_t *cpu, const char *pfx); +//atic proc_t **summary_show (void); +//atic void task_show (const WIN_t *q, const proc_t *p); +//atic int window_show (proc_t **ppt, WIN_t *q, int wmax); +/*------ Entry point plus two ------------------------------------------*/ +//atic void framehlp (int wix, int max); +//atic void frame_make (void); +// int main (int dont_care_argc, char **argv); #endif /* _Itop */ +