source: branches/stable/mindi-busybox/procps/top.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 17.4 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * A tiny 'top' utility.
4 *
5 * This is written specifically for the linux /proc/<PID>/stat(m)
6 * files format.
7
8 * This reads the PIDs of all processes and their status and shows
9 * the status of processes (first ones that fit to screen) at given
10 * intervals.
11 *
12 * NOTES:
13 * - At startup this changes to /proc, all the reads are then
14 *   relative to that.
15 *
16 * (C) Eero Tamminen <oak at welho dot com>
17 *
18 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
19 */
20
21/* Original code Copyrights */
22/*
23 * Copyright (c) 1992 Branko Lankester
24 * Copyright (c) 1992 Roger Binns
25 * Copyright (C) 1994-1996 Charles L. Blake.
26 * Copyright (C) 1992-1998 Michael K. Johnson
27 * May be distributed under the conditions of the
28 * GNU Library General Public License
29 */
30
31#include "libbb.h"
32
33
34typedef struct top_status_t {
35    unsigned long vsz;
36#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
37    unsigned long ticks;
38    unsigned pcpu; /* delta of ticks */
39#endif
40    unsigned pid, ppid;
41    unsigned uid;
42    char state[4];
43    char comm[COMM_LEN];
44} top_status_t;
45
46typedef struct jiffy_counts_t {
47    unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;
48    unsigned long long total;
49    unsigned long long busy;
50} jiffy_counts_t;
51
52/* This structure stores some critical information from one frame to
53   the next. Used for finding deltas. */
54typedef struct save_hist {
55    unsigned long ticks;
56    unsigned pid;
57} save_hist;
58
59typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
60
61enum { SORT_DEPTH = 3 };
62
63struct globals {
64    top_status_t *top;
65    int ntop;
66#if ENABLE_FEATURE_USE_TERMIOS
67    struct termios initial_settings;
68#endif
69#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
70    cmp_funcp sort_function;
71#else
72    cmp_funcp sort_function[SORT_DEPTH];
73    struct save_hist *prev_hist;
74    int prev_hist_count;
75    jiffy_counts_t jif, prev_jif;
76    /* int hist_iterations; */
77    unsigned total_pcpu;
78    /* unsigned long total_vsz; */
79#endif
80};
81#define G (*(struct globals*)&bb_common_bufsiz1)
82#define top              (G.top               )
83#define ntop             (G.ntop              )
84#if ENABLE_FEATURE_USE_TERMIOS
85#define initial_settings (G. initial_settings )
86#endif
87#define sort_function    (G.sort_function     )
88#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
89#define prev_hist        (G.prev_hist         )
90#define prev_hist_count  (G.prev_hist_count   )
91#define jif              (G.jif               )
92#define prev_jif         (G.prev_jif          )
93#define total_pcpu       (G.total_pcpu        )
94#endif
95
96#define OPT_BATCH_MODE (option_mask32 & 0x4)
97
98
99#if ENABLE_FEATURE_USE_TERMIOS
100static int pid_sort(top_status_t *P, top_status_t *Q)
101{
102    /* Buggy wrt pids with high bit set */
103    /* (linux pids are in [1..2^15-1]) */
104    return (Q->pid - P->pid);
105}
106#endif
107
108static int mem_sort(top_status_t *P, top_status_t *Q)
109{
110    /* We want to avoid unsigned->signed and truncation errors */
111    if (Q->vsz < P->vsz) return -1;
112    return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
113}
114
115
116#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
117
118static int pcpu_sort(top_status_t *P, top_status_t *Q)
119{
120    /* Buggy wrt ticks with high bit set */
121    /* Affects only processes for which ticks overflow */
122    return (int)Q->pcpu - (int)P->pcpu;
123}
124
125static int time_sort(top_status_t *P, top_status_t *Q)
126{
127    /* We want to avoid unsigned->signed and truncation errors */
128    if (Q->ticks < P->ticks) return -1;
129    return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
130}
131
132static int mult_lvl_cmp(void* a, void* b)
133{
134    int i, cmp_val;
135
136    for (i = 0; i < SORT_DEPTH; i++) {
137        cmp_val = (*sort_function[i])(a, b);
138        if (cmp_val != 0)
139            return cmp_val;
140    }
141    return 0;
142}
143
144
145static void get_jiffy_counts(void)
146{
147    FILE* fp = xfopen("stat", "r");
148    prev_jif = jif;
149    if (fscanf(fp, "cpu  %lld %lld %lld %lld %lld %lld %lld %lld",
150            &jif.usr,&jif.nic,&jif.sys,&jif.idle,
151            &jif.iowait,&jif.irq,&jif.softirq,&jif.steal) < 4) {
152        bb_error_msg_and_die("failed to read /proc/stat");
153    }
154    fclose(fp);
155    jif.total = jif.usr + jif.nic + jif.sys + jif.idle
156            + jif.iowait + jif.irq + jif.softirq + jif.steal;
157    /* procps 2.x does not count iowait as busy time */
158    jif.busy = jif.total - jif.idle - jif.iowait;
159}
160
161
162static void do_stats(void)
163{
164    top_status_t *cur;
165    pid_t pid;
166    int i, last_i, n;
167    struct save_hist *new_hist;
168
169    get_jiffy_counts();
170    total_pcpu = 0;
171    /* total_vsz = 0; */
172    new_hist = xmalloc(sizeof(struct save_hist)*ntop);
173    /*
174     * Make a pass through the data to get stats.
175     */
176    /* hist_iterations = 0; */
177    i = 0;
178    for (n = 0; n < ntop; n++) {
179        cur = top + n;
180
181        /*
182         * Calculate time in cur process.  Time is sum of user time
183         * and system time
184         */
185        pid = cur->pid;
186        new_hist[n].ticks = cur->ticks;
187        new_hist[n].pid = pid;
188
189        /* find matching entry from previous pass */
190        cur->pcpu = 0;
191        /* do not start at index 0, continue at last used one
192         * (brought hist_iterations from ~14000 down to 172) */
193        last_i = i;
194        if (prev_hist_count) do {
195            if (prev_hist[i].pid == pid) {
196                cur->pcpu = cur->ticks - prev_hist[i].ticks;
197                total_pcpu += cur->pcpu;
198                break;
199            }
200            i = (i+1) % prev_hist_count;
201            /* hist_iterations++; */
202        } while (i != last_i);
203        /* total_vsz += cur->vsz; */
204    }
205
206    /*
207     * Save cur frame's information.
208     */
209    free(prev_hist);
210    prev_hist = new_hist;
211    prev_hist_count = ntop;
212}
213#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
214
215#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
216/* formats 7 char string (8 with terminating NUL) */
217static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
218{
219    unsigned t;
220    if (value >= total) { /* 100% ? */
221        strcpy(pbuf, "  100% ");
222        return pbuf;
223    }
224    /* else generate " [N/space]N.N% " string */
225    value = 1000 * value / total;
226    t = value / 100;
227    value = value % 100;
228    pbuf[0] = ' ';
229    pbuf[1] = t ? t + '0' : ' ';
230    pbuf[2] = '0' + (value / 10);
231    pbuf[3] = '.';
232    pbuf[4] = '0' + (value % 10);
233    pbuf[5] = '%';
234    pbuf[6] = ' ';
235    pbuf[7] = '\0';
236    return pbuf;
237}
238#endif
239
240/* display generic info (meminfo / loadavg) */
241static unsigned long display_generic(int scr_width)
242{
243    FILE *fp;
244    char buf[80];
245    char scrbuf[80];
246    unsigned long total, used, mfree, shared, buffers, cached;
247#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
248    unsigned total_diff;
249#endif
250
251    /* read memory info */
252    fp = xfopen("meminfo", "r");
253
254    /*
255     * Old kernels (such as 2.4.x) had a nice summary of memory info that
256     * we could parse, however this is gone entirely in 2.6. Try parsing
257     * the old way first, and if that fails, parse each field manually.
258     *
259     * First, we read in the first line. Old kernels will have bogus
260     * strings we don't care about, whereas new kernels will start right
261     * out with MemTotal:
262     *                              -- PFM.
263     */
264    if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
265        fgets(buf, sizeof(buf), fp);    /* skip first line */
266
267        fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
268            &total, &used, &mfree, &shared, &buffers, &cached);
269        /* convert to kilobytes */
270        used /= 1024;
271        mfree /= 1024;
272        shared /= 1024;
273        buffers /= 1024;
274        cached /= 1024;
275        total /= 1024;
276    } else {
277        /*
278         * Revert to manual parsing, which incidentally already has the
279         * sizes in kilobytes. This should be safe for both 2.4 and
280         * 2.6.
281         */
282
283        fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
284
285        /*
286         * MemShared: is no longer present in 2.6. Report this as 0,
287         * to maintain consistent behavior with normal procps.
288         */
289        if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
290            shared = 0;
291
292        fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
293        fscanf(fp, "Cached: %lu %s\n", &cached, buf);
294
295        used = total - mfree;
296    }
297    fclose(fp);
298
299    /* output memory info */
300    if (scr_width > sizeof(scrbuf))
301        scr_width = sizeof(scrbuf);
302    snprintf(scrbuf, scr_width,
303        "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
304        used, mfree, shared, buffers, cached);
305    /* clear screen & go to top */
306    printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf);
307
308#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
309    /*
310     * xxx% = (jif.xxx - prev_jif.xxx) / (jif.total - prev_jif.total) * 100%
311     */
312    /* using (unsigned) casts to make operations cheaper */
313    total_diff = ((unsigned)(jif.total - prev_jif.total) ? : 1);
314#if ENABLE_FEATURE_TOP_DECIMALS
315/* Generated code is approx +0.3k */
316#define CALC_STAT(xxx) char xxx[8]
317#define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(jif.xxx - prev_jif.xxx), total_diff)
318#define FMT "%s"
319#else
320#define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(jif.xxx - prev_jif.xxx) / total_diff
321#define SHOW_STAT(xxx) xxx
322#define FMT "%4u%% "
323#endif
324    { /* need block: CALC_STAT are declarations */
325        CALC_STAT(usr);
326        CALC_STAT(sys);
327        CALC_STAT(nic);
328        CALC_STAT(idle);
329        CALC_STAT(iowait);
330        CALC_STAT(irq);
331        CALC_STAT(softirq);
332        //CALC_STAT(steal);
333
334        snprintf(scrbuf, scr_width,
335            /* Barely fits in 79 chars when in "decimals" mode. */
336            "CPU:"FMT"usr"FMT"sys"FMT"nice"FMT"idle"FMT"io"FMT"irq"FMT"softirq",
337            SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
338            SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
339            //, SHOW_STAT(steal) - what is this 'steal' thing?
340            // I doubt anyone wants to know it
341        );
342    }
343    puts(scrbuf);
344#undef SHOW_STAT
345#undef CALC_STAT
346#undef FMT
347#endif
348
349    /* read load average as a string */
350    buf[0] = '\0';
351    open_read_close("loadavg", buf, sizeof("N.NN N.NN N.NN")-1);
352    buf[sizeof("N.NN N.NN N.NN")-1] = '\0';
353    snprintf(scrbuf, scr_width, "Load average: %s", buf);
354    puts(scrbuf);
355
356    return total;
357}
358
359/* display process statuses */
360static void display_status(int count, int scr_width)
361{
362    enum {
363        BITS_PER_INT = sizeof(int)*8
364    };
365
366    top_status_t *s = top;
367    char vsz_str_buf[8];
368    unsigned long total_memory = display_generic(scr_width); /* or use total_vsz? */
369    /* xxx_shift and xxx_scale variables allow us to replace
370     * expensive divides with multiply and shift */
371    unsigned pmem_shift, pmem_scale, pmem_half;
372#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
373    unsigned pcpu_shift, pcpu_scale, pcpu_half;
374    unsigned busy_jifs;
375
376    /* what info of the processes is shown */
377    printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
378        "  PID  PPID USER     STAT   VSZ %MEM %CPU COMMAND");
379#define MIN_WIDTH \
380    sizeof( "  PID  PPID USER     STAT   VSZ %MEM %CPU C")
381#else
382
383    /* !CPU_USAGE_PERCENTAGE */
384    printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
385        "  PID  PPID USER     STAT   VSZ %MEM COMMAND");
386#define MIN_WIDTH \
387    sizeof( "  PID  PPID USER     STAT   VSZ %MEM C")
388#endif
389
390#if ENABLE_FEATURE_TOP_DECIMALS
391#define UPSCALE 1000
392#define CALC_STAT(name, val) div_t name = div((val), 10)
393#define SHOW_STAT(name) name.quot, '0'+name.rem
394#define FMT "%3u.%c"
395#else
396#define UPSCALE 100
397#define CALC_STAT(name, val) unsigned name = (val)
398#define SHOW_STAT(name) name
399#define FMT "%4u%%"
400#endif
401    /*
402     * MEM% = s->vsz/MemTotal
403     */
404    pmem_shift = BITS_PER_INT-11;
405    pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
406    /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
407    while (pmem_scale >= 512) {
408        pmem_scale /= 4;
409        pmem_shift -= 2;
410    }
411    pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
412#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
413    busy_jifs = jif.busy - prev_jif.busy;
414    /* This happens if there were lots of short-lived processes
415     * between two top updates (e.g. compilation) */
416    if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
417
418    /*
419     * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
420     * (pcpu is delta of sys+user time between samples)
421     */
422    /* (jif.xxx - prev_jif.xxx) and s->pcpu are
423     * in 0..~64000 range (HZ*update_interval).
424     * we assume that unsigned is at least 32-bit.
425     */
426    pcpu_shift = 6;
427    pcpu_scale = (UPSCALE*64*(uint16_t)busy_jifs ? : 1);
428    while (pcpu_scale < (1U<<(BITS_PER_INT-2))) {
429        pcpu_scale *= 4;
430        pcpu_shift += 2;
431    }
432    pcpu_scale /= ( (uint16_t)(jif.total-prev_jif.total)*total_pcpu ? : 1);
433    /* we want (s->pcpu * pcpu_scale) to never overflow */
434    while (pcpu_scale >= 1024) {
435        pcpu_scale /= 4;
436        pcpu_shift -= 2;
437    }
438    pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
439    /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
440#endif
441
442    /* Ok, all prelim data is ready, go thru the list */
443    while (count-- > 0) {
444        int col = scr_width;
445        CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
446#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
447        CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
448#endif
449
450        if (s->vsz >= 100*1024)
451            sprintf(vsz_str_buf, "%6ldM", s->vsz/1024);
452        else
453            sprintf(vsz_str_buf, "%7ld", s->vsz);
454        // PID PPID USER STAT VSZ %MEM [%CPU] COMMAND
455        col -= printf("\n" "%5u%6u %-8.8s %s%s" FMT
456#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
457                FMT
458#endif
459                " ",
460                s->pid, s->ppid, get_cached_username(s->uid),
461                s->state, vsz_str_buf,
462                SHOW_STAT(pmem)
463#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
464                , SHOW_STAT(pcpu)
465#endif
466        );
467        if (col > 0) {
468            char buf[col + 1];
469            read_cmdline(buf, col, s->pid, s->comm);
470            fputs(buf, stdout);
471        }
472        /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
473            jif.busy - prev_jif.busy, jif.total - prev_jif.total); */
474        s++;
475    }
476    /* printf(" %d", hist_iterations); */
477    putchar(OPT_BATCH_MODE ? '\n' : '\r');
478    fflush(stdout);
479}
480#undef UPSCALE
481#undef SHOW_STAT
482#undef CALC_STAT
483#undef FMT
484
485
486static void clearmems(void)
487{
488    clear_username_cache();
489    free(top);
490    top = 0;
491    ntop = 0;
492}
493
494
495#if ENABLE_FEATURE_USE_TERMIOS
496#include <termios.h>
497#include <signal.h>
498
499static void reset_term(void)
500{
501    tcsetattr(0, TCSANOW, (void *) &initial_settings);
502#if ENABLE_FEATURE_CLEAN_UP
503    clearmems();
504#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
505    free(prev_hist);
506#endif
507#endif /* FEATURE_CLEAN_UP */
508}
509
510static void sig_catcher(int sig ATTRIBUTE_UNUSED)
511{
512    reset_term();
513    exit(1);
514}
515#endif /* FEATURE_USE_TERMIOS */
516
517
518int top_main(int argc, char **argv);
519int top_main(int argc, char **argv)
520{
521    int count, lines, col;
522    unsigned interval = 5; /* default update rate is 5 seconds */
523    unsigned iterations = UINT_MAX; /* 2^32 iterations by default :) */
524    char *sinterval, *siterations;
525#if ENABLE_FEATURE_USE_TERMIOS
526    struct termios new_settings;
527    struct timeval tv;
528    fd_set readfds;
529    unsigned char c;
530#endif /* FEATURE_USE_TERMIOS */
531
532    interval = 5;
533
534    /* do normal option parsing */
535    opt_complementary = "-";
536    getopt32(argv, "d:n:b", &sinterval, &siterations);
537    if (option_mask32 & 0x1) interval = xatou(sinterval); // -d
538    if (option_mask32 & 0x2) iterations = xatou(siterations); // -n
539    //if (option_mask32 & 0x4) // -b
540
541    /* change to /proc */
542    xchdir("/proc");
543#if ENABLE_FEATURE_USE_TERMIOS
544    tcgetattr(0, (void *) &initial_settings);
545    memcpy(&new_settings, &initial_settings, sizeof(struct termios));
546    /* unbuffered input, turn off echo */
547    new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
548
549    signal(SIGTERM, sig_catcher);
550    signal(SIGINT, sig_catcher);
551    tcsetattr(0, TCSANOW, (void *) &new_settings);
552    atexit(reset_term);
553#endif /* FEATURE_USE_TERMIOS */
554
555#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
556    sort_function[0] = pcpu_sort;
557    sort_function[1] = mem_sort;
558    sort_function[2] = time_sort;
559#else
560    sort_function = mem_sort;
561#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
562
563    while (1) {
564        procps_status_t *p = NULL;
565
566        /* Default to 25 lines - 5 lines for status */
567        lines = 24 - 3 USE_FEATURE_TOP_CPU_GLOBAL_PERCENTS( - 1);
568        col = 79;
569#if ENABLE_FEATURE_USE_TERMIOS
570        get_terminal_width_height(0, &col, &lines);
571        if (lines < 5 || col < MIN_WIDTH) {
572            sleep(interval);
573            continue;
574        }
575        lines -= 3 USE_FEATURE_TOP_CPU_GLOBAL_PERCENTS( + 1);
576#endif /* FEATURE_USE_TERMIOS */
577
578        /* read process IDs & status for all the processes */
579        while ((p = procps_scan(p, 0
580                | PSSCAN_PID
581                | PSSCAN_PPID
582                | PSSCAN_VSZ
583                | PSSCAN_STIME
584                | PSSCAN_UTIME
585                | PSSCAN_STATE
586                | PSSCAN_COMM
587                | PSSCAN_SID
588                | PSSCAN_UIDGID
589        ))) {
590            int n = ntop;
591            top = xrealloc(top, (++ntop) * sizeof(*top));
592            top[n].pid = p->pid;
593            top[n].ppid = p->ppid;
594            top[n].vsz = p->vsz;
595#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
596            top[n].ticks = p->stime + p->utime;
597#endif
598            top[n].uid = p->uid;
599            strcpy(top[n].state, p->state);
600            strcpy(top[n].comm, p->comm);
601        }
602        if (ntop == 0) {
603            bb_error_msg_and_die("no process info in /proc");
604        }
605#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
606        if (!prev_hist_count) {
607            do_stats();
608            sleep(1);
609            clearmems();
610            continue;
611        }
612        do_stats();
613/* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
614        qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
615#else
616        qsort(top, ntop, sizeof(top_status_t), (void*)sort_function);
617#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
618        count = lines;
619        if (OPT_BATCH_MODE || count > ntop) {
620            count = ntop;
621        }
622        /* show status for each of the processes */
623        display_status(count, col);
624#if ENABLE_FEATURE_USE_TERMIOS
625        tv.tv_sec = interval;
626        tv.tv_usec = 0;
627        FD_ZERO(&readfds);
628        FD_SET(0, &readfds);
629        select(1, &readfds, NULL, NULL, &tv);
630        if (FD_ISSET(0, &readfds)) {
631            if (read(0, &c, 1) <= 0) {   /* signal */
632                return EXIT_FAILURE;
633            }
634            if (c == 'q' || c == initial_settings.c_cc[VINTR])
635                break;
636            if (c == 'M') {
637#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
638                sort_function[0] = mem_sort;
639                sort_function[1] = pcpu_sort;
640                sort_function[2] = time_sort;
641#else
642                sort_function = mem_sort;
643#endif
644            }
645#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
646            if (c == 'P') {
647                sort_function[0] = pcpu_sort;
648                sort_function[1] = mem_sort;
649                sort_function[2] = time_sort;
650            }
651            if (c == 'T') {
652                sort_function[0] = time_sort;
653                sort_function[1] = mem_sort;
654                sort_function[2] = pcpu_sort;
655            }
656#endif
657            if (c == 'N') {
658#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
659                sort_function[0] = pid_sort;
660#else
661                sort_function = pid_sort;
662#endif
663            }
664        }
665        if (!--iterations)
666            break;
667#else
668        sleep(interval);
669#endif /* FEATURE_USE_TERMIOS */
670        clearmems();
671    }
672    if (ENABLE_FEATURE_CLEAN_UP)
673        clearmems();
674    putchar('\n');
675    return EXIT_SUCCESS;
676}
Note: See TracBrowser for help on using the repository browser.